@dxos/react-ui-list 0.8.4-main.a4bbb77 → 0.8.4-main.abd8ff62ef

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 (101) hide show
  1. package/dist/lib/browser/index.mjs +1349 -718
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +1349 -718
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/components/Accordion/Accordion.d.ts +1 -1
  8. package/dist/types/src/components/Accordion/Accordion.d.ts.map +1 -1
  9. package/dist/types/src/components/Accordion/Accordion.stories.d.ts +0 -3
  10. package/dist/types/src/components/Accordion/Accordion.stories.d.ts.map +1 -1
  11. package/dist/types/src/components/Accordion/AccordionItem.d.ts.map +1 -1
  12. package/dist/types/src/components/Accordion/AccordionRoot.d.ts +1 -1
  13. package/dist/types/src/components/Accordion/AccordionRoot.d.ts.map +1 -1
  14. package/dist/types/src/components/Combobox/Combobox.d.ts +105 -0
  15. package/dist/types/src/components/Combobox/Combobox.d.ts.map +1 -0
  16. package/dist/types/src/components/Combobox/Combobox.stories.d.ts +12 -0
  17. package/dist/types/src/components/Combobox/Combobox.stories.d.ts.map +1 -0
  18. package/dist/types/src/components/Combobox/index.d.ts +2 -0
  19. package/dist/types/src/components/Combobox/index.d.ts.map +1 -0
  20. package/dist/types/src/components/List/List.d.ts +19 -8
  21. package/dist/types/src/components/List/List.d.ts.map +1 -1
  22. package/dist/types/src/components/List/List.stories.d.ts +2 -2
  23. package/dist/types/src/components/List/List.stories.d.ts.map +1 -1
  24. package/dist/types/src/components/List/ListItem.d.ts +10 -8
  25. package/dist/types/src/components/List/ListItem.d.ts.map +1 -1
  26. package/dist/types/src/components/List/ListRoot.d.ts +2 -2
  27. package/dist/types/src/components/List/ListRoot.d.ts.map +1 -1
  28. package/dist/types/src/components/List/testing.d.ts +1 -1
  29. package/dist/types/src/components/List/testing.d.ts.map +1 -1
  30. package/dist/types/src/components/Listbox/Listbox.d.ts +27 -0
  31. package/dist/types/src/components/Listbox/Listbox.d.ts.map +1 -0
  32. package/dist/types/src/components/Listbox/Listbox.stories.d.ts +12 -0
  33. package/dist/types/src/components/Listbox/Listbox.stories.d.ts.map +1 -0
  34. package/dist/types/src/components/Listbox/index.d.ts +2 -0
  35. package/dist/types/src/components/Listbox/index.d.ts.map +1 -0
  36. package/dist/types/src/components/Picker/Picker.d.ts +49 -0
  37. package/dist/types/src/components/Picker/Picker.d.ts.map +1 -0
  38. package/dist/types/src/components/Picker/Picker.stories.d.ts +28 -0
  39. package/dist/types/src/components/Picker/Picker.stories.d.ts.map +1 -0
  40. package/dist/types/src/components/Picker/context.d.ts +29 -0
  41. package/dist/types/src/components/Picker/context.d.ts.map +1 -0
  42. package/dist/types/src/components/Picker/index.d.ts +3 -0
  43. package/dist/types/src/components/Picker/index.d.ts.map +1 -0
  44. package/dist/types/src/components/RowList/RowList.d.ts +61 -0
  45. package/dist/types/src/components/RowList/RowList.d.ts.map +1 -0
  46. package/dist/types/src/components/RowList/RowList.stories.d.ts +35 -0
  47. package/dist/types/src/components/RowList/RowList.stories.d.ts.map +1 -0
  48. package/dist/types/src/components/RowList/index.d.ts +3 -0
  49. package/dist/types/src/components/RowList/index.d.ts.map +1 -0
  50. package/dist/types/src/components/Tree/Tree.d.ts +10 -6
  51. package/dist/types/src/components/Tree/Tree.d.ts.map +1 -1
  52. package/dist/types/src/components/Tree/Tree.stories.d.ts +9 -28
  53. package/dist/types/src/components/Tree/Tree.stories.d.ts.map +1 -1
  54. package/dist/types/src/components/Tree/TreeContext.d.ts +24 -10
  55. package/dist/types/src/components/Tree/TreeContext.d.ts.map +1 -1
  56. package/dist/types/src/components/Tree/TreeItem.d.ts +25 -4
  57. package/dist/types/src/components/Tree/TreeItem.d.ts.map +1 -1
  58. package/dist/types/src/components/Tree/TreeItemHeading.d.ts +4 -3
  59. package/dist/types/src/components/Tree/TreeItemHeading.d.ts.map +1 -1
  60. package/dist/types/src/components/Tree/TreeItemToggle.d.ts +3 -3
  61. package/dist/types/src/components/Tree/TreeItemToggle.d.ts.map +1 -1
  62. package/dist/types/src/components/Tree/helpers.d.ts.map +1 -1
  63. package/dist/types/src/components/Tree/index.d.ts +2 -0
  64. package/dist/types/src/components/Tree/index.d.ts.map +1 -1
  65. package/dist/types/src/components/Tree/testing.d.ts +3 -3
  66. package/dist/types/src/components/Tree/testing.d.ts.map +1 -1
  67. package/dist/types/src/components/index.d.ts +4 -0
  68. package/dist/types/src/components/index.d.ts.map +1 -1
  69. package/dist/types/src/util/path.d.ts.map +1 -1
  70. package/dist/types/tsconfig.tsbuildinfo +1 -1
  71. package/package.json +34 -31
  72. package/src/components/Accordion/Accordion.stories.tsx +5 -8
  73. package/src/components/Accordion/AccordionItem.tsx +3 -4
  74. package/src/components/Accordion/AccordionRoot.tsx +1 -1
  75. package/src/components/Combobox/Combobox.stories.tsx +60 -0
  76. package/src/components/Combobox/Combobox.tsx +387 -0
  77. package/src/components/Combobox/index.ts +5 -0
  78. package/src/components/List/List.stories.tsx +34 -22
  79. package/src/components/List/List.tsx +14 -10
  80. package/src/components/List/ListItem.tsx +60 -40
  81. package/src/components/List/ListRoot.tsx +3 -3
  82. package/src/components/List/testing.ts +7 -7
  83. package/src/components/Listbox/Listbox.stories.tsx +48 -0
  84. package/src/components/Listbox/Listbox.tsx +201 -0
  85. package/src/components/Listbox/index.ts +5 -0
  86. package/src/components/Picker/Picker.stories.tsx +131 -0
  87. package/src/components/Picker/Picker.tsx +439 -0
  88. package/src/components/Picker/context.ts +43 -0
  89. package/src/components/Picker/index.ts +6 -0
  90. package/src/components/RowList/RowList.stories.tsx +163 -0
  91. package/src/components/RowList/RowList.tsx +353 -0
  92. package/src/components/RowList/index.ts +6 -0
  93. package/src/components/Tree/Tree.stories.tsx +153 -64
  94. package/src/components/Tree/Tree.tsx +43 -40
  95. package/src/components/Tree/TreeContext.tsx +21 -9
  96. package/src/components/Tree/TreeItem.tsx +214 -127
  97. package/src/components/Tree/TreeItemHeading.tsx +10 -8
  98. package/src/components/Tree/TreeItemToggle.tsx +29 -18
  99. package/src/components/Tree/index.ts +2 -0
  100. package/src/components/Tree/testing.ts +10 -9
  101. package/src/components/index.ts +4 -0
@@ -1,85 +1,63 @@
1
1
  // src/components/Accordion/AccordionItem.tsx
2
- import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
3
2
  import * as AccordionPrimitive2 from "@radix-ui/react-accordion";
4
3
  import { createContext as createContext2 } from "@radix-ui/react-context";
5
4
  import React2 from "react";
6
5
  import { Icon } from "@dxos/react-ui";
7
- import { mx as mx2 } from "@dxos/react-ui-theme";
6
+ import { mx as mx2 } from "@dxos/ui-theme";
8
7
 
9
8
  // src/components/Accordion/AccordionRoot.tsx
10
- import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
11
9
  import * as AccordionPrimitive from "@radix-ui/react-accordion";
12
10
  import { createContext } from "@radix-ui/react-context";
13
11
  import React from "react";
14
- import { mx } from "@dxos/react-ui-theme";
12
+ import { mx } from "@dxos/ui-theme";
15
13
  var ACCORDION_NAME = "Accordion";
16
14
  var [AccordionProvider, useAccordionContext] = createContext(ACCORDION_NAME);
17
15
  var defaultGetId = (item) => item?.id;
18
16
  var AccordionRoot = ({ classNames, items, getId = defaultGetId, children, value, defaultValue, onValueChange }) => {
19
- var _effect = _useSignals();
20
- try {
21
- return /* @__PURE__ */ React.createElement(AccordionProvider, {
22
- getId
23
- }, /* @__PURE__ */ React.createElement(AccordionPrimitive.Root, {
24
- type: "multiple",
25
- value,
26
- defaultValue,
27
- onValueChange,
28
- className: mx(classNames)
29
- }, children?.({
30
- items: items ?? []
31
- })));
32
- } finally {
33
- _effect.f();
34
- }
17
+ return /* @__PURE__ */ React.createElement(AccordionProvider, {
18
+ getId
19
+ }, /* @__PURE__ */ React.createElement(AccordionPrimitive.Root, {
20
+ type: "multiple",
21
+ value,
22
+ defaultValue,
23
+ onValueChange,
24
+ className: mx(classNames)
25
+ }, children?.({
26
+ items: items ?? []
27
+ })));
35
28
  };
36
29
 
37
30
  // src/components/Accordion/AccordionItem.tsx
38
31
  var ACCORDION_ITEM_NAME = "AccordionItem";
39
32
  var [AccordionItemProvider, useDxAccordionItemContext] = createContext2(ACCORDION_ITEM_NAME);
40
33
  var AccordionItem = ({ children, classNames, item }) => {
41
- var _effect = _useSignals2();
42
- try {
43
- const { getId } = useAccordionContext(ACCORDION_ITEM_NAME);
44
- return /* @__PURE__ */ React2.createElement(AccordionItemProvider, {
45
- item
46
- }, /* @__PURE__ */ React2.createElement(AccordionPrimitive2.Item, {
47
- value: getId(item),
48
- className: mx2("overflow-hidden", classNames)
49
- }, children));
50
- } finally {
51
- _effect.f();
52
- }
34
+ const { getId } = useAccordionContext(ACCORDION_ITEM_NAME);
35
+ return /* @__PURE__ */ React2.createElement(AccordionItemProvider, {
36
+ item
37
+ }, /* @__PURE__ */ React2.createElement(AccordionPrimitive2.Item, {
38
+ value: getId(item),
39
+ className: mx2("overflow-hidden", classNames)
40
+ }, children));
53
41
  };
54
42
  var AccordionItemHeader = ({ classNames, children, ...props }) => {
55
- var _effect = _useSignals2();
56
- try {
57
- return /* @__PURE__ */ React2.createElement(AccordionPrimitive2.Header, {
58
- ...props,
59
- className: mx2(classNames)
60
- }, /* @__PURE__ */ React2.createElement(AccordionPrimitive2.Trigger, {
61
- className: "group flex items-center p-2 dx-focus-ring-inset is-full text-start"
62
- }, children, /* @__PURE__ */ React2.createElement(Icon, {
63
- icon: "ph--caret-right--regular",
64
- size: 4,
65
- classNames: "transition-transform duration-200 group-data-[state=open]:rotate-90"
66
- })));
67
- } finally {
68
- _effect.f();
69
- }
43
+ return /* @__PURE__ */ React2.createElement(AccordionPrimitive2.Header, {
44
+ ...props,
45
+ className: mx2(classNames)
46
+ }, /* @__PURE__ */ React2.createElement(AccordionPrimitive2.Trigger, {
47
+ className: "group flex items-center p-2 dx-focus-ring-inset w-full text-start"
48
+ }, children, /* @__PURE__ */ React2.createElement(Icon, {
49
+ icon: "ph--caret-right--regular",
50
+ size: 4,
51
+ classNames: "transition-transform duration-200 group-data-[state=open]:rotate-90"
52
+ })));
70
53
  };
