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

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 (57) hide show
  1. package/Select.d.ts +4 -1
  2. package/Select.js +19 -6
  3. package/Select.js.map +1 -1
  4. package/SelectSearchTrigger.d.ts +3 -0
  5. package/SelectSearchTrigger.js +61 -0
  6. package/SelectSearchTrigger.js.map +1 -0
  7. package/SelectTrigger.d.ts +3 -0
  8. package/SelectTrigger.js +42 -0
  9. package/SelectTrigger.js.map +1 -0
  10. package/dataProviderTypes.js +1 -0
  11. package/index.css +25 -0
  12. package/index.d.ts +1 -0
  13. package/index.js +17 -5
  14. package/index.js.map +1 -1
  15. package/internal/ConfigContext.d.ts +1 -3
  16. package/internal/ConfigContext.js +3 -4
  17. package/internal/ConfigContext.js.map +1 -1
  18. package/internal/NavigationManagement.d.ts +8 -0
  19. package/internal/NavigationManagement.js +29 -0
  20. package/internal/NavigationManagement.js.map +1 -0
  21. package/internal/SearchStateContext.d.ts +10 -0
  22. package/internal/SearchStateContext.js +21 -0
  23. package/internal/SearchStateContext.js.map +1 -0
  24. package/internal/SelectDataProvider.d.ts +2 -2
  25. package/internal/SelectDataProvider.js +6 -4
  26. package/internal/SelectDataProvider.js.map +1 -1
  27. package/internal/SelectStateContext.d.ts +1 -1
  28. package/internal/SelectStateContext.js +1 -0
  29. package/internal/SelectStateContext.js.map +1 -1
  30. package/internal/VisibilityContext.js +1 -0
  31. package/internal/focusOrchestrator.d.ts +35 -0
  32. package/internal/focusOrchestrator.js +93 -0
  33. package/internal/focusOrchestrator.js.map +1 -0
  34. package/internal/slots.js +1 -0
  35. package/internal/useDataProvider.d.ts +8 -0
  36. package/internal/{useSearch.js → useDataProvider.js} +11 -31
  37. package/internal/useDataProvider.js.map +1 -0
  38. package/internal/useFocusWithin.d.ts +11 -0
  39. package/internal/useFocusWithin.js +87 -0
  40. package/internal/useFocusWithin.js.map +1 -0
  41. package/internal/useSelectState.d.ts +1 -1
  42. package/internal/useSelectState.js +1 -0
  43. package/internal/useSelectState.js.map +1 -1
  44. package/internal/utils.d.ts +1 -0
  45. package/internal/utils.js +1 -0
  46. package/internal/utils.js.map +1 -1
  47. package/package.json +4 -2
  48. package/types.d.ts +9 -5
  49. package/types.js +1 -0
  50. package/useSelectTrigger.d.ts +6 -0
  51. package/useSelectTrigger.js +111 -0
  52. package/useSelectTrigger.js.map +1 -0
  53. package/internal/NavigationContext.d.ts +0 -17
  54. package/internal/NavigationContext.js +0 -30
  55. package/internal/NavigationContext.js.map +0 -1
  56. package/internal/useSearch.d.ts +0 -8
  57. package/internal/useSearch.js.map +0 -1
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Идея focusOrchestrator заменить «команду через стейт» (forcedState или что-то такое)
3
+ * на менеджер передачи фокуса по принципу chain of responsibility
4
+ * Разделение:
5
+ * - useFocusWithin - ушёл ли фокус из селекта наружу, обрабатывает onFocus/onBlur (внешние переходы)
6
+ * - focusOrchestrator - кто держит фокус, куда его вернуть (внутренние)
7
+ */
8
+ import { PropsWithChildren, FC } from 'react';
9
+ export declare const FocusTier: {
10
+ readonly Trigger: 0;
11
+ readonly OptionsList: 2;
12
+ };
13
+ export type FocusTier = (typeof FocusTier)[keyof typeof FocusTier];
14
+ /**
15
+ * Функция приёма фокуса кандидатом.
16
+ * Возвращает false, если сейчас принять фокус не может (оркестратор идёт к следующему по приоритету)
17
+ */
18
+ type FocusAcceptor = () => boolean;
19
+ export interface FocusOrchestrator {
20
+ registerCandidate(id: string, tier: FocusTier, accept: FocusAcceptor): () => void;
21
+ /**
22
+ * Оркестратор перебирает кандидатов по приоритету,
23
+ * Останавливается на первом, кто вернул true.
24
+ */
25
+ yieldFocus(): void;
26
+ /**
27
+ * Подписка для useFocusWithin: сигнал «состояние фокуса стабилизировалось».
28
+ * Пока идёт перенос, useFocusWithin откладывает разрешение blur.
29
+ */
30
+ subscribeStability(listener: (isStable: boolean) => void): () => void;
31
+ }
32
+ export declare const useFocusOrchestrator: () => FocusOrchestrator;
33
+ export declare const useFocusCandidate: (id: string, tier: FocusTier, acceptExternal: FocusAcceptor) => void;
34
+ export declare const FocusOrchestratorProvider: FC<PropsWithChildren>;
35
+ export {};
@@ -0,0 +1,93 @@
1
+ import './../index.css';
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { createContext, useContext, useLayoutEffect, useRef, useReducer, useCallback, useMemo } from 'react';
4
+ import { assertSelectContext, useEventCallback } from './utils.js';
5
+ import '@hh.ru/magritte-common-keyboard';
6
+
7
+ // меньшее значение = выше приоритет при возврате фокуса
8
+ const FocusTier = {
9
+ Trigger: 0,
10
+ OptionsList: 2,
11
+ };
12
+ const FocusOrchestratorContext = createContext(null);
13
+ const useFocusOrchestrator = () => {
14
+ return assertSelectContext(useContext(FocusOrchestratorContext), 'useFocusOrchestrator');
15
+ };
16
+ const useFocusCandidate = (id, tier, acceptExternal) => {
17
+ const orchestrator = useFocusOrchestrator();
18
+ const accept = useEventCallback(acceptExternal);
19
+ useLayoutEffect(() => {
20
+ if (!orchestrator) {
21
+ return void 0;
22
+ }
23
+ return orchestrator.registerCandidate(id, tier, accept);
24
+ }, [orchestrator, id, tier, accept]);
25
+ };
26
+ const FocusOrchestratorProvider = ({ children }) => {
27
+ const candidatesRef = useRef(new Map());
28
+ const isStableRef = useRef(true);
29
+ const stabilityListenersRef = useRef(new Set());
30
+ // Изменение version нужно только для запуска useLayoutEffect после commit.
31
+ const [flushVersion, requestFlush] = useReducer((version) => version + 1, 0);
32
+ const setStable = useCallback((isStable) => {
33
+ if (isStableRef.current === isStable) {
34
+ return;
35
+ }
36
+ isStableRef.current = isStable;
37
+ stabilityListenersRef.current.forEach((listener) => {
38
+ listener(isStable);
39
+ });
40
+ }, []);
41
+ const registerCandidate = useCallback((id, tier, accept) => {
42
+ const candidate = {
43
+ tier,
44
+ accept,
45
+ };
46
+ candidatesRef.current.set(id, candidate);
47
+ return () => {
48
+ if (candidatesRef.current.get(id) === candidate) {
49
+ candidatesRef.current.delete(id);
50
+ }
51
+ };
52
+ }, []);
53
+ const hasFocusRequestRef = useRef(false);
54
+ const yieldFocus = useCallback(() => {
55
+ hasFocusRequestRef.current = true;
56
+ // С этого момента useFocusWithin может отложить blur:
57
+ setStable(false);
58
+ // Не вызываем accept прямо здесь, ждем когда рякт откомитит
59
+ requestFlush();
60
+ }, [setStable]);
61
+ useLayoutEffect(() => {
62
+ if (!hasFocusRequestRef.current) {
63
+ return;
64
+ }
65
+ const candidates = [...candidatesRef.current.values()].sort((a, b) => a.tier - b.tier);
66
+ try {
67
+ for (const candidate of candidates) {
68
+ if (candidate.accept()) {
69
+ break;
70
+ }
71
+ }
72
+ }
73
+ finally {
74
+ setStable(true);
75
+ }
76
+ }, [flushVersion, setStable]);
77
+ const subscribeStability = useCallback((listener) => {
78
+ stabilityListenersRef.current.add(listener);
79
+ listener(isStableRef.current);
80
+ return () => {
81
+ stabilityListenersRef.current.delete(listener);
82
+ };
83
+ }, []);
84
+ const value = useMemo(() => ({
85
+ registerCandidate,
86
+ yieldFocus,
87
+ subscribeStability,
88
+ }), [registerCandidate, yieldFocus, subscribeStability]);
89
+ return jsx(FocusOrchestratorContext.Provider, { value: value, children: children });
90
+ };
91
+
92
+ export { FocusOrchestratorProvider, FocusTier, useFocusCandidate, useFocusOrchestrator };
93
+ //# sourceMappingURL=focusOrchestrator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focusOrchestrator.js","sources":["src/internal/focusOrchestrator.tsx"],"sourcesContent":["/**\n * Идея focusOrchestrator заменить «команду через стейт» (forcedState или что-то такое)\n * на менеджер передачи фокуса по принципу chain of responsibility\n * Разделение:\n * - useFocusWithin - ушёл ли фокус из селекта наружу, обрабатывает onFocus/onBlur (внешние переходы)\n * - focusOrchestrator - кто держит фокус, куда его вернуть (внутренние)\n */\nimport {\n createContext,\n PropsWithChildren,\n useCallback,\n useContext,\n useLayoutEffect,\n useMemo,\n useReducer,\n useRef,\n FC,\n} from 'react';\n\nimport { assertSelectContext, useEventCallback } from '@hh.ru/magritte-ui-new-select/internal/utils';\n\n// меньшее значение = выше приоритет при возврате фокуса\nexport const FocusTier = {\n Trigger: 0,\n OptionsList: 2,\n} as const;\n\nexport type FocusTier = (typeof FocusTier)[keyof typeof FocusTier];\n\n/**\n * Функция приёма фокуса кандидатом.\n * Возвращает false, если сейчас принять фокус не может (оркестратор идёт к следующему по приоритету)\n */\ntype FocusAcceptor = () => boolean;\n\nexport interface FocusOrchestrator {\n // кандидат регистрируется, только когда реально фокусабелен\n registerCandidate(id: string, tier: FocusTier, accept: FocusAcceptor): () => void;\n\n /**\n * Оркестратор перебирает кандидатов по приоритету,\n * Останавливается на первом, кто вернул true.\n */\n yieldFocus(): void;\n\n /**\n * Подписка для useFocusWithin: сигнал «состояние фокуса стабилизировалось».\n * Пока идёт перенос, useFocusWithin откладывает разрешение blur.\n */\n subscribeStability(listener: (isStable: boolean) => void): () => void;\n}\n\ninterface FocusCandidate {\n tier: FocusTier;\n accept: FocusAcceptor;\n}\n\nconst FocusOrchestratorContext = createContext<FocusOrchestrator | null>(null);\n\nexport const useFocusOrchestrator = (): FocusOrchestrator => {\n return assertSelectContext(useContext(FocusOrchestratorContext), 'useFocusOrchestrator');\n};\n\nexport const useFocusCandidate = (id: string, tier: FocusTier, acceptExternal: FocusAcceptor): void => {\n const orchestrator = useFocusOrchestrator();\n const accept = useEventCallback(acceptExternal);\n\n useLayoutEffect(() => {\n if (!orchestrator) {\n return void 0;\n }\n return orchestrator.registerCandidate(id, tier, accept);\n }, [orchestrator, id, tier, accept]);\n};\n\nexport const FocusOrchestratorProvider: FC<PropsWithChildren> = ({ children }) => {\n const candidatesRef = useRef<Map<string, FocusCandidate>>(new Map());\n\n const isStableRef = useRef(true);\n const stabilityListenersRef = useRef(new Set<(isStable: boolean) => void>());\n\n // Изменение version нужно только для запуска useLayoutEffect после commit.\n const [flushVersion, requestFlush] = useReducer((version: number) => version + 1, 0);\n\n const setStable = useCallback((isStable: boolean) => {\n if (isStableRef.current === isStable) {\n return;\n }\n isStableRef.current = isStable;\n stabilityListenersRef.current.forEach((listener) => {\n listener(isStable);\n });\n }, []);\n\n const registerCandidate = useCallback((id: string, tier: FocusTier, accept: FocusAcceptor): (() => void) => {\n const candidate: FocusCandidate = {\n tier,\n accept,\n };\n candidatesRef.current.set(id, candidate);\n return () => {\n if (candidatesRef.current.get(id) === candidate) {\n candidatesRef.current.delete(id);\n }\n };\n }, []);\n\n const hasFocusRequestRef = useRef(false);\n\n const yieldFocus = useCallback(() => {\n hasFocusRequestRef.current = true;\n // С этого момента useFocusWithin может отложить blur:\n setStable(false);\n // Не вызываем accept прямо здесь, ждем когда рякт откомитит\n requestFlush();\n }, [setStable]);\n\n useLayoutEffect(() => {\n if (!hasFocusRequestRef.current) {\n return;\n }\n const candidates = [...candidatesRef.current.values()].sort((a, b) => a.tier - b.tier);\n try {\n for (const candidate of candidates) {\n if (candidate.accept()) {\n break;\n }\n }\n } finally {\n setStable(true);\n }\n }, [flushVersion, setStable]);\n\n const subscribeStability = useCallback((listener: (isStable: boolean) => void): (() => void) => {\n stabilityListenersRef.current.add(listener);\n listener(isStableRef.current);\n return () => {\n stabilityListenersRef.current.delete(listener);\n };\n }, []);\n\n const value = useMemo<FocusOrchestrator>(\n () => ({\n registerCandidate,\n yieldFocus,\n subscribeStability,\n }),\n [registerCandidate, yieldFocus, subscribeStability]\n );\n\n return <FocusOrchestratorContext.Provider value={value}>{children}</FocusOrchestratorContext.Provider>;\n};\n"],"names":["_jsx"],"mappings":"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"}
package/internal/slots.js CHANGED
@@ -1,3 +1,4 @@
1
+ import './../index.css';
1
2
  import { createSlotRegistry } from '@hh.ru/magritte-internal-slot-registry';
