@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
367 lines (366 loc) • 11.7 kB
JavaScript
"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