@dxos/react-ui-list 0.6.13-main.548ca8d → 0.6.14-main.1366248

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.
Files changed (52) hide show
  1. package/dist/lib/browser/index.mjs +413 -31
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/List/List.d.ts +1 -1
  5. package/dist/types/src/components/List/List.d.ts.map +1 -1
  6. package/dist/types/src/components/List/List.stories.d.ts +6 -10
  7. package/dist/types/src/components/List/List.stories.d.ts.map +1 -1
  8. package/dist/types/src/components/List/ListItem.d.ts +1 -3
  9. package/dist/types/src/components/List/ListItem.d.ts.map +1 -1
  10. package/dist/types/src/components/List/ListRoot.d.ts +5 -3
  11. package/dist/types/src/components/List/ListRoot.d.ts.map +1 -1
  12. package/dist/types/src/components/List/testing.d.ts.map +1 -0
  13. package/dist/types/src/components/Tree/DropIndicator.d.ts +7 -0
  14. package/dist/types/src/components/Tree/DropIndicator.d.ts.map +1 -0
  15. package/dist/types/src/components/Tree/Tree.d.ts +24 -0
  16. package/dist/types/src/components/Tree/Tree.d.ts.map +1 -0
  17. package/dist/types/src/components/Tree/Tree.stories.d.ts +8 -0
  18. package/dist/types/src/components/Tree/Tree.stories.d.ts.map +1 -0
  19. package/dist/types/src/components/Tree/TreeItem.d.ts +34 -0
  20. package/dist/types/src/components/Tree/TreeItem.d.ts.map +1 -0
  21. package/dist/types/src/components/Tree/TreeItemHeading.d.ts +12 -0
  22. package/dist/types/src/components/Tree/TreeItemHeading.d.ts.map +1 -0
  23. package/dist/types/src/components/Tree/TreeItemToggle.d.ts +8 -0
  24. package/dist/types/src/components/Tree/TreeItemToggle.d.ts.map +1 -0
  25. package/dist/types/src/components/Tree/helpers.d.ts +8 -0
  26. package/dist/types/src/components/Tree/helpers.d.ts.map +1 -0
  27. package/dist/types/src/components/Tree/index.d.ts +4 -0
  28. package/dist/types/src/components/Tree/index.d.ts.map +1 -0
  29. package/dist/types/src/components/Tree/testing.d.ts +26 -0
  30. package/dist/types/src/components/Tree/testing.d.ts.map +1 -0
  31. package/dist/types/src/components/Tree/types.d.ts +18 -0
  32. package/dist/types/src/components/Tree/types.d.ts.map +1 -0
  33. package/dist/types/src/components/index.d.ts +1 -0
  34. package/dist/types/src/components/index.d.ts.map +1 -1
  35. package/package.json +20 -15
  36. package/src/components/List/List.stories.tsx +25 -19
  37. package/src/components/List/ListItem.tsx +11 -9
  38. package/src/components/List/ListRoot.tsx +47 -23
  39. package/src/components/Tree/DropIndicator.tsx +79 -0
  40. package/src/components/Tree/Tree.stories.tsx +116 -0
  41. package/src/components/Tree/Tree.tsx +56 -0
  42. package/src/components/Tree/TreeItem.tsx +237 -0
  43. package/src/components/Tree/TreeItemHeading.tsx +62 -0
  44. package/src/components/Tree/TreeItemToggle.tsx +35 -0
  45. package/src/components/Tree/helpers.ts +25 -0
  46. package/src/components/Tree/index.ts +7 -0
  47. package/src/components/Tree/testing.ts +170 -0
  48. package/src/components/Tree/types.ts +34 -0
  49. package/src/components/index.ts +1 -0
  50. package/dist/types/src/testing.d.ts.map +0 -1
  51. /package/dist/types/src/{testing.d.ts → components/List/testing.d.ts} +0 -0
  52. /package/src/{testing.ts → components/List/testing.ts} +0 -0