2
3
 
3
4
  // наличие кнопок влияет на аттрибуты доступности в пикере
@@ -0,0 +1,8 @@
1
+ import { StaticDataFetcherItem, DataProvider as DataProviderType } from '@hh.ru/magritte-common-data-provider';
2
+ import { OnOptionsChangeCallback, SelectOption } from '@hh.ru/magritte-ui-new-select/types';
3
+ export interface UseDataProvider<DataType extends StaticDataFetcherItem> {
4
+ internalError?: boolean;
5
+ internalLoading?: boolean;
6
+ optionList: SelectOption<DataType>[];
7
+ }
8
+ export declare const useDataProvider: <DataType extends StaticDataFetcherItem>(dataProvider: DataProviderType<DataType>, onOptionsChangeExternal?: OnOptionsChangeCallback<DataType>) => UseDataProvider<DataType>;
@@ -1,23 +1,21 @@
1
+ import './../index.css';
1
2
  import { useState, useRef, useCallback, useEffect, useMemo } from 'react';
2
3
  import { DataProviderError } from '@hh.ru/magritte-common-data-provider';
4
+ import { useSearchState } from './SearchStateContext.js';
3
5
  import { useVisibility } from './VisibilityContext.js';
4
6
  import 'react/jsx-runtime';
5
7
  import './utils.js';