71
54
  var AccordionItemBody = ({ children, classNames }) => {
72
- var _effect = _useSignals2();
73
- try {
74
- return /* @__PURE__ */ React2.createElement(AccordionPrimitive2.Content, {
75
- className: "overflow-hidden data-[state=closed]:animate-slideUp data-[state=open]:animate-slideDown"
76
- }, /* @__PURE__ */ React2.createElement("div", {
77
- role: "none",
78
- className: mx2("p-2", classNames)
79
- }, children));
80
- } finally {
81
- _effect.f();
82
- }
55
+ return /* @__PURE__ */ React2.createElement(AccordionPrimitive2.Content, {
56
+ className: "overflow-hidden data-[state=closed]:animate-slide-up data-[state=open]:animate-slide-down"
57
+ }, /* @__PURE__ */ React2.createElement("div", {
58
+ role: "none",
59
+ className: mx2("p-2", classNames)
60
+ }, children));
83
61
  };
84
62
 
85
63
  // src/components/Accordion/Accordion.tsx
@@ -90,92 +68,525 @@ var Accordion = {
90
68
  ItemBody: AccordionItemBody
91
69
  };
92
70
 
71
+ // src/components/Combobox/Combobox.tsx
72
+ import { createContext as createContext4 } from "@radix-ui/react-context";
73
+ import { useControllableState } from "@radix-ui/react-use-controllable-state";
74
+ import React4, { forwardRef as forwardRef2, useCallback as useCallback2 } from "react";
75
+ import { Button, Icon as Icon2, Popover, ScrollArea, useId } from "@dxos/react-ui";
76
+ import { composable, composableProps, mx as mx4 } from "@dxos/ui-theme";
77
+
78
+ // src/components/Picker/Picker.tsx
79
+ import { Slot } from "@radix-ui/react-slot";
80
+ import React3, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react";
81
+ import { Input, useThemeContext } from "@dxos/react-ui";
82
+ import { mx as mx3 } from "@dxos/ui-theme";
83
+
84
+ // src/components/Picker/context.ts
85
+ import { createContext as createContext3 } from "@radix-ui/react-context";
86
+ var [PickerItemContextProvider, usePickerItemContext] = createContext3("PickerItem");
87
+ var [PickerInputContextProvider, usePickerInputContext] = createContext3("PickerInput");
88
+
89
+ // src/components/Picker/Picker.tsx
90
+ var PickerRoot = ({ children }) => {
91
+ const [selectedValue, setSelectedValue] = useState(void 0);
92
+ const itemsRef = useRef(/* @__PURE__ */ new Map());
93
+ const [itemVersion, setItemVersion] = useState(0);
94
+ useEffect(() => {
95
+ const current = selectedValue !== void 0 ? itemsRef.current.get(selectedValue) : void 0;
96
+ const isValid = current !== void 0 && !current.disabled;
97
+ if (!isValid && itemsRef.current.size > 0) {
98
+ const entries = Array.from(itemsRef.current.entries()).filter(([, data]) => !data.disabled);
99
+ if (entries.length > 0) {
100
+ entries.sort(([, a], [, b]) => {
101
+ const position = a.element.compareDocumentPosition(b.element);
102
+ if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
103
+ return -1;
104
+ }
105
+ if (position & Node.DOCUMENT_POSITION_PRECEDING) {
106
+ return 1;
107
+ }
108
+ return 0;
109
+ });
110
+ const firstValue = entries[0]?.[0];
111
+ if (firstValue !== void 0 && firstValue !== selectedValue) {
112
+ setSelectedValue(firstValue);
113
+ }
114
+ } else if (selectedValue !== void 0) {
115
+ setSelectedValue(void 0);
116
+ }
117
+ }
118
+ }, [
119
+ itemVersion,
120
+ selectedValue
121
+ ]);
122
+ const registerItem = useCallback((value, element, onSelect, disabled) => {
123
+ if (element) {
124
+ itemsRef.current.set(value, {
125
+ element,
126
+ onSelect,
127
+ disabled
128
+ });
129
+ setItemVersion((v) => v + 1);
130
+ }
131
+ }, []);
132
+ const unregisterItem = useCallback((value) => {
133
+ itemsRef.current.delete(value);
134
+ setItemVersion((v) => v + 1);
135
+ }, []);
136
+ const getItemValues = useCallback(() => {
137
+ return Array.from(itemsRef.current.entries()).filter(([, data]) => !data.disabled).sort(([, a], [, b]) => {
138
+ const position = a.element.compareDocumentPosition(b.element);
139
+ return position & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : position & Node.DOCUMENT_POSITION_PRECEDING ? 1 : 0;
140
+ }).map(([value]) => value);
141
+ }, []);
142
+ const triggerSelect = useCallback(() => {
143
+ if (selectedValue !== void 0) {
144
+ const item = itemsRef.current.get(selectedValue);
145
+ item?.onSelect?.();
146
+ }
147
+ }, [
148
+ selectedValue
149
+ ]);
150
+ const itemContextValue = useMemo(() => ({
151
+ selectedValue,
152
+ onSelectedValueChange: setSelectedValue,
153
+ registerItem,
154
+ unregisterItem
155
+ }), [
156
+ selectedValue,
157
+ registerItem,
158
+ unregisterItem
159
+ ]);
160
+ const inputContextValue = useMemo(() => ({
161
+ selectedValue,
162
+ onSelectedValueChange: setSelectedValue,
163
+ getItemValues,
164
+ triggerSelect
165
+ }), [
166
+ selectedValue,
167
+ getItemValues,
168
+ triggerSelect
169
+ ]);
170
+ return /* @__PURE__ */ React3.createElement(PickerInputContextProvider, inputContextValue, /* @__PURE__ */ React3.createElement(PickerItemContextProvider, itemContextValue, children));
171
+ };
172
+ PickerRoot.displayName = "Picker.Root";
173
+ var PickerInput = /* @__PURE__ */ forwardRef(({ value, onValueChange, onChange, onKeyDown, autoFocus, ...props }, forwardedRef) => {
174
+ const { hasIosKeyboard } = useThemeContext();
175
+ const { selectedValue, onSelectedValueChange, getItemValues, triggerSelect } = usePickerInputContext("Picker.Input");
176
+ const handleChange = useCallback((event) => {
177
+ onValueChange?.(event.target.value);
178
+ onChange?.(event);
179
+ }, [
180
+ onValueChange,
181
+ onChange
182
+ ]);
183
+ const handleKeyDown = useCallback((event) => {
184
+ onKeyDown?.(event);
185
+ if (event.defaultPrevented) {
186
+ return;
187
+ }
188
+ const values = getItemValues();
189
+ if (values.length === 0) {
190
+ if (event.key === "Escape") {
191
+ onValueChange?.("");
192
+ }
193
+ return;
194
+ }
195
+ const currentIndex = selectedValue !== void 0 ? values.indexOf(selectedValue) : -1;
196
+ switch (event.key) {
197
+ case "ArrowDown": {
198
+ event.preventDefault();
199
+ const nextIndex = currentIndex === -1 ? 0 : Math.min(currentIndex + 1, values.length - 1);
200
+ const nextValue = values[nextIndex];
201
+ if (nextValue !== void 0) {
202
+ onSelectedValueChange(nextValue);
203
+ }
204
+ break;
205
+ }
206
+ case "ArrowUp": {
207
+ event.preventDefault();
208
+ const prevIndex = currentIndex === -1 ? values.length - 1 : Math.max(currentIndex - 1, 0);
209
+ const prevValue = values[prevIndex];
210
+ if (prevValue !== void 0) {
211
+ onSelectedValueChange(prevValue);
212
+ }
213
+ break;
214
+ }
215
+ case "Enter": {
216
+ if (selectedValue !== void 0) {
217
+ event.preventDefault();
218
+ triggerSelect();
219
+ }
220
+ break;
221
+ }
222
+ case "Home": {
223
+ event.preventDefault();
224
+ const firstValue = values[0];
225
+ if (firstValue !== void 0) {
226
+ onSelectedValueChange(firstValue);
227
+ }
228
+ break;
229
+ }
230
+ case "End": {
231
+ event.preventDefault();
232
+ const lastValue = values[values.length - 1];
233
+ if (lastValue !== void 0) {
234
+ onSelectedValueChange(lastValue);
235
+ }
236
+ break;
237
+ }
238
+ case "Escape": {
239
+ event.preventDefault();
240
+ if (selectedValue !== void 0) {
241
+ onSelectedValueChange(void 0);
242
+ } else {
243
+ onValueChange?.("");
244
+ }
245
+ break;
246
+ }
247
+ }
248
+ }, [
249
+ selectedValue,
250
+ onSelectedValueChange,
251
+ getItemValues,
252
+ triggerSelect,
253
+ onValueChange,
254
+ onKeyDown
255
+ ]);
256
+ return /* @__PURE__ */ React3.createElement(Input.Root, null, /* @__PURE__ */ React3.createElement(Input.TextInput, {
257
+ ...props,
258
+ autoFocus: autoFocus && !hasIosKeyboard,
259
+ ...value !== void 0 && {
260
+ value
261
+ },
262
+ onChange: handleChange,
263
+ onKeyDown: handleKeyDown,
264
+ ref: forwardedRef
265
+ }));
266
+ });
267
+ PickerInput.displayName = "Picker.Input";
268
+ var PickerItem = /* @__PURE__ */ forwardRef(({ classNames, value, onSelect, disabled, asChild, children, ...props }, forwardedRef) => {
269
+ const { selectedValue, onSelectedValueChange, registerItem, unregisterItem } = usePickerItemContext("Picker.Item");
270
+ const internalRef = useRef(null);
271
+ const isSelected = selectedValue === value && !disabled;
272
+ useEffect(() => {
273
+ const element = internalRef.current;
274
+ if (element) {
275
+ registerItem(value, element, onSelect, disabled);
276
+ }
277
+ return () => unregisterItem(value);
278
+ }, [
279
+ value,
280
+ onSelect,
281
+ disabled,
282
+ registerItem,
283
+ unregisterItem
284
+ ]);
285
+ useEffect(() => {
286
+ if (isSelected && internalRef.current) {
287
+ internalRef.current.scrollIntoView({
288
+ block: "nearest",
289
+ behavior: "smooth"
290
+ });
291
+ }
292
+ }, [
293
+ isSelected
294
+ ]);
295
+ const handleClick = useCallback(() => {
296
+ if (disabled) {
297
+ return;
298
+ }
299
+ onSelectedValueChange(value);
300
+ onSelect?.();
301
+ }, [
302
+ disabled,
303
+ value,
304
+ onSelectedValueChange,
305
+ onSelect
306
+ ]);
307
+ const handleMouseDown = useCallback((event) => {
308
+ event.preventDefault();
309
+ }, []);
310
+ const Comp = asChild ? Slot : "div";
311
+ return /* @__PURE__ */ React3.createElement(Comp, {
312
+ ...props,
313
+ ref: (node) => {
314
+ internalRef.current = node;
315
+ if (typeof forwardedRef === "function") {
316
+ forwardedRef(node);
317
+ } else if (forwardedRef) {
318
+ forwardedRef.current = node;
319
+ }
320
+ },
321
+ role: "option",
322
+ "aria-selected": isSelected,
323
+ "aria-disabled": disabled,
324
+ "data-selected": isSelected,
325
+ "data-disabled": disabled,
326
+ "data-value": value,
327
+ // tabIndex={-1} — combobox pattern keeps browser focus on the
328
+ // input; the selected option is highlighted via `aria-selected`,
329
+ // not via DOM focus. Differs from `Row` (listbox pattern,
330
+ // tabIndex={0}). See header comment.
331
+ tabIndex: -1,
332
+ className: mx3("dx-hover dx-selected px-[var(--gutter,0.75rem)] py-1 cursor-pointer select-none", disabled && "opacity-50 cursor-not-allowed", classNames),
333
+ onMouseDown: handleMouseDown,
334
+ onClick: handleClick
335
+ }, children);
336
+ });
337
+ PickerItem.displayName = "Picker.Item";
338
+ var Picker = {
339
+ Root: PickerRoot,
340
+ Input: PickerInput,
341
+ Item: PickerItem
342
+ };
343
+
344
+ // src/components/Combobox/Combobox.tsx
345
+ var COMBOBOX_NAME = "Combobox";
346
+ var COMBOBOX_CONTENT_NAME = "ComboboxContent";
347
+ var COMBOBOX_ITEM_NAME = "ComboboxItem";
348
+ var COMBOBOX_TRIGGER_NAME = "ComboboxTrigger";
349
+ var [ComboboxProvider, useComboboxContext] = createContext4(COMBOBOX_NAME, {});
350
+ var ComboboxRoot = ({ children, modal, modalId: modalIdProp, open: openProp, defaultOpen, onOpenChange: propsOnOpenChange, value: valueProp, defaultValue, onValueChange: propsOnValueChange, placeholder }) => {
351
+ const modalId = useId(COMBOBOX_NAME, modalIdProp);
352
+ const [open = false, onOpenChange] = useControllableState({
353
+ prop: openProp,
354
+ defaultProp: defaultOpen,
355
+ onChange: propsOnOpenChange
356
+ });
357
+ const [value = "", onValueChange] = useControllableState({
358
+ prop: valueProp,
359
+ defaultProp: defaultValue,
360
+ onChange: propsOnValueChange
361
+ });
362
+ return /* @__PURE__ */ React4.createElement(Popover.Root, {
363
+ open,
364
+ onOpenChange,
365
+ modal
366
+ }, /* @__PURE__ */ React4.createElement(ComboboxProvider, {
367
+ isCombobox: true,
368
+ modalId,
369
+ placeholder,
370
+ open,
371
+ onOpenChange,
372
+ value,
373
+ onValueChange
374
+ }, children));
375
+ };
376
+ var ComboboxContent = composable(({ children, ...props }, forwardedRef) => {
377
+ const { modalId } = useComboboxContext(COMBOBOX_CONTENT_NAME);
378
+ return /* @__PURE__ */ React4.createElement(Popover.Content, {
379
+ ...composableProps(props, {
380
+ id: modalId
381
+ }),
382
+ ref: forwardedRef
383
+ }, /* @__PURE__ */ React4.createElement(Popover.Viewport, {
384
+ classNames: "w-(--radix-popover-trigger-width)"
385
+ }, /* @__PURE__ */ React4.createElement(Picker.Root, null, children)));
386
+ });
387
+ ComboboxContent.displayName = COMBOBOX_CONTENT_NAME;
388
+ var ComboboxTrigger = /* @__PURE__ */ forwardRef2(({ children, onClick, ...props }, forwardedRef) => {
389
+ const { modalId, open, onOpenChange, placeholder, value } = useComboboxContext(COMBOBOX_TRIGGER_NAME);
390
+ const handleClick = useCallback2((event) => {
391
+ onClick?.(event);
392
+ onOpenChange?.(true);
393
+ }, [
394
+ onClick,
395
+ onOpenChange
396
+ ]);
397
+ return /* @__PURE__ */ React4.createElement(Popover.Trigger, {
398
+ asChild: true
399
+ }, /* @__PURE__ */ React4.createElement(Button, {
400
+ ...props,
401
+ role: "combobox",
402
+ "aria-expanded": open,
403
+ "aria-controls": modalId,
404
+ "aria-haspopup": "dialog",
405
+ onClick: handleClick,
406
+ ref: forwardedRef
407
+ }, children ?? /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement("span", {
408
+ className: mx4("font-normal text-start flex-1 min-w-0 truncate me-2", !value && "text-subdued")
409
+ }, value || placeholder), /* @__PURE__ */ React4.createElement(Icon2, {
410
+ icon: "ph--caret-down--bold",
411
+ size: 3
412
+ }))));
413
+ });
414
+ ComboboxTrigger.displayName = COMBOBOX_TRIGGER_NAME;
415
+ var ComboboxVirtualTrigger = Popover.VirtualTrigger;
416
+ var ComboboxInput = /* @__PURE__ */ forwardRef2(({ classNames, ...props }, forwardedRef) => {
417
+ return /* @__PURE__ */ React4.createElement(Picker.Input, {
418
+ ...props,
419
+ classNames: [
420
+ "m-form-chrome mb-0 w-[calc(100%-2*var(--spacing-form-chrome))]",
421
+ classNames
422
+ ],
423
+ ref: forwardedRef
424
+ });
425
+ });
426
+ ComboboxInput.displayName = "Combobox.Input";
427
+ var ComboboxList = /* @__PURE__ */ forwardRef2(({ classNames, children, ...props }, forwardedRef) => {
428
+ return /* @__PURE__ */ React4.createElement(ScrollArea.Root, {
429
+ ...composableProps(props, {
430
+ classNames: [
431
+ "py-form-chrome",
432
+ classNames
433
+ ]
434
+ }),
435
+ role: "listbox",
436
+ centered: true,
437
+ padding: true,
438
+ thin: true,
439
+ ref: forwardedRef
440
+ }, /* @__PURE__ */ React4.createElement(ScrollArea.Viewport, null, children));
441
+ });
442
+ ComboboxList.displayName = "Combobox.List";
443
+ var ComboboxItem = /* @__PURE__ */ forwardRef2(({ classNames, onSelect, value, label, icon, iconClassNames, checked, suffix, disabled, closeOnSelect = true, children }, forwardedRef) => {
444
+ const { onValueChange, onOpenChange } = useComboboxContext(COMBOBOX_ITEM_NAME);
445
+ const handleSelect = useCallback2(() => {
446
+ onSelect?.();
447
+ if (value !== void 0) {
448
+ onValueChange?.(value);
449
+ }
450
+ if (closeOnSelect) {
451
+ onOpenChange?.(false);
452
+ }
453
+ }, [
454
+ onSelect,
455
+ onValueChange,
456
+ onOpenChange,
457
+ value,
458
+ closeOnSelect
459
+ ]);
460
+ return /* @__PURE__ */ React4.createElement(Picker.Item, {
461
+ value,
462
+ disabled,
463
+ onSelect: handleSelect,
464
+ ref: forwardedRef,
465
+ classNames: [
466
+ // Full width inside the viewport (no horizontal margin).
467
+ // `px-3 py-1`, `cursor-pointer`, `select-none` and the
468
+ // `dx-hover` / `dx-selected` pairing come from `Picker.Item`'s
469
+ // defaults; we only add the row-shape (flex / icons + label)
470
+ // and the disabled overrides on top.
471
+ "flex w-full gap-2 items-center",
472
+ disabled && "hover:bg-transparent data-[selected=true]:bg-transparent",
473
+ classNames
474
+ ]
475
+ }, children ?? /* @__PURE__ */ React4.createElement(React4.Fragment, null, icon && /* @__PURE__ */ React4.createElement(Icon2, {
476
+ icon,
477
+ classNames: iconClassNames
478
+ }), /* @__PURE__ */ React4.createElement("span", {
479
+ className: "w-0 grow truncate"
480
+ }, label), suffix && /* @__PURE__ */ React4.createElement("span", {
481
+ className: "shrink-0 text-description"
482
+ }, suffix), checked && /* @__PURE__ */ React4.createElement(Icon2, {
483
+ icon: "ph--check--regular"
484
+ })));
485
+ });
486
+ ComboboxItem.displayName = COMBOBOX_ITEM_NAME;
487
+ var ComboboxArrow = Popover.Arrow;
488
+ var ComboboxEmpty = /* @__PURE__ */ forwardRef2(({ classNames, children }, forwardedRef) => {
489
+ return /* @__PURE__ */ React4.createElement("div", {
490
+ ref: forwardedRef,
491
+ role: "status",
492
+ className: mx4(classNames)
493
+ }, children);
494
+ });
495
+ ComboboxEmpty.displayName = "Combobox.Empty";
496
+ var ComboboxPortal = Popover.Portal;
497
+ var Combobox = {
498
+ Root: ComboboxRoot,
499
+ Portal: ComboboxPortal,
500
+ Content: ComboboxContent,
501
+ Trigger: ComboboxTrigger,
502
+ VirtualTrigger: ComboboxVirtualTrigger,
503
+ Input: ComboboxInput,
504
+ List: ComboboxList,
505
+ Item: ComboboxItem,
506
+ Arrow: ComboboxArrow,
507
+ Empty: ComboboxEmpty
508
+ };
509
+
93
510
  // src/components/List/ListItem.tsx
