UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

367 lines (366 loc) 11.7 kB
"use strict"; "use client"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _hasOwn = _interopRequireDefault(require("core-js-pure/stable/object/has-own.js")); var _react = require("react"); var _clsx = require("clsx"); var _useSharedState = require("../../shared/helpers/useSharedState.js"); var _Context = _interopRequireDefault(require("../../shared/Context.js")); var _FilterContext = require("./FilterContext.js"); var _SpacingUtils = require("../space/SpacingUtils.js"); var _withComponentMarkers = _interopRequireDefault(require("../../shared/helpers/withComponentMarkers.js")); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const initialState = { search: '', filters: {} }; function toChangeState({ search, filters }) { return { search, filters }; } function deriveAccordionState(filters) { const keys = new Set(); for (const key of Object.keys(filters)) { const lastSlash = key.lastIndexOf('/'); if (lastSlash > 0) { keys.add(key.slice(0, lastSlash)); } } const state = {}; for (const k of Array.from(keys)) { state[k] = true; } return state; } function FilterRoot({ id, behavior = 'realtime', defaultFilters, defaultPanelOpen, resultCount, resultLoading, onChange, className, style, children, ...spacingRest }) { const sharedContext = (0, _react.useContext)(_Context.default); const { ariaLabel } = sharedContext.getTranslation({}).Filter; const onChangeRef = (0, _react.useRef)(onChange); onChangeRef.current = onChange; const hasDefaultFilters = defaultFilters && Object.keys(defaultFilters).length > 0; const defaultAccordionState = (0, _react.useMemo)(() => { if (!hasDefaultFilters) { return {}; } return deriveAccordionState(defaultFilters); }, []); const accordionStateId = id ? `${id}__accordion` : undefined; const { data: savedAccordionState, extend: extendAccordionState } = (0, _useSharedState.useSharedState)(accordionStateId, defaultAccordionState); const accordionStateRef = (0, _react.useRef)(savedAccordionState !== null && savedAccordionState !== void 0 ? savedAccordionState : defaultAccordionState); const defaultState = (0, _react.useMemo)(() => defaultFilters ? { search: '', filters: defaultFilters } : initialState, [defaultFilters]); const { data, get, extend } = (0, _useSharedState.useSharedState)(id, defaultState); const isManual = behavior === 'manual'; const [localState, setLocalState] = (0, _react.useState)(defaultState); const committedState = id ? data !== null && data !== void 0 ? data : defaultState : localState; const committedStateRef = (0, _react.useRef)(committedState); committedStateRef.current = committedState; const getCommittedState = (0, _react.useCallback)(() => { var _get; return id ? (_get = get()) !== null && _get !== void 0 ? _get : defaultState : committedStateRef.current; }, [defaultState, get, id]); const updateCommittedState = (0, _react.useCallback)(nextState => { if (id) { extend(nextState); } else { const next = { ...committedStateRef.current, ...nextState }; committedStateRef.current = next; setLocalState(next); } }, [extend, id]); const [draftState, setDraftState] = (0, _react.useState)(() => data !== null && data !== void 0 ? data : defaultState); const draftRef = (0, _react.useRef)(draftState); draftRef.current = draftState; (0, _react.useEffect)(() => { if (hasDefaultFilters) { updateCommittedState({ filters: defaultFilters }); } }, []); (0, _react.useEffect)(() => { const updates = {}; if (resultLoading !== undefined) { updates.resultLoading = resultLoading; } if (resultCount !== undefined) { updates.resultCount = resultCount; } if (Object.keys(updates).length > 0) { updateCommittedState(updates); } }, [resultLoading, resultCount, updateCommittedState]); const state = isManual ? draftState : committedState; const setSearch = (0, _react.useCallback)(search => { if (isManual) { const filters = getCommittedState().filters; setDraftState(prev => ({ ...prev, search })); updateCommittedState({ search }); onChangeRef.current?.({ search, filters }); } else { updateCommittedState({ search }); const filters = getCommittedState().filters; onChangeRef.current?.({ search, filters }); } }, [getCommittedState, isManual, updateCommittedState]); const setFilter = (0, _react.useCallback)((filterKey, value) => { if (isManual) { setDraftState(prev => { const next = { ...prev.filters }; if (value === undefined) { delete next[filterKey]; } else { next[filterKey] = value; } return { ...prev, filters: next }; }); } else { const latest = getCommittedState(); const next = { ...latest.filters }; if (value === undefined) { delete next[filterKey]; } else { next[filterKey] = value; } updateCommittedState({ filters: next }); onChangeRef.current?.(toChangeState({ ...latest, filters: next })); } }, [getCommittedState, isManual, updateCommittedState]); const getFilter = (0, _react.useCallback)(filterKey => { return state.filters[filterKey]; }, [state.filters]); const submitFilterRemoval = (0, _react.useCallback)(filterKey => { const committed = getCommittedState(); const nextAppliedFilters = { ...committed.filters }; delete nextAppliedFilters[filterKey]; const draft = draftRef.current; const nextDraftFilters = { ...draft.filters }; delete nextDraftFilters[filterKey]; const nextDraftState = { ...draft, filters: nextDraftFilters }; const nextAppliedState = { search: committed.search, filters: nextAppliedFilters }; setDraftState(nextDraftState); updateCommittedState(nextAppliedState); onChangeRef.current?.(toChangeState(nextAppliedState)); }, [getCommittedState, updateCommittedState]); const removeFilter = (0, _react.useCallback)(filterKey => { if (isManual) { const appliedFilters = getCommittedState().filters; if ((0, _hasOwn.default)(appliedFilters, filterKey)) { submitFilterRemoval(filterKey); } else { setFilter(filterKey, undefined); } } else { setFilter(filterKey, undefined); } }, [getCommittedState, isManual, setFilter, submitFilterRemoval]); const removeAppliedFilter = (0, _react.useCallback)(filterKey => { if (isManual) { submitFilterRemoval(filterKey); } else { removeFilter(filterKey); } }, [isManual, removeFilter, submitFilterRemoval]); const clearFilters = (0, _react.useCallback)(() => { const latest = isManual ? draftRef.current : getCommittedState(); const nextState = { ...latest, filters: {} }; if (isManual) { setDraftState(nextState); } updateCommittedState({ search: nextState.search, filters: nextState.filters }); onChangeRef.current?.(toChangeState(nextState)); }, [getCommittedState, isManual, updateCommittedState]); const replaceFilters = (0, _react.useCallback)(filters => { if (isManual) { setDraftState(prev => ({ ...prev, filters })); } else { updateCommittedState({ filters }); const latest = getCommittedState(); onChangeRef.current?.(toChangeState({ ...latest, filters })); } }, [getCommittedState, isManual, updateCommittedState]); const resetFilters = (0, _react.useCallback)(() => { const next = { search: '', filters: {} }; if (isManual) { setDraftState(next); } updateCommittedState(next); onChangeRef.current?.(next); }, [isManual, updateCommittedState]); const commitFilters = (0, _react.useCallback)(() => { const draft = draftRef.current; updateCommittedState({ search: draft.search, filters: draft.filters }); onChangeRef.current?.(toChangeState(draft)); }, [updateCommittedState]); const revertFilters = (0, _react.useCallback)(() => { const committed = getCommittedState(); setDraftState({ search: committed.search, filters: committed.filters }); }, [getCommittedState]); const hasActiveFilters = state.search.length > 0 || Object.keys(state.filters).length > 0; const [panelOpen, setPanelOpen] = (0, _react.useState)(defaultPanelOpen !== null && defaultPanelOpen !== void 0 ? defaultPanelOpen : !!hasDefaultFilters); const panelButtonRef = (0, _react.useRef)(null); (0, _react.useEffect)(() => { var _current$filters; if (defaultPanelOpen !== undefined || hasDefaultFilters) { return; } const current = getCommittedState(); const filters = (_current$filters = current?.filters) !== null && _current$filters !== void 0 ? _current$filters : {}; if (Object.keys(filters).length > 0) { setPanelOpen(true); const accordionState = deriveAccordionState(filters); accordionStateRef.current = { ...accordionStateRef.current, ...accordionState }; extendAccordionState(accordionState); } }, []); const getAccordionOpen = (0, _react.useCallback)(filterKey => { return accordionStateRef.current[filterKey]; }, []); const setAccordionOpen = (0, _react.useCallback)((filterKey, open) => { accordionStateRef.current[filterKey] = open; extendAccordionState({ [filterKey]: open }); }, [extendAccordionState]); const appliedState = committedState; const resolvedResultCount = resultCount !== null && resultCount !== void 0 ? resultCount : committedState.resultCount; const resolvedResultLoading = resultLoading !== null && resultLoading !== void 0 ? resultLoading : committedState.resultLoading; const contextValue = (0, _react.useMemo)(() => ({ id, behavior, state, appliedState, setSearch, setFilter, getFilter, removeFilter, removeAppliedFilter, clearFilters, replaceFilters, resetFilters, commitFilters, revertFilters, hasActiveFilters, resultCount: resolvedResultCount, resultLoading: resolvedResultLoading, panelOpen, setPanelOpen, panelButtonRef, getAccordionOpen, setAccordionOpen }), [id, behavior, state, appliedState, setSearch, setFilter, getFilter, removeFilter, removeAppliedFilter, clearFilters, replaceFilters, resetFilters, commitFilters, revertFilters, hasActiveFilters, resolvedResultCount, resolvedResultLoading, panelOpen, setPanelOpen, panelButtonRef, getAccordionOpen, setAccordionOpen]); const spacingProps = (0, _SpacingUtils.useSpacing)(spacingRest, { className: (0, _clsx.clsx)('dnb-filter', className), style }); return (0, _jsxRuntime.jsx)(_FilterContext.FilterContext, { value: contextValue, children: (0, _jsxRuntime.jsx)("div", { ...spacingProps, role: "search", "aria-label": ariaLabel, children: children }) }); } (0, _withComponentMarkers.default)(FilterRoot, { _supportsSpacingProps: true }); var _default = exports.default = FilterRoot; //# sourceMappingURL=FilterRoot.js.map