6
8
  import '@hh.ru/magritte-common-keyboard';
7
9
 
8
10
  const EMPTY_OPTION_LIST = [];
9
- const useSearch = (dataProvider, onOptionsChangeExternal) => {
11
+ const useDataProvider = (dataProvider, onOptionsChangeExternal) => {
12
+ const { searchValue } = useSearchState();
10
13
  const { showOptions } = useVisibility();
11
- const [searchValue, setSearchValue] = useState('');
12
14
  const [optionList, setOptionList] = useState(EMPTY_OPTION_LIST);
13
15
  const [internalError, setInternalError] = useState(false);
14
16
  const [optionsLoading, setOptionsLoading] = useState(false);
15
17
  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) => {
18
+ const searchRequest = useCallback((text) => {
21
19
  if (!dataProvider) {
22
20
  return;
23
21
  }
@@ -69,27 +67,11 @@ const useSearch = (dataProvider, onOptionsChangeExternal) => {
69
67
  notifiedListRef.current = optionList;
70
68
  onOptionsChangeExternal?.(optionList);
71
69
  }, [optionList, onOptionsChangeExternal]);
72
- // обрабатываем обновление провайдера из клиента
70
+ // фетчим при смене провайдера или поискового запроса
73
71
  useEffect(() => {
74
- updateOptionsFromProvider(searchValue);
72
+ searchRequest(searchValue);
75
73
  // 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]);*/
74
+ }, [dataProvider, searchValue]);
93
75
  // после анмаунта надо отменить все запросы
94
76
  useEffect(() => () => {
95
77
  currentRequestRef.current?.cancel();
@@ -98,10 +80,8 @@ const useSearch = (dataProvider, onOptionsChangeExternal) => {
98
80
  internalError,
99
81
  internalLoading: optionsLoading && showOptions,
100
82
  optionList,
101
- searchValue,
102
- setSearchValue,
103
- }), [internalError, optionList, optionsLoading, searchValue, setSearchValue, showOptions]);
83
+ }), [internalError, optionList, optionsLoading, showOptions]);
104
84
  };
