import { createContext, ReactNode, useCallback, useContext } from 'react'; import { useValueControl } from '@snack-uikit/utils'; import { AnyType, ItemId } from '../../Items'; export type OnChangeHandler = (value: T) => void; export type SelectionSingleValueType = ItemId; export type SelectionSingleState = { /** Начальное состояние */ defaultValue?: ItemId; /** Controlled состояние */ value?: ItemId; /** Controlled обработчик измения состояния */ onChange?: OnChangeHandler; /** Режим выбора */ mode: 'single'; }; export type SelectionSingleProps = { setValue?(value: AnyType): void; /** Режим выбора single */ isSelectionSingle: true; /** Режим выбора multi */ isSelectionMultiple: false; } & SelectionSingleState; export type SelectionMultipleState = { /** Начальное состояние */ defaultValue?: ItemId[]; /** Controlled состояние */ value?: ItemId[]; /** Controlled обработчик измения состояния */ onChange?: OnChangeHandler; /** Режим выбора */ mode: 'multiple'; }; export type SelectionMultipleProps = { setValue?(value: AnyType): void; /** Режим выбора single */ isSelectionSingle: false; /** Режим выбора multi */ isSelectionMultiple: true; } & SelectionMultipleState; type SelectionNoneProps = { mode?: 'none'; value?: undefined; onChange?: undefined; setValue?: undefined; defaultValue?: undefined; isSelectionSingle?: undefined; isSelectionMultiple?: undefined; }; type SelectionContextType = | Omit | Omit | Omit; export type SelectionState = { selection?: SelectionSingleState | SelectionMultipleState; }; export const SelectionContext = createContext({ value: undefined, onChange: undefined, mode: undefined, }); export function isSelectionMultipleProps(props: AnyType): props is SelectionMultipleProps { return 'mode' in props && props['mode'] === 'multiple'; } export function isSelectionSingleProps(props: AnyType): props is SelectionSingleProps { return 'mode' in props && props['mode'] === 'single'; } type Child = { children: ReactNode; }; function SelectionNoneProvider({ children }: SelectionNoneProps & Child) { return ( {children} ); } function SelectionSingleProvider({ value: valueProp, defaultValue, onChange: onChangeProp, children, }: SelectionSingleProps & Child) { const [value, setValue] = useValueControl({ value: valueProp, defaultValue, onChange: onChangeProp, }); const onChange = useCallback( (newValue: SelectionSingleValueType | undefined) => setValue((oldValue: SelectionSingleValueType) => { if (newValue !== oldValue) { return newValue; } return undefined; }), [setValue], ); return ( {children} ); } function SelectionMultipleProvider({ value: valueProp, defaultValue, onChange: onChangeProp, children, }: SelectionMultipleProps & Child) { const [value, setValue] = useValueControl({ value: valueProp, defaultValue, onChange: onChangeProp, }); const onChange = useCallback( (newValue: SelectionSingleValueType) => { setValue((oldValues: SelectionSingleValueType[]) => { if (Array.isArray(oldValues)) { if (oldValues.includes(newValue)) { return oldValues.filter(oldValue => oldValue !== newValue); } return oldValues.concat(newValue); } if (oldValues === undefined) { return Array.isArray(newValue) ? newValue : [newValue]; } return undefined; }); }, [setValue], ); return ( {children} ); } type SelectionProviderProps = { /** Начальное состояние */ defaultValue?: AnyType; /** Controlled состояние */ value?: AnyType; /** Controlled обработчик измения состояния */ onChange?: OnChangeHandler; /** Режим выбора */ mode?: 'multiple' | 'single' | 'none'; }; export function SelectionProvider({ children, ...props }: SelectionProviderProps & Child) { if (isSelectionSingleProps(props)) { return {children}; } if (isSelectionMultipleProps(props)) { return {children}; } return {children}; } export function useSelectionContext() { return useContext(SelectionContext); }