@snack-uikit/list 0.1.3-preview-3a43a07a.0 → 0.1.3

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,16 @@
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.3 (2024-02-01)
7
+
8
+ ### Only dependencies have been changed
9
+ * [@snack-uikit/dropdown@0.2.0](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/dropdown/CHANGELOG.md)
10
+ * [@snack-uikit/truncate-string@0.4.6](https://github.com/cloud-ru-tech/snack-uikit/blob/master/packages/truncate-string/CHANGELOG.md)
11
+
12
+
13
+
14
+
15
+
6
16
  ## 0.1.2 (2024-01-31)
7
17
 
8
18
 
package/README.md CHANGED
@@ -28,10 +28,6 @@
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
- | value | `SelectionSingleValueType \| SelectionSingleValueType[]` | - | Controlled состояние |
32
- | defaultValue | `SelectionSingleValueType \| SelectionSingleValueType[]` | - | Начальное состояние |
33
- | onChange | `((value: any) => void) \| ((value: any) => void)` | - | Controlled обработчик измения состояния |
34
- | selection | "single" \| "multiple" | - | Режим выбора |
35
31
  | pinTop | `ItemProps[]` | - | Элементы списка, закрепленные сверху |
36
32
  | pinBottom | `ItemProps[]` | - | Элементы списка, закрепленные снизу |
37
33
  | footer | `ReactNode` | - | Кастомизируемый элемент, помещаемый внизу списка |
@@ -40,6 +36,10 @@
40
36
  | loading | `boolean` | - | Флаг, отвещающий за состояние загрузки списка |
41
37
  | noData | `string` | - | Текст для состояния "Отсутсвие данных" |
42
38
  | noResults | `string` | - | Текст для состояния "Отсутсвие результата" при поиске |
39
+ | value | `SelectionSingleValueType \| SelectionSingleValueType[]` | - | Controlled состояние |
40
+ | defaultValue | `SelectionSingleValueType \| SelectionSingleValueType[]` | - | Начальное состояние |
41
+ | onChange | `((value: any) => void) \| ((value: any) => void)` | - | Controlled обработчик измения состояния |
42
+ | selection | "single" \| "multiple" | - | Режим выбора |
43
43
  | size | "s" \| "m" \| "l" | - | Размер списка |
44
44
  | marker | `boolean` | - | Отображать ли маркер у выбранного жлемента списка |
45
45
  | scroll | `boolean` | - | Включить ли скролл для основной части списка |
@@ -58,8 +58,6 @@
58
58
  | loading | `boolean` | - | Флаг, отвещающий за состояние загрузки списка |
59
59
  | noData | `string` | - | Текст для состояния "Отсутсвие данных" |
60
60
  | noResults | `string` | - | Текст для состояния "Отсутсвие результата" при поиске |
61
- | tabIndex | `number` | - | |
62
- | onKeyDown | `(e: KeyboardEvent<HTMLElement>) => void` | - | |
63
61
  | value | `SelectionSingleValueType \| SelectionSingleValueType[]` | - | Controlled состояние |
64
62
  | defaultValue | `SelectionSingleValueType \| SelectionSingleValueType[]` | - | Начальное состояние |
65
63
  | onChange | `((value: any) => void) \| ((value: any) => void)` | - | Controlled обработчик измения состояния |
@@ -1,8 +1,8 @@
1
- import { BaseItemPrivateProps, BaseItemProps } from '../types';
2
- type AllBaseItemProps = BaseItemProps & BaseItemPrivateProps & {
1
+ import { BaseItemPrivateProps, BaseItemProps, SwitchProps } from '../types';
2
+ type AllBaseItemProps = BaseItemProps & BaseItemPrivateProps & SwitchProps & {
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, className, inactive, ...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, ...rest }: AllBaseItemProps): import("react/jsx-runtime").JSX.Element;
8
8
  export {};
@@ -19,7 +19,7 @@ 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, className, inactive } = _a, rest = __rest(_a, ["beforeContent", "afterContent", "content", "onClick", "id", "expandIcon", "disabled", "open", "itemRef", "switch", "onKeyDown", "onFocus", "indeterminate", "onSelect", "isParentNode", "className", "inactive"]);
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"]);
23
23
  const { option, caption, description } = content || {};
