@snack-uikit/list 0.1.1 → 0.1.3-preview-3a43a07a.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
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.1.2 (2024-01-31)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **FF-4198:** reset active focus index when clicking the item with mouse ([638e6f0](https://github.com/cloud-ru-tech/snack-uikit/commit/638e6f0069ec7f444b39d62d91d6e845e13aee9f))
12
+
13
+
14
+
15
+
16
+
6
17
  ## 0.1.1 (2024-01-30)
7
18
 
8
19
 
package/README.md CHANGED
@@ -20,52 +20,55 @@
20
20
  ### Props
21
21
  | name | type | default value | description |
22
22
  |------|------|---------------|-------------|
23
- | items* | `ItemProps[]` | - | |
24
- | triggerElemRef | `RefObject<HTMLElement>` | - | |
25
- | open | `boolean` | - | |
26
- | onOpenChange | `(open: boolean) => void` | - | |
27
- | trigger | enum Trigger: `"click"`, `"hover"`, `"focusVisible"`, `"focus"`, `"hoverAndFocusVisible"`, `"hoverAndFocus"`, `"clickAndFocusVisible"` | - | |
28
- | placement | enum Placement: `"left"`, `"left-start"`, `"left-end"`, `"right"`, `"right-start"`, `"right-end"`, `"top"`, `"top-start"`, `"top-end"`, `"bottom"`, `"bottom-start"`, `"bottom-end"` | - | |
29
- | widthStrategy | enum PopoverWidthStrategy: `"auto"`, `"gte"`, `"eq"` | - | |
30
- | pinTop | `ItemProps[]` | - | |
31
- | pinBottom | `ItemProps[]` | - | |
32
- | footer | `ReactNode` | - | |
33
- | search | `SearchState` | - | |
34
- | loading | `boolean` | - | |
35
- | noData | `string` | - | |
36
- | noResults | `string` | - | |
37
- | footerActiveElementsRefs | `RefObject<HTMLElement>[]` | - | |
23
+ | items* | `ItemProps[]` | - | Основные элементы списка |
24
+ | triggerElemRef | `RefObject<HTMLElement>` | - | Ссылка на элемент-триггер для дроплиста |
25
+ | children | `ReactNode` | - | Триггер для дроплиста |
26
+ | open | `boolean` | - | Управляет состоянием показан/не показан. |
27
+ | onOpenChange | `(isOpen: boolean) => void` | - | Колбек отображения компонента. Срабатывает при изменении состояния open. |
28
+ | widthStrategy | enum PopoverWidthStrategy: `"auto"`, `"gte"`, `"eq"` | auto | Стратегия управления шириной контейнера поповера <br> - `auto` - соответствует ширине контента, <br> - `gte` - Great Than or Equal, равен ширине таргета или больше ее, если контент в поповере шире, <br> - `eq` - Equal, строго равен ширине таргета. |
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
+ | 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). |
38
31
  | value | `SelectionSingleValueType \| SelectionSingleValueType[]` | - | Controlled состояние |
39
32
  | defaultValue | `SelectionSingleValueType \| SelectionSingleValueType[]` | - | Начальное состояние |
40
33
  | onChange | `((value: any) => void) \| ((value: any) => void)` | - | Controlled обработчик измения состояния |
41
34
  | selection | "single" \| "multiple" | - | Режим выбора |
42
- | size | "s" \| "m" \| "l" | - | |
43
- | marker | `boolean` | - | |
44
- | scroll | `boolean` | - | |
45
- | scrollRef | `RefObject<HTMLElement>` | - | |
46
- | scrollContainerRef | `RefObject<HTMLElement>` | - | |
35
+ | pinTop | `ItemProps[]` | - | Элементы списка, закрепленные сверху |
36
+ | pinBottom | `ItemProps[]` | - | Элементы списка, закрепленные снизу |
37
+ | footer | `ReactNode` | - | Кастомизируемый элемент, помещаемый внизу списка |
38
+ | footerActiveElementsRefs | `RefObject<HTMLElement>[]` | - | Список ссылок на костомные элементы, помещенные в специальную секцию внизу списка |
39
+ | search | `SearchState` | - | Настройки поисковой строки |
40
+ | loading | `boolean` | - | Флаг, отвещающий за состояние загрузки списка |
41
+ | noData | `string` | - | Текст для состояния "Отсутсвие данных" |
42
+ | noResults | `string` | - | Текст для состояния "Отсутсвие результата" при поиске |
43
+ | size | "s" \| "m" \| "l" | - | Размер списка |
44
+ | marker | `boolean` | - | Отображать ли маркер у выбранного жлемента списка |
45
+ | scroll | `boolean` | - | Включить ли скролл для основной части списка |
46
+ | scrollRef | `RefObject<HTMLElement>` | - | Ссылка на элемент, обозначающий самый конец прокручиваемого списка |
47
+ | scrollContainerRef | `RefObject<HTMLElement>` | - | Ссылка на контейнер, который скроллится |
47
48
  ## List
48
49
  ### Props
49
50
  | name | type | default value | description |
50
51
  |------|------|---------------|-------------|
51
- | items* | `ItemProps[]` | - | |
52
- | pinTop | `ItemProps[]` | - | |
53
- | pinBottom | `ItemProps[]` | - | |
54
- | footer | `ReactNode` | - | |
55
- | search | `SearchState` | - | |
56
- | loading | `boolean` | - | |
57
- | noData | `string` | - | |
58
- | noResults | `string` | - | |
59
- | footerActiveElementsRefs | `RefObject<HTMLElement>[]` | - | |
52
+ | items* | `ItemProps[]` | - | Основные элементы списка |
53
+ | pinTop | `ItemProps[]` | - | Элементы списка, закрепленные сверху |
54
+ | pinBottom | `ItemProps[]` | - | Элементы списка, закрепленные снизу |
55
+ | footer | `ReactNode` | - | Кастомизируемый элемент, помещаемый внизу списка |
56
+ | footerActiveElementsRefs | `RefObject<HTMLElement>[]` | - | Список ссылок на костомные элементы, помещенные в специальную секцию внизу списка |
57
+ | search | `SearchState` | - | Настройки поисковой строки |
58
+ | loading | `boolean` | - | Флаг, отвещающий за состояние загрузки списка |
59
+ | noData | `string` | - | Текст для состояния "Отсутсвие данных" |
60
+ | noResults | `string` | - | Текст для состояния "Отсутсвие результата" при поиске |
61
+ | tabIndex | `number` | - | |
62
+ | onKeyDown | `(e: KeyboardEvent<HTMLElement>) => void` | - | |
60
63
  | value | `SelectionSingleValueType \| SelectionSingleValueType[]` | - | Controlled состояние |
61
64
  | defaultValue | `SelectionSingleValueType \| SelectionSingleValueType[]` | - | Начальное состояние |
62
65
  | onChange | `((value: any) => void) \| ((value: any) => void)` | - | Controlled обработчик измения состояния |
63
66
  | selection | "single" \| "multiple" | - | Режим выбора |
64
- | size | "s" \| "m" \| "l" | - | |
65
- | marker | `boolean` | - | |
66
- | scroll | `boolean` | - | |
67
- | scrollRef | `RefObject<HTMLElement>` | - | |
68
- | scrollContainerRef | `RefObject<HTMLElement>` | - | |
67
+ | size | "s" \| "m" \| "l" | - | Размер списка |
68
+ | marker | `boolean` | - | Отображать ли маркер у выбранного жлемента списка |
69
+ | scroll | `boolean` | - | Включить ли скролл для основной части списка |
70
+ | scrollRef | `RefObject<HTMLElement>` | - | Ссылка на элемент, обозначающий самый конец прокручиваемого списка |
71
+ | scrollContainerRef | `RefObject<HTMLElement>` | - | Ссылка на контейнер, который скроллится |
69
72
  | ref | `Ref<HTMLElement>` | - | Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref). @see https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom |
70
73
  | key | `Key` | - | |
71
74
 
@@ -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 {};
@@ -14,13 +14,14 @@ import cn from 'classnames';
14
14
  import { Checkbox, Switch } from '@snack-uikit/toggles';
15
15
  import { TruncateString } from '@snack-uikit/truncate-string';
16
16
  import { extractSupportProps } from '@snack-uikit/utils';
17
- import { useCollapseContext, useListContext, useSelectionContext } from '../../Lists/contexts';
17
+ import { useCollapseContext, useListContext, useParentListContext, useSelectionContext } from '../../Lists/contexts';
18
18
  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 { parentResetActiveFocusIndex } = useParentListContext();
24
25
  const { size, marker } = useListContext();
25
26
  const { level = 0 } = useCollapseContext();
26
27
  const { value, onChange, selection, isSelectionSingle, isSelectionMultiple } = useSelectionContext();
@@ -29,8 +30,11 @@ export function BaseItem(_a) {
29
30
  onChange === null || onChange === void 0 ? void 0 : onChange(id !== null && id !== void 0 ? id : '');
30
31
  };
31
32
  const handleItemClick = (e) => {
32
- if (!isParentNode) {
33
- handleChange();
33
+ if (!inactive) {
34
+ parentResetActiveFocusIndex === null || parentResetActiveFocusIndex === void 0 ? void 0 : parentResetActiveFocusIndex();
35
+ if (!isParentNode) {
36
+ handleChange();
37
+ }
34
38
  }
35
39
  onClick === null || onClick === void 0 ? void 0 : onClick(e);
36
40
  };
@@ -63,5 +67,5 @@ export function BaseItem(_a) {
63
67
  e.stopPropagation();
64
68
  };
65
69
  const props = extractSupportProps(rest);
66
- 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 })] }) }));
70
+ 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": selection || undefined, "data-open": open || undefined, disabled: disabled, onKeyDown: handleItemKeyDown, onFocus: handleItemFocus, style: { '--level': level }, children: [!switchProp && isSelectionSingle && marker && !isParentNode && !inactive && (_jsx("div", { className: styles.markerContainer, "data-test-id": 'list__base-item-marker' })), !switchProp && isSelectionMultiple && !inactive && (_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 && !inactive && (_jsx(Switch, { disabled: disabled, checked: isChecked, "data-test-id": 'list__base-item-switch' })), !switchProp && expandIcon && _jsx("span", { className: styles.expandableIcon, children: expandIcon })] }) }));
67
71
  }