@@ -0,0 +1,237 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
6
+ import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
7
+ // https://github.com/atlassian/pragmatic-drag-and-drop/blob/main/packages/hitbox/constellation/index/about.mdx
8
+ import {
9
+ attachInstruction,
10
+ extractInstruction,
11
+ type Instruction,
12
+ type ItemMode,
13
+ } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
14
+ import React, { memo, useCallback, useEffect, useRef, useState, type FC, type KeyboardEvent } from 'react';
15
+
16
+ import { invariant } from '@dxos/invariant';
17
+ import { Treegrid } from '@dxos/react-ui';
18
+ import {
19
+ focusRing,
20
+ hoverableControls,
21
+ hoverableFocusedKeyboardControls,
22
+ hoverableFocusedWithinControls,
23
+ mx,
24
+ } from '@dxos/react-ui-theme';
25
+
26
+ import { DropIndicator } from './DropIndicator';
27
+ import { TreeItemHeading } from './TreeItemHeading';
28
+ import { TreeItemToggle } from './TreeItemToggle';
29
+ import { DEFAULT_INDENTATION, paddingIndendation } from './helpers';
30
+ import { type ItemType } from './types';
31
+
32
+ type TreeItemState = 'idle' | 'dragging' | 'preview' | 'parent-of-instruction';
33
+
34
+ const hoverableDescriptionIcons =
35
+ '[--icons-color:inherit] hover-hover:[--icons-color:var(--description-text)] hover-hover:hover:[--icons-color:inherit] focus-within:[--icons-color:inherit]';
36
+
37
+ // TODO(burdon): Make generic?
38
+ export type TreeItemProps<T extends ItemType = ItemType> = {
39
+ item: T;
40
+ mode: ItemMode;
41
+ open: boolean;
42
+ current: boolean;
43
+ draggable?: boolean;
44
+ renderColumns?: FC<{ item: T; menuOpen: boolean; setMenuOpen: (open: boolean) => void }>;
45
+ canDrop?: (source: T, target: T) => boolean;
46
+ onOpenChange?: (item: T, nextOpen: boolean) => void;
47
+ onSelect?: (item: T, nextState: boolean) => void;
48
+ };
49
+
50
+ // TODO(wittjosiah): Styles.
51
+ export const RawTreeItem = <T extends ItemType = ItemType>({
52
+ item,
53
+ mode,
54
+ open,
55
+ current,
56
+ draggable: _draggable,
57
+ renderColumns: Columns,
58
+ canDrop,
59
+ onOpenChange,
60
+ onSelect,
61
+ }: TreeItemProps<T>) => {
62
+ const { id, label, icon, className, headingClassName, disabled, path, parentOf } = item;
63
+ const level = path.length - 2;
64
+ const isBranch = !!parentOf;
65
+
66
+ const rowRef = useRef<HTMLDivElement | null>(null);
67
+ const buttonRef = useRef<HTMLButtonElement | null>(null);
68
+ const openRef = useRef(false);
69
+ const cancelExpandRef = useRef<NodeJS.Timeout | null>(null);
70
+ const [_state, setState] = useState<TreeItemState>('idle');
71
+ const [instruction, setInstruction] = useState<Instruction | null>(null);
72
+ const [menuOpen, setMenuOpen] = useState(false);
73
+
74
+ const cancelExpand = useCallback(() => {
75
+ if (cancelExpandRef.current) {
76
+ clearTimeout(cancelExpandRef.current);
77
+ cancelExpandRef.current = null;
78
+ }
79
+ }, []);
80
+
81
+ useEffect(() => {
82
+ if (!_draggable) {
83
+ return;
84
+ }
85
+
86
+ invariant(buttonRef.current);
87
+
88
+ // https://atlassian.design/components/pragmatic-drag-and-drop/core-package/adapters/element/about
89
+ return combine(
90
+ draggable({
91
+ element: buttonRef.current,
92
+ getInitialData: () => item,
93
+ onDragStart: () => {
94
+ setState('dragging');
95
+ if (open) {
96
+ openRef.current = true;
97
+ onOpenChange?.(item, false);
98
+ }
99
+ },
100
+ onDrop: () => {
101
+ setState('idle');
102
+ if (openRef.current) {
103
+ onOpenChange?.(item, true);
104
+ }
105
+ },
106
+ }),
107
+ dropTargetForElements({
108
+ element: buttonRef.current,
109
+ getData: ({ input, element }) => {
110
+ return attachInstruction(item, {
111
+ input,
112
+ element,
113
+ indentPerLevel: DEFAULT_INDENTATION,
114
+ currentLevel: level,
115
+ mode,
116
+ block: isBranch ? [] : ['make-child'],
117
+ });
118
+ },
119
+ canDrop: ({ source }) => {
120
+ const _canDrop = canDrop ?? (() => true);
121
+ return source.element !== buttonRef.current && _canDrop(source.data as T, item);
122
+ },
123
+ getIsSticky: () => true,
124
+ onDrag: ({ self, source }) => {
125
+ const instruction = extractInstruction(self.data);
126
+
127
+ if (source.data.id !== item.id) {
128
+ if (instruction?.type === 'make-child' && isBranch && !open && !cancelExpandRef.current) {
129
+ cancelExpandRef.current = setTimeout(() => {
130
+ onOpenChange?.(item, true);
131
+ }, 500);
132
+ }
133
+
134
+ if (instruction?.type !== 'make-child') {
135
+ cancelExpand();
136
+ }
137
+
138
+ setInstruction(instruction);
139
+ } else if (instruction?.type === 'reparent') {
140
+ // TODO(wittjosiah): This is not occurring in the current implementation.
141
+ setInstruction(instruction);
142
+ } else {
143
+ setInstruction(null);
144
+ }
145
+ },
146
+ onDragLeave: () => {
147
+ cancelExpand();
148
+ setInstruction(null);
149
+ },
150
+ onDrop: () => {
151
+ cancelExpand();
152
+ setInstruction(null);
153
+ },
154
+ }),
155
+ );
156
+ }, [draggable, item, mode, open, canDrop]);
157
+
158
+ // Cancel expand on unmount.
159
+ useEffect(() => () => cancelExpand(), [cancelExpand]);
160
+
161
+ const handleOpenChange = useCallback(() => onOpenChange?.(item, !open), [onOpenChange, item, open]);
162
+
163
+ const handleSelect = useCallback(() => {
164
+ rowRef.current?.focus();
165
+ onSelect?.(item, !current);
166
+ }, [onSelect, item, current]);
167
+
168
+ const handleKeyDown = useCallback(
169
+ (event: KeyboardEvent) => {
170
+ switch (event.key) {
171
+ case 'ArrowRight':
172
+ isBranch && !open && handleOpenChange();
173
+ break;
174
+ case 'ArrowLeft':
175
+ isBranch && open && handleOpenChange();
176
+ break;
177
+ case ' ':
178
+ handleSelect();
179
+ break;
180
+ }
181
+ },
182
+ [isBranch, open, handleOpenChange, handleSelect],
183
+ );
184
+
185
+ return (
186
+ <Treegrid.Row
187
+ ref={rowRef}
188
+ key={id}
189
+ id={id}
190
+ aria-labelledby={`${id}__label`}
191
+ parentOf={parentOf?.join(Treegrid.PARENT_OF_SEPARATOR)}
192
+ classNames={mx(
193
+ 'grid grid-cols-subgrid col-[tree-row] aria-[current]:bg-input',
194
+ hoverableControls,
195
+ hoverableFocusedKeyboardControls,
196
+ hoverableFocusedWithinControls,
197
+ hoverableDescriptionIcons,
198
+ focusRing,
199
+ className,
200
+ )}
201
+ data-itemid={item.id}
202
+ data-testid={item.testId}
203
+ // NOTE(thure): This is intentionally an empty string to for descendents to select by in the CSS
204
+ // without alerting the user (except for in the correct link element). See also:
205
+ // https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current#description
206
+ aria-current={current ? ('' as 'page') : undefined}
207
+ onKeyDown={handleKeyDown}
208
+ onContextMenu={(event) => {
209
+ event.preventDefault();
210
+ setMenuOpen(true);
211
+ }}
212
+ >
213
+ <Treegrid.Cell
214
+ indent
215
+ classNames='relative grid grid-cols-subgrid col-[tree-row]'
216
+ style={paddingIndendation(level)}
217
+ >
218
+ <div role='none' className='flex items-center'>
219
+ <TreeItemToggle open={open} isBranch={isBranch} onToggle={handleOpenChange} />
220
+ <TreeItemHeading
221
+ ref={buttonRef}
222
+ label={label}
223
+ icon={icon}
224
+ className={headingClassName}
225
+ disabled={disabled}
226
+ current={current}
227
+ onSelect={handleSelect}
228
+ />
229
+ </div>
230
+ {Columns && <Columns item={item} menuOpen={menuOpen} setMenuOpen={setMenuOpen} />}
231
+ {instruction && <DropIndicator instruction={instruction} />}
232
+ </Treegrid.Cell>
233
+ </Treegrid.Row>
234
+ );
235
+ };
236
+
237
+ export const TreeItem = memo(RawTreeItem) as <T extends ItemType = ItemType>(prosp: TreeItemProps<T>) => JSX.Element;
@@ -0,0 +1,62 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { type KeyboardEvent, forwardRef, memo, useCallback } from 'react';
6
+
7
+ import { Button, Icon, toLocalizedString, useTranslation, type Label } from '@dxos/react-ui';
8
+ import { TextTooltip } from '@dxos/react-ui-text-tooltip';
9
+ import { mx } from '@dxos/react-ui-theme';
10
+
11
+ export type NavTreeItemHeadingProps = {
12
+ label: Label;
13
+ icon?: string;
14
+ className?: string;
15
+ disabled?: boolean;
16
+ current?: boolean;
17
+ onSelect?: () => void;
18
+ };
19
+
20
+ export const TreeItemHeading = memo(
21
+ forwardRef<HTMLButtonElement, NavTreeItemHeadingProps>(
22
+ ({ label, icon, className, disabled, current, onSelect }, forwardedRef) => {
23
+ const { t } = useTranslation();
24
+
25
+ const handleButtonKeydown = useCallback(
26
+ (event: KeyboardEvent) => {
27
+ if (event.key === ' ' || event.key === 'Enter') {
28
+ event.preventDefault();
29
+ event.stopPropagation();
30
+ onSelect?.();
31
+ }
32
+ },
33
+ [onSelect],
34
+ );
35
+
36
+ return (
37
+ <TextTooltip
38
+ text={toLocalizedString(label, t)}
39
+ side='bottom'
40
+ truncateQuery='span[data-tooltip]'
41
+ onlyWhenTruncating
42
+ asChild
43
+ ref={forwardedRef}
44
+ >
45
+ <Button
46
+ data-testid='treeItem.heading'
47
+ variant='ghost'
48
+ density='fine'
49
+ classNames={mx('grow gap-1 !pis-0.5 hover:!bg-transparent dark:hover:!bg-transparent', className)}
50
+ disabled={disabled}
51
+ onClick={onSelect}
52
+ onKeyDown={handleButtonKeydown}
53
+ {...(current && { 'aria-current': 'location' })}
54
+ >
55
+ {icon && <Icon icon={icon ?? 'ph--placeholder--regular'} size={4} classNames='is-[1em] bs-[1em] mlb-1' />}
56
+ <span className='flex-1 is-0 truncate text-start text-sm font-normal'>{toLocalizedString(label, t)}</span>
57
+ </Button>
58
+ </TextTooltip>
59
+ );
60
+ },
61
+ ),
62
+ );
@@ -0,0 +1,35 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { forwardRef, memo } from 'react';
6
+
7
+ import { Button, Icon } from '@dxos/react-ui';
8
+ import { mx } from '@dxos/react-ui-theme';
9
+
10
+ export type TreeItemToggleProps = {
11
+ open?: boolean;
12
+ isBranch?: boolean;
13
+ onToggle?: () => void;
14
+ };
15
+
16
+ export const TreeItemToggle = memo(
17
+ forwardRef<HTMLButtonElement, TreeItemToggleProps>(({ open, isBranch, onToggle }, forwardedRef) => {
18
+ return (
19
+ <Button
20
+ ref={forwardedRef}
21
+ data-testid='treeItem.toggle'
22
+ variant='ghost'
23
+ density='fine'
24
+ classNames={mx('!pli-1', !isBranch && 'invisible')}
25
+ onClick={onToggle}
26
+ >
27
+ <Icon
28
+ icon='ph--caret-right--regular'
29
+ size={3}
30
+ classNames={mx('transition duration-200', open && 'rotate-90')}
31
+ />
32
+ </Button>
33
+ );
34
+ }),
35
+ );
@@ -0,0 +1,25 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type ItemMode } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
6
+
7
+ import { type ItemType } from './types';
8
+
9
+ export const DEFAULT_INDENTATION = 8;
10
+
11
+ export const paddingIndendation = (level: number, indentation = DEFAULT_INDENTATION) => ({
12
+ paddingInlineStart: `${(level - 1) * indentation}px`,
13
+ });
14
+
15
+ export const getMode = (items: ItemType[], index: number): ItemMode => {
16
+ const item = items[index];
17
+ const next = items[index + 1];
18
+ if (!next || item.path.length > next.path.length) {
19
+ return 'last-in-group';
20
+ } else if (item.path.length < next.path.length) {
21
+ return 'expanded';
22
+ } else {
23
+ return 'standard';
24
+ }
25
+ };
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './Tree';
6
+ export * from './TreeItem';
7
+ export * from './types';
@@ -0,0 +1,170 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type Instruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
6
+
7
+ import { S } from '@dxos/echo-schema';
8
+ import { log } from '@dxos/log';
9
+ import { faker } from '@dxos/random';
10
+ import { Path } from '@dxos/react-ui-mosaic';
11
+
12
+ import { type ItemType } from './types';
13
+
14
+ export type TestItem = {
15
+ id: string;
16
+ name: string;
17
+ icon?: string;
18
+ items: TestItem[];
19
+ };
20
+
21
+ export const TestItemSchema = S.Struct({
22
+ id: S.String,
23
+ name: S.String,
24
+ icon: S.optional(S.String),
25
+ items: S.mutable(S.Array(S.suspend((): S.Schema<TestItem> => TestItemSchema))),
26
+ });
27
+
28
+ export const createTree = (n = 4, d = 4): TestItem => ({
29
+ id: faker.string.uuid(),
30
+ name: faker.commerce.productName(),
31
+ icon:
32
+ d === 3
33
+ ? undefined
34
+ : faker.helpers.arrayElement([
35
+ 'ph--planet--regular',
36
+ 'ph--sailboat--regular',
37
+ 'ph--house--regular',
38
+ 'ph--gear--regular',
39
+ ]),
40
+ items: d > 0 ? faker.helpers.multiple(() => createTree(n, d - 1), { count: n }) : [],
41
+ });
42
+
43
+ function* visitor({
44
+ testItem,
45
+ getItem,
46
+ isOpen,
47
+ }: {
48
+ testItem: TestItem;
49
+ getItem: (testItem: TestItem, parent?: readonly string[]) => ItemType;
50
+ isOpen?: (testItem: ItemType) => boolean;
51
+ }): Generator<ItemType> {
52
+ const stack: [TestItem, ItemType][] = [[testItem, getItem(testItem)]];
53
+ while (stack.length > 0) {
54
+ const [testItem, item] = stack.pop()!;
55
+ if (item.path.length > 1) {
56
+ yield item;
57
+ }
58
+
59
+ const children = Array.from(testItem.items ?? []);
60
+ if (item.path.length === 1 || isOpen?.(item)) {
61
+ for (let i = children.length - 1; i >= 0; i--) {
62
+ const child = children[i];
63
+ stack.push([child, getItem(child, item.path)]);
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ export const flattenTree = (tree: TestItem, open: string[], getItem: (tree: TestItem) => ItemType): ItemType[] => {
70
+ return Array.from(
71
+ visitor({
72
+ testItem: tree,
73
+ getItem,
74
+ isOpen: ({ path }) => open.includes(Path.create(...path)),
75
+ }),
76
+ );
77
+ };
78
+
79
+ // Ensures that the same item is not created multiple times, causing the tree to be re-rendered.
80
+ const itemsCache: Record<string, ItemType> = {};
81
+
82
+ export const getItem = (testItem: TestItem, parent?: string[]): ItemType => {
83
+ const cachedItem = itemsCache[testItem.id];
84
+ if (cachedItem) {
85
+ return cachedItem;
86
+ }
87
+
88
+ const item = {
89
+ id: testItem.id,
90
+ label: testItem.name,
91
+ icon: testItem.icon,
92
+ path: parent ? [...parent, testItem.id] : [testItem.id],
93
+ ...((testItem.items?.length ?? 0) > 0 && {
94
+ parentOf: testItem.items!.map(({ id }) => id),
95
+ }),
96
+ };
97
+ itemsCache[testItem.id] = item;
98
+ return item;
99
+ };
100
+
101
+ export const invalidateCache = (id: string) => {
102
+ delete itemsCache[id];
103
+ };
104
+
105
+ const removeItem = (tree: TestItem, source: ItemType) => {
106
+ const parent = getTestItem(tree, source.path.slice(1, -1));
107
+ const index = parent.items!.findIndex(({ id }) => id === source.id);
108
+ const item = parent.items[index];
109
+ parent.items!.splice(index, 1);
110
+ return item;
111
+ };
112
+
113
+ const getTestItem = (tree: TestItem, path: string[]) => {
114
+ let item = tree;
115
+ for (const part of path) {
116
+ item = item.items!.find(({ id }) => id === part)!;
117
+ }
118
+ return item;
119
+ };
120
+
121
+ // TODO(wittjosiah): Reconcile with plugin-navtree/testing/vistor.
122
+ export const updateState = ({
123
+ state,
124
+ instruction,
125
+ source,
126
+ target,
127
+ }: {
128
+ state: TestItem;
129
+ instruction: Instruction;
130
+ source: ItemType;
131
+ target: ItemType;
132
+ }) => {
133
+ switch (instruction.type) {
134
+ case 'reorder-above': {
135
+ invalidateCache(source.id);
136
+ const item = removeItem(state, source);
137
+ const parent = getTestItem(state, target.path.slice(1, -1));
138
+ const index = parent.items!.findIndex(({ id }) => id === target.id);
139
+ invalidateCache(target.id);
140
+ parent.items!.splice(index, 0, item);
141
+ break;
142
+ }
143
+
144
+ case 'reorder-below': {
145
+ invalidateCache(source.id);
146
+ const item = removeItem(state, source);
147
+ const parent = getTestItem(state, target.path.slice(1, -1));
148
+ const index = parent.items!.findIndex(({ id }) => id === target.id);
149
+ invalidateCache(target.id);
150
+ parent.items!.splice(index + 1, 0, item);
151
+ break;
152
+ }
153
+
154
+ case 'make-child': {
155
+ invalidateCache(source.id);
156
+ const item = removeItem(state, source);
157
+ const parent = getTestItem(state, target.path.slice(1));
158
+ invalidateCache(target.id);
159
+ parent.items!.push(item);
160
+ break;
161
+ }
162
+
163
+ case 'instruction-blocked':
164
+ break;
165
+
166
+ default:
167
+ log.warn('Unsupported instruction', instruction);
168
+ break;
169
+ }
170
+ };
@@ -0,0 +1,34 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { S } from '@dxos/echo-schema';
6
+
7
+ const LabelSchema = S.Union(
8
+ S.String,
9
+ S.Tuple(
10
+ S.String,
11
+ S.Struct({
12
+ ns: S.String,
13
+ count: S.optional(S.Number),
14
+ }),
15
+ ),
16
+ );
17
+
18
+ export const ItemSchema = S.mutable(
19
+ S.Struct({
20
+ id: S.String,
21
+ label: S.mutable(LabelSchema),
22
+ icon: S.optional(S.String),
23
+ disabled: S.optional(S.Boolean),
24
+ className: S.optional(S.String),
25
+ headingClassName: S.optional(S.String),
26
+ testId: S.optional(S.String),
27
+ path: S.Array(S.String),
28
+ parentOf: S.optional(S.Array(S.String)),
29
+ }),
30
+ );
31
+
32
+ export type ItemType = S.Schema.Type<typeof ItemSchema>;
33
+
34
+ export const isItem: (item: unknown) => boolean = S.is(ItemSchema);
@@ -3,3 +3,4 @@
3
3
  //
4
4
 
5
5
  export * from './List';
6
+ export * from './Tree';
@@ -1 +0,0 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../../src/testing.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,mBAAmB,CAAC;AAGtC,eAAO,MAAM,cAAc;;;EAGzB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,cAAc,CAAC,CAAC;AAEhE,eAAO,MAAM,QAAQ;;;;;EAEnB,CAAC;AAEH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,QAAQ,CAAC,CAAC;AAEtD,eAAO,MAAM,UAAU,kBAAa,QAQlC,CAAC"}
File without changes