@cloud-ru/ds-list 1.1.4 → 2.0.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,12 @@
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
+ # 2.0.0 (2026-08-25)
7
+
8
+ ### BREAKING CHANGES
9
+
10
+ - **FF-8948:** toggle collapse item by clicking the whole row, chevron is a state indicator ([40ceab4](https://github.com/cloud-ru-tech/snack-v2/commit/40ceab47b8075c866a592ebf02f37b773f534fe1))
11
+
6
12
  ## 1.1.4 (2026-08-25)
7
13
 
8
14
  **Note:** Version bump only for package @ds/list
@@ -22,16 +22,10 @@ function AccordionItem({ id, disabled, allChildIds, items, ...option }) {
22
22
  const isOpen = Boolean(openCollapseItems.includes(id ?? ''));
23
23
  const checked = Boolean((indeterminate && !isOpen && isSelectionSingle && value && allChildIds.includes(value)) ||
24
24
  (isSelectionMultiple && checkedProp));
25
- const handleKeyDown = (0, react_1.useCallback)(() => {
25
+ // Раскрытие переключает вся строка целиком: клик/Enter по телу и ArrowRight с клавиатуры.
26
+ const handleToggle = (0, react_1.useCallback)(() => {
26
27
  toggleOpenCollapseItem?.(id ?? '');
27
28
  }, [id, toggleOpenCollapseItem]);
28
29
  const itemsJSX = (0, hooks_1.useRenderItems)(items);
29
- // Раскрытие привязано к явной кнопке-триггеру `groupIndicator` (шеврон справа) и
30
- // ТОЛЬКО переключает collapse — `option.onClick` сюда не зовём, иначе у потребителя,
31
- // который вешает на onClick собственный toggle, выходит двойное переключение.
32
- // Клик по телу строки (выбор/навигация) идёт через `option.onClick` в BaseItem.
33
- const handleExpandIconClick = () => {
34
- toggleOpenCollapseItem?.(id ?? '');
35
- };
36
- return ((0, jsx_runtime_1.jsx)(helperComponents_1.CollapseBlockPrivate, { header: (0, jsx_runtime_1.jsx)(BaseItem_1.BaseItem, { ...option, id: id, disabled: disabled, open: isOpen, expandIcon: isOpen ? (0, jsx_runtime_1.jsx)(system_1.ChevronUpSVG, {}) : (0, jsx_runtime_1.jsx)(system_1.ChevronDownSVG, {}), onExpandIconClick: handleExpandIconClick, isParentNode: true, onOpenNestedList: handleKeyDown, checked: checked, indeterminate: indeterminate, onSelect: !disabled ? handleOnSelect : undefined }), expanded: isOpen, "data-test-id": `${constants_1.TEST_IDS.accordionItem}-${id}`, children: (0, jsx_runtime_1.jsx)(contexts_1.CollapseLevelContext.Provider, { value: { level: level + 1 }, children: itemsJSX }) }));
30
+ return ((0, jsx_runtime_1.jsx)(helperComponents_1.CollapseBlockPrivate, { header: (0, jsx_runtime_1.jsx)(BaseItem_1.BaseItem, { ...option, id: id, disabled: disabled, open: isOpen, expandIcon: isOpen ? (0, jsx_runtime_1.jsx)(system_1.ChevronUpSVG, {}) : (0, jsx_runtime_1.jsx)(system_1.ChevronDownSVG, {}), onToggleExpand: handleToggle, isParentNode: true, onOpenNestedList: handleToggle, checked: checked, indeterminate: indeterminate, onSelect: !disabled ? handleOnSelect : undefined }), expanded: isOpen, "data-test-id": `${constants_1.TEST_IDS.accordionItem}-${id}`, children: (0, jsx_runtime_1.jsx)(contexts_1.CollapseLevelContext.Provider, { value: { level: level + 1 }, children: itemsJSX }) }));
37
31
  }
@@ -1,4 +1,4 @@
1
- import { KeyboardEvent, MouseEvent, ReactNode } from 'react';
1
+ import { KeyboardEvent, ReactNode } from 'react';
2
2
  import { FlattenBaseItem } from '../types';
3
3
  type AllBaseItemProps = FlattenBaseItem & {
4
4
  expandIcon?: ReactNode;
@@ -8,11 +8,11 @@ type AllBaseItemProps = FlattenBaseItem & {
8
8
  isParentNode?: boolean;
9
9
  onOpenNestedList?(e?: KeyboardEvent<HTMLElement>): void;
10
10
  /**
11
- * Клик по явной кнопке-триггеру `groupIndicator` (шеврон справа), открывающей/закрывающей
12
- * вложенный список. Если передан шеврон рендерится как отдельная интерактивная кнопка
13
- * со своим state-layer'ом, а клик по телу строки больше не переключает раскрытие.
11
+ * Переключение раскрытия вложенного списка. Триггер — вся строка целиком (клик или
12
+ * Enter/Space), шеврон `groupIndicator` при этом остаётся неинтерактивным индикатором
13
+ * состояния.
14
14
  */
15
- onExpandIconClick?(e: MouseEvent<HTMLElement>): void;
15
+ onToggleExpand?(): void;
16
16
  /**
17
17
  * Слот ручки drag&drop (Figma `centeredWrapper`) — рендерится первым в строке, перед
18
18
  * маркером/чекбоксом. Интерактивность (обработчики `@dnd-kit`) и содержимое (иконка)
@@ -20,5 +20,5 @@ type AllBaseItemProps = FlattenBaseItem & {
20
20
  */
21
21
  dragHandle?: ReactNode;
22
22
  };
23
- export declare function BaseItem({ beforeContent, afterContent, content, onClick, onMouseDown, id, expandIcon, disabled, open, itemRef, switch: switchProp, showSwitchIcon, onKeyDown, onFocus, indeterminate, checked: checkedProp, onSelect, onOpenNestedList, onExpandIconClick, isParentNode, className, inactive, itemWrapRender, dragHandle, ...rest }: AllBaseItemProps): string | number | boolean | Iterable<ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
23
+ export declare function BaseItem({ beforeContent, afterContent, content, onClick, onMouseDown, id, expandIcon, disabled, open, itemRef, switch: switchProp, showSwitchIcon, onKeyDown, onFocus, indeterminate, checked: checkedProp, onSelect, onOpenNestedList, onToggleExpand, isParentNode, className, inactive, itemWrapRender, dragHandle, ...rest }: AllBaseItemProps): string | number | boolean | Iterable<ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
24
24
  export {};
@@ -15,7 +15,7 @@ const styles_module_scss_1 = __importDefault(require('../styles.module.css'));
15
15
  const utils_2 = require("../utils");
16
16
  const constants_2 = require("./constants");
17
17
  const styles_module_scss_2 = __importDefault(require('./styles.module.css'));
18
- function BaseItem({ beforeContent, afterContent, content, onClick, onMouseDown, id, expandIcon, disabled, open, itemRef, switch: switchProp, showSwitchIcon, onKeyDown, onFocus, indeterminate, checked: checkedProp, onSelect, onOpenNestedList, onExpandIconClick, isParentNode, className, inactive, itemWrapRender, dragHandle, ...rest }) {
18
+ function BaseItem({ beforeContent, afterContent, content, onClick, onMouseDown, id, expandIcon, disabled, open, itemRef, switch: switchProp, showSwitchIcon, onKeyDown, onFocus, indeterminate, checked: checkedProp, onSelect, onOpenNestedList, onToggleExpand, isParentNode, className, inactive, itemWrapRender, dragHandle, ...rest }) {
19
19
  const interactive = !inactive;
20
20
  const { size = constants_1.DEFAULT_SIZE, marker, contentRender, firstItemId, focusFlattenItems } = (0, contexts_1.useNewListContext)();
21
21
  const { level = 0 } = (0, contexts_1.useCollapseLevelContext)();
@@ -50,7 +50,12 @@ function BaseItem({ beforeContent, afterContent, content, onClick, onMouseDown,
50
50
  // `onClick` в публичном API типизирован под `MouseEvent` (это семантически клик по
51
51
  // пункту меню), поэтому keyboard-событие пробрасывается под тем же типом.
52
52
  onClick?.(e);
53
- if (interactive && !isParentNode) {
53
+ if (isParentNode) {
54
+ // Раскрытие вложенного списка триггерит вся строка целиком: шеврон `groupIndicator` —
55
+ // только индикатор состояния, собственной интерактивности у него нет.
56
+ onToggleExpand?.();
57
+ }
58
+ else if (interactive) {
54
59
  handleChange();
55
60
  }
56
61
  if (!isSelectionMultiple && closeDroplistOnItemClick) {
@@ -66,13 +71,21 @@ function BaseItem({ beforeContent, afterContent, content, onClick, onMouseDown,
66
71
  return;
67
72
  }
68
73
  if (e.key === 'Enter' || e.key === ' ') {
69
- if (isSelectionMultiple && isParentNode && onSelect) {
70
- onSelect();
74
+ if (isParentNode) {
75
+ // В multiple-режиме Enter/Space на группе выбирает всю ветку, иначе — раскрывает её.
76
+ if (isSelectionMultiple && onSelect) {
77
+ onSelect();
78
+ }
79
+ else {
80
+ onToggleExpand?.();
81
+ }
82
+ }
83
+ else {
84
+ // Повторяем клик по элементу: `handleItemClick` сам вызывает `handleChange` /
85
+ // `closeDroplist`. Отдельный `handleChange` здесь не нужен — иначе в multiple-режиме
86
+ // выбор переключался бы дважды (toggle + откат) и Enter не менял бы состояние.
87
+ handleItemClick(e);
71
88
  }
72
- // Повторяем клик по элементу: `handleItemClick` сам вызывает `handleChange` /
73
- // `closeDroplist`. Отдельный `handleChange` здесь не нужен — иначе в multiple-режиме
74
- // выбор переключался бы дважды (toggle + откат) и Enter не менял бы состояние.
75
- !isParentNode && handleItemClick(e);
76
89
  e.stopPropagation();
77
90
  e.preventDefault();
78
91
  }
@@ -82,16 +95,6 @@ function BaseItem({ beforeContent, afterContent, content, onClick, onMouseDown,
82
95
  e.stopPropagation();
83
96
  }
84
97
  };
85
- const handleExpandIconClick = (e) => {
86
- if (disabled) {
87
- return;
88
- }
89
- // Триггер раскрытия изолирован от клика по строке: всплытие останавливаем,
90
- // чтобы клик по шеврону не активировал выбор/навигацию пункта.
91
- e.stopPropagation();
92
- e.preventDefault();
93
- onExpandIconClick?.(e);
94
- };
95
98
  const props = (0, utils_1.extractSupportProps)(rest);
96
99
  let contentNode;
97
100
  if (content && (0, utils_2.isContentItem)(content)) {
@@ -108,9 +111,7 @@ function BaseItem({ beforeContent, afterContent, content, onClick, onMouseDown,
108
111
  const stateLayerState = isSelectionSingle && isChecked && !switchProp && !isParentNode
109
112
  ? 'activatedOnBackground'
110
113
  : 'emptyNeutralOnBackground';
111
- const itemJSX = ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.itemWrapper, styles_module_scss_2.default.innerWrapper, className), "data-inactive": inactive || undefined, "data-disabled": disabled || undefined, "data-variant": mode || undefined, "data-checked": (isParentNode && isChecked) || (!isParentNode && isChecked && !switchProp) || undefined, children: [(0, jsx_runtime_1.jsx)("span", { className: styles_module_scss_1.default.stateLayer, "aria-hidden": true, "data-state": stateLayerState }), (0, jsx_runtime_1.jsxs)("li", { "data-type": 'outside', role: 'menuitem', "data-test-id": props['data-test-id'] || `${constants_1.TEST_IDS.baseItem}_${id}`, ref: itemRef, className: (0, classnames_1.default)(styles_module_scss_1.default.listItem, styles_module_scss_2.default.droplistItem), "data-size": size, onClick: handleItemClick, onMouseDown: handleItemMouseDown, tabIndex: firstItemId && id === focusFlattenItems[firstItemId]?.originalId ? 0 : -1, "data-non-pointer": (inactive && !onClick) || undefined, "data-variant": mode || undefined, "data-open": open || undefined, onKeyDown: handleItemKeyDown, onFocus: handleItemFocus, style: { '--level': level }, "data-level-one": level === 1 || undefined, "data-level-more-one": level > 1 || undefined, "data-checked": (isParentNode && (indeterminate || isChecked)) || (isChecked && !switchProp) || undefined, children: [dragHandle && (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_2.default.dragHandle, children: dragHandle }), !switchProp && isSelectionSingle && marker && !isParentNode && interactive && ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_2.default.markerContainer, "data-test-id": constants_1.TEST_IDS.baseItemMarker })), !switchProp && isSelectionMultiple && interactive && ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_2.default.checkbox, children: (0, jsx_runtime_1.jsx)(toggles_1.Checkbox, { size: constants_2.TOGGLE_SIZE_MAP[size], disabled: disabled, tabIndex: -1, onChange: isParentNode ? handleCheckboxChange : undefined, checked: isChecked, "data-test-id": constants_1.TEST_IDS.baseItemCheckbox, onClick: handleCheckboxClick, indeterminate: indeterminate }) })), beforeContent && (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_2.default.beforeContent, children: beforeContent }), contentNode, afterContent && (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_2.default.afterContent, children: afterContent }), switchProp && interactive && ((0, jsx_runtime_1.jsx)(toggles_1.Switch, { size: constants_2.TOGGLE_SIZE_MAP[size], disabled: disabled, checked: isChecked, "data-test-id": constants_1.TEST_IDS.baseItemSwitch, showIcon: showSwitchIcon })), !switchProp &&
112
- expandIcon &&
113
- (onExpandIconClick ? ((0, jsx_runtime_1.jsxs)("button", { type: 'button', className: styles_module_scss_2.default.groupIndicator, "data-size": size, "data-open": open || undefined, "data-test-id": constants_1.TEST_IDS.groupIndicator, tabIndex: -1, disabled: disabled, onClick: handleExpandIconClick, onMouseDown: e => e.stopPropagation(), children: [(0, jsx_runtime_1.jsx)("span", { className: styles_module_scss_2.default.groupIndicatorState, "aria-hidden": true }), expandIcon] })) : ((0, jsx_runtime_1.jsx)("span", { className: styles_module_scss_2.default.expandableIcon, children: expandIcon })))] })] }));
114
+ const itemJSX = ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.itemWrapper, styles_module_scss_2.default.innerWrapper, className), "data-inactive": inactive || undefined, "data-disabled": disabled || undefined, "data-variant": mode || undefined, "data-checked": (isParentNode && isChecked) || (!isParentNode && isChecked && !switchProp) || undefined, children: [(0, jsx_runtime_1.jsx)("span", { className: styles_module_scss_1.default.stateLayer, "aria-hidden": true, "data-state": stateLayerState }), (0, jsx_runtime_1.jsxs)("li", { "data-type": 'outside', role: 'menuitem', "data-test-id": props['data-test-id'] || `${constants_1.TEST_IDS.baseItem}_${id}`, ref: itemRef, className: (0, classnames_1.default)(styles_module_scss_1.default.listItem, styles_module_scss_2.default.droplistItem), "data-size": size, onClick: handleItemClick, onMouseDown: handleItemMouseDown, tabIndex: firstItemId && id === focusFlattenItems[firstItemId]?.originalId ? 0 : -1, "data-non-pointer": (inactive && !onClick) || undefined, "data-variant": mode || undefined, "data-open": open || undefined, "aria-expanded": isParentNode ? Boolean(open) : undefined, onKeyDown: handleItemKeyDown, onFocus: handleItemFocus, style: { '--level': level }, "data-level-one": level === 1 || undefined, "data-level-more-one": level > 1 || undefined, "data-checked": (isParentNode && (indeterminate || isChecked)) || (isChecked && !switchProp) || undefined, children: [dragHandle && (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_2.default.dragHandle, children: dragHandle }), !switchProp && isSelectionSingle && marker && !isParentNode && interactive && ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_2.default.markerContainer, "data-test-id": constants_1.TEST_IDS.baseItemMarker })), !switchProp && isSelectionMultiple && interactive && ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_2.default.checkbox, children: (0, jsx_runtime_1.jsx)(toggles_1.Checkbox, { size: constants_2.TOGGLE_SIZE_MAP[size], disabled: disabled, tabIndex: -1, onChange: isParentNode ? handleCheckboxChange : undefined, checked: isChecked, "data-test-id": constants_1.TEST_IDS.baseItemCheckbox, onClick: handleCheckboxClick, indeterminate: indeterminate }) })), beforeContent && (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_2.default.beforeContent, children: beforeContent }), contentNode, afterContent && (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_2.default.afterContent, children: afterContent }), switchProp && interactive && ((0, jsx_runtime_1.jsx)(toggles_1.Switch, { size: constants_2.TOGGLE_SIZE_MAP[size], disabled: disabled, checked: isChecked, "data-test-id": constants_1.TEST_IDS.baseItemSwitch, showIcon: showSwitchIcon })), !switchProp && expandIcon && ((0, jsx_runtime_1.jsx)("span", { className: styles_module_scss_2.default.groupIndicator, "data-open": open || undefined, "data-test-id": constants_1.TEST_IDS.groupIndicator, "aria-hidden": true, children: expandIcon }))] })] }));
114
115
  if (!itemWrapRender) {
115
116
  return itemJSX;
116
117
  }