@@ -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,
@@ -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
  }
@@ -22,22 +22,24 @@ export type BaseItemProps = WithSupportProps<{
22
22
  id?: string | number;
23
23
  disabled?: boolean;
24
24
  itemRef?: RefObject<HTMLButtonElement>;
25
- }>;
26
- export type SwitchProps = {
25
+ className?: string;
26
+ inactive?: boolean;
27
27
  switch?: boolean;
28
- };
29
- export type ItemProps = (BaseItemProps & SwitchProps) | AccordionItemProps | NextListItemProps | GroupItemProps;
30
- export type AccordionItemProps = BaseItemProps & {
28
+ }>;
29
+ type BaseItemsWithoutNonGroupProps = Omit<BaseItemProps, 'switch' | 'inactive'>;
30
+ export type ItemProps = BaseItemProps | AccordionItemProps | NextListItemProps | GroupItemProps;
31
+ export type AccordionItemProps = BaseItemsWithoutNonGroupProps & {
31
32
  items: ItemProps[];
32
33
  type: 'collapse';
33
34
  };
34
- export type NextListItemProps = BaseItemProps & {
35
+ export type NextListItemProps = BaseItemsWithoutNonGroupProps & {
35
36
  items: ItemProps[];
36
37
  type: 'next-list';
37
38
  placement?: 'right-start' | 'left-start' | 'left' | 'right' | 'left-end' | 'right-end';
38
39
  search?: SearchState;
39
40
  } & ScrollProps;
40
- export type GroupItemProps = SeparatorProps & {
41
+ export type GroupItemProps = Omit<SeparatorProps, 'size'> & {
41
42
  items: ItemProps[];
42
43
  id?: string | number;
43
44
  };
45
+ export {};
@@ -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 & {
@@ -6,9 +6,11 @@ export declare const List: import("react").ForwardRefExoticComponent<{
6
6
  pinTop?: import("../../Items").ItemProps[] | undefined;
7
7
  pinBottom?: import("../../Items").ItemProps[] | undefined;
8
8
  footer?: import("react").ReactNode;
9
+ footerActiveElementsRefs?: import("react").RefObject<HTMLElement>[] | undefined;
9
10
  search?: import("../../../types").SearchState | undefined;
10
11
  loading?: boolean | undefined;
11
12
  noData?: string | undefined;
12
13
  noResults?: string | undefined;
13
- footerActiveElementsRefs?: import("react").RefObject<HTMLElement>[] | undefined;
14
+ tabIndex?: number | undefined;
15
+ onKeyDown?(e: KeyboardEvent<HTMLElement>): void;
14
16
  } & Pick<import("../contexts").SelectionProviderProps, "value" | "defaultValue" | "selection" | "onChange"> & import("../contexts").ListContextType & import("../../../types").ScrollProps & import("react").RefAttributes<HTMLElement>>;
@@ -10,6 +10,7 @@ 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 mergeRefs from 'merge-refs';
13
14
  import { forwardRef, useMemo, useRef, useState } from 'react';
14
15
  import { HiddenTabButton } from '../../../helperComponents';
15
16
  import { extractItemIds, extractItemRefs, withCollapsedItems } from '../../../utils';
@@ -18,8 +19,8 @@ import { extractSelectionProps, ParentListContext, SelectionProvider, useParentL
18
19
  import { useKeyboardNavigation } from '../hooks';
19
20
  import { ListPrivate } from '../ListPrivate';
20
21
  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"]);
22
+ export const List = forwardRef((_a, ref) => {
23
+ var { items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs, onKeyDown, tabIndex = 0 } = _a, props = __rest(_a, ["items", "search", "pinBottom", "pinTop", "footerActiveElementsRefs", "onKeyDown", "tabIndex"]);
23
24
  const hasSearch = useMemo(() => Boolean(search), [search]);
24
25
  const memorizedItems = useMemo(() => addItemsIds((pinTop !== null && pinTop !== void 0 ? pinTop : []).concat(itemsProp).concat(pinBottom !== null && pinBottom !== void 0 ? pinBottom : [])), [itemsProp, pinBottom, pinTop]);
25
26
  const [openCollapsedItems, setOpenCollapsedItems] = useState([]);
@@ -52,6 +53,16 @@ export const List = forwardRef((_a) => {
52
53
  itemRefs,
53
54
  });
54
55
  const isActive = listRef.current === document.activeElement && activeFocusIndex === -1 && openNestedIndex === -1;
56
+ const mergedHandlerKeyDown = (e) => {
57
+ onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(e);
58
+ handleListKeyDown === null || handleListKeyDown === void 0 ? void 0 : handleListKeyDown(e);
59
+ };
60
+ const handleOnFocus = (e) => {
61
+ if (e.relatedTarget === null ||
62
+ (e.relatedTarget && itemRefs.every(({ current }) => current !== e.relatedTarget))) {
63
+ resetActiveFocusIndex();
64
+ }
65
+ };
55
66
  return (
56
67
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
57
68
  // @ts-ignore
@@ -68,10 +79,5 @@ export const List = forwardRef((_a) => {
68
79
  parentResetActiveFocusIndex: resetActiveFocusIndex,
69
80
  openCollapsedItems,
70
81
  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 })] }) }) })));
