UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

1,046 lines (1,045 loc) 39 kB
"use client"; import _pushInstanceProperty from "core-js-pure/stable/instance/push.js"; import React, { useCallback, useContext, useEffect, useMemo, useReducer, useRef } from 'react'; import { useIsomorphicLayoutEffect } from "../../shared/helpers/useIsomorphicLayoutEffect.js"; import useMountEffect from "../../shared/helpers/useMountEffect.js"; import useUpdateEffect from "../../shared/helpers/useUpdateEffect.js"; import Context from "../../shared/Context.js"; import { warn, roundToNearest, getClosestScrollViewElement, detectOutsideClick, dispatchCustomElementEvent, getClosestParent } from "../../shared/component-helper.js"; import { getOffsetTop, getOffsetLeft, hasSelectedText, getSelectedElement as getSelectedTextElement } from "../../shared/helpers.js"; import { getData, normalizeData, findClosest, getSelectedItemValue, parseContentTitle, getEventData, prepareStartupState, prepareDerivedState, drawerListDefaultProps, drawerListProviderDefaultProps } from "./DrawerListHelpers.js"; import DrawerListContext from "./DrawerListContext.js"; import { disableBodyScroll, enableBodyScroll } from "../../components/modal/bodyScrollLock.js"; import { jsx as _jsx } from "react/jsx-runtime"; const allDefaultProps = { ...drawerListDefaultProps, ...drawerListProviderDefaultProps }; function DrawerListProviderComponent(ownProps) { const context = useContext(Context); const props = useMemo(() => ({ ...allDefaultProps, ...ownProps }), [ownProps]); const propsRef = useRef(props); propsRef.current = props; const stateRef = useRef(null); if (!stateRef.current) { stateRef.current = { cacheHash: '', activeItem: undefined, selectedItem: undefined, ignoreEvents: false, ...prepareStartupState(props) }; } const [, forceUpdate] = useReducer(() => ({}), {}); const callbacksRef = useRef([]); const pendingUpdatesRef = useRef([]); const mergeState = useCallback((partial, cb) => { var _context; _pushInstanceProperty(_context = pendingUpdatesRef.current).call(_context, partial); if (cb) { var _context2; _pushInstanceProperty(_context2 = callbacksRef.current).call(_context2, cb); } forceUpdate(); }, []); if (pendingUpdatesRef.current.length > 0) { const updates = pendingUpdatesRef.current; pendingUpdatesRef.current = []; for (const partial of updates) { Object.assign(stateRef.current, partial); } } useIsomorphicLayoutEffect(() => { const cbs = callbacksRef.current; if (cbs.length > 0) { callbacksRef.current = []; cbs.forEach(cb => cb()); } }); prepareDerivedState(props, stateRef.current); const _refRoot = useRef(null); const _refShell = useRef(null); const _refUl = useRef(null); const _refTriangle = useRef(null); const attributesRef = useRef({}); const showTimeoutRef = useRef(null); const hideTimeoutRef = useRef(null); const scrollTimeoutRef = useRef(null); const directionTimeoutRef = useRef(null); const itemSpotsRef = useRef({}); const itemSpotsCountRef = useRef(0); const setOnScrollRef = useRef(null); const bodyLockEnabledRef = useRef(false); const setDirectionFnRef = useRef(null); const rootElemRef = useRef(null); const changedOrderForRef = useRef(null); const searchCacheRef = useRef(null); const metaRef = useRef({ cmd: false, ctrl: false, shift: false, alt: false }); const outsideClickRef = useRef(null); const isOpenRef = useRef(false); const refreshScrollObserver = useCallback(() => { var _refUl$current; if (typeof window === 'undefined' || !_refUl.current) { return; } const elements = (_refUl$current = _refUl.current) === null || _refUl$current === void 0 ? void 0 : _refUl$current.querySelectorAll('li.dnb-drawer-list__option,li.dnb-drawer-list__group-title'); itemSpotsRef.current = {}; elements.forEach(element => { itemSpotsRef.current[element.offsetTop] = { id: element.getAttribute('id') }; }); itemSpotsCountRef.current = Object.keys(itemSpotsRef.current).length; }, []); const removeScrollObserverFn = useCallback(() => { if (typeof window !== 'undefined' && setOnScrollRef.current) { window.removeEventListener('resize', setOnScrollRef.current); setOnScrollRef.current = null; } }, []); const setScrollObserver = useCallback(() => { if (typeof window === 'undefined' || !_refUl.current) { return; } removeScrollObserverFn(); itemSpotsCountRef.current = 1; try { let closestToTop = null, closestToBottom = null, tmpToTop, tmpToBottom; setOnScrollRef.current = () => { if (!_refUl.current) { return; } if (itemSpotsCountRef.current <= 1) { refreshScrollObserver(); } const counts = Object.keys(itemSpotsRef.current); closestToBottom = findClosest(counts, _refUl.current.scrollTop + _refUl.current.offsetHeight); closestToTop = findClosest(counts, _refUl.current.scrollTop); if (itemSpotsRef.current[closestToTop] && itemSpotsRef.current[closestToTop].id !== tmpToTop) { tmpToTop = itemSpotsRef.current[closestToTop].id; mergeState({ closestToTop: itemSpotsRef.current[closestToTop].id }); } if (itemSpotsRef.current[closestToBottom] && itemSpotsRef.current[closestToBottom].id !== tmpToBottom) { tmpToBottom = itemSpotsRef.current[closestToBottom].id; mergeState({ closestToBottom: itemSpotsRef.current[closestToBottom].id }); } }; _refUl.current.addEventListener('scroll', setOnScrollRef.current); setOnScrollRef.current(); } catch (e) { warn('List could not set onScroll:', e); } }, [removeScrollObserverFn, refreshScrollObserver, mergeState]); const enableBodyLock = useCallback(() => { if (_refUl.current) { bodyLockEnabledRef.current = true; disableBodyScroll(_refUl.current); } }, []); const disableBodyLockFn = useCallback(() => { if (bodyLockEnabledRef.current && _refUl.current) { bodyLockEnabledRef.current = false; enableBodyScroll(_refUl.current); } }, []); const correctHiddenView = useCallback(() => { if (!_refShell.current || !_refUl.current) { return; } try { const spaceToLeft = getOffsetLeft(_refUl.current); const spaceToRight = window.innerWidth - (getOffsetLeft(_refUl.current) + _refUl.current.offsetWidth); const triangleStyle = _refTriangle.current.style; const shellStyle = _refShell.current.style; if (spaceToLeft < 0) { shellStyle.transform = 'translateX(' + Math.abs(spaceToLeft / 16) + 'rem)'; triangleStyle.right = Math.abs(spaceToLeft / 16) + 'rem'; } else if (spaceToRight < 0) { shellStyle.transform = 'translateX(' + spaceToRight / 16 + 'rem)'; triangleStyle.left = Math.abs(spaceToRight / 16) + 'rem'; } else { if (shellStyle.transform) { shellStyle.transform = ''; triangleStyle.left = 'auto'; triangleStyle.right = 'auto'; } } } catch (e) {} }, []); const removeDirectionObserver = useCallback(() => { disableBodyLockFn(); clearTimeout(directionTimeoutRef.current); if (typeof window !== 'undefined' && setDirectionFnRef.current) { var _rootElemRef$current; (_rootElemRef$current = rootElemRef.current) === null || _rootElemRef$current === void 0 || _rootElemRef$current.removeEventListener('scroll', setDirectionFnRef.current); if (typeof window.visualViewport !== 'undefined') { window.visualViewport.removeEventListener('scroll', setDirectionFnRef.current); window.visualViewport.removeEventListener('resize', setDirectionFnRef.current); } else { window.removeEventListener('resize', setDirectionFnRef.current); } setDirectionFnRef.current = null; } }, [disableBodyLockFn]); const setDirectionObserver = useCallback(() => { if (typeof window === 'undefined' || typeof document === 'undefined' || !(stateRef.current.wrapperElement || _refRoot.current)) { return undefined; } const { enableBodyLock: useBodyLockProp, scrollable, minHeight, maxHeight, onResize, pageOffset, observerElement, direction: directionProp } = propsRef.current; const useBodyLock = useBodyLockProp; const isScrollable = scrollable; const customMinHeight = parseFloat(minHeight) * 16; const customMaxHeight = parseFloat(maxHeight) || 0; let customElem = typeof observerElement === 'string' ? document.querySelector(observerElement) : null; if (!customElem) { customElem = getClosestScrollViewElement(_refRoot.current); } removeDirectionObserver(); const directionOffset = 96; const spaceToTopOffset = 2 * 16; const spaceToBottomOffset = 2 * 16; const elem = stateRef.current.wrapperElement || _refRoot.current; const getSpaceToBottom = ({ rootElem, pageYOffset }) => { const spaceToBottom = rootElem.clientHeight - (getOffsetTop(elem) + elem.offsetHeight) + pageYOffset; const html = document.documentElement; if (spaceToBottom < customMinHeight && rootElem !== html) { return getSpaceToBottom({ rootElem: html, pageYOffset }); } return spaceToBottom; }; const calculateMaxHeight = () => { const rootElem = customElem || document.documentElement; const pageYOffset = !isNaN(parseFloat(pageOffset)) ? parseFloat(pageOffset) : rootElem.scrollTop; const spaceToTop = getOffsetTop(elem) + elem.offsetHeight - pageYOffset; const spaceToBottom = getSpaceToBottom({ rootElem, pageYOffset }); let direction = directionProp; if (!direction || direction === 'auto') { direction = Math.max(spaceToBottom - directionOffset, directionOffset) < customMinHeight && spaceToTop > customMinHeight ? 'top' : 'bottom'; } let maxH = customMaxHeight; if (!(maxH > 0)) { if (direction === 'top') { maxH = spaceToTop - ((stateRef.current.wrapperElement || _refRoot.current).offsetHeight || 0) - spaceToTopOffset; } if (direction === 'bottom') { maxH = spaceToBottom - spaceToBottomOffset; } let vh = 0; if (typeof window.visualViewport !== 'undefined') { vh = window.visualViewport.height; } else { vh = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); } vh = vh * (isScrollable ? 0.7 : 0.9); if (maxH > vh) { maxH = vh; } maxH = roundToNearest(maxH, 8) / 16; } return { direction, maxHeight: maxH }; }; const renderDirection = () => { var _window, _window$requestAnimat; try { const { direction, maxHeight: mh } = calculateMaxHeight(); if (propsRef.current.direction === 'auto') { mergeState({ direction }); } mergeState({ maxHeight: mh }); if (onResize) { dispatchCustomElementEvent(stateRef.current, 'onResize', { direction, maxHeight: mh }); } } catch (e) { warn('List could not set onResize:', e); } ((_window = window) === null || _window === void 0 || (_window$requestAnimat = _window.requestAnimationFrame) === null || _window$requestAnimat === void 0 ? void 0 : _window$requestAnimat.call(_window, correctHiddenView)) || correctHiddenView(); }; setDirectionFnRef.current = () => { clearTimeout(directionTimeoutRef.current); directionTimeoutRef.current = setTimeout(renderDirection, 50); }; rootElemRef.current = customElem || window; rootElemRef.current.addEventListener('scroll', setDirectionFnRef.current); if (typeof window.visualViewport !== 'undefined') { window.visualViewport.addEventListener('scroll', setDirectionFnRef.current); window.visualViewport.addEventListener('resize', setDirectionFnRef.current); } else { window.addEventListener('resize', setDirectionFnRef.current); } if (useBodyLock) { enableBodyLock(); } refreshScrollObserver(); renderDirection(); }, [removeDirectionObserver, mergeState, correctHiddenView, enableBodyLock, refreshScrollObserver]); const findItemByValue = useCallback(value => { if (propsRef.current.skipKeysearch) { return undefined; } let index = -1; try { value = String(value).toLowerCase(); if (changedOrderForRef.current !== value) { searchCacheRef.current = null; changedOrderForRef.current = null; } searchCacheRef.current = searchCacheRef.current || stateRef.current.data.reduce((acc, itemData, i) => { var _context3; const str = String(parseContentTitle(itemData, { separator: ' ', removeNumericOnlyValues: true })); const firstLetter = String(str[0]).toLowerCase(); acc[firstLetter] = acc[firstLetter] || []; _pushInstanceProperty(_context3 = acc[firstLetter]).call(_context3, { i }); return acc; }, {}); const found = searchCacheRef.current[value]; index = found && found[0] && found[0].i > -1 ? found[0].i : -1; if (found && found.length > 1) { _pushInstanceProperty(found).call(found, found.shift()); changedOrderForRef.current = value; } } catch (e) { warn('List could not findItemByValue:', e); } return index; }, []); const getActiveElement = useCallback(() => { var _refUl$current2; return (_refUl$current2 = _refUl.current) === null || _refUl$current2 === void 0 ? void 0 : _refUl$current2.querySelector('li.dnb-drawer-list__option--focus'); }, []); const getSelectedElement = useCallback(() => { var _refUl$current3; return ((_refUl$current3 = _refUl.current) === null || _refUl$current3 === void 0 ? void 0 : _refUl$current3.querySelector('li.dnb-drawer-list__option--selected')) || _refUl.current; }, []); const getItemData = useCallback(element => { const item = parseFloat(element && element.getAttribute('data-item')); return isNaN(item) ? undefined : item; }, []); const getElementGroup = useCallback(element => { var _element$parentElemen; return element !== null && element !== void 0 && (_element$parentElemen = element.parentElement) !== null && _element$parentElemen !== void 0 && _element$parentElemen.classList.contains('dnb-drawer-list__group') ? element.parentElement : null; }, []); const getCurrentSelectedItem = useCallback(() => { const elem = getSelectedElement(); return getItemData(elem); }, [getSelectedElement, getItemData]); const getCurrentActiveItem = useCallback(() => { const elem = getActiveElement(); return getItemData(elem); }, [getActiveElement, getItemData]); const getNextActiveItem = useCallback(() => { var _getElementGroup; const activeElement = getActiveElement(); const elem = (activeElement === null || activeElement === void 0 ? void 0 : activeElement.nextElementSibling) || ((_getElementGroup = getElementGroup(activeElement)) === null || _getElementGroup === void 0 || (_getElementGroup = _getElementGroup.nextElementSibling) === null || _getElementGroup === void 0 ? void 0 : _getElementGroup.querySelector('li.dnb-drawer-list__option.first-of-type')); return getItemData(elem); }, [getActiveElement, getElementGroup, getItemData]); const getPrevActiveItem = useCallback(() => { var _activeElement$previo, _getElementGroup2; const activeElement = getActiveElement(); const elem = (activeElement === null || activeElement === void 0 || (_activeElement$previo = activeElement.previousElementSibling) === null || _activeElement$previo === void 0 ? void 0 : _activeElement$previo.classList.contains('dnb-drawer-list__option')) && (activeElement === null || activeElement === void 0 ? void 0 : activeElement.previousElementSibling) || ((_getElementGroup2 = getElementGroup(activeElement)) === null || _getElementGroup2 === void 0 || (_getElementGroup2 = _getElementGroup2.previousElementSibling) === null || _getElementGroup2 === void 0 ? void 0 : _getElementGroup2.querySelector('li.dnb-drawer-list__option.last-of-type')); return getItemData(elem); }, [getActiveElement, getElementGroup, getItemData]); const getFirstItem = useCallback(() => { var _refUl$current4; const elem = (_refUl$current4 = _refUl.current) === null || _refUl$current4 === void 0 ? void 0 : _refUl$current4.querySelector('li.dnb-drawer-list__option.first-item'); return getItemData(elem); }, [getItemData]); const getLastItem = useCallback(() => { var _refUl$current5; const elem = (_refUl$current5 = _refUl.current) === null || _refUl$current5 === void 0 ? void 0 : _refUl$current5.querySelector('li.dnb-drawer-list__option.last-item'); return getItemData(elem); }, [getItemData]); const getAnchorElem = useCallback(activeElement => { try { return activeElement === null || activeElement === void 0 ? void 0 : activeElement.querySelector('a:first-of-type'); } catch (e) { return null; } }, []); const setActiveState = useCallback(active => { if (typeof document !== 'undefined') { try { if (active) { document.documentElement.setAttribute('data-dnb-drawer-list-active', String(stateRef.current.id)); } else { document.documentElement.removeAttribute('data-dnb-drawer-list-active'); } } catch (e) { warn('DrawerList: Error on set "data-dnb-drawer-list-active" by using element.setAttribute()', e); } } }, []); const scrollToItem = useCallback((activeItem, { scrollTo = true, element = null } = {}) => { clearTimeout(scrollTimeoutRef.current); scrollTimeoutRef.current = setTimeout(() => { if (_refUl.current && parseFloat(activeItem) > -1) { try { const ulElement = _refUl.current; const liElement = element || getActiveElement() || getSelectedElement(); if (liElement) { const top = liElement.offsetTop; if (ulElement.scrollTo) { if (scrollTo === false || window['IS_TEST']) { ulElement.style.scrollBehavior = 'auto'; } ulElement.scrollTo({ top, behavior: scrollTo ? 'smooth' : 'auto' }); if (scrollTo === false) { ulElement.style.scrollBehavior = 'smooth'; } } else if (ulElement.scrollTop) { ulElement.scrollTop = top; } if (!propsRef.current.preventFocus && liElement) { liElement.focus(); dispatchCustomElementEvent({ props: propsRef.current, state: stateRef.current }, 'onOpenFocus', { element: liElement }); } } else { warn('The DrawerList item was not a DOM Element'); } } catch (e) { warn('List could not scroll into element:', e); } } }, 1); }, [getActiveElement, getSelectedElement]); const setActiveItemAndScrollToIt = useCallback((activeItem, { fireSelectEvent = false, scrollTo = true, event = null } = {}) => { mergeState({ activeItem }, () => { if (parseFloat(activeItem) === -1) { var _document$activeEleme; if (((_document$activeEleme = document.activeElement) === null || _document$activeEleme === void 0 ? void 0 : _document$activeEleme.tagName) !== 'INPUT') { var _refUl$current6; (_refUl$current6 = _refUl.current) === null || _refUl$current6 === void 0 || _refUl$current6.focus({ preventScroll: true }); } dispatchCustomElementEvent({ props: propsRef.current, state: stateRef.current }, 'onOpenFocus', { element: _refUl.current }); } else if (parseFloat(activeItem) > -1) { const { selectedItem } = stateRef.current; if (fireSelectEvent) { const attributes = attributesRef.current; const ret = dispatchCustomElementEvent(stateRef.current, 'onSelect', { activeItem, value: getSelectedItemValue(selectedItem, stateRef.current), data: getEventData(activeItem, stateRef.current.data), event, attributes }); if (ret === false) { return; } } if (propsRef.current.noAnimation) { scrollTo = false; } scrollToItem(activeItem, { scrollTo }); } }); }, [mergeState, scrollToItem]); const setWrapperElement = useCallback((wrapperElement = propsRef.current.wrapperElement) => { if (typeof wrapperElement === 'string') { wrapperElement = typeof document !== 'undefined' ? document.querySelector(wrapperElement) : undefined; } if (wrapperElement !== stateRef.current.wrapperElement) { mergeState({ wrapperElement }); } return selfRef.current; }, [mergeState]); const setMetaKey = useCallback(e => { metaRef.current = { cmd: e.metaKey, ctrl: e.ctrlKey, shift: e.shiftKey, alt: e.altKey }; }, []); const onKeyUpHandlerRef = useRef(null); const onKeyDownHandlerRef = useRef(null); const onKeyUpHandler = useCallback(e => { onKeyUpHandlerRef.current(e); }, []); const onKeyDownHandler = useCallback(e => { onKeyDownHandlerRef.current(e); }, []); onKeyUpHandlerRef.current = e => { setMetaKey(e); }; const removeOutsideClickObserver = useCallback(() => { if (outsideClickRef.current) { outsideClickRef.current.remove(); } if (typeof document !== 'undefined') { document.removeEventListener('keydown', onKeyDownHandler, true); document.removeEventListener('keyup', onKeyUpHandler, true); } }, [onKeyDownHandler, onKeyUpHandler]); const setOutsideClickObserver = useCallback(() => { removeOutsideClickObserver(); outsideClickRef.current = detectOutsideClick([stateRef.current.wrapperElement, _refRoot.current, _refUl.current], () => setHiddenFnRef.current({ preventHideFocus: true }), { includedKeys: ['Tab'] }); if (typeof document !== 'undefined') { document.addEventListener('keydown', onKeyDownHandler, true); document.addEventListener('keyup', onKeyUpHandler, true); } }, [removeOutsideClickObserver, onKeyDownHandler, onKeyUpHandler]); const addObservers = useCallback(() => { setDirectionObserver(); setScrollObserver(); setOutsideClickObserver(); }, [setDirectionObserver, setScrollObserver, setOutsideClickObserver]); const removeObservers = useCallback(() => { removeDirectionObserver(); removeScrollObserverFn(); removeOutsideClickObserver(); }, [removeDirectionObserver, removeScrollObserverFn, removeOutsideClickObserver]); const setVisibleFnRef = useRef(null); const setHiddenFnRef = useRef(null); const setVisible = useCallback((args = {}, onStateComplete = null) => { setVisibleFnRef.current(args, onStateComplete); }, []); const setHidden = useCallback((args = {}, onStateComplete = null) => { setHiddenFnRef.current(args, onStateComplete); }, []); setVisibleFnRef.current = (args = {}, onStateComplete = null) => { if (stateRef.current.open && stateRef.current.hidden === false) { if (typeof onStateComplete === 'function') { onStateComplete(true); } return; } clearTimeout(showTimeoutRef.current); clearTimeout(hideTimeoutRef.current); searchCacheRef.current = null; const handleSingleComponentCheck = () => { mergeState({ hidden: false, open: true }); const animationDelayHandler = () => { isOpenRef.current = true; mergeState({ isOpen: true }); if (typeof onStateComplete === 'function') { onStateComplete(true); } setActiveState(true); }; if (propsRef.current.noAnimation) { if (process.env.NODE_ENV === 'test') { animationDelayHandler(); } else { clearTimeout(showTimeoutRef.current); showTimeoutRef.current = setTimeout(animationDelayHandler, 0); } } else { clearTimeout(showTimeoutRef.current); showTimeoutRef.current = setTimeout(animationDelayHandler, DrawerListProvider.blurDelay); } const { selectedItem, activeItem } = stateRef.current; const newActiveItem = parseFloat(selectedItem) > -1 ? selectedItem : activeItem; dispatchCustomElementEvent(stateRef.current, 'onOpen', { ...args, data: getEventData(newActiveItem, stateRef.current.data), attributes: attributesRef.current, ulElement: _refUl.current }); setActiveItemAndScrollToIt(parseFloat(newActiveItem) > -1 ? newActiveItem : -1, { scrollTo: false }); }; if (isOpenRef.current && !propsRef.current.noAnimation) { clearTimeout(hideTimeoutRef.current); hideTimeoutRef.current = setTimeout(handleSingleComponentCheck, DrawerListProvider.blurDelay); } else { handleSingleComponentCheck(); } }; setHiddenFnRef.current = (args = {}, onStateComplete = null) => { if (!stateRef.current.open || propsRef.current.preventClose) { if (typeof onStateComplete === 'function') { onStateComplete(false); } return; } clearTimeout(showTimeoutRef.current); clearTimeout(hideTimeoutRef.current); const { selectedItem, activeItem } = stateRef.current; const res = dispatchCustomElementEvent(stateRef.current, 'onClose', { ...args, data: getEventData(parseFloat(selectedItem) > -1 ? selectedItem : activeItem, stateRef.current.data), attributes: attributesRef.current }); if (res !== false) { mergeState({ open: false }); const delayHandler = () => { removeObservers(); mergeState({ hidden: true, isOpen: false }); if (typeof onStateComplete === 'function') { onStateComplete(false); } isOpenRef.current = false; setActiveState(false); }; if (propsRef.current.noAnimation) { delayHandler(); } else { clearTimeout(hideTimeoutRef.current); hideTimeoutRef.current = setTimeout(delayHandler, DrawerListProvider.blurDelay); } } }; const toggleVisible = useCallback((...args) => { return stateRef.current.open ? setHidden(...args) : setVisible(...args); }, [setHidden, setVisible]); const setDataHandler = useCallback((data, cb = null, { overwriteOriginalData = false } = {}) => { if (!data) { return undefined; } if (typeof data === 'function') { data = getData(data); } data = normalizeData(data); mergeState({ data, originalData: overwriteOriginalData ? data : stateRef.current.originalData }, () => { refreshScrollObserver(); typeof cb === 'function' && cb(data); }); return selfRef.current; }, [mergeState, refreshScrollObserver]); const setStateHandler = useCallback((state, cb = null) => { mergeState({ ...state }, cb); return selfRef.current; }, [mergeState]); const selectItem = useCallback((itemToSelect, { fireSelectEvent = false, event = null, closeOnSelection = false } = {}) => { if (itemToSelect === -1) { itemToSelect = null; } const data = getEventData(itemToSelect, stateRef.current.data) || null; const value = getSelectedItemValue(itemToSelect, stateRef.current); const attributes = attributesRef.current; const attr = { selectedItem: itemToSelect, value, data, event, attributes }; if (data !== null && data !== void 0 && data.disabled) { return false; } const res = dispatchCustomElementEvent(stateRef.current, 'onPreChange', attr); if (res === false) { return res; } if (hasSelectedText()) { const elem = getSelectedTextElement(); const isInput = elem instanceof Element ? getClosestParent('dnb-input', elem) : null; if (!isInput) { return; } } const { keepOpen, preventSelection } = propsRef.current; const doCallOnChange = parseFloat(itemToSelect) > -1 && itemToSelect !== stateRef.current.selectedItem; const onSelectionIsComplete = () => { if (doCallOnChange) { dispatchCustomElementEvent(stateRef.current, 'onChange', attr); } if (fireSelectEvent) { dispatchCustomElementEvent(stateRef.current, 'onSelect', { ...attr, activeItem: itemToSelect }); } if (closeOnSelection && !keepOpen) { setHidden(); } }; if (preventSelection) { onSelectionIsComplete(); } else { mergeState({ selectedItem: itemToSelect, activeItem: itemToSelect }, onSelectionIsComplete); } }, [mergeState, setHidden]); const selectItemAndClose = useCallback((itemToSelect, args = {}) => { args.closeOnSelection = true; return selectItem(itemToSelect, args); }, [selectItem]); onKeyDownHandlerRef.current = e => { const key = e.key; if (/Meta|Alt|Shift|Control/.test(key)) { setMetaKey(e); } dispatchCustomElementEvent(stateRef.current, 'onKeyDown', { event: e, key }); if (propsRef.current.preventClose) { let isSameDrawer = false; try { const ulElem = getClosestParent('dnb-drawer-list__options', document.activeElement); isSameDrawer = ulElem === _refUl.current || (ulElem === null || ulElem === void 0 ? void 0 : ulElem.getAttribute('id')) === stateRef.current.id; } catch (err) { warn(err); } if (!isSameDrawer && key !== 'Tab') { return; } } if (!stateRef.current.isOpen) { return; } if (stateRef.current.ignoreEvents && key !== 'Tab') { return; } let activeItem = parseFloat(stateRef.current.activeItem); if (isNaN(activeItem)) { activeItem = -1; } const total = stateRef.current.data && stateRef.current.data.length - 1; switch (key) { case 'ArrowUp': { var _getPrevActiveItem; e.preventDefault(); activeItem = (_getPrevActiveItem = getPrevActiveItem()) !== null && _getPrevActiveItem !== void 0 ? _getPrevActiveItem : getLastItem(); } break; case 'ArrowDown': { var _getNextActiveItem; e.preventDefault(); activeItem = (_getNextActiveItem = getNextActiveItem()) !== null && _getNextActiveItem !== void 0 ? _getNextActiveItem : getFirstItem(); } break; case 'PageUp': case 'Home': { var _getFirstItem; e.preventDefault(); activeItem = (_getFirstItem = getFirstItem()) !== null && _getFirstItem !== void 0 ? _getFirstItem : 0; } break; case 'PageDown': case 'End': { var _getLastItem; e.preventDefault(); activeItem = (_getLastItem = getLastItem()) !== null && _getLastItem !== void 0 ? _getLastItem : total; } break; case 'Enter': case ' ': { var _getCurrentActiveItem; if (e.target.tagName === 'A') { ; e.target.dispatchEvent(new MouseEvent('click')); setHidden(); return; } activeItem = (_getCurrentActiveItem = getCurrentActiveItem()) !== null && _getCurrentActiveItem !== void 0 ? _getCurrentActiveItem : getCurrentSelectedItem(); if (propsRef.current.skipKeysearch ? activeItem > -1 && key !== ' ' : true) { e.preventDefault(); const result = selectItemAndClose(activeItem, { fireSelectEvent: true, event: e }); if (result === false) { return; } } } break; case 'Escape': { setHidden({ event: e }); e.preventDefault(); e.stopPropagation(); } break; case 'Tab': { if (activeItem > -1) { const activeElement = getActiveElement(); const hasFocusOnElement = Boolean(getAnchorElem(activeElement)); mergeState({ hasFocusOnElement }); if (hasFocusOnElement) { e.stopPropagation(); const currentActiveElement = getClosestParent('dnb-drawer-list__option', document.activeElement); if (currentActiveElement !== activeElement) { const createTabElem = () => { try { const elem = document.createElement('BUTTON'); elem.setAttribute('style', 'opacity:0;position:absolute;'); const focus = () => { prevActiveElement.focus(); elem.removeEventListener('focus', focus); activeElement.removeChild(after); activeElement.removeChild(before); }; elem.addEventListener('focus', focus); return elem; } catch (err) {} return undefined; }; const prevActiveElement = document.activeElement; const after = createTabElem(); const before = createTabElem(); activeElement.focus(); const insertElem = () => { try { activeElement.appendChild(after); activeElement.insertBefore(before, activeElement.firstChild); } catch (err) {} }; if (typeof window.requestAnimationFrame === 'function') { window.requestAnimationFrame(insertElem); } else { insertElem(); } } return; } else if (propsRef.current.preventClose) { activeItem = -1; } } setHidden({ event: e }); } break; default: { const searchIndex = findItemByValue(e.key); if (searchIndex > -1) { activeItem = searchIndex; } } break; } if (activeItem === -1 && _refUl.current && typeof document !== 'undefined') { const ulElem = getClosestParent('dnb-drawer-list__options', document.activeElement); if (ulElem === _refUl.current) { mergeState({ showFocusRing: true, activeItem }); _refUl.current.focus({ preventScroll: true }); dispatchCustomElementEvent(stateRef.current, 'handleDismissFocus'); } } else if (activeItem > -1 && activeItem !== stateRef.current.activeItem) { mergeState({ showFocusRing: false }); setActiveItemAndScrollToIt(activeItem, { fireSelectEvent: true, event: e }); } }; useMountEffect(() => { if (propsRef.current.open) { setVisible(); } return () => { clearTimeout(showTimeoutRef.current); clearTimeout(hideTimeoutRef.current); clearTimeout(scrollTimeoutRef.current); clearTimeout(directionTimeoutRef.current); isOpenRef.current = false; removeObservers(); setActiveState(false); }; }); useUpdateEffect(() => { if (props.open !== null) { if (props.open) { setVisible(); } else if (props.open === false) { setHidden(); } } }, [props.open, setVisible, setHidden]); const prevDataRef = useRef(props.data); const prevDirectionRef = useRef(stateRef.current.direction); useEffect(() => { if (stateRef.current.open) { var _document$activeEleme2; if (props.data !== prevDataRef.current && typeof document !== 'undefined' && ((_document$activeEleme2 = document.activeElement) === null || _document$activeEleme2 === void 0 ? void 0 : _document$activeEleme2.tagName) === 'BODY') { var _refUl$current7; (_refUl$current7 = _refUl.current) === null || _refUl$current7 === void 0 || _refUl$current7.focus(); } if (stateRef.current.direction !== prevDirectionRef.current || props.data !== prevDataRef.current) { var _window2, _window2$requestAnima; (_window2 = window) === null || _window2 === void 0 || (_window2$requestAnima = _window2.requestAnimationFrame) === null || _window2$requestAnima === void 0 || _window2$requestAnima.call(_window2, () => { var _setOnScrollRef$curre; refreshScrollObserver(); (_setOnScrollRef$curre = setOnScrollRef.current) === null || _setOnScrollRef$curre === void 0 || _setOnScrollRef$curre.call(setOnScrollRef); }); } } prevDataRef.current = props.data; prevDirectionRef.current = stateRef.current.direction; }); const selfRef = useRef(null); selfRef.current = { setVisible, setHidden, toggleVisible, setWrapperElement, setData: setDataHandler, setState: setStateHandler, selectItem, selectItemAndClose, scrollToItem, setActiveItemAndScrollToIt, addObservers, removeObservers }; return _jsx(DrawerListContext, { value: { ...context, drawerList: { attributes: attributesRef.current, _refRoot, _refShell, _refUl, _refTriangle, _rootElem: rootElemRef.current, setData: setDataHandler, setState: setStateHandler, setWrapperElement, addObservers, removeObservers, setVisible, setHidden, toggleVisible, selectItem, selectItemAndClose, scrollToItem, setActiveItemAndScrollToIt, ...stateRef.current } }, children: props.children }); } DrawerListProviderComponent.displayName = 'DrawerListProvider'; const DrawerListProvider = React.memo(DrawerListProviderComponent); DrawerListProvider.blurDelay = 201; export default DrawerListProvider; //# sourceMappingURL=DrawerListProvider.js.map