UNPKG

ag-grid-quick-filter

Version:

A function rich all select that supports multiple data sources and integrates with ag-grid

909 lines (888 loc) 66.3 kB
'use strict'; var jsxRuntime = require('react/jsx-runtime'); var React = require('react'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); function isDocumentElement(el) { return [document.documentElement, document.body, window].indexOf(el) > -1; } const errorMessage = (error) => `${error instanceof Error ? error.message : error}`; function scrollTo(el, top) { if (isDocumentElement(el)) { window.scrollTo(0, top); return; } el.scrollTop = top; } function scrollIntoView(listElement, elementToShow) { const menuRect = listElement.getBoundingClientRect(); const focusedRect = elementToShow.getBoundingClientRect(); const overScroll = elementToShow.offsetHeight / 3; if (focusedRect.bottom + overScroll > menuRect.bottom) { scrollTo(listElement, Math.min(elementToShow.offsetTop + elementToShow.clientHeight - listElement.offsetHeight + overScroll, listElement.scrollHeight)); } else if (focusedRect.top - overScroll < menuRect.top) { scrollTo(listElement, Math.max(elementToShow.offsetTop - overScroll, 0)); } } const itemText = (item, text) => { var _a; try { return (_a = item.text) !== null && _a !== void 0 ? _a : (text ? text(item) : item); } catch (error) { console.log(`Object type either does not implement Option, the property getter (itemText) or is not a string, error: ${errorMessage(error)}`); } return ''; }; const stringValue = (value) => typeof value === 'string' ? value : value.toString(); const itemValue = (item, value) => { try { return item.value ? stringValue(item.value) : value ? value(item) : item; } catch (error) { console.log(`Object type either does not implement Option, the property getter (itemValue) or is not a string, error: ${errorMessage(error)}`); } return ''; }; var DefaultContext = { color: undefined, size: undefined, className: undefined, style: undefined, attr: undefined }; var IconContext = React__default["default"].createContext && React__default["default"].createContext(DefaultContext); var __assign = undefined && undefined.__assign || function () { __assign = Object.assign || function (t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = undefined && undefined.__rest || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; function Tree2Element(tree) { return tree && tree.map(function (node, i) { return React__default["default"].createElement(node.tag, __assign({ key: i }, node.attr), Tree2Element(node.child)); }); } function GenIcon(data) { // eslint-disable-next-line react/display-name return function (props) { return React__default["default"].createElement(IconBase, __assign({ attr: __assign({}, data.attr) }, props), Tree2Element(data.child)); }; } function IconBase(props) { var elem = function (conf) { var attr = props.attr, size = props.size, title = props.title, svgProps = __rest(props, ["attr", "size", "title"]); var computedSize = size || conf.size || "1em"; var className; if (conf.className) className = conf.className; if (props.className) className = (className ? className + " " : "") + props.className; return React__default["default"].createElement("svg", __assign({ stroke: "currentColor", fill: "currentColor", strokeWidth: "0" }, conf.attr, attr, svgProps, { className: className, style: __assign(__assign({ color: props.color || conf.color }, conf.style), props.style), height: computedSize, width: computedSize, xmlns: "http://www.w3.org/2000/svg" }), title && React__default["default"].createElement("title", null, title), props.children); }; return IconContext !== undefined ? React__default["default"].createElement(IconContext.Consumer, null, function (conf) { return elem(conf); }) : elem(DefaultContext); } // THIS FILE IS AUTO GENERATED function IoIosClose (props) { return GenIcon({"tag":"svg","attr":{"viewBox":"0 0 512 512"},"child":[{"tag":"path","attr":{"d":"M278.6 256l68.2-68.2c6.2-6.2 6.2-16.4 0-22.6-6.2-6.2-16.4-6.2-22.6 0L256 233.4l-68.2-68.2c-6.2-6.2-16.4-6.2-22.6 0-3.1 3.1-4.7 7.2-4.7 11.3 0 4.1 1.6 8.2 4.7 11.3l68.2 68.2-68.2 68.2c-3.1 3.1-4.7 7.2-4.7 11.3 0 4.1 1.6 8.2 4.7 11.3 6.2 6.2 16.4 6.2 22.6 0l68.2-68.2 68.2 68.2c6.2 6.2 16.4 6.2 22.6 0 6.2-6.2 6.2-16.4 0-22.6L278.6 256z"}}]})(props); }function IoIosRemoveCircle (props) { return GenIcon({"tag":"svg","attr":{"viewBox":"0 0 512 512"},"child":[{"tag":"path","attr":{"d":"M256 48C141.1 48 48 141.1 48 256s93.1 208 208 208 208-93.1 208-208S370.9 48 256 48zm90.5 224h-181c-8.5 0-16-6-16-16s7.2-16 16-16h181c8.8 0 16 7.2 16 16s-7.2 16-16 16z"}}]})(props); } // THIS FILE IS AUTO GENERATED function RiArrowDropDownFill (props) { return GenIcon({"tag":"svg","attr":{"viewBox":"0 0 24 24"},"child":[{"tag":"g","attr":{},"child":[{"tag":"path","attr":{"fill":"none","d":"M0 0h24v24H0z"}},{"tag":"path","attr":{"d":"M12 14l-4-4h8z"}}]}]})(props); } function styleInject(css, ref) { if ( ref === void 0 ) ref = {}; var insertAt = ref.insertAt; if (!css || typeof document === 'undefined') { return; } var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } var css_248z$2 = ".csQuickFilterChoice {\n display: flex;\n flex-direction: row;\n align-items: center;\n}\n\n.csQuickFilterChoiceText {\n margin-top: 2px;\n margin-bottom: 2px;\n margin-left: 2px;\n margin-right: 2px;\n}\n\n.csQuickFilterChoiceHighlighed {\n background-color: var(--QuickFilterSelectHighlihtedBackgroundColor, lightgray);\n background-image: var(--QuickFilterSelectHighlihtedBackgroundImage);\n}\n"; styleInject(css_248z$2); const FilterChoice = ({ choiceText, choice, choiceHighlighted, onSelected, choiceStyle, choiceHoverStyle, choiceClassName, choiceHoverClassName }) => { const selectItem = (event) => { onSelected(choice); event.stopPropagation(); }; const getChoiceStyle = () => choiceHighlighted && choiceHoverStyle ? choiceHoverStyle : choiceStyle !== null && choiceStyle !== void 0 ? choiceStyle : {}; const getChoiceClassName = () => choiceHighlighted ? (choiceHoverClassName ? ` ${choiceHoverClassName}` : " csQuickFilterChoiceHighlighed") : (choiceClassName ? ` ${choiceClassName}` : ""); return (jsxRuntime.jsx("div", Object.assign({ className: "csQuickFilterChoice" + getChoiceClassName(), style: getChoiceStyle(), onClick: selectItem }, { children: jsxRuntime.jsx("p", Object.assign({ className: "csQuickFilterChoiceText" }, { children: choiceText })) }))); }; var css_248z$1 = ".csQuickFilterWrapper {\n display: inline-block;\n}\n\n.csQuickFilterSelect {\n display: flex;\n flex-direction: row;\n align-items: center;\n position: relative;\n padding: 4px;\n border: var(--QuickFilterSelectBorder, 2px solid Lightgray);\n background-color: var(--QuickFilterSelectBackgroundColor, White);\n background-image: var(--QuickFilterSelectBackgroundImage);\n color: var(--QuickFilterSelectFontColor, Black);\n font-weight: var(--QuickFilterSelectFontWeight);\n font-family: var(--QuickFilterSelectFontFamily);\n font-size: var(--QuickFilterSelectFontSize);\n font-style: var(--QuickFilterSelectFontStyle);\n border-radius: 5px;\n}\n\n.csQuickFilterSelectDsiabled {\n color: var(--QuickFilterSelectDisabledFontColor);\n background-color: var(--QuickFilterSelectDisabledBackgroundColor, darkgray);\n background-image: var(--QuickFilterSelectDisabledBackgroundImage);\n}\n\n.csQuickFilterDropDownIcon {\n font-size: var(--QuickFilterSelectDropdownIconSize, large);\n color: var(--QuickFilterSelectDropdownIconColor);\n display: flex;\n}\n\n.csQuickFilterClearSelection {\n padding-right: 2px;\n font-size: var(--QuickFilterSelectClearSelectionIconSize, large);\n color: var(--QuickFilterSelectClearSelectionIconColor);\n display: flex;\n}\n\n.csQuickFilterClearSelection:hover {\n color: var(--QuickFilterSelectClearSelectionIconHighlightColor, Lightgray);\n}\n\n.csQuickFilterTextWrapper {\n flex: 1;\n flex-basis: 100%;\n}\n\n.csQuickFilterInput {\n color: var(--QuickFilterSelectFontColor, Black);\n background-color: transparent;\n border: none;\n}\n\n.csQuickFilterInputDisabled {\n color: var(--QuickFilterSelectDisabledFontColor);\n}\n\n.csQuickFilterInput:focus,\ninput:focus {\n outline: none;\n}\n\n.csQuickFilterSelectCommon {\n font-size: var(--QuickFilterSelectTitleFontSize, small);\n}\n\n.csQuickFilterSelectTitle {\n min-width: 30px;\n -webkit-margin-before: 0px;\n margin-block-start: 0px;\n -webkit-margin-after: 0px;\n margin-block-end: 0px;\n}\n\n.csQuickFilterSelectfloatingTitle {\n position: absolute;\n}\n\n.csQuickFilterSelectDisabled {\n color: var(--QuickFilterSelectDisabledFontColor);\n}\n\n.csQuickFilterChoiceContainer {\n position: absolute;\n width: var(--QuickFilterSelectListWidth,-webkit-fill-available);\n left: 0px;\n overflow: auto;\n border-radius: 5px;\n z-index: 1;\n border: var(--QuickFilterSelectBorder, 2px solid Lightgray);\n background-color: var(--QuickFilterSelectBackgroundColor, White);\n background-image: var(--QuickFilterSelectBackgroundImage);\n max-height: var(--QuickFilterMaxListHeight);\n}\n\n.csQuickFilterChoiceList {\n -webkit-padding-start: 0px;\n padding-inline-start: 0px;\n -webkit-margin-before: 2px;\n margin-block-start: 2px;\n -webkit-margin-after: 2px;\n margin-block-end: 2px;\n}\n\n.csQuickFilterCatagoryTitle {\n -webkit-margin-before: 0px;\n margin-block-start: 0px;\n -webkit-margin-after: 0px;\n margin-block-end: 0px;\n text-align: center;\n font-size: var(--QuickFilterSelectCatagoryTitleFontSize, x-small);\n font-weight: var(--QuickFilterSelectCatagoryTitleFontWeight, bold);;\n background-color: var(--QuickFilterSelectCatagoryTitleBackgroundColor);\n background-image: var(--QuickFilterSelectCatagoryTitleBackgroundImage);\n color: var(--QuickFilterSelectCatagorFontColor);\n}\n\n.csQuickFilterSelections {\n display: flex;\n flex: 1;\n flex-flow: row wrap;\n align-items: center;\n gap: 3px;\n}\n\n.csQuickFilterNoItems {\n -webkit-margin-before: 0px;\n margin-block-start: 0px;\n -webkit-margin-after: 0px;\n margin-block-end: 0px;\n}\n\n.csQuickFilterDividor {\n position: absolute;\n top: 20%;\n height: 60%;\n border-left: Lightgray 2px solid;\n}"; styleInject(css_248z$1); var css_248z = ".csQuickFilterSelection {\n border-radius: 2px;\n padding: 2px;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n background-color: var(--QuickFilterSelectSelectedBackgroundColor);\n border: var(--QuickFilterSelectSelectedBorder,dashed black 1px);\n background-image: var(--QuickFilterSelectSelectedBackgroundImage);\n}\n\n.csQuickFilterSelectionDisabled {\n color: var(--QuickFilterSelectDisabledFontColor);\n background-color: var(--QuickFilterSelectDisabledBackgroundColor);\n background-image: var(--QuickFilterSelectDisabledBackgroundImage);\n}\n\n.csQuickFilterSelectionActive {\n background-color: var(--QuickFilterSelectSelectedBackgroundColor);\n background-image: var(--QuickFilterSelectSelectedBackgroundImage);\n border: var(--QuickFilterSelectSelectedActiveColor, yellow) 2px solid;\n}\n\n.csQuickFilterContentAlign {\n display: flex;\n flex: 1;\n flex-direction: row;\n align-items: center;\n}\n\n.csQuickFilterSelectionText {\n -webkit-margin-before: 0px;\n margin-block-start: 0px;\n -webkit-margin-after: 0px;\n margin-block-end: 0px;\n}\n\n.csQuickFilterPrefixText {\n -webkit-margin-before: 0px;\n margin-block-start: 0px;\n -webkit-margin-after: 0px;\n margin-block-end: 0px;\n font-size: var(--QuickFilterSelectPrefixFontSize, xx-small);\n font-weight: var(--QuickFilterSelectPrefixFontWeight, bold);\n}\n\n.csQuickFilterDeselect {\n color: var(--QuickFilterSelectDeslectIconColor, red);\n font-size: var(--QuickFilterSelectDeslectIconSize, small);\n}\n\n.csQuickFilterDeselect:hover {\n color: var(--QuickFilterSelectDeslectHighlightIconColor, #efb6b6);\n}\n\n.csQuickFilterDeselectDividor {\n align-self: stretch;\n margin-right: 2px;\n display: flex;\n align-items: center;\n}\n\n.csQuickFilterTitleDividor {\n align-self: stretch;\n display: flex;\n width: 100%;\n justify-content: center;\n}"; styleInject(css_248z); const FilterSelection = ({ prefix, selectionText, selection, disabled, onDeselected, selectionStyle, selectionDisabledStyle, selectionClassName, selectionDisabledClassName, deselectStyle, deselectClassName, deselectIcon, selectionPrefixClassName, selectionPrefixDisabledClassName, selectionPrefixDisabledStyle, selectionPrefixStyle, selectionTextClassName, selectionTextDisabledClassName, selectionTextDisabledStyle, selectionTextStyle, selectionActiveClassName, active }) => { const deselectItem = (event) => { onDeselected(selection); event.stopPropagation(); }; const getSelectionStyle = () => disabled && selectionDisabledStyle ? selectionDisabledStyle : selectionStyle !== null && selectionStyle !== void 0 ? selectionStyle : {}; const getSelectionClassName = () => disabled ? (selectionDisabledClassName ? ` ${selectionDisabledClassName}` : " csQuickFilterSelectionDisabled") : active ? (selectionActiveClassName ? ` ${selectionActiveClassName}` : " csQuickFilterSelectionActive") : (selectionClassName ? ` ${selectionClassName}` : ""); const getDeselectStyle = () => deselectStyle !== null && deselectStyle !== void 0 ? deselectStyle : {}; const getDeselectClassName = () => deselectClassName ? ` ${deselectClassName}` : ""; const getSelectionPrefixStyle = () => disabled && selectionPrefixDisabledStyle ? selectionPrefixDisabledStyle : selectionPrefixStyle !== null && selectionPrefixStyle !== void 0 ? selectionPrefixStyle : {}; const getSelectionPrefixClassName = () => disabled ? (selectionPrefixDisabledClassName ? ` ${selectionPrefixDisabledClassName}` : "") : (selectionPrefixClassName ? ` ${selectionPrefixClassName}` : ""); const getSelectionTextStyle = () => disabled && selectionTextDisabledStyle ? selectionTextDisabledStyle : selectionTextStyle !== null && selectionTextStyle !== void 0 ? selectionTextStyle : {}; const getSelectionTextClassName = () => disabled ? (selectionTextDisabledClassName ? ` ${selectionTextDisabledClassName}` : "") : (selectionTextClassName ? ` ${selectionTextClassName}` : ""); const DeselectIcon = deselectIcon !== null && deselectIcon !== void 0 ? deselectIcon : IoIosRemoveCircle; return (jsxRuntime.jsx("div", { children: jsxRuntime.jsxs("div", Object.assign({ className: "csQuickFilterSelection" + getSelectionClassName(), style: getSelectionStyle() }, { children: [prefix && (jsxRuntime.jsx("div", Object.assign({ className: "csQuickFilterTitleDividor" }, { children: jsxRuntime.jsx("p", Object.assign({ className: "csQuickFilterPrefixText" + getSelectionPrefixClassName(), style: getSelectionPrefixStyle() }, { children: prefix })) }))), jsxRuntime.jsxs("div", Object.assign({ className: "csQuickFilterContentAlign" }, { children: [jsxRuntime.jsx("div", Object.assign({ className: "csQuickFilterDeselectDividor" }, { children: jsxRuntime.jsx(DeselectIcon, { onClick: deselectItem, className: "csQuickFilterDeselect" + getDeselectClassName(), style: getDeselectStyle() }) })), jsxRuntime.jsx("p", Object.assign({ className: "csQuickFilterSelectionText" + getSelectionTextClassName(), style: getSelectionTextStyle() }, { children: selectionText }))] }))] })) })); }; const cacheMap = new Map(); const createCache = (id, timeToLive, delay) => { //check if cache already exists and return const existingCache = cacheMap.get(id); if (existingCache) { return existingCache; } //create new map to store items const itemCache = new Map(); //create expiry check const checkCacheEntries = (timeToLive) => { const expiryTime = new Date(Date.now()); expiryTime.setSeconds(expiryTime.getSeconds() - timeToLive); Array.from(itemCache) .filter((kvp) => kvp[1].created < expiryTime) .forEach((kvp) => itemCache.delete(kvp[0])); }; //run expiry check on specified interval or default to every 5 minutes const expireCacheItemsTimerId = timeToLive ? setInterval(checkCacheEntries, delay ? delay * 1000 : 5 * 60 * 1000, timeToLive) : undefined; //create cache const cache = { itemCache, getCachedItems: (text) => { var _a; return (_a = itemCache.get(text)) === null || _a === void 0 ? void 0 : _a.items; }, cacheItems: (text, items) => { itemCache.set(text, { created: new Date(Date.now()), items }); }, dispose: () => { if (expireCacheItemsTimerId) { clearInterval(expireCacheItemsTimerId); } itemCache.clear(); }, }; cacheMap.set(id, cache); return cache; }; const generateGuid = () => { const ch4 = () => { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) .substring(1); }; return `${ch4()}${ch4()}-${ch4()}-${ch4()}-${ch4()}-${ch4()}${ch4()}${ch4()}`; }; const createAgGridQuickFilterModel = (initialProps) => { const constructChoiceMap = (choices) => { const map = new Map(); choices.forEach((choice) => map.set(choice.key, choice)); return map; }; const comparisonSupported = (comparison, definition) => { switch (definition.filterType) { case "number": return comparison === "=" || comparison === "!" || comparison === "<" || comparison === ">" || comparison === "<=" || comparison === ">="; case "date": return comparison === "=" || comparison === "!" || comparison === "<" || comparison === ">"; default: //text return comparison === "=" || comparison === "!" || comparison === "%" || comparison === "!%" || comparison === "%<" || comparison === "%>"; } }; const getValue = (option, definition) => { if (definition.valueConverter) { return definition.valueConverter(option); } switch (definition.filterType) { case "number": return +itemText(option, definition === null || definition === void 0 ? void 0 : definition.text); case "date": return new Date(itemText(option, definition === null || definition === void 0 ? void 0 : definition.text)); default: return option; } }; const getFilterType = (comparison) => { switch (comparison) { case "!": return "notEqual"; case ">": return "greaterThan"; case "<": return "lessThan"; case ">=": return "greaterThanOrEqual"; case "<=": return "lessThanOrEqual"; case "%": return "contains"; case "!%": return "notContains"; case "%<": return "startsWith"; case "%>": return "endsWith"; default: return "equals"; } }; const createFilter = (selection, definition) => { var _a; const [selection1, selection2] = selection; if (!selection2) { return createCondition(selection1, definition); } return { condition1: createCondition(selection1, definition), condition2: createCondition(selection2, definition), filterType: (_a = definition === null || definition === void 0 ? void 0 : definition.filterType) !== null && _a !== void 0 ? _a : "text", operator: selection2.operand === "|" ? "OR" : "AND" }; }; const createCondition = (selection, definition) => { var _a, _b, _c; switch (definition === null || definition === void 0 ? void 0 : definition.filterType) { case "date": return { filterType: "date", dateFrom: getValue(selection.option, definition), dateTo: null, type: getFilterType((_a = selection.comparison) !== null && _a !== void 0 ? _a : "") }; default: return { filterType: (_b = definition.filterType) !== null && _b !== void 0 ? _b : "text", filter: getValue(selection.option, definition), type: getFilterType((_c = selection.comparison) !== null && _c !== void 0 ? _c : "") }; } }; const getDistinctValuesFromAgGrid = (agGridApi, column) => { const unique = new Set(); const callback = (row) => { if (row.data) { const value = row.data[column]; if (value) { unique.add(value); } } }; if (model.operand) { agGridApi.forEachNode(callback); } else { agGridApi.forEachNodeAfterFilter(callback); } return [...unique].sort(); }; const model = { agGridApi: initialProps.agGridApi, definitionMap: constructChoiceMap(initialProps.choices), lookUpGuidMap: new Map(), lookUpChoiceMap: new Map(), cache: initialProps.cacheLookUp ? createCache(initialProps.title, initialProps.cacheTimeToLive, initialProps.cacheExpiryCheck) : undefined, inputText: "", filterText: "", showChoices: false, highlightedIndex: -1, visibleChoices: [], visibleChoiceCount: 0, selected: [], selectId: generateGuid(), token: "", listPosition: "40px", props: initialProps, updateProps: (props) => { if (props.choices !== model.props.choices) { model.definitionMap = constructChoiceMap(initialProps.choices); model.updateVisibleChoices(); model.updateDisplay(); } if (props.agGridApi !== model.agGridApi) { model.agGridApi = props.agGridApi; } model.props = props; }, updateRefs: (inputRef, selectRef) => { model.inputRef = inputRef; model.selectRef = selectRef; }, updateDisplay: () => { if (model.refresh) { model.refresh(); } }, signalChange: () => { if (model.props.onChange) { model.props.onChange(model.selected); } }, fetchChoices: (text) => { model.definitionMap.forEach((definition) => { var _a, _b; try { let regEx; if (definition.regExMatch) { model.lookUpChoiceMap.set(definition.key, []); const selectedCount = model.selected.filter(sel => sel.key == definition.key).length; if (text !== "" && model.comparison && definition.regExMatch.test(text) && (!(definition === null || definition === void 0 ? void 0 : definition.maximumSelections) || selectedCount < (definition === null || definition === void 0 ? void 0 : definition.maximumSelections)) && (!model.agGridApi || definition.replaceExisting || selectedCount < 2) //when using ag-grid limit selection to 2 ) { regEx = (definition === null || definition === void 0 ? void 0 : definition.valueGetter) ? definition === null || definition === void 0 ? void 0 : definition.valueGetter(text) : text; if (regEx && !definition.lookUp) { model.lookUpChoiceMap.set(definition.key, [regEx]); model.updateVisibleChoices(); } } } if (definition.lookUp) { const limit = (_b = (_a = definition.maxAvailableChoices) !== null && _a !== void 0 ? _a : model.props.maxAvailableChoices) !== null && _b !== void 0 ? _b : 5; model.lookUpChoiceMap.set(definition.key, []); if (text !== "" && model.cache) { //fetch options from cache try { const cachedItems = model.cache.getCachedItems(definition.key + "_" + text); if (cachedItems) { //return caches items plus reg ex model.lookUpChoiceMap.set(definition.key, (regEx ? [regEx, ...cachedItems] : cachedItems).slice(0, limit)); model.updateVisibleChoices(); return; } } catch (error) { console.log(`Failed to fetch options from cache, reason: ${errorMessage(error)}`); } } if (text !== "") { //look up options const guid = model.token = generateGuid(); //store guid to check if call is still the current call model.lookUpGuidMap.set(definition.key, guid); definition .lookUp(text) .then((options) => { try { if (model.cache) { //if there is a cache store items model.cache.cacheItems(definition.key + "_" + text, options); } //check if the guide is the most recent if (guid === model.lookUpGuidMap.get(definition.key)) { //if it is then update options model.lookUpChoiceMap.set(definition.key, (regEx ? [regEx, ...options] : options).slice(0, limit)); model.updateVisibleChoices(); } } catch (error) { console.log(`Failed to fetch items from options for ${definition.key}, reason: ${errorMessage(error)}`); } }) .catch((error) => { console.log(`Failed to fetch options, reason: ${errorMessage(error)}`); }); } } } catch (error) { console.log(`Failed to fetch options, reason: ${errorMessage(error)}`); } }); }, buildVisibleChoices: () => { try { //filter out selected items and items that do not contain text input model.visibleChoiceCount = 0; const visibleChoices = []; model.definitionMap.forEach((definition, key) => { var _a, _b, _c, _d, _e, _f, _g; const lookupChoices = (_a = model.lookUpChoiceMap.get(key)) !== null && _a !== void 0 ? _a : []; const agOptions = definition.sourceAg && definition.agGridColumn && model.agGridApi ? getDistinctValuesFromAgGrid(model.agGridApi, definition.agGridColumn) : undefined; const staticOptions = ((_b = agOptions !== null && agOptions !== void 0 ? agOptions : definition.options) !== null && _b !== void 0 ? _b : []).filter(item => model.includeItem(definition, item, false)); const exactMatch = staticOptions.find(item => model.includeItem(definition, item, true)) !== undefined; if (staticOptions.length > 0 || //do we have options or lookup options lookupChoices.length > 0) { const limit = (_d = (_c = definition.maxAvailableChoices) !== null && _c !== void 0 ? _c : model.props.maxAvailableChoices) !== null && _d !== void 0 ? _d : 5; const options = { key, priority: exactMatch ? -1 : staticOptions.length === 1 ? 0 : 1, options: (model.props.showWithNoFilter || //no filter (model.filterText !== "" && //or we have text and (not comparison or current comparison is supported) (!model.comparison || comparisonSupported(model.comparison, definition))) ? (staticOptions.length > 1 ? [...lookupChoices, ...staticOptions] : [...staticOptions, ...lookupChoices]) : []).slice(0, limit) }; if (options.options && ((_e = options.options) === null || _e === void 0 ? void 0 : _e.length) > 0) { model.visibleChoiceCount += (_g = (_f = options.options) === null || _f === void 0 ? void 0 : _f.length) !== null && _g !== void 0 ? _g : 0; visibleChoices.push(options); } } }); visibleChoices.sort((x, y) => { var _a, _b; return ((_a = x.priority) !== null && _a !== void 0 ? _a : 0) < ((_b = y.priority) !== null && _b !== void 0 ? _b : 0) ? -1 : x.priority === y.priority ? 0 : 1; }); let offset = 0; visibleChoices.forEach(choices => { var _a, _b; choices.offset = offset; offset += (_b = (_a = choices.options) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0; }); model.visibleChoices = visibleChoices; } catch (error) { console.log(`Failed to build visible choices, reason: ${errorMessage(error)}`); } }, includeItemText: (item, exact, lookUp) => { const text = !model.props.caseSensitive ? itemText(item, lookUp).toLocaleLowerCase() : itemText(item, lookUp); const compareToValue = !model.props.caseSensitive ? model.filterText.toLowerCase() : model.filterText; return exact ? text === compareToValue : text.includes(compareToValue); }, includeItemValue: (item, exact, lookUp) => { const text = !model.props.caseSensitive ? itemValue(item, lookUp).toLocaleLowerCase() : itemValue(item, lookUp); const compareToValue = !model.props.caseSensitive ? model.filterText.toLowerCase() : model.filterText; return exact ? text === compareToValue : text.includes(compareToValue); }, //check if item should be filtered includeItem: (definition, item, exact) => { var _a; if (typeof item === "string") { return model.includeItemText(item, exact, definition.text); } switch ((_a = definition.search) !== null && _a !== void 0 ? _a : "both") { case "text": return model.includeItemText(item, exact, definition.text); case "value": return model.includeItemValue(item, exact, definition.value); default: return (model.includeItemText(item, exact, definition.text) || model.includeItemValue(item, exact, definition.value)); } }, updateVisibleChoices: () => { model.buildVisibleChoices(); if (model.visibleChoiceCount === 0) { model.adjustHighlightedIndex(-1); } else if (model.highlightedIndex >= model.visibleChoiceCount) { model.adjustHighlightedIndex(model.visibleChoiceCount - 1); } else if (model.highlightedIndex === -1) { model.adjustHighlightedIndex(0); } model.updateListPosition(); model.updateDisplay(); }, //hides the list and clears the input choices hideList: () => { model.showChoices = false; model.comparison = undefined; model.inputText = ""; model.showChoices = false; model.updateFilterText(""); model.updateDisplay(); }, //shows the list and sets the highlighted index to -1 showList: () => { model.showChoices = true; model.adjustHighlightedIndex(-1); model.updateListPosition(); model.updateDisplay(); }, updateListPosition: () => { setTimeout(() => { var _a, _b; if (model.selectRef && model.selectRef.current) { model.listPosition = (_b = `${((_a = model.selectRef.current) === null || _a === void 0 ? void 0 : _a.clientHeight) + 2}px`) !== null && _b !== void 0 ? _b : "40px"; model.updateDisplay(); } }, 1); }, //document click handler clickedAway: (mouseEvent) => { if (!model.showChoices) { return; } try { //get the control const input = document.getElementById("csInput" + model.selectId); const list = document.getElementById("csList" + model.selectId); //check if the click was outside the controls area if (mouseEvent.target !== null && !(input === null || input === void 0 ? void 0 : input.contains(mouseEvent.target)) === true && !(list === null || list === void 0 ? void 0 : list.contains(mouseEvent.target)) === true) { model.hideList(); } } catch (error) { console.log(`Failed to handle click away, reason: ${errorMessage(error)}`); } }, setFocus: () => { setTimeout(() => { if (model.inputRef && model.inputRef.current) { model.inputRef.current.focus(); } }, 10); }, //update the text input into the input contorl updateFilterText: (text) => { model.filterText = text; model.updateVisibleChoices(); }, //hanlder for clicking on the control textInputClicked: () => { if (model.props.disabled) { return; } model.setFocus(); //if list already shown do nothing. if (model.showChoices) { return; } //clear the input text model.updateFilterText(""); model.showList(); }, //called when text entered into the input control textChanged: (event) => { //update text model.comparison = undefined; model.operand = undefined; var filterText = event.target.value; //capture operand if (filterText.startsWith("&")) { model.operand = "&"; filterText = filterText.substring(1); } else if (filterText.startsWith("|")) { model.operand = "|"; filterText = filterText.substring(1); } filterText = filterText.trimStart(); //capture operation if (filterText.startsWith("!%")) { model.comparison = "!%"; filterText = filterText.substring(2); } else if (filterText.startsWith("%<")) { model.comparison = "%<"; filterText = filterText.substring(2); } else if (filterText.startsWith("%>")) { model.comparison = "%>"; filterText = filterText.substring(2); } else if (filterText.startsWith(">=")) { model.comparison = ">="; filterText = filterText.substring(2); } else if (filterText.startsWith("<=")) { model.comparison = "<="; filterText = filterText.substring(2); } else if (filterText.startsWith("!")) { model.comparison = "!"; filterText = filterText.substring(1); } else if (filterText.startsWith("%")) { model.comparison = "%"; filterText = filterText.substring(1); } else if (filterText.startsWith("=")) { model.comparison = "="; filterText = filterText.substring(1); } else if (filterText.startsWith(">")) { model.comparison = ">"; filterText = filterText.substring(1); } else if (filterText.startsWith("<")) { model.comparison = "<"; filterText = filterText.substring(1); } model.fetchChoices(filterText); model.updateFilterText(filterText); model.inputText = event.target.value; model.updateDisplay(); //required due to update filter and fetch choices }, updateAgGrid: (definition) => { var _a, _b; if (!definition || !definition.agGridColumn) { return; } const instance = (_a = model.agGridApi) === null || _a === void 0 ? void 0 : _a.getFilterInstance(definition.agGridColumn); if (instance && instance !== null) { const selectedItems = model.selected.filter(sel => sel.key === definition.key); const filter = selectedItems.length > 0 ? createFilter(selectedItems, definition) : null; instance.setModel(filter); (_b = model.agGridApi) === null || _b === void 0 ? void 0 : _b.onFilterChanged(); } }, updateSelection: () => { model.selected = [...model.selected]; model.updateVisibleChoices(); model.signalChange(); }, //called when a choice is selected. selectItem: (choice) => { var _a, _b; const definition = model.definitionMap.get(choice.key); //get the number of selected items in the catagory const defintionSelectionCount = model.selected.filter(sel => sel.key === choice.key).length; if (!model.selected.find(sel => { var _a; return sel.key === choice.key && //can't add if choice already selected sel.option === choice.option && sel.comparison == ((_a = model.comparison) !== null && _a !== void 0 ? _a : "="); }) && (!(definition === null || definition === void 0 ? void 0 : definition.maximumSelections) || //no maxium selection or selected count is less (definition === null || definition === void 0 ? void 0 : definition.replaceExisting) || defintionSelectionCount < (definition === null || definition === void 0 ? void 0 : definition.maximumSelections)) && ((definition === null || definition === void 0 ? void 0 : definition.replaceExisting) || //using ag-grid, and either no items are selected or we can replace !model.agGridApi || defintionSelectionCount < 2) //when using ag-grid limit selection to 2 ) { //create item const selection = { key: choice.key, option: choice.option, comparison: (_a = model.comparison) !== null && _a !== void 0 ? _a : "=", operand: defintionSelectionCount > 0 ? (_b = model.operand) !== null && _b !== void 0 ? _b : "&" : undefined }; if (((model.agGridApi && defintionSelectionCount > 1) || (definition === null || definition === void 0 ? void 0 : definition.maximumSelections) === defintionSelectionCount) && (definition === null || definition === void 0 ? void 0 : definition.replaceExisting)) { //if there is a limit to the number of items and repalced selected then replace the last item const [toRemove] = model.selected.filter(sel => sel.key === choice.key).reverse(); model.selected.splice(model.selected.indexOf(toRemove), 1, selection); } else { if (model.agGridApi && defintionSelectionCount > 0) { //if agrid and and we have an item, then get item and insert at the next index const item = model.selected.find(sel => sel.key === choice.key); model.selected.splice(model.selected.indexOf(item) + 1, 0, selection); } else { model.selected.push(selection); } } model.updateSelection(); if (model.agGridApi) { model.updateAgGrid(definition); } } if (!model.props.noClearTextOnSelect) { model.inputText = ""; model.updateFilterText(""); } if (model.props.hideOnSelect) { model.hideList(); } else { model.updateDisplay(); } }, //called when a deselected item is clicked deselectItem: (selection) => { try { // if the selected items greater than minimum then deselect const selectedItem = model.selected.find(sel => sel.key === selection.key && sel.option === selection.option && sel.comparison == selection.comparison); if (selectedItem) { model.selected.splice(model.selected.indexOf(selectedItem), 1); model.selected.filter(sel => sel.key === selection.key).forEach(sel => sel.operand = undefined); model.updateSelection(); if (model.agGridApi) { model.updateAgGrid(model.definitionMap.get(selection.key)); } model.updateDisplay(); } } catch (error) { console.log(`Failed to deselect item, reason: ${errorMessage(error)}`); } }, //called when clear all selected items clicked. clearSelection: (event) => { if (model.props.disabled) { return; } const activeFilters = []; model.selected.forEach(selection => { if (activeFilters.indexOf(selection.key) === -1) { //only clear grid filter once activeFilters.push(selection.key); } }); model.selected = []; activeFilters.forEach(key => { const definition = model.definitionMap.get(key); //get definition if (definition && definition.agGridColumn) { model.updateAgGrid(definition); //call update ag-grid for defintion - selection will be blank } }); model.updateSelection(); model.updateDisplay(); event.stopPropagation(); }, //makes highlighted item visible makeItemVisible: (index) => { try { const input = document.getElementById(`item_${index}`); const list = document.getElementById("csList" + model.selectId); if (input && list) { scrollIntoView(list, input); } } catch (error) { console.log(`Failed to make highlighted item visible, reason: ${errorMessage(error)}`); } }, //updates the highlighted item index adjustHighlightedIndex: (index) => { model.highlightedIndex = index; if (index !== -1) { model.makeItemVisible(index); } }, adjustHighlightedIndexOnly: (index) => { model.adjustHighlightedIndex(index); model.updateDisplay(); }, getItemAtIndex: () => { var _a, _b, _c, _d, _e; for (let index = 0; index < model.visibleChoices.length; index++) { const choices = model.visibleChoices[index]; if (choices.options && model.highlightedIndex >= ((_a = choices.offset) !== null && _a !== void 0 ? _a : 0) && model.highlightedIndex < ((_b = choices.offset) !== null && _b !== void 0 ? _b : 0) + ((_d = (_c = choices.options) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0)) { return { key: choices.key, option: choices.options[model.highlightedIndex - ((_e = choices.offset) !== null && _e !== void 0 ? _e : 0)], }; } } return undefined; }, //called when a key is pressed inputKeyPressed: (event) => { try { switch (event.code) { case "ArrowLeft": if (event.target.value.length === 0) { if (model.active === undefined) { if (model.selected.length > 0) { model.active = model.selected.length - 1; } } else { if (model.selected.length === 0) { model.active = undefined; } else { model.active = model.active > 0 ? model.active - 1 : model.selected.length - 1; } } model.updateDisplay(); } break; case "ArrowRight": if (event.target.value.length === 0) { if (model.active === undefined) { if (model.selected.length > 0) { model.active = 0; } } else { if (model.selected.length === 0) { model.active = und