@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
1,046 lines (1,045 loc) • 39 kB
JavaScript
"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