@hh.ru/magritte-ui-tree-selector 1.3.1 → 1.4.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 (45) hide show
  1. package/Item.d.ts +2 -1
  2. package/Item.js +3 -3
  3. package/Item.js.map +1 -1
  4. package/ItemContent-f98f0b9a.js +22 -0
  5. package/ItemContent-f98f0b9a.js.map +1 -0
  6. package/ItemContent.d.ts +5 -4
  7. package/ItemContent.js +1 -1
  8. package/ItemsList.d.ts +1 -0
  9. package/ItemsList.js +3 -3
  10. package/ItemsList.js.map +1 -1
  11. package/MobileItem.d.ts +2 -1
  12. package/MobileItem.js +2 -2
  13. package/MobileItem.js.map +1 -1
  14. package/MobileItemsList.d.ts +1 -0
  15. package/MobileItemsList.js +3 -3
  16. package/MobileItemsList.js.map +1 -1
  17. package/TreeSelector.d.ts +2 -2
  18. package/TreeSelector.js +41 -108
  19. package/TreeSelector.js.map +1 -1
  20. package/TreeSelectorDummy.d.ts +4 -0
  21. package/TreeSelectorDummy.js +129 -0
  22. package/TreeSelectorDummy.js.map +1 -0
  23. package/collection/treeCollectionHelper.d.ts +6 -0
  24. package/collection/treeCollectionHelper.js +16 -1
  25. package/collection/treeCollectionHelper.js.map +1 -1
  26. package/index.css +11 -11
  27. package/index.d.ts +1 -1
  28. package/index.js +11 -10
  29. package/index.js.map +1 -1
  30. package/package.json +4 -4
  31. package/types.d.ts +17 -7
  32. package/useDisabled.d.ts +13 -0
  33. package/useDisabled.js +20 -0
  34. package/useDisabled.js.map +1 -0
  35. package/useExpanded.d.ts +2 -2
  36. package/useExpanded.js +1 -1
  37. package/useExpanded.js.map +1 -1
  38. package/useSelected.d.ts +6 -1
  39. package/useSelected.js +39 -8
  40. package/useSelected.js.map +1 -1
  41. package/ItemContent-74eb482b.js +0 -22
  42. package/ItemContent-74eb482b.js.map +0 -1
  43. package/UncontrolledTreeSelector.d.ts +0 -7
  44. package/UncontrolledTreeSelector.js +0 -49
  45. package/UncontrolledTreeSelector.js.map +0 -1
