@hh.ru/magritte-ui-new-select 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/Select.d.ts +9 -0
  2. package/Select.js +32 -0
  3. package/Select.js.map +1 -0
  4. package/dataProviderTypes.d.ts +34 -0
  5. package/dataProviderTypes.js +2 -0
  6. package/dataProviderTypes.js.map +1 -0
  7. package/index.d.ts +1 -0
  8. package/index.js +16 -0
  9. package/index.js.map +1 -0
  10. package/internal/ConfigContext.d.ts +16 -0
  11. package/internal/ConfigContext.js +25 -0
  12. package/internal/ConfigContext.js.map +1 -0
  13. package/internal/NavigationContext.d.ts +17 -0
  14. package/internal/NavigationContext.js +30 -0
  15. package/internal/NavigationContext.js.map +1 -0
  16. package/internal/SelectDataProvider.d.ts +18 -0
  17. package/internal/SelectDataProvider.js +39 -0
  18. package/internal/SelectDataProvider.js.map +1 -0
  19. package/internal/SelectStateContext.d.ts +13 -0
  20. package/internal/SelectStateContext.js +20 -0
  21. package/internal/SelectStateContext.js.map +1 -0
  22. package/internal/VisibilityContext.d.ts +13 -0
  23. package/internal/VisibilityContext.js +21 -0
  24. package/internal/VisibilityContext.js.map +1 -0
  25. package/internal/slots.d.ts +11 -0
  26. package/internal/slots.js +7 -0
  27. package/internal/slots.js.map +1 -0
  28. package/internal/useSearch.d.ts +8 -0
  29. package/internal/useSearch.js +107 -0
  30. package/internal/useSearch.js.map +1 -0
  31. package/internal/useSelectState.d.ts +16 -0
  32. package/internal/useSelectState.js +145 -0
  33. package/internal/useSelectState.js.map +1 -0
  34. package/internal/utils.d.ts +7 -0
  35. package/internal/utils.js +42 -0
  36. package/internal/utils.js.map +1 -0
  37. package/package.json +44 -0
  38. package/types.d.ts +55 -0
  39. package/types.js +2 -0
  40. package/types.js.map +1 -0