@@ -56,62 +56,22 @@
56
56
  color:var(--sn-theme-color-available-version-textSecondary, var(--sn-brand-color-text-version-secondary, rgba(65, 66, 78, 0.8)));
57
57
  }
58
58
 
59
- .expandableIcon{
60
- display:flex;
61
- align-items:center;
62
- color:var(--sn-theme-color-available-version-textSecondary, var(--sn-brand-color-text-version-secondary, rgba(65, 66, 78, 0.8)));
63
- fill:currentcolor;
64
- }
65
-
66
59
  .groupIndicator{
67
- cursor:pointer;
68
- position:relative;
60
+ pointer-events:none;
69
61
  display:flex;
70
62
  flex-shrink:0;
71
63
  align-items:center;
72
64
  justify-content:center;
73
- padding:0;
74
- border:none;
75
- border-radius:var(--sn-primitive-dimension-8, 8px);
76
65
  color:var(--sn-theme-color-available-version-textSecondary, var(--sn-brand-color-text-version-secondary, rgba(65, 66, 78, 0.8)));
77
- -webkit-appearance:none;
78
- -moz-appearance:none;
79
- appearance:none;
80
- background:transparent;
81
- outline:none;
66
+ fill:currentcolor;
82
67
  }
83
68
  .groupIndicator svg{
84
- position:relative;
85
- z-index:1;
86
- width:var(--sn-density-size-icon-xs, var(--sn-primitive-dimension-24, 24px));
87
- height:var(--sn-density-size-icon-xs, var(--sn-primitive-dimension-24, 24px));
88
- }
89
- .groupIndicator:focus-visible{
90
- outline:var(--sn-theme-color-available-complementary, #000000) solid var(--sn-primitive-strokeWeight-strokeSemiBold, 2px);
91
- outline-offset:calc(0px - var(--sn-primitive-strokeWeight-strokeSemiBold, 2px));
69
+ width:100%;
70
+ height:100%;
92
71
  }
93
72
  .groupIndicator[data-open]{
94
73
  border-radius:var(--sn-primitive-dimension-2, 2px);
95
74
  color:var(--sn-theme-color-available-version-textMain, var(--sn-brand-color-text-version-main, #41424e));
96
- }
97
-
98
- .groupIndicatorState{
99
- pointer-events:none;
100
- position:absolute;
101
- inset:0;
102
- border-radius:inherit;
103
- background:transparent;
104
- }
105
-
106
- .groupIndicator:hover > .groupIndicatorState{
107
- background:var(--sn-theme-color-material-stateLayer-neutral-onBackground-hovered-filled, var(--sn-brand-color-state-neutral-forLightMode-hovered-background, rgba(188, 195, 220, 0.1490196078)));
108
- }
109
-
110
- .groupIndicator:active > .groupIndicatorState{
111
- background:var(--sn-theme-color-material-stateLayer-neutral-onBackground-pressed-filled, var(--sn-brand-color-state-neutral-forLightMode-pressed-background, rgba(188, 195, 220, 0.3019607843)));
112
- }
113
-
114
- .groupIndicator[data-open] > .groupIndicatorState{
115
75
  background:var(--sn-theme-color-neutral-decorTransparent, var(--sn-brand-color-neutral-transparent-forLightMode, rgba(124, 131, 182, 0.0784313725)));
116
76
  }
117
77
 
@@ -157,10 +117,6 @@
157
117
  padding-top:var(--sn-list-anatomy-size-s-listItem-centeredWrapper-paddingVertical, var(--sn-density-spacing-control-container-vertical-s, var(--sn-primitive-dimension-6, 6px)));
158
118
  padding-bottom:var(--sn-list-anatomy-size-s-listItem-centeredWrapper-paddingVertical, var(--sn-density-spacing-control-container-vertical-s, var(--sn-primitive-dimension-6, 6px)));
159
119
  }
160
- .droplistItem[data-size=s] .expandableIcon{
161
- width:var(--sn-density-size-icon-xs, var(--sn-primitive-dimension-24, 24px));
162
- height:var(--sn-density-size-icon-xs, var(--sn-primitive-dimension-24, 24px));
163
- }
164
120
  .droplistItem[data-size=s] .groupIndicator{
165
121
  width:var(--sn-density-size-icon-xs, var(--sn-primitive-dimension-24, 24px));
166
122
  height:var(--sn-density-size-icon-xs, var(--sn-primitive-dimension-24, 24px));
@@ -204,10 +160,6 @@
204
160
  padding-top:var(--sn-list-anatomy-size-m-listItem-centeredWrapper-paddingVertical, var(--sn-density-spacing-control-container-vertical-m, var(--sn-primitive-dimension-8, 8px)));
205
161
  padding-bottom:var(--sn-list-anatomy-size-m-listItem-centeredWrapper-paddingVertical, var(--sn-density-spacing-control-container-vertical-m, var(--sn-primitive-dimension-8, 8px)));
206
162
  }
207
- .droplistItem[data-size=m] .expandableIcon{
208
- width:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
209
- height:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
210
- }
211
163
  .droplistItem[data-size=m] .groupIndicator{
212
164
  width:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
213
165
  height:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
@@ -251,10 +203,6 @@
251
203
  padding-top:var(--sn-list-anatomy-size-l-listItem-centeredWrapper-paddingVertical, var(--sn-density-spacing-control-container-vertical-l, var(--sn-primitive-dimension-10, 10px)));
252
204
  padding-bottom:var(--sn-list-anatomy-size-l-listItem-centeredWrapper-paddingVertical, var(--sn-density-spacing-control-container-vertical-l, var(--sn-primitive-dimension-10, 10px)));
253
205
  }
254
- .droplistItem[data-size=l] .expandableIcon{
255
- width:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
256
- height:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
257
- }
258
206
  .droplistItem[data-size=l] .groupIndicator{
259
207
  width:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
260
208
  height:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
@@ -280,11 +228,7 @@
280
228
  cursor:inherit;
281
229
  }
282
230
 
283
- .innerWrapper[data-disabled] .expandableIcon{
284
- color:var(--sn-theme-color-available-version-textDisabled, var(--sn-brand-color-text-version-disabled, rgba(65, 66, 78, 0.3490196078)));
285
- }
286
231
  .innerWrapper[data-disabled] .groupIndicator{
287
- cursor:not-allowed;
288
232
  color:var(--sn-theme-color-available-version-textDisabled, var(--sn-brand-color-text-version-disabled, rgba(65, 66, 78, 0.3490196078)));
289
233
  }
290
234
  .innerWrapper[data-disabled] .beforeContent,
@@ -67,5 +67,5 @@ function NextListItem({ items, placement = 'right-start', id, scroll, scrollRef,
67
67
  }, scrollRef: scrollRef, limitedScrollHeight: true, untouchableScrollbars: untouchableScrollbars, loading: loading, dataError: dataError, dataFiltered: dataFiltered }), trigger: 'hover', open: isOpen || open, onOpenChange: value => {
68
68
  setOpen(value);
69
69
  onSublistOpenChanged?.(value, id);
70
- }, placement: placement, widthStrategy: 'auto', children: (0, jsx_runtime_1.jsx)(BaseItem_1.BaseItem, { ...option, disabled: disabled, open: isOpen || open, expandIcon: (0, jsx_runtime_1.jsx)(system_1.ChevronRightSVG, {}), id: id, isParentNode: true, indeterminate: indeterminate, checked: checked, onOpenNestedList: openSublist, onExpandIconClick: openSublist, onSelect: handleOnSelect }) }));
70
+ }, placement: placement, widthStrategy: 'auto', children: (0, jsx_runtime_1.jsx)(BaseItem_1.BaseItem, { ...option, disabled: disabled, open: isOpen || open, expandIcon: (0, jsx_runtime_1.jsx)(system_1.ChevronRightSVG, {}), id: id, isParentNode: true, indeterminate: indeterminate, checked: checked, onOpenNestedList: openSublist, onToggleExpand: openSublist, onSelect: handleOnSelect }) }));
71
71
  }
