UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

1,370 lines (1,369 loc) 56.5 kB
"use strict"; "use client"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _push = _interopRequireDefault(require("core-js-pure/stable/instance/push.js")); var _react = _interopRequireWildcard(require("react")); var _withComponentMarkers = _interopRequireDefault(require("../../shared/helpers/withComponentMarkers.js")); var _clsx = _interopRequireDefault(require("clsx")); var _componentHelper = require("../../shared/component-helper.js"); var _helpers = require("../../shared/helpers.js"); var _useId = _interopRequireDefault(require("../../shared/helpers/useId.js")); var _useMountEffect = _interopRequireDefault(require("../../shared/helpers/useMountEffect.js")); var _useIsomorphicLayoutEffect = require("../../shared/helpers/useIsomorphicLayoutEffect.js"); var _SpacingUtils = require("../space/SpacingUtils.js"); var _filterValidProps = require("../../shared/helpers/filterValidProps.js"); var _AlignmentHelper2 = _interopRequireDefault(require("../../shared/AlignmentHelper.js")); var _Suffix = _interopRequireDefault(require("../../shared/helpers/Suffix.js")); var _AriaLive = _interopRequireDefault(require("../aria-live/AriaLive.js")); var _FormLabel = _interopRequireDefault(require("../form-label/FormLabel.js")); var _FormStatus = _interopRequireDefault(require("../form-status/FormStatus.js")); var _IconPrimary2 = _interopRequireDefault(require("../icon-primary/IconPrimary.js")); var _Input = _interopRequireWildcard(require("../input/Input.js")); var _ProgressIndicator = _interopRequireDefault(require("../progress-indicator/ProgressIndicator.js")); var _DrawerList = _interopRequireDefault(require("../../fragments/drawer-list/DrawerList.js")); var _DrawerListItem = require("../../fragments/drawer-list/DrawerListItem.js"); var _DrawerListContext = _interopRequireDefault(require("../../fragments/drawer-list/DrawerListContext.js")); var _DrawerListProvider = _interopRequireDefault(require("../../fragments/drawer-list/DrawerListProvider.js")); var _DrawerListHelpers = require("../../fragments/drawer-list/DrawerListHelpers.js"); var _jsxRuntime = require("react/jsx-runtime"); var _IconPrimary, _AlignmentHelper; function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const autocompleteDefaultProps = { id: null, mode: 'sync', title: 'Option Menu', placeholder: null, noOptions: null, showAll: null, ariaLiveOptions: null, indicatorLabel: null, showOptionsSr: null, selectedSr: null, submitButtonTitle: null, submitButtonIcon: 'chevron_down', inputRef: null, icon: 'loupe', iconSize: null, iconPosition: 'left', arrowPosition: null, label: null, labelDirection: 'vertical', labelSrOnly: null, keepValue: null, keepSelection: null, keepValueAndSelection: null, showClearButton: null, status: null, statusState: 'error', statusProps: null, statusNoAnimation: null, globalStatus: null, suffix: null, disableFilter: false, disableReorder: false, scrollable: true, focusable: false, disableHighlighting: false, maxHeight: null, direction: 'auto', skipPortal: null, noAnimation: false, noScrollAnimation: false, showSubmitButton: false, submitElement: null, preventSelection: false, size: 'default', align: null, optionsRender: null, data: null, searchInWordIndex: null, searchNumbers: null, defaultValue: null, value: 'initval', inputValue: 'initval', autoComplete: 'off', openOnFocus: false, preventClose: false, keepOpen: false, open: null, disabled: null, stretch: null, skeleton: null, portalClass: null, drawerClass: null, pageOffset: null, observerElement: null, enableBodyLock: false, className: null, children: null, onOpen: null, onClose: null, onType: null, onFocus: null, onBlur: null, onChange: null, onSelect: null, onClear: null, onSubmit: null, inputElement: null }; function Autocomplete(props) { const _id = (0, _useId.default)(props.id); const providerProps = { ...props, id: _id, data: props.data || props.children, open: null, tagName: 'dnb-autocomplete', ignoreEvents: false, preventFocus: true, skipKeysearch: true }; return (0, _jsxRuntime.jsx)(_DrawerListProvider.default, { ...providerProps, children: (0, _jsxRuntime.jsx)(AutocompleteInstance, { ...props, id: _id }) }); } function parseDataItem(dataItem) { const searchWord = (0, _DrawerListHelpers.parseContentTitle)(dataItem.searchContent || dataItem, { separator: ' ' }); if (typeof searchWord !== 'string' && Array.isArray(searchWord)) { return parseDataItem(searchWord); } return searchWord; } function createSearchIndex(data) { return data.map(dataItem => { const contentChunk = parseDataItem(dataItem); return { dataItem, contentChunk }; }); } function getCurrentDataTitle(selectedItem, data) { const currentData = (0, _DrawerListHelpers.getCurrentData)(selectedItem, data); return (0, _DrawerListHelpers.parseContentTitle)(currentData, { separator: ' ', preferSelectedValue: true }); } function AutocompleteInstance(ownProps) { var _context$getTranslati, _Fragment2; const context = (0, _react.useContext)(_DrawerListContext.default); const drawerList = context.drawerList; const filteredOwnProps = Object.fromEntries(Object.entries(ownProps).filter(([, v]) => v !== undefined)); const props = (0, _componentHelper.extendPropsWithContext)(filteredOwnProps, autocompleteDefaultProps, (_context$getTranslati = context.getTranslation) === null || _context$getTranslati === void 0 || (_context$getTranslati = _context$getTranslati.call(context, ownProps)) === null || _context$getTranslati === void 0 ? void 0 : _context$getTranslati.Autocomplete, (0, _filterValidProps.pickFormElementProps)(context === null || context === void 0 ? void 0 : context.formElement), context === null || context === void 0 ? void 0 : context.Autocomplete); const { title, placeholder, label, labelDirection, labelSrOnly, icon, iconSize, size, align, fixedPosition, status, statusState, statusProps, statusNoAnimation, globalStatus, suffix, scrollable, focusable, keepOpen, keepValue, keepValueAndSelection, keepSelection, showClearButton, preventClose, noAnimation, noScrollAnimation, showSubmitButton, submitElement, inputElement: CustomInput, optionsRender, preventSelection, maxHeight, defaultValue, searchNumbers, searchInWordIndex, searchMatch, showOptionsSr, selectedSr, submitButtonTitle, submitButtonIcon, portalClass, drawerClass, inputRef, className, disabled, stretch, skeleton, arrowPosition, iconPosition, skipPortal, independentWidth, autoComplete, openOnFocus, disableFilter, disableReorder, onClear, selectAll, mode: _mode, data: _data, children: _children, direction: _direction, pageOffset: _pageOffset, observerElement: _observerElement, id: _id, open: _open, value: _value, inputValue: _inputValue, enableBodyLock: _enableBodyLock, listClass: _listClass, indicatorLabel: _indicatorLabel, noOptions: _noOptions, showAll: _showAll, ariaLiveOptions: _ariaLiveOptions, disableHighlighting: _disableHighlighting, onOpen: _onOpen, onType: _onType, onFocus: _onFocus, onBlur: _onBlur, onClose: _onClose, onChange: _onChange, onSelect: _onSelect, onSubmit: _onSubmit, onItemMouseEnter, ...attributes } = props; const [inputValue, setInputValueState] = (0, _react.useState)(() => { if (props.inputValue !== 'initval' && props.inputValue != null) { return props.inputValue; } if (drawerList && drawerList.currentTitle) { return drawerList.currentTitle; } return null; }); const [typedInputValue, setTypedInputValue] = (0, _react.useState)(null); const [mode, setModeState] = (0, _react.useState)(props.mode); const [hasFocus, setHasFocus] = (0, _react.useState)(false); const hasBlurRef = (0, _react.useRef)(false); const setHasBlur = (0, _react.useCallback)(v => { hasBlurRef.current = v; }, []); const [showAllNextTime, setShowAllNextTime] = (0, _react.useState)(false); const [skipFocusDuringChange, setSkipFocusDuringChange] = (0, _react.useState)(false); const [disableHighlightingState, setDisableHighlighting] = (0, _react.useState)(props.disableHighlighting); const [visibleIndicator, setVisibleIndicator] = (0, _react.useState)(false); const [searchIndex, setSearchIndexState] = (0, _react.useState)(null); const _ref = (0, _react.useRef)(null); const _refShell = (0, _react.useRef)(null); const _refInput = (0, _react.useRef)(null); const _selectTimeout = (0, _react.useRef)(null); const _blurTimeout = (0, _react.useRef)(null); const _focusTimeout = (0, _react.useRef)(null); const showAllTimeoutRef = (0, _react.useRef)(null); const preventFiringBlurEvent = (0, _react.useRef)(null); const closingFromChangeRef = (0, _react.useRef)(false); const suppressFocusHandlerRef = (0, _react.useRef)(false); const selectAllActiveRef = (0, _react.useRef)(false); const debouncedEventFnsRef = (0, _react.useRef)({}); const cacheMemoryRef = (0, _react.useRef)({}); const attributesRef = (0, _react.useRef)({}); const wasVisibleRef = (0, _react.useRef)(false); const skipFilterRef = (0, _react.useRef)(disableFilter); const skipReorderRef = (0, _react.useRef)(disableReorder); const searchIndexRef = (0, _react.useRef)(searchIndex); const prevDataRef = (0, _react.useRef)(props.data); const dataChangedRef = (0, _react.useRef)(false); const lastUpdateDataRef = (0, _react.useRef)(null); const prevValueRef = (0, _react.useRef)(props.value); const prevInputValuePropRef = (0, _react.useRef)(props.inputValue); const prevDisableHighlightingRef = (0, _react.useRef)(props.disableHighlighting); const inputValueRef = (0, _react.useRef)(inputValue); const typedInputValueRef = (0, _react.useRef)(typedInputValue); const modeRef = (0, _react.useRef)(mode); const hasFocusRef = (0, _react.useRef)(hasFocus); searchIndexRef.current = searchIndex; inputValueRef.current = inputValue; typedInputValueRef.current = typedInputValue; modeRef.current = mode; hasFocusRef.current = hasFocus; const drawerListRef = (0, _react.useRef)(drawerList); drawerListRef.current = drawerList; const propsRef = (0, _react.useRef)(props); propsRef.current = props; const hasInjectedDataItem = (0, _react.useCallback)((data = drawerList.data) => { const lastItem = data.slice(-1)[0]; return lastItem ? lastItem.showAll || String(lastItem.__id) === 'noOptions' : false; }, [drawerList.data]); const countData = (0, _react.useCallback)((data = drawerList.data) => { const count = data.length; return count > 0 && hasInjectedDataItem(data) ? count - 1 : count; }, [drawerList.data, hasInjectedDataItem]); const hasValidData = (0, _react.useCallback)((data = drawerList.data) => { if (countData(data) > 0) { const first = data[0]; if (!first.showAll && !['noOptions', 'indicator'].includes(String(first.__id))) { return true; } } return false; }, [drawerList.data, countData]); const hasSelectedItem = (0, _react.useCallback)(() => { return parseFloat(String(drawerListRef.current.selectedItem)) > -1; }, []); const hasActiveItem = (0, _react.useCallback)(() => { return parseFloat(String(drawerList.activeItem)) > -1; }, [drawerList.activeItem]); const hasFilterActive = (0, _react.useCallback)((data = drawerListRef.current.data) => { const originalData = drawerListRef.current.originalData; return !(originalData && originalData.length === countData(data)); }, [countData]); const focusDrawerList = (0, _react.useCallback)(() => { try { drawerList._refUl.current.focus({ preventScroll: true }); } catch (e) {} }, [drawerList._refUl]); const focusInput = (0, _react.useCallback)(() => { try { if (_refInput.current) { _refInput.current.focus({ preventScroll: true }); } } catch (e) { (0, _componentHelper.warn)(e); } }, []); const setVisible = (0, _react.useCallback)((args = null, onStateComplete = null) => { wasVisibleRef.current = true; drawerListRef.current.setWrapperElement(_ref.current).setVisible(args, onStateComplete); }, []); const setHidden = (0, _react.useCallback)((args = null, onStateComplete = null) => { drawerListRef.current.setHidden(args, onStateComplete); setHasFocus(false); setHasBlur(false); }, [setHasBlur]); const resetActiveItem = (0, _react.useCallback)(() => { drawerListRef.current.setState({ activeItem: null }); }, []); const resetFilter = (0, _react.useCallback)(() => { drawerListRef.current.setData(drawerListRef.current.originalData); }, []); const setInputValue = (0, _react.useCallback)(val => { setInputValueState(val); }, []); const clearInputValue = (0, _react.useCallback)(() => { setInputValueState(''); setTypedInputValue(null); }, []); const ignoreEvents = (0, _react.useCallback)(() => { clearTimeout(showAllTimeoutRef.current); drawerListRef.current.setState({ ignoreEvents: true }, () => { showAllTimeoutRef.current = setTimeout(() => { drawerListRef.current.setState({ ignoreEvents: false }); }, 10); }); }, []); const showAllItems = (0, _react.useCallback)(() => { var _getCurrentIndex; resetFilter(); const selectedItem = (_getCurrentIndex = (0, _DrawerListHelpers.getCurrentIndex)(propsRef.current.value, drawerListRef.current.originalData)) !== null && _getCurrentIndex !== void 0 ? _getCurrentIndex : drawerListRef.current.selectedItem; drawerListRef.current.setState({ cacheHash: 'all' }); drawerListRef.current.setActiveItemAndScrollToIt(selectedItem, { scrollTo: false }); }, [resetFilter]); const setSearchIndex = (0, _react.useCallback)(({ overwriteSearchIndex = false, data = drawerList.originalData } = {}, cb = null) => { cacheMemoryRef.current = {}; if (!overwriteSearchIndex && searchIndexRef.current) { return searchIndexRef.current; } const newSearchIndex = createSearchIndex(data); setSearchIndexState(newSearchIndex); searchIndexRef.current = newSearchIndex; if (cb) { cb(); } return newSearchIndex; }, [drawerList.originalData]); const revalidateSelectedItem = (0, _react.useCallback)(() => { const selectedItem = (0, _DrawerListHelpers.getCurrentIndex)(props.value, drawerListRef.current.originalData); drawerListRef.current.setState({ selectedItem }); }, [props.value]); const revalidateInputValue = (0, _react.useCallback)(() => { if (props.inputValue && props.inputValue !== 'initval') { return undefined; } const selectedItem = (0, _DrawerListHelpers.getCurrentIndex)(props.value, drawerListRef.current.originalData); const usedInputValue = getCurrentDataTitle(selectedItem, drawerListRef.current.originalData); setInputValue(usedInputValue); }, [props.inputValue, props.value, setInputValue]); const resetSelectedItem = (0, _react.useCallback)(() => { const hadValue = hasSelectedItem(); drawerListRef.current.setState({ selectedItem: null }, () => { if (hadValue) { (0, _componentHelper.dispatchCustomElementEvent)(propsRef.current, 'onChange', { ...getEventObjects('onChange') }); } }); }, [hasSelectedItem]); const totalReset = (0, _react.useCallback)(() => { setInputValueState(null); setTypedInputValue(null); resetActiveItem(); resetSelectedItem(); }, [resetActiveItem, resetSelectedItem]); const resetInputValue = (0, _react.useCallback)(() => { var _props$inputValue; if (keepValue || keepValueAndSelection || props.inputValue !== 'initval' && ((_props$inputValue = props.inputValue) === null || _props$inputValue === void 0 ? void 0 : _props$inputValue.length) > 0) { return undefined; } clearTimeout(_selectTimeout.current); _selectTimeout.current = setTimeout(() => { if (hasSelectedItem()) { const val = getCurrentDataTitle(drawerListRef.current.selectedItem, drawerListRef.current.originalData); setInputValue(val); } else { clearInputValue(); } }, 1); }, [keepValue, keepValueAndSelection, props.inputValue, hasSelectedItem, setInputValue, clearInputValue]); const showNoOptionsItem = (0, _react.useCallback)(() => { resetActiveItem(); ignoreEvents(); drawerListRef.current.setData(props.noOptions === false ? [] : [{ className: 'dnb-autocomplete__no-options', content: props.noOptions, ignoreEvents: true, __id: 'noOptions' }]); drawerListRef.current.setState({ cacheHash: 'noOptions' }); setVisible(); }, [resetActiveItem, ignoreEvents, props.noOptions, setVisible]); const showIndicatorItem = (0, _react.useCallback)(() => { resetActiveItem(); ignoreEvents(); drawerListRef.current.setData([{ className: 'dnb-autocomplete__indicator', content: (0, _jsxRuntime.jsx)(_ProgressIndicator.default, { label: props.indicatorLabel, labelDirection: "horizontal" }), ignoreEvents: true, __id: 'indicator' }]); drawerListRef.current.setState({ cacheHash: 'indicator' }); setVisible(); }, [resetActiveItem, ignoreEvents, props.indicatorLabel, setVisible]); const showIndicator = (0, _react.useCallback)(() => { setVisibleIndicator(true); }, []); const hideIndicator = (0, _react.useCallback)(() => { setVisibleIndicator(false); }, []); const setMode = (0, _react.useCallback)(newMode => { setModeState(newMode); }, []); const wrapWithShowAll = (0, _react.useCallback)(data => { if (!data || !hasFilterActive(data)) { return data; } const lastItem = drawerListRef.current.originalData.slice(-1)[0]; if (lastItem && !lastItem.showAll) { const lastActiveItem = data.slice(-1)[0]; if (lastActiveItem) { (0, _push.default)(data).call(data, { __id: lastItem.__id + 1, lastActiveItem: lastActiveItem.__id, className: 'dnb-autocomplete__show-all', showAll: true, activeItem: false, selectedItem: false, content: (0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, { children: [_IconPrimary || (_IconPrimary = (0, _jsxRuntime.jsx)(_IconPrimary2.default, { icon: "arrow_down" })), props.showAll] }) }); } } return data; }, [hasFilterActive, props.showAll]); const runFilter = (0, _react.useCallback)((value, { data = null, searchIndex: siParam = searchIndexRef.current, searchNumbers: snParam = props.searchNumbers, inWordIndex = parseFloat(String((_props$searchInWordIn => (_props$searchInWordIn = props.searchInWordIndex) !== null && _props$searchInWordIn !== void 0 ? _props$searchInWordIn : skipFilterRef.current ? 1 : 3)())) - 1, disableHighlighting: disableHL = false, skipFilter = false, skipReorder = false } = {}) => { let currentSearchIndex = siParam; if (data) { currentSearchIndex = setSearchIndex({ data }); } else if (!currentSearchIndex) { currentSearchIndex = setSearchIndex(); } if (typeof currentSearchIndex === 'undefined') { return []; } const startsWithMatch = props.searchMatch === 'starts-with'; const rawValue = value !== null && value !== void 0 ? value : ''; let searchWords = rawValue.split(/\s+/g).filter(Boolean); if (startsWithMatch) { const hasLetters = /[\p{L}]/u.test(rawValue); const hasNumbers = /[\p{N}]/u.test(rawValue); if (startsWithMatch && snParam && hasNumbers && !hasLetters) { const normalizedNumeric = rawValue.replace(/[^\p{N}]+/gu, ''); searchWords = normalizedNumeric ? [normalizedNumeric] : []; } } const getWordBoundary = wordIndex => startsWithMatch && wordIndex === 0 ? '^' : snParam ? '' : '^|\\s'; const searchWordsData = searchWords.map((word, wordIndex) => { const processedWord = snParam ? word.replace(/[^\p{L}\p{N}]+/gu, '') : (0, _componentHelper.escapeRegexChars)(word); const wordBoundary = getWordBoundary(wordIndex); return { originalWord: word, processedWord, wordIndex, filterRegex: new RegExp(wordIndex >= inWordIndex ? `${processedWord}` : `(${wordBoundary})${processedWord}`, 'i'), scoreRegex: new RegExp(`(${wordBoundary})${(0, _componentHelper.escapeRegexChars)(word)}`, 'ig') }; }); const firstWordRegex = searchWords.length > 0 ? new RegExp(`^${(0, _componentHelper.escapeRegexChars)(searchWords[0])}`, 'i') : null; const findSearchWords = contentChunk => { if (typeof contentChunk !== 'string') { return []; } return searchWordsData.filter(({ filterRegex }) => { if (filterRegex.test(contentChunk)) { return true; } if (snParam && filterRegex.test(contentChunk.replace(/[^0-9]/g, ''))) { return true; } return false; }).map(({ originalWord, wordIndex, scoreRegex }) => { let wordScore = 0; wordScore += (contentChunk.match(scoreRegex) || []).length; if (wordIndex === 0 && firstWordRegex) { const isFirstWord = firstWordRegex.test(contentChunk.split(' ')[0]); if (isFirstWord) { wordScore += searchWords.length + 1; } } return { word: originalWord, wordIndex, wordScore }; }); }; const strS = '\uFFFE'; const strE = '\uFFFF'; const mappedIndex = currentSearchIndex.map(item => { const listOfFoundWords = findSearchWords(item.contentChunk); const allWordsAreNumeric = snParam ? searchWords.every(word => /^[\p{N}\s.,]+$/u.test(word)) : false; const hasMultipleNumericTerms = snParam && searchWords && searchWords.length > 1 && allWordsAreNumeric; if (hasMultipleNumericTerms && listOfFoundWords.length !== searchWords.length) { return { totalScore: 0, item }; } if (typeof item.dataItem === 'string') { item.dataItem = { content: item.dataItem }; } if (!item.dataItem.render) { item.dataItem = { ...item.dataItem }; } item.dataItem.render = (children, id) => { var _props; if (disableHL || disableHighlightingState) { return children; } const cacheHash = id + value; cacheMemoryRef.current = cacheMemoryRef.current || {}; if (cacheMemoryRef.current[cacheHash]) { return cacheMemoryRef.current[cacheHash]; } const isComponent = typeof children !== 'string' && _react.default.isValidElement(children); let childArray; if (isComponent && Array.isArray((_props = children.props) === null || _props === void 0 ? void 0 : _props.children)) { childArray = children.props.children; } else if (!Array.isArray(children)) { childArray = [children]; } else { childArray = children; } const segments = childArray.map(originalChild => ({ originalChild, segment: (0, _componentHelper.convertJsxToString)(originalChild, ' ') })); const processed = segments.map(({ originalChild, segment }, idx) => { searchWords.forEach((word, wordIndex) => { if (segment) { word = (0, _componentHelper.escapeRegexChars)(word); if (snParam) { const cleanedWord = word.replace(/[^\p{L}\p{N}]+/gu, ''); if (cleanedWord) { const escapedWord = (0, _componentHelper.escapeRegexChars)(cleanedWord); segment = segment.replace(new RegExp(`(${escapedWord})`, 'gi'), match => { if (match.includes(strS)) { return match; } return `${strS}${match}${strE}`; }); } } else { if (wordIndex >= inWordIndex) { segment = segment.replace(new RegExp(`(${word})`, 'gi'), `${strS}$1${strE}`); } else { segment = segment.replace(new RegExp(`(${getWordBoundary(wordIndex)})(${word})`, 'gi'), `$1${strS}$2${strE}`); } } } }); let result = segment; if (segment.includes(strS)) { const startRepeatRegex = new RegExp(`(${strS})+`, 'g'); const endRepeatRegex = new RegExp(`(${strE})+`, 'g'); const adjacentRegex = new RegExp(`(${strE}${strS})`, 'g'); const splitRegex = new RegExp(`(${strS}|${strE})`, 'g'); const normalized = segment.replace(startRepeatRegex, strS).replace(endRepeatRegex, strE).replace(adjacentRegex, ''); const tokens = normalized.split(splitRegex).filter(Boolean); let isHighlighted = false; let highlightIndex = 0; const parts = tokens.map(token => { if (token === strS) { isHighlighted = true; return null; } if (token === strE) { isHighlighted = false; return null; } if (isHighlighted) { const key = `highlight-${cacheHash}-${idx}-${highlightIndex++}`; return (0, _jsxRuntime.jsx)("span", { className: "dnb-drawer-list__option__item--highlight", children: token }, key); } return token; }); result = (0, _jsxRuntime.jsx)("span", { children: parts }, cacheHash + idx); } else { result = (0, _jsxRuntime.jsx)("span", { children: segment }, cacheHash + idx); } if (isComponent) { var _element$props; const element = originalChild; if (Array.isArray(element === null || element === void 0 || (_element$props = element.props) === null || _element$props === void 0 ? void 0 : _element$props.children)) { result = element.props.children.map(Comp => { const compEl = Comp; return Comp === originalChild || compEl.props && compEl.props.children === originalChild ? result : Comp; }); } else if (typeof originalChild === 'string') { result = originalChild; } if (_react.default.isValidElement(originalChild)) { result = _react.default.createElement(originalChild.type, { ...originalChild.props, key: 'clone' + cacheHash + idx }, result); } } return result; }); return cacheMemoryRef.current[cacheHash] = processed; }; if (skipFilterRef.current || skipFilter) { return item.dataItem; } let totalScore = listOfFoundWords.length; for (const { wordScore } of listOfFoundWords) { totalScore += wordScore; } return { totalScore, item }; }); if (!skipFilterRef.current && !skipFilter) { const scored = mappedIndex.filter(({ totalScore }) => totalScore); if (!skipReorderRef.current && !skipReorder) { scored.sort(({ totalScore: a }, { totalScore: b }) => b - a); } return scored.map(({ item }) => item.dataItem); } return mappedIndex; }, [setSearchIndex, props.searchMatch, props.searchNumbers, props.searchInWordIndex, disableHighlightingState]); const runFilterToHighlight = (0, _react.useCallback)(({ fillDataIfEmpty = false, ...options } = {}, value = inputValueRef.current) => { const possibleTitle = getCurrentDataTitle(drawerListRef.current.selectedItem, drawerListRef.current.originalData); if (value === possibleTitle) { return undefined; } value = String(value || '').trim(); setDisableHighlighting(false); let data = runFilter(value, options); if (fillDataIfEmpty && data.length === 0 && value === '') { data = drawerListRef.current.originalData; } drawerListRef.current.setData(wrapWithShowAll(data)); drawerListRef.current.setState({ cacheHash: value + countData(data) }); return data; }, [runFilter, wrapWithShowAll, countData]); const runFilterWithSideEffects = (0, _react.useCallback)((value, options = {}) => { const data = runFilter(value, options); const count = countData(data); if ((value === null || value === void 0 ? void 0 : value.length) > 0) { if (count === 0) { if (modeRef.current !== 'async') { showNoOptionsItem(); } } else if (count > 0) { drawerListRef.current.setData(wrapWithShowAll(data)); drawerListRef.current.setState({ cacheHash: value + count }); if (count === 1) { drawerListRef.current.setState({ activeItem: data[0].__id }); } } } else { var _inputValueRef$curren; if (!keepValue && !keepSelection && !keepValueAndSelection) { totalReset(); } else if (keepValue) { resetSelectedItem(); } showAllItems(); if (((_inputValueRef$curren = inputValueRef.current) === null || _inputValueRef$curren === void 0 ? void 0 : _inputValueRef$curren.length) > 0) { setVisible(); } } if (hasFocusRef.current) { setVisible(); } return data; }, [runFilter, countData, showNoOptionsItem, wrapWithShowAll, keepValue, keepSelection, keepValueAndSelection, totalReset, resetSelectedItem, showAllItems, setVisible]); const showAll = (0, _react.useCallback)(() => { resetFilter(); drawerListRef.current.setState({ cacheHash: 'all' }); runFilterToHighlight({ skipFilter: true, fillDataIfEmpty: true }); }, [resetFilter, runFilterToHighlight]); const setVisibleByContext = (0, _react.useCallback)((options = {}, onStateComplete = null) => { const skipFilter = showAllNextTime; if (skipFilter) { setShowAllNextTime(false); } runFilterToHighlight({ fillDataIfEmpty: true, skipFilter, ...options }); setVisible(null, onStateComplete); }, [showAllNextTime, runFilterToHighlight, setVisible]); const toggleVisible = (0, _react.useCallback)((args = null, onStateComplete = null) => { args = args || {}; if (typeof args.hasFilter === 'undefined') { args.hasFilter = false; } if (disabled) { return undefined; } if (!args.hasFilter && !preventClose && !drawerList.hidden && drawerList.isOpen) { setHidden(null, onStateComplete); } else { setVisibleByContext(null, onStateComplete); } }, [disabled, preventClose, drawerList.hidden, drawerList.isOpen, setHidden, setVisibleByContext]); const toggleVisibleAndFocusOptions = (0, _react.useCallback)(() => { drawerListRef.current.toggleVisible(null, isVisible => { if (isVisible) { focusDrawerList(); } }); }, [focusDrawerList]); const hasDatasetChanged = (0, _react.useCallback)(rawData => { const { selectedItem } = drawerListRef.current; if (parseFloat(String(selectedItem)) > -1) { const newItem = rawData === null || rawData === void 0 ? void 0 : rawData[selectedItem]; const oldItem = drawerListRef.current.originalData[selectedItem]; if ((newItem === null || newItem === void 0 ? void 0 : newItem.selectedKey) !== (oldItem === null || oldItem === void 0 ? void 0 : oldItem.selectedKey)) { return true; } } return false; }, []); const emptyData = (0, _react.useCallback)(() => { cacheMemoryRef.current = {}; clearInputValue(); drawerListRef.current.setData(() => [], () => { setSearchIndex({ overwriteSearchIndex: true }, null); resetActiveItem(); totalReset(); }, { overwriteOriginalData: true }); }, [clearInputValue, setSearchIndex, resetActiveItem, totalReset]); const updateData = (0, _react.useCallback)(rawData => { if (rawData === lastUpdateDataRef.current) { return; } lastUpdateDataRef.current = rawData; const hasChanged = hasDatasetChanged(rawData); drawerListRef.current.setState({ cacheHash: 'updateData' }, () => { if (hasChanged) { if (propsRef.current.value && propsRef.current.value !== 'initval') { revalidateSelectedItem(); revalidateInputValue(); } else { resetSelectedItem(); } } }); drawerListRef.current.setData(() => rawData, newData => { setSearchIndex({ overwriteSearchIndex: true, data: newData }, () => { const typed = typedInputValueRef.current; if ((typed === null || typed === void 0 ? void 0 : typed.length) > 0) { const filteredData = runFilterWithSideEffects(typed); if (countData(filteredData) === 0) { if (modeRef.current !== 'async') { showNoOptionsItem(); } } } else { resetActiveItem(); if (drawerListRef.current.open) { showAllItems(); } } }); }, { overwriteOriginalData: true }); }, [hasDatasetChanged, revalidateSelectedItem, revalidateInputValue, resetSelectedItem, setSearchIndex, runFilterWithSideEffects, countData, showNoOptionsItem, resetActiveItem, showAllItems]); const eventMethodsRef = (0, _react.useRef)(null); eventMethodsRef.current = { updateData, revalidateSelectedItem, revalidateInputValue, resetSelectedItem, clearInputValue, showAllItems, setVisible, resetInputValue, setHidden, emptyData, focusInput, setInputValue, showNoOptionsItem, showIndicatorItem, showIndicator, hideIndicator, setMode }; function getEventObjects(key) { return { attributes: attributesRef.current, dataList: drawerListRef.current.data, ...eventMethodsRef.current, debounce: (func, cbProps = {}, wait = 250) => { const existingDebouncedFn = debouncedEventFnsRef.current[key]; if (existingDebouncedFn) { return existingDebouncedFn(cbProps); } const newDebouncedFn = (0, _helpers.debounce)(func, wait); debouncedEventFnsRef.current[key] = newDebouncedFn; return newDebouncedFn(cbProps); } }; } const setFocusOnInput = (0, _react.useCallback)(() => { suppressFocusHandlerRef.current = true; focusInput(); suppressFocusHandlerRef.current = false; }, [focusInput]); const setVisibleAndFocusOnInput = (0, _react.useCallback)(() => { if (!hasFocusRef.current && !(0, _helpers.hasSelectedText)()) { setFocusOnInput(); setVisible(); } }, [setFocusOnInput, setVisible]); const onInputChangeHandler = (0, _react.useCallback)(({ value: val, event }) => { selectAllActiveRef.current = false; setTypedInputValue(val); setInputValueState(val); (0, _componentHelper.dispatchCustomElementEvent)(propsRef.current, 'onType', { value: val, event, ...getEventObjects('onType') }); const trimmed = String(val).trim(); if (trimmed !== inputValueRef.current) { runFilterWithSideEffects(trimmed); } }, [runFilterWithSideEffects]); const onInputKeyDownHandler = (0, _react.useCallback)(({ event: e }) => { const key = e.key; switch (key) { case 'PageUp': case 'PageDown': case 'Home': case 'End': e.preventDefault(); break; case 'ArrowUp': case 'ArrowDown': e.preventDefault(); if (!drawerList.open) { setVisible(); } break; case 'Escape': setShowAllNextTime(true); break; case 'Enter': if (e.defaultPrevented) { break; } e.preventDefault(); if (!drawerList.open && hasFilterActive()) { ignoreEvents(); showAll(); } if ((!hasValidData() || !hasSelectedItem()) && !hasActiveItem()) { var _inputValueRef$curren2; (0, _componentHelper.dispatchCustomElementEvent)(propsRef.current, 'onSubmit', { value: (_inputValueRef$curren2 = inputValueRef.current) !== null && _inputValueRef$curren2 !== void 0 ? _inputValueRef$curren2 : '', event: e, ...getEventObjects('onSubmit') }); toggleVisible(); } else if (!drawerList.open) { setVisible(); } break; } }, [drawerList.open, setVisible, hasFilterActive, ignoreEvents, showAll, hasValidData, hasSelectedItem, hasActiveItem, toggleVisible]); const onInputClickHandler = (0, _react.useCallback)(e => { if (!drawerList.open && hasFilterActive()) { ignoreEvents(); showAll(); } const { value } = e.target; setVisibleByContext({ value }); }, [drawerList.open, hasFilterActive, ignoreEvents, showAll, setVisibleByContext]); const onInputFocusHandler = (0, _react.useCallback)(event => { if (skipFocusDuringChange) { return undefined; } if (suppressFocusHandlerRef.current) { return undefined; } if (!hasFocusRef.current) { if (openOnFocus && hasValidData()) { const { value } = event.target; setVisibleByContext({ value }); } else { setSearchIndex({}, null); } if (keepValueAndSelection) { showAll(); } if (selectAll) { selectAllActiveRef.current = true; } setHasFocus(true); setHasBlur(false); (0, _componentHelper.dispatchCustomElementEvent)(propsRef.current, 'onFocus', { event, ...getEventObjects('onFocus') }); } }, [skipFocusDuringChange, openOnFocus, hasValidData, setVisibleByContext, setSearchIndex, keepValueAndSelection, showAll, selectAll, setHasBlur]); const reserveActivityHandler = (0, _react.useCallback)(event => { preventFiringBlurEvent.current = Boolean('key' in event && event.key === 'Enter' || (event !== null && event !== void 0 && event.currentTarget ? (0, _componentHelper.getClosestParent)('dnb-drawer-list', event.currentTarget) || (0, _componentHelper.getClosestParent)('dnb-input__submit-button__button', event.currentTarget) : false)); if (preventFiringBlurEvent.current) { setTimeout(() => { preventFiringBlurEvent.current = false; }, noAnimation ? 1 : _DrawerList.default.blurDelay); } }, [noAnimation]); const onBlurHandler = (0, _react.useCallback)(event => { if (preventFiringBlurEvent.current || drawerList.hasFocusOnElement || hasBlurRef.current) { preventFiringBlurEvent.current = null; return false; } selectAllActiveRef.current = false; setHasBlur(true); setHasFocus(false); if (!keepValue && !keepValueAndSelection) { setTypedInputValue(null); } if (!preventSelection) { const existingValue = inputValueRef.current; resetInputValue(); const resetAfterClose = () => { if (!keepValue || !existingValue || hasSelectedItem()) { resetActiveItem(); } resetFilter(); }; if (noAnimation) { resetAfterClose(); } else { clearTimeout(_blurTimeout.current); _blurTimeout.current = setTimeout(resetAfterClose, _DrawerList.default.blurDelay); } } if (openOnFocus) { setHidden(); } (0, _componentHelper.dispatchCustomElementEvent)(propsRef.current, 'onBlur', { event, ...getEventObjects('onBlur') }); return undefined; }, [drawerList.hasFocusOnElement, keepValue, keepValueAndSelection, preventSelection, noAnimation, openOnFocus, resetInputValue, hasSelectedItem, resetActiveItem, resetFilter, setHidden, setHasBlur]); const onTriggerKeyDownHandler = (0, _react.useCallback)(e => { const key = e.key; switch (key) { case ' ': case 'Enter': { setVisible(); } break; } switch (key) { case ' ': case 'Enter': case 'PageUp': case 'PageDown': case 'ArrowDown': case 'ArrowUp': { e.preventDefault(); focusInput(); } break; } }, [setVisible, focusInput]); const onCloseHandler = (0, _react.useCallback)((args = {}) => { const res = (0, _componentHelper.dispatchCustomElementEvent)(propsRef.current, 'onClose', { ...args, ...getEventObjects('onClose') }); if (res !== false && !closingFromChangeRef.current) { setFocusOnInput(); } return res; }, [setFocusOnInput]); const onSelectHandler = (0, _react.useCallback)(args => { if (parseFloat(String(args.activeItem)) > -1) { (0, _componentHelper.dispatchCustomElementEvent)(propsRef.current, 'onSelect', { ...args, ...getEventObjects('onSelect') }); } }, []); const onPreChangeHandler = (0, _react.useCallback)(({ data }) => { if (data && data.showAll) { showAll(); const activeItem = data.lastActiveItem; if (parseFloat(String(activeItem)) > -1) { drawerListRef.current.setActiveItemAndScrollToIt(activeItem, { scrollTo: false }); } setFocusOnInput(); return false; } return undefined; }, [showAll, setFocusOnInput]); const onChangeHandler = (0, _react.useCallback)(args => { var _args$data; const selectedItem = args.selectedItem; if (!preventSelection) { if (!keepOpen) { setSkipFocusDuringChange(true); setDisableHighlighting(true); closingFromChangeRef.current = true; setHidden(); focusDrawerList(); closingFromChangeRef.current = false; setSkipFocusDuringChange(false); _focusTimeout.current = setTimeout(() => { setFocusOnInput(); setHasFocus(true); setHasBlur(false); }, 0); } const val = getCurrentDataTitle(selectedItem, drawerListRef.current.data); setInputValue(val); } if (typeof ((_args$data = args.data) === null || _args$data === void 0 ? void 0 : _args$data.render) === 'function') { delete args.data.render; } (0, _componentHelper.dispatchCustomElementEvent)(propsRef.current, 'onChange', { ...args, ...getEventObjects('onChange') }); }, [preventSelection, keepOpen, setHidden, focusDrawerList, setFocusOnInput, setInputValue, setHasBlur]); if (props.disableHighlighting !== prevDisableHighlightingRef.current) { prevDisableHighlightingRef.current = props.disableHighlighting; setDisableHighlighting(props.disableHighlighting); } if (props.inputValue !== 'initval' && props.inputValue !== prevInputValuePropRef.current) { prevInputValuePropRef.current = props.inputValue; setInputValueState(props.inputValue); } if (props.data !== prevDataRef.current) { var _props$data, _prevDataRef$current; if (((_props$data = props.data) === null || _props$data === void 0 ? void 0 : _props$data.length) > 0 && ((_prevDataRef$current = prevDataRef.current) === null || _prevDataRef$current === void 0 ? void 0 : _prevDataRef$current.length) === 0) { let selectedItem = drawerList.selectedItem; if (props.defaultValue) { selectedItem = props.defaultValue; } if (!props.defaultValue && props.value && props.value !== 'initval') { selectedItem = props.value; } const currentData = (0, _DrawerListHelpers.getCurrentData)(selectedItem, (0, _DrawerListHelpers.normalizeData)(props.data)); const newInputValue = (0, _DrawerListHelpers.parseContentTitle)(currentData, { separator: ' ', preferSelectedValue: true }); setInputValueState(newInputValue); } prevDataRef.current = props.data; dataChangedRef.current = true; } (0, _react.useEffect)(() => { if (inputRef && _refInput.current) { if (typeof inputRef === 'function') { inputRef(_refInput.current); } else { inputRef.current = _refInput.current; } } }, [inputRef]); (0, _useMountEffect.default)(() => { if (props.open) { runFilterToHighlight({ fillDataIfEmpty: true }); setVisible(); } }); (0, _react.useEffect)(() => { if (dataChangedRef.current) { dataChangedRef.current = false; lastUpdateDataRef.current = null; updateData(props.data); if (drawerList.open || hasFocus) { setSearchIndex({ overwriteSearchIndex: true }, () => { runFilterWithSideEffects(inputValueRef.current); }); } } }, [props.data]); (0, _react.useEffect)(() => { if (props.value !== prevValueRef.current) { prevValueRef.current = props.value; revalidateSelectedItem(); revalidateInputValue(); } }, [props.value, revalidateSelectedItem, revalidateInputValue]); (0, _useMountEffect.default)(() => { return () => { clearTimeout(_selectTimeout.current); clearTimeout(_blurTimeout.current); clearTimeout(_focusTimeout.current); clearTimeout(showAllTimeoutRef.current); }; }); (0, _useIsomorphicLayoutEffect.useIsomorphicLayoutEffect)(() => { if (selectAllActiveRef.current && _refInput.current) { try { _refInput.current.select(); } catch (e) {} } }); const showStatus = (0, _componentHelper.getStatusState)(status); const { id, hidden, selectedItem, direction, open } = drawerList; const isExpanded = Boolean(open) && hasValidData(); attributesRef.current = (0, _componentHelper.validateDOMAttributes)(null, attributes); Object.assign(drawerList.attributes, attributesRef.current); const mainParams = (0, _SpacingUtils.applySpacing)(props, { className: (0, _clsx.default)("dnb-autocomplete dnb-form-component", className, direction && `dnb-autocomplete--${direction}`, disabled && 'dnb-autocomplete--disabled', open && 'dnb-autocomplete--open', labelDirection && `dnb-autocomplete--${labelDirection}`, iconPosition && `dnb-autocomplete--icon-position-${iconPosition}`, align && `dnb-autocomplete--${align}`, visibleIndicator && 'dnb-autocomplete--show-indicator', size && `dnb-autocomplete--${size}`, stretch && `dnb-autocomplete--stretch`, status && `dnb-autocomplete__status--${statusState}`, showStatus && 'dnb-autocomplete__form-status') }); const shellParams = { className: 'dnb-autocomplete__shell dnb-no-focus', ref: _refShell }; const inputParams = { className: 'dnb-autocomplete__input', id, value: inputValue !== null && inputValue !== void 0 ? inputValue : '', placeholder: undefined, autoCapitalize: 'none', spellCheck: false, autoCorrect: 'off', autoComplete, role: 'combobox', 'aria-autocomplete': 'both', 'aria-controls': isExpanded ? `${id}-ul` : undefined, 'aria-haspopup': 'listbox', 'aria-expanded': isExpanded, onMouseDown: onInputClickHandler, onKeyDown: onInputKeyDownHandler, onChange: onInputChangeHandler, onFocus: onInputFocusHandler, onBlur: onBlurHandler, iconPosition: iconPosition, disabled, skeleton, ...attributes }; if (!(parseFloat(String(selectedItem)) > -1)) { inputParams.placeholder = placeholder || title; } inputParams['aria-placeholder'] = undefined; if (isExpanded) { inputParams['aria-activedescendant'] = drawerList.ariaActiveDescendant; } if (showStatus || suffix) { inputParams['aria-describedby'] = (0, _componentHelper.combineDescribedBy)(inputParams, showStatus ? id + '-status' : null, suffix ? id + '-suffix' : null); } const { iconPosition: _iconPosition, ...customInputParams } = inputParams; let submitButton = false; const triggerParams = { id: id + '-submit-button', disabled, status: status ? statusState : null, onKeyDown: onTriggerKeyDownHandler, onSubmit: toggleVisible, onMouseDown: reserveActivityHandler, 'aria-haspopup': 'listbox', 'aria-expanded': isExpanded, 'aria-label': !hidden ? submitButtonTitle : undefined, tooltip: showSubmitButton ? submitButtonTitle : null }; if (submitElement && _react.default.isValidElement(submitElement)) { submitButton = _react.default.createElemen