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
JavaScript
'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