105
85
 
106
- export { useSearch };
107
- //# sourceMappingURL=useSearch.js.map
86
+ export { useDataProvider };
87
+ //# sourceMappingURL=useDataProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDataProvider.js","sources":["src/internal/useDataProvider.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 { useSearchState } from '@hh.ru/magritte-ui-new-select/internal/SearchStateContext';\nimport { useVisibility } from '@hh.ru/magritte-ui-new-select/internal/VisibilityContext';\nimport { OnOptionsChangeCallback, SelectOption } from '@hh.ru/magritte-ui-new-select/types';\n\nconst EMPTY_OPTION_LIST: never[] = [];\n\nexport interface UseDataProvider<DataType extends StaticDataFetcherItem> {\n internalError?: boolean;\n internalLoading?: boolean;\n optionList: SelectOption<DataType>[];\n}\n\nexport const useDataProvider = <DataType extends StaticDataFetcherItem>(\n dataProvider: DataProviderType<DataType>,\n onOptionsChangeExternal?: OnOptionsChangeCallback<DataType>\n): UseDataProvider<DataType> => {\n const { searchValue } = useSearchState();\n const { showOptions } = useVisibility();\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 const searchRequest = 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 searchRequest(searchValue);\n // eslint-disable-next-line disable-autofix/react-hooks/exhaustive-deps\n }, [dataProvider, 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 }),\n [internalError, optionList, optionsLoading, showOptions]\n );\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { FocusEventHandler } from 'react';
2
+ interface UseFocusWithin {
3
+ onFocus: FocusEventHandler;
4
+ onBlur: FocusEventHandler;
5
+ }
6
+ /**
7
+ * Отслеживает, находится ли фокус внутри поддерева, слушая всплывающие
8
+ * React-события onFocus/onBlur на одном контейнере.
9
+ */
10
+ export declare const useFocusWithin: (onFocusWithinExternal: (event: FocusEvent) => void, onBlurWithinExternal: (event: FocusEvent) => void, subscribeStability?: (listener: (isStable: boolean) => void) => () => void, predicateBlurExternal?: (event: FocusEvent) => boolean) => UseFocusWithin;
11
+ export {};
@@ -0,0 +1,87 @@
1
+ import './../index.css';
2
+ import { useRef, useCallback, useLayoutEffect } from 'react';
3
+ import { useEventCallback } from './utils.js';
4
+ import '@hh.ru/magritte-common-keyboard';
5
+
6
+ /**
7
+ * Отслеживает, находится ли фокус внутри поддерева, слушая всплывающие
8
+ * React-события onFocus/onBlur на одном контейнере.
9
+ */
10
+ const useFocusWithin = (
11
+ // фокус зашёл в поддерево целиком (извне)
12
+ onFocusWithinExternal,
13
+ // фокус покинул поддерево целиком (наружу, не между детьми)
14
+ onBlurWithinExternal,
15
+ // подписка "идёт ли сейчас внутренний перенос фокуса"
16
+ subscribeStability,
17
+ // некоторые компоненты составные
18
+ // иногда надо проверить inline компонент и дроп, которым он управляет
19
+ predicateBlurExternal) => {
20
+ const isFocusWithinRef = useRef(false);
21
+ // Событие blur, по которому ещё не принято решение
22
+ const pendingBlurRef = useRef(null);
23
+ // Если false — оркестратор сейчас передаёт фокус.
24
+ const isStableRef = useRef(true);
25
+ const onFocusWithin = useEventCallback(onFocusWithinExternal);
26
+ const onBlurWithin = useEventCallback(onBlurWithinExternal);
27
+ const predicateBlur = useEventCallback(predicateBlurExternal);
28
+ /**
29
+ * Пытаемся завершить pending blur.
30
+ * Если оркестратор ещё переносит фокус — ничего не делаем.
31
+ */
32
+ const commitPendingBlur = useCallback(() => {
33
+ const pendingBlur = pendingBlurRef.current;
34
+ if (!pendingBlur) {
35
+ return;
36
+ }
37
+ if (!isStableRef.current) {
38
+ return;
39
+ }
40
+ if (predicateBlur?.(pendingBlur)) {
41
+ pendingBlurRef.current = null;
42
+ return;
43
+ }
44
+ pendingBlurRef.current = null;
45
+ isFocusWithinRef.current = false;
46
+ onBlurWithin(pendingBlur);
47
+ }, [onBlurWithin, predicateBlur]);
48
+ /**
49
+ * Когда внутренний перенос закончился:
50
+ * - если новый элемент уже получил focus, pendingBlur будет null;
51
+ * - если никто не принял focus, завершаем настоящий blur.
52
+ */
53
+ useLayoutEffect(() => {
54
+ if (!subscribeStability) {
55
+ return void 0;
56
+ }
57
+ const unsubscribeStability = subscribeStability((isStable) => {
58
+ isStableRef.current = isStable;
59
+ if (isStable) {
60
+ queueMicrotask(commitPendingBlur);
61
+ }
62
+ });
63
+ return unsubscribeStability;
64
+ }, [subscribeStability, commitPendingBlur]);
65
+ const onFocus = useCallback((event) => {
66
+ pendingBlurRef.current = null;
67
+ if (!isFocusWithinRef.current) {
68
+ isFocusWithinRef.current = true;
69
+ onFocusWithin(event.nativeEvent);
70
+ }
71
+ }, [onFocusWithin]);
72
+ const onBlur = useCallback((event) => {
73
+ if (!isFocusWithinRef.current) {
74
+ return;
75
+ }
76
+ // Пока не считаем это настоящим выходом.
77
+ pendingBlurRef.current = event.nativeEvent;
78
+ queueMicrotask(commitPendingBlur);
79
+ }, [commitPendingBlur]);
80
+ return {
81
+ onFocus,
82
+ onBlur,
83
+ };
84
+ };
85
+
86
+ export { useFocusWithin };
87
+ //# sourceMappingURL=useFocusWithin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFocusWithin.js","sources":["src/internal/useFocusWithin.ts"],"sourcesContent":["import { FocusEventHandler, useCallback, useLayoutEffect, useRef } from 'react';\n\nimport { useEventCallback } from '@hh.ru/magritte-ui-new-select/internal/utils';\n\ninterface UseFocusWithin {\n onFocus: FocusEventHandler;\n onBlur: FocusEventHandler;\n}\n\n/**\n * Отслеживает, находится ли фокус внутри поддерева, слушая всплывающие\n * React-события onFocus/onBlur на одном контейнере.\n */\nexport const useFocusWithin = (\n // фокус зашёл в поддерево целиком (извне)\n onFocusWithinExternal: (event: FocusEvent) => void,\n // фокус покинул поддерево целиком (наружу, не между детьми)\n onBlurWithinExternal: (event: FocusEvent) => void,\n // подписка \"идёт ли сейчас внутренний перенос фокуса\"\n subscribeStability?: (listener: (isStable: boolean) => void) => () => void,\n // некоторые компоненты составные\n // иногда надо проверить inline компонент и дроп, которым он управляет\n predicateBlurExternal?: (event: FocusEvent) => boolean\n): UseFocusWithin => {\n const isFocusWithinRef = useRef(false);\n\n // Событие blur, по которому ещё не принято решение\n const pendingBlurRef = useRef<FocusEvent | null>(null);\n\n // Если false — оркестратор сейчас передаёт фокус.\n const isStableRef = useRef(true);\n\n const onFocusWithin = useEventCallback(onFocusWithinExternal);\n const onBlurWithin = useEventCallback(onBlurWithinExternal);\n const predicateBlur = useEventCallback(predicateBlurExternal);\n\n /**\n * Пытаемся завершить pending blur.\n * Если оркестратор ещё переносит фокус — ничего не делаем.\n */\n const commitPendingBlur = useCallback(() => {\n const pendingBlur = pendingBlurRef.current;\n\n if (!pendingBlur) {\n return;\n }\n\n if (!isStableRef.current) {\n return;\n }\n\n if (predicateBlur?.(pendingBlur)) {\n pendingBlurRef.current = null;\n return;\n }\n\n pendingBlurRef.current = null;\n isFocusWithinRef.current = false;\n\n onBlurWithin(pendingBlur);\n }, [onBlurWithin, predicateBlur]);\n\n /**\n * Когда внутренний перенос закончился:\n * - если новый элемент уже получил focus, pendingBlur будет null;\n * - если никто не принял focus, завершаем настоящий blur.\n */\n useLayoutEffect(() => {\n if (!subscribeStability) {\n return void 0;\n }\n const unsubscribeStability = subscribeStability((isStable) => {\n isStableRef.current = isStable;\n\n if (isStable) {\n queueMicrotask(commitPendingBlur);\n }\n });\n return unsubscribeStability;\n }, [subscribeStability, commitPendingBlur]);\n\n const onFocus = useCallback<FocusEventHandler>(\n (event) => {\n pendingBlurRef.current = null;\n if (!isFocusWithinRef.current) {\n isFocusWithinRef.current = true;\n onFocusWithin(event.nativeEvent);\n }\n },\n [onFocusWithin]\n );\n\n const onBlur = useCallback<FocusEventHandler>(\n (event) => {\n if (!isFocusWithinRef.current) {\n return;\n }\n // Пока не считаем это настоящим выходом.\n pendingBlurRef.current = event.nativeEvent;\n queueMicrotask(commitPendingBlur);\n },\n [commitPendingBlur]\n );\n\n return {\n onFocus,\n onBlur,\n };\n};\n"],"names":[],"mappings":";;;;AASA;;;AAGG;AACU,MAAA,cAAc,GAAG;AAC1B;AACA,qBAAkD;AAClD;AACA,oBAAiD;AACjD;AACA,kBAA0E;AAC1E;AACA;AACA,qBAAsD,KACtC;AAChB,IAAA,MAAM,gBAAgB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;;AAGvC,IAAA,MAAM,cAAc,GAAG,MAAM,CAAoB,IAAI,CAAC,CAAC;;AAGvD,IAAA,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;AAEjC,IAAA,MAAM,aAAa,GAAG,gBAAgB,CAAC,qBAAqB,CAAC,CAAC;AAC9D,IAAA,MAAM,YAAY,GAAG,gBAAgB,CAAC,oBAAoB,CAAC,CAAC;AAC5D,IAAA,MAAM,aAAa,GAAG,gBAAgB,CAAC,qBAAqB,CAAC,CAAC;AAE9D;;;AAGG;AACH,IAAA,MAAM,iBAAiB,GAAG,WAAW,CAAC,MAAK;AACvC,QAAA,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC;QAE3C,IAAI,CAAC,WAAW,EAAE;YACd,OAAO;SACV;AAED,QAAA,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE;YACtB,OAAO;SACV;AAED,QAAA,IAAI,aAAa,GAAG,WAAW,CAAC,EAAE;AAC9B,YAAA,cAAc,CAAC,OAAO,GAAG,IAAI,CAAC;YAC9B,OAAO;SACV;AAED,QAAA,cAAc,CAAC,OAAO,GAAG,IAAI,CAAC;AAC9B,QAAA,gBAAgB,CAAC,OAAO,GAAG,KAAK,CAAC;QAEjC,YAAY,CAAC,WAAW,CAAC,CAAC;AAC9B,KAAC,EAAE,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC;AAElC;;;;AAIG;IACH,eAAe,CAAC,MAAK;QACjB,IAAI,CAAC,kBAAkB,EAAE;YACrB,OAAO,KAAK,CAAC,CAAC;SACjB;AACD,QAAA,MAAM,oBAAoB,GAAG,kBAAkB,CAAC,CAAC,QAAQ,KAAI;AACzD,YAAA,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;YAE/B,IAAI,QAAQ,EAAE;gBACV,cAAc,CAAC,iBAAiB,CAAC,CAAC;aACrC;AACL,SAAC,CAAC,CAAC;AACH,QAAA,OAAO,oBAAoB,CAAC;AAChC,KAAC,EAAE,CAAC,kBAAkB,EAAE,iBAAiB,CAAC,CAAC,CAAC;AAE5C,IAAA,MAAM,OAAO,GAAG,WAAW,CACvB,CAAC,KAAK,KAAI;AACN,QAAA,cAAc,CAAC,OAAO,GAAG,IAAI,CAAC;AAC9B,QAAA,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE;AAC3B,YAAA,gBAAgB,CAAC,OAAO,GAAG,IAAI,CAAC;AAChC,YAAA,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;SACpC;AACL,KAAC,EACD,CAAC,aAAa,CAAC,CAClB,CAAC;AAEF,IAAA,MAAM,MAAM,GAAG,WAAW,CACtB,CAAC,KAAK,KAAI;AACN,QAAA,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE;YAC3B,OAAO;SACV;;AAED,QAAA,cAAc,CAAC,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC;QAC3C,cAAc,CAAC,iBAAiB,CAAC,CAAC;AACtC,KAAC,EACD,CAAC,iBAAiB,CAAC,CACtB,CAAC;IAEF,OAAO;QACH,OAAO;QACP,MAAM;KACT,CAAC;AACN;;;;"}
@@ -13,4 +13,4 @@ export interface UseSelectState<MultipleType extends boolean, DataType extends S
13
13
  onChange: OnChangeAction<DataType>;
