import React, { createContext, useContext, useCallback, useMemo, RefObject, ReactNode, } from 'react'; import { ButtonProps } from '../../Button/Button'; import { FilterValues } from './types'; import { isDefaultValue } from './utils'; import { noop } from '../../../fp/function'; export type FiltersContextType = { applyButtonProps?: ButtonProps; clearButtonProps?: Omit; isGrouped: boolean; onSave?: (values: FilterValues) => void; onValuesChange: (values: FilterValues) => void; selectedTagsRef: RefObject; values: FilterValues; }; const FiltersContext = createContext({ values: {}, onValuesChange: noop, isGrouped: false, selectedTagsRef: { current: null }, }); export const FiltersProvider = ({ value, children, }: { children: ReactNode; value: Omit & { onSave?: (v: T) => void; onValuesChange: (v: T) => void; }; }) => { const { onValuesChange, onSave } = value; const retypedOnValuesChange = useCallback( (v: FilterValues) => { onValuesChange(v as T); }, [onValuesChange] ); const retypedOnSave = useCallback((v: FilterValues) => onSave?.(v as T), [ onSave, ]); const providerValue = useMemo( () => ({ ...value, onValuesChange: retypedOnValuesChange, onSave: retypedOnSave, }), [value, retypedOnValuesChange, retypedOnSave] ); return ( {children} ); }; export const useFilterState = ( key: K ) => { const { values, onValuesChange, onSave } = useContext(FiltersContext); const value = (values as T)[key]; const onChange = useCallback( (newValue: T[K]) => onValuesChange({ ...values, [key]: newValue, }), [key, values, onValuesChange] ); const changeAndSave = useCallback( (newValue: T[K]) => { onChange(newValue); onSave?.({ [key]: newValue, }); }, [key, onChange, onSave] ); return [value, onChange, changeAndSave] as const; }; export const useChangeGroupFilter = ( keys: K[] ) => { const { values, onValuesChange, onSave } = useContext(FiltersContext); const getNewFilterValues = useCallback( (newValues: Array) => keys.reduce>( (filterValuesObj, key, i) => ({ ...filterValuesObj, [key]: newValues[i], }), {} ), [keys] ); const onChange = useCallback( (newValues: Array) => onValuesChange({ ...values, ...getNewFilterValues(newValues), }), [getNewFilterValues, onValuesChange, values] ); const changeAndSaveGroup = useCallback( (newValues: Array) => { onChange(newValues); onSave?.(getNewFilterValues(newValues)); }, [getNewFilterValues, onChange, onSave] ); return [changeAndSaveGroup] as const; }; export const useDirtyCheck = ( key: K ) => { const { values } = useContext(FiltersContext); const value = (values as T)[key]; return !isDefaultValue(value); }; export const useGroupDirtyCheck = ( keys: K[] ) => { const { values } = useContext(FiltersContext); const isGroupDirty = useMemo( () => keys.reduce( (current, key) => current || !isDefaultValue((values as T)[key]), false ), [values, keys] ); return isGroupDirty; }; export default FiltersContext;