@@ -0,0 +1,129 @@
1
+ import './index.css';
2
+ import { jsx, Fragment } from 'react/jsx-runtime';
3
+ import { forwardRef, useState, useRef, useEffect, useCallback } from 'react';
4
+ import { match } from '@hh.ru/magritte-common-fuzzy-search';
5
+ import { useBreakpoint } from '@hh.ru/magritte-ui-breakpoint';
6
+ import { VSpacingContainer } from '@hh.ru/magritte-ui-spacing';
7
+ import { ItemsList } from './ItemsList.js';
8
+ import { MobileItemsList } from './MobileItemsList.js';
9
+ import { filterWithParents } from './collection/treeCollectionHelper.js';
10
+ import { useExpanded } from './useExpanded.js';
11
+ import { useIndeterminate } from './useIndeterminate.js';
12
+ import { useRenderInput } from './useRenderInput.js';
13
+ import './Item.js';
14
+ import 'classnames';
15
+ import './ItemContent-f98f0b9a.js';
16
+ import '@hh.ru/magritte-ui-cell';
17
+ import '@hh.ru/magritte-ui-icon/icon';
18
+ import './Action.js';
19
+ import '@hh.ru/magritte-ui-checkbox-radio';
20
+ import '@hh.ru/magritte-ui-typography';
21
+ import '@hh.ru/magritte-ui-checkable-card/CheckableCardElement';
22
+ import './MobileItem.js';
23
+ import './MobileParentItem.js';
24
+ import '@hh.ru/magritte-ui-card';
25
+ import './collection/treeCollection.js';
26
+ import '@hh.ru/magritte-ui-input';
27
+
28
+ const defaultCheckSelectable = () => true;
29
+ const defaultArray = [];
30
+ const needToApply = (query) => {
31
+ return !!(query && query.length);
32
+ };
33
+ const TreeSelectorDummy = forwardRef(({ collection: initialCollection, checkSelectable = defaultCheckSelectable, initialExpanded = defaultArray, value: selected, leavesOnly, singleChoice, expanded, disabled = defaultArray, treeFilter = filterWithParents, suggestedNotFound = defaultArray, constantlySuggested = defaultArray, onExpand, onChange, onChangeFilterQuery, children, }, ref) => {
34
+ const [collection, setCollection] = useState(initialCollection);
35
+ const currentQuery = useRef('');
36
+ const suggestedNotFoundModels = useRef(initialCollection.getExistModels(suggestedNotFound));
37
+ const constantlySuggestedModels = useRef(initialCollection.getExistModels(constantlySuggested));
38
+ const { indeterminate } = useIndeterminate({ collection: initialCollection, selected });
39
+ const { expanded: currentExpanded, setExpanded, handleExpansion, } = useExpanded({
40
+ initialValue: expanded ? expanded.slice() : initialExpanded.slice(),
41
+ controlledExpanded: expanded,
42
+ onExpand,
43
+ });
44
+ const { isMobile } = useBreakpoint();
45
+ const { contentFilterQuery, setInputValue, handleChangeInput, renderInput } = useRenderInput();
46
+ useEffect(() => {
47
+ const contentFilterQueryTrimmed = contentFilterQuery.trim();
48
+ const queryWasChanged = contentFilterQueryTrimmed !== currentQuery.current.trim();
49
+ if (queryWasChanged && needToApply(contentFilterQueryTrimmed)) {
50
+ const newExpanded = [];
51
+ const newCollection = treeFilter(initialCollection, (item) => match(contentFilterQueryTrimmed, item.text), isMobile);
52
+ newCollection.toList().forEach((item) => {
53
+ if (newCollection.hasChildren(item.id)) {
54
+ // Если есть в отфильтрованной коллекции есть дочерние элементы,
55
+ // значит они заматчились, и нужно открыть родителя.
56
+ newExpanded.push(item.id);
57
+ }
58
+ else {
59
+ // Если заматчился только сам родитель, показываем его
60
+ // схлопнутым => нужно добавить его ветку в коллекцию.
61
+ initialCollection.walkChildren(item.id, (child, parents) => {
62
+ newCollection.addModel({ ...child }, parents[0].id);
63
+ });
64
+ }
65
+ });
66
+ const filteredIds = newCollection.toList().map((model) => model.id);
67
+ // Если в отфильтрованной коллекции нет моделей, но заданы предложенные,
68
+ // то показываем их
69
+ // Если в отфильтрованной коллекции модели есть, то пробуем добавить к ним
70
+ // всегда показывающиеся модели, если их ещё нет в коллекции
71
+ if (!filteredIds.length && suggestedNotFoundModels.current.length) {
72
+ suggestedNotFoundModels.current.forEach((model) => newCollection.addModel({ ...model }));
73
+ }
74
+ else {
75
+ constantlySuggestedModels.current.forEach((model) => {
76
+ if (!filteredIds.includes(model.id)) {
77
+ newCollection.addModel({ ...model });
78
+ }
79
+ });
80
+ }
81
+ setCollection(newCollection);
82
+ currentQuery.current = contentFilterQuery;
83
+ onChangeFilterQuery?.(filteredIds, contentFilterQueryTrimmed);
84
+ setExpanded(newExpanded);
85
+ }
86
+ else if (queryWasChanged) {
87
+ // Запрос не годится для поиска.
88
+ const newExpanded = initialExpanded.slice();
89
+ setCollection(initialCollection);
90
+ onChangeFilterQuery?.(initialCollection.toList().map((model) => model.id), contentFilterQueryTrimmed);
91
+ currentQuery.current = contentFilterQuery;
92
+ setExpanded(newExpanded);
93
+ }
94
+ }, [
95
+ initialCollection,
96
+ contentFilterQuery,
97
+ treeFilter,
98
+ selected,
99
+ initialExpanded,
100
+ setExpanded,
101
+ isMobile,
102
+ onChangeFilterQuery,
103
+ ]);
104
+ const renderTreeSelector = useCallback(() => {
105
+ return (jsx(VSpacingContainer, { default: 12, gteM: 0, children: isMobile ? (jsx(MobileItemsList, { getSearchItemOrder: (model) => initialCollection.getModelLevel(model.id), collection: collection, selected: selected, disabled: disabled, onChange: onChange, leavesOnly: leavesOnly, checkSelectable: checkSelectable, singleChoice: singleChoice, ref: ref, setInputValue: setInputValue, handleChangeInput: handleChangeInput, contentFilterQuery: contentFilterQuery.trim() })) : (jsx(ItemsList, { collection: collection, items: collection.getTopLevel(), leavesOnly: leavesOnly, checkSelectable: checkSelectable, expanded: currentExpanded, onExpansion: handleExpansion, selected: selected, onChange: onChange, disabled: disabled, singleChoice: singleChoice, indeterminate: indeterminate })) }));
106
+ }, [
107
+ isMobile,
108
+ collection,
109
+ selected,
110
+ disabled,
111
+ onChange,
112
+ leavesOnly,
113
+ checkSelectable,
114
+ singleChoice,
115
+ ref,
116
+ setInputValue,
117
+ handleChangeInput,
118
+ contentFilterQuery,
119
+ currentExpanded,
120
+ handleExpansion,
121
+ indeterminate,
122
+ initialCollection,
123
+ ]);
124
+ return jsx(Fragment, { children: children({ renderTreeSelector, renderInput }) });
125
+ });
126
+ TreeSelectorDummy.displayName = 'TreeSelectorDummy';
127
+
128
+ export { TreeSelectorDummy };
129
+ //# sourceMappingURL=TreeSelectorDummy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TreeSelectorDummy.js","sources":["../src/TreeSelectorDummy.tsx"],"sourcesContent":["import { ReactElement, forwardRef, ForwardedRef, useCallback, useEffect, useRef, useState } from 'react';\n\nimport { match } from '@hh.ru/magritte-common-fuzzy-search';\nimport { useBreakpoint } from '@hh.ru/magritte-ui-breakpoint';\nimport { VSpacingContainer } from '@hh.ru/magritte-ui-spacing';\nimport { ItemsList } from '@hh.ru/magritte-ui-tree-selector/ItemsList';\nimport { MobileItemsList } from '@hh.ru/magritte-ui-tree-selector/MobileItemsList';\nimport { filterWithParents } from '@hh.ru/magritte-ui-tree-selector/collection/treeCollectionHelper';\nimport { AdditionalDefault } from '@hh.ru/magritte-ui-tree-selector/collection/types';\nimport { ListControls, TreeSelectorDummyProps } from '@hh.ru/magritte-ui-tree-selector/types';\nimport { useExpanded } from '@hh.ru/magritte-ui-tree-selector/useExpanded';\nimport { useIndeterminate } from '@hh.ru/magritte-ui-tree-selector/useIndeterminate';\nimport { useRenderInput } from '@hh.ru/magritte-ui-tree-selector/useRenderInput';\n\nconst defaultCheckSelectable = () => true;\nconst defaultArray: string[] = [];\n\nconst needToApply = (query: string): boolean => {\n return !!(query && query.length);\n};\n\nexport const TreeSelectorDummy = forwardRef(\n <A extends AdditionalDefault>(\n {\n collection: initialCollection,\n checkSelectable = defaultCheckSelectable,\n initialExpanded = defaultArray,\n value: selected,\n leavesOnly,\n singleChoice,\n expanded,\n disabled = defaultArray,\n treeFilter = filterWithParents,\n suggestedNotFound = defaultArray,\n constantlySuggested = defaultArray,\n onExpand,\n onChange,\n onChangeFilterQuery,\n children,\n }: TreeSelectorDummyProps<A>,\n ref: ForwardedRef<ListControls>\n ): ReactElement => {\n const [collection, setCollection] = useState(initialCollection);\n const currentQuery = useRef('');\n const suggestedNotFoundModels = useRef(initialCollection.getExistModels(suggestedNotFound));\n const constantlySuggestedModels = useRef(initialCollection.getExistModels(constantlySuggested));\n\n const { indeterminate } = useIndeterminate({ collection: initialCollection, selected });\n const {\n expanded: currentExpanded,\n setExpanded,\n handleExpansion,\n } = useExpanded({\n initialValue: expanded ? expanded.slice() : initialExpanded.slice(),\n controlledExpanded: expanded,\n onExpand,\n });\n const { isMobile } = useBreakpoint();\n\n const { contentFilterQuery, setInputValue, handleChangeInput, renderInput } = useRenderInput();\n\n useEffect(() => {\n const contentFilterQueryTrimmed = contentFilterQuery.trim();\n const queryWasChanged = contentFilterQueryTrimmed !== currentQuery.current.trim();\n\n if (queryWasChanged && needToApply(contentFilterQueryTrimmed)) {\n const newExpanded: string[] = [];\n const newCollection = treeFilter(\n initialCollection,\n (item) => match(contentFilterQueryTrimmed, item.text),\n isMobile\n );\n newCollection.toList().forEach((item) => {\n if (newCollection.hasChildren(item.id)) {\n // Если есть в отфильтрованной коллекции есть дочерние элементы,\n // значит они заматчились, и нужно открыть родителя.\n newExpanded.push(item.id);\n } else {\n // Если заматчился только сам родитель, показываем его\n // схлопнутым => нужно добавить его ветку в коллекцию.\n initialCollection.walkChildren(item.id, (child, parents) => {\n newCollection.addModel({ ...child }, parents[0].id);\n });\n }\n });\n\n const filteredIds = newCollection.toList().map((model) => model.id);\n\n // Если в отфильтрованной коллекции нет моделей, но заданы предложенные,\n // то показываем их\n // Если в отфильтрованной коллекции модели есть, то пробуем добавить к ним\n // всегда показывающиеся модели, если их ещё нет в коллекции\n if (!filteredIds.length && suggestedNotFoundModels.current.length) {\n suggestedNotFoundModels.current.forEach((model) => newCollection.addModel({ ...model }));\n } else {\n constantlySuggestedModels.current.forEach((model) => {\n if (!filteredIds.includes(model.id)) {\n newCollection.addModel({ ...model });\n }\n });\n }\n\n setCollection(newCollection);\n currentQuery.current = contentFilterQuery;\n onChangeFilterQuery?.(filteredIds, contentFilterQueryTrimmed);\n setExpanded(newExpanded);\n } else if (queryWasChanged) {\n // Запрос не годится для поиска.\n const newExpanded = initialExpanded.slice();\n setCollection(initialCollection);\n\n onChangeFilterQuery?.(\n initialCollection.toList().map((model) => model.id),\n contentFilterQueryTrimmed\n );\n currentQuery.current = contentFilterQuery;\n setExpanded(newExpanded);\n }\n }, [\n initialCollection,\n contentFilterQuery,\n treeFilter,\n selected,\n initialExpanded,\n setExpanded,\n isMobile,\n onChangeFilterQuery,\n ]);\n\n const renderTreeSelector = useCallback(() => {\n return (\n <VSpacingContainer default={12} gteM={0}>\n {isMobile ? (\n <MobileItemsList\n getSearchItemOrder={(model) => initialCollection.getModelLevel(model.id)}\n collection={collection}\n selected={selected}\n disabled={disabled}\n onChange={onChange}\n leavesOnly={leavesOnly}\n checkSelectable={checkSelectable}\n singleChoice={singleChoice}\n ref={ref}\n setInputValue={setInputValue}\n handleChangeInput={handleChangeInput}\n contentFilterQuery={contentFilterQuery.trim()}\n />\n ) : (\n <ItemsList\n collection={collection}\n items={collection.getTopLevel()}\n leavesOnly={leavesOnly}\n checkSelectable={checkSelectable}\n expanded={currentExpanded}\n onExpansion={handleExpansion}\n selected={selected}\n onChange={onChange}\n disabled={disabled}\n singleChoice={singleChoice}\n indeterminate={indeterminate}\n />\n )}\n </VSpacingContainer>\n );\n }, [\n isMobile,\n collection,\n selected,\n disabled,\n onChange,\n leavesOnly,\n checkSelectable,\n singleChoice,\n ref,\n setInputValue,\n handleChangeInput,\n contentFilterQuery,\n currentExpanded,\n handleExpansion,\n indeterminate,\n initialCollection,\n ]);\n\n return <>{children({ renderTreeSelector, renderInput })}</>;\n }\n);\n\nTreeSelectorDummy.displayName = 'TreeSelectorDummy';\n"],"names":["_jsx","_Fragment"],"mappings":"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"}
@@ -30,3 +30,9 @@ export declare const filterWithParents: TreeFilter;
30
30
  * для которых `filterFunction` вернула true (плоский список).