14
14
  setSelectedValues: VoidFunction;
15
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>;
16
+ export declare const useSelectState: <MultipleType extends boolean, DataType extends StaticDataFetcherItem>(value: DataType[] | DataType, onChangeExternal?: (value: ValueTypeHelper<MultipleType, DataType>) => void, onSelectOption?: (values: DataType[]) => void) => UseSelectState<MultipleType, DataType>;
@@ -1,3 +1,4 @@
1
+ import './../index.css';
1
2
  import { useReducer, useRef, useState, useCallback, useEffect, useMemo } from 'react';
2
3
  import { useSelectConfig } from './ConfigContext.js';
3
4
  import { useVisibility } from './VisibilityContext.js';
@@ -1 +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":"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"}
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 = <MultipleType extends boolean, DataType extends StaticDataFetcherItem>(\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":"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"}
@@ -5,3 +5,4 @@ export declare const assertSelectContext: <T>(value: T | null, hookName: string)
5
5
  export declare const useEventCallback: <Callback extends (...args: never[]) => unknown>(callback: Callback | undefined) => Callback;
6
6
  export declare const useSelectedLabels: <DataType extends StaticDataFetcherItem>(values: DataType | DataType[]) => string[];
7
7
  export declare const focusOptionsList: (event: SyntheticEvent, drop: HTMLElement | null | undefined, direction: "up" | "down") => void;
8
+ export type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
package/internal/utils.js CHANGED
@@ -1,3 +1,4 @@
1
+ import './../index.css';
1
2
  import { useRef, useLayoutEffect, useCallback, useMemo } from 'react';
2
3
  import { findNextFocusableElement } from '@hh.ru/magritte-common-keyboard';
3
4
 
@@ -1 +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;;;;"}
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\n// применяет Omit к каждому варианту union-типа отдельно\nexport type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hh.ru/magritte-ui-new-select",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts",
6
6
  "sideEffects": [
@@ -28,6 +28,8 @@
28
28
  "dependencies": {
29
29
  "@hh.ru/magritte-common-data-provider": "1.3.7",
30
30
  "@hh.ru/magritte-common-keyboard": "4.3.4",
31
+ "@hh.ru/magritte-common-use-disabled": "1.0.15",
32
+ "@hh.ru/magritte-common-use-multiple-refs": "1.1.11",
31
33
  "@hh.ru/magritte-common-use-translation": "3.1.0",
32
34
  "@hh.ru/magritte-internal-slot-registry": "1.1.0",
33
35
  "@hh.ru/magritte-ui-bottom-sheet": "9.4.11",
@@ -40,5 +42,5 @@
40
42
  "@hh.ru/magritte-ui-title": "8.3.4",
41
43
  "@hh.ru/magritte-ui-trigger": "4.3.26"
42
44
  },
43
- "gitHead": "73e0fbef72d8f731bbda034d85b96c50f7fe3bd3"
45
+ "gitHead": "e3b164dddb3a1f9322da3180f130b54c1b17a72a"
44
46
  }
package/types.d.ts CHANGED
@@ -1,24 +1,27 @@
1
- import { Dispatch, SetStateAction, ReactNode } from 'react';
1
+ import { ReactNode, Dispatch, SetStateAction } from 'react';
2
2
  import { StaticDataFetcherItem } from '@hh.ru/magritte-common-data-provider';
3
3
  import { DataProviderProps } from '@hh.ru/magritte-ui-new-select/dataProviderTypes';
4
+ import { DistributiveOmit } from '@hh.ru/magritte-ui-new-select/internal/utils';
5
+ import { TriggerProps } from '@hh.ru/magritte-ui-trigger';
4
6
  export type DelimiterOptionType = 'delimiter';
5
7
  export type SelectOptionType = 'checkbox' | 'label' | 'radio';
6
8
  export type CommitModel = 'instant' | 'confirm';
9
+ type ControlledTriggerKeys = 'expanded' | 'onBlur' | 'onChange' | 'onClear' | 'onFocus' | 'onKeyDown' | 'values';
10
+ export type SelectUncontrolledTriggerProps = DistributiveOmit<TriggerProps, ControlledTriggerKeys>;
11
+ export type SelectControlledTriggerProps = Pick<TriggerProps, ControlledTriggerKeys>;
7
12
  export interface OnChangeAction<DataType extends StaticDataFetcherItem> {
8
13
  (value: DataType, needToPut: boolean): void;
9
14
  }
10
15
  export interface OnOptionsChangeCallback<DataType extends StaticDataFetcherItem> {
11
16
  (options: SelectOption<DataType>[]): void;
12
17
  }
13
- export interface SearchValue {
18
+ export interface SearchValueState {
14
19
  searchValue: string;
20
+ setSearchValue: Dispatch<SetStateAction<string>>;
15
21
  }
16
22
  export interface OptionsCount {
17
23
  optionsCount: number;
18
24
  }
19
- export interface SetSearchValue {
20
- setSearchValue: Dispatch<SetStateAction<string>>;
21
- }
22
25
  export interface SelectOption<DataType extends StaticDataFetcherItem> {
23
26
  data: DataType;
24
27
  disabled?: boolean;
@@ -53,3 +56,4 @@ export interface SelectBaseProps<MultipleType extends boolean, DataType extends
53
56
  loading?: boolean;
54
57
  }
55
58
  export type SelectProps<MultipleType extends boolean, DataType extends StaticDataFetcherItem> = SelectBaseProps<MultipleType, DataType> & DataProviderProps<DataType>;
59
+ export {};
package/types.js CHANGED
@@ -1,2 +1,3 @@
1
+ import './index.css';
1
2
 
2
3
  //# sourceMappingURL=types.js.map
@@ -0,0 +1,6 @@
1
+ import { RefObject } from 'react';
2
+ import { StaticDataFetcherItem } from '@hh.ru/magritte-common-data-provider';
3
+ import { SelectControlledTriggerProps } from '@hh.ru/magritte-ui-new-select/types';
4
+ export declare const useGetTriggerRef: () => RefObject<HTMLElement> | undefined;
5
+ export declare const useTriggerRef: () => RefObject<HTMLElement>;
6
+ export declare const useSelectTrigger: <MultipleType extends boolean, DataType extends StaticDataFetcherItem>(disabled?: boolean) => SelectControlledTriggerProps;