@lendi-ui/auto-complete
Advanced tools
Comparing version 5.2.0 to 5.2.1
## [5.1.0] 2020-03-17 [FUNNEL-1124]( | ||
## 5.2.1 | ||
### Patch Changes | ||
- fix publish issue | ||
- Updated dependencies [undefined] | ||
- @lendi-ui/icon@9.7.1 | ||
- @lendi-ui/breakpoint@5.2.1 | ||
- @lendi-ui/color@5.1.1 | ||
- @lendi-ui/depth@4.1.1 | ||
- @lendi-ui/spacing@7.1.1 | ||
- @lendi-ui/spinner@4.1.1 | ||
- @lendi-ui/text-input@4.2.1 | ||
- @lendi-ui/typography@5.1.1 | ||
- @lendi-ui/utils@5.1.1 | ||
## 5.2.0 | ||
@@ -4,0 +20,0 @@ |
@@ -1,11 +0,1 @@ | ||
// are you seeing an error that a default export doesn't exist but your source file has a default export? | ||
// you should run `yarn` or `yarn preconstruct dev` if preconstruct dev isn't in your postinstall hook | ||
// curious why you need to? | ||
// this file exists so that you can import from the entrypoint normally | ||
// except that it points to your source file and you don't need to run build constantly | ||
// which means we need to re-export all of the modules from your source file | ||
// and since export * doesn't include default exports, we need to read your source file | ||
// to check for a default export and re-export it if it exists | ||
// it's not ideal, but it works pretty well ¯\_(ツ)_/¯ | ||
export * from "../src/index"; | ||
export * from "./declarations/src/index"; |
@@ -1,21 +0,7 @@ | ||
"use strict"; | ||
// this file might look strange and you might be wondering what it's for | ||
// it's lets you import your source files by importing this entrypoint | ||
// as you would import it if it was built with preconstruct build | ||
// this file is slightly different to some others though | ||
// it has a require hook which compiles your code with Babel | ||
// this means that you don't have to set up @babel/register or anything like that | ||
// but you can still require this module and it'll be compiled | ||
'use strict'; | ||
const path = require("path"); | ||
// this bit of code imports the require hook and registers it | ||
let unregister = require("../../../node_modules/@preconstruct/hook/dist/hook.cjs.js").___internalHook(path.resolve(__dirname, "../../.."), path.resolve(__dirname, "..")); | ||
// this re-exports the source file | ||
module.exports = require("../src/index.tsx"); | ||
// this unregisters the require hook so that any modules required after this one | ||
// aren't compiled with the require hook in case you have some other require hook | ||
// or something that should be used on other modules | ||
unregister(); | ||
if (process.env.NODE_ENV === "production") { | ||
module.exports = require("./auto-complete.cjs.prod.js"); | ||
} else { | ||
module.exports = require("./auto-complete.cjs.dev.js"); | ||
} |
@@ -1,15 +0,847 @@ | ||
// 👋 hey!! | ||
// you might be reading this and seeing .esm in the filename | ||
// and being confused why there is commonjs below this filename | ||
// DON'T WORRY! | ||
// this is intentional | ||
// it's only commonjs with `preconstruct dev` | ||
// when you run `preconstruct build`, it will be ESM | ||
// why is it commonjs? | ||
// we need to re-export every export from the source file | ||
// but we can't do that with ESM without knowing what the exports are (because default exports aren't included in export/import *) | ||
// and they could change after running `preconstruct dev` so we can't look at the file without forcing people to | ||
// run preconstruct dev again which wouldn't be ideal | ||
// this solution could change but for now, it's working | ||
import { Component, createElement, createRef, cloneElement } from 'react'; | ||
import { debounce } from 'lodash'; | ||
import { Input } from '@lendi-ui/text-input'; | ||
import styled, { css } from 'styled-components'; | ||
import Spinner from '@lendi-ui/spinner'; | ||
import { normalise, deriveSize } from '@lendi-ui/utils'; | ||
import { gte, map } from '@lendi-ui/breakpoint'; | ||
import { m, py, px, p } from '@lendi-ui/spacing'; | ||
import { bg, fg } from '@lendi-ui/color'; | ||
import { Close } from '@lendi-ui/icon'; | ||
import { depth } from '@lendi-ui/depth'; | ||
import { select } from '@lendi-ui/theme'; | ||
module.exports = require("../src/index.tsx") | ||
function _templateObject13() { | ||
var data = _taggedTemplateLiteral(["\n top: ", ";\n width: ", ";\n height: ", ";\n "]); | ||
_templateObject13 = function _templateObject13() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject12() { | ||
var data = _taggedTemplateLiteral(["\n top: ", ";\n width: ", ";\n height: ", ";\n "]); | ||
_templateObject12 = function _templateObject12() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject11() { | ||
var data = _taggedTemplateLiteral(["\n top: ", ";\n width: ", ";\n height: ", ";\n "]); | ||
_templateObject11 = function _templateObject11() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject10() { | ||
var data = _taggedTemplateLiteral(["\n ", ";\n"]); | ||
_templateObject10 = function _templateObject10() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject9() { | ||
var data = _taggedTemplateLiteral(["\n ", ";\n"]); | ||
_templateObject9 = function _templateObject9() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject8() { | ||
var data = _taggedTemplateLiteral(["\n cursor: pointer;\n"]); | ||
_templateObject8 = function _templateObject8() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject7() { | ||
var data = _taggedTemplateLiteral(["\n ", ";\n"]); | ||
_templateObject7 = function _templateObject7() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject6() { | ||
var data = _taggedTemplateLiteral(["\n ", ";\n min-width: 110px;\n"]); | ||
_templateObject6 = function _templateObject6() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject5() { | ||
var data = _taggedTemplateLiteral(["\n background-color: initial;\n color: initial;\n "]); | ||
_templateObject5 = function _templateObject5() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject4() { | ||
var data = _taggedTemplateLiteral(["\n ", ";\n ", ";\n "]); | ||
_templateObject4 = function _templateObject4() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject3() { | ||
var data = _taggedTemplateLiteral(["\n max-height: 40px;\n "]); | ||
_templateObject3 = function _templateObject3() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject2() { | ||
var data = _taggedTemplateLiteral(["\n width: auto;\n word-wrap: break-word;\n list-style: none;\n ", ";\n ", ";\n max-height: 48px;\n ", ";\n font-family: ", ";\n :hover {\n ", ";\n ", " cursor: pointer;\n font-weight: 700;\n }\n\n ", ";\n"]); | ||
_templateObject2 = function _templateObject2() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _templateObject() { | ||
var data = _taggedTemplateLiteral(["\n ", ";\n ", ";\n ", ";\n border-radius: ", ";\n max-height: 192px;\n position: absolute;\n ", ";\n z-index: 1;\n ", ";\n overflow: auto;\n outline: none;\n width: ", ";\n"]); | ||
_templateObject = function _templateObject() { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); } | ||
var AutoCompleteList = styled.ul(_templateObject(), m('nil'), py('xxs'), px('nil'), select('borderRadius'), bg('shade.0'), depth(1), props => props.customWidth ? props.customWidth + 'px' : 'auto'); | ||
var AutoCompleteListItem = styled.li(_templateObject2(), px('sm'), py('xs'), gte('tablet')(_templateObject3()), select('typography.body.fontFamily'), bg('secondary.500'), fg('shade.0'), (_ref) => { | ||
var { | ||
isActive | ||
} = _ref; | ||
return isActive ? css(_templateObject4(), bg('secondary.500'), fg('shade.0')) : css(_templateObject5()); | ||
}); | ||
var AutoCompleteWrapper = styled.div(_templateObject6(), normalise); | ||
var AfterIconWrapper = styled.span(_templateObject7(), p('sm')); | ||
var CloseWrapper = styled.span(_templateObject8()); | ||
var SpinnerWrapper = styled(Spinner)(_templateObject9(), (_ref2) => { | ||
var { | ||
size | ||
} = _ref2; | ||
return iconBySizeMixin(size); | ||
}); | ||
var CloseIcon = styled(Close)(_templateObject10(), (_ref3) => { | ||
var { | ||
size | ||
} = _ref3; | ||
return iconBySizeMixin(size); | ||
}); | ||
var iconBySizeMixin = size => map(size, val => { | ||
switch (val) { | ||
case 'sm': | ||
return css(_templateObject11(), deriveSize(0.35), deriveSize(1), deriveSize(1)); | ||
case 'md': | ||
return css(_templateObject12(), deriveSize(0.45), deriveSize(1.25), deriveSize(1.25)); | ||
case 'lg': | ||
return css(_templateObject13(), deriveSize(0.35), deriveSize(1.75), deriveSize(1.75)); | ||
default: | ||
return undefined; | ||
} | ||
}); | ||
var KEY_UP = 'ArrowUp'; | ||
var KEY_DOWN = 'ArrowDown'; | ||
var KEY_ENTER = 'Enter'; | ||
var KEY_TAB = 'Tab'; | ||
var KEY_ESCAPE = 'Escape'; | ||
class AutoCompleteMenuList extends Component { | ||
componentDidMount() { | ||
this.props.debounceWindowResize(); | ||
} | ||
render() { | ||
var { | ||
filteredDataSource, | ||
onSelectItem = () => {}, | ||
activeSelection = 0, | ||
menuWidth, | ||
innerRef, | ||
onMouseEnter: _onMouseEnter = () => {} | ||
} = this.props; | ||
return /*#__PURE__*/createElement(AutoCompleteList, { | ||
customWidth: menuWidth, | ||
ref: innerRef | ||
}, filteredDataSource.map((option, index) => /*#__PURE__*/createElement(AutoCompleteListItem, { | ||
key: index, | ||
tabIndex: index, | ||
className: index === activeSelection ? 'selectedItem' : '', | ||
isActive: index === activeSelection, | ||
onClick: () => onSelectItem(option), | ||
value: option.value, | ||
onMouseEnter: () => _onMouseEnter(index) | ||
}, /*#__PURE__*/createElement("div", { | ||
dangerouslySetInnerHTML: { | ||
__html: option.label | ||
} | ||
})))); | ||
} | ||
} | ||
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } | ||
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } | ||
function extractData(_x, _x2) { | ||
return _extractData.apply(this, arguments); | ||
} | ||
function _extractData() { | ||
_extractData = _asyncToGenerator(function* (userInput, dataSource) { | ||
var filteredDataSource = []; | ||
if (userInput !== '') { | ||
if (!Array.isArray(dataSource)) { | ||
filteredDataSource = yield dataSource(userInput); | ||
} else { | ||
filteredDataSource = dataSource.filter(data => data.label.toLowerCase().indexOf(userInput.toLowerCase()) > -1); | ||
} | ||
filteredDataSource = filteredDataSource.map(d => makeInputKeyBold(d, userInput.toLowerCase())); | ||
} else { | ||
filteredDataSource = []; | ||
} | ||
return filteredDataSource; | ||
}); | ||
return _extractData.apply(this, arguments); | ||
} | ||
var makeInputKeyBold = (str, find) => { | ||
var regex = new RegExp(escapeSpecialCharacters(find), 'gi'); | ||
var matchings = str.label.match(regex); | ||
if (matchings) { | ||
return { | ||
label: str.label.replace(new RegExp(escapeSpecialCharacters(matchings[0]), 'g'), '<b>' + matchings[0] + '</b>'), | ||
value: str.value | ||
}; | ||
} else { | ||
return str; | ||
} | ||
}; | ||
function escapeSpecialCharacters(text) { | ||
return text.replace(/[-[\]{}()*+?.,\\/^$|#\s]/g, '\\$&'); | ||
} | ||
var getOffsetScrollTop = menuContainer => { | ||
// The idea is to calculate the new scrollTop for menuContainer as you scroll up/down. Just a simple mathematics to scroll the | ||
// container by selected item height. If the additon of selectedItem's offsetTop and offsetHeight cannot be accomodated inside menucontainer, | ||
// which means if their addition is bigger than menuContainer's offsetHeight then we will make its difference to menuContainer's scrollTop | ||
// and the addiiton of 7, is just to have extra padding to accodomate the padding we are giving at menuContainer's level - ${py('xxs')}; | ||
// for example - if we change the padding ${py('xxs')} to ${py('xs')}, then we need to change this factor of 7 to 12. | ||
var newScrollTop = 0; | ||
if (menuContainer.current) { | ||
menuContainer.current.scrollTop = 0; // just a reset and calculate again. | ||
newScrollTop = menuContainer.current.querySelector('.selectedItem').offsetTop + menuContainer.current.querySelector('.selectedItem').offsetHeight - menuContainer.current.offsetHeight + 7; | ||
} | ||
return newScrollTop; | ||
}; // makeInputKeyBold makes you item label to contain html <b> tag to make your selection | ||
// bold, this function remove that, before we send the item to consumer. | ||
function transformedItem(item) { | ||
return { | ||
label: item.label.replace(/<\/?[^>]+(>|$)/g, ''), | ||
// remove html tag like <b> | ||
value: item.value | ||
}; | ||
} | ||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } | ||
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } | ||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } | ||
function asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } | ||
function _asyncToGenerator$1(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
var DEBOUNCE_WAIT = 300; | ||
var WINDOW_RESIZE_WAIT = 100; | ||
class AutoComplete extends Component { | ||
constructor(props) { | ||
var _this; | ||
super(props); | ||
_this = this; | ||
_defineProperty(this, "state", { | ||
activeSelection: 0, | ||
filteredDataSource: [], | ||
userInput: this.props.initialValue || '', | ||
showList: false, | ||
isLoading: false, | ||
menuWidth: 0 | ||
}); | ||
_defineProperty(this, "menuContainerRef", /*#__PURE__*/createRef()); | ||
_defineProperty(this, "inputWrapper", /*#__PURE__*/createRef()); | ||
_defineProperty(this, "calcInputWidth", () => { | ||
// @TODO - Future enhancement, ref forwarding https://creditandfinance.atlassian.net/browse/HUB-510 | ||
if (this.inputWrapper && this.inputWrapper.current) { | ||
var inputChild = this.inputWrapper.current.firstElementChild; | ||
if (inputChild !== null) { | ||
this.setState({ | ||
menuWidth: inputChild.offsetWidth | ||
}); | ||
} | ||
} | ||
}); | ||
_defineProperty(this, "getFilteredData", /*#__PURE__*/function () { | ||
var _ref = _asyncToGenerator$1(function* (userInput) { | ||
var { | ||
dataSource, | ||
backupOption | ||
} = _this.props; | ||
var filteredDataSource = yield extractData(userInput, dataSource); | ||
if (!filteredDataSource.length && backupOption) { | ||
filteredDataSource.push(backupOption); | ||
} | ||
_this.setState({ | ||
activeSelection: 0, | ||
filteredDataSource, | ||
showList: true, | ||
isLoading: false | ||
}); | ||
}); | ||
return function (_x) { | ||
return _ref.apply(this, arguments); | ||
}; | ||
}()); | ||
_defineProperty(this, "onHoverOff", e => { | ||
// this component is uncontrolled component, and showing-menu is internal to it, | ||
// so just checking if consumer clicks outside `AC`, it will find out and close the menu | ||
var theClickedElement = e.target.parentElement; // @TODO - Future enhancement, ref forwarding https://creditandfinance.atlassian.net/browse/HUB-510 | ||
var inputChild = this.inputWrapper.current.firstElementChild; | ||
if (inputChild !== theClickedElement) { | ||
this.setState({ | ||
showList: false | ||
}); | ||
} | ||
}); | ||
_defineProperty(this, "renderAfterIcon", () => /*#__PURE__*/createElement(AfterIconWrapper, null, this.state.isLoading ? /*#__PURE__*/createElement(SpinnerWrapper, { | ||
size: this.props.size, | ||
variant: "dark" | ||
}) : /*#__PURE__*/createElement(CloseWrapper, { | ||
onClick: this.clearInput | ||
}, /*#__PURE__*/createElement(CloseIcon, { | ||
color: "primary.500", | ||
size: this.props.size | ||
})))); | ||
_defineProperty(this, "listComponent", () => { | ||
if (this.state.userInput && !this.state.isLoading && this.state.filteredDataSource.length > 0) { | ||
return /*#__PURE__*/createElement(AutoCompleteMenuList, { | ||
onSelectItem: this.onSelectItem, | ||
activeSelection: this.state.activeSelection, | ||
filteredDataSource: this.state.filteredDataSource, | ||
innerRef: this.menuContainerRef, | ||
menuWidth: this.state.menuWidth, | ||
debounceWindowResize: this.calcInputWidth, | ||
onMouseEnter: index => this.highlightItemFromMouse(index) | ||
}); | ||
} | ||
return; | ||
}); | ||
_defineProperty(this, "onSelectItem", item => { | ||
var { | ||
onSelectItem = () => {} | ||
} = this.props; | ||
var textWithoutHTMLJunk = transformedItem(item).label; | ||
onSelectItem(transformedItem(item)); | ||
this.setState({ | ||
activeSelection: 0, | ||
filteredDataSource: [], | ||
userInput: textWithoutHTMLJunk, | ||
showList: false | ||
}); | ||
}); | ||
_defineProperty(this, "onChange", event => { | ||
event.persist(); | ||
var { | ||
onChange = () => {} | ||
} = this.props; | ||
onChange(event); | ||
var userInput = event.currentTarget.value; | ||
this.setState({ | ||
userInput: userInput, | ||
isLoading: true, | ||
filteredDataSource: [] | ||
}, () => this.getFilteredData(event.target.value)); | ||
}); | ||
_defineProperty(this, "onKeyDown", event => { | ||
var { | ||
activeSelection, | ||
filteredDataSource | ||
} = this.state; | ||
var { | ||
onSelectItem = () => {} | ||
} = this.props; | ||
switch (event.key) { | ||
case KEY_ENTER: | ||
if (!filteredDataSource.length) return; | ||
this.setState({ | ||
activeSelection: 0, | ||
userInput: filteredDataSource[activeSelection].label.replace(/<\/?[^>]+(>|$)/g, ''), | ||
showList: false | ||
}, () => { | ||
onSelectItem(transformedItem(filteredDataSource[activeSelection])); | ||
}); | ||
return; | ||
case KEY_UP: | ||
if (activeSelection === 0) { | ||
return; | ||
} | ||
this.setState({ | ||
activeSelection: activeSelection - 1 | ||
}, this.focusItem); | ||
return; | ||
case KEY_DOWN: | ||
if (activeSelection === filteredDataSource.length - 1) { | ||
return this.setState({ | ||
activeSelection: 0 | ||
}, this.focusItem); | ||
} | ||
this.setState({ | ||
activeSelection: activeSelection + 1 | ||
}, this.focusItem); | ||
return; | ||
case KEY_TAB: | ||
case KEY_ESCAPE: | ||
this.setState({ | ||
activeSelection: 0, | ||
showList: false, | ||
filteredDataSource: [] | ||
}); | ||
return; | ||
default: | ||
return; | ||
} | ||
}); | ||
_defineProperty(this, "clearInput", () => { | ||
var { | ||
onSelectItem = () => {} | ||
} = this.props; | ||
return this.setState({ | ||
activeSelection: 0, | ||
filteredDataSource: [], | ||
userInput: '', | ||
showList: false | ||
}, () => onSelectItem({ | ||
label: '', | ||
value: '' | ||
})); | ||
}); | ||
this.getFilteredData = debounce(this.getFilteredData, DEBOUNCE_WAIT); | ||
this.calcInputWidth = debounce(this.calcInputWidth, WINDOW_RESIZE_WAIT); | ||
} | ||
componentDidMount() { | ||
window.addEventListener('click', this.onHoverOff); | ||
window.addEventListener('resize', this.calcInputWidth); | ||
this.calcInputWidth(); | ||
} | ||
componentWillUnmount() { | ||
window.removeEventListener('resize', this.calcInputWidth); | ||
window.removeEventListener('click', this.onHoverOff); | ||
} | ||
highlightItemFromMouse(index) { | ||
this.setState({ | ||
activeSelection: index | ||
}); | ||
} // Event fired when the input value is changed | ||
focusItem() { | ||
if (this.menuContainerRef.current) { | ||
this.menuContainerRef.current.scrollTop = getOffsetScrollTop(this.menuContainerRef); | ||
} | ||
} | ||
render() { | ||
var _this$props = this.props, | ||
{ | ||
dataSource = [], | ||
placeholder = '', | ||
size = 'md', | ||
onSelectItem = () => {} | ||
} = _this$props, | ||
inputProps = _objectWithoutProperties(_this$props, ["dataSource", "placeholder", "size", "onSelectItem"]); | ||
return /*#__PURE__*/createElement(AutoCompleteWrapper, { | ||
ref: this.inputWrapper | ||
}, /*#__PURE__*/createElement(Input, _extends({}, inputProps, { | ||
size: size, | ||
autoComplete: "off" // The attribute specifies whether or not an input field should have autocomplete enabled | ||
, | ||
placeholder: placeholder, | ||
onChange: this.onChange, | ||
onKeyDown: this.onKeyDown, | ||
value: String(this.state.userInput), | ||
after: this.state.userInput && this.renderAfterIcon() | ||
})), this.state.showList && this.listComponent()); | ||
} | ||
} | ||
function _extends$1() { _extends$1 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$1.apply(this, arguments); } | ||
function _objectWithoutProperties$1(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose$1(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } | ||
function _objectWithoutPropertiesLoose$1(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } | ||
function _defineProperty$1(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
class AutoCompleteStateless extends Component { | ||
constructor(_props) { | ||
super(_props); | ||
_defineProperty$1(this, "inputWrapper", /*#__PURE__*/createRef()); | ||
_defineProperty$1(this, "menuContainerRef", /*#__PURE__*/createRef()); | ||
_defineProperty$1(this, "state", { | ||
isOpen: false, | ||
highlightedIndex: null, | ||
menuWidth: 0 | ||
}); | ||
_defineProperty$1(this, "calcInputWidth", () => { | ||
if (this.inputWrapper.current && this.inputWrapper.current.firstElementChild) { | ||
this.setState({ | ||
menuWidth: this.inputWrapper.current.firstElementChild.offsetWidth | ||
}); | ||
} | ||
}); | ||
_defineProperty$1(this, "renderComposeMenu", children => { | ||
return /*#__PURE__*/createElement(AutoCompleteList, { | ||
customWidth: this.state.menuWidth, | ||
ref: this.menuContainerRef | ||
}, children); | ||
}); | ||
_defineProperty$1(this, "renderItem", (item, isHighlighted, index) => /*#__PURE__*/createElement(AutoCompleteListItem, { | ||
isActive: index === this.state.highlightedIndex, | ||
className: "".concat(isHighlighted ? 'selectedItem' : ''), | ||
key: index, | ||
value: item.value | ||
}, /*#__PURE__*/createElement("div", { | ||
dangerouslySetInnerHTML: { | ||
__html: item.label | ||
} | ||
}))); | ||
_defineProperty$1(this, "renderAfterIcon", () => /*#__PURE__*/createElement(AfterIconWrapper, null, this.props.isLoading ? /*#__PURE__*/createElement(SpinnerWrapper, { | ||
size: this.props.size, | ||
variant: "dark" | ||
}) : /*#__PURE__*/createElement(CloseWrapper, { | ||
onClick: this.clearInput | ||
}, /*#__PURE__*/createElement(CloseIcon, { | ||
color: "primary.500", | ||
size: this.props.size | ||
})))); | ||
_defineProperty$1(this, "handleChange", event => { | ||
var { | ||
onChange = () => {} | ||
} = this.props; | ||
onChange(event); | ||
}); | ||
_defineProperty$1(this, "handleInputBlur", event => { | ||
var selectCallback; | ||
var { | ||
highlightedIndex | ||
} = this.state; | ||
if (highlightedIndex !== null) { | ||
var items = this.getFilteredItems(this.props); | ||
var item = items[highlightedIndex]; | ||
selectCallback = () => this.props.onSelectItem(transformedItem(item)); | ||
} | ||
this.setState({ | ||
isOpen: false, | ||
highlightedIndex: null | ||
}, selectCallback); | ||
var { | ||
onBlur | ||
} = this.props; | ||
if (onBlur) { | ||
onBlur(event); | ||
} | ||
}); | ||
_defineProperty$1(this, "selectItemFromMouse", item => { | ||
this.setState({ | ||
isOpen: false, | ||
highlightedIndex: null | ||
}, () => { | ||
this.props.onSelectItem(transformedItem(item)); | ||
}); | ||
}); | ||
_defineProperty$1(this, "clearInput", () => { | ||
var { | ||
onReset = () => {} | ||
} = this.props; | ||
this.setState({ | ||
isOpen: false, | ||
highlightedIndex: null | ||
}); | ||
onReset(); | ||
}); | ||
_defineProperty$1(this, "getFilteredItems", props => { | ||
var items = props.dataSource; // some extra processing can be done here like make the input bold in the selections. | ||
items = items.map(d => makeInputKeyBold(d, String(props.value).toLowerCase())); | ||
return items; | ||
}); | ||
_defineProperty$1(this, "isOpen", () => { | ||
return 'open' in this.props ? this.props.open : this.state.isOpen; | ||
}); | ||
this.calcInputWidth = debounce(this.calcInputWidth, AutoCompleteStateless.WINDOW_RESIZE_WAIT); | ||
} | ||
componentDidMount() { | ||
window.addEventListener('resize', this.calcInputWidth); | ||
this.calcInputWidth(); | ||
} | ||
componentDidUpdate(_prevProps, prevState) { | ||
var { | ||
onMenuVisibilityChange = () => {} | ||
} = this.props; | ||
if (prevState.isOpen !== this.state.isOpen) { | ||
onMenuVisibilityChange(this.state.isOpen); | ||
} | ||
} | ||
componentWillUnmount() { | ||
window.removeEventListener('resize', this.calcInputWidth); | ||
} | ||
renderMenu() { | ||
var children = this.getFilteredItems(this.props).map((item, index) => { | ||
var element = this.renderItem(item, this.state.highlightedIndex === index, index); | ||
return /*#__PURE__*/cloneElement(element, { | ||
onClick: () => this.selectItemFromMouse(item), | ||
onMouseEnter: () => this.highlightItemFromMouse(index) | ||
}); | ||
}); | ||
return this.renderComposeMenu(children); | ||
} | ||
highlightItemFromMouse(index) { | ||
this.setState({ | ||
highlightedIndex: index | ||
}); | ||
} | ||
handleKeyDown(event) { | ||
// this is internal keyDown event to do interacttion upon Key_UP/Down, Enter, Tab | ||
var items = this.getFilteredItems(this.props); | ||
var { | ||
highlightedIndex | ||
} = this.state; | ||
var index = 0; | ||
switch (event.key) { | ||
case KEY_DOWN: | ||
if (!items.length) return; | ||
index = highlightedIndex === null ? -1 : highlightedIndex; | ||
var p = (index + 1) % items.length; | ||
index = p; | ||
if (index > -1 && index !== highlightedIndex) { | ||
this.setState({ | ||
highlightedIndex: index, | ||
isOpen: true | ||
}, this.focusItem); | ||
} | ||
return; | ||
case KEY_UP: | ||
if (!items.length) return; | ||
index = highlightedIndex === null ? items.length : highlightedIndex; | ||
index = (index - 1 + items.length) % items.length; | ||
if (index !== items.length) { | ||
this.setState({ | ||
highlightedIndex: index, | ||
isOpen: true | ||
}, this.focusItem); | ||
} | ||
return; | ||
case KEY_ENTER: | ||
// menu is closed so there is no selection to accept -> do nothing | ||
if (!items.length || !this.isOpen()) { | ||
return; | ||
} else if (this.state.highlightedIndex == null) { | ||
// input has focus but no menu item is selected + enter is hit -> close the menu, highlight whatever's in input | ||
this.setState({ | ||
isOpen: false | ||
}); | ||
} else { | ||
// text entered + menu item has been highlighted + enter is hit -> update value to that of selected menu item, close the menu | ||
event.preventDefault(); | ||
var item = this.getFilteredItems(this.props)[this.state.highlightedIndex]; | ||
this.setState({ | ||
isOpen: false, | ||
highlightedIndex: null | ||
}, () => { | ||
this.props.onSelectItem(transformedItem(item)); | ||
}); | ||
} | ||
return; | ||
case KEY_TAB: | ||
case KEY_ESCAPE: | ||
return this.setState({ | ||
highlightedIndex: null, | ||
isOpen: false | ||
}); | ||
default: | ||
if (!this.isOpen()) { | ||
this.setState({ | ||
isOpen: true | ||
}); | ||
} | ||
} // if there is an external keydown event | ||
var { | ||
onKeyDown | ||
} = this.props; | ||
if (onKeyDown) { | ||
onKeyDown(event); | ||
} | ||
} | ||
focusItem() { | ||
if (this.menuContainerRef.current) this.menuContainerRef.current.scrollTop = getOffsetScrollTop(this.menuContainerRef); | ||
} | ||
render() { | ||
var open = this.isOpen(); | ||
var _this$props = this.props, | ||
{ | ||
dataSource, | ||
value = '', | ||
onChange = () => {}, | ||
placeholder = '', | ||
size = 'md', | ||
onSelectItem = () => {}, | ||
onMenuVisibilityChange | ||
} = _this$props, | ||
inputProps = _objectWithoutProperties$1(_this$props, ["dataSource", "value", "onChange", "placeholder", "size", "onSelectItem", "onMenuVisibilityChange"]); | ||
return /*#__PURE__*/createElement(AutoCompleteWrapper, { | ||
ref: this.inputWrapper | ||
}, /*#__PURE__*/createElement(Input, _extends$1({}, inputProps, { | ||
size: size, | ||
placeholder: placeholder, | ||
autoComplete: "off" // The attribute specifies whether or not an input field should have autocomplete enabled | ||
, | ||
onChange: this.handleChange, | ||
onKeyDown: event => this.handleKeyDown(event), | ||
value: String(this.props.value), | ||
after: (this.props.isLoading || this.props.value) && this.renderAfterIcon(), | ||
onBlur: this.handleInputBlur | ||
})), open && dataSource.length > 0 && this.renderMenu()); | ||
} | ||
} | ||
_defineProperty$1(AutoCompleteStateless, "WINDOW_RESIZE_WAIT", 100); | ||
export { AutoComplete, AutoCompleteStateless }; |
{ | ||
"name": "@lendi-ui/auto-complete", | ||
"version": "5.2.0", | ||
"version": "5.2.1", | ||
"license": "ISC", | ||
@@ -19,11 +19,11 @@ "source": "src/index.tsx", | ||
"dependencies": { | ||
"@lendi-ui/breakpoint": "^5.2.0", | ||
"@lendi-ui/color": "^5.1.0", | ||
"@lendi-ui/depth": "^4.1.0", | ||
"@lendi-ui/icon": "^9.7.0", | ||
"@lendi-ui/spacing": "^7.1.0", | ||
"@lendi-ui/spinner": "^4.1.0", | ||
"@lendi-ui/text-input": "^4.2.0", | ||
"@lendi-ui/typography": "^5.1.0", | ||
"@lendi-ui/utils": "^5.1.0", | ||
"@lendi-ui/breakpoint": "^5.2.1", | ||
"@lendi-ui/color": "^5.1.1", | ||
"@lendi-ui/depth": "^4.1.1", | ||
"@lendi-ui/icon": "^9.7.1", | ||
"@lendi-ui/spacing": "^7.1.1", | ||
"@lendi-ui/spinner": "^4.1.1", | ||
"@lendi-ui/text-input": "^4.2.1", | ||
"@lendi-ui/typography": "^5.1.1", | ||
"@lendi-ui/utils": "^5.1.1", | ||
"@types/lodash": "^4.14.144", | ||
@@ -33,3 +33,3 @@ "lodash": "^4.17.15" | ||
"devDependencies": { | ||
"@lendi-ui/field": "^3.1.0", | ||
"@lendi-ui/field": "^3.1.1", | ||
"@lendi-ui/theme": "*", | ||
@@ -36,0 +36,0 @@ "@types/react": "^16.4.8", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Unpublished package
Supply chain riskPackage version was not found on the registry. It may exist on a different registry and need to be configured to pull from that registry.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Unpublished package
Supply chain riskPackage version was not found on the registry. It may exist on a different registry and need to be configured to pull from that registry.
Found 1 instance in 1 package
101467
16
2149
2
Updated@lendi-ui/breakpoint@^5.2.1
Updated@lendi-ui/color@^5.1.1
Updated@lendi-ui/depth@^4.1.1
Updated@lendi-ui/icon@^9.7.1
Updated@lendi-ui/spacing@^7.1.1
Updated@lendi-ui/spinner@^4.1.1
Updated@lendi-ui/text-input@^4.2.1
Updated@lendi-ui/typography@^5.1.1
Updated@lendi-ui/utils@^5.1.1