31
31
  */
32
32
  export declare const filterUniqueLeavesOnly: TreeFilter;
33
+ /**
34
+ * Возвращает элементы из списка, родители которых отсутствуют в этом же списке.
35
+ * Полезно для определения минимального набора выбранных элементов (если выбран родитель
36
+ * целиком, игнорируем его дочерние элементы)
37
+ */
38
+ export declare const getIdsWithNoParentsInSameList: <A extends AdditionalDefault>(collection: TreeCollection<A>, ids: string[]) => string[];
@@ -103,6 +103,21 @@ const filterUniqueLeavesOnly = (collection, filterFunction) => {
103
103
  });
104
104
  return filteredCollection;
105
105
  };
106
+ /**
107
+ * Возвращает элементы из списка, родители которых отсутствуют в этом же списке.
108
+ * Полезно для определения минимального набора выбранных элементов (если выбран родитель
109
+ * целиком, игнорируем его дочерние элементы)
110
+ */
111
+ const getIdsWithNoParentsInSameList = (collection, ids) => {
112
+ const hasChildrenHash = ids.reduce((result, id) => {
113
+ result[id] = collection.getChildren(id).length > 0;
114
+ return result;
115
+ }, {});
116
+ return ids.filter((id) => {
117
+ const parentIds = collection.getParentIdsDuplicates(id);
118
+ return !parentIds.some((id) => hasChildrenHash.hasOwnProperty(id) && hasChildrenHash[id]);
119
+ });
120
+ };
106
121
 
107
- export { filterMissingIds, filterParents, filterUniqueLeavesOnly, filterWithParents, fromTree, walk };
122
+ export { filterMissingIds, filterParents, filterUniqueLeavesOnly, filterWithParents, fromTree, getIdsWithNoParentsInSameList, walk };
108
123
  //# sourceMappingURL=treeCollectionHelper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"treeCollectionHelper.js","sources":["../../src/collection/treeCollectionHelper.ts"],"sourcesContent":["import TreeCollection from '@hh.ru/magritte-ui-tree-selector/collection/treeCollection';\nimport {\n AdditionalDefault,\n WalkCallback,\n ModelData,\n TreeFilter,\n TreeModel,\n} from '@hh.ru/magritte-ui-tree-selector/collection/types';\n\n/**\n * Рекурсивно проходит по списку моделей, применяет к каждой модели переданную функцию.\n */\nexport const walk = <A extends AdditionalDefault>(\n /** Дерево для обработки. */\n tree: ModelData<A>[],\n /** Вызываемая функция. */\n callback: WalkCallback<A>,\n /** Массив моделей родителей от ближнего к дальнему. */\n parents?: ModelData<A>[]\n): void => {\n const currentParents = parents ? parents.slice() : [];\n tree.forEach((item) => {\n callback(item, currentParents);\n if (item.items && item.items.length) {\n walk(item.items, callback, [item].concat(currentParents));\n }\n });\n};\n\n/**\n * Возвращает коллекцию, созданную из дерева.\n * @param tree\n * @param [callback] Функция, вызываемая на каждом элементе.\n */\nexport const fromTree = <A extends AdditionalDefault = never>(\n tree: ModelData<A>[],\n callback?: WalkCallback<A>\n): TreeCollection<A> => {\n const collection = new TreeCollection<A>();\n walk(tree, (item, parents): void => {\n if (typeof callback === 'function') {\n callback(item, parents);\n }\n collection.addModel(item, parents.length ? parents[0].id : undefined);\n });\n return collection;\n};\n\n/**\n * Возвращает только те ID, которые присутствуют в коллекции\n */\nexport const filterMissingIds = <A extends AdditionalDefault>(\n collection: TreeCollection<A>,\n ids: string[]\n): string[] => {\n const filteredIds: string[] = [];\n ids.forEach((id) => {\n if (collection.getModel(id)) {\n filteredIds.push(id);\n }\n });\n return filteredIds;\n};\n\n/**\n * Возвращает ID только тех элементов, у которых нет потомков.\n */\nexport const filterParents = <A extends AdditionalDefault>(collection: TreeCollection<A>, ids: string[]): string[] => {\n return ids.filter((id) => {\n return collection.getChildrenIds(id).length === 0;\n });\n};\n\n/**\n * Возвращает новую коллекцию, содержащую элементы, для которых `filterFunction` вернула true.\n * Если заматчился дочерний элемент, к результатам добавляются его родители до самого верха.\n * Если заматчился родитель, к результатам НЕ добавляются его дочерние элементы (кроме тех,\n * что тоже заматчились).\n */\nexport const filterWithParents: TreeFilter = (collection, filterFunction, isMobile) => {\n const filteredCollection: typeof collection = new (collection.constructor as typeof TreeCollection)();\n const ids = new Set<TreeModel['id']>();\n collection.walk((model, parents) => {\n if (filterFunction(model)) {\n ids.add(model.id);\n if (!isMobile) {\n parents.forEach((parent) => {\n ids.add(parent.id);\n });\n }\n }\n });\n ids.forEach((id) => {\n const model = collection.getModel(id);\n if (model) {\n // В модели с дубликатами у элемента может быть несколько родителей\n const parentIds = collection.getParentIdsDuplicates(model.id);\n if (parentIds.length && !isMobile) {\n parentIds.forEach((parendId) => {\n filteredCollection.addModel({ ...model }, parendId);\n });\n } else {\n filteredCollection.addModel({ ...model });\n }\n }\n });\n return filteredCollection;\n};\n\n/**\n * Возвращает новую коллекцию, содержащую уникальные элементы самого нижнего уровня,\n * для которых `filterFunction` вернула true (плоский список).\n */\nexport const filterUniqueLeavesOnly: TreeFilter = (collection, filterFunction) => {\n const filteredCollection: typeof collection = new TreeCollection();\n collection.walk((model) => {\n if (!filteredCollection.getModel(model.id) && !collection.hasChildren(model.id) && filterFunction(model)) {\n filteredCollection.addModel({ ...model });\n }\n });\n return filteredCollection;\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"treeCollectionHelper.js","sources":["../../src/collection/treeCollectionHelper.ts"],"sourcesContent":["import TreeCollection from '@hh.ru/magritte-ui-tree-selector/collection/treeCollection';\nimport {\n AdditionalDefault,\n WalkCallback,\n ModelData,\n TreeFilter,\n TreeModel,\n} from '@hh.ru/magritte-ui-tree-selector/collection/types';\n\n/**\n * Рекурсивно проходит по списку моделей, применяет к каждой модели переданную функцию.\n */\nexport const walk = <A extends AdditionalDefault>(\n /** Дерево для обработки. */\n tree: ModelData<A>[],\n /** Вызываемая функция. */\n callback: WalkCallback<A>,\n /** Массив моделей родителей от ближнего к дальнему. */\n parents?: ModelData<A>[]\n): void => {\n const currentParents = parents ? parents.slice() : [];\n tree.forEach((item) => {\n callback(item, currentParents);\n if (item.items && item.items.length) {\n walk(item.items, callback, [item].concat(currentParents));\n }\n });\n};\n\n/**\n * Возвращает коллекцию, созданную из дерева.\n * @param tree\n * @param [callback] Функция, вызываемая на каждом элементе.\n */\nexport const fromTree = <A extends AdditionalDefault = never>(\n tree: ModelData<A>[],\n callback?: WalkCallback<A>\n): TreeCollection<A> => {\n const collection = new TreeCollection<A>();\n walk(tree, (item, parents): void => {\n if (typeof callback === 'function') {\n callback(item, parents);\n }\n collection.addModel(item, parents.length ? parents[0].id : undefined);\n });\n return collection;\n};\n\n/**\n * Возвращает только те ID, которые присутствуют в коллекции\n */\nexport const filterMissingIds = <A extends AdditionalDefault>(\n collection: TreeCollection<A>,\n ids: string[]\n): string[] => {\n const filteredIds: string[] = [];\n ids.forEach((id) => {\n if (collection.getModel(id)) {\n filteredIds.push(id);\n }\n });\n return filteredIds;\n};\n\n/**\n * Возвращает ID только тех элементов, у которых нет потомков.\n */\nexport const filterParents = <A extends AdditionalDefault>(collection: TreeCollection<A>, ids: string[]): string[] => {\n return ids.filter((id) => {\n return collection.getChildrenIds(id).length === 0;\n });\n};\n\n/**\n * Возвращает новую коллекцию, содержащую элементы, для которых `filterFunction` вернула true.\n * Если заматчился дочерний элемент, к результатам добавляются его родители до самого верха.\n * Если заматчился родитель, к результатам НЕ добавляются его дочерние элементы (кроме тех,\n * что тоже заматчились).\n */\nexport const filterWithParents: TreeFilter = (collection, filterFunction, isMobile) => {\n const filteredCollection: typeof collection = new (collection.constructor as typeof TreeCollection)();\n const ids = new Set<TreeModel['id']>();\n collection.walk((model, parents) => {\n if (filterFunction(model)) {\n ids.add(model.id);\n if (!isMobile) {\n parents.forEach((parent) => {\n ids.add(parent.id);\n });\n }\n }\n });\n ids.forEach((id) => {\n const model = collection.getModel(id);\n if (model) {\n // В модели с дубликатами у элемента может быть несколько родителей\n const parentIds = collection.getParentIdsDuplicates(model.id);\n if (parentIds.length && !isMobile) {\n parentIds.forEach((parendId) => {\n filteredCollection.addModel({ ...model }, parendId);\n });\n } else {\n filteredCollection.addModel({ ...model });\n }\n }\n });\n return filteredCollection;\n};\n\n/**\n * Возвращает новую коллекцию, содержащую уникальные элементы самого нижнего уровня,\n * для которых `filterFunction` вернула true (плоский список).\n */\nexport const filterUniqueLeavesOnly: TreeFilter = (collection, filterFunction) => {\n const filteredCollection: typeof collection = new TreeCollection();\n collection.walk((model) => {\n if (!filteredCollection.getModel(model.id) && !collection.hasChildren(model.id) && filterFunction(model)) {\n filteredCollection.addModel({ ...model });\n }\n });\n return filteredCollection;\n};\n\n/**\n * Возвращает элементы из списка, родители которых отсутствуют в этом же списке.\n * Полезно для определения минимального набора выбранных элементов (если выбран родитель\n * целиком, игнорируем его дочерние элементы)\n */\nexport const getIdsWithNoParentsInSameList = <A extends AdditionalDefault>(\n collection: TreeCollection<A>,\n ids: string[]\n): string[] => {\n const hasChildrenHash = ids.reduce((result: Record<string, boolean>, id) => {\n result[id] = collection.getChildren(id).length > 0;\n return result;\n }, {});\n\n return ids.filter((id) => {\n const parentIds = collection.getParentIdsDuplicates(id);\n return !parentIds.some((id) => hasChildrenHash.hasOwnProperty(id) && hasChildrenHash[id]);\n });\n};\n"],"names":[],"mappings":"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"}