24
24
  const { parentResetActiveFocusIndex } = useParentListContext();
25
25
  const { size, marker } = useListContext();
@@ -30,11 +30,9 @@ export function BaseItem(_a) {
30
30
  onChange === null || onChange === void 0 ? void 0 : onChange(id !== null && id !== void 0 ? id : '');
31
31
  };
32
32
  const handleItemClick = (e) => {
33
- if (!inactive) {
34
- parentResetActiveFocusIndex === null || parentResetActiveFocusIndex === void 0 ? void 0 : parentResetActiveFocusIndex();
35
- if (!isParentNode) {
36
- handleChange();
37
- }
33
+ parentResetActiveFocusIndex === null || parentResetActiveFocusIndex === void 0 ? void 0 : parentResetActiveFocusIndex();
34
+ if (!isParentNode) {
35
+ handleChange();
38
36
  }
39
37
  onClick === null || onClick === void 0 ? void 0 : onClick(e);
40
38
  };
@@ -67,5 +65,5 @@ export function BaseItem(_a) {
67
65
  e.stopPropagation();
68
66
  };
69
67
  const props = extractSupportProps(rest);
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 })] }) }));
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
69
  }
@@ -170,9 +170,6 @@
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
- }
176
173
  .droplistItem:disabled .option,
177
174
  .droplistItem:disabled .description,
178
175
  .droplistItem:disabled .caption,