82
+ }, children: _jsxs("div", { className: styles.wrapper, "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
83
  });
@@ -6,11 +6,13 @@ export declare const ListPrivate: import("react").ForwardRefExoticComponent<{
6
6
  pinTop?: import("../../Items").ItemProps[] | undefined;
7
7
  pinBottom?: import("../../Items").ItemProps[] | undefined;
8
8
  footer?: import("react").ReactNode;
9
+ footerActiveElementsRefs?: RefObject<HTMLElement>[] | undefined;
9
10
  search?: import("../../../types").SearchState | undefined;
10
11
  loading?: boolean | undefined;
11
12
  noData?: string | undefined;
12
13
  noResults?: string | undefined;
13
- footerActiveElementsRefs?: RefObject<HTMLElement>[] | undefined;
14
+ tabIndex?: number | undefined;
15
+ onKeyDown?(e: import("react").KeyboardEvent<HTMLElement>): void;
14
16
  } & Pick<import("../contexts").SelectionProviderProps, "value" | "defaultValue" | "selection" | "onChange"> & import("../contexts").ListContextType & import("../../../types").ScrollProps & {
15
17
  nested?: boolean | undefined;
16
18
  active?: boolean | undefined;
@@ -1,6 +1,8 @@
1
1
  import { ReactNode } from 'react';
2
2
  export type ListContextType = {
3
+ /** Размер списка */
3
4
  size?: 's' | 'm' | 'l';
5
+ /** Отображать ли маркер у выбранного жлемента списка */
4
6
  marker?: boolean;
5
7
  };
6
8
  type Child = {
@@ -5,25 +5,33 @@ import { ScrollProps, SearchState } from '../../types';
5
5
  import { ItemProps } from '../Items';
6
6
  import { ListContextType, SelectionProviderProps } from './contexts';
7
7
  export type ListProps = WithSupportProps<{
8
+ /** Основные элементы списка */
8
9
  items: ItemProps[];
10
+ /** Элементы списка, закрепленные сверху */
9
11
  pinTop?: ItemProps[];
12
+ /** Элементы списка, закрепленные снизу */
10
13
  pinBottom?: ItemProps[];
14
+ /** Кастомизируемый элемент, помещаемый внизу списка */
11
15
  footer?: ReactNode;
16
+ /** Список ссылок на костомные элементы, помещенные в специальную секцию внизу списка */
17
+ footerActiveElementsRefs?: RefObject<HTMLElement>[];
18
+ /** Настройки поисковой строки */
12
19
  search?: SearchState;
20
+ /** Флаг, отвещающий за состояние загрузки списка */
13
21
  loading?: boolean;
22
+ /** Текст для состояния "Отсутсвие данных" */
14
23
  noData?: string;
24
+ /** Текст для состояния "Отсутсвие результата" при поиске */
15
25
  noResults?: string;
16
- footerActiveElementsRefs?: RefObject<HTMLElement>[];
26
+ tabIndex?: number;
27
+ onKeyDown?(e: KeyboardEvent<HTMLElement>): void;
17
28
  } & Pick<SelectionProviderProps, 'value' | 'defaultValue' | 'onChange' | 'selection'> & ListContextType & ScrollProps>;
18
29
  export type DroplistProps = {
30
+ /** Ссылка на элемент-триггер для дроплиста */
19
31
  triggerElemRef?: RefObject<HTMLElement>;
32
+ /** Триггер для дроплиста */
20
33
  children?: ReactNode;
21
- open?: boolean;
22
- onOpenChange?(open: boolean): void;
23
- trigger?: DropdownProps['trigger'];
24
- placement?: DropdownProps['placement'];
25
- widthStrategy?: DropdownProps['widthStrategy'];
26
- } & ListProps;
34
+ } & Pick<DropdownProps, 'trigger' | 'placement' | 'widthStrategy' | 'open' | 'onOpenChange'> & Omit<ListProps, 'tabIndex' | 'onKeyDown'>;
27
35
  export type ListPrivateProps = ListProps & {
28
36
  nested?: boolean;
29
37
  active?: boolean;
@@ -1,6 +1,7 @@
1
1
  import { RefObject } from 'react';
2
2
  type HiddenTabButtonProps = {
3
3
  listRef: RefObject<HTMLElement>;
4
+ tabIndex?: number;
4
5
  };
5
6
  export declare const HiddenTabButton: import("react").ForwardRefExoticComponent<HiddenTabButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
6
7
  export {};
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { forwardRef, useCallback } from 'react';
3
3
  import styles from './styles.module.css';
4
- export const HiddenTabButton = forwardRef(({ listRef }, ref) => {
4
+ export const HiddenTabButton = forwardRef(({ listRef, tabIndex }, ref) => {
5
5
  const handleFocus = useCallback((e) => {
6
6
  var _a;
7
7
  if (e.relatedTarget !== listRef.current) {
@@ -13,5 +13,5 @@ export const HiddenTabButton = forwardRef(({ listRef }, ref) => {
13
13
  const handleKeyDown = useCallback((e) => {
14
14
  e.stopPropagation();
15
15
  }, []);
16
- return _jsx("button", { "aria-hidden": true, ref: ref, onKeyDown: handleKeyDown, onFocus: handleFocus, className: styles.hiddenBtn });
16
+ return (_jsx("button", { "aria-hidden": true, ref: ref, onKeyDown: handleKeyDown, onFocus: handleFocus, className: styles.hiddenBtn, tabIndex: tabIndex }));
17
17
  });
package/dist/types.d.ts CHANGED
@@ -6,7 +6,10 @@ export type SearchState = {
6
6
  loading?: boolean;
7
7
  };
8
8
  export type ScrollProps = {
9
+ /** Включить ли скролл для основной части списка */
9
10
  scroll?: boolean;
11
+ /** Ссылка на элемент, обозначающий самый конец прокручиваемого списка */
10
12
  scrollRef?: RefObject<HTMLElement>;
13
+ /** Ссылка на контейнер, который скроллится */
11
14
  scrollContainerRef?: RefObject<HTMLElement>;
12
15
  };
package/dist/utils.js CHANGED
@@ -6,7 +6,8 @@ export function withCollapsedItems({ items, openCollapsedItems }) {
6
6
  let expandedIds = [];
7
7
  items.forEach(item => {
8
8
  var _a;
9
- if ((isBaseItemProps(item) || isNextListItemProps(item) || isAccordionItemProps(item)) && !item.disabled) {
9
+ if (((isBaseItemProps(item) && !item.inactive) || isNextListItemProps(item) || isAccordionItemProps(item)) &&
10
+ !item.disabled) {
10
11
  newItems = newItems.concat([item]);
11
12
  ids = ids.concat([(_a = item.id) !== null && _a !== void 0 ? _a : '']);
12
13
  if (item.itemRef) {
@@ -55,7 +56,10 @@ export function extractItemIds(items) {
55
56
  }
56
57
  export function extractChildIds({ items }) {
57
58
  return items
58
- .filter(item => isAccordionItemProps(item) || isNextListItemProps(item) || isGroupItemProps(item) || !item.disabled)
59
+ .filter(item => isAccordionItemProps(item) ||
60
+ isNextListItemProps(item) ||
61
+ isGroupItemProps(item) ||
62
+ (isBaseItemProps(item) && !item.disabled && !item.inactive))
59
63
  .reduce((prev, item) => {
60
64
  var _a;
61
65
  if (isAccordionItemProps(item) || isNextListItemProps(item)) {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "title": "List",
7
- "version": "0.1.1",
7
+ "version": "0.1.3-preview-3a43a07a.0",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -42,7 +42,8 @@
42
42
  "@snack-uikit/truncate-string": "0.4.5",
43
43
  "@snack-uikit/utils": "3.2.0",
44
44
  "classnames": "2.5.1",
45
+ "merge-refs": "1.2.2",
45
46
  "uncontrollable": "8.0.4"
46
47
  },
47
- "gitHead": "54873fdb2f34f26a771f62568d3d6fcd62ea1694"
48
+ "gitHead": "cd354254d1d434829a3d3235ac4b46723a853636"
48
49
  }
@@ -5,15 +5,14 @@ import { Checkbox, Switch } from '@snack-uikit/toggles';
5
5
  import { TruncateString } from '@snack-uikit/truncate-string';
6
6
  import { extractSupportProps } from '@snack-uikit/utils';
7
7
 
8
- import { useCollapseContext, useListContext, useSelectionContext } from '../../Lists/contexts';
8
+ import { useCollapseContext, useListContext, useParentListContext, useSelectionContext } from '../../Lists/contexts';
9
9
  import commonStyles from '../styles.module.scss';
10
- import { BaseItemPrivateProps, BaseItemProps, SwitchProps } from '../types';
10
+ import { BaseItemPrivateProps, BaseItemProps } from '../types';
11
11
  import { CHECKBOX_SIZE_MAP } from './constants';
12
12
  import styles from './styles.module.scss';
13
13
 
14
14
  type AllBaseItemProps = BaseItemProps &
15
- BaseItemPrivateProps &
16
- SwitchProps & { indeterminate?: boolean; onSelect?(): void; isParentNode?: boolean };
15
+ BaseItemPrivateProps & { indeterminate?: boolean; onSelect?(): void; isParentNode?: boolean };
17
16
 
18
17
  export function BaseItem({
19
18
  beforeContent,
@@ -31,10 +30,13 @@ export function BaseItem({
31
30
  indeterminate,
32
31
  onSelect,
33
32
  isParentNode,
33
+ className,
34
+ inactive,
34
35
  ...rest
35
36
  }: AllBaseItemProps) {
36
37
  const { option, caption, description } = content || {};
37
38
 
39
+ const { parentResetActiveFocusIndex } = useParentListContext();
38
40
  const { size, marker } = useListContext();
39
41
  const { level = 0 } = useCollapseContext();
40
42
  const { value, onChange, selection, isSelectionSingle, isSelectionMultiple } = useSelectionContext();
@@ -46,8 +48,12 @@ export function BaseItem({
46
48
  };
47
49
 
48
50
  const handleItemClick = (e: MouseEvent<HTMLButtonElement>) => {
49
- if (!isParentNode) {
50
- handleChange();
51
+ if (!inactive) {
52
+ parentResetActiveFocusIndex?.();
53
+
54
+ if (!isParentNode) {
55
+ handleChange();
56
+ }
51
57
  }
52
58
 
53
59
  onClick?.(e);
@@ -93,10 +99,12 @@ export function BaseItem({
93
99
  <li role={'menuitem'} data-test-id={props['data-test-id'] || 'list__base-item_' + id}>
94
100
  <button
95
101
  ref={itemRef}
96
- className={cn(commonStyles.listItem, styles.droplistItem)}
102
+ className={cn(commonStyles.listItem, styles.droplistItem, className)}
97
103
  data-size={size}
98
104
  onClick={handleItemClick}
99
105
  tabIndex={-1}
106
+ data-non-pointer={inactive && !onClick}
107
+ data-inactive={inactive || undefined}
100
108
  data-checked={(isParentNode && (indeterminate || isChecked)) || (isChecked && !switchProp) || undefined}
101
109
  data-variant={selection || undefined}
102
110
  data-open={open || undefined}
@@ -105,10 +113,10 @@ export function BaseItem({
105
113
  onFocus={handleItemFocus}
106
114
  style={{ '--level': level }}
107
115
  >
108
- {!switchProp && isSelectionSingle && marker && !isParentNode && (
116
+ {!switchProp && isSelectionSingle && marker && !isParentNode && !inactive && (
109
117
  <div className={styles.markerContainer} data-test-id='list__base-item-marker' />
110
118
  )}
111
- {!switchProp && isSelectionMultiple && (
119
+ {!switchProp && isSelectionMultiple && !inactive && (
112
120
  <div className={styles.checkbox}>
113
121
  <Checkbox
114
122
  size={CHECKBOX_SIZE_MAP[size ?? 's']}
@@ -142,7 +150,9 @@ export function BaseItem({
142
150
 
143
151
  {afterContent}
144
152
 
145
- {switchProp && <Switch disabled={disabled} checked={isChecked} data-test-id='list__base-item-switch' />}
153
+ {switchProp && !inactive && (
154
+ <Switch disabled={disabled} checked={isChecked} data-test-id='list__base-item-switch' />
155
+ )}
146
156
  {!switchProp && expandIcon && <span className={styles.expandableIcon}>{expandIcon}</span>}
147
157
  </button>
148
158
  </li>
@@ -134,6 +134,10 @@ $typography: (
134
134
  }
135
135
  }
136
136
 
137
+ &[data-non-pointer] {
138
+ cursor: inherit;
139
+ }
140
+
137
141
  &:disabled {
138
142
  .option,
139
143
  .description,
@@ -46,21 +46,6 @@ $sizes: 's', 'm', 'l';
46
46
  background-color: transparent;
47
47
  }
48
48
 
49
- &[data-open],
50
- &[data-focused],
51
- &:hover,
52
- &:focus-visible {
53
- &::after {
54
- opacity: $opacity-a008;
55
- background-color: $sys-neutral-accent-default;
56
- }
57
- }
58
-
59
- &[data-focused],
60
- &:focus-visible {
61
- @include outline-inside-var($container-focused-s);
62
- }
63
-
64
49
  &:disabled {
65
50
  cursor: not-allowed;
66
51
  background-color: transparent;
@@ -75,4 +60,21 @@ $sizes: 's', 'm', 'l';
75
60
  background-color: transparent;
76
61
  }
77
62
  }
63
+
64
+ &:not([data-inactive]) {
65
+ &[data-open],
66
+ &[data-focused],
67
+ &:hover,
68
+ &:focus-visible {
69
+ &::after {
70
+ opacity: $opacity-a008;
71
+ background-color: $sys-neutral-accent-default;
72
+ }
73
+ }
74
+
75
+ &[data-focused],
76
+ &:focus-visible {
77
+ @include outline-inside-var($container-focused-s);
78
+ }
79
+ }
78
80
  }
@@ -32,30 +32,32 @@ export type BaseItemProps = WithSupportProps<{
32
32
  disabled?: boolean;
33
33
 
34
34
  itemRef?: RefObject<HTMLButtonElement>;
35
- }>;
35
+ className?: string;
36
36
 
37
- export type SwitchProps = {
37
+ inactive?: boolean;
38
38
  switch?: boolean;
39
- };
39
+ }>;
40
+
41
+ type BaseItemsWithoutNonGroupProps = Omit<BaseItemProps, 'switch' | 'inactive'>;
40
42
 
41
43
  // eslint-disable-next-line no-use-before-define
42
- export type ItemProps = (BaseItemProps & SwitchProps) | AccordionItemProps | NextListItemProps | GroupItemProps;
44
+ export type ItemProps = BaseItemProps | AccordionItemProps | NextListItemProps | GroupItemProps;
43
45
 
44
- export type AccordionItemProps = BaseItemProps & {
46
+ export type AccordionItemProps = BaseItemsWithoutNonGroupProps & {
45
47
  items: ItemProps[];
46
48
  // TODO: add later
47
49
  // mode?: 'single' | 'multiple';
48
50
  type: 'collapse';
49
51
  };
50
52
 
51
- export type NextListItemProps = BaseItemProps & {
53
+ export type NextListItemProps = BaseItemsWithoutNonGroupProps & {
52
54
  items: ItemProps[];
53
55
  type: 'next-list';
54
56
  placement?: 'right-start' | 'left-start' | 'left' | 'right' | 'left-end' | 'right-end';
55
57
  search?: SearchState;
56
58
  } & ScrollProps;
57
59
 
58
- export type GroupItemProps = SeparatorProps & {
60
+ export type GroupItemProps = Omit<SeparatorProps, 'size'> & {
59
61
  items: ItemProps[];
60
62
  id?: string | number;
61
63
  };
@@ -1,4 +1,5 @@
1
- import { forwardRef, useMemo, useRef, useState } from 'react';
1
+ import mergeRefs from 'merge-refs';
2
+ import { FocusEvent, forwardRef, KeyboardEvent, useMemo, useRef, useState } from 'react';
2
3
 
3
4
  import { HiddenTabButton } from '../../../helperComponents';
4
5
  import { extractItemIds, extractItemRefs, withCollapsedItems } from '../../../utils';
@@ -10,7 +11,10 @@ import styles from '../styles.module.scss';
10
11
  import { ListProps } from '../types';
11
12
 
12
13
  export const List = forwardRef<HTMLElement, ListProps>(
13
- ({ items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs, ...props }) => {
14
+ (
15
+ { items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs, onKeyDown, tabIndex = 0, ...props },
16
+ ref,
17
+ ) => {
14
18
  const hasSearch = useMemo(() => Boolean(search), [search]);
15
19
 
16
20
  const memorizedItems = useMemo(
@@ -68,6 +72,20 @@ export const List = forwardRef<HTMLElement, ListProps>(
68
72
 
69
73
  const isActive = listRef.current === document.activeElement && activeFocusIndex === -1 && openNestedIndex === -1;
70
74
 
75
+ const mergedHandlerKeyDown = (e: KeyboardEvent<HTMLUListElement>) => {
76
+ onKeyDown?.(e);
77
+ handleListKeyDown?.(e);
78
+ };
79
+
80
+ const handleOnFocus = (e: FocusEvent<HTMLElement>) => {
81
+ if (
82
+ e.relatedTarget === null ||
83
+ (e.relatedTarget && itemRefs.every(({ current }) => current !== e.relatedTarget))
84
+ ) {
85
+ resetActiveFocusIndex();
86
+ }
87
+ };
88
+
71
89
  return (
72
90
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
73
91
  // @ts-ignore
@@ -96,22 +114,15 @@ export const List = forwardRef<HTMLElement, ListProps>(
96
114
  <ListPrivate
97
115
  {...props}
98
116
  {...slicedItems}
99
- ref={listRef}
100
- onFocus={e => {
101
- if (
102
- e.relatedTarget === null ||
103
- (e.relatedTarget && itemRefs.every(({ current }) => current !== e.relatedTarget))
104
- ) {
105
- resetActiveFocusIndex();
106
- }
107
- }}
108
- onKeyDown={handleListKeyDown}
109
- tabIndex={0}
117
+ ref={mergeRefs(ref, listRef)}
118
+ onFocus={handleOnFocus}
119
+ onKeyDown={mergedHandlerKeyDown}
120
+ tabIndex={tabIndex}
110
121
  search={search}
111
122
  nested={false}
112
123
  />
113
124
 
114
- <HiddenTabButton ref={btnRef} listRef={listRef} />
125
+ <HiddenTabButton ref={btnRef} listRef={listRef} tabIndex={tabIndex} />
115
126
  </div>
116
127
  </ParentListContext.Provider>
117
128
  </SelectionProvider>
@@ -1,9 +1,11 @@
1
1
  import { createContext, ReactNode, useContext } from 'react';
2
2
 
3
3
  export type ListContextType = {
4
+ /** Размер списка */
4
5
  size?: 's' | 'm' | 'l';
5
6
  // TODO
6
7
  // collapse?: 'single' | 'multiple';
8
+ /** Отображать ли маркер у выбранного жлемента списка */
7
9
  marker?: boolean;
8
10
  };
9
11
 
@@ -9,31 +9,40 @@ import { ListContextType, SelectionProviderProps } from './contexts';
9
9
 
10
10
  export type ListProps = WithSupportProps<
11
11
  {
12
+ /** Основные элементы списка */
12
13
  items: ItemProps[];
14
+ /** Элементы списка, закрепленные сверху */
13
15
  pinTop?: ItemProps[];
16
+ /** Элементы списка, закрепленные снизу */
14
17
  pinBottom?: ItemProps[];
18
+ /** Кастомизируемый элемент, помещаемый внизу списка */
15
19
  footer?: ReactNode;
20
+ /** Список ссылок на костомные элементы, помещенные в специальную секцию внизу списка */
21
+ footerActiveElementsRefs?: RefObject<HTMLElement>[];
16
22
  // TODO: add later
17
23
  // collapse?: 'single' | 'multiple';
24
+ /** Настройки поисковой строки */
18
25
  search?: SearchState;
26
+ /** Флаг, отвещающий за состояние загрузки списка */
19
27
  loading?: boolean;
28
+ /** Текст для состояния "Отсутсвие данных" */
20
29
  noData?: string;
30
+ /** Текст для состояния "Отсутсвие результата" при поиске */
21
31
  noResults?: string;
22
- footerActiveElementsRefs?: RefObject<HTMLElement>[];
32
+ tabIndex?: number;
33
+ onKeyDown?(e: KeyboardEvent<HTMLElement>): void;
23
34
  } & Pick<SelectionProviderProps, 'value' | 'defaultValue' | 'onChange' | 'selection'> &
24
35
  ListContextType &
25
36
  ScrollProps
26
37
  >;
27
38
 
28
39
  export type DroplistProps = {
40
+ /** Ссылка на элемент-триггер для дроплиста */
29
41
  triggerElemRef?: RefObject<HTMLElement>;
42
+ /** Триггер для дроплиста */
30
43
  children?: ReactNode;
31
- open?: boolean;
32
- onOpenChange?(open: boolean): void;
33
- trigger?: DropdownProps['trigger'];
34
- placement?: DropdownProps['placement'];
35
- widthStrategy?: DropdownProps['widthStrategy'];
36
- } & ListProps;
44
+ } & Pick<DropdownProps, 'trigger' | 'placement' | 'widthStrategy' | 'open' | 'onOpenChange'> &
45
+ Omit<ListProps, 'tabIndex' | 'onKeyDown'>;
37
46
 
38
47
  export type ListPrivateProps = ListProps & {
39
48
  nested?: boolean;
@@ -4,9 +4,10 @@ import styles from './styles.module.scss';
4
4
 
5
5
  type HiddenTabButtonProps = {
6
6
  listRef: RefObject<HTMLElement>;
7
+ tabIndex?: number;
7
8
  };
8
9
 
9
- export const HiddenTabButton = forwardRef<HTMLButtonElement, HiddenTabButtonProps>(({ listRef }, ref) => {
10
+ export const HiddenTabButton = forwardRef<HTMLButtonElement, HiddenTabButtonProps>(({ listRef, tabIndex }, ref) => {
10
11
  const handleFocus = useCallback(
11
12
  (e: FocusEvent<HTMLButtonElement>) => {
12
13
  if (e.relatedTarget !== listRef.current) {
@@ -23,5 +24,14 @@ export const HiddenTabButton = forwardRef<HTMLButtonElement, HiddenTabButtonProp
23
24
  e.stopPropagation();
24
25
  }, []);
25
26
 
26
- return <button aria-hidden ref={ref} onKeyDown={handleKeyDown} onFocus={handleFocus} className={styles.hiddenBtn} />;
27
+ return (
28
+ <button
29
+ aria-hidden
30
+ ref={ref}
31
+ onKeyDown={handleKeyDown}
32
+ onFocus={handleFocus}
33
+ className={styles.hiddenBtn}
34
+ tabIndex={tabIndex}
35
+ />
36
+ );
27
37
  });
package/src/types.ts CHANGED
@@ -8,7 +8,10 @@ export type SearchState = {
8
8
  };
9
9
 
10
10
  export type ScrollProps = {
11
+ /** Включить ли скролл для основной части списка */
11
12
  scroll?: boolean;
13
+ /** Ссылка на элемент, обозначающий самый конец прокручиваемого списка */
12
14
  scrollRef?: RefObject<HTMLElement>;
15
+ /** Ссылка на контейнер, который скроллится */
13
16
  scrollContainerRef?: RefObject<HTMLElement>;
14
17
  };
package/src/utils.ts CHANGED
@@ -20,7 +20,10 @@ export function withCollapsedItems({ items, openCollapsedItems }: WithCollapsedI
20
20
  let expandedIds: Array<string | number> = [];
21
21
 
22
22
  items.forEach(item => {
23
- if ((isBaseItemProps(item) || isNextListItemProps(item) || isAccordionItemProps(item)) && !item.disabled) {
23
+ if (
24
+ ((isBaseItemProps(item) && !item.inactive) || isNextListItemProps(item) || isAccordionItemProps(item)) &&
25
+ !item.disabled
26
+ ) {
24
27
  newItems = newItems.concat([item]);
25
28
  ids = ids.concat([item.id ?? '']);
26
29
 
@@ -88,7 +91,13 @@ export function extractItemIds(items: ItemProps[]): Array<string | number> {
88
91
 
89
92
  export function extractChildIds({ items }: { items: ItemProps[] }): Array<string | number> {
90
93
  return items
91
- .filter(item => isAccordionItemProps(item) || isNextListItemProps(item) || isGroupItemProps(item) || !item.disabled)
94
+ .filter(
95
+ item =>
96
+ isAccordionItemProps(item) ||
97
+ isNextListItemProps(item) ||
98
+ isGroupItemProps(item) ||
99
+ (isBaseItemProps(item) && !item.disabled && !item.inactive),
100
+ )
92
101
  .reduce(
93
102
  (prev: Array<string | number>, item: ItemProps) => {
94
103
  if (isAccordionItemProps(item) || isNextListItemProps(item)) {