package/index.css CHANGED
@@ -1,38 +1,38 @@
1
- .magritte-wrapper___GHKV6_1-3-1{
1
+ .magritte-wrapper___GHKV6_1-4-0{
2
2
  display:flex;
3
3
  padding:12px 0;
4
4
  gap:12px;
5
5
  align-items:center;
6
6
  }
7
- .magritte-letter___yZOCU_1-3-1{
7
+ .magritte-letter___yZOCU_1-4-0{
8
8
  width:24px;
9
9
  flex-shrink:0;
10
10
  text-align:center;
11
11
  }
12
- .magritte-icon___kO3Fj_1-3-1,
13
- .magritte-space___xTO79_1-3-1{
12
+ .magritte-icon___kO3Fj_1-4-0,
13
+ .magritte-space___xTO79_1-4-0{
14
14
  flex-shrink:0;
15
15
  line-height:0;
16
16
  width:24px;
17
17
  }
18
- .magritte-iconActive___4yrG5_1-3-1{
18
+ .magritte-iconActive___4yrG5_1-4-0{
19
19
  cursor:pointer;
20
20
  }
21
- .magritte-content___ZRc6R_1-3-1{
21
+ .magritte-content___ZRc6R_1-4-0{
22
22
  flex-grow:1;
23
23
  }
24
- .magritte-with-shift___ZErxZ_1-3-1{
24
+ .magritte-with-shift___ZErxZ_1-4-0{
25
25
  margin-left:-36px;
26
26
  }
27
- .magritte-with-indent___MH9Vy_1-3-1{
27
+ .magritte-with-indent___MH9Vy_1-4-0{
28
28
  margin-left:36px;
29
29
  }
30
- .magritte-item___2LtOL_1-3-1 > .magritte-children___kq-eq_1-3-1{
30
+ .magritte-item___2LtOL_1-4-0 > .magritte-children___kq-eq_1-4-0{
31
31
  padding-left:36px;
32
32
  }
33
- .magritte-item___2LtOL_1-3-1.magritte-with-two-boxes___LWOy2_1-3-1 > .magritte-children___kq-eq_1-3-1{
33
+ .magritte-item___2LtOL_1-4-0.magritte-with-two-boxes___LWOy2_1-4-0 > .magritte-children___kq-eq_1-4-0{
34
34
  padding-left:72px;
35
35
  }
36
- .magritte-item___2LtOL_1-3-1.magritte-with-three-boxes___cyVao_1-3-1 > .magritte-children___kq-eq_1-3-1{
36
+ .magritte-item___2LtOL_1-4-0.magritte-with-three-boxes___cyVao_1-4-0 > .magritte-children___kq-eq_1-4-0{
37
37
  padding-left:108px;
38
38
  }
package/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export * from './TreeSelector';
2
- export * from './UncontrolledTreeSelector';
2
+ export * from './TreeSelectorDummy';
3
3
  export * from './types';
package/index.js CHANGED
@@ -1,15 +1,24 @@
1
1
  import './index.css';
2
2
  export { TreeSelector } from './TreeSelector.js';
3
- export { UncontrolledTreeSelector } from './UncontrolledTreeSelector.js';
3
+ export { TreeSelectorDummy } from './TreeSelectorDummy.js';
4
4
  import 'react/jsx-runtime';
5
5
  import 'react';
6
+ import './strategy/immutableSelectionStrategy.js';
7
+ import './strategy/selectionStrategy.js';
8
+ import './collection/treeCollectionHelper.js';
9
+ import './collection/treeCollection.js';
10
+ import './strategy/createSingleValueToggler.js';
11
+ import './strategy/createTreeCollectionToggler.js';
12
+ import './strategy/dummyToggle.js';
13
+ import './useDisabled.js';
14
+ import './useSelected.js';
6
15
  import '@hh.ru/magritte-common-fuzzy-search';
7
16
  import '@hh.ru/magritte-ui-breakpoint';
8
17
  import '@hh.ru/magritte-ui-spacing';
9
18
  import './ItemsList.js';
10
19
  import './Item.js';
11
20
  import 'classnames';
12
- import './ItemContent-74eb482b.js';
21
+ import './ItemContent-f98f0b9a.js';
13
22
  import '@hh.ru/magritte-ui-cell';
14
23
  import '@hh.ru/magritte-ui-icon/icon';
15
24
  import './Action.js';
@@ -20,16 +29,8 @@ import '@hh.ru/magritte-ui-checkable-card/CheckableCardElement';
20
29
  import './MobileItem.js';
21
30
  import './MobileParentItem.js';
22
31
  import '@hh.ru/magritte-ui-card';
23
- import './collection/treeCollectionHelper.js';
24
- import './collection/treeCollection.js';
25
32
  import './useExpanded.js';
26
33
  import './useIndeterminate.js';
27
34
  import './useRenderInput.js';
28
35
  import '@hh.ru/magritte-ui-input';
29
- import './strategy/immutableSelectionStrategy.js';
30
- import './strategy/selectionStrategy.js';
31
- import './strategy/createSingleValueToggler.js';
32
- import './strategy/createTreeCollectionToggler.js';
33
- import './strategy/dummyToggle.js';
34
- import './useSelected.js';
35
36
  //# sourceMappingURL=index.js.map
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hh.ru/magritte-ui-tree-selector",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts",
6
6
  "sideEffects": [
@@ -21,9 +21,9 @@
21
21
  "dependencies": {
22
22
  "@hh.ru/magritte-common-fuzzy-search": "1.0.4",
23
23
  "@hh.ru/magritte-ui-breakpoint": "4.0.2",
24
- "@hh.ru/magritte-ui-card": "6.0.5",
24
+ "@hh.ru/magritte-ui-card": "6.0.6",
25
25
  "@hh.ru/magritte-ui-cell": "2.2.12",
26
- "@hh.ru/magritte-ui-checkable-card": "3.0.8",
26
+ "@hh.ru/magritte-ui-checkable-card": "3.0.9",
27
27
  "@hh.ru/magritte-ui-checkbox-radio": "3.0.3",
28
28
  "@hh.ru/magritte-ui-icon": "7.1.7",
29
29
  "@hh.ru/magritte-ui-input": "5.0.21",
@@ -39,5 +39,5 @@
39
39
  "publishConfig": {
40
40
  "access": "public"
41
41
  },
42
- "gitHead": "98a8e09926b544aea2b9255436cf0605ad12e3f1"
42
+ "gitHead": "079f9054662c714de59315a0d7652297c5817647"
43
43
  }
package/types.d.ts CHANGED
@@ -8,12 +8,13 @@ interface ChildrenProps {
8
8
  interface Children {
9
9
  (props: ChildrenProps): ReactNode;
10
10
  }
11
- export interface TreeSelectorProps<A extends AdditionalDefault> {
11
+ export interface BaseTreeSelectorProps<A extends AdditionalDefault> {
12
12
  collection: TreeCollection<A>;
13
13
  singleChoice?: boolean;
14
14
  leavesOnly?: boolean;
15
15
  checkSelectable?: IdCollectionPredicate;
16
16
  initialExpanded?: string[];
17
+ disabled?: string[];
17
18
  expanded?: string[];
18
19
  onExpand?: (expanded: string[]) => void;
19
20
  children: Children;
@@ -23,16 +24,25 @@ export interface TreeSelectorProps<A extends AdditionalDefault> {
23
24
  constantlySuggested?: string[];
24
25
  onChangeFilterQuery?: (ids: string[], query: string) => void;
25
26
  }
26
- export interface ControlledProps {
27
+ export interface DummyProps {
27
28
  value: string[];
28
29
  onChange: (id: string, isSelected: boolean) => void;
30
+ collapseToParentId?: never;
31
+ maxSelected?: never;
29
32
  }
30
- type UncontrolledProps = {
31
- value?: string[];
33
+ type NonDummyProps = {
32
34
  onChange?: (id: string, isSelected: boolean, allSelected: string[]) => void;
33
- };
34
- export type ControlledTreeSelectorProps<A extends AdditionalDefault> = TreeSelectorProps<A> & ControlledProps;
35
- export type UncontrolledTreeSelectorProps<A extends AdditionalDefault> = TreeSelectorProps<A> & UncontrolledProps;
35
+ collapseToParentId?: boolean;
36
+ maxSelected?: number;
37
+ } & ({
38
+ value: string[];
39
+ initialValue?: never;
40
+ } | {
41
+ value?: never;
42
+ initialValue: string[];
43
+ });
44
+ export type TreeSelectorDummyProps<A extends AdditionalDefault> = BaseTreeSelectorProps<A> & DummyProps;
45
+ export type TreeSelectorProps<A extends AdditionalDefault> = BaseTreeSelectorProps<A> & NonDummyProps;
36
46
  export interface ListControls {
37
47
  back: () => void;
38
48
  }
@@ -0,0 +1,13 @@
1
+ import TreeCollection from './collection/treeCollection';
2
+ import { AdditionalDefault } from './collection/types';
3
+ interface UseDisabledHookProps<A extends AdditionalDefault> {
4
+ selected: string[];
5
+ disabled?: string[];
6
+ collection: TreeCollection<A>;
7
+ maxSelected?: number;
8
+ }
9
+ interface UseDisabledHook {
10
+ <A extends AdditionalDefault>(props: UseDisabledHookProps<A>): string[] | undefined;
11
+ }
12
+ export declare const useDisabled: UseDisabledHook;
13
+ export {};
package/useDisabled.js ADDED
@@ -0,0 +1,20 @@
1
+ import './index.css';
2
+ import { useMemo } from 'react';
3
+ import { getIdsWithNoParentsInSameList } from './collection/treeCollectionHelper.js';
4
+ import './collection/treeCollection.js';
5
+
6
+ const useDisabled = ({ selected, disabled, collection, maxSelected }) => {
7
+ return useMemo(() => {
8
+ const valueIds = getIdsWithNoParentsInSameList(collection, selected);
9
+ if (maxSelected && valueIds.length >= maxSelected) {
10
+ return collection
11
+ .toList()
12
+ .map(({ id }) => id)
13
+ .filter((id) => !selected.includes(id));
14
+ }
15
+ return disabled;
16
+ }, [selected, collection, disabled, maxSelected]);
17
+ };
18
+
19
+ export { useDisabled };
20
+ //# sourceMappingURL=useDisabled.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDisabled.js","sources":["../src/useDisabled.ts"],"sourcesContent":["import { useMemo } from 'react';\n\nimport TreeCollection from '@hh.ru/magritte-ui-tree-selector/collection/treeCollection';\nimport { getIdsWithNoParentsInSameList } from '@hh.ru/magritte-ui-tree-selector/collection/treeCollectionHelper';\nimport { AdditionalDefault } from '@hh.ru/magritte-ui-tree-selector/collection/types';\n\ninterface UseDisabledHookProps<A extends AdditionalDefault> {\n selected: string[];\n disabled?: string[];\n collection: TreeCollection<A>;\n maxSelected?: number;\n}\ninterface UseDisabledHook {\n <A extends AdditionalDefault>(props: UseDisabledHookProps<A>): string[] | undefined;\n}\n\nexport const useDisabled: UseDisabledHook = ({ selected, disabled, collection, maxSelected }) => {\n return useMemo(() => {\n const valueIds = getIdsWithNoParentsInSameList(collection, selected);\n if (maxSelected && valueIds.length >= maxSelected) {\n return collection\n .toList()\n .map(({ id }) => id)\n .filter((id) => !selected.includes(id));\n }\n return disabled;\n }, [selected, collection, disabled, maxSelected]);\n};\n"],"names":[],"mappings":";;;;AAgBO,MAAM,WAAW,GAAoB,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,KAAI;IAC5F,OAAO,OAAO,CAAC,MAAK;QAChB,MAAM,QAAQ,GAAG,6BAA6B,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;AACrE,QAAA,IAAI,WAAW,IAAI,QAAQ,CAAC,MAAM,IAAI,WAAW,EAAE;AAC/C,YAAA,OAAO,UAAU;AACZ,iBAAA,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;AACnB,iBAAA,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/C,SAAA;AACD,QAAA,OAAO,QAAQ,CAAC;KACnB,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC;AACtD;;;;"}
package/useExpanded.d.ts CHANGED
@@ -12,5 +12,5 @@ interface UseExpandedHookReturn {
12
12
  interface UseExpandedHook {
13
13
  (props: UseExpandedHookProps): UseExpandedHookReturn;
14
14
  }
15
- declare const useExpanded: UseExpandedHook;
16
- export default useExpanded;
15
+ export declare const useExpanded: UseExpandedHook;
16
+ export {};
package/useExpanded.js CHANGED
@@ -46,5 +46,5 @@ const useExpanded = ({ initialValue, controlledExpanded, onExpand }) => {
46
46
  return { expanded, setExpanded: handleSetExpanded, handleExpansion };
47
47
  };
48
48
 
49
- export { areEqualArrays, useExpanded as default };
49
+ export { areEqualArrays, useExpanded };
50
50
  //# sourceMappingURL=useExpanded.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useExpanded.js","sources":["../src/useExpanded.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nexport const areEqualArrays = (arr1: string[], arr2: string[]): boolean => {\n if (arr1.length !== arr2.length) {\n return false;\n }\n const sortArr2 = arr2.slice().sort();\n return arr1\n .slice()\n .sort()\n .every((item, index) => item === sortArr2[index]);\n};\n\ninterface UseExpandedHookProps {\n initialValue: string[];\n controlledExpanded?: string[];\n onExpand?: (expanded: string[]) => void;\n}\n\ninterface UseExpandedHookReturn {\n expanded: string[];\n setExpanded: (expanded: string[]) => void;\n handleExpansion: (id: string) => void;\n}\ninterface UseExpandedHook {\n (props: UseExpandedHookProps): UseExpandedHookReturn;\n}\n\nconst useExpanded: UseExpandedHook = ({ initialValue, controlledExpanded, onExpand }) => {\n const [expanded, setExpanded] = useState(initialValue);\n const expandedRef = useRef(initialValue);\n const handlerRef = useRef(onExpand);\n\n const handleSetExpanded = useCallback((updatedExpanded: string[]) => {\n handlerRef.current?.(updatedExpanded.slice());\n\n if (!areEqualArrays(expandedRef.current, updatedExpanded)) {\n setExpanded(updatedExpanded);\n }\n }, []);\n\n const handleExpansion = useCallback(\n (id: string): void => {\n let updatedExpanded;\n if (expandedRef.current.includes(id)) {\n updatedExpanded = expandedRef.current.filter((itemId) => itemId !== id);\n } else {\n updatedExpanded = expandedRef.current.slice();\n updatedExpanded.push(id);\n }\n handleSetExpanded(updatedExpanded);\n },\n [handleSetExpanded]\n );\n\n useEffect(() => {\n handlerRef.current = onExpand;\n }, [onExpand]);\n\n useEffect(() => {\n expandedRef.current = expanded;\n }, [expanded]);\n\n useEffect(() => {\n if (controlledExpanded) {\n handleSetExpanded(controlledExpanded.slice());\n }\n }, [controlledExpanded, handleSetExpanded]);\n\n return { expanded, setExpanded: handleSetExpanded, handleExpansion };\n};\n\nexport default useExpanded;\n"],"names":[],"mappings":";;MAEa,cAAc,GAAG,CAAC,IAAc,EAAE,IAAc,KAAa;AACtE,IAAA,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE;AAC7B,QAAA,OAAO,KAAK,CAAC;AAChB,KAAA;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC;AACrC,IAAA,OAAO,IAAI;AACN,SAAA,KAAK,EAAE;AACP,SAAA,IAAI,EAAE;AACN,SAAA,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AAC1D,EAAE;AAiBI,MAAA,WAAW,GAAoB,CAAC,EAAE,YAAY,EAAE,kBAAkB,EAAE,QAAQ,EAAE,KAAI;IACpF,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC;AACvD,IAAA,MAAM,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;AACzC,IAAA,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;AAEpC,IAAA,MAAM,iBAAiB,GAAG,WAAW,CAAC,CAAC,eAAyB,KAAI;QAChE,UAAU,CAAC,OAAO,GAAG,eAAe,CAAC,KAAK,EAAE,CAAC,CAAC;QAE9C,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE;YACvD,WAAW,CAAC,eAAe,CAAC,CAAC;AAChC,SAAA;KACJ,EAAE,EAAE,CAAC,CAAC;AAEP,IAAA,MAAM,eAAe,GAAG,WAAW,CAC/B,CAAC,EAAU,KAAU;AACjB,QAAA,IAAI,eAAe,CAAC;QACpB,IAAI,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE;AAClC,YAAA,eAAe,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,MAAM,KAAK,EAAE,CAAC,CAAC;AAC3E,SAAA;AAAM,aAAA;AACH,YAAA,eAAe,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;AAC9C,YAAA,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC5B,SAAA;QACD,iBAAiB,CAAC,eAAe,CAAC,CAAC;AACvC,KAAC,EACD,CAAC,iBAAiB,CAAC,CACtB,CAAC;IAEF,SAAS,CAAC,MAAK;AACX,QAAA,UAAU,CAAC,OAAO,GAAG,QAAQ,CAAC;AAClC,KAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,SAAS,CAAC,MAAK;AACX,QAAA,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;AACnC,KAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,SAAS,CAAC,MAAK;AACX,QAAA,IAAI,kBAAkB,EAAE;AACpB,YAAA,iBAAiB,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC,CAAC;AACjD,SAAA;AACL,KAAC,EAAE,CAAC,kBAAkB,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAE5C,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,iBAAiB,EAAE,eAAe,EAAE,CAAC;AACzE;;;;"}
1
+ {"version":3,"file":"useExpanded.js","sources":["../src/useExpanded.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nexport const areEqualArrays = (arr1: string[], arr2: string[]): boolean => {\n if (arr1.length !== arr2.length) {\n return false;\n }\n const sortArr2 = arr2.slice().sort();\n return arr1\n .slice()\n .sort()\n .every((item, index) => item === sortArr2[index]);\n};\n\ninterface UseExpandedHookProps {\n initialValue: string[];\n controlledExpanded?: string[];\n onExpand?: (expanded: string[]) => void;\n}\n\ninterface UseExpandedHookReturn {\n expanded: string[];\n setExpanded: (expanded: string[]) => void;\n handleExpansion: (id: string) => void;\n}\ninterface UseExpandedHook {\n (props: UseExpandedHookProps): UseExpandedHookReturn;\n}\n\nexport const useExpanded: UseExpandedHook = ({ initialValue, controlledExpanded, onExpand }) => {\n const [expanded, setExpanded] = useState(initialValue);\n const expandedRef = useRef(initialValue);\n const handlerRef = useRef(onExpand);\n\n const handleSetExpanded = useCallback((updatedExpanded: string[]) => {\n handlerRef.current?.(updatedExpanded.slice());\n\n if (!areEqualArrays(expandedRef.current, updatedExpanded)) {\n setExpanded(updatedExpanded);\n }\n }, []);\n\n const handleExpansion = useCallback(\n (id: string): void => {\n let updatedExpanded;\n if (expandedRef.current.includes(id)) {\n updatedExpanded = expandedRef.current.filter((itemId) => itemId !== id);\n } else {\n updatedExpanded = expandedRef.current.slice();\n updatedExpanded.push(id);\n }\n handleSetExpanded(updatedExpanded);\n },\n [handleSetExpanded]\n );\n\n useEffect(() => {\n handlerRef.current = onExpand;\n }, [onExpand]);\n\n useEffect(() => {\n expandedRef.current = expanded;\n }, [expanded]);\n\n useEffect(() => {\n if (controlledExpanded) {\n handleSetExpanded(controlledExpanded.slice());\n }\n }, [controlledExpanded, handleSetExpanded]);\n\n return { expanded, setExpanded: handleSetExpanded, handleExpansion };\n};\n"],"names":[],"mappings":";;MAEa,cAAc,GAAG,CAAC,IAAc,EAAE,IAAc,KAAa;AACtE,IAAA,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE;AAC7B,QAAA,OAAO,KAAK,CAAC;AAChB,KAAA;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC;AACrC,IAAA,OAAO,IAAI;AACN,SAAA,KAAK,EAAE;AACP,SAAA,IAAI,EAAE;AACN,SAAA,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AAC1D,EAAE;AAiBK,MAAM,WAAW,GAAoB,CAAC,EAAE,YAAY,EAAE,kBAAkB,EAAE,QAAQ,EAAE,KAAI;IAC3F,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC;AACvD,IAAA,MAAM,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;AACzC,IAAA,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;AAEpC,IAAA,MAAM,iBAAiB,GAAG,WAAW,CAAC,CAAC,eAAyB,KAAI;QAChE,UAAU,CAAC,OAAO,GAAG,eAAe,CAAC,KAAK,EAAE,CAAC,CAAC;QAE9C,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE;YACvD,WAAW,CAAC,eAAe,CAAC,CAAC;AAChC,SAAA;KACJ,EAAE,EAAE,CAAC,CAAC;AAEP,IAAA,MAAM,eAAe,GAAG,WAAW,CAC/B,CAAC,EAAU,KAAU;AACjB,QAAA,IAAI,eAAe,CAAC;QACpB,IAAI,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE;AAClC,YAAA,eAAe,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,MAAM,KAAK,EAAE,CAAC,CAAC;AAC3E,SAAA;AAAM,aAAA;AACH,YAAA,eAAe,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;AAC9C,YAAA,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC5B,SAAA;QACD,iBAAiB,CAAC,eAAe,CAAC,CAAC;AACvC,KAAC,EACD,CAAC,iBAAiB,CAAC,CACtB,CAAC;IAEF,SAAS,CAAC,MAAK;AACX,QAAA,UAAU,CAAC,OAAO,GAAG,QAAQ,CAAC;AAClC,KAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,SAAS,CAAC,MAAK;AACX,QAAA,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;AACnC,KAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,SAAS,CAAC,MAAK;AACX,QAAA,IAAI,kBAAkB,EAAE;AACpB,YAAA,iBAAiB,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC,CAAC;AACjD,SAAA;AACL,KAAC,EAAE,CAAC,kBAAkB,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAE5C,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,iBAAiB,EAAE,eAAe,EAAE,CAAC;AACzE;;;;"}
package/useSelected.d.ts CHANGED
@@ -1,9 +1,14 @@
1
+ import TreeCollection from './collection/treeCollection';
1
2
  import { AdditionalDefault } from './collection/types';
2
3
  import ImmutableSelectionStrategy from './strategy/immutableSelectionStrategy';
3
4
  interface UseSelectedHookProps<A extends AdditionalDefault> {
4
- value: string[];
5
+ value?: string[];
6
+ initialValue?: string[];
5
7
  strategy: ImmutableSelectionStrategy<A>;
8
+ collection: TreeCollection<A>;
6
9
  onChange?: (id: string, isSelected: boolean, allSelected: string[]) => void;
10
+ collapseToParentId?: boolean;
11
+ maxSelected?: number;
7
12
  }
8
13
  type UseSelectedHookReturn = {
9
14
  selected: string[];
package/useSelected.js CHANGED
@@ -1,16 +1,47 @@
1
1
  import './index.css';
2
- import { useState, useCallback } from 'react';
2
+ import { useCallback, useState, useRef, useEffect } from 'react';
3
+ import { getIdsWithNoParentsInSameList } from './collection/treeCollectionHelper.js';
4
+ import './collection/treeCollection.js';
3
5
 
4
- const useSelected = ({ value: initialValue, strategy, onChange }) => {
5
- const [values, setValues] = useState(initialValue);
6
+ const defaultArray = [];
7
+ const useSelected = ({ value, initialValue = defaultArray, maxSelected, strategy, collection, collapseToParentId, onChange, }) => {
8
+ const getSelectedFromValue = useCallback(() => {
9
+ let newValue = value?.slice();
10
+ if (maxSelected && newValue && newValue.length > maxSelected) {
11
+ newValue = newValue.slice(0, maxSelected);
12
+ }
13
+ return strategy.add([], newValue);
14
+ }, [value, maxSelected, strategy]);
15
+ const isControlled = !!value;
16
+ const slicedInitialValue = initialValue && maxSelected ? initialValue.slice(0, maxSelected) : initialValue;
17
+ const [selected, setSelectedState] = useState(isControlled ? getSelectedFromValue : slicedInitialValue);
18
+ const firstRender = useRef(true);
6
19
  const setSelected = useCallback((id, isSelected) => {
7
20
  const ids = [id];
8
- const updatedSelected = isSelected ? strategy.add(values, ids) : strategy.remove(values, ids);
9
- setValues(updatedSelected);
10
- onChange?.(id, isSelected, updatedSelected);
11
- }, [strategy, values, onChange]);
21
+ const updatedSelected = isSelected ? strategy.add(selected, ids) : strategy.remove(selected, ids);
22
+ const filteredUpdatedSelected = collapseToParentId
23
+ ? getIdsWithNoParentsInSameList(collection, updatedSelected)
24
+ : updatedSelected;
25
+ if (maxSelected && filteredUpdatedSelected.length > maxSelected) {
26
+ filteredUpdatedSelected.length = maxSelected;
27
+ }
28
+ if (!isControlled) {
29
+ setSelectedState(strategy.add([], filteredUpdatedSelected));
30
+ }
31
+ onChange?.(id, isSelected, filteredUpdatedSelected);
32
+ }, [strategy, selected, collection, collapseToParentId, onChange, isControlled, maxSelected]);
33
+ useEffect(() => {
34
+ if (isControlled) {
35
+ if (firstRender.current) {
36
+ firstRender.current = false;
37
+ return;
38
+ }
39
+ const selected = getSelectedFromValue();
40
+ setSelectedState(selected);
41
+ }
42
+ }, [getSelectedFromValue, firstRender, isControlled]);
12
43
  return {
13
- selected: values,
44
+ selected,
14
45
  setSelected,
15
46
  };
16
47
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useSelected.js","sources":["../src/useSelected.ts"],"sourcesContent":["import { useState, useCallback } from 'react';\n\nimport { AdditionalDefault } from '@hh.ru/magritte-ui-tree-selector/collection/types';\nimport ImmutableSelectionStrategy from '@hh.ru/magritte-ui-tree-selector/strategy/immutableSelectionStrategy';\n\ninterface UseSelectedHookProps<A extends AdditionalDefault> {\n value: string[];\n strategy: ImmutableSelectionStrategy<A>;\n onChange?: (id: string, isSelected: boolean, allSelected: string[]) => void;\n}\n\ntype UseSelectedHookReturn = {\n selected: string[];\n setSelected: (id: string, isSelected: boolean) => void;\n};\n\ninterface UseSelectedHook {\n <A extends AdditionalDefault>(props: UseSelectedHookProps<A>): UseSelectedHookReturn;\n}\n\nexport const useSelected: UseSelectedHook = ({ value: initialValue, strategy, onChange }) => {\n const [values, setValues] = useState(initialValue);\n\n const setSelected = useCallback(\n (id: string, isSelected: boolean) => {\n const ids = [id];\n const updatedSelected = isSelected ? strategy.add(values, ids) : strategy.remove(values, ids);\n\n setValues(updatedSelected);\n onChange?.(id, isSelected, updatedSelected);\n },\n [strategy, values, onChange]\n );\n\n return {\n selected: values,\n setSelected,\n };\n};\n"],"names":[],"mappings":";;AAoBO,MAAM,WAAW,GAAoB,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAI;IACxF,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC;IAEnD,MAAM,WAAW,GAAG,WAAW,CAC3B,CAAC,EAAU,EAAE,UAAmB,KAAI;AAChC,QAAA,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;QACjB,MAAM,eAAe,GAAG,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAE9F,SAAS,CAAC,eAAe,CAAC,CAAC;QAC3B,QAAQ,GAAG,EAAE,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC;KAC/C,EACD,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAC/B,CAAC;IAEF,OAAO;AACH,QAAA,QAAQ,EAAE,MAAM;QAChB,WAAW;KACd,CAAC;AACN;;;;"}
1
+ {"version":3,"file":"useSelected.js","sources":["../src/useSelected.ts"],"sourcesContent":["import { useState, useCallback, useRef, useEffect } from 'react';\n\nimport TreeCollection from '@hh.ru/magritte-ui-tree-selector/collection/treeCollection';\nimport { getIdsWithNoParentsInSameList } from '@hh.ru/magritte-ui-tree-selector/collection/treeCollectionHelper';\nimport { AdditionalDefault } from '@hh.ru/magritte-ui-tree-selector/collection/types';\nimport ImmutableSelectionStrategy from '@hh.ru/magritte-ui-tree-selector/strategy/immutableSelectionStrategy';\n\ninterface UseSelectedHookProps<A extends AdditionalDefault> {\n value?: string[];\n initialValue?: string[];\n strategy: ImmutableSelectionStrategy<A>;\n collection: TreeCollection<A>;\n onChange?: (id: string, isSelected: boolean, allSelected: string[]) => void;\n collapseToParentId?: boolean;\n maxSelected?: number;\n}\n\ntype UseSelectedHookReturn = {\n selected: string[];\n setSelected: (id: string, isSelected: boolean) => void;\n};\n\ninterface UseSelectedHook {\n <A extends AdditionalDefault>(props: UseSelectedHookProps<A>): UseSelectedHookReturn;\n}\n\nconst defaultArray: string[] = [];\n\nexport const useSelected: UseSelectedHook = ({\n value,\n initialValue = defaultArray,\n maxSelected,\n strategy,\n collection,\n collapseToParentId,\n onChange,\n}) => {\n const getSelectedFromValue = useCallback((): string[] => {\n let newValue = value?.slice();\n if (maxSelected && newValue && newValue.length > maxSelected) {\n newValue = newValue.slice(0, maxSelected);\n }\n return strategy.add([], newValue as string[]);\n }, [value, maxSelected, strategy]);\n const isControlled = !!value;\n const slicedInitialValue = initialValue && maxSelected ? initialValue.slice(0, maxSelected) : initialValue;\n\n const [selected, setSelectedState] = useState(isControlled ? getSelectedFromValue : slicedInitialValue);\n const firstRender = useRef(true);\n\n const setSelected = useCallback(\n (id: string, isSelected: boolean) => {\n const ids = [id];\n const updatedSelected = isSelected ? strategy.add(selected, ids) : strategy.remove(selected, ids);\n const filteredUpdatedSelected = collapseToParentId\n ? getIdsWithNoParentsInSameList(collection, updatedSelected)\n : updatedSelected;\n if (maxSelected && filteredUpdatedSelected.length > maxSelected) {\n filteredUpdatedSelected.length = maxSelected;\n }\n if (!isControlled) {\n setSelectedState(strategy.add([], filteredUpdatedSelected));\n }\n onChange?.(id, isSelected, filteredUpdatedSelected);\n },\n [strategy, selected, collection, collapseToParentId, onChange, isControlled, maxSelected]\n );\n\n useEffect(() => {\n if (isControlled) {\n if (firstRender.current) {\n firstRender.current = false;\n return;\n }\n\n const selected = getSelectedFromValue();\n\n setSelectedState(selected);\n }\n }, [getSelectedFromValue, firstRender, isControlled]);\n\n return {\n selected,\n setSelected,\n };\n};\n"],"names":[],"mappings":";;;;AA0BA,MAAM,YAAY,GAAa,EAAE,CAAC;MAErB,WAAW,GAAoB,CAAC,EACzC,KAAK,EACL,YAAY,GAAG,YAAY,EAC3B,WAAW,EACX,QAAQ,EACR,UAAU,EACV,kBAAkB,EAClB,QAAQ,GACX,KAAI;AACD,IAAA,MAAM,oBAAoB,GAAG,WAAW,CAAC,MAAe;AACpD,QAAA,IAAI,QAAQ,GAAG,KAAK,EAAE,KAAK,EAAE,CAAC;QAC9B,IAAI,WAAW,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,WAAW,EAAE;YAC1D,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;AAC7C,SAAA;QACD,OAAO,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,QAAoB,CAAC,CAAC;KACjD,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;AACnC,IAAA,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC;IAC7B,MAAM,kBAAkB,GAAG,YAAY,IAAI,WAAW,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,GAAG,YAAY,CAAC;AAE3G,IAAA,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAC,YAAY,GAAG,oBAAoB,GAAG,kBAAkB,CAAC,CAAC;AACxG,IAAA,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAEjC,MAAM,WAAW,GAAG,WAAW,CAC3B,CAAC,EAAU,EAAE,UAAmB,KAAI;AAChC,QAAA,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;QACjB,MAAM,eAAe,GAAG,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAClG,MAAM,uBAAuB,GAAG,kBAAkB;AAC9C,cAAE,6BAA6B,CAAC,UAAU,EAAE,eAAe,CAAC;cAC1D,eAAe,CAAC;AACtB,QAAA,IAAI,WAAW,IAAI,uBAAuB,CAAC,MAAM,GAAG,WAAW,EAAE;AAC7D,YAAA,uBAAuB,CAAC,MAAM,GAAG,WAAW,CAAC;AAChD,SAAA;QACD,IAAI,CAAC,YAAY,EAAE;YACf,gBAAgB,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,uBAAuB,CAAC,CAAC,CAAC;AAC/D,SAAA;QACD,QAAQ,GAAG,EAAE,EAAE,UAAU,EAAE,uBAAuB,CAAC,CAAC;AACxD,KAAC,EACD,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,kBAAkB,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,CAAC,CAC5F,CAAC;IAEF,SAAS,CAAC,MAAK;AACX,QAAA,IAAI,YAAY,EAAE;YACd,IAAI,WAAW,CAAC,OAAO,EAAE;AACrB,gBAAA,WAAW,CAAC,OAAO,GAAG,KAAK,CAAC;gBAC5B,OAAO;AACV,aAAA;AAED,YAAA,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;YAExC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;AAC9B,SAAA;KACJ,EAAE,CAAC,oBAAoB,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC;IAEtD,OAAO;QACH,QAAQ;QACR,WAAW;KACd,CAAC;AACN;;;;"}
@@ -1,22 +0,0 @@
1
- import './index.css';
2
- import { jsxs, jsx } from 'react/jsx-runtime';
3
- import { useCallback } from 'react';
4
- import classnames from 'classnames';
5
- import { Cell, CellText } from '@hh.ru/magritte-ui-cell';
6
- import { DotFilledSize24, ChevronDownOutlinedSize24, ChevronUpOutlinedSize24 } from '@hh.ru/magritte-ui-icon/icon';
7
- import { Action } from './Action.js';
8
- import { Text } from '@hh.ru/magritte-ui-typography';
9
-
10
- var styles = {"wrapper":"magritte-wrapper___GHKV6_1-3-1","letter":"magritte-letter___yZOCU_1-3-1","icon":"magritte-icon___kO3Fj_1-3-1","space":"magritte-space___xTO79_1-3-1","iconActive":"magritte-iconActive___4yrG5_1-3-1","content":"magritte-content___ZRc6R_1-3-1","with-shift":"magritte-with-shift___ZErxZ_1-3-1","withShift":"magritte-with-shift___ZErxZ_1-3-1","with-indent":"magritte-with-indent___MH9Vy_1-3-1","withIndent":"magritte-with-indent___MH9Vy_1-3-1","item":"magritte-item___2LtOL_1-3-1","children":"magritte-children___kq-eq_1-3-1","with-two-boxes":"magritte-with-two-boxes___LWOy2_1-3-1","withTwoBoxes":"magritte-with-two-boxes___LWOy2_1-3-1","with-three-boxes":"magritte-with-three-boxes___cyVao_1-3-1","withThreeBoxes":"magritte-with-three-boxes___cyVao_1-3-1"};
11
-
12
- const ItemContent = ({ item, hasAction, hasChildren, letter, expanded, onExpansion, hasLetterOnLevel, selected, onChange, indeterminate, singleChoice, hasDot, maxControlsOnLevel, }) => {
13
- const handleExpandableClick = useCallback(() => onExpansion && onExpansion(item.id), [item.id, onExpansion]);
14
- const currentActionCount = +hasDot + +hasAction + +hasChildren;
15
- const neededSpacesCount = maxControlsOnLevel - currentActionCount;
16
- return (jsxs("div", { className: styles.wrapper, children: [(letter || hasLetterOnLevel) && (jsx("div", { className: styles.letter, children: letter && (jsx(Text, { typography: "subtitle-1-semibold", style: "secondary", Element: "span", children: letter })) })), hasDot && (jsx("div", { className: styles.icon, children: jsx(DotFilledSize24, { initial: "tertiary" }) })), [...Array(neededSpacesCount).keys()].map((_, i) => (jsx("div", { className: styles.space }, i))), hasChildren && (jsxs("div", { className: classnames(styles.icon, {
17
- [styles.iconActive]: hasChildren,
18
- }), onClick: handleExpandableClick, children: [expanded && jsx(ChevronDownOutlinedSize24, { initial: "tertiary" }), !expanded && jsx(ChevronUpOutlinedSize24, { initial: "tertiary" })] })), jsx(Cell, { left: hasAction ? (jsx(Action, { selected: selected, onChange: onChange, id: item.id, indeterminate: indeterminate, singleChoice: singleChoice })) : undefined, children: jsx(CellText, { children: item.text }) })] }));
19
- };
20
-
21
- export { ItemContent as I, styles as s };
22
- //# sourceMappingURL=ItemContent-74eb482b.js.map