@dxos/react-ui-list 0.8.4-main.3f58842 → 0.8.4-main.548089c
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/dist/lib/browser/index.mjs +94 -90
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +94 -90
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Accordion/Accordion.stories.d.ts +7 -4
- package/dist/types/src/components/Accordion/Accordion.stories.d.ts.map +1 -1
- package/dist/types/src/components/Accordion/AccordionItem.d.ts +1 -1
- package/dist/types/src/components/Accordion/AccordionItem.d.ts.map +1 -1
- package/dist/types/src/components/List/List.d.ts +6 -6
- package/dist/types/src/components/List/List.d.ts.map +1 -1
- package/dist/types/src/components/List/List.stories.d.ts +14 -5
- package/dist/types/src/components/List/List.stories.d.ts.map +1 -1
- package/dist/types/src/components/List/ListItem.d.ts +4 -7
- package/dist/types/src/components/List/ListItem.d.ts.map +1 -1
- package/dist/types/src/components/List/ListRoot.d.ts.map +1 -1
- package/dist/types/src/components/List/testing.d.ts +1 -1
- package/dist/types/src/components/List/testing.d.ts.map +1 -1
- package/dist/types/src/components/Tree/Tree.d.ts +3 -3
- package/dist/types/src/components/Tree/Tree.d.ts.map +1 -1
- package/dist/types/src/components/Tree/Tree.stories.d.ts +36 -6
- package/dist/types/src/components/Tree/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/components/Tree/TreeContext.d.ts +3 -2
- package/dist/types/src/components/Tree/TreeContext.d.ts.map +1 -1
- package/dist/types/src/components/Tree/TreeItem.d.ts +14 -9
- package/dist/types/src/components/Tree/TreeItem.d.ts.map +1 -1
- package/dist/types/src/components/Tree/TreeItemHeading.d.ts +4 -3
- package/dist/types/src/components/Tree/TreeItemHeading.d.ts.map +1 -1
- package/dist/types/src/components/Tree/TreeItemToggle.d.ts +3 -3
- package/dist/types/src/components/Tree/TreeItemToggle.d.ts.map +1 -1
- package/dist/types/src/components/Tree/testing.d.ts +2 -2
- package/dist/types/src/components/Tree/testing.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +25 -24
- package/src/components/Accordion/Accordion.stories.tsx +6 -8
- package/src/components/Accordion/Accordion.tsx +1 -1
- package/src/components/Accordion/AccordionItem.tsx +5 -2
- package/src/components/List/List.stories.tsx +19 -17
- package/src/components/List/List.tsx +2 -5
- package/src/components/List/ListItem.tsx +39 -27
- package/src/components/List/ListRoot.tsx +1 -1
- package/src/components/List/testing.ts +2 -2
- package/src/components/Tree/Tree.stories.tsx +51 -48
- package/src/components/Tree/Tree.tsx +7 -2
- package/src/components/Tree/TreeContext.tsx +3 -2
- package/src/components/Tree/TreeItem.tsx +50 -43
- package/src/components/Tree/TreeItemHeading.tsx +9 -6
- package/src/components/Tree/TreeItemToggle.tsx +29 -18
- package/src/components/Tree/testing.ts +2 -2
|
@@ -5,34 +5,35 @@
|
|
|
5
5
|
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
|
|
6
6
|
import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
7
7
|
import {
|
|
8
|
-
attachInstruction,
|
|
9
|
-
extractInstruction,
|
|
10
8
|
type Instruction,
|
|
11
9
|
type ItemMode,
|
|
10
|
+
attachInstruction,
|
|
11
|
+
extractInstruction,
|
|
12
12
|
} from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
|
|
13
|
-
import
|
|
14
|
-
import React, { memo, useCallback, useEffect, useMemo, useRef, useState
|
|
13
|
+
import * as Schema from 'effect/Schema';
|
|
14
|
+
import React, { type FC, type KeyboardEvent, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
15
15
|
|
|
16
|
-
import { type HasId } from '@dxos/echo
|
|
16
|
+
import { type HasId } from '@dxos/echo/internal';
|
|
17
17
|
import { invariant } from '@dxos/invariant';
|
|
18
|
-
import {
|
|
18
|
+
import { TreeItem as NaturalTreeItem, Treegrid } from '@dxos/react-ui';
|
|
19
19
|
import {
|
|
20
|
+
ghostFocusWithin,
|
|
20
21
|
ghostHover,
|
|
21
22
|
hoverableControls,
|
|
22
23
|
hoverableFocusedKeyboardControls,
|
|
23
24
|
hoverableFocusedWithinControls,
|
|
24
25
|
} from '@dxos/react-ui-theme';
|
|
25
26
|
|
|
27
|
+
import { DEFAULT_INDENTATION, paddingIndentation } from './helpers';
|
|
26
28
|
import { useTree } from './TreeContext';
|
|
27
29
|
import { TreeItemHeading } from './TreeItemHeading';
|
|
28
30
|
import { TreeItemToggle } from './TreeItemToggle';
|
|
29
|
-
import { DEFAULT_INDENTATION, paddingIndentation } from './helpers';
|
|
30
|
-
|
|
31
|
-
type TreeItemState = 'idle' | 'dragging' | 'preview' | 'parent-of-instruction';
|
|
32
31
|
|
|
33
32
|
const hoverableDescriptionIcons =
|
|
34
33
|
'[--icons-color:inherit] hover-hover:[--icons-color:var(--description-text)] hover-hover:hover:[--icons-color:inherit] focus-within:[--icons-color:inherit]';
|
|
35
34
|
|
|
35
|
+
type TreeItemState = 'idle' | 'dragging' | 'preview' | 'parent-of-instruction';
|
|
36
|
+
|
|
36
37
|
export const TreeDataSchema = Schema.Struct({
|
|
37
38
|
id: Schema.String,
|
|
38
39
|
path: Schema.Array(Schema.String),
|
|
@@ -40,23 +41,25 @@ export const TreeDataSchema = Schema.Struct({
|
|
|
40
41
|
});
|
|
41
42
|
|
|
42
43
|
export type TreeData = Schema.Schema.Type<typeof TreeDataSchema>;
|
|
43
|
-
|
|
44
44
|
export const isTreeData = (data: unknown): data is TreeData => Schema.is(TreeDataSchema)(data);
|
|
45
45
|
|
|
46
|
+
export type ColumnRenderer<T extends HasId = any> = FC<{
|
|
47
|
+
item: T;
|
|
48
|
+
path: string[];
|
|
49
|
+
open: boolean;
|
|
50
|
+
menuOpen: boolean;
|
|
51
|
+
setMenuOpen: (open: boolean) => void;
|
|
52
|
+
}>;
|
|
53
|
+
|
|
46
54
|
export type TreeItemProps<T extends HasId = any> = {
|
|
47
55
|
item: T;
|
|
48
56
|
path: string[];
|
|
49
57
|
levelOffset?: number;
|
|
50
58
|
last: boolean;
|
|
51
59
|
draggable?: boolean;
|
|
52
|
-
renderColumns?:
|
|
53
|
-
item: T;
|
|
54
|
-
path: string[];
|
|
55
|
-
open: boolean;
|
|
56
|
-
menuOpen: boolean;
|
|
57
|
-
setMenuOpen: (open: boolean) => void;
|
|
58
|
-
}>;
|
|
60
|
+
renderColumns?: ColumnRenderer<T>;
|
|
59
61
|
canDrop?: (params: { source: TreeData; target: TreeData }) => boolean;
|
|
62
|
+
canSelect?: (params: { item: T; path: string[] }) => boolean;
|
|
60
63
|
onOpenChange?: (params: { item: T; path: string[]; open: boolean }) => void;
|
|
61
64
|
onSelect?: (params: { item: T; path: string[]; current: boolean; option: boolean }) => void;
|
|
62
65
|
};
|
|
@@ -64,13 +67,14 @@ export type TreeItemProps<T extends HasId = any> = {
|
|
|
64
67
|
const RawTreeItem = <T extends HasId = any>({
|
|
65
68
|
item,
|
|
66
69
|
path: _path,
|
|
70
|
+
levelOffset = 2,
|
|
67
71
|
last,
|
|
68
72
|
draggable: _draggable,
|
|
69
73
|
renderColumns: Columns,
|
|
70
74
|
canDrop,
|
|
75
|
+
canSelect,
|
|
71
76
|
onOpenChange,
|
|
72
77
|
onSelect,
|
|
73
|
-
levelOffset = 2,
|
|
74
78
|
}: TreeItemProps<T>) => {
|
|
75
79
|
const rowRef = useRef<HTMLDivElement | null>(null);
|
|
76
80
|
const buttonRef = useRef<HTMLButtonElement | null>(null);
|
|
@@ -82,13 +86,14 @@ const RawTreeItem = <T extends HasId = any>({
|
|
|
82
86
|
|
|
83
87
|
const { useItems, getProps, isOpen, isCurrent } = useTree();
|
|
84
88
|
const items = useItems(item);
|
|
85
|
-
const { id, label,
|
|
89
|
+
const { id, parentOf, label, className, headingClassName, icon, iconHue, disabled, testId } = getProps(item, _path);
|
|
86
90
|
const path = useMemo(() => [..._path, id], [_path, id]);
|
|
87
91
|
const open = isOpen(path, item);
|
|
88
92
|
const current = isCurrent(path, item);
|
|
89
93
|
const level = path.length - levelOffset;
|
|
90
94
|
const isBranch = !!parentOf;
|
|
91
95
|
const mode: ItemMode = last ? 'last-in-group' : open ? 'expanded' : 'standard';
|
|
96
|
+
const canSelectItem = canSelect?.({ item, path }) ?? true;
|
|
92
97
|
|
|
93
98
|
const cancelExpand = useCallback(() => {
|
|
94
99
|
if (cancelExpandRef.current) {
|
|
@@ -180,38 +185,37 @@ const RawTreeItem = <T extends HasId = any>({
|
|
|
180
185
|
// Cancel expand on unmount.
|
|
181
186
|
useEffect(() => () => cancelExpand(), [cancelExpand]);
|
|
182
187
|
|
|
183
|
-
const
|
|
188
|
+
const handleOpenToggle = useCallback(
|
|
184
189
|
() => onOpenChange?.({ item, path, open: !open }),
|
|
185
190
|
[onOpenChange, item, path, open],
|
|
186
191
|
);
|
|
187
192
|
|
|
188
193
|
const handleSelect = useCallback(
|
|
189
194
|
(option = false) => {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
195
|
+
// If the item is a branch, toggle it if:
|
|
196
|
+
// - also holding down the option key
|
|
197
|
+
// - or the item is currently selected
|
|
198
|
+
if (isBranch && (option || current)) {
|
|
199
|
+
handleOpenToggle();
|
|
200
|
+
} else if (canSelectItem) {
|
|
201
|
+
canSelect?.({ item, path });
|
|
193
202
|
rowRef.current?.focus();
|
|
194
203
|
onSelect?.({ item, path, current: !current, option });
|
|
195
204
|
}
|
|
196
205
|
},
|
|
197
|
-
[item, path, current, isBranch,
|
|
206
|
+
[item, path, current, isBranch, canSelectItem, handleOpenToggle, onSelect],
|
|
198
207
|
);
|
|
199
208
|
|
|
200
209
|
const handleKeyDown = useCallback(
|
|
201
210
|
(event: KeyboardEvent) => {
|
|
202
211
|
switch (event.key) {
|
|
203
212
|
case 'ArrowRight':
|
|
204
|
-
isBranch && !open && handleOpenChange();
|
|
205
|
-
break;
|
|
206
213
|
case 'ArrowLeft':
|
|
207
|
-
isBranch &&
|
|
208
|
-
break;
|
|
209
|
-
case ' ':
|
|
210
|
-
handleSelect(event.altKey);
|
|
214
|
+
isBranch && handleOpenToggle();
|
|
211
215
|
break;
|
|
212
216
|
}
|
|
213
217
|
},
|
|
214
|
-
[isBranch, open,
|
|
218
|
+
[isBranch, open, handleOpenToggle, handleSelect],
|
|
215
219
|
);
|
|
216
220
|
|
|
217
221
|
return (
|
|
@@ -229,9 +233,10 @@ const RawTreeItem = <T extends HasId = any>({
|
|
|
229
233
|
hoverableFocusedWithinControls,
|
|
230
234
|
hoverableDescriptionIcons,
|
|
231
235
|
ghostHover,
|
|
236
|
+
ghostFocusWithin,
|
|
232
237
|
className,
|
|
233
238
|
]}
|
|
234
|
-
data-
|
|
239
|
+
data-object-id={id}
|
|
235
240
|
data-testid={testId}
|
|
236
241
|
// NOTE(thure): This is intentionally an empty string to for descendents to select by in the CSS
|
|
237
242
|
// without alerting the user (except for in the correct link element). See also:
|
|
@@ -243,26 +248,27 @@ const RawTreeItem = <T extends HasId = any>({
|
|
|
243
248
|
setMenuOpen(true);
|
|
244
249
|
}}
|
|
245
250
|
>
|
|
246
|
-
<
|
|
247
|
-
|
|
248
|
-
|
|
251
|
+
<div
|
|
252
|
+
role='none'
|
|
253
|
+
className='indent relative grid grid-cols-subgrid col-[tree-row]'
|
|
249
254
|
style={paddingIndentation(level)}
|
|
250
255
|
>
|
|
251
|
-
<
|
|
252
|
-
<TreeItemToggle isBranch={isBranch} open={open}
|
|
256
|
+
<Treegrid.Cell classNames='flex items-center'>
|
|
257
|
+
<TreeItemToggle isBranch={isBranch} open={open} onClick={handleOpenToggle} />
|
|
253
258
|
<TreeItemHeading
|
|
254
|
-
ref={buttonRef}
|
|
255
|
-
label={label}
|
|
256
|
-
icon={icon}
|
|
257
|
-
className={headingClassName}
|
|
258
259
|
disabled={disabled}
|
|
259
260
|
current={current}
|
|
261
|
+
label={label}
|
|
262
|
+
className={headingClassName}
|
|
263
|
+
icon={icon}
|
|
264
|
+
iconHue={iconHue}
|
|
260
265
|
onSelect={handleSelect}
|
|
266
|
+
ref={buttonRef}
|
|
261
267
|
/>
|
|
262
|
-
</
|
|
268
|
+
</Treegrid.Cell>
|
|
263
269
|
{Columns && <Columns item={item} path={path} open={open} menuOpen={menuOpen} setMenuOpen={setMenuOpen} />}
|
|
264
270
|
{instruction && <NaturalTreeItem.DropIndicator instruction={instruction} gap={2} />}
|
|
265
|
-
</
|
|
271
|
+
</div>
|
|
266
272
|
</Treegrid.Row>
|
|
267
273
|
{open &&
|
|
268
274
|
items.map((item, index) => (
|
|
@@ -274,6 +280,7 @@ const RawTreeItem = <T extends HasId = any>({
|
|
|
274
280
|
draggable={_draggable}
|
|
275
281
|
renderColumns={Columns}
|
|
276
282
|
canDrop={canDrop}
|
|
283
|
+
canSelect={canSelect}
|
|
277
284
|
onOpenChange={onOpenChange}
|
|
278
285
|
onSelect={onSelect}
|
|
279
286
|
/>
|
|
@@ -4,24 +4,27 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { type KeyboardEvent, type MouseEvent, forwardRef, memo, useCallback } from 'react';
|
|
6
6
|
|
|
7
|
-
import { Button, Icon, toLocalizedString, useTranslation
|
|
7
|
+
import { Button, Icon, type Label, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
8
8
|
import { TextTooltip } from '@dxos/react-ui-text-tooltip';
|
|
9
|
+
import { getStyles } from '@dxos/react-ui-theme';
|
|
9
10
|
|
|
10
11
|
// TODO(wittjosiah): Consider whether there should be a separate disabled prop which was visually distinct
|
|
11
12
|
// rather than just making the item unselectable.
|
|
12
|
-
export type
|
|
13
|
+
export type TreeItemHeadingProps = {
|
|
13
14
|
label: Label;
|
|
14
|
-
icon?: string;
|
|
15
15
|
className?: string;
|
|
16
|
+
icon?: string;
|
|
17
|
+
iconHue?: string;
|
|
16
18
|
disabled?: boolean;
|
|
17
19
|
current?: boolean;
|
|
18
20
|
onSelect?: (option: boolean) => void;
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
export const TreeItemHeading = memo(
|
|
22
|
-
forwardRef<HTMLButtonElement,
|
|
23
|
-
({ label, icon,
|
|
24
|
+
forwardRef<HTMLButtonElement, TreeItemHeadingProps>(
|
|
25
|
+
({ label, className, icon, iconHue, disabled, current, onSelect }, forwardedRef) => {
|
|
24
26
|
const { t } = useTranslation();
|
|
27
|
+
const styles = iconHue ? getStyles(iconHue) : undefined;
|
|
25
28
|
|
|
26
29
|
const handleSelect = useCallback(
|
|
27
30
|
(event: MouseEvent) => {
|
|
@@ -64,7 +67,7 @@ export const TreeItemHeading = memo(
|
|
|
64
67
|
onKeyDown={handleButtonKeydown}
|
|
65
68
|
{...(current && { 'aria-current': 'location' })}
|
|
66
69
|
>
|
|
67
|
-
{icon && <Icon icon={icon ?? 'ph--placeholder--regular'} size={5} classNames='mlb-1' />}
|
|
70
|
+
{icon && <Icon icon={icon ?? 'ph--placeholder--regular'} size={5} classNames={['mlb-1', styles?.icon]} />}
|
|
68
71
|
<span className='flex-1 is-0 truncate text-start text-sm font-normal' data-tooltip>
|
|
69
72
|
{toLocalizedString(label, t)}
|
|
70
73
|
</span>
|
|
@@ -4,29 +4,40 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { forwardRef, memo } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { IconButton, type IconButtonProps } from '@dxos/react-ui';
|
|
8
8
|
|
|
9
|
-
export type TreeItemToggleProps = {
|
|
9
|
+
export type TreeItemToggleProps = Omit<IconButtonProps, 'icon' | 'size' | 'label'> & {
|
|
10
10
|
open?: boolean;
|
|
11
11
|
isBranch?: boolean;
|
|
12
|
-
onToggle?: () => void;
|
|
13
12
|
hidden?: boolean;
|
|
14
13
|
};
|
|
15
14
|
|
|
16
15
|
export const TreeItemToggle = memo(
|
|
17
|
-
forwardRef<HTMLButtonElement, TreeItemToggleProps>(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
16
|
+
forwardRef<HTMLButtonElement, TreeItemToggleProps>(
|
|
17
|
+
({ open, isBranch, hidden, classNames, ...props }, forwardedRef) => {
|
|
18
|
+
return (
|
|
19
|
+
<IconButton
|
|
20
|
+
ref={forwardedRef}
|
|
21
|
+
data-testid='treeItem.toggle'
|
|
22
|
+
aria-expanded={open}
|
|
23
|
+
variant='ghost'
|
|
24
|
+
density='fine'
|
|
25
|
+
classNames={[
|
|
26
|
+
'bs-full is-6 pli-0',
|
|
27
|
+
'[&_svg]:transition-[transform] [&_svg]:duration-200',
|
|
28
|
+
open && '[&_svg]:rotate-90',
|
|
29
|
+
hidden ? 'hidden' : !isBranch && 'invisible',
|
|
30
|
+
classNames,
|
|
31
|
+
]}
|
|
32
|
+
size={3}
|
|
33
|
+
icon='ph--caret-right--bold'
|
|
34
|
+
iconOnly
|
|
35
|
+
noTooltip
|
|
36
|
+
label={open ? 'Click to close' : 'Click to open'}
|
|
37
|
+
tabIndex={-1}
|
|
38
|
+
{...props}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
},
|
|
42
|
+
),
|
|
32
43
|
);
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Instruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
|
|
6
|
-
import
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
7
|
|
|
8
|
-
import { type HasId, ObjectId } from '@dxos/echo
|
|
8
|
+
import { type HasId, ObjectId } from '@dxos/echo/internal';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
10
|
import { faker } from '@dxos/random';
|
|
11
11
|
|