mimir-ui-kit 1.44.3 → 1.46.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 (33) hide show
  1. package/dist/assets/SelectSearch.css +1 -1
  2. package/dist/assets/useNestedSelection.css +1 -0
  3. package/dist/components/MultiSelectSearch/MultiSelectSearch.d.ts +6 -3
  4. package/dist/components/MultiSelectSearch/MultiSelectSearch.js +283 -468
  5. package/dist/components/MultiSelectSearch/hooks/useChips.d.ts +18 -0
  6. package/dist/components/MultiSelectSearch/hooks/useChips.js +79 -0
  7. package/dist/components/MultiSelectSearch/hooks/useDataLoading.d.ts +10 -0
  8. package/dist/components/MultiSelectSearch/hooks/useDataLoading.js +21 -0
  9. package/dist/components/MultiSelectSearch/hooks/useEventHandling.d.ts +12 -0
  10. package/dist/components/MultiSelectSearch/hooks/useEventHandling.js +31 -0
  11. package/dist/components/MultiSelectSearch/hooks/useMenuPlacement.d.ts +19 -0
  12. package/dist/components/MultiSelectSearch/hooks/useMenuPlacement.js +38 -0
  13. package/dist/components/MultiSelectSearch/hooks/useMultiSelectState.d.ts +21 -0
  14. package/dist/components/MultiSelectSearch/hooks/useMultiSelectState.js +31 -0
  15. package/dist/components/MultiSelectSearch/hooks/useNestedSelection.d.ts +23 -0
  16. package/dist/components/MultiSelectSearch/hooks/useNestedSelection.js +11 -0
  17. package/dist/components/MultiSelectSearch/hooks/useRenderVirtualizedList.d.ts +27 -0
  18. package/dist/components/MultiSelectSearch/hooks/useRenderVirtualizedList.js +211 -0
  19. package/dist/components/MultiSelectSearch/hooks/useSearch.d.ts +14 -0
  20. package/dist/components/MultiSelectSearch/hooks/useSearch.js +44 -0
  21. package/dist/components/MultiSelectSearch/hooks/useVirtualization.d.ts +19 -0
  22. package/dist/components/MultiSelectSearch/hooks/useVirtualization.js +59 -0
  23. package/dist/components/MultiSelectSearch/types.d.ts +17 -1
  24. package/dist/components/MultiSelectSearch/utils.d.ts +50 -1
  25. package/dist/components/MultiSelectSearch/utils.js +165 -32
  26. package/dist/components/SelectSearch/SelectSearch.d.ts +4 -2
  27. package/dist/components/SelectSearch/SelectSearch.js +291 -287
  28. package/dist/components/SelectSearch/types.d.ts +5 -0
  29. package/dist/components/SelectSearch/utils.d.ts +16 -0
  30. package/dist/components/SelectSearch/utils.js +75 -28
  31. package/dist/useNestedSelection-DnuUbEqM.js +119 -0
  32. package/package.json +1 -1
  33. package/dist/assets/MultiSelectSearch.css +0 -1
@@ -0,0 +1,59 @@
1
+ import { u as h } from "../../../index-D5H8gPPn.js";
2
+ import { useState as p, useMemo as I, useEffect as z } from "react";
3
+ import { EMultiSelectSearchSize as M } from "../constants.js";
4
+ import { prepareGroupedItems as O } from "../utils.js";
5
+ const T = 10, S = {
6
+ M: 49,
7
+ L: 65
8
+ }, b = ({
9
+ filteredItems: u,
10
+ groupBy: e,
11
+ getGroupTitle: f,
12
+ size: x,
13
+ parentRef: a
14
+ }) => {
15
+ const [k, L] = p(null), r = I(() => e ? O(
16
+ u,
17
+ e,
18
+ f || ((t) => t)
19
+ ) : u, [u, e, f]), n = I(() => {
20
+ const t = [];
21
+ return r.forEach((c, i) => {
22
+ c.isGroupHeader && t.push(i);
23
+ }), t;
24
+ }, [r]), s = h({
25
+ count: r.length,
26
+ getScrollElement: () => a.current,
27
+ estimateSize: () => x === M.L ? S.L : S.M,
28
+ overscan: T
29
+ });
30
+ return z(() => {
31
+ const t = a.current;
32
+ if (!t || !e) return;
33
+ const c = () => {
34
+ var d, v;
35
+ const i = t.scrollTop;
36
+ let m = null;
37
+ for (let o = 0; o < n.length; o++) {
38
+ const E = n[o];
39
+ if ((((d = s.getVirtualItems().find((l) => l.index === E)) == null ? void 0 : d.start) || 0) <= i) {
40
+ m = E;
41
+ const l = n[o + 1];
42
+ if (l !== void 0 && (((v = s.getVirtualItems().find((V) => V.index === l)) == null ? void 0 : v.start) || 0) <= i)
43
+ continue;
44
+ } else
45
+ break;
46
+ }
47
+ L(m);
48
+ };
49
+ return t.addEventListener("scroll", c), () => t.removeEventListener("scroll", c);
50
+ }, [s, n, e, a.current]), {
51
+ virtualItems: r,
52
+ stickyIndices: n,
53
+ virtualizer: s,
54
+ activeSticky: k
55
+ };
56
+ };
57
+ export {
58
+ b as useVirtualization
59
+ };
@@ -4,13 +4,19 @@ import { EChipVariant } from '../Chip';
4
4
  import { TInputProps } from '../Input';
5
5
 
6
6
  export type TMultiSelectOption = {
7
- name: string;
7
+ name: string | ReactNode;
8
8
  id: number | string;
9
9
  bottom?: ReactNode;
10
10
  isGroupHeader?: boolean;
11
11
  originalValue?: string;
12
+ children?: TMultiSelectOption[];
12
13
  [index: string]: unknown;
13
14
  };
15
+ export type TChipItem = {
16
+ id: string | number;
17
+ name: string;
18
+ onClose: () => void;
19
+ };
14
20
  export type TMenuPlacement = 'top' | 'bottom';
15
21
  export type TLoadingIndicatorPlacement = 'input' | 'dropdown' | 'none';
16
22
  export type TMultiSelectSearchProps = Pick<TInputProps, 'withClearButton' | 'variant'> & {
@@ -101,4 +107,14 @@ export type TMultiSelectSearchProps = Pick<TInputProps, 'withClearButton' | 'var
101
107
  * Функция для получения заголовка группы из значения группировки
102
108
  */
103
109
  getGroupTitle?: (groupValue: string) => string;
110
+ /**
111
+ * Включить подсветку найденных значений в выпадающем списке
112
+ * @default false
113
+ */
114
+ highlightMatches?: boolean;
115
+ /**
116
+ * Включить поддержку вложенного выбора опций
117
+ * @default false
118
+ */
119
+ enableNestedSelection?: boolean;
104
120
  };
@@ -1,3 +1,4 @@
1
+ import { ReactElement, ReactNode, RefObject } from 'react';
1
2
  import { EMultiSelectSearchSize } from './constants';
2
3
  import { TMultiSelectOption } from './types';
3
4
  import { EInputSize } from '../Input';
@@ -8,5 +9,53 @@ export declare const getInitialInputValue: (value: TMultiSelectOption[] | undefi
8
9
  export declare const joinSelectedItems: (items: TMultiSelectOption[], displayValue: string) => string;
9
10
  export declare const MULTISELECT_OPEN_EVENT = "multiselectOpen";
10
11
  export declare const dispatchMultiselectOpen: (id: string) => void;
11
- export declare const shouldShowMenuOnTop: (selectRef: React.RefObject<HTMLDivElement>, size: EMultiSelectSearchSize) => boolean;
12
+ export declare const shouldShowMenuOnTop: (selectRef: RefObject<HTMLDivElement>, size: EMultiSelectSearchSize) => boolean;
12
13
  export declare const prepareGroupedItems: (items: TMultiSelectOption[], groupBy: string, getGroupTitle: (value: string) => string) => TMultiSelectOption[];
14
+ /**
15
+ * Рекурсивно подсвечивает совпадения поискового запроса в ReactNode (включая JSX элементы).
16
+ * @param node - ReactNode для обработки.
17
+ * @param searchQuery - Поисковый запрос.
18
+ * @param highlightClassName - CSS класс для подсвеченного текста.
19
+ * @returns ReactNode с подсвеченными совпадениями.
20
+ */
21
+ export declare const highlightReactNode: (node: ReactNode, searchQuery: string, highlightClassName: string) => ReactNode;
22
+ /**
23
+ * Подсвечивает совпадения поискового запроса в тексте.
24
+ * @param text - Исходный текст для подсветки.
25
+ * @param searchQuery - Поисковый запрос.
26
+ * @param highlightClassName - CSS класс для подсвеченного текста.
27
+ * @returns Массив частей текста, где совпадения обернуты в span с классом.
28
+ */
29
+ export declare const highlightText: (text: string, searchQuery: string, highlightClassName: string) => (string | ReactElement)[];
30
+ /**
31
+ * Рекурсивно получает все дочерние ID для опции.
32
+ */
33
+ export declare const getAllChildrenIds: (option: TMultiSelectOption, allOptions: TMultiSelectOption[]) => (number | string)[];
34
+ /**
35
+ * Получает плоский список всех опций, включая вложенные.
36
+ */
37
+ export declare const flattenOptions: (options: TMultiSelectOption[]) => TMultiSelectOption[];
38
+ /**
39
+ * Проверяет, выбрана ли опция, учитывая дочерние.
40
+ */
41
+ export declare const isOptionSelected: (option: TMultiSelectOption, selectedIds: (number | string)[]) => boolean;
42
+ /**
43
+ * Получает количество выбранных дочерних опций.
44
+ */
45
+ export declare const getSelectedChildrenCount: (option: TMultiSelectOption, selectedIds: (number | string)[]) => number;
46
+ /**
47
+ * Получает общее количество дочерних опций.
48
+ */
49
+ export declare const getTotalChildrenCount: (option: TMultiSelectOption) => number;
50
+ /**
51
+ * Форматирует отображение опции с счетчиком выбранных дочерних.
52
+ */
53
+ export declare const formatOptionWithCount: (option: TMultiSelectOption, selectedIds: (number | string)[], displayValue: string) => string;
54
+ /**
55
+ * Обрабатывает изменение выбора для nested selection.
56
+ */
57
+ export declare const processNestedSelectionChange: (newValue: TMultiSelectOption[], selectedItems: TMultiSelectOption[], items: TMultiSelectOption[]) => TMultiSelectOption[];
58
+ /**
59
+ * Обновляет значение input после изменения выбора.
60
+ */
61
+ export declare const updateInputValueAfterChange: (updatedValue: TMultiSelectOption[], enableNestedSelection: boolean, items: TMultiSelectOption[], displayValue: string) => string;
@@ -1,42 +1,175 @@
1
- import { EMultiSelectSearchSize as i, MOBILE_MENU_HEIGHT as c, DESKTOP_MENU_HEIGHT as a } from "./constants.js";
1
+ import { createElement as a, Fragment as I, isValidElement as E, cloneElement as S } from "react";
2
+ import { EMultiSelectSearchSize as h, MOBILE_MENU_HEIGHT as C, DESKTOP_MENU_HEIGHT as A } from "./constants.js";
2
3
  import "../../Input-IzZ6B9kw.js";
3
- import { EInputSize as u } from "../Input/constants.js";
4
- const m = (t) => {
4
+ import { EInputSize as f } from "../Input/constants.js";
5
+ const v = (t) => {
5
6
  switch (t) {
6
- case i.M:
7
- return u.M;
8
- case i.L:
9
- return u.L;
7
+ case h.M:
8
+ return f.M;
9
+ case h.L:
10
+ return f.L;
10
11
  default:
11
- return u.M;
12
+ return f.M;
12
13
  }
13
- }, E = (t, e) => t ? e === i.L ? "DropdownArrowUp24px" : "DropdownArrowUp16px" : e === i.L ? "DropdownArrowDown24px" : "DropdownArrowBottom16px", f = (t, e) => t && t.length > 0 ? t.map((r) => r[e]).join(", ") : "", g = (t, e) => t.map((r) => r[e]).join(", "), d = "multiselectOpen", S = (t) => {
14
- window.dispatchEvent(new CustomEvent(d, { detail: t }));
15
- }, I = (t, e) => {
14
+ }, G = (t, n) => t ? n === h.L ? "DropdownArrowUp24px" : "DropdownArrowUp16px" : n === h.L ? "DropdownArrowDown24px" : "DropdownArrowBottom16px", U = (t, n) => t && t.length > 0 ? t.map((r) => r[n]).join(", ") : "", M = (t, n) => t.map((r) => r[n]).join(", "), T = "multiselectOpen", j = (t) => {
15
+ window.dispatchEvent(new CustomEvent(T, { detail: t }));
16
+ }, R = (t, n) => {
16
17
  if (!t.current) return !1;
17
- const r = t.current.getBoundingClientRect(), o = window.innerHeight, s = e === i.M ? c : a, p = o - r.bottom, n = r.top;
18
- return p >= s ? !1 : n > p;
19
- }, M = (t, e, r) => {
20
- if (!t.length || !e) return t;
21
- const o = [], s = /* @__PURE__ */ new Set();
22
- for (const p of t) {
23
- const n = String(p[e] || "Без группы");
24
- s.has(n) || (s.add(n), o.push({
25
- id: `group-header-${n}`,
26
- name: r(n),
18
+ const r = t.current.getBoundingClientRect(), e = window.innerHeight, i = n === h.M ? C : A, s = e - r.bottom, c = r.top;
19
+ return s >= i ? !1 : c > s;
20
+ }, z = (t, n, r) => {
21
+ if (!t.length || !n) return t;
22
+ const e = [], i = /* @__PURE__ */ new Set();
23
+ for (const s of t) {
24
+ const c = String(s[n] || "Без группы");
25
+ i.has(c) || (i.add(c), e.push({
26
+ id: `group-header-${c}`,
27
+ name: r(c),
27
28
  isGroupHeader: !0,
28
- originalValue: n
29
- })), o.push(p);
29
+ originalValue: c
30
+ })), e.push(s);
30
31
  }
31
- return o;
32
+ return e;
33
+ }, m = (t, n, r) => {
34
+ if (!n.trim())
35
+ return t;
36
+ if (typeof t == "string") {
37
+ const e = O(
38
+ t,
39
+ n,
40
+ r
41
+ );
42
+ return a(
43
+ "span",
44
+ { className: "highlighted-text-wrapper" },
45
+ e
46
+ );
47
+ }
48
+ return Array.isArray(t) ? t.map(
49
+ (e, i) => a(
50
+ I,
51
+ { key: i },
52
+ m(e, n, r)
53
+ )
54
+ ) : E(t) ? S(t, {
55
+ ...t.props,
56
+ children: m(
57
+ t.props.children,
58
+ n,
59
+ r
60
+ )
61
+ }) : t;
62
+ }, O = (t, n, r) => {
63
+ if (!n.trim())
64
+ return [t];
65
+ const e = new RegExp(
66
+ `(${n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})`,
67
+ "gi"
68
+ );
69
+ return t.split(e).map((s, c) => e.test(s) ? a(
70
+ "span",
71
+ {
72
+ key: c,
73
+ className: r,
74
+ "data-testid": "highlighted-match"
75
+ },
76
+ s
77
+ ) : s);
78
+ }, p = (t, n) => {
79
+ const r = [];
80
+ if (t.children)
81
+ for (const e of t.children)
82
+ r.push(e.id), r.push(...p(e));
83
+ return r;
84
+ }, x = (t) => {
85
+ const n = [], r = (e) => {
86
+ for (const i of e)
87
+ n.push(i), i.children && r(i.children);
88
+ };
89
+ return r(t), n;
90
+ }, B = (t, n) => n.includes(t.id) ? !0 : p(t).some((e) => n.includes(e)), g = (t, n) => p(t).filter((e) => n.includes(e)).length, H = (t) => p(t).length, D = (t, n, r) => {
91
+ const e = g(t, n), i = H(t);
92
+ return i === 0 ? String(t[r] || "") : `${t[r]} (${e} из ${i})`;
93
+ }, P = (t, n, r) => {
94
+ let e = t;
95
+ const i = t.filter(
96
+ (o) => !n.some((d) => d.id === o.id)
97
+ ), s = n.filter(
98
+ (o) => !t.some((d) => d.id === o.id)
99
+ );
100
+ for (const o of i)
101
+ if (o.children && o.children.length > 0) {
102
+ const d = p(o), l = x(r).filter(
103
+ (u) => d.includes(u.id)
104
+ );
105
+ e = [...e, ...l];
106
+ } else {
107
+ const d = r.find(
108
+ (l) => {
109
+ var u;
110
+ return (u = l.children) == null ? void 0 : u.some((w) => w.id === o.id);
111
+ }
112
+ );
113
+ d && !e.some((l) => l.id === d.id) && (e = [...e, d]);
114
+ }
115
+ for (const o of s) {
116
+ const d = p(o);
117
+ e = e.filter((l) => !d.includes(l.id));
118
+ }
119
+ e = e.filter(
120
+ (o, d, l) => d === l.findIndex((u) => u.id === o.id)
121
+ );
122
+ const c = [];
123
+ for (const o of e)
124
+ o.children && o.children.length > 0 && g(
125
+ o,
126
+ e.map((l) => l.id)
127
+ ) === 0 && c.push(o.id);
128
+ return e = e.filter(
129
+ (o) => !c.includes(o.id)
130
+ ), e;
131
+ }, b = (t, n, r, e) => {
132
+ if (n) {
133
+ const i = /* @__PURE__ */ new Set();
134
+ for (const s of t)
135
+ if (s.children && s.children.length > 0)
136
+ i.add(s);
137
+ else {
138
+ const c = r.find(
139
+ (o) => {
140
+ var d;
141
+ return (d = o.children) == null ? void 0 : d.some((l) => l.id === s.id);
142
+ }
143
+ );
144
+ c ? i.add(c) : i.add(s);
145
+ }
146
+ return Array.from(i).map(
147
+ (s) => D(
148
+ s,
149
+ t.map((c) => c.id),
150
+ e
151
+ )
152
+ ).join(", ");
153
+ }
154
+ return M(t, e);
32
155
  };
33
156
  export {
34
- d as MULTISELECT_OPEN_EVENT,
35
- S as dispatchMultiselectOpen,
36
- E as getDropdownArrowIcon,
37
- f as getInitialInputValue,
38
- g as joinSelectedItems,
39
- m as mapSizeToInputSize,
40
- M as prepareGroupedItems,
41
- I as shouldShowMenuOnTop
157
+ T as MULTISELECT_OPEN_EVENT,
158
+ j as dispatchMultiselectOpen,
159
+ x as flattenOptions,
160
+ D as formatOptionWithCount,
161
+ p as getAllChildrenIds,
162
+ G as getDropdownArrowIcon,
163
+ U as getInitialInputValue,
164
+ g as getSelectedChildrenCount,
165
+ H as getTotalChildrenCount,
166
+ m as highlightReactNode,
167
+ O as highlightText,
168
+ B as isOptionSelected,
169
+ M as joinSelectedItems,
170
+ v as mapSizeToInputSize,
171
+ z as prepareGroupedItems,
172
+ P as processNestedSelectionChange,
173
+ R as shouldShowMenuOnTop,
174
+ b as updateInputValueAfterChange
42
175
  };
@@ -1,3 +1,4 @@
1
+ import { ReactNode } from 'react';
1
2
  import { ELoadingIndicatorPlacement, ESelectSearchBorderRadius, ESelectSearchSize } from './constants';
2
3
  import { TSelectOption } from './types';
3
4
 
@@ -20,8 +21,8 @@ export declare const SelectSearch: import('react').ForwardRefExoticComponent<Pic
20
21
  disableInput?: boolean;
21
22
  autocomplete?: "on" | "off";
22
23
  immediate?: boolean;
23
- noOptionsText?: string | import('react').ReactNode;
24
- noMatchText?: string | import('react').ReactNode;
24
+ noOptionsText?: string | ReactNode;
25
+ noMatchText?: string | ReactNode;
25
26
  loadOnOpen?: () => void;
26
27
  loading?: boolean;
27
28
  loadingIndicatorPlacement?: ELoadingIndicatorPlacement | `${ELoadingIndicatorPlacement}`;
@@ -30,4 +31,5 @@ export declare const SelectSearch: import('react').ForwardRefExoticComponent<Pic
30
31
  optionLayout?: import('./types').TOptionLayout;
31
32
  autoPlacement?: boolean;
32
33
  borderRadius?: ESelectSearchBorderRadius | `${ESelectSearchBorderRadius}`;
34
+ highlightMatches?: boolean;
33
35
  } & import('react').RefAttributes<HTMLElement>>;