@@ -50,6 +50,16 @@
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
+ }
53
63
  .listItem:disabled{
54
64
  cursor:not-allowed;
55
65
  background-color:transparent;
@@ -61,14 +71,4 @@
61
71
  .listItem:disabled::after{
62
72
  cursor:not-allowed;
63
73
  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,24 +22,22 @@ export type BaseItemProps = WithSupportProps<{
22
22
  id?: string | number;
23
23
  disabled?: boolean;
24
24
  itemRef?: RefObject<HTMLButtonElement>;
25
- className?: string;
26
- inactive?: boolean;
27
- switch?: boolean;
28
25
  }>;
29
- type BaseItemsWithoutNonGroupProps = Omit<BaseItemProps, 'switch' | 'inactive'>;
30
- export type ItemProps = BaseItemProps | AccordionItemProps | NextListItemProps | GroupItemProps;
31
- export type AccordionItemProps = BaseItemsWithoutNonGroupProps & {
26
+ export type SwitchProps = {
27
+ switch?: boolean;
28
+ };
29
+ export type ItemProps = (BaseItemProps & SwitchProps) | AccordionItemProps | NextListItemProps | GroupItemProps;
30
+ export type AccordionItemProps = BaseItemProps & {
32
31
  items: ItemProps[];
33
32
  type: 'collapse';
34
33
  };
35
- export type NextListItemProps = BaseItemsWithoutNonGroupProps & {
34
+ export type NextListItemProps = BaseItemProps & {
36
35
  items: ItemProps[];
37
36
  type: 'next-list';
38
37
  placement?: 'right-start' | 'left-start' | 'left' | 'right' | 'left-end' | 'right-end';
39
38
  search?: SearchState;
40
39
  } & ScrollProps;
41
- export type GroupItemProps = Omit<SeparatorProps, 'size'> & {
40
+ export type GroupItemProps = SeparatorProps & {
42
41
  items: ItemProps[];
43
42
  id?: string | number;
44
43
  };
45
- export {};
@@ -1,4 +1,4 @@
1
- import { KeyboardEvent } from 'react';
1
+ /// <reference types="react" />
2
2
  export declare const List: import("react").ForwardRefExoticComponent<{
3
3
  'data-test-id'?: string | undefined;
4
4
  } & import("react").AriaAttributes & {
@@ -11,6 +11,4 @@ export declare const List: import("react").ForwardRefExoticComponent<{
11
11
  loading?: boolean | undefined;
12
12
  noData?: string | undefined;
13
13
  noResults?: string | undefined;
14
- tabIndex?: number | undefined;
15
- onKeyDown?(e: KeyboardEvent<HTMLElement>): void;
16
14
  } & Pick<import("../contexts").SelectionProviderProps, "value" | "defaultValue" | "selection" | "onChange"> & import("../contexts").ListContextType & import("../../../types").ScrollProps & import("react").RefAttributes<HTMLElement>>;
@@ -10,7 +10,6 @@ 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';
14
13
  import { forwardRef, useMemo, useRef, useState } from 'react';
15
14
  import { HiddenTabButton } from '../../../helperComponents';
16
15
  import { extractItemIds, extractItemRefs, withCollapsedItems } from '../../../utils';
@@ -19,8 +18,8 @@ import { extractSelectionProps, ParentListContext, SelectionProvider, useParentL
19
18
  import { useKeyboardNavigation } from '../hooks';
20
19
  import { ListPrivate } from '../ListPrivate';
21
20
  import styles from '../styles.module.css';
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"]);
21
+ export const List = forwardRef((_a) => {
22
+ var { items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs } = _a, props = __rest(_a, ["items", "search", "pinBottom", "pinTop", "footerActiveElementsRefs"]);
24
23
  const hasSearch = useMemo(() => Boolean(search), [search]);
25
24
  const memorizedItems = useMemo(() => addItemsIds((pinTop !== null && pinTop !== void 0 ? pinTop : []).concat(itemsProp).concat(pinBottom !== null && pinBottom !== void 0 ? pinBottom : [])), [itemsProp, pinBottom, pinTop]);
26
25
  const [openCollapsedItems, setOpenCollapsedItems] = useState([]);
@@ -53,16 +52,6 @@ export const List = forwardRef((_a, ref) => {
53
52
  itemRefs,
54
53
  });
55
54
  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
- };
66
55
  return (
67
56
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
68
57
  // @ts-ignore
@@ -79,5 +68,10 @@ export const List = forwardRef((_a, ref) => {
79
68
  parentResetActiveFocusIndex: resetActiveFocusIndex,
80
69
  openCollapsedItems,
81
70
  toggleOpenCollapsedItems: id => setOpenCollapsedItems(items => items.includes(id) ? items.filter(item => item !== id) : items.concat([id])),
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 })] }) }) })));
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 })] }) }) })));
83
77
  });
@@ -11,8 +11,6 @@ export declare const ListPrivate: import("react").ForwardRefExoticComponent<{
11
11
  loading?: boolean | undefined;
12
12
  noData?: string | undefined;
13
13
  noResults?: string | undefined;
14
- tabIndex?: number | undefined;
15
- onKeyDown?(e: import("react").KeyboardEvent<HTMLElement>): void;
16
14
  } & Pick<import("../contexts").SelectionProviderProps, "value" | "defaultValue" | "selection" | "onChange"> & import("../contexts").ListContextType & import("../../../types").ScrollProps & {
17
15
  nested?: boolean | undefined;
18
16
  active?: boolean | undefined;
@@ -23,15 +23,13 @@ export type ListProps = WithSupportProps<{
23
23
  noData?: string;
24
24
  /** Текст для состояния "Отсутсвие результата" при поиске */
25
25
  noResults?: string;
26
- tabIndex?: number;
27
- onKeyDown?(e: KeyboardEvent<HTMLElement>): void;
28
26
  } & Pick<SelectionProviderProps, 'value' | 'defaultValue' | 'onChange' | 'selection'> & ListContextType & ScrollProps>;
29
27
  export type DroplistProps = {
30
28
  /** Ссылка на элемент-триггер для дроплиста */
31
29
  triggerElemRef?: RefObject<HTMLElement>;
32
30
  /** Триггер для дроплиста */
33
31
  children?: ReactNode;
34
- } & Pick<DropdownProps, 'trigger' | 'placement' | 'widthStrategy' | 'open' | 'onOpenChange'> & Omit<ListProps, 'tabIndex' | 'onKeyDown'>;
32
+ } & Pick<DropdownProps, 'trigger' | 'placement' | 'widthStrategy' | 'open' | 'onOpenChange'> & ListProps;
35
33
  export type ListPrivateProps = ListProps & {
36
34
  nested?: boolean;
37
35
  active?: boolean;
@@ -1,7 +1,6 @@
1
1
  import { RefObject } from 'react';
2
2
  type HiddenTabButtonProps = {
3
3
  listRef: RefObject<HTMLElement>;
4
- tabIndex?: number;
5
4
  };
6
5
  export declare const HiddenTabButton: import("react").ForwardRefExoticComponent<HiddenTabButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
7
6
  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, tabIndex }, ref) => {
4
+ export const HiddenTabButton = forwardRef(({ listRef }, 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, tabIndex }, 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, tabIndex: tabIndex }));
16
+ return _jsx("button", { "aria-hidden": true, ref: ref, onKeyDown: handleKeyDown, onFocus: handleFocus, className: styles.hiddenBtn });
17
17
  });