94
- import { useSignals as _useSignals4 } from "@preact-signals/safe-react/tracking";
511
+ import { attachClosestEdge, extractClosestEdge as extractClosestEdge2 } from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge";
95
512
  import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
96
513
  import { draggable, dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
97
514
  import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview";
98
- import { attachClosestEdge, extractClosestEdge as extractClosestEdge2 } from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge";
99
- import { createContext as createContext4 } from "@radix-ui/react-context";
100
- import React4, { useEffect as useEffect2, useRef, useState as useState2 } from "react";
515
+ import { createContext as createContext6 } from "@radix-ui/react-context";
516
+ import { Slot as Slot2 } from "@radix-ui/react-slot";
517
+ import React6, { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
101
518
  import { createPortal } from "react-dom";
102
519
  import { invariant } from "@dxos/invariant";
103
520
  import { IconButton, ListItem as NaturalListItem, useTranslation } from "@dxos/react-ui";
104
- import { mx as mx3 } from "@dxos/react-ui-theme";
521
+ import { mx as mx5, osTranslations } from "@dxos/ui-theme";
105
522
 
106
523
  // src/components/List/ListRoot.tsx
107
- import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
108
- import { monitorForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
109
524
  import { extractClosestEdge } from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge";
110
525
  import { getReorderDestinationIndex } from "@atlaskit/pragmatic-drag-and-drop-hitbox/util/get-reorder-destination-index";
111
- import { createContext as createContext3 } from "@radix-ui/react-context";
112
- import React3, { useCallback, useEffect, useState } from "react";
526
+ import { monitorForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
527
+ import { createContext as createContext5 } from "@radix-ui/react-context";
528
+ import React5, { useCallback as useCallback3, useEffect as useEffect2, useState as useState2 } from "react";
113
529
  var LIST_NAME = "List";
114
- var [ListProvider, useListContext] = createContext3(LIST_NAME);
530
+ var [ListProvider, useListContext] = createContext5(LIST_NAME);
115
531
  var defaultGetId2 = (item) => item?.id;
116
532
  var ListRoot = ({ children, items, isItem, getId = defaultGetId2, onMove, ...props }) => {
117
- var _effect = _useSignals3();
118
- try {
119
- const isEqual = useCallback((a, b) => {
120
- const idA = getId?.(a);
121
- const idB = getId?.(b);
122
- if (idA !== void 0 && idB !== void 0) {
123
- return idA === idB;
124
- } else {
125
- return a === b;
126
- }
127
- }, [
128
- getId
129
- ]);
130
- const [state, setState] = useState(idle);
131
- useEffect(() => {
132
- if (!items) {
133
- return;
134
- }
135
- return monitorForElements({
136
- canMonitor: ({ source }) => isItem?.(source.data) ?? false,
137
- onDrop: ({ location, source }) => {
138
- const target = location.current.dropTargets[0];
139
- if (!target) {
140
- return;
141
- }
142
- const sourceData = source.data;
143
- const targetData = target.data;
144
- if (!isItem?.(sourceData) || !isItem?.(targetData)) {
145
- return;
146
- }
147
- const sourceIdx = items.findIndex((item) => isEqual(item, sourceData));
148
- const targetIdx = items.findIndex((item) => isEqual(item, targetData));
149
- if (targetIdx < 0 || sourceIdx < 0) {
150
- return;
151
- }
152
- const closestEdgeOfTarget = extractClosestEdge(targetData);
153
- const destinationIndex = getReorderDestinationIndex({
154
- closestEdgeOfTarget,
155
- startIndex: sourceIdx,
156
- indexOfTarget: targetIdx,
157
- axis: "vertical"
158
- });
159
- onMove?.(sourceIdx, destinationIndex);
533
+ const isEqual = useCallback3((a, b) => {
534
+ const idA = getId?.(a);
535
+ const idB = getId?.(b);
536
+ if (idA !== void 0 && idB !== void 0) {
537
+ return idA === idB;
538
+ } else {
539
+ return a === b;
540
+ }
541
+ }, [
542
+ getId
543
+ ]);
544
+ const [state, setState] = useState2(idle);
545
+ useEffect2(() => {
546
+ if (!items) {
547
+ return;
548
+ }
549
+ return monitorForElements({
550
+ canMonitor: ({ source }) => isItem?.(source.data) ?? false,
551
+ onDrop: ({ location, source }) => {
552
+ const target = location.current.dropTargets[0];
553
+ if (!target) {
554
+ return;
160
555
  }
161
- });
162
- }, [
163
- items,
164
- isEqual,
165
- onMove
166
- ]);
167
- return /* @__PURE__ */ React3.createElement(ListProvider, {
168
- state,
169
- setState,
170
- isItem,
171
- ...props
172
- }, children?.({
173
- state,
174
- items: items ?? []
175
- }));
176
- } finally {
177
- _effect.f();
178
- }
556
+ const sourceData = source.data;
557
+ const targetData = target.data;
558
+ if (!isItem?.(sourceData) || !isItem?.(targetData)) {
559
+ return;
560
+ }
561
+ const sourceIdx = items.findIndex((item) => isEqual(item, sourceData));
562
+ const targetIdx = items.findIndex((item) => isEqual(item, targetData));
563
+ if (targetIdx < 0 || sourceIdx < 0) {
564
+ return;
565
+ }
566
+ const closestEdgeOfTarget = extractClosestEdge(targetData);
567
+ const destinationIndex = getReorderDestinationIndex({
568
+ closestEdgeOfTarget,
569
+ startIndex: sourceIdx,
570
+ indexOfTarget: targetIdx,
571
+ axis: "vertical"
572
+ });
573
+ onMove?.(sourceIdx, destinationIndex);
574
+ }
575
+ });
576
+ }, [
577
+ items,
578
+ isEqual,
579
+ onMove
580
+ ]);
581
+ return /* @__PURE__ */ React5.createElement(ListProvider, {
582
+ state,
583
+ setState,
584
+ isItem,
585
+ ...props
586
+ }, children?.({
587
+ state,
588
+ items: items ?? []
589
+ }));
179
590
  };
180
591
 
181
592
  // src/components/List/ListItem.tsx
@@ -188,196 +599,170 @@ var stateStyles = {
188
599
  };
189
600
  var defaultContext = {};
190
601
  var LIST_ITEM_NAME = "ListItem";
191
- var [ListItemProvider, useListItemContext] = createContext4(LIST_ITEM_NAME, defaultContext);
192
- var ListItem = ({ children, classNames, item, ...props }) => {
193
- var _effect = _useSignals4();
194
- try {
195
- const { isItem, readonly, dragPreview, setState: setRootState } = useListContext(LIST_ITEM_NAME);
196
- const ref = useRef(null);
197
- const dragHandleRef = useRef(null);
198
- const [state, setState] = useState2(idle);
199
- useEffect2(() => {
200
- const element = ref.current;
201
- invariant(element, void 0, {
202
- F: __dxlog_file,
203
- L: 98,
204
- S: void 0,
205
- A: [
206
- "element",
207
- ""
208
- ]
209
- });
210
- return combine(
211
- //
212
- // https://atlassian.design/components/pragmatic-drag-and-drop/core-package/adapters/element/about#draggable
213
- //
214
- draggable({
215
- element,
216
- dragHandle: dragHandleRef.current,
217
- canDrag: () => !readonly,
218
- getInitialData: () => item,
219
- onGenerateDragPreview: dragPreview ? ({ nativeSetDragImage, source }) => {
220
- const rect = source.element.getBoundingClientRect();
221
- setCustomNativeDragPreview({
222
- nativeSetDragImage,
223
- getOffset: ({ container }) => {
224
- const { height } = container.getBoundingClientRect();
225
- return {
226
- x: 20,
227
- y: height / 2
228
- };
229
- },
230
- render: ({ container }) => {
231
- container.style.width = rect.width + "px";
232
- setState({
233
- type: "preview",
234
- container
235
- });
236
- setRootState({
237
- type: "preview",
238
- container,
239
- item
240
- });
241
- return () => {
242
- };
243
- }
244
- });
245
- } : void 0,
246
- onDragStart: () => {
247
- setState({
248
- type: "is-dragging"
249
- });
250
- setRootState({
251
- type: "is-dragging",
252
- item
253
- });
254
- },
255
- onDrop: () => {
256
- setState(idle);
257
- setRootState(idle);
258
- }
259
- }),
260
- //
261
- // https://atlassian.design/components/pragmatic-drag-and-drop/core-package/adapters/element/about#drop-target-for-elements
262
- //
263
- dropTargetForElements({
264
- element,
265
- canDrop: ({ source }) => {
266
- return (source.element !== element && isItem?.(source.data)) ?? false;
267
- },
268
- getData: ({ input }) => {
269
- return attachClosestEdge(item, {
270
- element,
271
- input,
272
- allowedEdges: [
273
- "top",
274
- "bottom"
275
- ]
276
- });
277
- },
278
- getIsSticky: () => true,
279
- onDragEnter: ({ self }) => {
280
- const closestEdge = extractClosestEdge2(self.data);
281
- setState({
282
- type: "is-dragging-over",
283
- closestEdge
284
- });
285
- },
286
- onDragLeave: () => {
287
- setState(idle);
288
- },
289
- onDrag: ({ self }) => {
290
- const closestEdge = extractClosestEdge2(self.data);
291
- setState((current) => {
292
- if (current.type === "is-dragging-over" && current.closestEdge === closestEdge) {
293
- return current;
294
- }
602
+ var [ListItemProvider, useListItemContext] = createContext6(LIST_ITEM_NAME, defaultContext);
603
+ var ListItem = ({ children, classNames, item, asChild, selected, ...props }) => {
604
+ const Comp = asChild ? Slot2 : "div";
605
+ const { isItem, readonly, dragPreview, setState: setRootState } = useListContext(LIST_ITEM_NAME);
606
+ const rootRef = useRef2(null);
607
+ const dragHandleRef = useRef2(null);
608
+ const [state, setState] = useState3(idle);
609
+ useEffect3(() => {
610
+ const element = rootRef.current;
611
+ invariant(element, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 37, S: void 0, A: ["element", ""] });
612
+ return combine(
613
+ //
614
+ // https://atlassian.design/components/pragmatic-drag-and-drop/core-package/adapters/element/about#draggable
615
+ //
616
+ draggable({
617
+ element,
618
+ dragHandle: dragHandleRef.current,
619
+ canDrag: () => !readonly,
620
+ getInitialData: () => item,
621
+ onGenerateDragPreview: dragPreview ? ({ nativeSetDragImage, source }) => {
622
+ const rect = source.element.getBoundingClientRect();
623
+ setCustomNativeDragPreview({
624
+ nativeSetDragImage,
625
+ getOffset: ({ container }) => {
626
+ const { height } = container.getBoundingClientRect();
295
627
  return {
296
- type: "is-dragging-over",
297
- closestEdge
628
+ x: 20,
629
+ y: height / 2
298
630
  };
299
- });
300
- },
301
- onDrop: () => {
302
- setState(idle);
303
- }
304
- })
305
- );
306
- }, [
307
- item
308
- ]);
309
- return /* @__PURE__ */ React4.createElement(ListItemProvider, {
310
- item,
311
- dragHandleRef
312
- }, /* @__PURE__ */ React4.createElement("div", {
313
- ref,
314
- role: "listitem",
315
- className: mx3("flex relative", classNames, stateStyles[state.type]),
316
- ...props
317
- }, children, state.type === "is-dragging-over" && state.closestEdge && /* @__PURE__ */ React4.createElement(NaturalListItem.DropIndicator, {
318
- edge: state.closestEdge
319
- })));
320
- } finally {
321
- _effect.f();
322
- }
631
+ },
632
+ render: ({ container }) => {
633
+ container.style.width = rect.width + "px";
634
+ setState({
635
+ type: "preview",
636
+ container
637
+ });
638
+ setRootState({
639
+ type: "preview",
640
+ container,
641
+ item
642
+ });
643
+ return () => {
644
+ };
645
+ }
646
+ });
647
+ } : void 0,
648
+ onDragStart: () => {
649
+ setState({
650
+ type: "is-dragging"
651
+ });
652
+ setRootState({
653
+ type: "is-dragging",
654
+ item
655
+ });
656
+ },
657
+ onDrop: () => {
658
+ setState(idle);
659
+ setRootState(idle);
660
+ }
661
+ }),
662
+ //
663
+ // https://atlassian.design/components/pragmatic-drag-and-drop/core-package/adapters/element/about#drop-target-for-elements
664
+ //
665
+ dropTargetForElements({
666
+ element,
667
+ canDrop: ({ source }) => {
668
+ return (source.element !== element && isItem?.(source.data)) ?? false;
669
+ },
670
+ getData: ({ input }) => {
671
+ return attachClosestEdge(item, {
672
+ element,
673
+ input,
674
+ allowedEdges: [
675
+ "top",
676
+ "bottom"
677
+ ]
678
+ });
679
+ },
680
+ getIsSticky: () => true,
681
+ onDragEnter: ({ self }) => {
682
+ const closestEdge = extractClosestEdge2(self.data);
683
+ setState({
684
+ type: "is-dragging-over",
685
+ closestEdge
686
+ });
687
+ },
688
+ onDragLeave: () => {
689
+ setState(idle);
690
+ },
691
+ onDrag: ({ self }) => {
692
+ const closestEdge = extractClosestEdge2(self.data);
693
+ setState((current) => {
694
+ if (current.type === "is-dragging-over" && current.closestEdge === closestEdge) {
695
+ return current;
696
+ }
697
+ return {
698
+ type: "is-dragging-over",
699
+ closestEdge
700
+ };
701
+ });
702
+ },
703
+ onDrop: () => {
704
+ setState(idle);
705
+ }
706
+ })
707
+ );
708
+ }, [
709
+ item
710
+ ]);
711
+ return /* @__PURE__ */ React6.createElement(ListItemProvider, {
712
+ item,
713
+ dragHandleRef
714
+ }, /* @__PURE__ */ React6.createElement(Comp, {
715
+ ...props,
716
+ role: "listitem",
717
+ "aria-selected": selected,
718
+ className: mx5("relative p-1 dx-selected dx-hover", classNames, stateStyles[state.type]),
719
+ ref: rootRef
720
+ }, children), state.type === "is-dragging-over" && state.closestEdge && /* @__PURE__ */ React6.createElement(NaturalListItem.DropIndicator, {
721
+ edge: state.closestEdge
722
+ }));
323
723
  };
324
- var ListItemDeleteButton = ({ autoHide = true, classNames, disabled, icon = "ph--x--regular", label, ...props }) => {
325
- var _effect = _useSignals4();
326
- try {
327
- const { state } = useListContext("DELETE_BUTTON");
328
- const isDisabled = state.type !== "idle" || disabled;
329
- const { t } = useTranslation("os");
330
- return /* @__PURE__ */ React4.createElement(IconButton, {
331
- iconOnly: true,
332
- variant: "ghost",
333
- ...props,
334
- icon,
335
- disabled: isDisabled,
336
- label: label ?? t("delete label"),
337
- classNames: [
338
- classNames,
339
- autoHide && disabled && "hidden"
340
- ]
341
- });
342
- } finally {
343
- _effect.f();
344
- }
724
+ var ListItemIconButton = ({ autoHide = true, iconOnly = true, variant = "ghost", classNames, disabled, ...props }) => {
725
+ const { state } = useListContext("ITEM_BUTTON");
726
+ const isDisabled = state.type !== "idle" || disabled;
727
+ return /* @__PURE__ */ React6.createElement(IconButton, {
728
+ ...props,
729
+ disabled: isDisabled,
730
+ iconOnly,
731
+ variant,
732
+ classNames: [
733
+ classNames,
734
+ autoHide && disabled && "hidden"
735
+ ]
736
+ });
345
737
  };
346
- var ListItemButton = ({ autoHide = true, iconOnly = true, variant = "ghost", classNames, disabled, ...props }) => {
347
- var _effect = _useSignals4();
348
- try {
349
- const { state } = useListContext("ITEM_BUTTON");
350
- const isDisabled = state.type !== "idle" || disabled;
351
- return /* @__PURE__ */ React4.createElement(IconButton, {
352
- ...props,
353
- disabled: isDisabled,
354
- iconOnly,
355
- variant,
356
- classNames: [
357
- classNames,
358
- autoHide && disabled && "hidden"
359
- ]
360
- });
361
- } finally {
362
- _effect.f();
363
- }
738
+ var ListItemDeleteButton = ({ autoHide = true, classNames, disabled, icon = "ph--x--regular", label, ...props }) => {
739
+ const { state } = useListContext("DELETE_BUTTON");
740
+ const isDisabled = state.type !== "idle" || disabled;
741
+ const { t } = useTranslation(osTranslations);
742
+ return /* @__PURE__ */ React6.createElement(IconButton, {
743
+ ...props,
744
+ variant: "ghost",
745
+ disabled: isDisabled,
746
+ icon,
747
+ iconOnly: true,
748
+ label: label ?? t("delete.label"),
749
+ classNames: [
750
+ classNames,
751
+ autoHide && disabled && "hidden"
752
+ ]
753
+ });
364
754
  };
365
755
  var ListItemDragHandle = ({ disabled }) => {
366
- var _effect = _useSignals4();
367
- try {
368
- const { dragHandleRef } = useListItemContext("DRAG_HANDLE");
369
- const { t } = useTranslation("os");
370
- return /* @__PURE__ */ React4.createElement(IconButton, {
371
- iconOnly: true,
372
- variant: "ghost",
373
- label: t("drag handle label"),
374
- ref: dragHandleRef,
375
- icon: "ph--dots-six-vertical--regular",
376
- disabled
377
- });
378
- } finally {
379
- _effect.f();
380
- }
756
+ const { dragHandleRef } = useListItemContext("DRAG_HANDLE");
757
+ const { t } = useTranslation(osTranslations);
758
+ return /* @__PURE__ */ React6.createElement(IconButton, {
759
+ variant: "ghost",
760
+ disabled,
761
+ icon: "ph--dots-six-vertical--regular",
762
+ iconOnly: true,
763
+ label: t("drag-handle.label"),
764
+ ref: dragHandleRef
765
+ });
381
766
  };
382
767
  var ListItemDragPreview = ({ children }) => {
383
768
  const { state } = useListContext("DRAG_PREVIEW");
@@ -385,27 +770,15 @@ var ListItemDragPreview = ({ children }) => {
385
770
  item: state.item
386
771
  }), state.container) : null;
387
772
  };
388
- var ListItemWrapper = ({ classNames, children }) => {
389
- var _effect = _useSignals4();
390
- try {
391
- return /* @__PURE__ */ React4.createElement("div", {
392
- className: mx3("flex is-full gap-2", classNames)
393
- }, children);
394
- } finally {
395
- _effect.f();
396
- }
397
- };
398
- var ListItemTitle = ({ classNames, children, ...props }) => {
399
- var _effect = _useSignals4();
400
- try {
401
- return /* @__PURE__ */ React4.createElement("div", {
402
- className: mx3("flex grow items-center truncate", classNames),
403
- ...props
404
- }, children);
405
- } finally {
406
- _effect.f();
407
- }
408
- };
773
+ var ListItemWrapper = ({ classNames, children }) => /* @__PURE__ */ React6.createElement("div", {
774
+ role: "none",
775
+ className: mx5("flex w-full gap-2", classNames)
776
+ }, children);
777
+ var ListItemTitle = ({ classNames, children, ...props }) => /* @__PURE__ */ React6.createElement("div", {
778
+ role: "none",
779
+ className: mx5("flex grow items-center truncate", classNames),
780
+ ...props
781
+ }, children);
409
782
 
410
783
  // src/components/List/List.tsx
411
784
  var List = {
@@ -414,33 +787,253 @@ var List = {
414
787
  ItemDragPreview: ListItemDragPreview,
415
788
  ItemWrapper: ListItemWrapper,
416
789
  ItemDragHandle: ListItemDragHandle,
790
+ ItemIconButton: ListItemIconButton,
417
791
  ItemDeleteButton: ListItemDeleteButton,
418
- ItemButton: ListItemButton,
419
792
  ItemTitle: ListItemTitle
420
793
  };
421
794
 
795
+ // src/components/Listbox/Listbox.tsx
796
+ import { createContextScope as createContextScope2 } from "@radix-ui/react-context";
797
+ import React8, { forwardRef as forwardRef3 } from "react";
798
+ import { Icon as Icon3 } from "@dxos/react-ui";
799
+ import { mx as mx6 } from "@dxos/ui-theme";
800
+
801
+ // src/components/RowList/RowList.tsx
802
+ import { useArrowNavigationGroup } from "@fluentui/react-tabster";
803
+ import { createContextScope } from "@radix-ui/react-context";
804
+ import { useControllableState as useControllableState2 } from "@radix-ui/react-use-controllable-state";
805
+ import React7, { useCallback as useCallback4 } from "react";
806
+ import { List as List2, ListItem as ListItem2 } from "@dxos/react-list";
807
+ import { ScrollArea as ScrollArea2 } from "@dxos/react-ui";
808
+ import { composable as composable2, composableProps as composableProps2 } from "@dxos/ui-theme";
809
+ var ROW_LIST_NAME = "RowList";
810
+ var ROW_LIST_ROOT_NAME = "RowList.Root";
811
+ var ROW_LIST_VIEWPORT_NAME = "RowList.Viewport";
812
+ var ROW_LIST_CONTENT_NAME = "RowList.Content";
813
+ var ROW_NAME = "List.Row";
814
+ var [createRowListContext, createRowListScope] = createContextScope(ROW_LIST_NAME, []);
815
+ var [RowListProvider, useRowListContext] = createRowListContext(ROW_LIST_NAME);
816
+ var Root2 = ({ selectedId, defaultSelectedId, onSelectChange, children }) => {
817
+ const [resolved, setResolved] = useControllableState2({
818
+ prop: selectedId,
819
+ defaultProp: defaultSelectedId,
820
+ onChange: (next) => {
821
+ if (next !== void 0) {
822
+ onSelectChange?.(next);
823
+ }
824
+ }
825
+ });
826
+ const setSelected = useCallback4((id) => setResolved(id), [
827
+ setResolved
828
+ ]);
829
+ return /* @__PURE__ */ React7.createElement(RowListProvider, {
830
+ scope: void 0,
831
+ selectedId: resolved,
832
+ setSelected
833
+ }, children);
834
+ };
835
+ Root2.displayName = ROW_LIST_ROOT_NAME;
836
+ var Viewport = composable2((props, forwardedRef) => {
837
+ const { thin, padding, centered, children, ...rest } = props;
838
+ const composed = composableProps2(rest, {
839
+ classNames: "dx-container"
840
+ });
841
+ return /* @__PURE__ */ React7.createElement(ScrollArea2.Root, {
842
+ orientation: "vertical",
843
+ thin,
844
+ padding,
845
+ centered,
846
+ ...composed,
847
+ ref: forwardedRef
848
+ }, /* @__PURE__ */ React7.createElement(ScrollArea2.Viewport, null, children));
849
+ });
850
+ Viewport.displayName = ROW_LIST_VIEWPORT_NAME;
851
+ var firstEnabledOption = (ul) => {
852
+ if (!ul) {
853
+ return null;
854
+ }
855
+ return ul.querySelector('[role="option"]:not([aria-disabled="true"])');
856
+ };
857
+ var Content2 = composable2((props, forwardedRef) => {
858
+ useRowListContext(ROW_LIST_CONTENT_NAME, void 0);
859
+ const arrowGroup = useArrowNavigationGroup({
860
+ axis: "vertical",
861
+ memorizeCurrent: true
862
+ });
863
+ const { children, ...rest } = props;
864
+ const handleFocus = useCallback4((event) => {
865
+ if (event.target !== event.currentTarget) {
866
+ return;
867
+ }
868
+ const ul = event.currentTarget;
869
+ const selected = ul.querySelector('[role="option"][aria-selected="true"]:not([aria-disabled="true"])');
870
+ const target = selected ?? firstEnabledOption(ul);
871
+ target?.focus();
872
+ }, []);
873
+ const composed = composableProps2(rest, {
874
+ classNames: "flex flex-col"
875
+ });
876
+ return /* @__PURE__ */ React7.createElement(List2, {
877
+ variant: "unordered",
878
+ ...composed,
879
+ ...arrowGroup,
880
+ role: "listbox",
881
+ onFocus: handleFocus,
882
+ ref: forwardedRef
883
+ }, children);
884
+ });
885
+ Content2.displayName = ROW_LIST_CONTENT_NAME;
886
+ var ROW_BASE = "dx-hover dx-selected px-3 py-2 cursor-pointer outline-none border-b border-separator last:border-b-0";
887
+ var Row = composable2((props, forwardedRef) => {
888
+ const { id, disabled, onClick, onFocus, children, ...rest } = props;
889
+ const { selectedId, setSelected } = useRowListContext(ROW_NAME, void 0);
890
+ const isSelected = selectedId === id;
891
+ const handleClick = useCallback4((event) => {
892
+ if (disabled) {
893
+ return;
894
+ }
895
+ setSelected(id);
896
+ onClick?.(event);
897
+ }, [
898
+ disabled,
899
+ id,
900
+ setSelected,
901
+ onClick
902
+ ]);
903
+ const handleFocus = useCallback4((event) => {
904
+ if (!disabled && selectedId !== id) {
905
+ setSelected(id);
906
+ }
907
+ onFocus?.(event);
908
+ }, [
909
+ disabled,
910
+ selectedId,
911
+ id,
912
+ setSelected,
913
+ onFocus
914
+ ]);
915
+ const composed = composableProps2(rest, {
916
+ classNames: [
917
+ ROW_BASE,
918
+ disabled && "opacity-50 cursor-not-allowed"
919
+ ]
920
+ });
921
+ return /* @__PURE__ */ React7.createElement(ListItem2, {
922
+ ...composed,
923
+ role: "option",
924
+ tabIndex: 0,
925
+ "aria-selected": isSelected,
926
+ "aria-disabled": disabled || void 0,
927
+ onClick: handleClick,
928
+ onFocus: handleFocus,
929
+ ref: forwardedRef
930
+ }, children);
931
+ });
932
+ Row.displayName = ROW_NAME;
933
+ var useRowListSelection = (id) => {
934
+ const { selectedId } = useRowListContext("useRowListSelection", void 0);
935
+ return selectedId === id;
936
+ };
937
+ var RowList = {
938
+ Root: Root2,
939
+ Viewport,
940
+ Content: Content2
941
+ };
942
+
943
+ // src/components/Listbox/Listbox.tsx
944
+ var commandItem = "flex items-center overflow-hidden";
945
+ var LISTBOX_NAME = "Listbox";
946
+ var LISTBOX_OPTION_NAME = "ListboxOption";
947
+ var LISTBOX_OPTION_LABEL_NAME = "ListboxOptionLabel";
948
+ var LISTBOX_OPTION_INDICATOR_NAME = "ListboxOptionIndicator";
949
+ var [createListboxContext, createListboxScope] = createContextScope2(LISTBOX_NAME, [
950
+ createRowListScope
951
+ ]);
952
+ var [createListboxOptionContext, createListboxOptionScope] = createContextScope2(LISTBOX_OPTION_NAME, [
953
+ createListboxScope
954
+ ]);
955
+ var [ListboxOptionProvider, useListboxOptionContext] = createListboxOptionContext(LISTBOX_OPTION_NAME);
956
+ var ListboxRoot = /* @__PURE__ */ forwardRef3((props, forwardedRef) => {
957
+ const { __listboxScope: _scope, children, classNames, value, defaultValue, onValueChange, autoFocus: _autoFocus, ...rootProps } = props;
958
+ return /* @__PURE__ */ React8.createElement(RowList.Root, {
959
+ selectedId: value,
960
+ defaultSelectedId: defaultValue,
961
+ onSelectChange: onValueChange
962
+ }, /* @__PURE__ */ React8.createElement(RowList.Content, {
963
+ ...rootProps,
964
+ classNames: mx6("w-full", classNames),
965
+ ref: forwardedRef
966
+ }, children));
967
+ });
968
+ ListboxRoot.displayName = LISTBOX_NAME;
969
+ var ListboxOption = /* @__PURE__ */ forwardRef3((props, forwardedRef) => {
970
+ const { __listboxScope, children, classNames, value, ...rootProps } = props;
971
+ return /* @__PURE__ */ React8.createElement(Row, {
972
+ id: value,
973
+ ...rootProps,
974
+ classNames: mx6("dx-focus-ring rounded-xs", commandItem, classNames),
975
+ ref: forwardedRef
976
+ }, /* @__PURE__ */ React8.createElement(ListboxOptionProviderHost, {
977
+ value
978
+ }, children));
979
+ });
980
+ ListboxOption.displayName = LISTBOX_OPTION_NAME;
981
+ var ListboxOptionProviderHost = ({ value, children }) => {
982
+ const isSelected = useRowListSelection(value);
983
+ return /* @__PURE__ */ React8.createElement(ListboxOptionProvider, {
984
+ scope: void 0,
985
+ value,
986
+ isSelected
987
+ }, children);
988
+ };
989
+ var ListboxOptionLabel = /* @__PURE__ */ forwardRef3(({ children, classNames, ...rootProps }, forwardedRef) => {
990
+ return /* @__PURE__ */ React8.createElement("span", {
991
+ ...rootProps,
992
+ className: mx6("grow truncate", classNames),
993
+ ref: forwardedRef
994
+ }, children);
995
+ });
996
+ ListboxOptionLabel.displayName = LISTBOX_OPTION_LABEL_NAME;
997
+ var ListboxOptionIndicator = /* @__PURE__ */ forwardRef3((props, forwardedRef) => {
998
+ const { __listboxOptionScope, classNames, ...rootProps } = props;
999
+ const { isSelected } = useListboxOptionContext(LISTBOX_OPTION_INDICATOR_NAME, __listboxOptionScope);
1000
+ return /* @__PURE__ */ React8.createElement(Icon3, {
1001
+ icon: "ph--check--regular",
1002
+ ...rootProps,
1003
+ classNames: mx6(!isSelected && "invisible", classNames),
1004
+ ref: forwardedRef
1005
+ });
1006
+ });
1007
+ ListboxOptionIndicator.displayName = LISTBOX_OPTION_INDICATOR_NAME;
1008
+ var Listbox = {
1009
+ Root: ListboxRoot,
1010
+ Option: ListboxOption,
1011
+ OptionLabel: ListboxOptionLabel,
1012
+ OptionIndicator: ListboxOptionIndicator
1013
+ };
1014
+
422
1015
  // src/components/Tree/Tree.tsx
423
- import { useSignals as _useSignals8 } from "@preact-signals/safe-react/tracking";
424
- import React8, { useMemo as useMemo2 } from "react";
1016
+ import { useAtomValue as useAtomValue2 } from "@effect-atom/atom-react";
1017
+ import React12, { useMemo as useMemo3 } from "react";
425
1018
  import { Treegrid as Treegrid2 } from "@dxos/react-ui";
426
1019
 
427
1020
  // src/components/Tree/TreeContext.tsx
428
- import { createContext as createContext5, useContext } from "react";
1021
+ import { createContext as createContext7, useContext } from "react";
429
1022
  import { raise } from "@dxos/debug";
430
- var TreeContext = /* @__PURE__ */ createContext5(null);
1023
+ var TreeContext = /* @__PURE__ */ createContext7(null);
431
1024
  var TreeProvider = TreeContext.Provider;
432
1025
  var useTree = () => useContext(TreeContext) ?? raise(new Error("TreeContext not found"));
433
1026
 
434
1027
  // src/components/Tree/TreeItem.tsx
435
- import { useSignals as _useSignals7 } from "@preact-signals/safe-react/tracking";
1028
+ import { attachInstruction, extractInstruction } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item";
436
1029
  import { combine as combine2 } from "@atlaskit/pragmatic-drag-and-drop/combine";
437
1030
  import { draggable as draggable2, dropTargetForElements as dropTargetForElements2 } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
438
- import { attachInstruction, extractInstruction } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item";
439
- import { Schema } from "effect";
440
- import React7, { memo as memo3, useCallback as useCallback3, useEffect as useEffect3, useMemo, useRef as useRef2, useState as useState3 } from "react";
1031
+ import { useAtomValue } from "@effect-atom/atom-react";
1032
+ import * as Schema from "effect/Schema";
1033
+ import React11, { memo as memo3, useCallback as useCallback6, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef3, useState as useState4 } from "react";
441
1034
  import { invariant as invariant2 } from "@dxos/invariant";
442
- import { TreeItem as NaturalTreeItem, Treegrid } from "@dxos/react-ui";
443
- import { ghostHover, hoverableControls, hoverableFocusedKeyboardControls, hoverableFocusedWithinControls } from "@dxos/react-ui-theme";
1035
+ import { TreeItem as NaturalTreeItem, Treegrid, TREEGRID_PARENT_OF_SEPARATOR } from "@dxos/react-ui";
1036
+ import { ghostFocusWithin, ghostHover, hoverableControls, hoverableFocusedKeyboardControls, hoverableFocusedWithinControls, mx as mx7 } from "@dxos/ui-theme";
444
1037
 
445
1038
  // src/components/Tree/helpers.ts
446
1039
  var DEFAULT_INDENTATION = 8;
@@ -449,92 +1042,85 @@ var paddingIndentation = (level, indentation = DEFAULT_INDENTATION) => ({
449
1042
  });
450
1043
 
451
1044
  // src/components/Tree/TreeItemHeading.tsx
452
- import { useSignals as _useSignals5 } from "@preact-signals/safe-react/tracking";
453
- import React5, { forwardRef, memo, useCallback as useCallback2 } from "react";
454
- import { Button, Icon as Icon2, toLocalizedString, useTranslation as useTranslation2 } from "@dxos/react-ui";
1045
+ import React9, { forwardRef as forwardRef4, memo, useCallback as useCallback5 } from "react";
1046
+ import { Button as Button2, Icon as Icon4, toLocalizedString, useTranslation as useTranslation2 } from "@dxos/react-ui";
455
1047
  import { TextTooltip } from "@dxos/react-ui-text-tooltip";
456
- var TreeItemHeading = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef(({ label, icon, className, disabled, current, onSelect }, forwardedRef) => {
457
- var _effect = _useSignals5();
458
- try {
459
- const { t } = useTranslation2();
460
- const handleSelect = useCallback2((event) => {
1048
+ import { getStyles } from "@dxos/ui-theme";
1049
+ var TreeItemHeading = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef4(({ label, className, icon, iconHue, disabled, current, onSelect }, forwardedRef) => {
1050
+ const { t } = useTranslation2();
1051
+ const styles = iconHue ? getStyles(iconHue) : void 0;
1052
+ const handleSelect = useCallback5((event) => {
1053
+ onSelect?.(event.altKey);
1054
+ }, [
1055
+ onSelect
1056
+ ]);
1057
+ const handleButtonKeydown = useCallback5((event) => {
1058
+ if (event.key === " " || event.key === "Enter") {
1059
+ event.preventDefault();
1060
+ event.stopPropagation();
461
1061
  onSelect?.(event.altKey);
462
- }, [
463
- onSelect
464
- ]);
465
- const handleButtonKeydown = useCallback2((event) => {
466
- if (event.key === " " || event.key === "Enter") {
467
- event.preventDefault();
468
- event.stopPropagation();
469
- onSelect?.(event.altKey);
470
- }
471
- }, [
472
- onSelect
473
- ]);
474
- return /* @__PURE__ */ React5.createElement(TextTooltip, {
475
- text: toLocalizedString(label, t),
476
- side: "bottom",
477
- truncateQuery: "span[data-tooltip]",
478
- onlyWhenTruncating: true,
479
- asChild: true,
480
- ref: forwardedRef
481
- }, /* @__PURE__ */ React5.createElement(Button, {
482
- "data-testid": "treeItem.heading",
483
- variant: "ghost",
484
- density: "fine",
485
- classNames: [
486
- "grow gap-2 pis-0.5 hover:bg-transparent dark:hover:bg-transparent",
487
- "disabled:cursor-default disabled:opacity-100",
488
- className
489
- ],
490
- disabled,
491
- onClick: handleSelect,
492
- onKeyDown: handleButtonKeydown,
493
- ...current && {
494
- "aria-current": "location"
495
- }
496
- }, icon && /* @__PURE__ */ React5.createElement(Icon2, {
497
- icon: icon ?? "ph--placeholder--regular",
498
- size: 5,
499
- classNames: "mlb-1"
500
- }), /* @__PURE__ */ React5.createElement("span", {
501
- className: "flex-1 is-0 truncate text-start text-sm font-normal",
502
- "data-tooltip": true
503
- }, toLocalizedString(label, t))));
504
- } finally {
505
- _effect.f();
506
- }
1062
+ }
1063
+ }, [
1064
+ onSelect
1065
+ ]);
1066
+ return /* @__PURE__ */ React9.createElement(TextTooltip, {
1067
+ text: toLocalizedString(label, t),
1068
+ side: "bottom",
1069
+ truncateQuery: "span[data-tooltip]",
1070
+ onlyWhenTruncating: true,
1071
+ asChild: true,
1072
+ ref: forwardedRef
1073
+ }, /* @__PURE__ */ React9.createElement(Button2, {
1074
+ "data-testid": "treeItem.heading",
1075
+ variant: "ghost",
1076
+ classNames: [
1077
+ "grow gap-2 ps-0.5 hover:bg-transparent dark:hover:bg-transparent",
1078
+ "disabled:cursor-default disabled:opacity-100",
1079
+ className
1080
+ ],
1081
+ disabled,
1082
+ onClick: handleSelect,
1083
+ onKeyDown: handleButtonKeydown,
1084
+ ...current && {
1085
+ "aria-current": "location"
1086
+ }
1087
+ }, icon && /* @__PURE__ */ React9.createElement(Icon4, {
1088
+ icon: icon ?? "ph--placeholder--regular",
1089
+ classNames: [
1090
+ "my-1",
1091
+ styles?.surfaceText
1092
+ ]
1093
+ }), /* @__PURE__ */ React9.createElement("span", {
1094
+ className: "flex-1 w-0 truncate text-start font-normal",
1095
+ "data-tooltip": true
1096
+ }, toLocalizedString(label, t))));
507
1097
  }));
508
1098
 
509
1099
  // src/components/Tree/TreeItemToggle.tsx
510
- import { useSignals as _useSignals6 } from "@preact-signals/safe-react/tracking";
511
- import React6, { forwardRef as forwardRef2, memo as memo2 } from "react";
512
- import { Button as Button2, Icon as Icon3 } from "@dxos/react-ui";
513
- var TreeItemToggle = /* @__PURE__ */ memo2(/* @__PURE__ */ forwardRef2(({ open, isBranch, hidden, onToggle }, forwardedRef) => {
514
- var _effect = _useSignals6();
515
- try {
516
- return /* @__PURE__ */ React6.createElement(Button2, {
517
- ref: forwardedRef,
518
- "data-testid": "treeItem.toggle",
519
- "aria-expanded": open,
520
- variant: "ghost",
521
- density: "fine",
522
- classNames: [
523
- "is-6 pli-0 dx-focus-ring-inset",
524
- hidden ? "hidden" : !isBranch && "invisible"
525
- ],
526
- onClick: onToggle
527
- }, /* @__PURE__ */ React6.createElement(Icon3, {
528
- icon: "ph--caret-right--bold",
529
- size: 3,
530
- classNames: [
531
- "transition duration-200",
532
- open && "rotate-90"
533
- ]
534
- }));
535
- } finally {
536
- _effect.f();
537
- }
1100
+ import React10, { forwardRef as forwardRef5, memo as memo2 } from "react";
1101
+ import { IconButton as IconButton2 } from "@dxos/react-ui";
1102
+ var TreeItemToggle = /* @__PURE__ */ memo2(/* @__PURE__ */ forwardRef5(({ classNames, open, isBranch, hidden, ...props }, forwardedRef) => {
1103
+ return /* @__PURE__ */ React10.createElement(IconButton2, {
1104
+ ref: forwardedRef,
1105
+ "data-testid": "treeItem.toggle",
1106
+ "aria-expanded": open,
1107
+ variant: "ghost",
1108
+ density: "fine",
1109
+ classNames: [
1110
+ "h-full w-6 px-0",
1111
+ "[&_svg]:transition-transform [&_svg]:duration-200",
1112
+ open ? "[&_svg]:rotate-90" : "[&_svg]:rotate-0",
1113
+ hidden ? "hidden" : !isBranch && "invisible",
1114
+ classNames
1115
+ ],
1116
+ size: 3,
1117
+ icon: "ph--caret-right--bold",
1118
+ iconOnly: true,
1119
+ noTooltip: true,
1120
+ label: open ? "Click to close" : "Click to open",
1121
+ tabIndex: -1,
1122
+ ...props
1123
+ });
538
1124
  }));
539
1125
 
540
1126
  // src/components/Tree/TreeItem.tsx
@@ -546,312 +1132,343 @@ var TreeDataSchema = Schema.Struct({
546
1132
  item: Schema.Any
547
1133
  });
548
1134
  var isTreeData = (data) => Schema.is(TreeDataSchema)(data);
549
- var RawTreeItem = ({ item, path: _path, last, draggable: _draggable, renderColumns: Columns, canDrop, onOpenChange, onSelect, levelOffset = 2 }) => {
550
- var _effect = _useSignals7();
551
- try {
552
- const rowRef = useRef2(null);
553
- const buttonRef = useRef2(null);
554
- const openRef = useRef2(false);
555
- const cancelExpandRef = useRef2(null);
556
- const [_state, setState] = useState3("idle");
557
- const [instruction, setInstruction] = useState3(null);
558
- const [menuOpen, setMenuOpen] = useState3(false);
559
- const { useItems, getProps, isOpen, isCurrent } = useTree();
560
- const items = useItems(item);
561
- const { id, label, parentOf, icon, disabled, className, headingClassName, testId } = getProps(item, _path);
562
- const path = useMemo(() => [
563
- ..._path,
564
- id
565
- ], [
566
- _path,
567
- id
568
- ]);
569
- const open = isOpen(path, item);
570
- const current = isCurrent(path, item);
571
- const level = path.length - levelOffset;
572
- const isBranch = !!parentOf;
573
- const mode = last ? "last-in-group" : open ? "expanded" : "standard";
574
- const cancelExpand = useCallback3(() => {
575
- if (cancelExpandRef.current) {
576
- clearTimeout(cancelExpandRef.current);
577
- cancelExpandRef.current = null;
578
- }
579
- }, []);
580
- useEffect3(() => {
581
- if (!_draggable) {
582
- return;
1135
+ var RawTreeItem = ({ item, path: pathProp, levelOffset = 2, last, draggable: draggableProp, renderColumns: Columns, blockInstruction, canDrop, canSelect, onOpenChange, onSelect, onItemHover }) => {
1136
+ const rowRef = useRef3(null);
1137
+ const buttonRef = useRef3(null);
1138
+ const openRef = useRef3(false);
1139
+ const cancelExpandRef = useRef3(null);
1140
+ const [_state, setState] = useState4("idle");
1141
+ const [instruction, setInstruction] = useState4(null);
1142
+ const [menuOpen, setMenuOpen] = useState4(false);
1143
+ const { itemProps: itemPropsAtom, childIds: childIdsAtom, itemOpen: itemOpenAtom, itemCurrent: itemCurrentAtom } = useTree();
1144
+ const path = useMemo2(() => [
1145
+ ...pathProp,
1146
+ item.id
1147
+ ], [
1148
+ pathProp,
1149
+ item.id
1150
+ ]);
1151
+ const { id, parentOf, draggable: itemDraggable, droppable: itemDroppable, label, className, headingClassName, icon, iconHue, disabled, testId } = useAtomValue(itemPropsAtom(path));
1152
+ const childIds = useAtomValue(childIdsAtom(item.id));
1153
+ const open = useAtomValue(itemOpenAtom(path));
1154
+ const current = useAtomValue(itemCurrentAtom(path));
1155
+ const level = path.length - levelOffset;
1156
+ const isBranch = !!parentOf;
1157
+ const mode = last ? "last-in-group" : open ? "expanded" : "standard";
1158
+ const canSelectItem = canSelect?.({
1159
+ item,
1160
+ path
1161
+ }) ?? true;
1162
+ const data = {
1163
+ id,
1164
+ path,
1165
+ item
1166
+ };
1167
+ const shouldSeedNativeDragData = typeof document !== "undefined" && document.body.hasAttribute("data-platform");
1168
+ const cancelExpand = useCallback6(() => {
1169
+ if (cancelExpandRef.current) {
1170
+ clearTimeout(cancelExpandRef.current);
1171
+ cancelExpandRef.current = null;
1172
+ }
1173
+ }, []);
1174
+ const isItemDraggable = draggableProp && itemDraggable !== false;
1175
+ const isItemDroppable = itemDroppable !== false;
1176
+ const nativeDragText = id;
1177
+ useEffect4(() => {
1178
+ if (!draggableProp) {
1179
+ return;
1180
+ }
1181
+ invariant2(buttonRef.current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 70, S: void 0, A: ["buttonRef.current", ""] });
1182
+ const makeDraggable = () => draggable2({
1183
+ element: buttonRef.current,
1184
+ getInitialData: () => data,
1185
+ getInitialDataForExternal: () => {
1186
+ if (!shouldSeedNativeDragData) {
1187
+ return {};
1188
+ }
1189
+ return {
1190
+ "text/plain": nativeDragText
1191
+ };
1192
+ },
1193
+ onDragStart: () => {
1194
+ setState("dragging");
1195
+ if (open) {
1196
+ openRef.current = true;
1197
+ onOpenChange?.({
1198
+ item,
1199
+ path,
1200
+ open: false
1201
+ });
1202
+ }
1203
+ },
1204
+ onDrop: () => {
1205
+ setState("idle");
1206
+ if (openRef.current) {
1207
+ onOpenChange?.({
1208
+ item,
1209
+ path,
1210
+ open: true
1211
+ });
1212
+ }
583
1213
  }
584
- invariant2(buttonRef.current, void 0, {
585
- F: __dxlog_file2,
586
- L: 106,
587
- S: void 0,
588
- A: [
589
- "buttonRef.current",
590
- ""
591
- ]
592
- });
593
- const data = {
594
- id,
595
- path,
596
- item
597
- };
598
- return combine2(
599
- draggable2({
600
- element: buttonRef.current,
601
- getInitialData: () => data,
602
- onDragStart: () => {
603
- setState("dragging");
604
- if (open) {
605
- openRef.current = true;
606
- onOpenChange?.({
607
- item,
608
- path,
609
- open: false
610
- });
611
- }
612
- },
613
- onDrop: () => {
614
- setState("idle");
615
- if (openRef.current) {
1214
+ });
1215
+ if (!isItemDroppable) {
1216
+ return isItemDraggable ? makeDraggable() : void 0;
1217
+ }
1218
+ const dropTarget = dropTargetForElements2({
1219
+ element: buttonRef.current,
1220
+ getData: ({ input, element }) => {
1221
+ return attachInstruction(data, {
1222
+ input,
1223
+ element,
1224
+ indentPerLevel: DEFAULT_INDENTATION,
1225
+ currentLevel: level,
1226
+ mode,
1227
+ block: isBranch ? [] : [
1228
+ "make-child"
1229
+ ]
1230
+ });
1231
+ },
1232
+ canDrop: ({ source }) => {
1233
+ const _canDrop = canDrop ?? (() => true);
1234
+ return source.element !== buttonRef.current && _canDrop({
1235
+ source: source.data,
1236
+ target: data
1237
+ });
1238
+ },
1239
+ getIsSticky: () => true,
1240
+ onDrag: ({ self, source }) => {
1241
+ const desired = extractInstruction(self.data);
1242
+ const block = desired && blockInstruction?.({
1243
+ instruction: desired,
1244
+ source: source.data,
1245
+ target: data
1246
+ });
1247
+ const instruction2 = block && desired.type !== "instruction-blocked" ? {
1248
+ type: "instruction-blocked",
1249
+ desired
1250
+ } : desired;
1251
+ if (source.data.id !== id) {
1252
+ if (instruction2?.type === "make-child" && isBranch && !open && !cancelExpandRef.current) {
1253
+ cancelExpandRef.current = setTimeout(() => {
616
1254
  onOpenChange?.({
617
1255
  item,
618
1256
  path,
619
1257
  open: true
620
1258
  });
621
- }
1259
+ }, 500);
622
1260
  }
623
- }),
624
- // https://github.com/atlassian/pragmatic-drag-and-drop/blob/main/packages/hitbox/constellation/index/about.mdx
625
- dropTargetForElements2({
626
- element: buttonRef.current,
627
- getData: ({ input, element }) => {
628
- return attachInstruction(data, {
629
- input,
630
- element,
631
- indentPerLevel: DEFAULT_INDENTATION,
632
- currentLevel: level,
633
- mode,
634
- block: isBranch ? [] : [
635
- "make-child"
636
- ]
637
- });
638
- },
639
- canDrop: ({ source }) => {
640
- const _canDrop = canDrop ?? (() => true);
641
- return source.element !== buttonRef.current && _canDrop({
642
- source: source.data,
643
- target: data
644
- });
645
- },
646
- getIsSticky: () => true,
647
- onDrag: ({ self, source }) => {
648
- const instruction2 = extractInstruction(self.data);
649
- if (source.data.id !== id) {
650
- if (instruction2?.type === "make-child" && isBranch && !open && !cancelExpandRef.current) {
651
- cancelExpandRef.current = setTimeout(() => {
652
- onOpenChange?.({
653
- item,
654
- path,
655
- open: true
656
- });
657
- }, 500);
658
- }
659
- if (instruction2?.type !== "make-child") {
660
- cancelExpand();
661
- }
662
- setInstruction(instruction2);
663
- } else if (instruction2?.type === "reparent") {
664
- setInstruction(instruction2);
665
- } else {
666
- setInstruction(null);
667
- }
668
- },
669
- onDragLeave: () => {
670
- cancelExpand();
671
- setInstruction(null);
672
- },
673
- onDrop: () => {
1261
+ if (instruction2?.type !== "make-child") {
674
1262
  cancelExpand();
675
- setInstruction(null);
676
1263
  }
677
- })
678
- );
679
- }, [
680
- _draggable,
681
- item,
682
- id,
683
- mode,
684
- path,
685
- open,
686
- canDrop
687
- ]);
688
- useEffect3(() => () => cancelExpand(), [
689
- cancelExpand
690
- ]);
691
- const handleOpenChange = useCallback3(() => onOpenChange?.({
692
- item,
693
- path,
694
- open: !open
695
- }), [
696
- onOpenChange,
697
- item,
698
- path,
699
- open
700
- ]);
701
- const handleSelect = useCallback3((option = false) => {
702
- if (isBranch) {
703
- handleOpenChange();
704
- } else {
705
- rowRef.current?.focus();
706
- onSelect?.({
707
- item,
708
- path,
709
- current: !current,
710
- option
711
- });
712
- }
713
- }, [
714
- item,
715
- path,
716
- current,
717
- isBranch,
718
- handleOpenChange,
719
- onSelect
720
- ]);
721
- const handleKeyDown = useCallback3((event) => {
722
- switch (event.key) {
723
- case "ArrowRight":
724
- isBranch && !open && handleOpenChange();
725
- break;
726
- case "ArrowLeft":
727
- isBranch && open && handleOpenChange();
728
- break;
729
- case " ":
730
- handleSelect(event.altKey);
731
- break;
732
- }
733
- }, [
734
- isBranch,
735
- open,
736
- handleOpenChange,
737
- handleSelect
738
- ]);
739
- return /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Treegrid.Row, {
740
- ref: rowRef,
741
- key: id,
742
- id,
743
- "aria-labelledby": `${id}__label`,
744
- parentOf: parentOf?.join(Treegrid.PARENT_OF_SEPARATOR),
745
- classNames: [
746
- "grid grid-cols-subgrid col-[tree-row] mbs-0.5 aria-[current]:bg-activeSurface",
747
- hoverableControls,
748
- hoverableFocusedKeyboardControls,
749
- hoverableFocusedWithinControls,
750
- hoverableDescriptionIcons,
751
- ghostHover,
752
- className
753
- ],
754
- "data-itemid": id,
755
- "data-testid": testId,
756
- // NOTE(thure): This is intentionally an empty string to for descendents to select by in the CSS
757
- // without alerting the user (except for in the correct link element). See also:
758
- // https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current#description
759
- "aria-current": current ? "" : void 0,
760
- onKeyDown: handleKeyDown,
761
- onContextMenu: (event) => {
762
- event.preventDefault();
763
- setMenuOpen(true);
1264
+ setInstruction(instruction2);
1265
+ } else if (instruction2?.type === "reparent") {
1266
+ setInstruction(instruction2);
1267
+ } else {
1268
+ setInstruction(null);
1269
+ }
1270
+ },
1271
+ onDragLeave: () => {
1272
+ cancelExpand();
1273
+ setInstruction(null);
1274
+ },
1275
+ onDrop: () => {
1276
+ cancelExpand();
1277
+ setInstruction(null);
764
1278
  }
765
- }, /* @__PURE__ */ React7.createElement(Treegrid.Cell, {
766
- indent: true,
767
- classNames: "relative grid grid-cols-subgrid col-[tree-row]",
768
- style: paddingIndentation(level)
769
- }, /* @__PURE__ */ React7.createElement("div", {
770
- role: "none",
771
- className: "flex items-center"
772
- }, /* @__PURE__ */ React7.createElement(TreeItemToggle, {
773
- isBranch,
774
- open,
775
- onToggle: handleOpenChange
776
- }), /* @__PURE__ */ React7.createElement(TreeItemHeading, {
777
- ref: buttonRef,
778
- label,
779
- icon,
780
- className: headingClassName,
781
- disabled,
782
- current,
783
- onSelect: handleSelect
784
- })), Columns && /* @__PURE__ */ React7.createElement(Columns, {
785
- item,
786
- path,
787
- open,
788
- menuOpen,
789
- setMenuOpen
790
- }), instruction && /* @__PURE__ */ React7.createElement(NaturalTreeItem.DropIndicator, {
791
- instruction,
792
- gap: 2
793
- }))), open && items.map((item2, index) => /* @__PURE__ */ React7.createElement(TreeItem, {
794
- key: item2.id,
795
- item: item2,
796
- path,
797
- last: index === items.length - 1,
798
- draggable: _draggable,
799
- renderColumns: Columns,
800
- canDrop,
801
- onOpenChange,
802
- onSelect
803
- })));
804
- } finally {
805
- _effect.f();
806
- }
1279
+ });
1280
+ if (!isItemDraggable) {
1281
+ return dropTarget;
1282
+ }
1283
+ return combine2(makeDraggable(), dropTarget);
1284
+ }, [
1285
+ draggableProp,
1286
+ isItemDraggable,
1287
+ isItemDroppable,
1288
+ item,
1289
+ id,
1290
+ mode,
1291
+ path,
1292
+ open,
1293
+ blockInstruction,
1294
+ canDrop
1295
+ ]);
1296
+ useEffect4(() => () => cancelExpand(), [
1297
+ cancelExpand
1298
+ ]);
1299
+ const handleOpenToggle = useCallback6(() => onOpenChange?.({
1300
+ item,
1301
+ path,
1302
+ open: !open
1303
+ }), [
1304
+ onOpenChange,
1305
+ item,
1306
+ path,
1307
+ open
1308
+ ]);
1309
+ const handleSelect = useCallback6((option = false) => {
1310
+ if (isBranch && (option || current)) {
1311
+ handleOpenToggle();
1312
+ } else if (canSelectItem) {
1313
+ canSelect?.({
1314
+ item,
1315
+ path
1316
+ });
1317
+ rowRef.current?.focus();
1318
+ onSelect?.({
1319
+ item,
1320
+ path,
1321
+ current: !current,
1322
+ option
1323
+ });
1324
+ }
1325
+ }, [
1326
+ item,
1327
+ path,
1328
+ current,
1329
+ isBranch,
1330
+ canSelectItem,
1331
+ handleOpenToggle,
1332
+ onSelect
1333
+ ]);
1334
+ const handleKeyDown = useCallback6((event) => {
1335
+ switch (event.key) {
1336
+ case "ArrowRight":
1337
+ case "ArrowLeft":
1338
+ isBranch && handleOpenToggle();
1339
+ break;
1340
+ }
1341
+ }, [
1342
+ isBranch,
1343
+ open,
1344
+ handleOpenToggle,
1345
+ handleSelect
1346
+ ]);
1347
+ const handleItemHover = useCallback6(() => {
1348
+ onItemHover?.({
1349
+ item
1350
+ });
1351
+ }, [
1352
+ onItemHover,
1353
+ item
1354
+ ]);
1355
+ const handleContextMenu = useCallback6((event) => {
1356
+ event.preventDefault();
1357
+ setMenuOpen(true);
1358
+ }, [
1359
+ setMenuOpen
1360
+ ]);
1361
+ const childProps = {
1362
+ draggable: draggableProp,
1363
+ renderColumns: Columns,
1364
+ blockInstruction,
1365
+ canDrop,
1366
+ canSelect,
1367
+ onItemHover,
1368
+ onOpenChange,
1369
+ onSelect
1370
+ };
1371
+ return /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(Treegrid.Row, {
1372
+ ref: rowRef,
1373
+ key: id,
1374
+ id,
1375
+ "aria-labelledby": `${id}__label`,
1376
+ parentOf: parentOf?.join(TREEGRID_PARENT_OF_SEPARATOR),
1377
+ "data-object-id": id,
1378
+ "data-testid": testId,
1379
+ // NOTE(thure): This is intentionally an empty string to for descendents to select by in the CSS
1380
+ // without alerting the user (except for in the correct link element). See also:
1381
+ // https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current#description
1382
+ "aria-current": current ? "" : void 0,
1383
+ classNames: mx7("grid grid-cols-subgrid col-[tree-row] mt-0.5 is-current:bg-active-surface", hoverableControls, hoverableFocusedKeyboardControls, hoverableFocusedWithinControls, hoverableDescriptionIcons, ghostFocusWithin, ghostHover, className),
1384
+ onKeyDown: handleKeyDown,
1385
+ onMouseEnter: handleItemHover,
1386
+ onContextMenu: handleContextMenu
1387
+ }, /* @__PURE__ */ React11.createElement("div", {
1388
+ role: "none",
1389
+ className: "indent relative grid grid-cols-subgrid col-[tree-row]",
1390
+ style: paddingIndentation(level)
1391
+ }, /* @__PURE__ */ React11.createElement(Treegrid.Cell, {
1392
+ classNames: "flex items-center"
1393
+ }, /* @__PURE__ */ React11.createElement(TreeItemToggle, {
1394
+ isBranch,
1395
+ open,
1396
+ onClick: handleOpenToggle
1397
+ }), /* @__PURE__ */ React11.createElement(TreeItemHeading, {
1398
+ disabled,
1399
+ current,
1400
+ label,
1401
+ className: headingClassName,
1402
+ icon,
1403
+ iconHue,
1404
+ onSelect: handleSelect,
1405
+ ref: buttonRef
1406
+ })), Columns && /* @__PURE__ */ React11.createElement(Columns, {
1407
+ item,
1408
+ path,
1409
+ open,
1410
+ menuOpen,
1411
+ setMenuOpen
1412
+ }), instruction && /* @__PURE__ */ React11.createElement(NaturalTreeItem.DropIndicator, {
1413
+ instruction,
1414
+ gap: 2
1415
+ }))), open && childIds.map((childId, index) => /* @__PURE__ */ React11.createElement(TreeItemById, {
1416
+ key: childId,
1417
+ id: childId,
1418
+ path,
1419
+ last: index === childIds.length - 1,
1420
+ ...childProps
1421
+ })));
807
1422
  };
808
1423
  var TreeItem = /* @__PURE__ */ memo3(RawTreeItem);
1424
+ var RawTreeItemById = ({ id, ...props }) => {
1425
+ const { item: itemAtom } = useTree();
1426
+ const item = useAtomValue(itemAtom(id));
1427
+ if (!item) {
1428
+ return null;
1429
+ }
1430
+ return /* @__PURE__ */ React11.createElement(TreeItem, {
1431
+ item,
1432
+ ...props
1433
+ });
1434
+ };
1435
+ var TreeItemById = /* @__PURE__ */ memo3(RawTreeItemById);
809
1436
 
810
1437
  // src/components/Tree/Tree.tsx
811
- var Tree = ({ root, path, id, useItems, getProps, isOpen, isCurrent, draggable: draggable3 = false, gridTemplateColumns = "[tree-row-start] 1fr min-content [tree-row-end]", classNames, levelOffset, renderColumns, canDrop, onOpenChange, onSelect }) => {
812
- var _effect = _useSignals8();
813
- try {
814
- const context = useMemo2(() => ({
815
- useItems,
816
- getProps,
817
- isOpen,
818
- isCurrent
819
- }), [
820
- useItems,
821
- getProps,
822
- isOpen,
823
- isCurrent
824
- ]);
825
- const items = useItems(root);
826
- const treePath = useMemo2(() => path ? [
827
- ...path,
828
- id
829
- ] : [
830
- id
831
- ], [
832
- id,
833
- path
834
- ]);
835
- return /* @__PURE__ */ React8.createElement(Treegrid2.Root, {
836
- gridTemplateColumns,
837
- classNames
838
- }, /* @__PURE__ */ React8.createElement(TreeProvider, {
839
- value: context
840
- }, items.map((item, index) => /* @__PURE__ */ React8.createElement(TreeItem, {
841
- key: item.id,
842
- item,
843
- last: index === items.length - 1,
844
- path: treePath,
845
- levelOffset,
846
- draggable: draggable3,
847
- renderColumns,
848
- canDrop,
849
- onOpenChange,
850
- onSelect
851
- }))));
852
- } finally {
853
- _effect.f();
854
- }
1438
+ var Tree = ({ model, rootId, path, id, draggable: draggable3 = false, gridTemplateColumns = "[tree-row-start] 1fr min-content [tree-row-end]", classNames, levelOffset, renderColumns, blockInstruction, canDrop, canSelect, onOpenChange, onSelect, onItemHover }) => {
1439
+ const childIds = useAtomValue2(model.childIds(rootId));
1440
+ const treePath = useMemo3(() => path ? [
1441
+ ...path,
1442
+ id
1443
+ ] : [
1444
+ id
1445
+ ], [
1446
+ id,
1447
+ path
1448
+ ]);
1449
+ const childProps = {
1450
+ path: treePath,
1451
+ levelOffset,
1452
+ draggable: draggable3,
1453
+ renderColumns,
1454
+ blockInstruction,
1455
+ canDrop,
1456
+ canSelect,
1457
+ onOpenChange,
1458
+ onSelect,
1459
+ onItemHover
1460
+ };
1461
+ return /* @__PURE__ */ React12.createElement(Treegrid2.Root, {
1462
+ gridTemplateColumns,
1463
+ classNames
1464
+ }, /* @__PURE__ */ React12.createElement(TreeProvider, {
1465
+ value: model
1466
+ }, childIds.map((childId, index) => /* @__PURE__ */ React12.createElement(TreeItemById, {
1467
+ key: childId,
1468
+ id: childId,
1469
+ last: index === childIds.length - 1,
1470
+ ...childProps
1471
+ }))));
855
1472
  };
856
1473
 
857
1474
  // src/util/path.ts
@@ -871,13 +1488,27 @@ var Path = {
871
1488
  };
872
1489
  export {
873
1490
  Accordion,
1491
+ Combobox,
1492
+ DEFAULT_INDENTATION,
874
1493
  List,
1494
+ Listbox,
875
1495
  Path,
1496
+ Picker,
1497
+ Row,
1498
+ RowList,
876
1499
  Tree,
877
1500
  TreeDataSchema,
878
1501
  TreeItem,
1502
+ TreeItemById,
1503
+ TreeItemToggle,
879
1504
  TreeProvider,
1505
+ createListboxScope,
1506
+ createRowListScope,
880
1507
  isTreeData,
1508
+ paddingIndentation,
1509
+ usePickerInputContext,
1510
+ usePickerItemContext,
1511
+ useRowListSelection,
881
1512
  useTree
882
1513
  };
883
1514
  //# sourceMappingURL=index.mjs.map