@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
126 lines (125 loc) • 4.05 kB
JavaScript
"use client";
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
import { useSharedState } from "../../../shared/helpers/useSharedState.js";
import { debounceAsync } from "../../../shared/helpers/debounce.js";
import { FilterContext } from "../FilterContext.js";
const emptyState = {
search: '',
filters: {}
};
export function useFilter(id) {
const {
data,
get,
extend
} = useSharedState(id, emptyState);
const state = data !== null && data !== void 0 ? data : emptyState;
const resetFilters = useCallback(() => {
extend({
search: '',
filters: {}
});
}, [extend]);
const removeFilter = useCallback(filterKey => {
var _get;
const latest = (_get = get()) !== null && _get !== void 0 ? _get : emptyState;
const next = {
...latest.filters
};
delete next[filterKey];
extend({
filters: next
});
}, [extend, get]);
return {
filters: state.filters,
search: state.search,
hasActiveFilters: state.search.length > 0 || Object.keys(state.filters).length > 0,
resetFilters,
removeFilter
};
}
export function useFilterContext() {
const context = useContext(FilterContext);
if (!context) {
throw new Error('Filter.useFilterContext() must be used inside a Filter.Root.');
}
return {
setFilter: context.setFilter,
getFilter: context.getFilter,
removeFilter: context.removeFilter,
resetFilters: context.resetFilters,
commitFilters: context.commitFilters,
revertFilters: context.revertFilters,
filters: context.state.filters,
search: context.state.search,
hasActiveFilters: context.hasActiveFilters
};
}
export function useFilterAsync(id, fetcher, options) {
var _options$debounce, _state$resultLoading;
const {
data: filterData,
extend
} = useSharedState(id, emptyState);
const state = filterData !== null && filterData !== void 0 ? filterData : emptyState;
const [result, setResult] = useState(options?.initialData);
const [error, setError] = useState(undefined);
const requestRef = useRef(0);
const fetcherRef = useRef(fetcher);
fetcherRef.current = fetcher;
const initialDataRef = useRef(options?.initialData);
initialDataRef.current = options?.initialData;
const debounceMs = (_options$debounce = options?.debounce) !== null && _options$debounce !== void 0 ? _options$debounce : 0;
const debouncedFetcherRef = useRef(undefined);
if (debounceMs > 0 && !debouncedFetcherRef.current) {
debouncedFetcherRef.current = debounceAsync(params => fetcherRef.current(params), debounceMs);
}
const filtersKey = JSON.stringify(state.filters);
const search = state.search;
useEffect(() => {
const filters = JSON.parse(filtersKey);
const requestId = ++requestRef.current;
const isFirstRun = requestId === 1;
let cancelled = false;
if (!isFirstRun || !initialDataRef.current) {
extend({
resultLoading: true
});
}
setError(undefined);
const shouldDebounce = debounceMs > 0 && !isFirstRun;
const fetchFn = shouldDebounce ? debouncedFetcherRef.current : fetcherRef.current;
fetchFn({
filters,
search
}).then(data => {
if (!cancelled && requestId === requestRef.current) {
setResult(data);
extend({
resultLoading: false,
resultCount: Array.isArray(data) ? data.length : undefined
});
}
}).catch(err => {
if (!cancelled && requestId === requestRef.current) {
setError(err instanceof Error ? err : new Error(String(err)));
extend({
resultLoading: false
});
}
});
return () => {
cancelled = true;
if (shouldDebounce) {
debouncedFetcherRef.current?.cancel();
}
};
}, [filtersKey, search, extend, debounceMs]);
return {
data: result,
loading: (_state$resultLoading = state.resultLoading) !== null && _state$resultLoading !== void 0 ? _state$resultLoading : false,
error
};
}
//# sourceMappingURL=useFilter.js.map