package/dist/utils.js CHANGED
@@ -6,8 +6,7 @@ export function withCollapsedItems({ items, openCollapsedItems }) {
6
6
  let expandedIds = [];
7
7
  items.forEach(item => {
8
8
  var _a;
9
- if (((isBaseItemProps(item) && !item.inactive) || isNextListItemProps(item) || isAccordionItemProps(item)) &&
10
- !item.disabled) {
9
+ if ((isBaseItemProps(item) || isNextListItemProps(item) || isAccordionItemProps(item)) && !item.disabled) {
11
10
  newItems = newItems.concat([item]);
12
11
  ids = ids.concat([(_a = item.id) !== null && _a !== void 0 ? _a : '']);
13
12
  if (item.itemRef) {
@@ -56,10 +55,7 @@ export function extractItemIds(items) {
56
55
  }
57
56
  export function extractChildIds({ items }) {
58
57
  return items
59
- .filter(item => isAccordionItemProps(item) ||
60
- isNextListItemProps(item) ||
61
- isGroupItemProps(item) ||
62
- (isBaseItemProps(item) && !item.disabled && !item.inactive))
58
+ .filter(item => isAccordionItemProps(item) || isNextListItemProps(item) || isGroupItemProps(item) || !item.disabled)
63
59
  .reduce((prev, item) => {
64
60
  var _a;
65
61
  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.3-preview-3a43a07a.0",
7
+ "version": "0.1.3",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -33,17 +33,16 @@
33
33
  "scripts": {},
34
34
  "dependencies": {
35
35
  "@snack-uikit/divider": "3.0.1",
36
- "@snack-uikit/dropdown": "0.1.0",
36
+ "@snack-uikit/dropdown": "0.2.0",
37
37
  "@snack-uikit/icons": "0.20.0",
38
38
  "@snack-uikit/loaders": "0.5.0",
39
39
  "@snack-uikit/scroll": "0.5.0",
40
40
  "@snack-uikit/search-private": "0.1.2",
41
41
  "@snack-uikit/toggles": "0.9.4",
42
- "@snack-uikit/truncate-string": "0.4.5",
42
+ "@snack-uikit/truncate-string": "0.4.6",
43
43
  "@snack-uikit/utils": "3.2.0",
44
44
  "classnames": "2.5.1",
45
- "merge-refs": "1.2.2",
46
45
  "uncontrollable": "8.0.4"
47
46
  },
48
- "gitHead": "cd354254d1d434829a3d3235ac4b46723a853636"
47
+ "gitHead": "0726752996aeaf01f58b892fef581c38c1528ea1"
49
48
  }
@@ -7,12 +7,13 @@ import { extractSupportProps } from '@snack-uikit/utils';
7
7
 
8
8
  import { useCollapseContext, useListContext, useParentListContext, useSelectionContext } from '../../Lists/contexts';
9
9
  import commonStyles from '../styles.module.scss';
10
- import { BaseItemPrivateProps, BaseItemProps } from '../types';
10
+ import { BaseItemPrivateProps, BaseItemProps, SwitchProps } 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 & { indeterminate?: boolean; onSelect?(): void; isParentNode?: boolean };
15
+ BaseItemPrivateProps &
16
+ SwitchProps & { indeterminate?: boolean; onSelect?(): void; isParentNode?: boolean };
16
17
 
17
18
  export function BaseItem({
18
19
  beforeContent,
@@ -30,8 +31,6 @@ export function BaseItem({
30
31
  indeterminate,
31
32
  onSelect,
32
33
  isParentNode,
33
- className,
34
- inactive,
35
34
  ...rest
36
35
  }: AllBaseItemProps) {
37
36
  const { option, caption, description } = content || {};
@@ -48,12 +47,10 @@ export function BaseItem({
48
47
  };
49
48
 
50
49
  const handleItemClick = (e: MouseEvent<HTMLButtonElement>) => {
51
- if (!inactive) {
52
- parentResetActiveFocusIndex?.();
50
+ parentResetActiveFocusIndex?.();
53
51
 
54
- if (!isParentNode) {
55
- handleChange();
56
- }
52
+ if (!isParentNode) {
53
+ handleChange();
57
54
  }
58
55
 
59
56
  onClick?.(e);
@@ -99,12 +96,10 @@ export function BaseItem({
99
96
  <li role={'menuitem'} data-test-id={props['data-test-id'] || 'list__base-item_' + id}>
100
97
  <button
101
98
  ref={itemRef}
102
- className={cn(commonStyles.listItem, styles.droplistItem, className)}
99
+ className={cn(commonStyles.listItem, styles.droplistItem)}
103
100
  data-size={size}
104
101
  onClick={handleItemClick}
105
102
  tabIndex={-1}
106
- data-non-pointer={inactive && !onClick}
107
- data-inactive={inactive || undefined}
108
103
  data-checked={(isParentNode && (indeterminate || isChecked)) || (isChecked && !switchProp) || undefined}
109
104
  data-variant={selection || undefined}
110
105
  data-open={open || undefined}
@@ -113,10 +108,10 @@ export function BaseItem({
113
108
  onFocus={handleItemFocus}
114
109
  style={{ '--level': level }}
115
110
  >
116
- {!switchProp && isSelectionSingle && marker && !isParentNode && !inactive && (
111
+ {!switchProp && isSelectionSingle && marker && !isParentNode && (
117
112
  <div className={styles.markerContainer} data-test-id='list__base-item-marker' />
118
113
  )}
119
- {!switchProp && isSelectionMultiple && !inactive && (
114
+ {!switchProp && isSelectionMultiple && (
120
115
  <div className={styles.checkbox}>
121
116
  <Checkbox
122
117
  size={CHECKBOX_SIZE_MAP[size ?? 's']}
@@ -150,9 +145,7 @@ export function BaseItem({
150
145
 
151
146
  {afterContent}
152
147
 
153
- {switchProp && !inactive && (
154
- <Switch disabled={disabled} checked={isChecked} data-test-id='list__base-item-switch' />
155
- )}
148
+ {switchProp && <Switch disabled={disabled} checked={isChecked} data-test-id='list__base-item-switch' />}
156
149
  {!switchProp && expandIcon && <span className={styles.expandableIcon}>{expandIcon}</span>}
157
150
  </button>
158
151
  </li>
@@ -134,10 +134,6 @@ $typography: (
134
134
  }
135
135
  }
136
136
 
137
- &[data-non-pointer] {
138
- cursor: inherit;
139
- }
140
-
141
137
  &:disabled {
142
138
  .option,
143
139
  .description,
@@ -46,6 +46,21 @@ $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
+
49
64
  &:disabled {
50
65
  cursor: not-allowed;
51
66
  background-color: transparent;
@@ -60,21 +75,4 @@ $sizes: 's', 'm', 'l';
60
75
  background-color: transparent;
61
76
  }
62
77
  }
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
- }
80
78
  }
@@ -32,32 +32,30 @@ export type BaseItemProps = WithSupportProps<{
32
32
  disabled?: boolean;
33
33
 
34
34
  itemRef?: RefObject<HTMLButtonElement>;
35
- className?: string;
36
-
37
- inactive?: boolean;
38
- switch?: boolean;
39
35
  }>;
40
36
 
41
- type BaseItemsWithoutNonGroupProps = Omit<BaseItemProps, 'switch' | 'inactive'>;
37
+ export type SwitchProps = {
38
+ switch?: boolean;
39
+ };
42
40
 
43
41
  // eslint-disable-next-line no-use-before-define
44
- export type ItemProps = BaseItemProps | AccordionItemProps | NextListItemProps | GroupItemProps;
42
+ export type ItemProps = (BaseItemProps & SwitchProps) | AccordionItemProps | NextListItemProps | GroupItemProps;
45
43
 
46
- export type AccordionItemProps = BaseItemsWithoutNonGroupProps & {
44
+ export type AccordionItemProps = BaseItemProps & {
47
45
  items: ItemProps[];
48
46
  // TODO: add later
49
47
  // mode?: 'single' | 'multiple';
50
48
  type: 'collapse';
51
49
  };
52
50
 
53
- export type NextListItemProps = BaseItemsWithoutNonGroupProps & {
51
+ export type NextListItemProps = BaseItemProps & {
54
52
  items: ItemProps[];
55
53
  type: 'next-list';
56
54
  placement?: 'right-start' | 'left-start' | 'left' | 'right' | 'left-end' | 'right-end';
57
55
  search?: SearchState;
58
56
  } & ScrollProps;
59
57
 
60
- export type GroupItemProps = Omit<SeparatorProps, 'size'> & {
58
+ export type GroupItemProps = SeparatorProps & {
61
59
  items: ItemProps[];
62
60
  id?: string | number;
63
61
  };
@@ -1,5 +1,4 @@
1
- import mergeRefs from 'merge-refs';
2
- import { FocusEvent, forwardRef, KeyboardEvent, useMemo, useRef, useState } from 'react';
1
+ import { forwardRef, useMemo, useRef, useState } from 'react';
3
2
 
4
3
  import { HiddenTabButton } from '../../../helperComponents';
5
4
  import { extractItemIds, extractItemRefs, withCollapsedItems } from '../../../utils';
@@ -11,10 +10,7 @@ import styles from '../styles.module.scss';
11
10
  import { ListProps } from '../types';
12
11
 
13
12
  export const List = forwardRef<HTMLElement, ListProps>(
14
- (
15
- { items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs, onKeyDown, tabIndex = 0, ...props },
16
- ref,
17
- ) => {
13
+ ({ items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs, ...props }) => {
18
14
  const hasSearch = useMemo(() => Boolean(search), [search]);
19
15
 
20
16
  const memorizedItems = useMemo(
@@ -72,20 +68,6 @@ export const List = forwardRef<HTMLElement, ListProps>(
72
68
 
73
69
  const isActive = listRef.current === document.activeElement && activeFocusIndex === -1 && openNestedIndex === -1;
74
70
 
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
-
89
71
  return (
90
72
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
91
73
  // @ts-ignore
@@ -114,15 +96,22 @@ export const List = forwardRef<HTMLElement, ListProps>(
114
96
  <ListPrivate
115
97
  {...props}
116
98
  {...slicedItems}
117
- ref={mergeRefs(ref, listRef)}
118
- onFocus={handleOnFocus}
119
- onKeyDown={mergedHandlerKeyDown}
120
- tabIndex={tabIndex}
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}
121
110
  search={search}
122
111
  nested={false}
123
112
  />
124
113
 
125
- <HiddenTabButton ref={btnRef} listRef={listRef} tabIndex={tabIndex} />
114
+ <HiddenTabButton ref={btnRef} listRef={listRef} />
126
115
  </div>
127
116
  </ParentListContext.Provider>
128
117
  </SelectionProvider>
@@ -29,8 +29,6 @@ export type ListProps = WithSupportProps<
29
29
  noData?: string;
30
30
  /** Текст для состояния "Отсутсвие результата" при поиске */
31
31
  noResults?: string;
32
- tabIndex?: number;
33
- onKeyDown?(e: KeyboardEvent<HTMLElement>): void;
34
32
  } & Pick<SelectionProviderProps, 'value' | 'defaultValue' | 'onChange' | 'selection'> &
35
33
  ListContextType &
36
34
  ScrollProps
@@ -42,7 +40,7 @@ export type DroplistProps = {
42
40
  /** Триггер для дроплиста */
43
41
  children?: ReactNode;
44
42
  } & Pick<DropdownProps, 'trigger' | 'placement' | 'widthStrategy' | 'open' | 'onOpenChange'> &
45
- Omit<ListProps, 'tabIndex' | 'onKeyDown'>;
43
+ ListProps;
46
44
 
47
45
  export type ListPrivateProps = ListProps & {
48
46
  nested?: boolean;
@@ -4,10 +4,9 @@ import styles from './styles.module.scss';
4
4
 
5
5
  type HiddenTabButtonProps = {
6
6
  listRef: RefObject<HTMLElement>;
7
- tabIndex?: number;
8
7
  };
9
8
 
10
- export const HiddenTabButton = forwardRef<HTMLButtonElement, HiddenTabButtonProps>(({ listRef, tabIndex }, ref) => {
9
+ export const HiddenTabButton = forwardRef<HTMLButtonElement, HiddenTabButtonProps>(({ listRef }, ref) => {
11
10
  const handleFocus = useCallback(
12
11
  (e: FocusEvent<HTMLButtonElement>) => {
13
12
  if (e.relatedTarget !== listRef.current) {
@@ -24,14 +23,5 @@ export const HiddenTabButton = forwardRef<HTMLButtonElement, HiddenTabButtonProp
24
23
  e.stopPropagation();
25
24
  }, []);
26
25
 
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
- );
26
+ return <button aria-hidden ref={ref} onKeyDown={handleKeyDown} onFocus={handleFocus} className={styles.hiddenBtn} />;
37
27
  });
package/src/utils.ts CHANGED
@@ -20,10 +20,7 @@ export function withCollapsedItems({ items, openCollapsedItems }: WithCollapsedI
20
20
  let expandedIds: Array<string | number> = [];
21
21
 
22
22
  items.forEach(item => {
23
- if (
24
- ((isBaseItemProps(item) && !item.inactive) || isNextListItemProps(item) || isAccordionItemProps(item)) &&
25
- !item.disabled
26
- ) {
23
+ if ((isBaseItemProps(item) || isNextListItemProps(item) || isAccordionItemProps(item)) && !item.disabled) {
27
24
  newItems = newItems.concat([item]);
28
25
  ids = ids.concat([item.id ?? '']);
29
26
 
@@ -91,13 +88,7 @@ export function extractItemIds(items: ItemProps[]): Array<string | number> {
91
88
 
92
89
  export function extractChildIds({ items }: { items: ItemProps[] }): Array<string | number> {
93
90
  return items
94
- .filter(
95
- item =>
96
- isAccordionItemProps(item) ||
97
- isNextListItemProps(item) ||
98
- isGroupItemProps(item) ||
99
- (isBaseItemProps(item) && !item.disabled && !item.inactive),
100
- )
91
+ .filter(item => isAccordionItemProps(item) || isNextListItemProps(item) || isGroupItemProps(item) || !item.disabled)
101
92
  .reduce(
102
93
  (prev: Array<string | number>, item: ItemProps) => {
103
94
  if (isAccordionItemProps(item) || isNextListItemProps(item)) {