@snack-uikit/list 0.10.0 → 0.10.1-preview-69abc1d3.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/README.md +56 -24
- package/dist/components/Items/AccordionItem/AccordionItem.d.ts +4 -2
- package/dist/components/Items/AccordionItem/AccordionItem.js +18 -16
- package/dist/components/Items/BaseItem/BaseItem.d.ts +8 -4
- package/dist/components/Items/BaseItem/BaseItem.js +20 -14
- package/dist/components/Items/BaseItem/styles.module.css +9 -122
- package/dist/components/Items/GroupItem/GroupItem.d.ts +5 -1
- package/dist/components/Items/GroupItem/GroupItem.js +1 -3
- package/dist/components/Items/GroupSelectItem/GroupSelectItem.d.ts +4 -0
- package/dist/components/Items/GroupSelectItem/GroupSelectItem.js +18 -0
- package/dist/components/Items/GroupSelectItem/index.d.ts +1 -0
- package/dist/components/Items/GroupSelectItem/index.js +1 -0
- package/dist/components/Items/NextListItem/NextListItem.d.ts +6 -2
- package/dist/components/Items/NextListItem/NextListItem.js +53 -45
- package/dist/components/Items/NextListItem/constants.d.ts +2 -0
- package/dist/components/Items/NextListItem/constants.js +10 -0
- package/dist/components/Items/SearchItem/SearchItem.d.ts +3 -1
- package/dist/components/Items/SearchItem/SearchItem.js +5 -8
- package/dist/components/Items/hooks.d.ts +11 -10
- package/dist/components/Items/hooks.js +64 -46
- package/dist/components/Items/index.d.ts +1 -5
- package/dist/components/Items/index.js +1 -5
- package/dist/components/Items/types.d.ts +68 -34
- package/dist/components/Items/utils.d.ts +33 -16
- package/dist/components/Items/utils.js +132 -37
- package/dist/components/Lists/Droplist/DropList.d.ts +1 -1
- package/dist/components/Lists/Droplist/DropList.js +73 -50
- package/dist/components/Lists/List/List.d.ts +5 -9
- package/dist/components/Lists/List/List.js +56 -54
- package/dist/components/Lists/ListPrivate/ListPrivate.d.ts +6 -20
- package/dist/components/Lists/ListPrivate/ListPrivate.js +9 -8
- package/dist/components/Lists/ListPrivate/styles.module.css +1 -0
- package/dist/components/Lists/contexts/CollapseProvider.d.ts +13 -1
- package/dist/components/Lists/contexts/CollapseProvider.js +2 -0
- package/dist/components/Lists/contexts/FocusListProvider.d.ts +9 -0
- package/dist/components/Lists/contexts/FocusListProvider.js +5 -0
- package/dist/components/Lists/contexts/NewListProvider.d.ts +32 -0
- package/dist/components/Lists/contexts/{ListProvider.js → NewListProvider.js} +10 -5
- package/dist/components/Lists/contexts/SelectionProvider.d.ts +25 -15
- package/dist/components/Lists/contexts/SelectionProvider.js +17 -13
- package/dist/components/Lists/contexts/index.d.ts +2 -2
- package/dist/components/Lists/contexts/index.js +2 -2
- package/dist/components/Lists/hooks.d.ts +10 -12
- package/dist/components/Lists/hooks.js +73 -40
- package/dist/components/Lists/types.d.ts +12 -14
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.js +1 -1
- package/dist/helperComponents/ItemContent/ItemContent.d.ts +15 -0
- package/dist/helperComponents/ItemContent/ItemContent.js +23 -0
- package/dist/helperComponents/ItemContent/index.d.ts +1 -0
- package/dist/helperComponents/ItemContent/index.js +1 -0
- package/dist/helperComponents/ItemContent/styles.module.css +117 -0
- package/dist/helperComponents/ListEmptyState/ListEmptyState.d.ts +2 -2
- package/dist/helperComponents/ListEmptyState/ListEmptyState.js +13 -4
- package/dist/helperComponents/Separator/Separator.d.ts +8 -2
- package/dist/helperComponents/Separator/Separator.js +22 -2
- package/dist/helperComponents/Separator/constants.d.ts +2 -0
- package/dist/helperComponents/Separator/constants.js +5 -0
- package/dist/helperComponents/Separator/styles.module.css +20 -1
- package/dist/helperComponents/index.d.ts +1 -0
- package/dist/helperComponents/index.js +1 -0
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +15 -10
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/legacy/components/Items/hooks.d.ts +12 -0
- package/dist/legacy/components/Items/hooks.js +33 -0
- package/dist/legacy/components/index.d.ts +1 -0
- package/dist/legacy/components/index.js +1 -0
- package/dist/legacy/hooks.d.ts +5 -0
- package/dist/legacy/hooks.js +15 -0
- package/dist/legacy/index.d.ts +3 -0
- package/dist/legacy/index.js +3 -0
- package/dist/{utils.d.ts → legacy/utils.d.ts} +2 -3
- package/dist/{utils.js → legacy/utils.js} +4 -12
- package/dist/types.d.ts +1 -1
- package/package.json +14 -14
- package/src/components/Items/AccordionItem/AccordionItem.tsx +24 -20
- package/src/components/Items/BaseItem/BaseItem.tsx +48 -43
- package/src/components/Items/BaseItem/styles.module.scss +8 -70
- package/src/components/Items/GroupItem/GroupItem.tsx +5 -5
- package/src/components/Items/GroupSelectItem/GroupSelectItem.tsx +44 -0
- package/src/components/Items/GroupSelectItem/index.ts +1 -0
- package/src/components/Items/NextListItem/NextListItem.tsx +86 -85
- package/src/components/Items/NextListItem/constants.ts +12 -0
- package/src/components/Items/SearchItem/SearchItem.tsx +6 -8
- package/src/components/Items/hooks.tsx +86 -66
- package/src/components/Items/index.ts +1 -5
- package/src/components/Items/types.ts +91 -42
- package/src/components/Items/utils.ts +193 -52
- package/src/components/Lists/Droplist/DropList.tsx +151 -98
- package/src/components/Lists/List/List.tsx +124 -104
- package/src/components/Lists/ListPrivate/ListPrivate.tsx +19 -20
- package/src/components/Lists/ListPrivate/styles.module.scss +1 -0
- package/src/components/Lists/contexts/CollapseProvider.tsx +22 -0
- package/src/components/Lists/contexts/FocusListProvider.tsx +16 -0
- package/src/components/Lists/contexts/NewListProvider.tsx +54 -0
- package/src/components/Lists/contexts/SelectionProvider.tsx +56 -39
- package/src/components/Lists/contexts/index.ts +3 -2
- package/src/components/Lists/hooks.ts +124 -75
- package/src/components/Lists/types.ts +20 -24
- package/src/components/index.ts +16 -2
- package/src/helperComponents/ItemContent/ItemContent.tsx +63 -0
- package/src/helperComponents/ItemContent/index.ts +1 -0
- package/src/helperComponents/ItemContent/styles.module.scss +78 -0
- package/src/helperComponents/ListEmptyState/ListEmptyState.tsx +26 -12
- package/src/helperComponents/Separator/Separator.tsx +49 -3
- package/src/helperComponents/Separator/constants.ts +7 -0
- package/src/helperComponents/Separator/styles.module.scss +41 -1
- package/src/helperComponents/index.ts +1 -0
- package/src/hooks.ts +21 -15
- package/src/index.ts +5 -2
- package/src/legacy/components/Items/hooks.tsx +53 -0
- package/src/legacy/components/index.ts +1 -0
- package/src/legacy/hooks.ts +27 -0
- package/src/legacy/index.ts +3 -0
- package/src/{utils.ts → legacy/utils.ts} +4 -13
- package/src/types.ts +1 -1
- package/dist/components/Items/NextListItem/hooks.d.ts +0 -19
- package/dist/components/Items/NextListItem/hooks.js +0 -80
- package/dist/components/Lists/contexts/ListProvider.d.ts +0 -17
- package/dist/components/Lists/contexts/ParentListProvider.d.ts +0 -18
- package/dist/components/Lists/contexts/ParentListProvider.js +0 -12
- package/src/components/Items/NextListItem/hooks.ts +0 -105
- package/src/components/Lists/contexts/CollapseProvider.ts +0 -9
- package/src/components/Lists/contexts/ListProvider.tsx +0 -28
- package/src/components/Lists/contexts/ParentListProvider.tsx +0 -35
|
@@ -1,18 +1,31 @@
|
|
|
1
1
|
import cn from 'classnames';
|
|
2
|
-
import { FocusEvent, KeyboardEvent, MouseEvent, RefObject } from 'react';
|
|
2
|
+
import { FocusEvent, KeyboardEvent, MouseEvent, ReactNode, RefObject } from 'react';
|
|
3
3
|
|
|
4
4
|
import { Checkbox, Switch } from '@snack-uikit/toggles';
|
|
5
|
-
import { TruncateString } from '@snack-uikit/truncate-string';
|
|
6
5
|
import { extractSupportProps } from '@snack-uikit/utils';
|
|
7
6
|
|
|
8
|
-
import {
|
|
7
|
+
import { ItemContent } from '../../../helperComponents';
|
|
8
|
+
import {
|
|
9
|
+
useCollapseLevelContext,
|
|
10
|
+
useFocusListContext,
|
|
11
|
+
useNewListContext,
|
|
12
|
+
useSelectionContext,
|
|
13
|
+
} from '../../Lists/contexts';
|
|
9
14
|
import commonStyles from '../styles.module.scss';
|
|
10
|
-
import {
|
|
15
|
+
import { FlattenBaseItem } from '../types';
|
|
16
|
+
import { isContentItem } from '../utils';
|
|
11
17
|
import { CHECKBOX_SIZE_MAP } from './constants';
|
|
12
18
|
import styles from './styles.module.scss';
|
|
13
19
|
|
|
14
|
-
type AllBaseItemProps =
|
|
15
|
-
|
|
20
|
+
type AllBaseItemProps = FlattenBaseItem & {
|
|
21
|
+
expandIcon?: ReactNode;
|
|
22
|
+
open?: boolean;
|
|
23
|
+
indeterminate?: boolean;
|
|
24
|
+
checked?: boolean;
|
|
25
|
+
onSelect?(): void;
|
|
26
|
+
isParentNode?: boolean;
|
|
27
|
+
onOpenNestedList?(e?: KeyboardEvent<HTMLElement>): void;
|
|
28
|
+
};
|
|
16
29
|
|
|
17
30
|
export function BaseItem({
|
|
18
31
|
beforeContent,
|
|
@@ -29,44 +42,54 @@ export function BaseItem({
|
|
|
29
42
|
onKeyDown,
|
|
30
43
|
onFocus,
|
|
31
44
|
indeterminate,
|
|
45
|
+
checked: checkedProp,
|
|
32
46
|
onSelect,
|
|
47
|
+
onOpenNestedList,
|
|
33
48
|
isParentNode,
|
|
34
49
|
className,
|
|
35
50
|
inactive,
|
|
36
51
|
itemWrapRender,
|
|
37
52
|
...rest
|
|
38
53
|
}: AllBaseItemProps) {
|
|
39
|
-
const { option, caption, description, truncate: contentTruncate } = content || {};
|
|
40
54
|
const interactive = !inactive;
|
|
41
55
|
|
|
42
|
-
const {
|
|
43
|
-
const {
|
|
44
|
-
const {
|
|
56
|
+
const { size = 's', marker, contentRender } = useNewListContext();
|
|
57
|
+
const { level = 0 } = useCollapseLevelContext();
|
|
58
|
+
const { forceUpdateActiveItemId } = useFocusListContext();
|
|
45
59
|
const { value, onChange, mode, isSelectionSingle, isSelectionMultiple } = useSelectionContext();
|
|
46
60
|
|
|
47
|
-
const isChecked = isSelectionSingle ? value === id : value?.includes(id);
|
|
61
|
+
const isChecked = isSelectionSingle ? value === id : checkedProp ?? value?.includes(id ?? '');
|
|
48
62
|
|
|
49
63
|
const handleChange = () => {
|
|
50
|
-
onChange?.(id
|
|
64
|
+
onChange?.(id);
|
|
51
65
|
};
|
|
52
66
|
|
|
53
67
|
const handleItemMouseDown = (e: MouseEvent<HTMLElement>) => {
|
|
54
68
|
if (disabled) return;
|
|
55
69
|
|
|
56
70
|
if (interactive) {
|
|
57
|
-
parentResetActiveFocusIndex?.();
|
|
58
|
-
|
|
59
71
|
if (!isParentNode) {
|
|
60
72
|
handleChange();
|
|
61
73
|
}
|
|
62
74
|
}
|
|
63
75
|
|
|
64
76
|
onMouseDown?.(e);
|
|
77
|
+
|
|
78
|
+
forceUpdateActiveItemId?.('~drop-focus');
|
|
65
79
|
};
|
|
66
80
|
|
|
67
81
|
const handleItemKeyDown = (e: KeyboardEvent<HTMLElement>) => {
|
|
68
82
|
onKeyDown?.(e);
|
|
69
83
|
|
|
84
|
+
if (e.code === 'ArrowRight') {
|
|
85
|
+
onOpenNestedList?.(e);
|
|
86
|
+
|
|
87
|
+
e.preventDefault();
|
|
88
|
+
e.stopPropagation();
|
|
89
|
+
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
70
93
|
if (e.code === 'Space' || e.key === 'Enter') {
|
|
71
94
|
if (isSelectionMultiple && isParentNode && onSelect) {
|
|
72
95
|
onSelect();
|
|
@@ -100,7 +123,7 @@ export function BaseItem({
|
|
|
100
123
|
|
|
101
124
|
const props = extractSupportProps(rest);
|
|
102
125
|
|
|
103
|
-
const
|
|
126
|
+
const itemJSX = (
|
|
104
127
|
<li
|
|
105
128
|
data-type='outside'
|
|
106
129
|
role={'menuitem'}
|
|
@@ -126,13 +149,14 @@ export function BaseItem({
|
|
|
126
149
|
{!switchProp && isSelectionSingle && marker && !isParentNode && interactive && (
|
|
127
150
|
<div className={styles.markerContainer} data-test-id='list__base-item-marker' />
|
|
128
151
|
)}
|
|
152
|
+
|
|
129
153
|
{!switchProp && isSelectionMultiple && interactive && (
|
|
130
154
|
<div className={styles.checkbox}>
|
|
131
155
|
<Checkbox
|
|
132
156
|
size={CHECKBOX_SIZE_MAP[size ?? 's']}
|
|
133
157
|
disabled={disabled}
|
|
134
158
|
tabIndex={-1}
|
|
135
|
-
onChange={handleCheckboxChange}
|
|
159
|
+
onChange={isParentNode ? handleCheckboxChange : undefined}
|
|
136
160
|
checked={isChecked}
|
|
137
161
|
data-test-id='list__base-item-checkbox'
|
|
138
162
|
onClick={handleCheckboxClick}
|
|
@@ -142,43 +166,24 @@ export function BaseItem({
|
|
|
142
166
|
)}
|
|
143
167
|
|
|
144
168
|
{beforeContent && <div className={styles.beforeContent}>{beforeContent}</div>}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
variant={contentTruncate?.variant}
|
|
151
|
-
text={option}
|
|
152
|
-
maxLines={contentTruncate?.option ?? 1}
|
|
153
|
-
data-test-id='list__base-item-option'
|
|
154
|
-
/>
|
|
155
|
-
</span>
|
|
156
|
-
{caption && <span className={styles.caption}>{caption}</span>}
|
|
157
|
-
</div>
|
|
158
|
-
|
|
159
|
-
{description && (
|
|
160
|
-
<div className={styles.description}>
|
|
161
|
-
<TruncateString
|
|
162
|
-
text={description}
|
|
163
|
-
maxLines={contentTruncate?.description ?? 2}
|
|
164
|
-
data-test-id='list__base-item-description'
|
|
165
|
-
/>
|
|
166
|
-
</div>
|
|
167
|
-
)}
|
|
168
|
-
</div>
|
|
169
|
-
|
|
169
|
+
{content && isContentItem(content) ? (
|
|
170
|
+
contentRender?.({ id, content }) ?? <ItemContent disabled={disabled} {...content} />
|
|
171
|
+
) : (
|
|
172
|
+
<div className={styles.content}> {content} </div>
|
|
173
|
+
)}
|
|
170
174
|
{afterContent}
|
|
171
175
|
|
|
172
176
|
{switchProp && interactive && (
|
|
173
177
|
<Switch disabled={disabled} checked={isChecked} data-test-id='list__base-item-switch' />
|
|
174
178
|
)}
|
|
179
|
+
|
|
175
180
|
{!switchProp && expandIcon && <span className={styles.expandableIcon}>{expandIcon}</span>}
|
|
176
181
|
</li>
|
|
177
182
|
);
|
|
178
183
|
|
|
179
184
|
if (!itemWrapRender) {
|
|
180
|
-
return
|
|
185
|
+
return itemJSX;
|
|
181
186
|
}
|
|
182
187
|
|
|
183
|
-
return itemWrapRender(
|
|
188
|
+
return itemWrapRender(itemJSX);
|
|
184
189
|
}
|
|
@@ -2,48 +2,6 @@
|
|
|
2
2
|
@import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element';
|
|
3
3
|
|
|
4
4
|
$sizes: 's', 'm', 'l';
|
|
5
|
-
$typography: (
|
|
6
|
-
's': (
|
|
7
|
-
'label': $sans-body-s,
|
|
8
|
-
'caption': $sans-body-s,
|
|
9
|
-
'description': $sans-body-s,
|
|
10
|
-
'separator': $light-label-m,
|
|
11
|
-
),
|
|
12
|
-
'm': (
|
|
13
|
-
'label': $sans-body-m,
|
|
14
|
-
'caption': $sans-body-s,
|
|
15
|
-
'description': $sans-body-s,
|
|
16
|
-
'separator': $light-label-l,
|
|
17
|
-
),
|
|
18
|
-
'l': (
|
|
19
|
-
'label': $sans-body-l,
|
|
20
|
-
'caption': $sans-body-m,
|
|
21
|
-
'description': $sans-body-m,
|
|
22
|
-
'separator': $light-label-l,
|
|
23
|
-
),
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
.headline {
|
|
27
|
-
display: flex;
|
|
28
|
-
align-items: center;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.option {
|
|
32
|
-
overflow: hidden;
|
|
33
|
-
flex: 1;
|
|
34
|
-
color: $sys-neutral-text-main;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
.caption {
|
|
38
|
-
overflow: hidden;
|
|
39
|
-
color: $sys-neutral-text-light;
|
|
40
|
-
text-overflow: ellipsis;
|
|
41
|
-
white-space: nowrap;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.description {
|
|
45
|
-
color: $sys-neutral-text-support;
|
|
46
|
-
}
|
|
47
5
|
|
|
48
6
|
.checkbox {
|
|
49
7
|
display: inline-flex;
|
|
@@ -69,8 +27,8 @@ $typography: (
|
|
|
69
27
|
color: $sys-neutral-text-light;
|
|
70
28
|
|
|
71
29
|
svg {
|
|
72
|
-
width: 100
|
|
73
|
-
height: 100
|
|
30
|
+
max-width: 100%;
|
|
31
|
+
max-height: 100%;
|
|
74
32
|
}
|
|
75
33
|
}
|
|
76
34
|
|
|
@@ -102,13 +60,6 @@ $typography: (
|
|
|
102
60
|
}
|
|
103
61
|
}
|
|
104
62
|
|
|
105
|
-
.content {
|
|
106
|
-
overflow: hidden;
|
|
107
|
-
flex-grow: 1;
|
|
108
|
-
flex-shrink: 1;
|
|
109
|
-
box-sizing: border-box;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
63
|
.droplistItem {
|
|
113
64
|
width: 100%;
|
|
114
65
|
|
|
@@ -117,22 +68,6 @@ $typography: (
|
|
|
117
68
|
padding-left: calc(
|
|
118
69
|
var(--level, 0) * $dimension-050m + simple-var($drop-list, 'item', $size, 'container', 'padding-left')
|
|
119
70
|
);
|
|
120
|
-
|
|
121
|
-
.headline {
|
|
122
|
-
@include composite-var($drop-list, 'item', $size, 'headline');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.option {
|
|
126
|
-
@include composite-var($typography, $size, 'label');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.caption {
|
|
130
|
-
@include composite-var($typography, $size, 'caption');
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
.description {
|
|
134
|
-
@include composite-var($typography, $size, 'description');
|
|
135
|
-
}
|
|
136
71
|
}
|
|
137
72
|
}
|
|
138
73
|
|
|
@@ -142,9 +77,6 @@ $typography: (
|
|
|
142
77
|
|
|
143
78
|
&[aria-disabled],
|
|
144
79
|
&[data-disabled] {
|
|
145
|
-
.option,
|
|
146
|
-
.description,
|
|
147
|
-
.caption,
|
|
148
80
|
.expandableIcon {
|
|
149
81
|
color: $sys-neutral-text-disabled;
|
|
150
82
|
}
|
|
@@ -192,3 +124,9 @@ $typography: (
|
|
|
192
124
|
}
|
|
193
125
|
}
|
|
194
126
|
}
|
|
127
|
+
|
|
128
|
+
.content {
|
|
129
|
+
flex-grow: 1;
|
|
130
|
+
flex-shrink: 1;
|
|
131
|
+
box-sizing: border-box;
|
|
132
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { Separator } from '../../../helperComponents';
|
|
2
|
-
import { useListContext } from '../../Lists/contexts';
|
|
3
2
|
import { useRenderItems } from '../hooks';
|
|
4
|
-
import {
|
|
3
|
+
import { FlattenGroupListItem, ItemId } from '../types';
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
const { size } = useListContext();
|
|
5
|
+
type GroupItemProps = Omit<FlattenGroupListItem, 'type'> & { items: ItemId[] };
|
|
8
6
|
|
|
7
|
+
export function GroupItem({ label, truncate, divider, items, mode }: GroupItemProps) {
|
|
9
8
|
const itemsJSX = useRenderItems(items);
|
|
10
9
|
|
|
11
10
|
return (
|
|
12
11
|
<>
|
|
13
|
-
<Separator label={label} truncate={truncate} divider={divider} mode={mode}
|
|
12
|
+
<Separator label={label} truncate={truncate} divider={divider} mode={mode} />
|
|
13
|
+
|
|
14
14
|
{itemsJSX}
|
|
15
15
|
</>
|
|
16
16
|
);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Separator } from '../../../helperComponents';
|
|
2
|
+
import { useGroupItemSelection, useRenderItems } from '../hooks';
|
|
3
|
+
import { CommonFlattenProps, FlattenGroupSelectListItem } from '../types';
|
|
4
|
+
|
|
5
|
+
type GroupSelectItemProps = Omit<FlattenGroupSelectListItem, 'type'> & CommonFlattenProps;
|
|
6
|
+
|
|
7
|
+
export function GroupSelectItem({
|
|
8
|
+
label,
|
|
9
|
+
truncate,
|
|
10
|
+
divider,
|
|
11
|
+
items,
|
|
12
|
+
mode,
|
|
13
|
+
id,
|
|
14
|
+
itemRef,
|
|
15
|
+
allChildIds,
|
|
16
|
+
}: GroupSelectItemProps) {
|
|
17
|
+
const { indeterminate, checked, handleOnSelect } = useGroupItemSelection({
|
|
18
|
+
items,
|
|
19
|
+
id,
|
|
20
|
+
disabled: false,
|
|
21
|
+
allChildIds,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const itemsJSX = useRenderItems(items);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<>
|
|
28
|
+
<Separator
|
|
29
|
+
label={label}
|
|
30
|
+
truncate={truncate}
|
|
31
|
+
divider={divider}
|
|
32
|
+
mode={mode}
|
|
33
|
+
selectButton={{
|
|
34
|
+
indeterminate,
|
|
35
|
+
checked,
|
|
36
|
+
itemRef,
|
|
37
|
+
onClick: handleOnSelect,
|
|
38
|
+
}}
|
|
39
|
+
/>
|
|
40
|
+
|
|
41
|
+
{itemsJSX}
|
|
42
|
+
</>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './GroupSelectItem';
|
|
@@ -1,122 +1,116 @@
|
|
|
1
|
-
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
1
|
+
import { KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import { Dropdown
|
|
3
|
+
import { Dropdown } from '@snack-uikit/dropdown';
|
|
4
4
|
import { ChevronRightSVG } from '@snack-uikit/icons';
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import { ParentListContext, useParentListContext } from '../../Lists/contexts';
|
|
6
|
+
import { useCollapseContext, useFocusListContext, useNewListContext } from '../../Lists/contexts';
|
|
8
7
|
import { ListPrivate } from '../../Lists/ListPrivate';
|
|
9
8
|
import { BaseItem } from '../BaseItem';
|
|
10
9
|
import { useGroupItemSelection } from '../hooks';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
'right-start',
|
|
17
|
-
'right-end',
|
|
18
|
-
'left',
|
|
19
|
-
'left-start',
|
|
20
|
-
'left-end',
|
|
21
|
-
];
|
|
10
|
+
import { CommonFlattenProps, FlattenNextListItem, ItemId } from '../types';
|
|
11
|
+
import { extractActiveItems, isNextListItem } from '../utils';
|
|
12
|
+
import { FALLBACK_PLACEMENTS } from './constants';
|
|
13
|
+
|
|
14
|
+
type NextListItemProps = Omit<FlattenNextListItem, 'type'> & CommonFlattenProps & { focusId?: ItemId };
|
|
22
15
|
|
|
23
16
|
export function NextListItem({
|
|
24
|
-
items
|
|
17
|
+
items,
|
|
25
18
|
placement = 'right-start',
|
|
26
19
|
id,
|
|
27
|
-
search,
|
|
28
20
|
scroll,
|
|
29
21
|
scrollRef,
|
|
30
22
|
disabled,
|
|
31
23
|
onSublistOpenChanged,
|
|
24
|
+
allChildIds,
|
|
32
25
|
loading = false,
|
|
26
|
+
focusId = id,
|
|
33
27
|
...option
|
|
34
28
|
}: NextListItemProps) {
|
|
35
|
-
const
|
|
29
|
+
const { flattenItems, focusFlattenItems } = useNewListContext();
|
|
30
|
+
const { openCollapseItems = [] } = useCollapseContext();
|
|
36
31
|
|
|
37
|
-
const
|
|
32
|
+
const item = flattenItems[id];
|
|
38
33
|
|
|
39
|
-
const {
|
|
40
|
-
(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
34
|
+
const { ids, expandedIds } = useMemo(() => {
|
|
35
|
+
const { ids, expandedIds } = extractActiveItems({
|
|
36
|
+
focusCloseChildIds: items,
|
|
37
|
+
focusFlattenItems,
|
|
38
|
+
openCollapseItems,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return { ids, expandedIds: expandedIds.concat([id]) };
|
|
42
|
+
}, [focusFlattenItems, id, items, openCollapseItems]);
|
|
43
|
+
|
|
44
|
+
const { handleListKeyDownFactory, activeItemId, forceUpdateActiveItemId } = useFocusListContext();
|
|
45
|
+
|
|
46
|
+
const [open, setOpen] = useState<boolean>();
|
|
47
|
+
|
|
48
|
+
const handleListKeyDown = useCallback(
|
|
49
|
+
(e: KeyboardEvent<HTMLElement>) => {
|
|
50
|
+
handleListKeyDownFactory(ids, expandedIds)(e);
|
|
51
|
+
|
|
52
|
+
if (e.code === 'ArrowLeft') {
|
|
53
|
+
forceUpdateActiveItemId?.(focusId);
|
|
54
|
+
setOpen(false);
|
|
55
|
+
e.stopPropagation();
|
|
47
56
|
|
|
48
|
-
|
|
49
|
-
useParentListContext();
|
|
50
|
-
|
|
51
|
-
const {
|
|
52
|
-
open,
|
|
53
|
-
setOpen,
|
|
54
|
-
handleListKeyDown,
|
|
55
|
-
activeFocusIndex,
|
|
56
|
-
openNestedIndex,
|
|
57
|
-
resetNestedIndex,
|
|
58
|
-
resetActiveFocusIndex,
|
|
59
|
-
} = useKeyboardNavigation({ ids, expandedIds, itemRefs, id });
|
|
60
|
-
|
|
61
|
-
const { isIndeterminate, checked, handleOnSelect } = useGroupItemSelection({ items: itemsProp, id, disabled });
|
|
62
|
-
|
|
63
|
-
const handleOpenChange = useCallback(
|
|
64
|
-
(open: boolean) => {
|
|
65
|
-
if (!open) {
|
|
66
|
-
resetActiveFocusIndex();
|
|
67
|
-
resetNestedIndex();
|
|
68
|
-
setOpenCollapsedItems([]);
|
|
57
|
+
return;
|
|
69
58
|
}
|
|
70
|
-
onSublistOpenChanged?.(open, id);
|
|
71
|
-
setOpen(open);
|
|
72
59
|
},
|
|
73
|
-
[
|
|
60
|
+
[handleListKeyDownFactory, ids, expandedIds, forceUpdateActiveItemId, focusId],
|
|
74
61
|
);
|
|
75
62
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
63
|
+
const { indeterminate, checked, handleOnSelect } = useGroupItemSelection({
|
|
64
|
+
items: isNextListItem<FlattenNextListItem>(item) ? item.items : [],
|
|
65
|
+
id,
|
|
66
|
+
disabled,
|
|
67
|
+
allChildIds,
|
|
68
|
+
});
|
|
79
69
|
|
|
70
|
+
const handleOutsideClick = useCallback(() => {
|
|
71
|
+
forceUpdateActiveItemId?.('~drop-focus');
|
|
72
|
+
setOpen(false);
|
|
80
73
|
return true;
|
|
81
|
-
}, [
|
|
74
|
+
}, [forceUpdateActiveItemId]);
|
|
75
|
+
|
|
76
|
+
const isOpen = useMemo(
|
|
77
|
+
() => Boolean(!disabled && activeItemId && focusFlattenItems[focusId].allChildIds.includes(activeItemId)),
|
|
78
|
+
[activeItemId, disabled, focusFlattenItems, focusId],
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
setOpen(open => open && isOpen);
|
|
83
|
+
}, [id, isOpen]);
|
|
84
|
+
|
|
85
|
+
const listRef = useRef<HTMLElement>(null);
|
|
82
86
|
|
|
83
87
|
return (
|
|
84
88
|
<Dropdown
|
|
85
89
|
outsideClick={handleOutsideClick}
|
|
86
90
|
fallbackPlacements={FALLBACK_PLACEMENTS}
|
|
87
91
|
content={
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
parentResetActiveFocusIndex: resetActiveFocusIndex,
|
|
99
|
-
toggleOpenCollapsedItems: id =>
|
|
100
|
-
setOpenCollapsedItems(items =>
|
|
101
|
-
items.includes(id) ? items.filter(item => item !== id) : items.concat([id]),
|
|
102
|
-
),
|
|
92
|
+
<ListPrivate
|
|
93
|
+
onKeyDown={handleListKeyDown}
|
|
94
|
+
items={items}
|
|
95
|
+
nested
|
|
96
|
+
scroll={scroll}
|
|
97
|
+
tabIndex={0}
|
|
98
|
+
ref={listRef}
|
|
99
|
+
onFocus={e => {
|
|
100
|
+
e.stopPropagation();
|
|
101
|
+
forceUpdateActiveItemId?.(ids[0]);
|
|
103
102
|
}}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
nested
|
|
109
|
-
search={search}
|
|
110
|
-
scroll={scroll}
|
|
111
|
-
scrollRef={scrollRef}
|
|
112
|
-
limitedScrollHeight
|
|
113
|
-
loading={loading}
|
|
114
|
-
/>
|
|
115
|
-
</ParentListContext.Provider>
|
|
103
|
+
scrollRef={scrollRef}
|
|
104
|
+
limitedScrollHeight
|
|
105
|
+
loading={loading}
|
|
106
|
+
/>
|
|
116
107
|
}
|
|
117
108
|
trigger='hover'
|
|
118
|
-
open={
|
|
119
|
-
onOpenChange={
|
|
109
|
+
open={isOpen || open}
|
|
110
|
+
onOpenChange={value => {
|
|
111
|
+
setOpen(value);
|
|
112
|
+
onSublistOpenChanged?.(value, id);
|
|
113
|
+
}}
|
|
120
114
|
placement={placement}
|
|
121
115
|
widthStrategy='auto'
|
|
122
116
|
>
|
|
@@ -127,7 +121,14 @@ export function NextListItem({
|
|
|
127
121
|
expandIcon={<ChevronRightSVG />}
|
|
128
122
|
id={id}
|
|
129
123
|
isParentNode
|
|
130
|
-
indeterminate={
|
|
124
|
+
indeterminate={indeterminate}
|
|
125
|
+
checked={checked}
|
|
126
|
+
onOpenNestedList={() => {
|
|
127
|
+
setOpen(true);
|
|
128
|
+
setTimeout(() => {
|
|
129
|
+
listRef.current?.focus();
|
|
130
|
+
}, 0);
|
|
131
|
+
}}
|
|
131
132
|
onSelect={handleOnSelect}
|
|
132
133
|
/>
|
|
133
134
|
</Dropdown>
|
|
@@ -4,17 +4,17 @@ import { FocusEvent, KeyboardEvent, RefObject } from 'react';
|
|
|
4
4
|
import { SearchPrivate } from '@snack-uikit/search-private';
|
|
5
5
|
|
|
6
6
|
import { SearchState } from '../../../types';
|
|
7
|
-
import {
|
|
7
|
+
import { useNewListContext } from '../../Lists/contexts';
|
|
8
8
|
import commonStyles from '../styles.module.scss';
|
|
9
9
|
import styles from './styles.module.scss';
|
|
10
10
|
|
|
11
11
|
export type SearchItemProps = {
|
|
12
12
|
search?: SearchState;
|
|
13
|
+
itemRef?: RefObject<HTMLElement>;
|
|
13
14
|
};
|
|
14
15
|
|
|
15
|
-
export function SearchItem({ search }: SearchItemProps) {
|
|
16
|
-
const {
|
|
17
|
-
const { size } = useListContext();
|
|
16
|
+
export function SearchItem({ search, itemRef }: SearchItemProps) {
|
|
17
|
+
const { size = 's' } = useNewListContext();
|
|
18
18
|
|
|
19
19
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
20
20
|
if (['ArrowDown', 'ArrowUp'].includes(e.key)) {
|
|
@@ -23,13 +23,11 @@ export function SearchItem({ search }: SearchItemProps) {
|
|
|
23
23
|
|
|
24
24
|
if (['ArrowRight'].includes(e.key)) {
|
|
25
25
|
e.stopPropagation();
|
|
26
|
+
e.preventDefault();
|
|
26
27
|
}
|
|
27
28
|
};
|
|
28
29
|
|
|
29
30
|
const handleFocus = (e: FocusEvent<HTMLInputElement>) => {
|
|
30
|
-
if (e.target === parentItemRefs[0].current) {
|
|
31
|
-
parentSetActiveFocusIndex?.(0);
|
|
32
|
-
}
|
|
33
31
|
e.stopPropagation();
|
|
34
32
|
};
|
|
35
33
|
|
|
@@ -45,7 +43,7 @@ export function SearchItem({ search }: SearchItemProps) {
|
|
|
45
43
|
onKeyDown={handleKeyDown}
|
|
46
44
|
onFocus={handleFocus}
|
|
47
45
|
{...search}
|
|
48
|
-
ref={
|
|
46
|
+
ref={itemRef as RefObject<HTMLInputElement>}
|
|
49
47
|
/>
|
|
50
48
|
</div>
|
|
51
49
|
);
|