package/Select.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { ReactElement } from 'react';
2
+ import { StaticDataFetcherItem } from '@hh.ru/magritte-common-data-provider';
3
+ import { SelectProps } from '@hh.ru/magritte-ui-new-select/types';
4
+ export interface SelectComponents {
5
+ Trigger?: ReactElement;
6
+ }
7
+ declare const SelectRoot: <MultipleType extends boolean, DataType extends StaticDataFetcherItem>({ children, commitModel, dataProvider, error, errorTrls, hints, maxSelectedOptions, multiple, name, loading, onBlur: onBlurExternal, onChange: onChangeExternal, onFocus: onFocusExternal, onOptionsChange: onOptionsChangeExternal, onSelectOption: onSelectOptionExternal, options, value, }: SelectProps<MultipleType, DataType>) => ReactElement;
8
+ declare const Select: typeof SelectRoot & SelectComponents;
9
+ export { Select };
package/Select.js ADDED
@@ -0,0 +1,32 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useState } from 'react';
3
+ import { ConfigProvider } from './internal/ConfigContext.js';
4
+ import { NavigationProvider } from './internal/NavigationContext.js';
5
+ import { SelectDataProvider } from './internal/SelectDataProvider.js';
6
+ import { SelectStateProvider } from './internal/SelectStateContext.js';
7
+ import { VisibilityProvider } from './internal/VisibilityContext.js';
8
+ import { SelectElementsSlotRegistryProvider } from './internal/slots.js';
9
+ import { useEventCallback } from './internal/utils.js';
10
+ import '@hh.ru/magritte-common-data-provider';
11
+ import './internal/useSearch.js';
12
+ import './internal/useSelectState.js';
13
+ import '@hh.ru/magritte-internal-slot-registry';
14
+ import '@hh.ru/magritte-common-keyboard';
15
+
16
+ const SelectRoot = ({ children, commitModel = 'instant', dataProvider, error, errorTrls, hints, maxSelectedOptions,
17
+ // TODO commitModel === multiple?
18
+ multiple, name, loading, onBlur: onBlurExternal, onChange: onChangeExternal, onFocus: onFocusExternal, onOptionsChange: onOptionsChangeExternal, onSelectOption: onSelectOptionExternal, options, value, }) => {
19
+ const onBlur = useEventCallback(onBlurExternal);
20
+ const onFocus = useEventCallback(onFocusExternal);
21
+ const onChange = useEventCallback(onChangeExternal);
22
+ const onOptionsChange = useEventCallback(onOptionsChangeExternal);
23
+ const onSelectOption = useEventCallback(onSelectOptionExternal);
24
+ const [showOptions, setShowOptions] = useState(false);
25
+ // forcedState - это отложенная команда фокуса
26
+ const [forcedState, setForcedState] = useState(null);
27
+ return (jsx(SelectElementsSlotRegistryProvider, { children: jsx(ConfigProvider, { maxSelectedOptions: maxSelectedOptions, multiple: multiple, name: name, onBlur: onBlur, onFocus: onFocus, commitModel: commitModel, loading: loading, children: jsx(VisibilityProvider, { showOptions: showOptions, setShowOptions: setShowOptions, children: jsx(SelectStateProvider, { onChange: onChange, onSelectOption: onSelectOption, value: value, children: jsx(SelectDataProvider, { dataProvider: dataProvider, error: error, errorTrls: errorTrls, hints: hints, options: options, onOptionsChange: onOptionsChange, children: jsx(NavigationProvider, { forcedState: forcedState, setForcedState: setForcedState, children: children }) }) }) }) }) }));
28
+ };
29
+ const Select = SelectRoot;
30
+
31
+ export { Select };
32
+ //# sourceMappingURL=Select.js.map
package/Select.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Select.js","sources":["src/Select.tsx"],"sourcesContent":["import { ReactElement, useState } from 'react';\n\nimport { StaticDataFetcherItem } from '@hh.ru/magritte-common-data-provider';\n// import { ApplyChangesButton } from '@hh.ru/magritte-ui-new-select/ApplyChangesButton';\n// import { ClearButton } from '@hh.ru/magritte-ui-new-select/ClearButton';\n// import { CloseBottomSheet } from '@hh.ru/magritte-ui-new-select/CloseBottomSheet';\n// import { Footer } from '@hh.ru/magritte-ui-new-select/Footer';\n// import { Header } from '@hh.ru/magritte-ui-new-select/Header';\n// import { Options } from '@hh.ru/magritte-ui-new-select/Options';\n// import { SearchInput } from '@hh.ru/magritte-ui-new-select/SearchInput';\n// import { SelectBottomSheet } from '@hh.ru/magritte-ui-new-select/SelectBottomSheet';\n// import { SelectDrop } from '@hh.ru/magritte-ui-new-select/SelectDrop';\n// import { SelectTrigger } from '@hh.ru/magritte-ui-new-select/SelectTrigger';\nimport { ConfigProvider } from '@hh.ru/magritte-ui-new-select/internal/ConfigContext';\nimport { NavigationProvider, FocusStateType } from '@hh.ru/magritte-ui-new-select/internal/NavigationContext';\nimport { SelectDataProvider } from '@hh.ru/magritte-ui-new-select/internal/SelectDataProvider';\nimport { SelectStateProvider } from '@hh.ru/magritte-ui-new-select/internal/SelectStateContext';\nimport { VisibilityProvider } from '@hh.ru/magritte-ui-new-select/internal/VisibilityContext';\nimport { SelectElementsSlotRegistryProvider } from '@hh.ru/magritte-ui-new-select/internal/slots';\nimport { useEventCallback } from '@hh.ru/magritte-ui-new-select/internal/utils';\nimport { SelectProps } from '@hh.ru/magritte-ui-new-select/types';\n\nexport interface SelectComponents {\n Trigger?: ReactElement;\n // Trigger: typeof SelectTrigger;\n // Options: typeof Options;\n // Drop: typeof SelectDrop;\n // BottomSheet: typeof SelectBottomSheet;\n // SearchInput: typeof SearchInput;\n // ApplyChangesButton: typeof ApplyChangesButton;\n // ClearButton: typeof ClearButton;\n // Footer: typeof Footer;\n // Header: typeof Header;\n // CloseBottomSheet: typeof CloseBottomSheet;\n}\n\nconst SelectRoot = <MultipleType extends boolean, DataType extends StaticDataFetcherItem>({\n children,\n commitModel = 'instant',\n dataProvider,\n error,\n errorTrls,\n hints,\n maxSelectedOptions,\n // TODO commitModel === multiple?\n multiple,\n name,\n loading,\n onBlur: onBlurExternal,\n onChange: onChangeExternal,\n onFocus: onFocusExternal,\n onOptionsChange: onOptionsChangeExternal,\n onSelectOption: onSelectOptionExternal,\n options,\n value,\n}: SelectProps<MultipleType, DataType>): ReactElement => {\n const onBlur = useEventCallback(onBlurExternal);\n const onFocus = useEventCallback(onFocusExternal);\n const onChange = useEventCallback(onChangeExternal);\n const onOptionsChange = useEventCallback(onOptionsChangeExternal);\n const onSelectOption = useEventCallback(onSelectOptionExternal);\n const [showOptions, setShowOptions] = useState(false);\n // forcedState - это отложенная команда фокуса\n const [forcedState, setForcedState] = useState<FocusStateType>(null);\n\n return (\n <SelectElementsSlotRegistryProvider>\n <ConfigProvider\n maxSelectedOptions={maxSelectedOptions}\n multiple={multiple}\n name={name}\n onBlur={onBlur}\n onFocus={onFocus}\n commitModel={commitModel}\n loading={loading}\n >\n <VisibilityProvider showOptions={showOptions} setShowOptions={setShowOptions}>\n <SelectStateProvider<MultipleType, DataType>\n onChange={onChange}\n onSelectOption={onSelectOption}\n value={value}\n >\n <SelectDataProvider<DataType>\n dataProvider={dataProvider}\n error={error}\n errorTrls={errorTrls}\n hints={hints}\n options={options}\n onOptionsChange={onOptionsChange}\n >\n <NavigationProvider forcedState={forcedState} setForcedState={setForcedState}>\n {children}\n </NavigationProvider>\n </SelectDataProvider>\n </SelectStateProvider>\n </VisibilityProvider>\n </ConfigProvider>\n </SelectElementsSlotRegistryProvider>\n );\n};\n\nconst Select = SelectRoot as typeof SelectRoot & SelectComponents;\n\n// Select.Trigger = SelectTrigger;\n// Select.Options = Options;\n// Select.Drop = SelectDrop;\n// Select.BottomSheet = SelectBottomSheet;\n// Select.SearchInput = SearchInput;\n// Select.ApplyChangesButton = ApplyChangesButton;\n// Select.Footer = Footer;\n// Select.ClearButton = ClearButton;\n// Select.Header = Header;\n// Select.CloseBottomSheet = CloseBottomSheet;\n\nexport { Select };\n"],"names":["_jsx"],"mappings":";;;;;;;;;;;;;;;AAoCA,MAAM,UAAU,GAAG,CAAuE,EACtF,QAAQ,EACR,WAAW,GAAG,SAAS,EACvB,YAAY,EACZ,KAAK,EACL,SAAS,EACT,KAAK,EACL,kBAAkB;AAClB;AACA,QAAQ,EACR,IAAI,EACJ,OAAO,EACP,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,eAAe,EACxB,eAAe,EAAE,uBAAuB,EACxC,cAAc,EAAE,sBAAsB,EACtC,OAAO,EACP,KAAK,GAC6B,KAAkB;AACpD,IAAA,MAAM,MAAM,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAAC;AAChD,IAAA,MAAM,OAAO,GAAG,gBAAgB,CAAC,eAAe,CAAC,CAAC;AAClD,IAAA,MAAM,QAAQ,GAAG,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;AACpD,IAAA,MAAM,eAAe,GAAG,gBAAgB,CAAC,uBAAuB,CAAC,CAAC;AAClE,IAAA,MAAM,cAAc,GAAG,gBAAgB,CAAC,sBAAsB,CAAC,CAAC;IAChE,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;;IAEtD,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAiB,IAAI,CAAC,CAAC;IAErE,QACIA,IAAC,kCAAkC,EAAA,EAAA,QAAA,EAC/BA,IAAC,cAAc,EAAA,EACX,kBAAkB,EAAE,kBAAkB,EACtC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,OAAO,EAAA,QAAA,EAEhBA,GAAC,CAAA,kBAAkB,EAAC,EAAA,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,cAAc,EAAA,QAAA,EACxEA,IAAC,mBAAmB,EAAA,EAChB,QAAQ,EAAE,QAAQ,EAClB,cAAc,EAAE,cAAc,EAC9B,KAAK,EAAE,KAAK,YAEZA,GAAC,CAAA,kBAAkB,EACf,EAAA,YAAY,EAAE,YAAY,EAC1B,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,EAChB,eAAe,EAAE,eAAe,EAAA,QAAA,EAEhCA,IAAC,kBAAkB,EAAA,EAAC,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,cAAc,EACvE,QAAA,EAAA,QAAQ,EACQ,CAAA,EAAA,CACJ,GACH,EACL,CAAA,EAAA,CACR,EACgB,CAAA,EACvC;AACN,CAAC,CAAC;AAEI,MAAA,MAAM,GAAG;;;;"}
@@ -0,0 +1,34 @@
1
+ import { ReactNode } from 'react';
2
+ import { StaticDataFetcherItem, DataProvider } from '@hh.ru/magritte-common-data-provider';
3
+ export interface ErrorState {
4
+ placeholder: ReactNode;
5
+ resetButton: ReactNode;
6
+ }
7
+ export interface ErrorTrls {
8
+ description: string;
9
+ reloadButton: string;
10
+ title: string;
11
+ }
12
+ export interface Hints {
13
+ emptyOptions: ReactNode;
14
+ emptySearchHint: ReactNode;
15
+ }
16
+ interface DataProviderCommonProps {
17
+ error?: ErrorState;
18
+ errorTrls?: ErrorTrls;
19
+ hints?: Hints;
20
+ }
21
+ interface StaticDataProviderProps<DataType extends StaticDataFetcherItem> extends DataProviderCommonProps {
22
+ dataProvider?: never;
23
+ options: DataType[];
24
+ }
25
+ interface CustomDataProviderProps<DataType extends StaticDataFetcherItem> extends DataProviderCommonProps {
26
+ dataProvider: DataProvider<DataType>;
27
+ options?: never;
28
+ }
29
+ export type DataProviderProps<DataType extends StaticDataFetcherItem> = StaticDataProviderProps<DataType> | CustomDataProviderProps<DataType>;
30
+ export interface UseDataProviderParams<DataType extends StaticDataFetcherItem> extends DataProviderCommonProps {
31
+ dataProvider?: DataProvider<DataType>;
32
+ options?: DataType[];
33
+ }
34
+ export {};
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=dataProviderTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataProviderTypes.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { Select } from '@hh.ru/magritte-ui-new-select/Select';
package/index.js ADDED
@@ -0,0 +1,16 @@
1
+ export { Select } from './Select.js';
2
+ import 'react/jsx-runtime';
3
+ import 'react';
4
+ import './internal/ConfigContext.js';
5
+ import './internal/utils.js';
6
+ import '@hh.ru/magritte-common-keyboard';
7
+ import './internal/NavigationContext.js';
8
+ import './internal/SelectDataProvider.js';
9
+ import '@hh.ru/magritte-common-data-provider';
10
+ import './internal/useSearch.js';
11
+ import './internal/VisibilityContext.js';
12
+ import './internal/SelectStateContext.js';
13
+ import './internal/useSelectState.js';
14
+ import './internal/slots.js';
15
+ import '@hh.ru/magritte-internal-slot-registry';
16
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;"}
@@ -0,0 +1,16 @@
1
+ import { type ReactNode, type ReactElement } from 'react';
2
+ import { CommitModel } from '@hh.ru/magritte-ui-new-select/types';
3
+ interface ConfigContextProps {
4
+ commitModel: CommitModel;
5
+ maxSelectedOptions?: number;
6
+ multiple?: boolean;
7
+ name?: string;
8
+ onBlur?: VoidFunction;
9
+ onFocus?: VoidFunction;
10
+ loading?: boolean;
11
+ }
12
+ export declare const useSelectConfig: () => ConfigContextProps;
13
+ export declare const ConfigProvider: ({ children, commitModel, maxSelectedOptions, multiple, name, onBlur, onFocus, loading, }: ConfigContextProps & {
14
+ children: ReactNode;
15
+ }) => ReactElement;
16
+ export {};
@@ -0,0 +1,25 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { createContext, useContext, useMemo } from 'react';
3
+ import { assertSelectContext } from './utils.js';
4
+ import '@hh.ru/magritte-common-keyboard';
5
+
6
+ const ConfigContext = createContext(null);
7
+ const useSelectConfig = () => {
8
+ return assertSelectContext(useContext(ConfigContext), 'useSelectConfig');
9
+ };
10
+ // провайдер внешних/глобальных свойств select
11
+ const ConfigProvider = ({ children, commitModel, maxSelectedOptions, multiple, name, onBlur, onFocus, loading, }) => {
12
+ const value = useMemo(() => ({
13
+ commitModel,
14
+ maxSelectedOptions,
15
+ multiple,
16
+ name,
17
+ onBlur,
18
+ onFocus,
19
+ loading,
20
+ }), [commitModel, maxSelectedOptions, multiple, name, onBlur, onFocus, loading]);
21
+ return jsx(ConfigContext.Provider, { value: value, children: children });
22
+ };
23
+
24
+ export { ConfigProvider, useSelectConfig };
25
+ //# sourceMappingURL=ConfigContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigContext.js","sources":["src/internal/ConfigContext.tsx"],"sourcesContent":["import { createContext, useContext, useMemo, type ReactNode, type ReactElement } from 'react';\n\nimport { assertSelectContext } from '@hh.ru/magritte-ui-new-select/internal/utils';\nimport { CommitModel } from '@hh.ru/magritte-ui-new-select/types';\n\ninterface ConfigContextProps {\n commitModel: CommitModel;\n maxSelectedOptions?: number;\n multiple?: boolean;\n name?: string;\n onBlur?: VoidFunction;\n onFocus?: VoidFunction;\n loading?: boolean;\n}\n\nconst ConfigContext = createContext<ConfigContextProps | null>(null);\n\nexport const useSelectConfig = (): ConfigContextProps => {\n return assertSelectContext(useContext(ConfigContext), 'useSelectConfig');\n};\n\n// провайдер внешних/глобальных свойств select\nexport const ConfigProvider = ({\n children,\n commitModel,\n maxSelectedOptions,\n multiple,\n name,\n onBlur,\n onFocus,\n loading,\n}: ConfigContextProps & { children: ReactNode }): ReactElement => {\n const value = useMemo<ConfigContextProps>(\n () => ({\n commitModel,\n maxSelectedOptions,\n multiple,\n name,\n onBlur,\n onFocus,\n loading,\n }),\n [commitModel, maxSelectedOptions, multiple, name, onBlur, onFocus, loading]\n );\n\n return <ConfigContext.Provider value={value}>{children}</ConfigContext.Provider>;\n};\n"],"names":["_jsx"],"mappings":";;;;;AAeA,MAAM,aAAa,GAAG,aAAa,CAA4B,IAAI,CAAC,CAAC;AAE9D,MAAM,eAAe,GAAG,MAAyB;IACpD,OAAO,mBAAmB,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,iBAAiB,CAAC,CAAC;AAC7E,EAAE;AAEF;MACa,cAAc,GAAG,CAAC,EAC3B,QAAQ,EACR,WAAW,EACX,kBAAkB,EAClB,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,OAAO,EACP,OAAO,GACoC,KAAkB;AAC7D,IAAA,MAAM,KAAK,GAAG,OAAO,CACjB,OAAO;QACH,WAAW;QACX,kBAAkB;QAClB,QAAQ;QACR,IAAI;QACJ,MAAM;QACN,OAAO;QACP,OAAO;AACV,KAAA,CAAC,EACF,CAAC,WAAW,EAAE,kBAAkB,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAC9E,CAAC;IAEF,OAAOA,GAAA,CAAC,aAAa,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,KAAK,EAAA,QAAA,EAAG,QAAQ,EAAA,CAA0B,CAAC;AACrF;;;;"}
@@ -0,0 +1,17 @@
1
+ import { type ReactNode, type ReactElement, Dispatch, MutableRefObject, SetStateAction } from 'react';
2
+ export type FocusStateType = 'focus' | 'focusAndSelect' | null;
3
+ export interface UseNavigation {
4
+ forcedState: FocusStateType;
5
+ requestFocus: VoidFunction;
6
+ requestFocusAndSelect: VoidFunction;
7
+ clearFocusIntent: VoidFunction;
8
+ committedByKeyboardRef: MutableRefObject<boolean>;
9
+ }
10
+ export declare const useNavigation: () => UseNavigation;
11
+ interface NavigationProviderProps {
12
+ children: ReactNode;
13
+ forcedState: FocusStateType;
14
+ setForcedState: Dispatch<SetStateAction<FocusStateType>>;
15
+ }
16
+ export declare const NavigationProvider: ({ children, forcedState, setForcedState, }: NavigationProviderProps) => ReactElement;
17
+ export {};
@@ -0,0 +1,30 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { createContext, useContext, useCallback, useRef, useMemo } from 'react';
3
+ import { assertSelectContext } from './utils.js';
4
+ import '@hh.ru/magritte-common-keyboard';
5
+
6
+ const SelectNavigationContext = createContext(null);
7
+ const useNavigation = () => {
8
+ return assertSelectContext(useContext(SelectNavigationContext), 'useNavigation');
9
+ };
10
+ // фокус/клавиатура (самый нижний в цепочке — читает все что выше)
11
+ const NavigationProvider = ({ children, forcedState, setForcedState, }) => {
12
+ const requestFocus = useCallback(() => setForcedState('focus'), [setForcedState]);
13
+ const requestFocusAndSelect = useCallback(() => setForcedState('focusAndSelect'), [setForcedState]);
14
+ const clearFocusIntent = useCallback(() => setForcedState(null), [setForcedState]);
15
+ // надо перебросить фокус на тригер
16
+ // при выборе опции/использовании кнопок с клавиатуры
17
+ // реф обрабатывается в опции/кнопках/дропе
18
+ const committedByKeyboardRef = useRef(false);
19
+ const contextValue = useMemo(() => ({
20
+ forcedState,
21
+ requestFocus,
22
+ requestFocusAndSelect,
23
+ clearFocusIntent,
24
+ committedByKeyboardRef,
25
+ }), [forcedState, requestFocus, requestFocusAndSelect, clearFocusIntent]);
26
+ return jsx(SelectNavigationContext.Provider, { value: contextValue, children: children });
27
+ };
28
+
29
+ export { NavigationProvider, useNavigation };
30
+ //# sourceMappingURL=NavigationContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavigationContext.js","sources":["src/internal/NavigationContext.tsx"],"sourcesContent":["import {\n createContext,\n useContext,\n type ReactNode,\n type ReactElement,\n Dispatch,\n useMemo,\n MutableRefObject,\n useCallback,\n useRef,\n SetStateAction,\n} from 'react';\n\nimport { assertSelectContext } from '@hh.ru/magritte-ui-new-select/internal/utils';\n\nexport type FocusStateType = 'focus' | 'focusAndSelect' | null;\n\nexport interface UseNavigation {\n forcedState: FocusStateType;\n requestFocus: VoidFunction;\n requestFocusAndSelect: VoidFunction;\n clearFocusIntent: VoidFunction;\n committedByKeyboardRef: MutableRefObject<boolean>;\n}\n\nconst SelectNavigationContext = createContext<UseNavigation | null>(null);\n\nexport const useNavigation = (): UseNavigation => {\n return assertSelectContext(useContext(SelectNavigationContext), 'useNavigation');\n};\n\ninterface NavigationProviderProps {\n children: ReactNode;\n forcedState: FocusStateType;\n setForcedState: Dispatch<SetStateAction<FocusStateType>>;\n}\n\n// фокус/клавиатура (самый нижний в цепочке — читает все что выше)\nexport const NavigationProvider = ({\n children,\n forcedState,\n setForcedState,\n}: NavigationProviderProps): ReactElement => {\n const requestFocus = useCallback(() => setForcedState('focus'), [setForcedState]);\n const requestFocusAndSelect = useCallback(() => setForcedState('focusAndSelect'), [setForcedState]);\n const clearFocusIntent = useCallback(() => setForcedState(null), [setForcedState]);\n\n // надо перебросить фокус на тригер\n // при выборе опции/использовании кнопок с клавиатуры\n // реф обрабатывается в опции/кнопках/дропе\n const committedByKeyboardRef = useRef(false);\n\n const contextValue = useMemo(\n () => ({\n forcedState,\n requestFocus,\n requestFocusAndSelect,\n clearFocusIntent,\n committedByKeyboardRef,\n }),\n [forcedState, requestFocus, requestFocusAndSelect, clearFocusIntent]\n );\n return <SelectNavigationContext.Provider value={contextValue}>{children}</SelectNavigationContext.Provider>;\n};\n"],"names":["_jsx"],"mappings":";;;;;AAyBA,MAAM,uBAAuB,GAAG,aAAa,CAAuB,IAAI,CAAC,CAAC;AAEnE,MAAM,aAAa,GAAG,MAAoB;IAC7C,OAAO,mBAAmB,CAAC,UAAU,CAAC,uBAAuB,CAAC,EAAE,eAAe,CAAC,CAAC;AACrF,EAAE;AAQF;AACO,MAAM,kBAAkB,GAAG,CAAC,EAC/B,QAAQ,EACR,WAAW,EACX,cAAc,GACQ,KAAkB;AACxC,IAAA,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;AAClF,IAAA,MAAM,qBAAqB,GAAG,WAAW,CAAC,MAAM,cAAc,CAAC,gBAAgB,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;AACpG,IAAA,MAAM,gBAAgB,GAAG,WAAW,CAAC,MAAM,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;;;;AAKnF,IAAA,MAAM,sBAAsB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;AAE7C,IAAA,MAAM,YAAY,GAAG,OAAO,CACxB,OAAO;QACH,WAAW;QACX,YAAY;QACZ,qBAAqB;QACrB,gBAAgB;QAChB,sBAAsB;KACzB,CAAC,EACF,CAAC,WAAW,EAAE,YAAY,EAAE,qBAAqB,EAAE,gBAAgB,CAAC,CACvE,CAAC;IACF,OAAOA,GAAA,CAAC,uBAAuB,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,YAAY,EAAA,QAAA,EAAG,QAAQ,EAAA,CAAoC,CAAC;AAChH;;;;"}
@@ -0,0 +1,18 @@
1
+ import { type ReactNode, ReactElement } from 'react';
2
+ import { StaticDataFetcherItem } from '@hh.ru/magritte-common-data-provider';
3
+ import { UseDataProviderParams, ErrorState, ErrorTrls, Hints } from '@hh.ru/magritte-ui-new-select/dataProviderTypes';
4
+ import { UseSelectSearch } from '@hh.ru/magritte-ui-new-select/internal/useSearch';
5
+ import { OnOptionsChangeCallback } from '@hh.ru/magritte-ui-new-select/types';
6
+ interface DataProviderMetaContextValue {
7
+ error?: ErrorState;
8
+ errorTrls?: ErrorTrls;
9
+ hints?: Hints;
10
+ }
11
+ export declare const useSearch: <DataType extends StaticDataFetcherItem = StaticDataFetcherItem>() => UseSelectSearch<DataType>;
12
+ export declare const useDataProviderMeta: () => DataProviderMetaContextValue | undefined;
13
+ interface SelectDataProviderProps<DataType extends StaticDataFetcherItem> extends UseDataProviderParams<DataType> {
14
+ children: ReactNode;
15
+ onOptionsChange?: OnOptionsChangeCallback<DataType>;
16
+ }
17
+ declare const SelectDataProvider: <DataType extends StaticDataFetcherItem>({ children, error, errorTrls, hints, onOptionsChange, options, dataProvider: dataProviderExternal, }: SelectDataProviderProps<DataType>) => ReactElement;
18
+ export { SelectDataProvider };
@@ -0,0 +1,39 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { createContext, useContext, useMemo } from 'react';
3
+ import { createStaticDataProvider } from '@hh.ru/magritte-common-data-provider';
4
+ import { useSearch as useSearch$1 } from './useSearch.js';
5
+ import { assertSelectContext } from './utils.js';
6
+ import './VisibilityContext.js';
7
+ import '@hh.ru/magritte-common-keyboard';
8
+
9
+ const DataProviderContext = createContext(null);
10
+ const useSearch = () => {
11
+ return assertSelectContext(useContext(DataProviderContext), 'useSearch')
12
+ .searchData;
13
+ };
14
+ const useDataProviderMeta = () => {
15
+ return assertSelectContext(useContext(DataProviderContext), 'useMeta').meta;
16
+ };
17
+ const SelectDataProvider = ({ children, error, errorTrls, hints, onOptionsChange, options, dataProvider: dataProviderExternal, }) => {
18
+ const dataProvider = useMemo(() => {
19
+ if (!options && !dataProviderExternal) {
20
+ throw new Error(`dataProvider or options must be used within <Select>`);
21
+ }
22
+ if (options) {
23
+ return createStaticDataProvider(options);
24
+ }
25
+ return dataProviderExternal;
26
+ }, [dataProviderExternal, options]);
27
+ const searchData = useSearch$1(dataProvider, onOptionsChange);
28
+ const meta = useMemo(() => {
29
+ if (!error && !errorTrls && !hints) {
30
+ return undefined;
31
+ }
32
+ return { error, errorTrls, hints };
33
+ }, [error, errorTrls, hints]);
34
+ const contextValue = useMemo(() => ({ meta, searchData }), [meta, searchData]);
35
+ return jsx(DataProviderContext.Provider, { value: contextValue, children: children });
36
+ };
37
+
38
+ export { SelectDataProvider, useDataProviderMeta, useSearch };
39
+ //# sourceMappingURL=SelectDataProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectDataProvider.js","sources":["src/internal/SelectDataProvider.tsx"],"sourcesContent":["import { type ReactNode, useMemo, ReactElement, createContext, useContext } from 'react';\n\nimport {\n StaticDataFetcherItem,\n createStaticDataProvider,\n DataProvider as DataProviderType,\n} from '@hh.ru/magritte-common-data-provider';\nimport { UseDataProviderParams, ErrorState, ErrorTrls, Hints } from '@hh.ru/magritte-ui-new-select/dataProviderTypes';\nimport { useSearch as useSearchInternal, UseSelectSearch } from '@hh.ru/magritte-ui-new-select/internal/useSearch';\nimport { assertSelectContext } from '@hh.ru/magritte-ui-new-select/internal/utils';\nimport { OnOptionsChangeCallback } from '@hh.ru/magritte-ui-new-select/types';\n\ninterface DataProviderMetaContextValue {\n error?: ErrorState;\n errorTrls?: ErrorTrls;\n hints?: Hints;\n}\n\ninterface DataProviderContextValue<DataType extends StaticDataFetcherItem> {\n meta?: DataProviderMetaContextValue;\n searchData: UseSelectSearch<DataType>;\n}\n\nconst DataProviderContext = createContext<DataProviderContextValue<StaticDataFetcherItem> | null>(null);\n\nexport const useSearch = <\n DataType extends StaticDataFetcherItem = StaticDataFetcherItem,\n>(): UseSelectSearch<DataType> => {\n return assertSelectContext(useContext(DataProviderContext), 'useSearch')\n .searchData as unknown as UseSelectSearch<DataType>;\n};\n\nexport const useDataProviderMeta = (): DataProviderMetaContextValue | undefined => {\n return assertSelectContext(useContext(DataProviderContext), 'useMeta').meta;\n};\n\ninterface SelectDataProviderProps<DataType extends StaticDataFetcherItem> extends UseDataProviderParams<DataType> {\n children: ReactNode;\n onOptionsChange?: OnOptionsChangeCallback<DataType>;\n}\n\nconst SelectDataProvider = <DataType extends StaticDataFetcherItem>({\n children,\n error,\n errorTrls,\n hints,\n onOptionsChange,\n options,\n dataProvider: dataProviderExternal,\n}: SelectDataProviderProps<DataType>): ReactElement => {\n const dataProvider = useMemo(() => {\n if (!options && !dataProviderExternal) {\n throw new Error(`dataProvider or options must be used within <Select>`);\n }\n if (options) {\n return createStaticDataProvider<DataType>(options);\n }\n return dataProviderExternal as DataProviderType<DataType>;\n }, [dataProviderExternal, options]);\n\n const searchData = useSearchInternal<DataType>(dataProvider, onOptionsChange);\n\n const meta = useMemo<DataProviderMetaContextValue | undefined>(() => {\n if (!error && !errorTrls && !hints) {\n return undefined;\n }\n return { error, errorTrls, hints };\n }, [error, errorTrls, hints]);\n\n const contextValue = useMemo(() => ({ meta, searchData }), [meta, searchData]);\n\n return <DataProviderContext.Provider value={contextValue}>{children}</DataProviderContext.Provider>;\n};\n\nexport { SelectDataProvider };\n"],"names":["useSearchInternal","_jsx"],"mappings":";;;;;;;;AAuBA,MAAM,mBAAmB,GAAG,aAAa,CAAyD,IAAI,CAAC,CAAC;AAEjG,MAAM,SAAS,GAAG,MAEQ;IAC7B,OAAO,mBAAmB,CAAC,UAAU,CAAC,mBAAmB,CAAC,EAAE,WAAW,CAAC;AACnE,SAAA,UAAkD,CAAC;AAC5D,EAAE;AAEK,MAAM,mBAAmB,GAAG,MAA+C;IAC9E,OAAO,mBAAmB,CAAC,UAAU,CAAC,mBAAmB,CAAC,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC;AAChF,EAAE;AAOI,MAAA,kBAAkB,GAAG,CAAyC,EAChE,QAAQ,EACR,KAAK,EACL,SAAS,EACT,KAAK,EACL,eAAe,EACf,OAAO,EACP,YAAY,EAAE,oBAAoB,GACF,KAAkB;AAClD,IAAA,MAAM,YAAY,GAAG,OAAO,CAAC,MAAK;AAC9B,QAAA,IAAI,CAAC,OAAO,IAAI,CAAC,oBAAoB,EAAE;AACnC,YAAA,MAAM,IAAI,KAAK,CAAC,CAAA,oDAAA,CAAsD,CAAC,CAAC;SAC3E;QACD,IAAI,OAAO,EAAE;AACT,YAAA,OAAO,wBAAwB,CAAW,OAAO,CAAC,CAAC;SACtD;AACD,QAAA,OAAO,oBAAkD,CAAC;AAC9D,KAAC,EAAE,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC,CAAC;IAEpC,MAAM,UAAU,GAAGA,WAAiB,CAAW,YAAY,EAAE,eAAe,CAAC,CAAC;AAE9E,IAAA,MAAM,IAAI,GAAG,OAAO,CAA2C,MAAK;QAChE,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,IAAI,CAAC,KAAK,EAAE;AAChC,YAAA,OAAO,SAAS,CAAC;SACpB;AACD,QAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;KACtC,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;IAE9B,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IAE/E,OAAOC,GAAA,CAAC,mBAAmB,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,YAAY,EAAA,QAAA,EAAG,QAAQ,EAAA,CAAgC,CAAC;AACxG;;;;"}
@@ -0,0 +1,13 @@
1
+ import { type ReactNode, type ReactElement } from 'react';
2
+ import { StaticDataFetcherItem } from '@hh.ru/magritte-common-data-provider';
3
+ import { UseSelectState } from '@hh.ru/magritte-ui-new-select/internal/useSelectState';
4
+ import { ValueTypeHelper } from '@hh.ru/magritte-ui-new-select/types';
5
+ export declare const useSelectState: <MultipleType extends boolean = boolean, DataType extends StaticDataFetcherItem = StaticDataFetcherItem>() => UseSelectState<MultipleType, DataType>;
6
+ interface SelectStateProviderProps<MultipleType extends boolean, DataType extends StaticDataFetcherItem> {
7
+ children: ReactNode;
8
+ onChange?: (value: ValueTypeHelper<MultipleType, DataType>) => void;
9
+ onSelectOption?: (values: DataType[]) => void;
10
+ value?: ValueTypeHelper<MultipleType, DataType>;
11
+ }
12
+ export declare const SelectStateProvider: <MultipleType extends boolean, DataType extends StaticDataFetcherItem>({ children, onChange, onSelectOption, value, }: SelectStateProviderProps<MultipleType, DataType>) => ReactElement;
13
+ export {};
@@ -0,0 +1,20 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { createContext, useContext } from 'react';
3
+ import { useSelectState as useSelectState$1 } from './useSelectState.js';
4
+ import { assertSelectContext } from './utils.js';
5
+ import './ConfigContext.js';
6
+ import './VisibilityContext.js';
7
+ import '@hh.ru/magritte-common-keyboard';
8
+
9
+ const SelectStateContext = createContext(null);
10
+ const useSelectState = () => {
11
+ return assertSelectContext(useContext(SelectStateContext), 'useSelectState');
12
+ };
13
+ // выбранные значения и операции над ними
14
+ const SelectStateProvider = ({ children, onChange, onSelectOption, value, }) => {
15
+ const contextValue = useSelectState$1((value || []), onChange, onSelectOption);
16
+ return (jsx(SelectStateContext.Provider, { value: contextValue, children: children }));
17
+ };
18
+
19
+ export { SelectStateProvider, useSelectState };
20
+ //# sourceMappingURL=SelectStateContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectStateContext.js","sources":["src/internal/SelectStateContext.tsx"],"sourcesContent":["import { createContext, useContext, type ReactNode, type ReactElement } from 'react';\n\nimport { StaticDataFetcherItem } from '@hh.ru/magritte-common-data-provider';\nimport {\n useSelectState as useSelectStateInternal,\n UseSelectState,\n} from '@hh.ru/magritte-ui-new-select/internal/useSelectState';\nimport { assertSelectContext } from '@hh.ru/magritte-ui-new-select/internal/utils';\nimport { ValueTypeHelper } from '@hh.ru/magritte-ui-new-select/types';\n\nconst SelectStateContext = createContext<UseSelectState<boolean, StaticDataFetcherItem> | null>(null);\n\nexport const useSelectState = <\n MultipleType extends boolean = boolean,\n DataType extends StaticDataFetcherItem = StaticDataFetcherItem,\n>(): UseSelectState<MultipleType, DataType> => {\n return assertSelectContext(useContext(SelectStateContext), 'useSelectState') as unknown as UseSelectState<\n MultipleType,\n DataType\n >;\n};\n\ninterface SelectStateProviderProps<MultipleType extends boolean, DataType extends StaticDataFetcherItem> {\n children: ReactNode;\n onChange?: (value: ValueTypeHelper<MultipleType, DataType>) => void;\n onSelectOption?: (values: DataType[]) => void;\n value?: ValueTypeHelper<MultipleType, DataType>;\n}\n\n// выбранные значения и операции над ними\nexport const SelectStateProvider = <MultipleType extends boolean, DataType extends StaticDataFetcherItem>({\n children,\n onChange,\n onSelectOption,\n value,\n}: SelectStateProviderProps<MultipleType, DataType>): ReactElement => {\n const contextValue = useSelectStateInternal<DataType, MultipleType>(\n (value || []) as DataType[] | DataType,\n onChange,\n onSelectOption\n );\n\n return (\n <SelectStateContext.Provider value={contextValue as unknown as UseSelectState<boolean, StaticDataFetcherItem>}>\n {children}\n </SelectStateContext.Provider>\n );\n};\n"],"names":["useSelectStateInternal","_jsx"],"mappings":";;;;;;;;AAUA,MAAM,kBAAkB,GAAG,aAAa,CAAwD,IAAI,CAAC,CAAC;AAE/F,MAAM,cAAc,GAAG,MAGgB;IAC1C,OAAO,mBAAmB,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,gBAAgB,CAG1E,CAAC;AACN,EAAE;AASF;AACO,MAAM,mBAAmB,GAAG,CAAuE,EACtG,QAAQ,EACR,QAAQ,EACR,cAAc,EACd,KAAK,GAC0C,KAAkB;AACjE,IAAA,MAAM,YAAY,GAAGA,gBAAsB,EACtC,KAAK,IAAI,EAAE,GACZ,QAAQ,EACR,cAAc,CACjB,CAAC;AAEF,IAAA,QACIC,GAAA,CAAC,kBAAkB,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,YAAyE,EAAA,QAAA,EACxG,QAAQ,EAAA,CACiB,EAChC;AACN;;;;"}
@@ -0,0 +1,13 @@
1
+ import { type ReactNode, type ReactElement, SetStateAction, Dispatch } from 'react';
2
+ export interface VisibilityContextValue {
3
+ showOptions: boolean;
4
+ toggleListVisibility: (force?: boolean) => void;
5
+ }
6
+ export declare const useVisibility: () => VisibilityContextValue;
7
+ interface VisibilityProviderProps {
8
+ children: ReactNode;
9
+ showOptions: boolean;
10
+ setShowOptions: Dispatch<SetStateAction<boolean>>;
11
+ }
12
+ export declare const VisibilityProvider: ({ children, showOptions, setShowOptions, }: VisibilityProviderProps) => ReactElement;
13
+ export {};
@@ -0,0 +1,21 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { createContext, useContext, useCallback, useMemo } from 'react';
3
+ import { assertSelectContext } from './utils.js';
4
+ import '@hh.ru/magritte-common-keyboard';
5
+
6
+ const VisibilityContext = createContext(null);
7
+ const useVisibility = () => {
8
+ return assertSelectContext(useContext(VisibilityContext), 'useVisibility');
9
+ };
10
+ // видимость пикера
11
+ const VisibilityProvider = ({ children, showOptions, setShowOptions, }) => {
12
+ const toggleListVisibility = useCallback((force) => setShowOptions((showOptions) => (force !== undefined ? force : !showOptions)), [setShowOptions]);
13
+ const value = useMemo(() => ({
14
+ showOptions,
15
+ toggleListVisibility,
16
+ }), [showOptions, toggleListVisibility]);
17
+ return jsx(VisibilityContext.Provider, { value: value, children: children });
18
+ };
19
+
20
+ export { VisibilityProvider, useVisibility };
21
+ //# sourceMappingURL=VisibilityContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VisibilityContext.js","sources":["src/internal/VisibilityContext.tsx"],"sourcesContent":["import {\n createContext,\n useContext,\n type ReactNode,\n type ReactElement,\n useMemo,\n useCallback,\n SetStateAction,\n Dispatch,\n} from 'react';\n\nimport { assertSelectContext } from '@hh.ru/magritte-ui-new-select/internal/utils';\n\nexport interface VisibilityContextValue {\n showOptions: boolean;\n toggleListVisibility: (force?: boolean) => void;\n}\n\nconst VisibilityContext = createContext<VisibilityContextValue | null>(null);\n\nexport const useVisibility = (): VisibilityContextValue => {\n return assertSelectContext(useContext(VisibilityContext), 'useVisibility');\n};\n\ninterface VisibilityProviderProps {\n children: ReactNode;\n showOptions: boolean;\n setShowOptions: Dispatch<SetStateAction<boolean>>;\n}\n\n// видимость пикера\nexport const VisibilityProvider = ({\n children,\n showOptions,\n setShowOptions,\n}: VisibilityProviderProps): ReactElement => {\n const toggleListVisibility = useCallback(\n (force?: boolean) => setShowOptions((showOptions) => (force !== undefined ? force : !showOptions)),\n [setShowOptions]\n );\n const value = useMemo(\n () => ({\n showOptions,\n toggleListVisibility,\n }),\n [showOptions, toggleListVisibility]\n );\n\n return <VisibilityContext.Provider value={value}>{children}</VisibilityContext.Provider>;\n};\n"],"names":["_jsx"],"mappings":";;;;;AAkBA,MAAM,iBAAiB,GAAG,aAAa,CAAgC,IAAI,CAAC,CAAC;AAEtE,MAAM,aAAa,GAAG,MAA6B;IACtD,OAAO,mBAAmB,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,eAAe,CAAC,CAAC;AAC/E,EAAE;AAQF;AACO,MAAM,kBAAkB,GAAG,CAAC,EAC/B,QAAQ,EACR,WAAW,EACX,cAAc,GACQ,KAAkB;AACxC,IAAA,MAAM,oBAAoB,GAAG,WAAW,CACpC,CAAC,KAAe,KAAK,cAAc,CAAC,CAAC,WAAW,MAAM,KAAK,KAAK,SAAS,GAAG,KAAK,GAAG,CAAC,WAAW,CAAC,CAAC,EAClG,CAAC,cAAc,CAAC,CACnB,CAAC;AACF,IAAA,MAAM,KAAK,GAAG,OAAO,CACjB,OAAO;QACH,WAAW;QACX,oBAAoB;AACvB,KAAA,CAAC,EACF,CAAC,WAAW,EAAE,oBAAoB,CAAC,CACtC,CAAC;IAEF,OAAOA,GAAA,CAAC,iBAAiB,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,KAAK,EAAA,QAAA,EAAG,QAAQ,EAAA,CAA8B,CAAC;AAC7F;;;;"}
@@ -0,0 +1,11 @@
1
+ import { RefObject } from 'react';
2
+ interface SelectElementsSlots {
3
+ hasApplyChangesButton: boolean;
4
+ hasClearButton: boolean;
5
+ pickerRef: RefObject<HTMLElement>;
6
+ activatorRef: RefObject<HTMLElement>;
7
+ }
8
+ export declare const SelectElementsSlotRegistryProvider: import("react").FC<{
9
+ children?: import("react").ReactNode | undefined;
10
+ }>, useRegisterSelectElementsSlot: <K extends keyof SelectElementsSlots>(key: K, value: SelectElementsSlots[K] | null | undefined) => void, useSelectElementsValue: <K extends keyof SelectElementsSlots>(key: K) => SelectElementsSlots[K] | undefined;
11
+ export {};
@@ -0,0 +1,7 @@
1
+ import { createSlotRegistry } from '@hh.ru/magritte-internal-slot-registry';
2
+
3
+ // наличие кнопок влияет на аттрибуты доступности в пикере
4
+ const { SlotRegistryProvider: SelectElementsSlotRegistryProvider, useRegisterSlot: useRegisterSelectElementsSlot, useSlotRegistryValue: useSelectElementsValue, } = createSlotRegistry('SelectElements');
5
+
6
+ export { SelectElementsSlotRegistryProvider, useRegisterSelectElementsSlot, useSelectElementsValue };
7
+ //# sourceMappingURL=slots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slots.js","sources":["src/internal/slots.ts"],"sourcesContent":["import { RefObject } from 'react';\n\nimport { createSlotRegistry } from '@hh.ru/magritte-internal-slot-registry';\n\ninterface SelectElementsSlots {\n hasApplyChangesButton: boolean;\n hasClearButton: boolean;\n pickerRef: RefObject<HTMLElement>;\n activatorRef: RefObject<HTMLElement>;\n}\n\n// наличие кнопок влияет на аттрибуты доступности в пикере\nexport const {\n SlotRegistryProvider: SelectElementsSlotRegistryProvider,\n useRegisterSlot: useRegisterSelectElementsSlot,\n useSlotRegistryValue: useSelectElementsValue,\n} = createSlotRegistry<SelectElementsSlots>('SelectElements');\n"],"names":[],"mappings":";;AAWA;MACa,EACT,oBAAoB,EAAE,kCAAkC,EACxD,eAAe,EAAE,6BAA6B,EAC9C,oBAAoB,EAAE,sBAAsB,GAC/C,GAAG,kBAAkB,CAAsB,gBAAgB;;;;"}
@@ -0,0 +1,8 @@
1
+ import { StaticDataFetcherItem, DataProvider as DataProviderType } from '@hh.ru/magritte-common-data-provider';
2
+ import { OnOptionsChangeCallback, SelectOption, SearchValue, SetSearchValue } from '@hh.ru/magritte-ui-new-select/types';
3
+ export interface UseSelectSearch<DataType extends StaticDataFetcherItem> extends SearchValue, SetSearchValue {
4
+ internalError?: boolean;
5
+ internalLoading?: boolean;
6
+ optionList: SelectOption<DataType>[];
7
+ }
8
+ export declare const useSearch: <DataType extends StaticDataFetcherItem>(dataProvider: DataProviderType<DataType>, onOptionsChangeExternal?: OnOptionsChangeCallback<DataType>) => UseSelectSearch<DataType>;
@@ -0,0 +1,107 @@
1
+ import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
2
+ import { DataProviderError } from '@hh.ru/magritte-common-data-provider';
3
+ import { useVisibility } from './VisibilityContext.js';
4
+ import 'react/jsx-runtime';
5
+ import './utils.js';
6
+ import '@hh.ru/magritte-common-keyboard';
7
+
8
+ const EMPTY_OPTION_LIST = [];
9
+ const useSearch = (dataProvider, onOptionsChangeExternal) => {
10
+ const { showOptions } = useVisibility();
11
+ const [searchValue, setSearchValue] = useState('');
12
+ const [optionList, setOptionList] = useState(EMPTY_OPTION_LIST);
13
+ const [internalError, setInternalError] = useState(false);
14
+ const [optionsLoading, setOptionsLoading] = useState(false);
15
+ const currentRequestRef = useRef();
16
+ // TODO loading/inProgress реализовать в отдельном компоненте
17
+ // const loadingTimeoutRef = useRef<number>(0);
18
+ // const inProgressTimeoutRef = useRef<number>(0);
19
+ // const [searchInProgress, setSearchInProgress] = useState(false);
20
+ const updateOptionsFromProvider = useCallback((text) => {
21
+ if (!dataProvider) {
22
+ return;
23
+ }
24
+ setInternalError(false);
25
+ currentRequestRef.current?.cancel();
26
+ const request = dataProvider(text);
27
+ currentRequestRef.current = request;
28
+ request
29
+ .then((result) => {
30
+ currentRequestRef.current = undefined;
31
+ const newOptions = [];
32
+ result.forEach((section) => {
33
+ if (section.type === 'delimiter') {
34
+ newOptions.push({
35
+ data: { text: section.text, value: section.text },
36
+ type: 'delimiter',
37
+ });
38
+ }
39
+ if (section.type === 'cells') {
40
+ section.items.forEach((item) => {
41
+ newOptions.push({
42
+ data: item.data ? item.data : { text: item.value, value: item.value },
43
+ disabled: item.disabled,
44
+ });
45
+ });
46
+ }
47
+ });
48
+ const hasRealOptions = newOptions.some((item) => item.type !== 'delimiter');
49
+ setOptionList(hasRealOptions ? newOptions : EMPTY_OPTION_LIST);
50
+ })
51
+ .catch((error) => {
52
+ // включаем error state если это НЕ ошибка
53
+ // вызванная `minCharsCount` или отменненым запросом
54
+ if (!(error instanceof DataProviderError)) {
55
+ setInternalError(true);
56
+ setOptionList(EMPTY_OPTION_LIST);
57
+ }
58
+ })
59
+ .finally(() => {
60
+ setOptionsLoading(false);
61
+ });
62
+ }, [dataProvider]);
63
+ // уведомляем клиента об изменении списка опций вне фазы рендера (только при реальной смене ссылки)
64
+ const notifiedListRef = useRef(optionList);
65
+ useEffect(() => {
66
+ if (notifiedListRef.current === optionList) {
67
+ return;
68
+ }
69
+ notifiedListRef.current = optionList;
70
+ onOptionsChangeExternal?.(optionList);
71
+ }, [optionList, onOptionsChangeExternal]);
72
+ // обрабатываем обновление провайдера из клиента
73
+ useEffect(() => {
74
+ updateOptionsFromProvider(searchValue);
75
+ // eslint-disable-next-line disable-autofix/react-hooks/exhaustive-deps
76
+ }, [dataProvider]);
77
+ // TODO убрать в тригер/пикер
78
+ // сбрасываем select при закрытии компонента
79
+ /* useEffect(() => {
80
+ if (!showOptions) {
81
+ setSearchValue('');
82
+ updateOptionsFromProvider('');
83
+ }
84
+ // eslint-disable-next-line disable-autofix/react-hooks/exhaustive-deps
85
+ }, [showOptions]);
86
+
87
+ // обрабатываем поиск
88
+
89
+ useEffect(() => {
90
+ updateOptionsFromProvider(searchValue);
91
+ // eslint-disable-next-line disable-autofix/react-hooks/exhaustive-deps
92
+ }, [searchValue]);*/
93
+ // после анмаунта надо отменить все запросы
94
+ useEffect(() => () => {
95
+ currentRequestRef.current?.cancel();
96
+ }, []);
97
+ return useMemo(() => ({
98
+ internalError,
99
+ internalLoading: optionsLoading && showOptions,
100
+ optionList,
101
+ searchValue,
102
+ setSearchValue,
103
+ }), [internalError, optionList, optionsLoading, searchValue, setSearchValue, showOptions]);
104
+ };
105
+
106
+ export { useSearch };
107
+ //# sourceMappingURL=useSearch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSearch.js","sources":["src/internal/useSearch.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport {\n StaticDataFetcherItem,\n DataProvider as DataProviderType,\n DataProviderError,\n} from '@hh.ru/magritte-common-data-provider';\nimport { DataProviderRequest } from '@hh.ru/magritte-common-data-provider/types';\nimport { useVisibility } from '@hh.ru/magritte-ui-new-select/internal/VisibilityContext';\nimport {\n OnOptionsChangeCallback,\n SelectOption,\n SearchValue,\n SetSearchValue,\n} from '@hh.ru/magritte-ui-new-select/types';\n\nconst EMPTY_OPTION_LIST: never[] = [];\n\nexport interface UseSelectSearch<DataType extends StaticDataFetcherItem> extends SearchValue, SetSearchValue {\n internalError?: boolean;\n internalLoading?: boolean;\n optionList: SelectOption<DataType>[];\n}\n\nexport const useSearch = <DataType extends StaticDataFetcherItem>(\n dataProvider: DataProviderType<DataType>,\n onOptionsChangeExternal?: OnOptionsChangeCallback<DataType>\n): UseSelectSearch<DataType> => {\n const { showOptions } = useVisibility();\n const [searchValue, setSearchValue] = useState('');\n const [optionList, setOptionList] = useState<SelectOption<DataType>[]>(EMPTY_OPTION_LIST);\n const [internalError, setInternalError] = useState(false);\n const [optionsLoading, setOptionsLoading] = useState(false);\n const currentRequestRef = useRef<DataProviderRequest>();\n\n // TODO loading/inProgress реализовать в отдельном компоненте\n // const loadingTimeoutRef = useRef<number>(0);\n // const inProgressTimeoutRef = useRef<number>(0);\n // const [searchInProgress, setSearchInProgress] = useState(false);\n\n const updateOptionsFromProvider = useCallback(\n (text: string) => {\n if (!dataProvider) {\n return;\n }\n setInternalError(false);\n currentRequestRef.current?.cancel();\n const request = dataProvider(text);\n currentRequestRef.current = request;\n request\n .then((result) => {\n currentRequestRef.current = undefined;\n const newOptions: SelectOption<DataType>[] = [];\n result.forEach((section) => {\n if (section.type === 'delimiter') {\n newOptions.push({\n data: { text: section.text, value: section.text } as DataType,\n type: 'delimiter',\n });\n }\n if (section.type === 'cells') {\n section.items.forEach((item) => {\n newOptions.push({\n data: item.data ? item.data : ({ text: item.value, value: item.value } as DataType),\n disabled: item.disabled,\n });\n });\n }\n });\n\n const hasRealOptions = newOptions.some((item) => item.type !== 'delimiter');\n setOptionList(hasRealOptions ? newOptions : EMPTY_OPTION_LIST);\n })\n .catch((error) => {\n // включаем error state если это НЕ ошибка\n // вызванная `minCharsCount` или отменненым запросом\n if (!(error instanceof DataProviderError)) {\n setInternalError(true);\n setOptionList(EMPTY_OPTION_LIST);\n }\n })\n .finally(() => {\n setOptionsLoading(false);\n });\n },\n [dataProvider]\n );\n\n // уведомляем клиента об изменении списка опций вне фазы рендера (только при реальной смене ссылки)\n const notifiedListRef = useRef(optionList);\n useEffect(() => {\n if (notifiedListRef.current === optionList) {\n return;\n }\n notifiedListRef.current = optionList;\n onOptionsChangeExternal?.(optionList);\n }, [optionList, onOptionsChangeExternal]);\n\n // обрабатываем обновление провайдера из клиента\n useEffect(() => {\n updateOptionsFromProvider(searchValue);\n // eslint-disable-next-line disable-autofix/react-hooks/exhaustive-deps\n }, [dataProvider]);\n\n // TODO убрать в тригер/пикер\n // сбрасываем select при закрытии компонента\n /* useEffect(() => {\n if (!showOptions) {\n setSearchValue('');\n updateOptionsFromProvider('');\n }\n // eslint-disable-next-line disable-autofix/react-hooks/exhaustive-deps\n }, [showOptions]);\n\n // обрабатываем поиск\n\n useEffect(() => {\n updateOptionsFromProvider(searchValue);\n // eslint-disable-next-line disable-autofix/react-hooks/exhaustive-deps\n }, [searchValue]);*/\n\n // после анмаунта надо отменить все запросы\n useEffect(\n () => () => {\n currentRequestRef.current?.cancel();\n },\n []\n );\n\n return useMemo(\n () => ({\n internalError,\n internalLoading: optionsLoading && showOptions,\n optionList,\n searchValue,\n setSearchValue,\n }),\n [internalError, optionList, optionsLoading, searchValue, setSearchValue, showOptions]\n );\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { StaticDataFetcherItem } from '@hh.ru/magritte-common-data-provider';
2
+ import { ValueTypeHelper, OnChangeAction } from '@hh.ru/magritte-ui-new-select/types';
3
+ export declare const getToggledValues: <DataType extends StaticDataFetcherItem>(state: DataType[], value: DataType, needToPut: boolean, multiple?: boolean) => DataType[];
4
+ export interface UseSelectState<MultipleType extends boolean, DataType extends StaticDataFetcherItem> {
5
+ clearSelectedValues: VoidFunction;
6
+ clearSelectedValuesByTrigger: VoidFunction;
7
+ committedValues: ValueTypeHelper<MultipleType, DataType>;
8
+ setInitialValues: VoidFunction;
9
+ setInitialValuesOnClosePicker: VoidFunction;
10
+ values: ValueTypeHelper<MultipleType, DataType>;
11
+ valuesLength: number;
12
+ hasUnsavedChanges: boolean;
13
+ onChange: OnChangeAction<DataType>;
14
+ setSelectedValues: VoidFunction;
15
+ }
16
+ export declare const useSelectState: <DataType extends StaticDataFetcherItem, MultipleType extends boolean>(value: DataType[] | DataType, onChangeExternal?: (value: ValueTypeHelper<MultipleType, DataType>) => void, onSelectOption?: (values: DataType[]) => void) => UseSelectState<MultipleType, DataType>;
@@ -0,0 +1,145 @@
1
+ import { useReducer, useRef, useState, useCallback, useEffect, useMemo } from 'react';
2
+ import { useSelectConfig } from './ConfigContext.js';
3
+ import { useVisibility } from './VisibilityContext.js';
4
+ import { isArraysEquals } from './utils.js';
5
+ import 'react/jsx-runtime';
6
+ import '@hh.ru/magritte-common-keyboard';
7
+
8
+ const TOGGLE = 'TOGGLE';
9
+ const SET = 'SET';
10
+ const SYNC = 'SYNC';
11
+ const CLEAR = 'CLEAR';
12
+ const getToggledValues = (state, value, needToPut, multiple) => {
13
+ if (!multiple) {
14
+ return [value];
15
+ }
16
+ return needToPut ? [...state, value] : state.filter((item) => item.value !== value.value);
17
+ };
18
+ const reducer = (state, action) => {
19
+ switch (action.type) {
20
+ case TOGGLE:
21
+ return getToggledValues(state, action.value, action.needToPut, action.multiple);
22
+ case SET:
23
+ return [...action.payload];
24
+ case SYNC:
25
+ // синхронизация из внешнего value
26
+ return isArraysEquals(action.payload, state) ? state : [...action.payload];
27
+ case CLEAR:
28
+ return [];
29
+ default:
30
+ return state;
31
+ }
32
+ };
33
+ const useSelectState = (value, onChangeExternal, onSelectOption) => {
34
+ const { showOptions } = useVisibility();
35
+ const { multiple, commitModel } = useSelectConfig();
36
+ const valuesAsArray = Array.isArray(value) ? value : [value];
37
+ const [state, dispatch] = useReducer((reducer), valuesAsArray);
38
+ /**
39
+ * внешние снапшоты значений
40
+ * committed - отдано наружу (есть гард onChange)
41
+ * openBaseline - черновик на момент открытия пикера
42
+ */
43
+ const snapshotsRef = useRef({ committed: valuesAsArray, openBaseline: [] });
44
+ // реактивная копия committed для отрисовки (триггер показывает применённое, а не черновик)
45
+ const [committed, setCommitted] = useState(valuesAsArray);
46
+ const mountBaselineRef = useRef(valuesAsArray);
47
+ // эмит и гард
48
+ const commit = useCallback((values, emit = true) => {
49
+ if (emit) {
50
+ const hasUnsavedChanges = !isArraysEquals(snapshotsRef.current.committed.map((item) => item.value), values.map((item) => item.value));
51
+ if (hasUnsavedChanges) {
52
+ onChangeExternal?.((multiple ? values : values[0]));
53
+ }
54
+ }
55
+ snapshotsRef.current.committed = values;
56
+ setCommitted((prev) => isArraysEquals(prev.map((item) => item.value), values.map((item) => item.value))
57
+ ? prev
58
+ : values);
59
+ }, [multiple, onChangeExternal]);
60
+ // снимок черновика на открытии пикера
61
+ useEffect(() => {
62
+ if (showOptions) {
63
+ snapshotsRef.current.openBaseline = state;
64
+ }
65
+ // eslint-disable-next-line disable-autofix/react-hooks/exhaustive-deps
66
+ }, [showOptions]);
67
+ // синхронизация с внешним value
68
+ useEffect(() => {
69
+ const newValues = Array.isArray(value) ? value : [value];
70
+ dispatch({ type: SYNC, payload: newValues });
71
+ // без эмита, так как внешний стейт уже все знает
72
+ commit(newValues, false);
73
+ }, [commit, value]);
74
+ // используется для креста на тригере
75
+ const clearSelectedValuesByTrigger = useCallback(() => {
76
+ dispatch({ type: CLEAR });
77
+ commit([]);
78
+ }, [commit]);
79
+ // для кнопки очистки в пикере
80
+ const clearSelectedValues = useCallback((applyOnClear = true) => {
81
+ dispatch({ type: CLEAR });
82
+ if (applyOnClear) {
83
+ commit([]);
84
+ }
85
+ }, [commit]);
86
+ // сброс стейта в пикере на кнопке сброса
87
+ const setInitialValues = useCallback((applyOnClear = true) => {
88
+ dispatch({ type: SET, payload: mountBaselineRef.current });
89
+ if (applyOnClear) {
90
+ commit(mountBaselineRef.current);
91
+ }
92
+ }, [commit]);
93
+ // откат черновика при закрытии пикера
94
+ const setInitialValuesOnClosePicker = useCallback(() => {
95
+ const { openBaseline, committed } = snapshotsRef.current;
96
+ // если за сессию открытия ничего не закоммитили => возвращаем черновик к бейзлайну открытия
97
+ const nothingCommittedSinceOpen = isArraysEquals(openBaseline.map((item) => item.value), committed.map((item) => item.value));
98
+ if (nothingCommittedSinceOpen) {
99
+ dispatch({ type: SET, payload: openBaseline });
100
+ }
101
+ }, []);
102
+ const onChange = useCallback((value, needToPut) => {
103
+ // эффектам нужно новое значение до применения стейта
104
+ const newValue = getToggledValues(state, value, needToPut, multiple);
105
+ dispatch({ type: TOGGLE, value, needToPut, multiple });
106
+ onSelectOption?.(newValue);
107
+ if (commitModel === 'instant') {
108
+ commit(newValue);
109
+ }
110
+ }, [commitModel, multiple, state, commit, onSelectOption]);
111
+ // применение черновик
112
+ const setSelectedValues = useCallback(() => {
113
+ commit(state);
114
+ }, [commit, state]);
115
+ // флаг для алерта при несохраненных изменениях
116
+ const hasUnsavedChanges = !isArraysEquals(snapshotsRef.current.committed.map((item) => item.value), state.map((item) => item.value));
117
+ const values = (multiple ? state : state[0]);
118
+ const committedValues = (multiple ? committed : committed[0]);
119
+ return useMemo(() => ({
120
+ clearSelectedValues,
121
+ clearSelectedValuesByTrigger,
122
+ committedValues,
123
+ onChange,
124
+ setInitialValues,
125
+ setInitialValuesOnClosePicker,
126
+ setSelectedValues,
127
+ valuesLength: state.length,
128
+ values,
129
+ hasUnsavedChanges,
130
+ }), [
131
+ clearSelectedValues,
132
+ clearSelectedValuesByTrigger,
133
+ committedValues,
134
+ onChange,
135
+ setInitialValues,
136
+ setInitialValuesOnClosePicker,
137
+ setSelectedValues,
138
+ state.length,
139
+ values,
140
+ hasUnsavedChanges,
141
+ ]);
142
+ };
143
+
144
+ export { getToggledValues, useSelectState };
145
+ //# sourceMappingURL=useSelectState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSelectState.js","sources":["src/internal/useSelectState.ts"],"sourcesContent":["import { useCallback, useReducer, useEffect, useRef, useMemo, useState } from 'react';\n\nimport { StaticDataFetcherItem } from '@hh.ru/magritte-common-data-provider';\nimport { useSelectConfig } from '@hh.ru/magritte-ui-new-select/internal/ConfigContext';\nimport { useVisibility } from '@hh.ru/magritte-ui-new-select/internal/VisibilityContext';\nimport { isArraysEquals } from '@hh.ru/magritte-ui-new-select/internal/utils';\nimport { ValueTypeHelper, OnChangeAction } from '@hh.ru/magritte-ui-new-select/types';\n\nconst TOGGLE = 'TOGGLE';\nconst SET = 'SET';\nconst SYNC = 'SYNC';\nconst CLEAR = 'CLEAR';\n\ntype SelectStateAction<DataType extends StaticDataFetcherItem> =\n | { type: typeof TOGGLE; value: DataType; needToPut: boolean; multiple?: boolean }\n | { type: typeof SET; payload: DataType[] }\n | { type: typeof SYNC; payload: DataType[] }\n | { type: typeof CLEAR };\n\nexport const getToggledValues = <DataType extends StaticDataFetcherItem>(\n state: DataType[],\n value: DataType,\n needToPut: boolean,\n multiple?: boolean\n): DataType[] => {\n if (!multiple) {\n return [value];\n }\n return needToPut ? [...state, value] : state.filter((item) => item.value !== value.value);\n};\n\nconst reducer = <DataType extends StaticDataFetcherItem>(\n state: DataType[],\n action: SelectStateAction<DataType>\n): DataType[] => {\n switch (action.type) {\n case TOGGLE:\n return getToggledValues(state, action.value, action.needToPut, action.multiple);\n case SET:\n return [...action.payload];\n case SYNC:\n // синхронизация из внешнего value\n return isArraysEquals(action.payload, state) ? state : [...action.payload];\n case CLEAR:\n return [];\n default:\n return state;\n }\n};\n\nexport interface UseSelectState<MultipleType extends boolean, DataType extends StaticDataFetcherItem> {\n clearSelectedValues: VoidFunction;\n clearSelectedValuesByTrigger: VoidFunction;\n committedValues: ValueTypeHelper<MultipleType, DataType>;\n setInitialValues: VoidFunction;\n setInitialValuesOnClosePicker: VoidFunction;\n values: ValueTypeHelper<MultipleType, DataType>;\n valuesLength: number;\n hasUnsavedChanges: boolean;\n onChange: OnChangeAction<DataType>;\n setSelectedValues: VoidFunction;\n}\n\nexport const useSelectState = <DataType extends StaticDataFetcherItem, MultipleType extends boolean>(\n value: DataType[] | DataType,\n onChangeExternal?: (value: ValueTypeHelper<MultipleType, DataType>) => void,\n onSelectOption?: (values: DataType[]) => void\n): UseSelectState<MultipleType, DataType> => {\n const { showOptions } = useVisibility();\n const { multiple, commitModel } = useSelectConfig();\n const valuesAsArray = Array.isArray(value) ? value : [value];\n const [state, dispatch] = useReducer(reducer<DataType>, valuesAsArray);\n\n /**\n * внешние снапшоты значений\n * committed - отдано наружу (есть гард onChange)\n * openBaseline - черновик на момент открытия пикера\n */\n const snapshotsRef = useRef({ committed: valuesAsArray, openBaseline: [] as DataType[] });\n // реактивная копия committed для отрисовки (триггер показывает применённое, а не черновик)\n const [committed, setCommitted] = useState(valuesAsArray);\n const mountBaselineRef = useRef(valuesAsArray);\n\n // эмит и гард\n const commit = useCallback(\n (values: DataType[], emit = true) => {\n if (emit) {\n const hasUnsavedChanges = !isArraysEquals<string>(\n snapshotsRef.current.committed.map((item) => item.value),\n values.map((item) => item.value)\n );\n if (hasUnsavedChanges) {\n onChangeExternal?.((multiple ? values : values[0]) as ValueTypeHelper<MultipleType, DataType>);\n }\n }\n snapshotsRef.current.committed = values;\n setCommitted((prev) =>\n isArraysEquals<string>(\n prev.map((item) => item.value),\n values.map((item) => item.value)\n )\n ? prev\n : values\n );\n },\n [multiple, onChangeExternal]\n );\n\n // снимок черновика на открытии пикера\n useEffect(() => {\n if (showOptions) {\n snapshotsRef.current.openBaseline = state;\n }\n // eslint-disable-next-line disable-autofix/react-hooks/exhaustive-deps\n }, [showOptions]);\n\n // синхронизация с внешним value\n useEffect(() => {\n const newValues = Array.isArray(value) ? value : [value];\n dispatch({ type: SYNC, payload: newValues });\n // без эмита, так как внешний стейт уже все знает\n commit(newValues, false);\n }, [commit, value]);\n\n // используется для креста на тригере\n const clearSelectedValuesByTrigger = useCallback(() => {\n dispatch({ type: CLEAR });\n commit([]);\n }, [commit]);\n\n // для кнопки очистки в пикере\n const clearSelectedValues = useCallback(\n (applyOnClear = true) => {\n dispatch({ type: CLEAR });\n if (applyOnClear) {\n commit([]);\n }\n },\n [commit]\n );\n\n // сброс стейта в пикере на кнопке сброса\n const setInitialValues = useCallback(\n (applyOnClear = true) => {\n dispatch({ type: SET, payload: mountBaselineRef.current });\n if (applyOnClear) {\n commit(mountBaselineRef.current);\n }\n },\n [commit]\n );\n\n // откат черновика при закрытии пикера\n const setInitialValuesOnClosePicker = useCallback(() => {\n const { openBaseline, committed } = snapshotsRef.current;\n // если за сессию открытия ничего не закоммитили => возвращаем черновик к бейзлайну открытия\n const nothingCommittedSinceOpen = isArraysEquals<string>(\n openBaseline.map((item) => item.value),\n committed.map((item) => item.value)\n );\n if (nothingCommittedSinceOpen) {\n dispatch({ type: SET, payload: openBaseline });\n }\n }, []);\n\n const onChange = useCallback<UseSelectState<MultipleType, DataType>['onChange']>(\n (value, needToPut) => {\n // эффектам нужно новое значение до применения стейта\n const newValue = getToggledValues(state, value, needToPut, multiple);\n dispatch({ type: TOGGLE, value, needToPut, multiple });\n onSelectOption?.(newValue);\n if (commitModel === 'instant') {\n commit(newValue);\n }\n },\n [commitModel, multiple, state, commit, onSelectOption]\n );\n\n // применение черновик\n const setSelectedValues = useCallback(() => {\n commit(state);\n }, [commit, state]);\n\n // флаг для алерта при несохраненных изменениях\n const hasUnsavedChanges = !isArraysEquals<string>(\n snapshotsRef.current.committed.map((item) => item.value),\n state.map((item) => item.value)\n );\n\n const values = (multiple ? state : state[0]) as ValueTypeHelper<MultipleType, DataType>;\n const committedValues = (multiple ? committed : committed[0]) as ValueTypeHelper<MultipleType, DataType>;\n return useMemo<UseSelectState<MultipleType, DataType>>(\n () => ({\n clearSelectedValues,\n clearSelectedValuesByTrigger,\n committedValues,\n onChange,\n setInitialValues,\n setInitialValuesOnClosePicker,\n setSelectedValues,\n valuesLength: state.length,\n values,\n hasUnsavedChanges,\n }),\n [\n clearSelectedValues,\n clearSelectedValuesByTrigger,\n committedValues,\n onChange,\n setInitialValues,\n setInitialValuesOnClosePicker,\n setSelectedValues,\n state.length,\n values,\n hasUnsavedChanges,\n ]\n );\n};\n"],"names":[],"mappings":";;;;;;;AAQA,MAAM,MAAM,GAAG,QAAQ,CAAC;AACxB,MAAM,GAAG,GAAG,KAAK,CAAC;AAClB,MAAM,IAAI,GAAG,MAAM,CAAC;AACpB,MAAM,KAAK,GAAG,OAAO,CAAC;AAQf,MAAM,gBAAgB,GAAG,CAC5B,KAAiB,EACjB,KAAe,EACf,SAAkB,EAClB,QAAkB,KACN;IACZ,IAAI,CAAC,QAAQ,EAAE;QACX,OAAO,CAAC,KAAK,CAAC,CAAC;KAClB;AACD,IAAA,OAAO,SAAS,GAAG,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC,CAAC;AAC9F,EAAE;AAEF,MAAM,OAAO,GAAG,CACZ,KAAiB,EACjB,MAAmC,KACvB;AACZ,IAAA,QAAQ,MAAM,CAAC,IAAI;AACf,QAAA,KAAK,MAAM;AACP,YAAA,OAAO,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;AACpF,QAAA,KAAK,GAAG;AACJ,YAAA,OAAO,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;AAC/B,QAAA,KAAK,IAAI;;YAEL,OAAO,cAAc,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;AAC/E,QAAA,KAAK,KAAK;AACN,YAAA,OAAO,EAAE,CAAC;AACd,QAAA;AACI,YAAA,OAAO,KAAK,CAAC;KACpB;AACL,CAAC,CAAC;AAeW,MAAA,cAAc,GAAG,CAC1B,KAA4B,EAC5B,gBAA2E,EAC3E,cAA6C,KACL;AACxC,IAAA,MAAM,EAAE,WAAW,EAAE,GAAG,aAAa,EAAE,CAAC;IACxC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,eAAe,EAAE,CAAC;AACpD,IAAA,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC;AAC7D,IAAA,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,UAAU,EAAC,OAAiB,GAAE,aAAa,CAAC,CAAC;AAEvE;;;;AAIG;AACH,IAAA,MAAM,YAAY,GAAG,MAAM,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,EAAgB,EAAE,CAAC,CAAC;;IAE1F,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC;AAC1D,IAAA,MAAM,gBAAgB,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;;IAG/C,MAAM,MAAM,GAAG,WAAW,CACtB,CAAC,MAAkB,EAAE,IAAI,GAAG,IAAI,KAAI;QAChC,IAAI,IAAI,EAAE;AACN,YAAA,MAAM,iBAAiB,GAAG,CAAC,cAAc,CACrC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,EACxD,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,CACnC,CAAC;YACF,IAAI,iBAAiB,EAAE;AACnB,gBAAA,gBAAgB,IAAI,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,EAA6C,CAAC;aAClG;SACJ;AACD,QAAA,YAAY,CAAC,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC;AACxC,QAAA,YAAY,CAAC,CAAC,IAAI,KACd,cAAc,CACV,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,EAC9B,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,CACnC;AACG,cAAE,IAAI;cACJ,MAAM,CACf,CAAC;AACN,KAAC,EACD,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAC/B,CAAC;;IAGF,SAAS,CAAC,MAAK;QACX,IAAI,WAAW,EAAE;AACb,YAAA,YAAY,CAAC,OAAO,CAAC,YAAY,GAAG,KAAK,CAAC;SAC7C;;AAEL,KAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;;IAGlB,SAAS,CAAC,MAAK;AACX,QAAA,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC;QACzD,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;;AAE7C,QAAA,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;AAC7B,KAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;;AAGpB,IAAA,MAAM,4BAA4B,GAAG,WAAW,CAAC,MAAK;AAClD,QAAA,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC1B,MAAM,CAAC,EAAE,CAAC,CAAC;AACf,KAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;;IAGb,MAAM,mBAAmB,GAAG,WAAW,CACnC,CAAC,YAAY,GAAG,IAAI,KAAI;AACpB,QAAA,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC1B,IAAI,YAAY,EAAE;YACd,MAAM,CAAC,EAAE,CAAC,CAAC;SACd;AACL,KAAC,EACD,CAAC,MAAM,CAAC,CACX,CAAC;;IAGF,MAAM,gBAAgB,GAAG,WAAW,CAChC,CAAC,YAAY,GAAG,IAAI,KAAI;AACpB,QAAA,QAAQ,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3D,IAAI,YAAY,EAAE;AACd,YAAA,MAAM,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;SACpC;AACL,KAAC,EACD,CAAC,MAAM,CAAC,CACX,CAAC;;AAGF,IAAA,MAAM,6BAA6B,GAAG,WAAW,CAAC,MAAK;QACnD,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC;;AAEzD,QAAA,MAAM,yBAAyB,GAAG,cAAc,CAC5C,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,EACtC,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,CACtC,CAAC;QACF,IAAI,yBAAyB,EAAE;YAC3B,QAAQ,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;SAClD;KACJ,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,QAAQ,GAAG,WAAW,CACxB,CAAC,KAAK,EAAE,SAAS,KAAI;;AAEjB,QAAA,MAAM,QAAQ,GAAG,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AACrE,QAAA,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;AACvD,QAAA,cAAc,GAAG,QAAQ,CAAC,CAAC;AAC3B,QAAA,IAAI,WAAW,KAAK,SAAS,EAAE;YAC3B,MAAM,CAAC,QAAQ,CAAC,CAAC;SACpB;AACL,KAAC,EACD,CAAC,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,CAAC,CACzD,CAAC;;AAGF,IAAA,MAAM,iBAAiB,GAAG,WAAW,CAAC,MAAK;QACvC,MAAM,CAAC,KAAK,CAAC,CAAC;AAClB,KAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;;AAGpB,IAAA,MAAM,iBAAiB,GAAG,CAAC,cAAc,CACrC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,EACxD,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,CAClC,CAAC;AAEF,IAAA,MAAM,MAAM,IAAI,QAAQ,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAA4C,CAAC;AACxF,IAAA,MAAM,eAAe,IAAI,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,CAA4C,CAAC;AACzG,IAAA,OAAO,OAAO,CACV,OAAO;QACH,mBAAmB;QACnB,4BAA4B;QAC5B,eAAe;QACf,QAAQ;QACR,gBAAgB;QAChB,6BAA6B;QAC7B,iBAAiB;QACjB,YAAY,EAAE,KAAK,CAAC,MAAM;QAC1B,MAAM;QACN,iBAAiB;AACpB,KAAA,CAAC,EACF;QACI,mBAAmB;QACnB,4BAA4B;QAC5B,eAAe;QACf,QAAQ;QACR,gBAAgB;QAChB,6BAA6B;QAC7B,iBAAiB;AACjB,QAAA,KAAK,CAAC,MAAM;QACZ,MAAM;QACN,iBAAiB;AACpB,KAAA,CACJ,CAAC;AACN;;;;"}
@@ -0,0 +1,7 @@
1
+ import { SyntheticEvent } from 'react';
2
+ import { StaticDataFetcherItem } from '@hh.ru/magritte-common-data-provider';
3
+ export declare const isArraysEquals: <T>(arrayA: T[], arrayB: T[]) => boolean;
4
+ export declare const assertSelectContext: <T>(value: T | null, hookName: string) => T;
5
+ export declare const useEventCallback: <Callback extends (...args: never[]) => unknown>(callback: Callback | undefined) => Callback;
6
+ export declare const useSelectedLabels: <DataType extends StaticDataFetcherItem>(values: DataType | DataType[]) => string[];
7
+ export declare const focusOptionsList: (event: SyntheticEvent, drop: HTMLElement | null | undefined, direction: "up" | "down") => void;
@@ -0,0 +1,42 @@
1
+ import { useRef, useLayoutEffect, useCallback, useMemo } from 'react';
2
+ import { findNextFocusableElement } from '@hh.ru/magritte-common-keyboard';
3
+
4
+ // но не проверяет порядок и дубликаты
5
+ const isArraysEquals = (arrayA, arrayB) => arrayA.length === arrayB.length && arrayA.every((item) => arrayB.includes(item));
6
+ // убирает `null` с контекста и падает с внятной ошибкой
7
+ const assertSelectContext = (value, hookName) => {
8
+ if (value === null) {
9
+ throw new Error(`${hookName} must be used within <Select>`);
10
+ }
11
+ return value;
12
+ };
13
+ // возвращает стабильную ссылку на колбэк (не вызывать в рендере)
14
+ const useEventCallback = (callback) => {
15
+ const callbackRef = useRef(callback);
16
+ useLayoutEffect(() => {
17
+ callbackRef.current = callback;
18
+ });
19
+ const stableCallback = useCallback((...args) => {
20
+ return callbackRef.current?.(...args);
21
+ }, []);
22
+ return stableCallback;
23
+ };
24
+ // placeholder в тригерах собирается одинаково
25
+ const useSelectedLabels = (values) => {
26
+ return useMemo(() => {
27
+ const selectedItems = Array.isArray(values) ? values : [values].filter(Boolean);
28
+ return selectedItems.map((item) => item.text ?? item.value);
29
+ }, [values]);
30
+ };
31
+ const focusOptionsList = (event, drop, direction) => {
32
+ if (drop) {
33
+ const nextFocusableElement = findNextFocusableElement(drop, null, direction, 'list');
34
+ if (nextFocusableElement !== null) {
35
+ event.preventDefault();
36
+ nextFocusableElement.focus();
37
+ }
38
+ }
39
+ };
40
+
41
+ export { assertSelectContext, focusOptionsList, isArraysEquals, useEventCallback, useSelectedLabels };
42
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sources":["src/internal/utils.ts"],"sourcesContent":["import { useRef, useCallback, useLayoutEffect, useMemo, SyntheticEvent } from 'react';\n\nimport { StaticDataFetcherItem } from '@hh.ru/magritte-common-data-provider';\nimport { findNextFocusableElement } from '@hh.ru/magritte-common-keyboard';\n\n// но не проверяет порядок и дубликаты\nexport const isArraysEquals = <T>(arrayA: T[], arrayB: T[]): boolean =>\n arrayA.length === arrayB.length && arrayA.every((item) => arrayB.includes(item));\n\n// убирает `null` с контекста и падает с внятной ошибкой\nexport const assertSelectContext = <T>(value: T | null, hookName: string): T => {\n if (value === null) {\n throw new Error(`${hookName} must be used within <Select>`);\n }\n return value;\n};\n\n// возвращает стабильную ссылку на колбэк (не вызывать в рендере)\nexport const useEventCallback = <Callback extends (...args: never[]) => unknown>(\n callback: Callback | undefined\n): Callback => {\n const callbackRef = useRef(callback);\n\n useLayoutEffect(() => {\n callbackRef.current = callback;\n });\n\n const stableCallback = useCallback((...args: Parameters<Callback>) => {\n return callbackRef.current?.(...args);\n }, []) as Callback;\n\n return stableCallback;\n};\n\n// placeholder в тригерах собирается одинаково\nexport const useSelectedLabels = <DataType extends StaticDataFetcherItem>(values: DataType | DataType[]): string[] => {\n return useMemo(() => {\n const selectedItems = Array.isArray(values) ? values : [values].filter(Boolean);\n return selectedItems.map((item) => item.text ?? item.value);\n }, [values]);\n};\n\nexport const focusOptionsList = (\n event: SyntheticEvent,\n drop: HTMLElement | null | undefined,\n direction: 'up' | 'down'\n): void => {\n if (drop) {\n const nextFocusableElement = findNextFocusableElement(drop, null, direction, 'list');\n if (nextFocusableElement !== null) {\n event.preventDefault();\n nextFocusableElement.focus();\n }\n }\n};\n"],"names":[],"mappings":";;;AAKA;AACO,MAAM,cAAc,GAAG,CAAI,MAAW,EAAE,MAAW,KACtD,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;AAErF;MACa,mBAAmB,GAAG,CAAI,KAAe,EAAE,QAAgB,KAAO;AAC3E,IAAA,IAAI,KAAK,KAAK,IAAI,EAAE;AAChB,QAAA,MAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,CAAA,6BAAA,CAA+B,CAAC,CAAC;KAC/D;AACD,IAAA,OAAO,KAAK,CAAC;AACjB,EAAE;AAEF;AACa,MAAA,gBAAgB,GAAG,CAC5B,QAA8B,KACpB;AACV,IAAA,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAErC,eAAe,CAAC,MAAK;AACjB,QAAA,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;AACnC,KAAC,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,GAAG,IAA0B,KAAI;QACjE,OAAO,WAAW,CAAC,OAAO,GAAG,GAAG,IAAI,CAAC,CAAC;KACzC,EAAE,EAAE,CAAa,CAAC;AAEnB,IAAA,OAAO,cAAc,CAAC;AAC1B,EAAE;AAEF;AACa,MAAA,iBAAiB,GAAG,CAAyC,MAA6B,KAAc;IACjH,OAAO,OAAO,CAAC,MAAK;QAChB,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAChF,QAAA,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;AAChE,KAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;AACjB,EAAE;AAEW,MAAA,gBAAgB,GAAG,CAC5B,KAAqB,EACrB,IAAoC,EACpC,SAAwB,KAClB;IACN,IAAI,IAAI,EAAE;AACN,QAAA,MAAM,oBAAoB,GAAG,wBAAwB,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AACrF,QAAA,IAAI,oBAAoB,KAAK,IAAI,EAAE;YAC/B,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,oBAAoB,CAAC,KAAK,EAAE,CAAC;SAChC;KACJ;AACL;;;;"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@hh.ru/magritte-ui-new-select",
3
+ "version": "1.0.1",
4
+ "main": "index.js",
5
+ "types": "index.d.ts",
6
+ "sideEffects": [
7
+ "index.css"
8
+ ],
9
+ "scripts": {
10
+ "build": "yarn root:build $(pwd)",
11
+ "build-test-branch": "yarn root:build-test-branch $(pwd)",
12
+ "prepack": "yarn root:prepack $(pwd)",
13
+ "postpublish": "yarn root:postpublish $(pwd)",
14
+ "stylelint-check": "yarn root:stylelint-check $(pwd)",
15
+ "eslint-check": "yarn root:eslint-check $(pwd)",
16
+ "ts-config": "yarn root:ts-config $(pwd)",
17
+ "ts-check": "yarn root:ts-check $(pwd)",
18
+ "test": "yarn root:test $(pwd)",
19
+ "watch": "yarn root:watch $(pwd)"
20
+ },
21
+ "peerDependencies": {
22
+ "classnames": ">=2.3.2",
23
+ "react": ">=18.2.0"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "dependencies": {
29
+ "@hh.ru/magritte-common-data-provider": "1.3.7",
30
+ "@hh.ru/magritte-common-keyboard": "4.3.4",
31
+ "@hh.ru/magritte-common-use-translation": "3.1.0",
32
+ "@hh.ru/magritte-internal-slot-registry": "1.1.0",
33
+ "@hh.ru/magritte-ui-bottom-sheet": "9.4.11",
34
+ "@hh.ru/magritte-ui-breakpoint": "6.1.1",
35
+ "@hh.ru/magritte-ui-button": "7.2.17",
36
+ "@hh.ru/magritte-ui-cell": "7.2.29",
37
+ "@hh.ru/magritte-ui-checkable-card": "5.0.36",
38
+ "@hh.ru/magritte-ui-drop": "12.3.35",
39
+ "@hh.ru/magritte-ui-input": "9.4.31",
40
+ "@hh.ru/magritte-ui-title": "8.3.4",
41
+ "@hh.ru/magritte-ui-trigger": "4.3.26"
42
+ },
43
+ "gitHead": "73e0fbef72d8f731bbda034d85b96c50f7fe3bd3"
44
+ }
package/types.d.ts ADDED
@@ -0,0 +1,55 @@
1
+ import { Dispatch, SetStateAction, ReactNode } from 'react';
2
+ import { StaticDataFetcherItem } from '@hh.ru/magritte-common-data-provider';
3
+ import { DataProviderProps } from '@hh.ru/magritte-ui-new-select/dataProviderTypes';
4
+ export type DelimiterOptionType = 'delimiter';
5
+ export type SelectOptionType = 'checkbox' | 'label' | 'radio';
6
+ export type CommitModel = 'instant' | 'confirm';
7
+ export interface OnChangeAction<DataType extends StaticDataFetcherItem> {
8
+ (value: DataType, needToPut: boolean): void;
9
+ }
10
+ export interface OnOptionsChangeCallback<DataType extends StaticDataFetcherItem> {
11
+ (options: SelectOption<DataType>[]): void;
12
+ }
13
+ export interface SearchValue {
14
+ searchValue: string;
15
+ }
16
+ export interface OptionsCount {
17
+ optionsCount: number;
18
+ }
19
+ export interface SetSearchValue {
20
+ setSearchValue: Dispatch<SetStateAction<string>>;
21
+ }
22
+ export interface SelectOption<DataType extends StaticDataFetcherItem> {
23
+ data: DataType;
24
+ disabled?: boolean;
25
+ type?: DelimiterOptionType;
26
+ }
27
+ export type ValueTypeHelper<MultipleType extends boolean, DataType extends StaticDataFetcherItem> = [
28
+ MultipleType
29
+ ] extends [true] ? DataType[] : DataType;
30
+ export interface SelectBaseProps<MultipleType extends boolean, DataType extends StaticDataFetcherItem> {
31
+ /** Контент Select (Trigger, пикеры и т.д.) */
32
+ children: ReactNode;
33
+ commitModel?: CommitModel;
34
+ /** Максимальное количество выбранных опций */
35
+ maxSelectedOptions?: number;
36
+ /** Флаг включения мультивыбора */
37
+ multiple?: MultipleType;
38
+ /** Name атрибут селекта */
39
+ name?: string;
40
+ /** Обработчик события потери фокуса */
41
+ onBlur?: VoidFunction;
42
+ /** onChange хендлер изменения селекта, для multiple возвращает массив выбранных значений */
43
+ onChange: (value: ValueTypeHelper<MultipleType, DataType>) => void;
44
+ /** Обработчик события получения фокуса */
45
+ onFocus?: VoidFunction;
46
+ /** Обработчик получения опций */
47
+ onOptionsChange?: OnOptionsChangeCallback<DataType>;
48
+ /** Обработчик события выбора option */
49
+ onSelectOption?: (values: DataType[]) => void;
50
+ /** Значение Select, для multiple — это массив */
51
+ value?: ValueTypeHelper<MultipleType, DataType>;
52
+ /** Флаг включения загрузки */
53
+ loading?: boolean;
54
+ }
55
+ export type SelectProps<MultipleType extends boolean, DataType extends StaticDataFetcherItem> = SelectBaseProps<MultipleType, DataType> & DataProviderProps<DataType>;
package/types.js ADDED
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=types.js.map
package/types.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}