Socket
Socket
Sign inDemoInstall

react-datalist-input

Package Overview
Dependencies
8
Maintainers
1
Versions
100
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.3.13 to 2.0.0

796

dist/DataListInput.js

@@ -23,3 +23,4 @@

var React = _interopDefault(require('react'));
var React = require('react');
var React__default = _interopDefault(React);
var PropTypes = _interopDefault(require('prop-types'));

@@ -43,534 +44,427 @@

function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _iterableToArrayLimit(arr, i) {
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
var _arr = [];
var _n = true;
var _d = false;
var _e = 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;
}
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
return obj;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
return _arr;
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
return _setPrototypeOf(o, p);
}
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
return true;
} catch (e) {
return false;
}
return arr2;
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
}
var useStateRef = function useStateRef(initalState) {
var _useState = React.useState(initalState),
_useState2 = _slicedToArray(_useState, 2),
state = _useState2[0],
setState = _useState2[1];
return _assertThisInitialized(self);
}
var ref = React.useRef(initalState);
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
var setStateRef = function setStateRef(newState) {
setState(newState);
ref.current = newState;
};
return function _createSuperInternal() {
var Super = _getPrototypeOf(Derived),
result;
return [state, setStateRef, ref];
};
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
___$insertStyle(".datalist-input {\n /*the container must be positioned relative:*/\n position: relative;\n display: inline-block;\n width: 100%;\n}\n\n.datalist-input .autocomplete-input {\n width: 100%;\n}\n\n.datalist-input .datalist-items {\n position: absolute;\n z-index: 99;\n /*position the autocomplete items to be the same width as the container:*/\n top: 100%;\n left: 0;\n right: 0;\n}\n\n.datalist-input .default-datalist-items {\n border: 1px solid #d4d4d4;\n border-bottom: none;\n border-top: none;\n}\n\n.datalist-input .default-datalist-items div:not(.datalist-active-item) {\n padding: 10px;\n cursor: pointer;\n background-color: #fff;\n border-bottom: 1px solid #d4d4d4;\n}\n\n.datalist-input .default-datalist-items div:not(.datalist-active-item):hover {\n /*when hovering an item:*/\n background-color: #e9e9e9;\n}\n\n.datalist-input .datalist-active-item {\n /*when navigating through the items using the arrow keys:*/\n cursor: pointer;\n}\n\n.datalist-input .datalist-active-item-default {\n background-color: DodgerBlue;\n color: #ffffff;\n border-bottom: 1px solid #d4d4d4;\n padding: 10px;\n}");
var DataListInput = /*#__PURE__*/function (_React$Component) {
_inherits(DataListInput, _React$Component);
/**
* default function for matching the current input value (needle)
* and the values of the items array
* @param currentInput
* @param item
* @returns {boolean}
*/
var _super = _createSuper(DataListInput);
var labelMatch = function labelMatch(currentInput, item) {
return item.label.substr(0, currentInput.length).toLowerCase() === currentInput.toLowerCase();
};
/**
* function for getting the index of the currentValue inside a value of the values array
* @param currentInput
* @param item
* @returns {number}
*/
function DataListInput(props) {
var _this;
_classCallCheck(this, DataListInput);
var indexOfMatch = function indexOfMatch(currentInput, item) {
return item.label.toLowerCase().indexOf(currentInput.toLowerCase());
};
/**
* index of item in items
* @param {*} item
* @param {*} items
*/
_this = _super.call(this, props);
_defineProperty(_assertThisInitialized(_this), "componentDidMount", function () {
if (typeof window !== 'undefined') {
window.addEventListener('click', _this.onClickCloseMenu, false);
}
});
var indexOfItem = function indexOfItem(item, items) {
return items.indexOf(items.find(function (i) {
return i.key === item.key;
}));
};
_defineProperty(_assertThisInitialized(_this), "componentDidUpdate", function () {
var _this$state = _this.state,
currentInput = _this$state.currentInput,
visible = _this$state.visible,
isMatchingDebounced = _this$state.isMatchingDebounced;
var initialValue = _this.props.initialValue; // if we have an initialValue, we want to reset it everytime we update and are empty
// also setting a new initialValue will trigger this
var DataListInput = function DataListInput(_ref) {
var activeItemClassName = _ref.activeItemClassName,
clearInputOnSelect = _ref.clearInputOnSelect,
debounceLoader = _ref.debounceLoader,
debounceTime = _ref.debounceTime,
dropdownClassName = _ref.dropdownClassName,
dropDownLength = _ref.dropDownLength,
initialValue = _ref.initialValue,
inputClassName = _ref.inputClassName,
itemClassName = _ref.itemClassName,
match = _ref.match,
onDropdownClose = _ref.onDropdownClose,
onDropdownOpen = _ref.onDropdownOpen,
onInput = _ref.onInput,
onSelect = _ref.onSelect,
placeholder = _ref.placeholder,
requiredInputLength = _ref.requiredInputLength,
suppressReselect = _ref.suppressReselect,
items = _ref.items;
if (!currentInput && initialValue && !visible && !isMatchingDebounced) {
_this.setState({
currentInput: initialValue
});
}
});
/* last valid item that was selected from the drop down menu */
var _useState = React.useState(),
_useState2 = _slicedToArray(_useState, 2),
lastValidItem = _useState2[0],
setLastValidItem = _useState2[1];
/* current input text */
_defineProperty(_assertThisInitialized(_this), "componentWillUnmount", function () {
if (typeof window !== 'undefined') {
window.removeEventListener('click', _this.onClickCloseMenu);
}
});
_defineProperty(_assertThisInitialized(_this), "onClickCloseMenu", function (event) {
var menu = document.getElementsByClassName('datalist-items');
if (!menu || !menu.length) return; // if rerender, items inside might change, allow one click without further checking
var _useStateRef = useStateRef(initialValue),
_useStateRef2 = _slicedToArray(_useStateRef, 3),
currentInput = _useStateRef2[0],
setCurrentInput = _useStateRef2[1],
currentInputRef = _useStateRef2[2];
/* current set of matching items */
var interactionHappened = _this.state.interactionHappened;
if (interactionHappened) {
_this.setState({
interactionHappened: false
});
var _useState3 = React.useState([]),
_useState4 = _slicedToArray(_useState3, 2),
matchingItems = _useState4[0],
setMatchingItems = _useState4[1];
/* visibility property of the drop down menu */
return;
} // do not do anything if input is clicked, as we have a dedicated func for that
var _useStateRef3 = useStateRef(false),
_useStateRef4 = _slicedToArray(_useStateRef3, 3),
visible = _useStateRef4[0],
setVisible = _useStateRef4[1],
visibleRef = _useStateRef4[2];
/* index of the currently focused item in the drop down menu */
var input = document.getElementsByClassName('autocomplete-input');
if (!input) return;
for (var i = 0; i < input.length; i += 1) {
var targetIsInput = event.target === input[i];
var targetInInput = input[i].contains(event.target);
if (targetIsInput || targetInInput) return;
} // do not close menu if user clicked inside
var _useState5 = React.useState(0),
_useState6 = _slicedToArray(_useState5, 2),
focusIndex = _useState6[0],
setFocusIndex = _useState6[1];
/* cleaner click events, click interaction within dropdown menu */
for (var _i = 0; _i < menu.length; _i += 1) {
var targetInMenu = menu[_i].contains(event.target);
var interactionHappenedRef = React.useRef(false);
/* show loader if still matching in debounced mode */
var targetIsMenu = event.target === menu[_i];
if (targetInMenu || targetIsMenu) return;
}
var _useState7 = React.useState(false),
_useState8 = _slicedToArray(_useState7, 2),
isMatchingDebounced = _useState8[0],
setIsMatchingDebounced = _useState8[1];
/* to manage debouncing of matching, typing input into the input field */
var visible = _this.state.visible;
var onDropdownClose = _this.props.onDropdownClose;
if (visible) {
_this.setState({
visible: false,
focusIndex: -1
}, onDropdownClose);
}
});
var inputHappenedTimeout = React.useRef();
var menu = React.useRef();
var inputField = React.useRef();
React.useEffect(function () {
var onClickCloseMenu = function onClickCloseMenu(event) {
if (!menu.current) return; // if rerender, items inside might change, allow one click without further checking
_defineProperty(_assertThisInitialized(_this), "match", function (currentInput, item) {
return item.label.substr(0, currentInput.length).toUpperCase() === currentInput.toUpperCase();
});
if (interactionHappenedRef.current) {
interactionHappenedRef.current = false;
return;
} // do not do anything if input is clicked, as we have a dedicated func for that
_defineProperty(_assertThisInitialized(_this), "matching", function (currentInput, items, match) {
return items.filter(function (item) {
if (_typeof(match) === (typeof Function === "undefined" ? "undefined" : _typeof(Function))) {
return match(currentInput, item);
}
return _this.match(currentInput, item);
});
});
if (!inputField.current) return;
var targetIsInput = event.target === inputField.current;
var targetInInput = inputField.current.contains(event.target);
if (targetIsInput || targetInInput) return; // do not close menu if user clicked inside
_defineProperty(_assertThisInitialized(_this), "indexOfMatch", function (currentInput, item) {
return item.label.toUpperCase().indexOf(currentInput.toUpperCase());
});
var targetInMenu = menu.current.contains(event.target);
var targetIsMenu = event.target === menu.current;
if (targetInMenu || targetIsMenu) return;
_defineProperty(_assertThisInitialized(_this), "indexOfItem", function (item, items) {
return items.indexOf(items.find(function (i) {
return i.key === item.key;
}));
});
if (visibleRef.current) {
setVisible(false);
setFocusIndex(-1);
onDropdownClose();
}
};
_defineProperty(_assertThisInitialized(_this), "debouncedMatchingUpdateStep", function (currentInput) {
var lastValidItem = _this.state.lastValidItem;
var _this$props = _this.props,
items = _this$props.items,
match = _this$props.match,
debounceTime = _this$props.debounceTime,
dropDownLength = _this$props.dropDownLength,
requiredInputLength = _this$props.requiredInputLength,
clearInputOnSelect = _this$props.clearInputOnSelect,
onDropdownOpen = _this$props.onDropdownOpen,
onDropdownClose = _this$props.onDropdownClose; // cleanup waiting update step && be ssr safe
window.addEventListener('click', onClickCloseMenu, false);
return function () {
window.removeEventListener('click', onClickCloseMenu);
};
}, [onDropdownClose, setVisible, visibleRef]);
React.useEffect(function () {
// if we have an initialValue, we want to reset it everytime we update and are empty
// also setting a new initialValue will trigger this
if (!currentInput && initialValue && !visible && !isMatchingDebounced) {
setCurrentInput(initialValue);
}
}, [currentInput, visible, isMatchingDebounced, initialValue, setCurrentInput]);
/**
* runs the matching process of the current input
* and handles debouncing the different callback calls to reduce lag time
* for bigger datasets or heavier matching algorithms
* @param nextInput
*/
if (_this.inputHappenedTimeout && typeof window !== 'undefined') {
window.clearTimeout(_this.inputHappenedTimeout);
_this.inputHappenedTimeout = null;
} // set currentInput into input field and show loading if debounced mode is on
var debouncedMatchingUpdateStep = React.useCallback(function (nextInput) {
// cleanup waiting update step
if (inputHappenedTimeout.current) {
clearTimeout(inputHappenedTimeout.current);
inputHappenedTimeout.current = null;
} // set nextInput into input field and show loading if debounced mode is on
var reachedRequiredLength = currentInput.length >= requiredInputLength;
var showMatchingStillLoading = debounceTime >= 0 && reachedRequiredLength;
var reachedRequiredLength = nextInput.length >= requiredInputLength;
var showMatchingStillLoading = debounceTime >= 0 && reachedRequiredLength;
setCurrentInput(nextInput);
setIsMatchingDebounced(showMatchingStillLoading); // no matching if we do not reach required input length
_this.setState({
currentInput: currentInput,
isMatchingDebounced: showMatchingStillLoading
}); // no matching if we do not reach required input length
if (!reachedRequiredLength) return;
var updateMatchingItems = function updateMatchingItems() {
// matching process to find matching entries in items array
var updatedMatchingItems = items.filter(function (item) {
if (_typeof(match) === (typeof Function === "undefined" ? "undefined" : _typeof(Function))) return match(nextInput, item);
return labelMatch(nextInput, item);
});
var displayableItems = updatedMatchingItems.slice(0, dropDownLength);
var showDragIndex = lastValidItem && !clearInputOnSelect;
var index = showDragIndex ? indexOfItem(lastValidItem, displayableItems) : 0;
if (!reachedRequiredLength) return;
var updateMatchingItems = function updateMatchingItems() {
var matchingItems = _this.matching(currentInput, items, match);
var displayableItems = matchingItems.slice(0, dropDownLength);
var showDragIndex = lastValidItem && !clearInputOnSelect;
var index = showDragIndex ? _this.indexOfItem(lastValidItem, displayableItems) : 0;
if (matchingItems.length > 0) {
_this.setState({
matchingItems: displayableItems,
focusIndex: index > 0 ? index : 0,
visible: true,
isMatchingDebounced: false
}, onDropdownOpen);
} else {
_this.setState({
matchingItems: displayableItems,
visible: false,
focusIndex: -1,
isMatchingDebounced: false
}, onDropdownClose);
if (displayableItems.length) {
if (!visibleRef.current) {
onDropdownOpen();
}
};
if (debounceTime <= 0) {
updateMatchingItems();
setMatchingItems(displayableItems);
setFocusIndex(index > 0 ? index : 0);
setIsMatchingDebounced(false);
setVisible(true);
} else {
if (typeof window !== 'undefined') {
_this.inputHappenedTimeout = window.setTimeout(updateMatchingItems, debounceTime);
if (visibleRef.current) {
setVisible(false);
onDropdownClose();
}
setMatchingItems(displayableItems);
setFocusIndex(-1);
setIsMatchingDebounced(false);
}
});
};
_defineProperty(_assertThisInitialized(_this), "onHandleInput", function (event) {
var onInput = _this.props.onInput;
var currentInput = event.target.value;
if (debounceTime <= 0) {
updateMatchingItems();
} else {
inputHappenedTimeout.current = setTimeout(updateMatchingItems, debounceTime);
}
}, [requiredInputLength, debounceTime, setCurrentInput, items, dropDownLength, lastValidItem, clearInputOnSelect, match, setVisible, onDropdownOpen, visibleRef, onDropdownClose]);
/**
* gets called when someone starts to write in the input field
* @param value
*/
_this.debouncedMatchingUpdateStep(currentInput);
var onHandleInput = React.useCallback(function (event) {
var value = event.target.value;
debouncedMatchingUpdateStep(value);
onInput(value);
}, [debouncedMatchingUpdateStep, onInput]);
var onClickInput = React.useCallback(function () {
var value = currentInputRef.current; // if user clicks on input field with initialValue,
// the user most likely wants to clear the input field
onInput(currentInput);
});
if (initialValue && value === initialValue) {
value = '';
}
_defineProperty(_assertThisInitialized(_this), "onClickInput", function () {
var visible = _this.state.visible;
var currentInput = _this.state.currentInput;
var _this$props2 = _this.props,
requiredInputLength = _this$props2.requiredInputLength,
initialValue = _this$props2.initialValue; // if user clicks on input field with initialValue,
// the user most likely wants to clear the input field
var reachedRequiredLength = value.length >= requiredInputLength;
if (initialValue && currentInput === initialValue) {
_this.setState({
currentInput: ''
});
if (reachedRequiredLength && !visibleRef.current) {
debouncedMatchingUpdateStep(value);
}
}, [currentInputRef, initialValue, requiredInputLength, visibleRef, debouncedMatchingUpdateStep]);
/**
* handleSelect is called onClickItem and onEnter upon an option of the drop down menu
* does nothing if the key has not changed since the last onSelect event
* @param selectedItem
*/
currentInput = '';
}
var onHandleSelect = React.useCallback(function (selectedItem) {
// block select call until last matching went through
if (isMatchingDebounced) return;
setCurrentInput(clearInputOnSelect ? '' : selectedItem.label);
setVisible(false);
setFocusIndex(-1);
interactionHappenedRef.current = true;
onDropdownClose();
var reachedRequiredLength = currentInput.length >= requiredInputLength;
if (suppressReselect && lastValidItem && selectedItem.key === lastValidItem.key) {
// do not trigger the callback function
// but still change state to fit new selection
return;
} // change state to fit new selection
if (reachedRequiredLength && !visible) {
_this.debouncedMatchingUpdateStep(currentInput);
}
});
_defineProperty(_assertThisInitialized(_this), "onHandleKeydown", function (event) {
var _this$state2 = _this.state,
visible = _this$state2.visible,
focusIndex = _this$state2.focusIndex,
matchingItems = _this$state2.matchingItems; // only do something if drop-down div is visible
setLastValidItem(selectedItem); // callback function onSelect
if (!visible) return;
var currentFocusIndex = focusIndex;
onSelect(selectedItem);
}, [isMatchingDebounced, setCurrentInput, clearInputOnSelect, setVisible, onDropdownClose, suppressReselect, lastValidItem, onSelect]);
/**
* handle key events
* @param event
*/
if (event.keyCode === 40 || event.keyCode === 9) {
// If the arrow DOWN key or tab is pressed increase the currentFocus variable:
currentFocusIndex += 1;
if (currentFocusIndex >= matchingItems.length) currentFocusIndex = 0;
var onHandleKeydown = React.useCallback(function (event) {
// only do something if drop-down div is visible
if (!visibleRef.current) return;
var currentFocusIndex = focusIndex;
_this.setState({
focusIndex: currentFocusIndex
}); // prevent tab to jump to the next input field if drop down is still open
if (event.keyCode === 40 || event.keyCode === 9) {
// If the arrow DOWN key or tab is pressed increase the currentFocus variable:
currentFocusIndex += 1;
if (currentFocusIndex >= matchingItems.length) currentFocusIndex = 0;
setFocusIndex(currentFocusIndex); // prevent tab to jump to the next input field if drop down is still open
event.preventDefault();
} else if (event.keyCode === 38) {
// If the arrow UP key is pressed, decrease the currentFocus variable:
currentFocusIndex -= 1;
if (currentFocusIndex <= -1) currentFocusIndex = matchingItems.length - 1;
_this.setState({
focusIndex: currentFocusIndex
});
} else if (event.keyCode === 13) {
// Enter pressed, similar to onClickItem
if (focusIndex > -1) {
// Simulate a click on the "active" item:
var selectedItem = matchingItems[currentFocusIndex];
_this.onSelect(selectedItem);
}
event.preventDefault();
} else if (event.keyCode === 38) {
// If the arrow UP key is pressed, decrease the currentFocus variable:
currentFocusIndex -= 1;
if (currentFocusIndex <= -1) currentFocusIndex = matchingItems.length - 1;
setFocusIndex(currentFocusIndex);
} else if (event.keyCode === 13) {
// Enter pressed, similar to onClickItem
if (focusIndex > -1) {
// Simulate a click on the "active" item:
var selectedItem = matchingItems[currentFocusIndex];
onHandleSelect(selectedItem);
}
});
_defineProperty(_assertThisInitialized(_this), "onClickItem", function (event) {
var matchingItems = _this.state.matchingItems; // update the input value and close the dropdown again
var elements = event.currentTarget.children;
var selectedKey;
for (var i = 0; i < elements.length; i += 1) {
if (elements[i].tagName === 'INPUT') {
selectedKey = elements[i].value;
break;
}
}, [focusIndex, matchingItems, onHandleSelect, visibleRef]);
var renderItemLabel = React.useCallback(function (item) {
var index = indexOfMatch(currentInput, item);
var inputLength = currentInput.length;
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, index >= 0 && inputLength ?
/*#__PURE__*/
// renders label with matching search string marked
React__default.createElement(React__default.Fragment, null, item.label.substr(0, index), /*#__PURE__*/React__default.createElement("strong", null, item.label.substr(index, inputLength)), item.label.substr(index + inputLength, item.label.length)) : item.label);
}, [currentInput]);
var renderItems = React.useCallback(function () {
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, matchingItems.map(function (item, i) {
var isActive = focusIndex === i;
var itemActiveClasses = isActive ? "datalist-active-item ".concat(activeItemClassName || 'datalist-active-item-default') : '';
var itemClasses = "".concat(itemClassName, " ").concat(itemActiveClasses);
return /*#__PURE__*/React__default.createElement("div", {
onClick: function onClick() {
return onHandleSelect(item);
},
className: itemClasses,
key: item.key,
tabIndex: 0,
role: "button",
"aria-label": item.label,
onKeyUp: function onKeyUp(event) {
return event.preventDefault();
}
} // key can either be number or string
// eslint-disable-next-line eqeqeq
}, renderItemLabel(item));
}));
}, [matchingItems, focusIndex, activeItemClassName, itemClassName, onHandleSelect, renderItemLabel]);
var dropDown = React.useMemo(function () {
var reachedRequiredLength = currentInputRef.current.length >= requiredInputLength;
var selectedItem = matchingItems.find(function (item) {
return item.key == selectedKey;
});
_this.onSelect(selectedItem);
});
_defineProperty(_assertThisInitialized(_this), "onSelect", function (selectedItem) {
var _this$props3 = _this.props,
suppressReselect = _this$props3.suppressReselect,
clearInputOnSelect = _this$props3.clearInputOnSelect,
onDropdownClose = _this$props3.onDropdownClose;
var _this$state3 = _this.state,
lastValidItem = _this$state3.lastValidItem,
isMatchingDebounced = _this$state3.isMatchingDebounced; // block select call until last matching went through
if (isMatchingDebounced) return;
if (suppressReselect && lastValidItem && selectedItem.key === lastValidItem.key) {
// do not trigger the callback function
// but still change state to fit new selection
_this.setState({
currentInput: clearInputOnSelect ? '' : selectedItem.label,
visible: false,
focusIndex: -1,
interactionHappened: true
}, onDropdownClose);
return;
} // change state to fit new selection
_this.setState({
currentInput: clearInputOnSelect ? '' : selectedItem.label,
lastValidItem: selectedItem,
visible: false,
focusIndex: -1,
interactionHappened: true
}, onDropdownClose); // callback function onSelect
var onSelect = _this.props.onSelect;
onSelect(selectedItem);
});
_defineProperty(_assertThisInitialized(_this), "renderMatchingLabel", function (currentInput, item, indexOfMatch) {
return /*#__PURE__*/React.createElement(React.Fragment, null, item.label.substr(0, indexOfMatch), /*#__PURE__*/React.createElement("strong", null, item.label.substr(indexOfMatch, currentInput.length)), item.label.substr(indexOfMatch + currentInput.length, item.label.length));
});
_defineProperty(_assertThisInitialized(_this), "renderItemLabel", function (currentInput, item, indexOfMatch) {
return /*#__PURE__*/React.createElement(React.Fragment, null, indexOfMatch >= 0 && currentInput.length ? _this.renderMatchingLabel(currentInput, item, indexOfMatch) : item.label);
});
_defineProperty(_assertThisInitialized(_this), "renderItems", function (currentInput, items, focusIndex, activeItemClassName, itemClassName, dropdownClassName) {
return /*#__PURE__*/React.createElement("div", {
className: "datalist-items ".concat(dropdownClassName || 'default-datalist-items')
}, items.map(function (item, i) {
var isActive = focusIndex === i;
var itemActiveClasses = isActive ? "datalist-active-item ".concat(activeItemClassName || 'datalist-active-item-default') : '';
var itemClasses = "".concat(itemClassName, " ").concat(itemActiveClasses);
return /*#__PURE__*/React.createElement("div", {
onClick: _this.onClickItem,
className: itemClasses,
key: item.key,
tabIndex: 0,
role: "button",
onKeyUp: function onKeyUp(event) {
return event.preventDefault();
}
}, _this.renderItemLabel(currentInput, item, _this.indexOfMatch(currentInput, item)), /*#__PURE__*/React.createElement("input", {
type: "hidden",
value: item.key,
readOnly: true
}));
}));
});
_defineProperty(_assertThisInitialized(_this), "renderLoader", function (debounceLoader, dropdownClassName, itemClassName) {
return /*#__PURE__*/React.createElement("div", {
className: "datalist-items ".concat(dropdownClassName || 'default-datalist-items')
}, /*#__PURE__*/React.createElement("div", {
if (reachedRequiredLength && isMatchingDebounced) {
return /*#__PURE__*/React__default.createElement("div", {
ref: menu,
className: "datalist-items ".concat(dropdownClassName || 'default-datalist-items'),
role: "dialog",
"aria-label": "Dropdown menu"
}, /*#__PURE__*/React__default.createElement("div", {
className: itemClassName
}, debounceLoader || 'loading...'));
});
}
_defineProperty(_assertThisInitialized(_this), "renderInputField", function (placeholder, currentInput, inputClassName) {
return /*#__PURE__*/React.createElement("input", {
onChange: _this.onHandleInput,
onClick: _this.onClickInput,
onKeyDown: _this.onHandleKeydown,
type: "text",
className: "autocomplete-input ".concat(inputClassName),
placeholder: placeholder,
value: currentInput
});
});
var _initialValue = _this.props.initialValue;
_this.state = {
/* last valid item that was selected from the drop down menu */
lastValidItem: undefined,
/* current input text */
currentInput: _initialValue,
/* current set of matching items */
matchingItems: [],
/* visibility property of the drop down menu */
visible: false,
/* index of the currently focused item in the drop down menu */
focusIndex: 0,
/* cleaner click events, click interaction within dropdown menu */
interactionHappened: false,
/* show loader if still matching in debounced mode */
isMatchingDebounced: false
};
/* to manage debouncing of matching, typing input into the input field */
_this.inputHappenedTimeout = undefined;
return _this;
}
_createClass(DataListInput, [{
key: "render",
value: function render() {
var _this$state4 = this.state,
currentInput = _this$state4.currentInput,
matchingItems = _this$state4.matchingItems,
focusIndex = _this$state4.focusIndex,
visible = _this$state4.visible,
isMatchingDebounced = _this$state4.isMatchingDebounced;
var _this$props4 = this.props,
placeholder = _this$props4.placeholder,
inputClassName = _this$props4.inputClassName,
activeItemClassName = _this$props4.activeItemClassName,
itemClassName = _this$props4.itemClassName,
requiredInputLength = _this$props4.requiredInputLength,
dropdownClassName = _this$props4.dropdownClassName,
debounceLoader = _this$props4.debounceLoader;
var reachedRequiredLength = currentInput.length >= requiredInputLength;
var renderedResults;
if (reachedRequiredLength && isMatchingDebounced) {
renderedResults = this.renderLoader(debounceLoader, itemClassName, dropdownClassName);
} else if (reachedRequiredLength && visible) {
renderedResults = this.renderItems(currentInput, matchingItems, focusIndex, activeItemClassName, itemClassName, dropdownClassName);
}
return /*#__PURE__*/React.createElement("div", {
className: "datalist-input"
}, this.renderInputField(placeholder, currentInput, inputClassName), renderedResults);
if (reachedRequiredLength && visible) {
return /*#__PURE__*/React__default.createElement("div", {
ref: menu,
className: "datalist-items ".concat(dropdownClassName || 'default-datalist-items'),
role: "dialog",
"aria-label": "Dropdown menu"
}, renderItems());
}
}]);
return DataListInput;
}(React.Component);
return undefined;
}, [currentInputRef, requiredInputLength, isMatchingDebounced, visible, dropdownClassName, itemClassName, debounceLoader, renderItems]);
return /*#__PURE__*/React__default.createElement("div", {
className: "datalist-input"
}, /*#__PURE__*/React__default.createElement("input", {
ref: inputField,
onChange: onHandleInput,
onClick: onClickInput,
onKeyDown: onHandleKeydown,
type: "text",
className: "autocomplete-input ".concat(inputClassName),
placeholder: placeholder,
value: currentInput,
"aria-label": "Search"
}), dropDown);
};

@@ -577,0 +471,0 @@ DataListInput.propTypes = {

{
"name": "react-datalist-input",
"version": "1.3.13",
"version": "2.0.0",
"description": "This package provides a react component as follows: an input field with a drop down menu to pick a possible option based on the current input.",

@@ -37,2 +37,4 @@ "main": "./dist/DataListInput.js",

"@babel/plugin-proposal-class-properties": "^7.10.1",
"@babel/plugin-transform-regenerator": "^7.10.4",
"@babel/plugin-transform-runtime": "^7.11.5",
"@babel/preset-env": "^7.10.2",

@@ -42,7 +44,16 @@ "@babel/preset-react": "^7.10.1",

"@testing-library/react": "^10.2.1",
"eslint": "^7.2.0",
"eslint-config-airbnb": "^18.1.0",
"eslint-plugin-import": "^2.21.2",
"babel-eslint": "^9.0.0",
"eslint": "^6.8.0",
"eslint-config-airbnb": "^18.2.0",
"eslint-config-prettier": "^4.3.0",
"eslint-config-wesbos": "0.0.22",
"eslint-plugin-html": "^6.0.3",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-jest-dom": "^3.0.1",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-react": "^7.20.6",
"eslint-plugin-react-hooks": "^1.7.0",
"jest": "^26.0.1",
"prettier": "^1.19.1",
"prop-types": "^15.7.2",

@@ -49,0 +60,0 @@ "react": "^16.13.1",

## Info
This package provides a single react component. The component contains an input field with a drop down menu to pick a possible option based on the current input as a react component.
This package provides a single React component. The component contains an input field with a drop down menu to pick a possible option based on the current input as a React component.
Have a look at [w3schools.com](https://www.w3schools.com/howto/howto_js_autocomplete.asp) to see how you can do something similar with pure html, css, and js. For more information about react and the ecosystem see this [guide](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md).
Have a look at [w3schools.com](https://www.w3schools.com/howto/howto_js_autocomplete.asp) to see how you can do something similar with pure html, css, and js. For more information about React and the ecosystem see this [guide](https://reactjs.org/docs/getting-started.html).

@@ -15,3 +15,3 @@ ## Demo

### Using Gatsby or Next.js?
### Using Gatsby or Next.js?

@@ -22,2 +22,9 @@ This component is not compatible with server-side rendering since it has css bundled with it.

## Versions
- Version 2.x.x serves a functional component using hooks
- Version 1.x.x serves a class component
The documentation below mainly applies for both versions but will be updated based on version 2.x.x updates in the future.
## Installation

@@ -28,3 +35,3 @@

```bash
npm install react-datalist-input --save
npm i react-datalist-input
```

@@ -35,51 +42,42 @@

```javascript
import DataListInput from 'react-datalist-input';
import React, { useState, useMemo, useCallback } from "react";
import DataListInput from "react-datalist-input";
/**
* OPTIONAL, this packages comes with a simple default label matching function
* but feel free to create your own match algorithm if you want to do so
* @param {String} currentInput (the current user input)
* @param {object} item (one item of the items array)
* @returns {boolean}
*/
matchCurrentInput = (currentInput, item) => {
const yourLogic = item.someAdditionalValue;
return (yourLogic.substr(0, currentInput.length).toUpperCase() === currentInput.toUpperCase());
};
const YourComponent = ({ myValues }) => {
// selectedItem
const [item, setItem] = useState();
/**
* your callback function gets called if the user selects one option out of the drop down menu
* @param selectedItem object (the selected item / option)
* @returns {*}
*/
onSelect = (selectedItem) => {
this.doSomething(selectedItem);
};
/**
* your callback function gets called if the user selects one option out of the drop down menu
* @param selectedItem object (the selected item / option)
*/
const onSelect = useCallback((selectedItem) => {
console.log("selectedItem", selectedItem);
}, []);
render() {
// the array you want to pass to the react-data-list component
// each element at least needs a key and a label
const items = myValues.map((item, i) => {
return {
// what to show to the user
label: item.id + ": " + item.name,
// key to identify the item within the array
key: item.id,
// feel free to add your own app logic to access those properties in the onSelect function
someAdditionalValue: item.someAdditionalValue,
// or just keep everything
...item,
}
});
// the array you want to pass to the react-data-list component
// key and label are required properties
const items = useMemo(
() =>
myValues.map((oneItem) => ({
// required: what to show to the user
label: oneItem.name,
// required: key to identify the item within the array
key: oneItem.id,
// feel free to add your own app logic to access those properties in the onSelect function
someAdditionalValue: oneItem.someAdditionalValue,
// or just keep everything
...oneItem,
})),
[myValues]
);
return(
<div>
<DataListInput
placeholder={"Select an option from the drop down menu..."}
items={items}
onSelect={this.onSelect}
match={this.matchCurrentInput}
/>
</div>
);
return (
<DataListInput
placeholder="Select an option from the drop down menu..."
items={items}
onSelect={onSelect}
/>
);
};
```

@@ -89,22 +87,22 @@

| Prop | Type | Required/Optional | Default Value
|----------|------------- |------| ------|
| [items](#markdown-header-items) | array | required | - |
| [onSelect](#markdown-header-onSelect) | function | required | - |
| [match](#markdown-header-match) | function | optional | internal matching function |
| [onDropdownOpen](#markdown-header-onDropdownOpen) | function | optional | - |
| [onDropdownClose](#markdown-header-onDropdownClose) | function | optional | - |
| [placeholder](#markdown-header-placeholder) | string | optional | '' |
| [itemClassName](#markdown-header-itemClassName) | string | optional | - |
| [activeItemClassName](#markdown-header-activeItemClassName) | string | optional | - |
| [inputClassName](#markdown-header-inputClassName) | string | optional | - |
| [dropdownClassName](#markdown-header-dropdownClassName) | string | optional | - |
| [requiredInputLength](#markdown-header-requiredInputLength) | number | optional | 0 |
| [clearInputOnSelect](#markdown-header-clearInputOnSelect) | boolean | optional | false |
| [suppressReselect](#markdown-header-suppressReselect) | boolean | optional | true |
| [dropDownLength](#markdown-header-dropDownLength) | number | optional | infinite |
| [initialValue](#markdown-header-initialValue) | string | optional | - |
| [debounceTime](#markdown-header-debounceTime) | number | optional | 0 |
| [debounceLoader](#markdown-header-debounceLoader) | string | optional | 'Loading...' |
| [onInput](#markdown-header-onInput) | function | optional | - |
| Prop | Type | Required/Optional | Default Value |
| ----------------------------------------------------------- | -------- | ----------------- | -------------------------- |
| [items](#markdown-header-items) | array | required | - |
| [onSelect](#markdown-header-onSelect) | function | required | - |
| [match](#markdown-header-match) | function | optional | internal matching function |
| [onDropdownOpen](#markdown-header-onDropdownOpen) | function | optional | - |
| [onDropdownClose](#markdown-header-onDropdownClose) | function | optional | - |
| [placeholder](#markdown-header-placeholder) | string | optional | '' |
| [itemClassName](#markdown-header-itemClassName) | string | optional | - |
| [activeItemClassName](#markdown-header-activeItemClassName) | string | optional | - |
| [inputClassName](#markdown-header-inputClassName) | string | optional | - |
| [dropdownClassName](#markdown-header-dropdownClassName) | string | optional | - |
| [requiredInputLength](#markdown-header-requiredInputLength) | number | optional | 0 |
| [clearInputOnSelect](#markdown-header-clearInputOnSelect) | boolean | optional | false |
| [suppressReselect](#markdown-header-suppressReselect) | boolean | optional | true |
| [dropDownLength](#markdown-header-dropDownLength) | number | optional | infinite |
| [initialValue](#markdown-header-initialValue) | string | optional | - |
| [debounceTime](#markdown-header-debounceTime) | number | optional | 0 |
| [debounceLoader](#markdown-header-debounceLoader) | string | optional | 'Loading...' |
| [onInput](#markdown-header-onInput) | function | optional | - |

@@ -116,4 +114,4 @@ ### <a name="markdown-header-items"></a>items

- Every item inside the array needs to have following properties:
- key : an id that identifies the item within the array
- label: the label that will be shown in the drop down menu
- key : an id that identifies the item within the array
- label: the label that will be shown in the drop down menu

@@ -126,3 +124,3 @@ ### <a name="markdown-header-onSelect"></a>onSelect

- Parameter: (selectedKey)
- selectedKey: the Key Property of the item that the user selected
- selectedKey: the Key Property of the item that the user selected

@@ -133,6 +131,8 @@ ### <a name="markdown-header-match"></a>match

- Parameter: (currentInput, item)
- currentInput: String, the current user input typed into the input field
- item: Object, the item of the items array (with key and label properties)
- currentInput: String, the current user input typed into the input field
- item: Object, the item of the items array (with key and label properties)
- Default:
```javascript

@@ -146,3 +146,6 @@ /**

match = (currentInput, item) => {
return item.label.substr(0, currentInput.length).toUpperCase() === currentInput.toUpperCase();
return (
item.label.substr(0, currentInput.length).toUpperCase() ===
currentInput.toUpperCase()
);
};

@@ -154,2 +157,3 @@ ```

- The callback function that will be called after opening the drop down menu.
- It will fire only once and not be called again after new input.

@@ -218,3 +222,3 @@ ### <a name="markdown-header-onDropdownClose"></a>onDropdownClose

- Caution: Don't confuse this with a placeholder (see placerholder prop), this is an actual value in the input
and supports uses cases like saving user state or suggesting a search value.
and supports uses cases like saving user state or suggesting a search value.

@@ -232,3 +236,2 @@ ### <a name="markdown-header-debounceTime"></a>debounceTime

### <a name="markdown-header-debounceLoader"></a>debounceLoader

@@ -244,3 +247,1 @@

- Exposing this function supports use cases like resetting states on empty input field

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc