@snack-uikit/list 0.1.3-preview-3f90b7f1.0 → 0.2.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 (38) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +12 -12
  3. package/dist/components/Items/BaseItem/BaseItem.d.ts +3 -3
  4. package/dist/components/Items/BaseItem/BaseItem.js +9 -6
  5. package/dist/components/Items/BaseItem/styles.module.css +3 -0
  6. package/dist/components/Items/NextListItem/NextListItem.js +1 -1
  7. package/dist/components/Items/styles.module.css +10 -10
  8. package/dist/components/Items/types.d.ts +32 -7
  9. package/dist/components/Lists/Droplist/DropList.d.ts +1 -1
  10. package/dist/components/Lists/Droplist/DropList.js +11 -5
  11. package/dist/components/Lists/List/List.d.ts +10 -2
  12. package/dist/components/Lists/List/List.js +25 -11
  13. package/dist/components/Lists/ListPrivate/ListPrivate.d.ts +10 -2
  14. package/dist/components/Lists/ListPrivate/ListPrivate.js +8 -8
  15. package/dist/components/Lists/ListPrivate/styles.module.css +4 -0
  16. package/dist/components/Lists/contexts/SelectionProvider.d.ts +19 -12
  17. package/dist/components/Lists/contexts/SelectionProvider.js +37 -16
  18. package/dist/components/Lists/styles.module.css +4 -0
  19. package/dist/components/Lists/types.d.ts +22 -6
  20. package/dist/helperComponents/HiddenTabButton/HiddenTabButton.d.ts +1 -0
  21. package/dist/helperComponents/HiddenTabButton/HiddenTabButton.js +2 -2
  22. package/dist/utils.js +6 -2
  23. package/package.json +8 -7
  24. package/src/components/Items/BaseItem/BaseItem.tsx +20 -12
  25. package/src/components/Items/BaseItem/styles.module.scss +4 -0
  26. package/src/components/Items/NextListItem/NextListItem.tsx +1 -0
  27. package/src/components/Items/styles.module.scss +17 -15
  28. package/src/components/Items/types.ts +32 -10
  29. package/src/components/Lists/Droplist/DropList.tsx +14 -5
  30. package/src/components/Lists/List/List.tsx +50 -18
  31. package/src/components/Lists/ListPrivate/ListPrivate.tsx +11 -10
  32. package/src/components/Lists/ListPrivate/styles.module.scss +4 -0
  33. package/src/components/Lists/contexts/ListProvider.tsx +0 -2
  34. package/src/components/Lists/contexts/SelectionProvider.tsx +43 -29
  35. package/src/components/Lists/styles.module.scss +5 -0
  36. package/src/components/Lists/types.ts +22 -9
  37. package/src/helperComponents/HiddenTabButton/HiddenTabButton.tsx +12 -2
  38. package/src/utils.ts +11 -2
package/CHANGELOG.md CHANGED
@@ -3,6 +3,32 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 0.2.0 (2024-02-02)
7
+
8
+
9
+ ### Features
10
+
11
+ * **FF-4075:** add ref, onKeyDown props into list; add className, inactive props into items ([3ee860c](https://github.com/cloud-ru-tech/snack-uikit/commit/3ee860c5257be999ecf3a77d261d74240e7713f2))
12
+
13
+
14
+ ### BREAKING CHANGES
15
+
16
+
17
+ * **FF-4075:** hide all selection props into 1 prop; add collapse contolled state; list fill container ([e1f8e66](https://github.com/cloud-ru-tech/snack-uikit/commit/e1f8e663a43a1261916513237f3b96b6c1146a67))
18
+
19
+
20
+
21
+
22
+ ## 0.1.3 (2024-02-01)
23
+
24
+ ### Only dependencies have been changed
25
+ * [@snack-uikit/dropdown@0.2.0](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/dropdown/CHANGELOG.md)
26
+ * [@snack-uikit/truncate-string@0.4.6](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/truncate-string/CHANGELOG.md)
27
+
28
+
29
+
30
+
31
+
6
32
  ## 0.1.2 (2024-01-31)
7
33
 
8
34
 
package/README.md CHANGED
@@ -28,18 +28,17 @@
28
28
  | widthStrategy | enum PopoverWidthStrategy: `"auto"`, `"gte"`, `"eq"` | auto | Стратегия управления шириной контейнера поповера <br> - `auto` - соответствует ширине контента, <br> - `gte` - Great Than or Equal, равен ширине таргета или больше ее, если контент в поповере шире, <br> - `eq` - Equal, строго равен ширине таргета. |
29
29
  | trigger | enum Trigger: `"click"`, `"hover"`, `"focusVisible"`, `"focus"`, `"hoverAndFocusVisible"`, `"hoverAndFocus"`, `"clickAndFocusVisible"` | - | Условие отображения поповера: <br> - `click` - открывать по клику <br> - `hover` - открывать по ховеру <br> - `focusVisible` - открывать по focus-visible <br> - `focus` - открывать по фокусу <br> - `hoverAndFocusVisible` - открывать по ховеру и focus-visible <br> - `hoverAndFocus` - открывать по ховеру и фокусу <br> - `clickAndFocusVisible` - открывать по клику и focus-visible |
30
30
  | placement | enum Placement: `"left"`, `"left-start"`, `"left-end"`, `"right"`, `"right-start"`, `"right-end"`, `"top"`, `"top-start"`, `"top-end"`, `"bottom"`, `"bottom-start"`, `"bottom-end"` | top | Положение поповера относительно своего триггера (children). |
31
+ | className | `string` | - | CSS-класс |
31
32
  | pinTop | `ItemProps[]` | - | Элементы списка, закрепленные сверху |
32
33
  | pinBottom | `ItemProps[]` | - | Элементы списка, закрепленные снизу |
33
- | footer | `ReactNode` | - | Кастомизируемый элемент, помещаемый внизу списка |
34
- | footerActiveElementsRefs | `RefObject<HTMLElement>[]` | - | Список ссылок на костомные элементы, помещенные в специальную секцию внизу списка |
34
+ | footer | `ReactElement` | - | Кастомизируемый элемент в конце списка |
35
+ | footerActiveElementsRefs | `RefObject<HTMLElement>[]` | - | Список ссылок на кастомные элементы, помещенные в специальную секцию внизу списка |
35
36
  | search | `SearchState` | - | Настройки поисковой строки |
36
37
  | loading | `boolean` | - | Флаг, отвещающий за состояние загрузки списка |
37
38
  | noData | `string` | - | Текст для состояния "Отсутсвие данных" |
38
39
  | noResults | `string` | - | Текст для состояния "Отсутсвие результата" при поиске |
39
- | value | `SelectionSingleValueType \| SelectionSingleValueType[]` | - | Controlled состояние |
40
- | defaultValue | `SelectionSingleValueType \| SelectionSingleValueType[]` | - | Начальное состояние |
41
- | onChange | `((value: any) => void) \| ((value: any) => void)` | - | Controlled обработчик измения состояния |
42
- | selection | "single" \| "multiple" | - | Режим выбора |
40
+ | collapse | `CollapseState` | {} | Настройки раскрытия элементов |
41
+ | selection | `SelectionSingleState \| SelectionMultipleState` | - | |
43
42
  | size | "s" \| "m" \| "l" | - | Размер списка |
44
43
  | marker | `boolean` | - | Отображать ли маркер у выбранного жлемента списка |
45
44
  | scroll | `boolean` | - | Включить ли скролл для основной части списка |
@@ -52,16 +51,17 @@
52
51
  | items* | `ItemProps[]` | - | Основные элементы списка |
53
52
  | pinTop | `ItemProps[]` | - | Элементы списка, закрепленные сверху |
54
53
  | pinBottom | `ItemProps[]` | - | Элементы списка, закрепленные снизу |
55
- | footer | `ReactNode` | - | Кастомизируемый элемент, помещаемый внизу списка |
56
- | footerActiveElementsRefs | `RefObject<HTMLElement>[]` | - | Список ссылок на костомные элементы, помещенные в специальную секцию внизу списка |
54
+ | footer | `ReactElement` | - | Кастомизируемый элемент в конце списка |
55
+ | footerActiveElementsRefs | `RefObject<HTMLElement>[]` | - | Список ссылок на кастомные элементы, помещенные в специальную секцию внизу списка |
57
56
  | search | `SearchState` | - | Настройки поисковой строки |
58
57
  | loading | `boolean` | - | Флаг, отвещающий за состояние загрузки списка |
59
58
  | noData | `string` | - | Текст для состояния "Отсутсвие данных" |
60
59
  | noResults | `string` | - | Текст для состояния "Отсутсвие результата" при поиске |
61
- | value | `SelectionSingleValueType \| SelectionSingleValueType[]` | - | Controlled состояние |
62
- | defaultValue | `SelectionSingleValueType \| SelectionSingleValueType[]` | - | Начальное состояние |
63
- | onChange | `((value: any) => void) \| ((value: any) => void)` | - | Controlled обработчик измения состояния |
64
- | selection | "single" \| "multiple" | - | Режим выбора |
60
+ | tabIndex | `number` | - | Tab Index |
61
+ | collapse | `CollapseState` | {} | Настройки раскрытия элементов |
62
+ | className | `string` | - | CSS-класс |
63
+ | onKeyDown | `(e: KeyboardEvent<HTMLElement>) => void` | - | |
64
+ | selection | `SelectionSingleState \| SelectionMultipleState` | - | |
65
65
  | size | "s" \| "m" \| "l" | - | Размер списка |
66
66
  | marker | `boolean` | - | Отображать ли маркер у выбранного жлемента списка |
67
67
  | scroll | `boolean` | - | Включить ли скролл для основной части списка |
@@ -1,8 +1,8 @@
1
- import { BaseItemPrivateProps, BaseItemProps, SwitchProps } from '../types';
2
- type AllBaseItemProps = BaseItemProps & BaseItemPrivateProps & SwitchProps & {
1
+ import { BaseItemPrivateProps, BaseItemProps } from '../types';
2
+ type AllBaseItemProps = BaseItemProps & BaseItemPrivateProps & {
3
3
  indeterminate?: boolean;
4
4
  onSelect?(): void;
5
5
  isParentNode?: boolean;
6
6
  };
7
- export declare function BaseItem({ beforeContent, afterContent, content, onClick, id, expandIcon, disabled, open, itemRef, switch: switchProp, onKeyDown, onFocus, indeterminate, onSelect, isParentNode, ...rest }: AllBaseItemProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare function BaseItem({ beforeContent, afterContent, content, onClick, id, expandIcon, disabled, open, itemRef, switch: switchProp, onKeyDown, onFocus, indeterminate, onSelect, isParentNode, className, inactive, ...rest }: AllBaseItemProps): import("react/jsx-runtime").JSX.Element;
8
8
  export {};
@@ -19,20 +19,23 @@ import commonStyles from '../styles.module.css';
19
19
  import { CHECKBOX_SIZE_MAP } from './constants';
20
20
  import styles from './styles.module.css';
21
21
  export function BaseItem(_a) {
22
- var { beforeContent, afterContent, content, onClick, id, expandIcon, disabled, open, itemRef, switch: switchProp, onKeyDown, onFocus, indeterminate, onSelect, isParentNode } = _a, rest = __rest(_a, ["beforeContent", "afterContent", "content", "onClick", "id", "expandIcon", "disabled", "open", "itemRef", "switch", "onKeyDown", "onFocus", "indeterminate", "onSelect", "isParentNode"]);
22
+ var { beforeContent, afterContent, content, onClick, id, expandIcon, disabled, open, itemRef, switch: switchProp, onKeyDown, onFocus, indeterminate, onSelect, isParentNode, className, inactive } = _a, rest = __rest(_a, ["beforeContent", "afterContent", "content", "onClick", "id", "expandIcon", "disabled", "open", "itemRef", "switch", "onKeyDown", "onFocus", "indeterminate", "onSelect", "isParentNode", "className", "inactive"]);
23
23
  const { option, caption, description } = content || {};
24
+ const interactive = !inactive;
24
25
  const { parentResetActiveFocusIndex } = useParentListContext();
25
26
  const { size, marker } = useListContext();
26
27
  const { level = 0 } = useCollapseContext();
27
- const { value, onChange, selection, isSelectionSingle, isSelectionMultiple } = useSelectionContext();
28
+ const { value, onChange, mode, isSelectionSingle, isSelectionMultiple } = useSelectionContext();
28
29
  const isChecked = isSelectionSingle ? value === id : value === null || value === void 0 ? void 0 : value.includes(id);
29
30
  const handleChange = () => {
30
31
  onChange === null || onChange === void 0 ? void 0 : onChange(id !== null && id !== void 0 ? id : '');
31
32
  };
32
33
  const handleItemClick = (e) => {
33
- parentResetActiveFocusIndex === null || parentResetActiveFocusIndex === void 0 ? void 0 : parentResetActiveFocusIndex();
34
- if (!isParentNode) {
35
- handleChange();
34
+ if (interactive) {
35
+ parentResetActiveFocusIndex === null || parentResetActiveFocusIndex === void 0 ? void 0 : parentResetActiveFocusIndex();
36
+ if (!isParentNode) {
37
+ handleChange();
38
+ }
36
39
  }
37
40
  onClick === null || onClick === void 0 ? void 0 : onClick(e);
38
41
  };
@@ -65,5 +68,5 @@ export function BaseItem(_a) {
65
68
  e.stopPropagation();
66
69
  };
67
70
  const props = extractSupportProps(rest);
68
- return (_jsx("li", { role: 'menuitem', "data-test-id": props['data-test-id'] || 'list__base-item_' + id, children: _jsxs("button", { ref: itemRef, className: cn(commonStyles.listItem, styles.droplistItem), "data-size": size, onClick: handleItemClick, tabIndex: -1, "data-checked": (isParentNode && (indeterminate || isChecked)) || (isChecked && !switchProp) || undefined, "data-variant": selection || undefined, "data-open": open || undefined, disabled: disabled, onKeyDown: handleItemKeyDown, onFocus: handleItemFocus, style: { '--level': level }, children: [!switchProp && isSelectionSingle && marker && !isParentNode && (_jsx("div", { className: styles.markerContainer, "data-test-id": 'list__base-item-marker' })), !switchProp && isSelectionMultiple && (_jsx("div", { className: styles.checkbox, children: _jsx(Checkbox, { size: CHECKBOX_SIZE_MAP[size !== null && size !== void 0 ? size : 's'], disabled: disabled, tabIndex: -1, onChange: handleCheckboxChange, checked: isChecked, "data-test-id": 'list__base-item-checkbox', onClick: handleCheckboxClick, indeterminate: indeterminate }) })), beforeContent && _jsx("div", { className: styles.beforeContent, children: beforeContent }), _jsxs("div", { className: styles.content, children: [_jsxs("div", { className: styles.headline, children: [_jsx("span", { className: styles.option, children: _jsx(TruncateString, { text: option, maxLines: 1 }) }), caption && _jsx("span", { className: styles.caption, children: caption })] }), description && (_jsx("div", { className: styles.description, children: _jsx(TruncateString, { text: description, maxLines: 2 }) }))] }), afterContent, switchProp && _jsx(Switch, { disabled: disabled, checked: isChecked, "data-test-id": 'list__base-item-switch' }), !switchProp && expandIcon && _jsx("span", { className: styles.expandableIcon, children: expandIcon })] }) }));
71
+ return (_jsx("li", { role: 'menuitem', "data-test-id": props['data-test-id'] || 'list__base-item_' + id, children: _jsxs("button", { ref: itemRef, className: cn(commonStyles.listItem, styles.droplistItem, className), "data-size": size, onClick: handleItemClick, tabIndex: -1, "data-non-pointer": inactive && !onClick, "data-inactive": inactive || undefined, "data-checked": (isParentNode && (indeterminate || isChecked)) || (isChecked && !switchProp) || undefined, "data-variant": mode || undefined, "data-open": open || undefined, disabled: disabled, onKeyDown: handleItemKeyDown, onFocus: handleItemFocus, style: { '--level': level }, children: [!switchProp && isSelectionSingle && marker && !isParentNode && interactive && (_jsx("div", { className: styles.markerContainer, "data-test-id": 'list__base-item-marker' })), !switchProp && isSelectionMultiple && interactive && (_jsx("div", { className: styles.checkbox, children: _jsx(Checkbox, { size: CHECKBOX_SIZE_MAP[size !== null && size !== void 0 ? size : 's'], disabled: disabled, tabIndex: -1, onChange: handleCheckboxChange, checked: isChecked, "data-test-id": 'list__base-item-checkbox', onClick: handleCheckboxClick, indeterminate: indeterminate }) })), beforeContent && _jsx("div", { className: styles.beforeContent, children: beforeContent }), _jsxs("div", { className: styles.content, children: [_jsxs("div", { className: styles.headline, children: [_jsx("span", { className: styles.option, children: _jsx(TruncateString, { text: option, maxLines: 1 }) }), caption && _jsx("span", { className: styles.caption, children: caption })] }), description && (_jsx("div", { className: styles.description, children: _jsx(TruncateString, { text: description, maxLines: 2 }) }))] }), afterContent, switchProp && interactive && (_jsx(Switch, { disabled: disabled, checked: isChecked, "data-test-id": 'list__base-item-switch' })), !switchProp && expandIcon && _jsx("span", { className: styles.expandableIcon, children: expandIcon })] }) }));
69
72
  }
@@ -170,6 +170,9 @@
170
170
  letter-spacing:var(--sans-body-m-letter-spacing, 0.1px);
171
171
  paragraph-spacing:var(--sans-body-m-paragraph-spacing, 7.7px);
172
172
  }
173
+ .droplistItem[data-non-pointer]{
174
+ cursor:inherit;
175
+ }
173
176
  .droplistItem:disabled .option,
174
177
  .droplistItem:disabled .description,
175
178
  .droplistItem:disabled .caption,
@@ -62,5 +62,5 @@ export function NextListItem(_a) {
62
62
  parentResetNestedIndex: resetNestedIndex,
63
63
  parentResetActiveFocusIndex: resetActiveFocusIndex,
64
64
  toggleOpenCollapsedItems: id => setOpenCollapsedItems(items => items.includes(id) ? items.filter(item => item !== id) : items.concat([id])),
65
- }, children: _jsx(ListPrivate, { onKeyDown: handleListKeyDown, items: items, nested: true, search: search, scroll: scroll, scrollRef: scrollRef }) }), trigger: 'hover', open: (open || parentIds[parentOpenNestedIndex] === id) && !disabled, onOpenChange: handleOpenChange, placement: placement, children: _jsx(BaseItem, Object.assign({}, option, { disabled: disabled, open: open, expandIcon: _jsx(ChevronRightSVG, {}), id: id, isParentNode: true, indeterminate: isIndeterminate && !checked, onSelect: handleOnSelect })) }) }));
65
+ }, children: _jsx(ListPrivate, { onKeyDown: handleListKeyDown, items: items, nested: true, search: search, scroll: scroll, scrollRef: scrollRef, limitedScrollHeight: true }) }), trigger: 'hover', open: (open || parentIds[parentOpenNestedIndex] === id) && !disabled, onOpenChange: handleOpenChange, placement: placement, children: _jsx(BaseItem, Object.assign({}, option, { disabled: disabled, open: open, expandIcon: _jsx(ChevronRightSVG, {}), id: id, isParentNode: true, indeterminate: isIndeterminate && !checked, onSelect: handleOnSelect })) }) }));
66
66
  }