@@ -56,62 +56,22 @@
56
56
  color:var(--sn-theme-color-available-version-textSecondary, var(--sn-brand-color-text-version-secondary, rgba(65, 66, 78, 0.8)));
57
57
  }
58
58
 
59
- .expandableIcon{
60
- display:flex;
61
- align-items:center;
62
- color:var(--sn-theme-color-available-version-textSecondary, var(--sn-brand-color-text-version-secondary, rgba(65, 66, 78, 0.8)));
63
- fill:currentcolor;
64
- }
65
-
66
59
  .groupIndicator{
67
- cursor:pointer;
68
- position:relative;
60
+ pointer-events:none;
69
61
  display:flex;
70
62
  flex-shrink:0;
71
63
  align-items:center;
72
64
  justify-content:center;
73
- padding:0;
74
- border:none;
75
- border-radius:var(--sn-primitive-dimension-8, 8px);
76
65
  color:var(--sn-theme-color-available-version-textSecondary, var(--sn-brand-color-text-version-secondary, rgba(65, 66, 78, 0.8)));
77
- -webkit-appearance:none;
78
- -moz-appearance:none;
79
- appearance:none;
80
- background:transparent;
81
- outline:none;
66
+ fill:currentcolor;
82
67
  }
83
68
  .groupIndicator svg{
84
- position:relative;
85
- z-index:1;
86
- width:var(--sn-density-size-icon-xs, var(--sn-primitive-dimension-24, 24px));
87
- height:var(--sn-density-size-icon-xs, var(--sn-primitive-dimension-24, 24px));
88
- }
89
- .groupIndicator:focus-visible{
90
- outline:var(--sn-theme-color-available-complementary, #000000) solid var(--sn-primitive-strokeWeight-strokeSemiBold, 2px);
91
- outline-offset:calc(0px - var(--sn-primitive-strokeWeight-strokeSemiBold, 2px));
69
+ width:100%;
70
+ height:100%;
92
71
  }
93
72
  .groupIndicator[data-open]{
94
73
  border-radius:var(--sn-primitive-dimension-2, 2px);
95
74
  color:var(--sn-theme-color-available-version-textMain, var(--sn-brand-color-text-version-main, #41424e));
96
- }
97
-
98
- .groupIndicatorState{
99
- pointer-events:none;
100
- position:absolute;
101
- inset:0;
102
- border-radius:inherit;
103
- background:transparent;
104
- }
105
-
106
- .groupIndicator:hover > .groupIndicatorState{
107
- background:var(--sn-theme-color-material-stateLayer-neutral-onBackground-hovered-filled, var(--sn-brand-color-state-neutral-forLightMode-hovered-background, rgba(188, 195, 220, 0.1490196078)));
108
- }
109
-
110
- .groupIndicator:active > .groupIndicatorState{
111
- background:var(--sn-theme-color-material-stateLayer-neutral-onBackground-pressed-filled, var(--sn-brand-color-state-neutral-forLightMode-pressed-background, rgba(188, 195, 220, 0.3019607843)));
112
- }
113
-
114
- .groupIndicator[data-open] > .groupIndicatorState{
115
75
  background:var(--sn-theme-color-neutral-decorTransparent, var(--sn-brand-color-neutral-transparent-forLightMode, rgba(124, 131, 182, 0.0784313725)));
116
76
  }
117
77
 
@@ -157,10 +117,6 @@
157
117
  padding-top:var(--sn-list-anatomy-size-s-listItem-centeredWrapper-paddingVertical, var(--sn-density-spacing-control-container-vertical-s, var(--sn-primitive-dimension-6, 6px)));
158
118
  padding-bottom:var(--sn-list-anatomy-size-s-listItem-centeredWrapper-paddingVertical, var(--sn-density-spacing-control-container-vertical-s, var(--sn-primitive-dimension-6, 6px)));
159
119
  }
160
- .droplistItem[data-size=s] .expandableIcon{
161
- width:var(--sn-density-size-icon-xs, var(--sn-primitive-dimension-24, 24px));
162
- height:var(--sn-density-size-icon-xs, var(--sn-primitive-dimension-24, 24px));
163
- }
164
120
  .droplistItem[data-size=s] .groupIndicator{
165
121
  width:var(--sn-density-size-icon-xs, var(--sn-primitive-dimension-24, 24px));
166
122
  height:var(--sn-density-size-icon-xs, var(--sn-primitive-dimension-24, 24px));
@@ -204,10 +160,6 @@
204
160
  padding-top:var(--sn-list-anatomy-size-m-listItem-centeredWrapper-paddingVertical, var(--sn-density-spacing-control-container-vertical-m, var(--sn-primitive-dimension-8, 8px)));
205
161
  padding-bottom:var(--sn-list-anatomy-size-m-listItem-centeredWrapper-paddingVertical, var(--sn-density-spacing-control-container-vertical-m, var(--sn-primitive-dimension-8, 8px)));
206
162
  }
207
- .droplistItem[data-size=m] .expandableIcon{
208
- width:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
209
- height:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
210
- }
211
163
  .droplistItem[data-size=m] .groupIndicator{
212
164
  width:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
213
165
  height:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
@@ -251,10 +203,6 @@
251
203
  padding-top:var(--sn-list-anatomy-size-l-listItem-centeredWrapper-paddingVertical, var(--sn-density-spacing-control-container-vertical-l, var(--sn-primitive-dimension-10, 10px)));
252
204
  padding-bottom:var(--sn-list-anatomy-size-l-listItem-centeredWrapper-paddingVertical, var(--sn-density-spacing-control-container-vertical-l, var(--sn-primitive-dimension-10, 10px)));
253
205
  }
254
- .droplistItem[data-size=l] .expandableIcon{
255
- width:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
256
- height:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
257
- }
258
206
  .droplistItem[data-size=l] .groupIndicator{
259
207
  width:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
260
208
  height:var(--sn-density-size-icon-s, var(--sn-primitive-dimension-24, 24px));
@@ -280,11 +228,7 @@
280
228
  cursor:inherit;
281
229
  }
282
230
 
283
- .innerWrapper[data-disabled] .expandableIcon{
284
- color:var(--sn-theme-color-available-version-textDisabled, var(--sn-brand-color-text-version-disabled, rgba(65, 66, 78, 0.3490196078)));
285
- }
286
231
  .innerWrapper[data-disabled] .groupIndicator{
287
- cursor:not-allowed;
288
232
  color:var(--sn-theme-color-available-version-textDisabled, var(--sn-brand-color-text-version-disabled, rgba(65, 66, 78, 0.3490196078)));
289
233
  }
290
234
  .innerWrapper[data-disabled] .beforeContent,
@@ -19,16 +19,10 @@ export function AccordionItem({ id, disabled, allChildIds, items, ...option }) {
19
19
  const isOpen = Boolean(openCollapseItems.includes(id ?? ''));
20
20
  const checked = Boolean((indeterminate && !isOpen && isSelectionSingle && value && allChildIds.includes(value)) ||
21
21
  (isSelectionMultiple && checkedProp));
22
- const handleKeyDown = useCallback(() => {
22
+ // Раскрытие переключает вся строка целиком: клик/Enter по телу и ArrowRight с клавиатуры.
23
+ const handleToggle = useCallback(() => {
23
24
  toggleOpenCollapseItem?.(id ?? '');
24
25
  }, [id, toggleOpenCollapseItem]);
25
26
  const itemsJSX = useRenderItems(items);
26
- // Раскрытие привязано к явной кнопке-триггеру `groupIndicator` (шеврон справа) и
27
- // ТОЛЬКО переключает collapse — `option.onClick` сюда не зовём, иначе у потребителя,
28
- // который вешает на onClick собственный toggle, выходит двойное переключение.
29
- // Клик по телу строки (выбор/навигация) идёт через `option.onClick` в BaseItem.
30
- const handleExpandIconClick = () => {
31
- toggleOpenCollapseItem?.(id ?? '');
32
- };
33
- return (_jsx(CollapseBlockPrivate, { header: _jsx(BaseItem, { ...option, id: id, disabled: disabled, open: isOpen, expandIcon: isOpen ? _jsx(ChevronUpSVG, {}) : _jsx(ChevronDownSVG, {}), onExpandIconClick: handleExpandIconClick, isParentNode: true, onOpenNestedList: handleKeyDown, checked: checked, indeterminate: indeterminate, onSelect: !disabled ? handleOnSelect : undefined }), expanded: isOpen, "data-test-id": `${TEST_IDS.accordionItem}-${id}`, children: _jsx(CollapseLevelContext.Provider, { value: { level: level + 1 }, children: itemsJSX }) }));
27
+ return (_jsx(CollapseBlockPrivate, { header: _jsx(BaseItem, { ...option, id: id, disabled: disabled, open: isOpen, expandIcon: isOpen ? _jsx(ChevronUpSVG, {}) : _jsx(ChevronDownSVG, {}), onToggleExpand: handleToggle, isParentNode: true, onOpenNestedList: handleToggle, checked: checked, indeterminate: indeterminate, onSelect: !disabled ? handleOnSelect : undefined }), expanded: isOpen, "data-test-id": `${TEST_IDS.accordionItem}-${id}`, children: _jsx(CollapseLevelContext.Provider, { value: { level: level + 1 }, children: itemsJSX }) }));
34
28
  }
@@ -1,4 +1,4 @@
1
- import { KeyboardEvent, MouseEvent, ReactNode } from 'react';
1
+ import { KeyboardEvent, ReactNode } from 'react';
2
2
  import { FlattenBaseItem } from '../types.js';
3
3
  type AllBaseItemProps = FlattenBaseItem & {
4
4
  expandIcon?: ReactNode;
@@ -8,11 +8,11 @@ type AllBaseItemProps = FlattenBaseItem & {
8
8
  isParentNode?: boolean;
9
9
  onOpenNestedList?(e?: KeyboardEvent<HTMLElement>): void;
10
10
  /**
11
- * Клик по явной кнопке-триггеру `groupIndicator` (шеврон справа), открывающей/закрывающей
12
- * вложенный список. Если передан шеврон рендерится как отдельная интерактивная кнопка
13
- * со своим state-layer'ом, а клик по телу строки больше не переключает раскрытие.
11
+ * Переключение раскрытия вложенного списка. Триггер — вся строка целиком (клик или
12
+ * Enter/Space), шеврон `groupIndicator` при этом остаётся неинтерактивным индикатором
13
+ * состояния.
14
14
  */
15
- onExpandIconClick?(e: MouseEvent<HTMLElement>): void;
15
+ onToggleExpand?(): void;
16
16
  /**
17
17
  * Слот ручки drag&drop (Figma `centeredWrapper`) — рендерится первым в строке, перед
18
18
  * маркером/чекбоксом. Интерактивность (обработчики `@dnd-kit`) и содержимое (иконка)
@@ -20,5 +20,5 @@ type AllBaseItemProps = FlattenBaseItem & {
20
20
  */
21
21
  dragHandle?: ReactNode;
22
22
  };
23
- export declare function BaseItem({ beforeContent, afterContent, content, onClick, onMouseDown, id, expandIcon, disabled, open, itemRef, switch: switchProp, showSwitchIcon, onKeyDown, onFocus, indeterminate, checked: checkedProp, onSelect, onOpenNestedList, onExpandIconClick, isParentNode, className, inactive, itemWrapRender, dragHandle, ...rest }: AllBaseItemProps): string | number | boolean | Iterable<ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
23
+ export declare function BaseItem({ beforeContent, afterContent, content, onClick, onMouseDown, id, expandIcon, disabled, open, itemRef, switch: switchProp, showSwitchIcon, onKeyDown, onFocus, indeterminate, checked: checkedProp, onSelect, onOpenNestedList, onToggleExpand, isParentNode, className, inactive, itemWrapRender, dragHandle, ...rest }: AllBaseItemProps): string | number | boolean | Iterable<ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
24
24
  export {};
@@ -9,7 +9,7 @@ import commonStyles from '../styles.module.css';
9
9
  import { isContentItem, isPrimitiveContent } from '../utils.js';
10
10
  import { TOGGLE_SIZE_MAP } from './constants.js';
11
11
  import styles from './styles.module.css';
12
- export function BaseItem({ beforeContent, afterContent, content, onClick, onMouseDown, id, expandIcon, disabled, open, itemRef, switch: switchProp, showSwitchIcon, onKeyDown, onFocus, indeterminate, checked: checkedProp, onSelect, onOpenNestedList, onExpandIconClick, isParentNode, className, inactive, itemWrapRender, dragHandle, ...rest }) {
12
+ export function BaseItem({ beforeContent, afterContent, content, onClick, onMouseDown, id, expandIcon, disabled, open, itemRef, switch: switchProp, showSwitchIcon, onKeyDown, onFocus, indeterminate, checked: checkedProp, onSelect, onOpenNestedList, onToggleExpand, isParentNode, className, inactive, itemWrapRender, dragHandle, ...rest }) {
13
13
  const interactive = !inactive;
14
14
  const { size = DEFAULT_SIZE, marker, contentRender, firstItemId, focusFlattenItems } = useNewListContext();
15
15
  const { level = 0 } = useCollapseLevelContext();
@@ -44,7 +44,12 @@ export function BaseItem({ beforeContent, afterContent, content, onClick, onMous
44
44
  // `onClick` в публичном API типизирован под `MouseEvent` (это семантически клик по
45
45
  // пункту меню), поэтому keyboard-событие пробрасывается под тем же типом.
46
46
  onClick?.(e);
47
- if (interactive && !isParentNode) {
47
+ if (isParentNode) {
48
+ // Раскрытие вложенного списка триггерит вся строка целиком: шеврон `groupIndicator` —
49
+ // только индикатор состояния, собственной интерактивности у него нет.
50
+ onToggleExpand?.();
51
+ }
52
+ else if (interactive) {
48
53
  handleChange();
49
54
  }
50
55
  if (!isSelectionMultiple && closeDroplistOnItemClick) {
@@ -60,13 +65,21 @@ export function BaseItem({ beforeContent, afterContent, content, onClick, onMous
60
65
  return;
61
66
  }
62
67
  if (e.key === 'Enter' || e.key === ' ') {
63
- if (isSelectionMultiple && isParentNode && onSelect) {
64
- onSelect();
68
+ if (isParentNode) {
69
+ // В multiple-режиме Enter/Space на группе выбирает всю ветку, иначе — раскрывает её.
70
+ if (isSelectionMultiple && onSelect) {
71
+ onSelect();
72
+ }
73
+ else {
74
+ onToggleExpand?.();
75
+ }
76
+ }
77
+ else {
78
+ // Повторяем клик по элементу: `handleItemClick` сам вызывает `handleChange` /
79
+ // `closeDroplist`. Отдельный `handleChange` здесь не нужен — иначе в multiple-режиме
80
+ // выбор переключался бы дважды (toggle + откат) и Enter не менял бы состояние.
81
+ handleItemClick(e);
65
82
  }
66
- // Повторяем клик по элементу: `handleItemClick` сам вызывает `handleChange` /
67
- // `closeDroplist`. Отдельный `handleChange` здесь не нужен — иначе в multiple-режиме
68
- // выбор переключался бы дважды (toggle + откат) и Enter не менял бы состояние.
69
- !isParentNode && handleItemClick(e);
70
83
  e.stopPropagation();
71
84
  e.preventDefault();
72
85
  }
@@ -76,16 +89,6 @@ export function BaseItem({ beforeContent, afterContent, content, onClick, onMous
76
89
  e.stopPropagation();
77
90
  }
78
91
  };
79
- const handleExpandIconClick = (e) => {
80
- if (disabled) {
81
- return;
82
- }
83
- // Триггер раскрытия изолирован от клика по строке: всплытие останавливаем,
84
- // чтобы клик по шеврону не активировал выбор/навигацию пункта.
85
- e.stopPropagation();
86
- e.preventDefault();
87
- onExpandIconClick?.(e);
88
- };
89
92
  const props = extractSupportProps(rest);
90
93
  let contentNode;
91
94
  if (content && isContentItem(content)) {
@@ -102,9 +105,7 @@ export function BaseItem({ beforeContent, afterContent, content, onClick, onMous
102
105
  const stateLayerState = isSelectionSingle && isChecked && !switchProp && !isParentNode
103
106
  ? 'activatedOnBackground'
104
107
  : 'emptyNeutralOnBackground';
105
- const itemJSX = (_jsxs("div", { className: cn(commonStyles.itemWrapper, styles.innerWrapper, className), "data-inactive": inactive || undefined, "data-disabled": disabled || undefined, "data-variant": mode || undefined, "data-checked": (isParentNode && isChecked) || (!isParentNode && isChecked && !switchProp) || undefined, children: [_jsx("span", { className: commonStyles.stateLayer, "aria-hidden": true, "data-state": stateLayerState }), _jsxs("li", { "data-type": 'outside', role: 'menuitem', "data-test-id": props['data-test-id'] || `${TEST_IDS.baseItem}_${id}`, ref: itemRef, className: cn(commonStyles.listItem, styles.droplistItem), "data-size": size, onClick: handleItemClick, onMouseDown: handleItemMouseDown, tabIndex: firstItemId && id === focusFlattenItems[firstItemId]?.originalId ? 0 : -1, "data-non-pointer": (inactive && !onClick) || undefined, "data-variant": mode || undefined, "data-open": open || undefined, onKeyDown: handleItemKeyDown, onFocus: handleItemFocus, style: { '--level': level }, "data-level-one": level === 1 || undefined, "data-level-more-one": level > 1 || undefined, "data-checked": (isParentNode && (indeterminate || isChecked)) || (isChecked && !switchProp) || undefined, children: [dragHandle && _jsx("div", { className: styles.dragHandle, children: dragHandle }), !switchProp && isSelectionSingle && marker && !isParentNode && interactive && (_jsx("div", { className: styles.markerContainer, "data-test-id": TEST_IDS.baseItemMarker })), !switchProp && isSelectionMultiple && interactive && (_jsx("div", { className: styles.checkbox, children: _jsx(Checkbox, { size: TOGGLE_SIZE_MAP[size], disabled: disabled, tabIndex: -1, onChange: isParentNode ? handleCheckboxChange : undefined, checked: isChecked, "data-test-id": TEST_IDS.baseItemCheckbox, onClick: handleCheckboxClick, indeterminate: indeterminate }) })), beforeContent && _jsx("div", { className: styles.beforeContent, children: beforeContent }), contentNode, afterContent && _jsx("div", { className: styles.afterContent, children: afterContent }), switchProp && interactive && (_jsx(Switch, { size: TOGGLE_SIZE_MAP[size], disabled: disabled, checked: isChecked, "data-test-id": TEST_IDS.baseItemSwitch, showIcon: showSwitchIcon })), !switchProp &&
106
- expandIcon &&
107
- (onExpandIconClick ? (_jsxs("button", { type: 'button', className: styles.groupIndicator, "data-size": size, "data-open": open || undefined, "data-test-id": TEST_IDS.groupIndicator, tabIndex: -1, disabled: disabled, onClick: handleExpandIconClick, onMouseDown: e => e.stopPropagation(), children: [_jsx("span", { className: styles.groupIndicatorState, "aria-hidden": true }), expandIcon] })) : (_jsx("span", { className: styles.expandableIcon, children: expandIcon })))] })] }));
108
+ const itemJSX = (_jsxs("div", { className: cn(commonStyles.itemWrapper, styles.innerWrapper, className), "data-inactive": inactive || undefined, "data-disabled": disabled || undefined, "data-variant": mode || undefined, "data-checked": (isParentNode && isChecked) || (!isParentNode && isChecked && !switchProp) || undefined, children: [_jsx("span", { className: commonStyles.stateLayer, "aria-hidden": true, "data-state": stateLayerState }), _jsxs("li", { "data-type": 'outside', role: 'menuitem', "data-test-id": props['data-test-id'] || `${TEST_IDS.baseItem}_${id}`, ref: itemRef, className: cn(commonStyles.listItem, styles.droplistItem), "data-size": size, onClick: handleItemClick, onMouseDown: handleItemMouseDown, tabIndex: firstItemId && id === focusFlattenItems[firstItemId]?.originalId ? 0 : -1, "data-non-pointer": (inactive && !onClick) || undefined, "data-variant": mode || undefined, "data-open": open || undefined, "aria-expanded": isParentNode ? Boolean(open) : undefined, onKeyDown: handleItemKeyDown, onFocus: handleItemFocus, style: { '--level': level }, "data-level-one": level === 1 || undefined, "data-level-more-one": level > 1 || undefined, "data-checked": (isParentNode && (indeterminate || isChecked)) || (isChecked && !switchProp) || undefined, children: [dragHandle && _jsx("div", { className: styles.dragHandle, children: dragHandle }), !switchProp && isSelectionSingle && marker && !isParentNode && interactive && (_jsx("div", { className: styles.markerContainer, "data-test-id": TEST_IDS.baseItemMarker })), !switchProp && isSelectionMultiple && interactive && (_jsx("div", { className: styles.checkbox, children: _jsx(Checkbox, { size: TOGGLE_SIZE_MAP[size], disabled: disabled, tabIndex: -1, onChange: isParentNode ? handleCheckboxChange : undefined, checked: isChecked, "data-test-id": TEST_IDS.baseItemCheckbox, onClick: handleCheckboxClick, indeterminate: indeterminate }) })), beforeContent && _jsx("div", { className: styles.beforeContent, children: beforeContent }), contentNode, afterContent && _jsx("div", { className: styles.afterContent, children: afterContent }), switchProp && interactive && (_jsx(Switch, { size: TOGGLE_SIZE_MAP[size], disabled: disabled, checked: isChecked, "data-test-id": TEST_IDS.baseItemSwitch, showIcon: showSwitchIcon })), !switchProp && expandIcon && (_jsx("span", { className: styles.groupIndicator, "data-open": open || undefined, "data-test-id": TEST_IDS.groupIndicator, "aria-hidden": true, children: expandIcon }))] })] }));
108
109
  if (!itemWrapRender) {
109
110
  return itemJSX;
110
111
  }