@@ -50,16 +50,6 @@
50
50
  height:100%;
51
51
  background-color:transparent;
52
52
  }
53
- .listItem[data-open]::after, .listItem[data-focused]::after, .listItem:hover::after, .listItem:focus-visible::after{
54
- opacity:var(--opacity-a008, 0.08);
55
- background-color:var(--sys-neutral-accent-default, #757575);
56
- }
57
- .listItem[data-focused], .listItem:focus-visible{
58
- outline-width:var(--border-state-focus-s-border-width, 2px);
59
- outline-style:var(--border-state-focus-s-border-style, solid);
60
- outline-color:var(--border-state-focus-s-border-color, );
61
- outline-offset:calc(var(--border-state-focus-s-border-width, 2px) * -1);
62
- }
63
53
  .listItem:disabled{
64
54
  cursor:not-allowed;
65
55
  background-color:transparent;
@@ -71,4 +61,14 @@
71
61
  .listItem:disabled::after{
72
62
  cursor:not-allowed;
73
63
  background-color:transparent;
64
+ }
65
+ .listItem:not([data-inactive])[data-open]::after, .listItem:not([data-inactive])[data-focused]::after, .listItem:not([data-inactive]):hover::after, .listItem:not([data-inactive]):focus-visible::after{
66
+ opacity:var(--opacity-a008, 0.08);
67
+ background-color:var(--sys-neutral-accent-default, #757575);
68
+ }
69
+ .listItem:not([data-inactive])[data-focused], .listItem:not([data-inactive]):focus-visible{
70
+ outline-width:var(--border-state-focus-s-border-width, 2px);
71
+ outline-style:var(--border-state-focus-s-border-style, solid);
72
+ outline-color:var(--border-state-focus-s-border-color, );
73
+ outline-offset:calc(var(--border-state-focus-s-border-width, 2px) * -1);
74
74
  }
@@ -12,32 +12,57 @@ export type BaseItemPrivateProps = {
12
12
  open?: boolean;
13
13
  };
14
14
  export type BaseItemProps = WithSupportProps<{
15
+ /**
16
+ * Слот до основного контента
17
+ * @type ReactElement
18
+ */
15
19
  beforeContent?: ReactNode;
20
+ /**
21
+ * Слот после основного контента
22
+ * @type ReactElement
23
+ */
16
24
  afterContent?: ReactNode;
25
+ /** Основной контент айтема */
17
26
  content: ItemContentProps;
27
+ /** Колбек обработки клика */
18
28
  onClick?(e: MouseEvent<HTMLButtonElement>): void;
29
+ /** Колбек обработки нажатия клавиши */
19
30
  onKeyDown?(e: KeyboardEvent<HTMLButtonElement>): void;
31
+ /** Колбек обработки фокуса */
20
32
  onFocus?(e: FocusEvent<HTMLButtonElement>): void;
33
+ /** Колбек обработки блюра */
21
34
  onBlur?(e: FocusEvent<HTMLButtonElement>): void;
35
+ /** Уникальный идентификатор */
22
36
  id?: string | number;
37
+ /** Флаг неактивности элемента */
23
38
  disabled?: boolean;
24
39
  itemRef?: RefObject<HTMLButtonElement>;
25
- }>;
26
- export type SwitchProps = {
40
+ className?: string;
41
+ /**
42
+ * Флаг отображения отключения реакции на любое css состояние (hover/focus и тд)
43
+ * <br>
44
+ * Так же элемент пропадает из навигации с клавиатуры, и не может быть выбран (selection)
45
+ */
46
+ inactive?: boolean;
47
+ /**
48
+ * Флаг отображения состояния выбранного элемента через switch
49
+ */
27
50
  switch?: boolean;
28
- };
29
- export type ItemProps = (BaseItemProps & SwitchProps) | AccordionItemProps | NextListItemProps | GroupItemProps;
30
- export type AccordionItemProps = BaseItemProps & {
51
+ }>;
52
+ type BaseItemsWithoutNonGroupProps = Omit<BaseItemProps, 'switch' | 'inactive'>;
53
+ export type ItemProps = BaseItemProps | AccordionItemProps | NextListItemProps | GroupItemProps;
54
+ export type AccordionItemProps = BaseItemsWithoutNonGroupProps & {
31
55
  items: ItemProps[];
32
56
  type: 'collapse';
33
57
  };
34
- export type NextListItemProps = BaseItemProps & {
58
+ export type NextListItemProps = BaseItemsWithoutNonGroupProps & {
35
59
  items: ItemProps[];
36
60
  type: 'next-list';
37
61
  placement?: 'right-start' | 'left-start' | 'left' | 'right' | 'left-end' | 'right-end';
38
62
  search?: SearchState;
39
63
  } & ScrollProps;
40
- export type GroupItemProps = SeparatorProps & {
64
+ export type GroupItemProps = Omit<SeparatorProps, 'size'> & {
41
65
  items: ItemProps[];
42
66
  id?: string | number;
43
67
  };
68
+ export {};
@@ -1,2 +1,2 @@
1
1
  import { DroplistProps } from '../types';
2
- export declare function Droplist({ items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs, children, trigger, placement, widthStrategy, triggerElemRef: triggerElemRefProp, open: openProp, onOpenChange, ...props }: DroplistProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function Droplist({ items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs, children, trigger, placement, widthStrategy, triggerElemRef: triggerElemRefProp, open: openProp, onOpenChange, collapse, ...props }: DroplistProps): import("react/jsx-runtime").JSX.Element;
@@ -10,7 +10,7 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import { jsx as _jsx } from "react/jsx-runtime";
13
- import { cloneElement, isValidElement, useCallback, useMemo, useRef, useState } from 'react';
13
+ import { cloneElement, isValidElement, useCallback, useMemo, useRef } from 'react';
14
14
  import { useUncontrolledProp } from 'uncontrollable';
15
15
  import { Dropdown } from '@snack-uikit/dropdown';
16
16
  import { extractItemIds, extractItemRefs, withCollapsedItems } from '../../../utils';
@@ -19,10 +19,16 @@ import { extractSelectionProps, ParentListContext, SelectionProvider } from '../
19
19
  import { useKeyboardNavigation } from '../hooks';
20
20
  import { ListPrivate } from '../ListPrivate';
21
21
  export function Droplist(_a) {
22
- var { items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs, children, trigger, placement, widthStrategy, triggerElemRef: triggerElemRefProp, open: openProp, onOpenChange } = _a, props = __rest(_a, ["items", "search", "pinBottom", "pinTop", "footerActiveElementsRefs", "children", "trigger", "placement", "widthStrategy", "triggerElemRef", "open", "onOpenChange"]);
22
+ var _b;
23
+ var { items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs, children, trigger, placement, widthStrategy, triggerElemRef: triggerElemRefProp, open: openProp, onOpenChange, collapse = {} } = _a, props = __rest(_a, ["items", "search", "pinBottom", "pinTop", "footerActiveElementsRefs", "children", "trigger", "placement", "widthStrategy", "triggerElemRef", "open", "onOpenChange", "collapse"]);
23
24
  const hasSearch = useMemo(() => Boolean(search), [search]);
24
25
  const memorizedItems = useMemo(() => addItemsIds((pinBottom !== null && pinBottom !== void 0 ? pinBottom : []).concat(itemsProp).concat(pinTop !== null && pinTop !== void 0 ? pinTop : [])), [itemsProp, pinBottom, pinTop]);
25
- const [openCollapsedItems, setOpenCollapsedItems] = useState([]);
26
+ const [openCollapsedItems, setOpenCollapsedItems] = useUncontrolledProp(collapse.value, (_b = collapse.defaultValue) !== null && _b !== void 0 ? _b : [], collapse.onChange
27
+ ? cb => {
28
+ var _a;
29
+ (_a = collapse.onChange) === null || _a === void 0 ? void 0 : _a.call(collapse, cb(collapse.value));
30
+ }
31
+ : undefined);
26
32
  const { search: searchItem, footerRefs } = useItemsWithIds({
27
33
  search: hasSearch,
28
34
  footerActiveElementsRefs,
@@ -88,8 +94,8 @@ export function Droplist(_a) {
88
94
  parentRef: listRef,
89
95
  parentResetNestedIndex: resetNestedIndex,
90
96
  parentResetActiveFocusIndex: resetActiveFocusIndex,
91
- toggleOpenCollapsedItems: id => setOpenCollapsedItems(items => items.includes(id) ? items.filter(item => item !== id) : items.concat([id])),
92
- }, children: _jsx(Dropdown, Object.assign({ content: _jsx(ListPrivate, Object.assign({ onKeyDown: handleListKeyDown, tabIndex: 0, ref: listRef, search: search }, slicedItems, props)), trigger: trigger, placement: placement, widthStrategy: widthStrategy }, (triggerElemRefProp
97
+ toggleOpenCollapsedItems: id => setOpenCollapsedItems((items = []) => items.includes(id) ? items.filter(item => item !== id) : items === null || items === void 0 ? void 0 : items.concat([id])),
98
+ }, children: _jsx(Dropdown, Object.assign({ content: _jsx(ListPrivate, Object.assign({ onKeyDown: handleListKeyDown, tabIndex: 0, ref: listRef, search: search, limitedScrollHeight: true }, slicedItems, props)), trigger: trigger, placement: placement, widthStrategy: widthStrategy }, (triggerElemRefProp
93
99
  ? {}
94
100
  : {
95
101
  triggerRef: triggerElemRef,
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import { KeyboardEvent } from 'react';
2
2
  export declare const List: import("react").ForwardRefExoticComponent<{
3
3
  'data-test-id'?: string | undefined;
4
4
  } & import("react").AriaAttributes & {
@@ -11,4 +11,12 @@ export declare const List: import("react").ForwardRefExoticComponent<{
11
11
  loading?: boolean | undefined;
12
12
  noData?: string | undefined;
13
13
  noResults?: string | undefined;
14
- } & Pick<import("../contexts").SelectionProviderProps, "value" | "defaultValue" | "selection" | "onChange"> & import("../contexts").ListContextType & import("../../../types").ScrollProps & import("react").RefAttributes<HTMLElement>>;
14
+ tabIndex?: number | undefined;
15
+ collapse?: {
16
+ value?: (string | number)[] | undefined;
17
+ onChange?(value: (string | number)[]): void;
18
+ defaultValue?: (string | number)[] | undefined;
19
+ } | undefined;
20
+ className?: string | undefined;
21
+ onKeyDown?(e: KeyboardEvent<HTMLElement>): void;
22
+ } & import("../contexts").SelectionState & import("../contexts").ListContextType & import("../../../types").ScrollProps & import("react").RefAttributes<HTMLElement>>;
@@ -10,7 +10,10 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
- import { forwardRef, useMemo, useRef, useState } from 'react';
13
+ import cn from 'classnames';
14
+ import mergeRefs from 'merge-refs';
15
+ import { forwardRef, useMemo, useRef } from 'react';
16
+ import { useUncontrolledProp } from 'uncontrollable';
14
17
  import { HiddenTabButton } from '../../../helperComponents';
15
18
  import { extractItemIds, extractItemRefs, withCollapsedItems } from '../../../utils';
16
19
  import { addItemsIds, getSlicedItems, useItemsWithIds } from '../../Items';
@@ -18,11 +21,17 @@ import { extractSelectionProps, ParentListContext, SelectionProvider, useParentL
18
21
  import { useKeyboardNavigation } from '../hooks';
19
22
  import { ListPrivate } from '../ListPrivate';
20
23
  import styles from '../styles.module.css';
21
- export const List = forwardRef((_a) => {
22
- var { items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs } = _a, props = __rest(_a, ["items", "search", "pinBottom", "pinTop", "footerActiveElementsRefs"]);
24
+ export const List = forwardRef((_a, ref) => {
25
+ var _b;
26
+ var { items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs, onKeyDown, tabIndex = 0, className, collapse = {} } = _a, props = __rest(_a, ["items", "search", "pinBottom", "pinTop", "footerActiveElementsRefs", "onKeyDown", "tabIndex", "className", "collapse"]);
23
27
  const hasSearch = useMemo(() => Boolean(search), [search]);
24
28
  const memorizedItems = useMemo(() => addItemsIds((pinTop !== null && pinTop !== void 0 ? pinTop : []).concat(itemsProp).concat(pinBottom !== null && pinBottom !== void 0 ? pinBottom : [])), [itemsProp, pinBottom, pinTop]);
25
- const [openCollapsedItems, setOpenCollapsedItems] = useState([]);
29
+ const [openCollapsedItems, setOpenCollapsedItems] = useUncontrolledProp(collapse.value, (_b = collapse.defaultValue) !== null && _b !== void 0 ? _b : [], collapse.onChange
30
+ ? cb => {
31
+ var _a;
32
+ (_a = collapse.onChange) === null || _a === void 0 ? void 0 : _a.call(collapse, cb(collapse.value));
33
+ }
34
+ : undefined);
26
35
  const { search: searchItem, footerRefs } = useItemsWithIds({
27
36
  search: hasSearch,
28
37
  footerActiveElementsRefs,
@@ -52,6 +61,16 @@ export const List = forwardRef((_a) => {
52
61
  itemRefs,
53
62
  });
54
63
  const isActive = listRef.current === document.activeElement && activeFocusIndex === -1 && openNestedIndex === -1;
64
+ const mergedHandlerKeyDown = (e) => {
65
+ onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(e);
66
+ handleListKeyDown === null || handleListKeyDown === void 0 ? void 0 : handleListKeyDown(e);
67
+ };
68
+ const handleOnFocus = (e) => {
69
+ if (e.relatedTarget === null ||
70
+ (e.relatedTarget && itemRefs.every(({ current }) => current !== e.relatedTarget))) {
71
+ resetActiveFocusIndex();
72
+ }
73
+ };
55
74
  return (
56
75
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
57
76
  // @ts-ignore
@@ -67,11 +86,6 @@ export const List = forwardRef((_a) => {
67
86
  parentResetNestedIndex: resetNestedIndex,
68
87
  parentResetActiveFocusIndex: resetActiveFocusIndex,
69
88
  openCollapsedItems,
70
- toggleOpenCollapsedItems: id => setOpenCollapsedItems(items => items.includes(id) ? items.filter(item => item !== id) : items.concat([id])),
71
- }, children: _jsxs("div", { className: styles.wrapper, "data-active": isActive || undefined, children: [_jsx(ListPrivate, Object.assign({}, props, slicedItems, { ref: listRef, onFocus: e => {
72
- if (e.relatedTarget === null ||
73
- (e.relatedTarget && itemRefs.every(({ current }) => current !== e.relatedTarget))) {
74
- resetActiveFocusIndex();
75
- }
76
- }, onKeyDown: handleListKeyDown, tabIndex: 0, search: search, nested: false })), _jsx(HiddenTabButton, { ref: btnRef, listRef: listRef })] }) }) })));
89
+ toggleOpenCollapsedItems: id => setOpenCollapsedItems((items = []) => items.includes(id) ? items.filter(item => item !== id) : items === null || items === void 0 ? void 0 : items.concat([id])),
90
+ }, children: _jsxs("div", { className: cn(styles.wrapper, className), "data-active": isActive || undefined, children: [_jsx(ListPrivate, Object.assign({}, props, slicedItems, { ref: mergeRefs(ref, listRef), onFocus: handleOnFocus, onKeyDown: mergedHandlerKeyDown, tabIndex: tabIndex, search: search, nested: false })), _jsx(HiddenTabButton, { ref: btnRef, listRef: listRef, tabIndex: tabIndex })] }) }) })));
77
91
  });
@@ -11,12 +11,20 @@ export declare const ListPrivate: import("react").ForwardRefExoticComponent<{
11
11
  loading?: boolean | undefined;
12
12
  noData?: string | undefined;
13
13
  noResults?: string | undefined;
14
- } & Pick<import("../contexts").SelectionProviderProps, "value" | "defaultValue" | "selection" | "onChange"> & import("../contexts").ListContextType & import("../../../types").ScrollProps & {
14
+ tabIndex?: number | undefined;
15
+ collapse?: {
16
+ value?: (string | number)[] | undefined;
17
+ onChange?(value: (string | number)[]): void;
18
+ defaultValue?: (string | number)[] | undefined;
19
+ } | undefined;
20
+ className?: string | undefined;
21
+ onKeyDown?(e: import("react").KeyboardEvent<HTMLElement>): void;
22
+ } & import("../contexts").SelectionState & import("../contexts").ListContextType & import("../../../types").ScrollProps & {
15
23
  nested?: boolean | undefined;
16
24
  active?: boolean | undefined;
17
25
  tabIndex?: number | undefined;
18
26
  onFocus?(e: import("react").FocusEvent<HTMLElement, Element>): void;
19
27
  onBlur?(e: import("react").FocusEvent<HTMLElement, Element>): void;
20
28
  onKeyDown?(e: import("react").KeyboardEvent<HTMLElement>): void;
21
- collapse?: "single" | "multiple" | undefined;
29
+ limitedScrollHeight?: boolean | undefined;
22
30
  } & import("react").RefAttributes<HTMLElement>>;
@@ -9,7 +9,7 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
12
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
13
13
  import cn from 'classnames';
14
14
  import { forwardRef, useMemo } from 'react';
15
15
  import { Spinner } from '@snack-uikit/loaders';
@@ -22,18 +22,18 @@ import commonStyles from '../styles.module.css';
22
22
  import styles from './styles.module.css';
23
23
  export const ListPrivate = forwardRef((_a, ref) => {
24
24
  var _b, _c;
25
- var { items, pinTop, pinBottom, collapse = 'multiple', onKeyDown, onBlur, onFocus, tabIndex, active, scroll, nested, search, scrollRef, scrollContainerRef, footer, loading, noData = 'No data', noResults = 'No results', size = 's', marker } = _a, props = __rest(_a, ["items", "pinTop", "pinBottom", "collapse", "onKeyDown", "onBlur", "onFocus", "tabIndex", "active", "scroll", "nested", "search", "scrollRef", "scrollContainerRef", "footer", "loading", "noData", "noResults", "size", "marker"]);
25
+ var { items, pinTop, pinBottom, onKeyDown, onBlur, onFocus, tabIndex, active, scroll, nested, search, scrollRef, scrollContainerRef, footer, loading, noData = 'No data', noResults = 'No results', size = 's', marker, limitedScrollHeight, className } = _a, props = __rest(_a, ["items", "pinTop", "pinBottom", "onKeyDown", "onBlur", "onFocus", "tabIndex", "active", "scroll", "nested", "search", "scrollRef", "scrollContainerRef", "footer", "loading", "noData", "noResults", "size", "marker", "limitedScrollHeight", "className"]);
26
26
  const itemsJSX = useRenderItems(items);
27
27
  const itemsPinTopJSX = useRenderItems(pinTop !== null && pinTop !== void 0 ? pinTop : []);
28
28
  const itemsPinBottomJSX = useRenderItems(pinBottom !== null && pinBottom !== void 0 ? pinBottom : []);
29
29
  const hasNoItems = items.length + ((_b = pinTop === null || pinTop === void 0 ? void 0 : pinTop.length) !== null && _b !== void 0 ? _b : 0) + ((_c = pinBottom === null || pinBottom === void 0 ? void 0 : pinBottom.length) !== null && _c !== void 0 ? _c : 0) === 0;
30
30
  const loadingJSX = useMemo(() => loading && (_jsx("div", { role: 'spinbutton', tabIndex: -1, className: styles.loader, "data-size": size, "data-no-items": hasNoItems || undefined, "data-test-id": 'list__loader', children: _jsx(Spinner, { size: size === 'l' ? 's' : 'xs' }) })), [hasNoItems, loading, size]);
31
- const content = useMemo(() => (_jsxs(_Fragment, { children: [itemsJSX, loadingJSX, hasNoItems && !(search === null || search === void 0 ? void 0 : search.value) && !loading && (_jsx("div", { className: commonStyles.infoBlock, "data-test-id": 'list__no-data', children: noData })), hasNoItems && (search === null || search === void 0 ? void 0 : search.value) && !loading && (_jsx("div", { className: commonStyles.infoBlock, "data-test-id": 'list__no-results', children: noResults }))] })), [hasNoItems, itemsJSX, loading, loadingJSX, noData, noResults, search === null || search === void 0 ? void 0 : search.value]);
32
- const listJSX = (_jsx("ul", Object.assign({ className: commonStyles.listContainer, ref: ref, onKeyDown: onKeyDown, tabIndex: tabIndex, onFocus: !nested ? onFocus : undefined, onBlur: onBlur, "data-active": active || undefined, role: 'menu' }, extractSupportProps(props), { children: _jsxs(ToggleGroup, { selectionMode: collapse, children: [(Number(pinTop === null || pinTop === void 0 ? void 0 : pinTop.length) > 0 || search) && (_jsxs(PinTopGroupItem, { children: [search && _jsx(SearchItem, { search: search }), Number(pinTop === null || pinTop === void 0 ? void 0 : pinTop.length) > 0 && itemsPinTopJSX] })), scroll ? (_jsxs(Scroll, { className: cn({
33
- [commonStyles.scrollContainerS]: scroll && size === 's',
34
- [commonStyles.scrollContainerM]: scroll && size === 'm',
35
- [commonStyles.scrollContainerL]: scroll && size === 'l',
36
- }), barHideStrategy: 'never', size: 's', ref: scrollContainerRef, children: [content, _jsx("div", { className: styles.scrollStub, ref: scrollRef })] })) : (_jsx(_Fragment, { children: content })), (Number(pinBottom === null || pinBottom === void 0 ? void 0 : pinBottom.length) > 0 || footer) && (_jsxs(PinBottomGroupItem, { children: [Number(pinBottom === null || pinBottom === void 0 ? void 0 : pinBottom.length) > 0 && itemsPinBottomJSX, footer && _jsx("div", { className: styles.footer, children: footer })] }))] }) })));
31
+ const content = useMemo(() => (_jsxs("div", { className: styles.content, children: [itemsJSX, loadingJSX, hasNoItems && !(search === null || search === void 0 ? void 0 : search.value) && !loading && (_jsx("div", { className: commonStyles.infoBlock, "data-test-id": 'list__no-data', children: noData })), hasNoItems && (search === null || search === void 0 ? void 0 : search.value) && !loading && (_jsx("div", { className: commonStyles.infoBlock, "data-test-id": 'list__no-results', children: noResults }))] })), [hasNoItems, itemsJSX, loading, loadingJSX, noData, noResults, search === null || search === void 0 ? void 0 : search.value]);
32
+ const listJSX = (_jsx("ul", Object.assign({ className: cn(commonStyles.listContainer, className), ref: ref, onKeyDown: onKeyDown, tabIndex: tabIndex, onFocus: !nested ? onFocus : undefined, onBlur: onBlur, "data-active": active || undefined, role: 'menu' }, extractSupportProps(props), { children: _jsxs(ToggleGroup, { selectionMode: 'multiple', children: [(Number(pinTop === null || pinTop === void 0 ? void 0 : pinTop.length) > 0 || search) && (_jsxs(PinTopGroupItem, { children: [search && _jsx(SearchItem, { search: search }), Number(pinTop === null || pinTop === void 0 ? void 0 : pinTop.length) > 0 && itemsPinTopJSX] })), scroll ? (_jsxs(Scroll, { className: cn({
33
+ [commonStyles.scrollContainerS]: scroll && limitedScrollHeight && size === 's',
34
+ [commonStyles.scrollContainerM]: scroll && limitedScrollHeight && size === 'm',
35
+ [commonStyles.scrollContainerL]: scroll && limitedScrollHeight && size === 'l',
36
+ }), barHideStrategy: 'leave', size: size === 's' ? 's' : 'm', ref: scrollContainerRef, children: [content, _jsx("div", { className: styles.scrollStub, ref: scrollRef })] })) : (_jsx(_Fragment, { children: content })), (Number(pinBottom === null || pinBottom === void 0 ? void 0 : pinBottom.length) > 0 || footer) && (_jsxs(PinBottomGroupItem, { children: [Number(pinBottom === null || pinBottom === void 0 ? void 0 : pinBottom.length) > 0 && itemsPinBottomJSX, footer && _jsx("div", { className: styles.footer, children: footer })] }))] }) })));
37
37
  if (!nested) {
38
38
  return (_jsx(ListContextProvider, { size: size, marker: marker, children: listJSX }));
39
39
  }
@@ -43,4 +43,8 @@
43
43
  justify-content:center;
44
44
  box-sizing:border-box;
45
45
  padding:var(--dimension-050m, 4px);
46
+ }
47
+
48
+ .content{
49
+ flex-grow:1;
46
50
  }
@@ -1,37 +1,41 @@
1
1
  import { ReactNode } from 'react';
2
2
  type SelectionSingleValueType = string | number | undefined;
3
- export type SelectionSingleProps = {
3
+ type SelectionSingleState = {
4
4
  /** Начальное состояние */
5
5
  defaultValue?: SelectionSingleValueType;
6
6
  /** Controlled состояние */
7
7
  value?: SelectionSingleValueType;
8
8
  /** Controlled обработчик измения состояния */
9
9
  onChange?(value: any): void;
10
- setValue?(value: any): void;
11
10
  /** Режим выбора */
12
- selection: 'single';
11
+ mode: 'single';
12
+ };
13
+ export type SelectionSingleProps = {
14
+ setValue?(value: any): void;
13
15
  /** Режим выбора single */
14
16
  isSelectionSingle: true;
15
17
  /** Режим выбора multi */
16
18
  isSelectionMultiple: false;
17
- };
18
- export type SelectionMultipleProps = {
19
+ } & SelectionSingleState;
20
+ type SelectionMultipleState = {
19
21
  /** Начальное состояние */
20
22
  defaultValue?: SelectionSingleValueType[];
21
23
  /** Controlled состояние */
22
24
  value?: SelectionSingleValueType[];
23
25
  /** Controlled обработчик измения состояния */
24
26
  onChange?(value: any): void;
25
- setValue?(value: any): void;
26
27
  /** Режим выбора */
27
- selection: 'multiple';
28
+ mode: 'multiple';
29
+ };
30
+ export type SelectionMultipleProps = {
31
+ setValue?(value: any): void;
28
32
  /** Режим выбора single */
29
33
  isSelectionSingle: false;
30
34
  /** Режим выбора multi */
31
35
  isSelectionMultiple: true;
32
- };
36
+ } & SelectionMultipleState;
33
37
  type SelectionNoneProps = {
34
- selection?: undefined;
38
+ mode?: undefined;
35
39
  value?: undefined;
36
40
  onChange?: undefined;
37
41
  setValue?: undefined;
@@ -39,15 +43,18 @@ type SelectionNoneProps = {
39
43
  isSelectionSingle?: undefined;
40
44
  isSelectionMultiple?: undefined;
41
45
  };
42
- export type SelectionProviderProps = SelectionSingleProps | SelectionMultipleProps | SelectionNoneProps;
46
+ type SelectionProviderProps = SelectionSingleProps | SelectionMultipleProps | SelectionNoneProps;
43
47
  type SelectionContextType = Omit<SelectionNoneProps, 'defaultValue'> | Omit<SelectionSingleProps, 'defaultValue'> | Omit<SelectionMultipleProps, 'defaultValue'>;
48
+ export type SelectionState = {
49
+ selection?: SelectionSingleState | SelectionMultipleState;
50
+ };
44
51
  export declare const SelectionContext: import("react").Context<SelectionContextType>;
45
52
  export declare function isSelectionMultipleProps(props: any): props is SelectionMultipleProps;
46
53
  export declare function isSelectionSingleProps(props: any): props is SelectionSingleProps;
47
54
  type Child = {
48
55
  children: ReactNode;
49
56
  };
50
- export declare function SelectionProvider(props: SelectionProviderProps & Child): import("react/jsx-runtime").JSX.Element;
57
+ export declare function SelectionProvider({ children, ...props }: SelectionProviderProps & Child): import("react/jsx-runtime").JSX.Element;
51
58
  export declare const useSelectionContext: () => SelectionContextType;
52
- export declare function extractSelectionProps<T extends SelectionProviderProps>({ selection, value, defaultValue, onChange, }: T): SelectionProviderProps;
59
+ export declare function extractSelectionProps<T extends SelectionState>({ selection }: T): SelectionProviderProps;
53
60
  export {};