@edux-design/tree-select 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +83 -0
- package/README.md +118 -3
- package/dist/index.css +39 -0
- package/dist/index.d.mts +92 -18
- package/dist/index.d.ts +92 -18
- package/dist/index.js +257 -66
- package/dist/index.mjs +257 -66
- package/docs/DS15_INVENTORY.md +52 -0
- package/docs/DS15_VERIFICATION.md +62 -0
- package/package.json +7 -14
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/elements/TreeSelect.jsx
|
|
2
|
-
import React3, { useCallback as useCallback2, useMemo as useMemo2, useRef, useState } from "react";
|
|
2
|
+
import React3, { forwardRef, useCallback as useCallback2, useImperativeHandle, useMemo as useMemo2, useRef, useState } from "react";
|
|
3
3
|
import {
|
|
4
4
|
DndContext,
|
|
5
5
|
DragOverlay,
|
|
@@ -30,6 +30,21 @@ import {
|
|
|
30
30
|
TooltipTrigger
|
|
31
31
|
} from "@edux-design/tooltips";
|
|
32
32
|
|
|
33
|
+
// src/elements/treeSelectInteraction.js
|
|
34
|
+
function isTreeRowControl(event) {
|
|
35
|
+
const control = event.target?.closest?.(
|
|
36
|
+
'button, input, textarea, select, a[href], [contenteditable="true"], [role="button"], [role="checkbox"], [role="menuitem"], [tabindex]:not([tabindex="-1"]), [data-tree-interactive]'
|
|
37
|
+
);
|
|
38
|
+
return Boolean(control && control !== event.currentTarget && event.currentTarget.contains(control));
|
|
39
|
+
}
|
|
40
|
+
function hasActivationModifier(event) {
|
|
41
|
+
return event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
|
|
42
|
+
}
|
|
43
|
+
function getTreeItemLabel(node, getItemLabel) {
|
|
44
|
+
if (getItemLabel) return getItemLabel(node);
|
|
45
|
+
return typeof node.label === "string" || typeof node.label === "number" ? String(node.label) : String(node.id);
|
|
46
|
+
}
|
|
47
|
+
|
|
33
48
|
// src/elements/treeSelectConstants.js
|
|
34
49
|
var HANDLE_WIDTH = 28;
|
|
35
50
|
var TOGGLE_WIDTH = 28;
|
|
@@ -38,6 +53,8 @@ var INDICATOR_INSET = HANDLE_WIDTH + TOGGLE_WIDTH + 12;
|
|
|
38
53
|
|
|
39
54
|
// src/elements/TreeRow.jsx
|
|
40
55
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
56
|
+
var DRAG_ROW_BACKGROUND = "color-mix(in srgb, var(--color-fg-base) 4%, transparent)";
|
|
57
|
+
var SELECTED_ROW_BACKGROUND = "color-mix(in srgb, var(--color-primary-600) 8%, transparent)";
|
|
41
58
|
var TreeRow = ({
|
|
42
59
|
item,
|
|
43
60
|
depth,
|
|
@@ -49,6 +66,11 @@ var TreeRow = ({
|
|
|
49
66
|
dragAttributes,
|
|
50
67
|
dragRef,
|
|
51
68
|
rowRef,
|
|
69
|
+
rowAccessibility,
|
|
70
|
+
labelContext,
|
|
71
|
+
accessibleLabel,
|
|
72
|
+
rowClassName,
|
|
73
|
+
labelClassName,
|
|
52
74
|
isDragging,
|
|
53
75
|
isSelected,
|
|
54
76
|
onRowClick,
|
|
@@ -70,13 +92,23 @@ var TreeRow = ({
|
|
|
70
92
|
canDelete,
|
|
71
93
|
onDelete,
|
|
72
94
|
deleteTooltip,
|
|
73
|
-
additionalActionsMenu
|
|
95
|
+
additionalActionsMenu,
|
|
96
|
+
renderLabel
|
|
74
97
|
}) => {
|
|
75
98
|
const toggleLabel = isExpanded ? "Collapse item" : "Expand item";
|
|
76
99
|
const label = item.node.label ?? item.node.id;
|
|
100
|
+
const labelContent = renderLabel ? renderLabel({
|
|
101
|
+
id: item.id,
|
|
102
|
+
node: item.node,
|
|
103
|
+
depth,
|
|
104
|
+
parentId: item.parentId,
|
|
105
|
+
hasChildren: item.hasChildren,
|
|
106
|
+
isExpanded,
|
|
107
|
+
...labelContext
|
|
108
|
+
}) : label;
|
|
77
109
|
const showToggle = item.hasChildren;
|
|
78
110
|
const [additionalMenuOpen, setAdditionalMenuOpen] = React.useState(false);
|
|
79
|
-
const rowBackground = isDragging ?
|
|
111
|
+
const rowBackground = isDragging ? DRAG_ROW_BACKGROUND : isSelected ? SELECTED_ROW_BACKGROUND : void 0;
|
|
80
112
|
const rowPaddingLeft = depth * INDENT_WIDTH + 8;
|
|
81
113
|
const hasAdditionalActionsMenu = Boolean(additionalActionsMenu);
|
|
82
114
|
const showActions = canAddChild || canAddSibling || canDelete || hasAdditionalActionsMenu;
|
|
@@ -97,9 +129,12 @@ var TreeRow = ({
|
|
|
97
129
|
"div",
|
|
98
130
|
{
|
|
99
131
|
ref: rowRef,
|
|
100
|
-
|
|
132
|
+
...rowAccessibility,
|
|
133
|
+
onClick: (event) => onRowClick?.(event, item.id),
|
|
101
134
|
"data-tree-row": item.id,
|
|
102
|
-
|
|
135
|
+
"data-selected": isSelected || void 0,
|
|
136
|
+
"data-current": labelContext?.isCurrent || void 0,
|
|
137
|
+
className: ["transition-colors hover:bg-bg-primary-subtle focus-visible:shadow-focus", labelContext?.isCurrent ? "bg-bg-primary-subtle text-fg-primary-emphasis" : "", rowClassName].filter(Boolean).join(" "),
|
|
103
138
|
style: {
|
|
104
139
|
display: "flex",
|
|
105
140
|
alignItems: "center",
|
|
@@ -118,7 +153,7 @@ var TreeRow = ({
|
|
|
118
153
|
...dragAttributes,
|
|
119
154
|
...dragHandleProps,
|
|
120
155
|
ref: dragRef,
|
|
121
|
-
"aria-label":
|
|
156
|
+
"aria-label": `Move ${accessibleLabel}`,
|
|
122
157
|
onClick: (event) => event.stopPropagation(),
|
|
123
158
|
className: "border border-border-subtle bg-bg-base text-fg-base",
|
|
124
159
|
style: {
|
|
@@ -138,7 +173,8 @@ var TreeRow = ({
|
|
|
138
173
|
"button",
|
|
139
174
|
{
|
|
140
175
|
type: "button",
|
|
141
|
-
"aria-label": toggleLabel
|
|
176
|
+
"aria-label": `${toggleLabel}: ${accessibleLabel}`,
|
|
177
|
+
"aria-expanded": isExpanded,
|
|
142
178
|
onClick: () => onToggle(item.id),
|
|
143
179
|
style: {
|
|
144
180
|
width: 24,
|
|
@@ -160,6 +196,8 @@ var TreeRow = ({
|
|
|
160
196
|
/* @__PURE__ */ jsx(
|
|
161
197
|
Checkbox,
|
|
162
198
|
{
|
|
199
|
+
"aria-label": `${toggleLabel}: ${accessibleLabel}`,
|
|
200
|
+
"aria-expanded": isExpanded,
|
|
163
201
|
checked: false,
|
|
164
202
|
plus: !isExpanded,
|
|
165
203
|
indeterminate: isExpanded,
|
|
@@ -171,19 +209,21 @@ var TreeRow = ({
|
|
|
171
209
|
"input",
|
|
172
210
|
{
|
|
173
211
|
type: "text",
|
|
212
|
+
"aria-label": `Edit ${accessibleLabel}`,
|
|
213
|
+
onClick: (event) => event.stopPropagation(),
|
|
174
214
|
value: draftLabel,
|
|
175
215
|
autoFocus: true,
|
|
176
216
|
"data-tree-input": item.id,
|
|
177
217
|
onChange: (event) => onDraftChange(event.target.value),
|
|
178
218
|
onBlur: onCommitEdit,
|
|
179
219
|
onKeyDown: (event) => {
|
|
220
|
+
event.stopPropagation();
|
|
180
221
|
if (event.key === "Enter") onCommitEdit();
|
|
181
222
|
if (event.key === "Escape") onCancelEdit();
|
|
182
223
|
},
|
|
183
224
|
style: {
|
|
184
225
|
fontSize: 14,
|
|
185
226
|
border: "none",
|
|
186
|
-
outline: "none",
|
|
187
227
|
background: "transparent",
|
|
188
228
|
padding: 0
|
|
189
229
|
}
|
|
@@ -192,17 +232,32 @@ var TreeRow = ({
|
|
|
192
232
|
"span",
|
|
193
233
|
{
|
|
194
234
|
onClick: (event) => {
|
|
195
|
-
if (!isEditable) return;
|
|
235
|
+
if (!isEditable || event.defaultPrevented || isTreeRowControl(event)) return;
|
|
196
236
|
event.stopPropagation();
|
|
197
|
-
onStartEdit
|
|
237
|
+
onStartEdit?.();
|
|
198
238
|
},
|
|
239
|
+
role: isEditable ? "button" : void 0,
|
|
240
|
+
tabIndex: isEditable ? 0 : void 0,
|
|
241
|
+
"aria-label": isEditable ? `Edit ${accessibleLabel}` : void 0,
|
|
242
|
+
onKeyDown: isEditable ? (event) => {
|
|
243
|
+
if (event.target !== event.currentTarget) return;
|
|
244
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
245
|
+
event.preventDefault();
|
|
246
|
+
event.stopPropagation();
|
|
247
|
+
onStartEdit?.();
|
|
248
|
+
}
|
|
249
|
+
} : void 0,
|
|
199
250
|
"data-tree-label": item.id,
|
|
251
|
+
className: [renderLabel ? "inline-flex items-center gap-6" : "", labelClassName].filter(Boolean).join(" "),
|
|
200
252
|
style: {
|
|
201
253
|
fontSize: 14,
|
|
254
|
+
display: "inline-flex",
|
|
255
|
+
alignItems: "center",
|
|
256
|
+
minHeight: isEditable ? 24 : void 0,
|
|
202
257
|
cursor: isEditable ? "pointer" : "default",
|
|
203
|
-
|
|
258
|
+
...!renderLabel ? labelStyle : {}
|
|
204
259
|
},
|
|
205
|
-
children:
|
|
260
|
+
children: labelContent
|
|
206
261
|
}
|
|
207
262
|
),
|
|
208
263
|
showActions ? /* @__PURE__ */ jsxs(
|
|
@@ -220,7 +275,7 @@ var TreeRow = ({
|
|
|
220
275
|
isBare: true,
|
|
221
276
|
icon: /* @__PURE__ */ jsx(Plus, { "aria-hidden": "true" }),
|
|
222
277
|
"aria-label": "Add child",
|
|
223
|
-
onClick: () => onAddChild
|
|
278
|
+
onClick: () => onAddChild?.(item.id)
|
|
224
279
|
}
|
|
225
280
|
) }),
|
|
226
281
|
/* @__PURE__ */ jsx(TooltipContent, { children: addChildTooltip ?? "Add child" })
|
|
@@ -233,7 +288,7 @@ var TreeRow = ({
|
|
|
233
288
|
isBare: true,
|
|
234
289
|
icon: /* @__PURE__ */ jsx(List, { "aria-hidden": "true" }),
|
|
235
290
|
"aria-label": "Add sibling",
|
|
236
|
-
onClick: () => onAddSibling
|
|
291
|
+
onClick: () => onAddSibling?.(item.id)
|
|
237
292
|
}
|
|
238
293
|
) }),
|
|
239
294
|
/* @__PURE__ */ jsx(TooltipContent, { children: addSiblingTooltip ?? "Add sibling" })
|
|
@@ -246,7 +301,7 @@ var TreeRow = ({
|
|
|
246
301
|
isBare: true,
|
|
247
302
|
icon: /* @__PURE__ */ jsx(Close, { "aria-hidden": "true" }),
|
|
248
303
|
"aria-label": "Delete item",
|
|
249
|
-
onClick: () => onDelete
|
|
304
|
+
onClick: () => onDelete?.(item.id)
|
|
250
305
|
}
|
|
251
306
|
) }),
|
|
252
307
|
/* @__PURE__ */ jsx(TooltipContent, { children: deleteTooltip ?? "Delete item" })
|
|
@@ -287,6 +342,12 @@ var TreeSortableRow = ({
|
|
|
287
342
|
onToggle,
|
|
288
343
|
onRowClick,
|
|
289
344
|
rowRefs,
|
|
345
|
+
rowAccessibility,
|
|
346
|
+
labelContext,
|
|
347
|
+
accessibleLabel,
|
|
348
|
+
rowClassName,
|
|
349
|
+
labelClassName,
|
|
350
|
+
onRowSelect,
|
|
290
351
|
isActive,
|
|
291
352
|
isSelected,
|
|
292
353
|
isGroupDragging,
|
|
@@ -309,7 +370,8 @@ var TreeSortableRow = ({
|
|
|
309
370
|
canDelete,
|
|
310
371
|
onDelete,
|
|
311
372
|
deleteTooltip,
|
|
312
|
-
additionalActionsMenu
|
|
373
|
+
additionalActionsMenu,
|
|
374
|
+
renderLabel
|
|
313
375
|
}) => {
|
|
314
376
|
const {
|
|
315
377
|
attributes,
|
|
@@ -333,12 +395,11 @@ var TreeSortableRow = ({
|
|
|
333
395
|
() => ({
|
|
334
396
|
...listeners,
|
|
335
397
|
onPointerDown: (event) => {
|
|
336
|
-
|
|
337
|
-
(
|
|
338
|
-
onRowClick == null ? void 0 : onRowClick(event, item.id);
|
|
398
|
+
listeners?.onPointerDown?.(event);
|
|
399
|
+
onRowSelect?.(event, item.id);
|
|
339
400
|
}
|
|
340
401
|
}),
|
|
341
|
-
[item.id, listeners,
|
|
402
|
+
[item.id, listeners, onRowSelect]
|
|
342
403
|
);
|
|
343
404
|
return /* @__PURE__ */ jsx2(
|
|
344
405
|
TreeRow,
|
|
@@ -352,6 +413,11 @@ var TreeSortableRow = ({
|
|
|
352
413
|
dragAttributes: attributes,
|
|
353
414
|
dragRef: setActivatorNodeRef,
|
|
354
415
|
rowRef: rowRefCallback,
|
|
416
|
+
rowAccessibility,
|
|
417
|
+
labelContext,
|
|
418
|
+
accessibleLabel,
|
|
419
|
+
rowClassName,
|
|
420
|
+
labelClassName,
|
|
355
421
|
isDragging: isDragging || isActive || isGroupDragging,
|
|
356
422
|
isSelected,
|
|
357
423
|
isEditable,
|
|
@@ -373,7 +439,8 @@ var TreeSortableRow = ({
|
|
|
373
439
|
canDelete,
|
|
374
440
|
onDelete,
|
|
375
441
|
deleteTooltip,
|
|
376
|
-
additionalActionsMenu
|
|
442
|
+
additionalActionsMenu,
|
|
443
|
+
renderLabel
|
|
377
444
|
}
|
|
378
445
|
);
|
|
379
446
|
};
|
|
@@ -425,11 +492,10 @@ var collectDraggedIds = (items, rootIds) => {
|
|
|
425
492
|
var getSelectedRootIds = (items, selectedIds) => {
|
|
426
493
|
const itemById = new Map(items.map((item) => [item.id, item]));
|
|
427
494
|
return items.filter((item) => selectedIds.has(item.id)).filter((item) => {
|
|
428
|
-
|
|
429
|
-
let parentId = (_a = itemById.get(item.id)) == null ? void 0 : _a.parentId;
|
|
495
|
+
let parentId = itemById.get(item.id)?.parentId;
|
|
430
496
|
while (parentId) {
|
|
431
497
|
if (selectedIds.has(parentId)) return false;
|
|
432
|
-
parentId =
|
|
498
|
+
parentId = itemById.get(parentId)?.parentId;
|
|
433
499
|
}
|
|
434
500
|
return true;
|
|
435
501
|
}).map((item) => item.id);
|
|
@@ -729,7 +795,7 @@ var getDropIndicator = ({
|
|
|
729
795
|
|
|
730
796
|
// src/elements/TreeSelect.jsx
|
|
731
797
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
732
|
-
|
|
798
|
+
var TreeSelect = forwardRef(function TreeSelect2({
|
|
733
799
|
data,
|
|
734
800
|
onDataChange,
|
|
735
801
|
allowDragAndDrop = false,
|
|
@@ -748,9 +814,17 @@ function TreeSelect({
|
|
|
748
814
|
defaultExpanded,
|
|
749
815
|
expanded,
|
|
750
816
|
onExpandedChange,
|
|
751
|
-
renderAdditionalActionsMenu
|
|
752
|
-
|
|
753
|
-
|
|
817
|
+
renderAdditionalActionsMenu,
|
|
818
|
+
renderLabel,
|
|
819
|
+
onNodeActivate,
|
|
820
|
+
currentId,
|
|
821
|
+
getItemLabel,
|
|
822
|
+
rowClassName,
|
|
823
|
+
labelClassName,
|
|
824
|
+
"aria-label": ariaLabel = "Tree",
|
|
825
|
+
"aria-labelledby": ariaLabelledBy,
|
|
826
|
+
...rootProps
|
|
827
|
+
}, forwardedRef) {
|
|
754
828
|
const [uncontrolledExpandedIds, setExpandedIds] = useState(
|
|
755
829
|
() => getExpandedIds(data ?? [], defaultExpanded)
|
|
756
830
|
);
|
|
@@ -760,6 +834,8 @@ function TreeSelect({
|
|
|
760
834
|
const [horizontalOffset, setHorizontalOffset] = useState(0);
|
|
761
835
|
const [selectedIds, setSelectedIds] = useState(() => /* @__PURE__ */ new Set());
|
|
762
836
|
const [lastSelectedId, setLastSelectedId] = useState(null);
|
|
837
|
+
const [focusedId, setFocusedId] = useState(null);
|
|
838
|
+
const [focusWithinId, setFocusWithinId] = useState(null);
|
|
763
839
|
const [editingId, setEditingId] = useState(null);
|
|
764
840
|
const [draftLabel, setDraftLabel] = useState("");
|
|
765
841
|
const idCounterRef = useRef(0);
|
|
@@ -779,6 +855,22 @@ function TreeSelect({
|
|
|
779
855
|
() => new Map(visibleItems.map((item) => [item.id, item])),
|
|
780
856
|
[visibleItems]
|
|
781
857
|
);
|
|
858
|
+
const getRowElement = useCallback2((id) => rowRefs.current.get(normaliseId(id)) ?? null, []);
|
|
859
|
+
useImperativeHandle(forwardedRef, () => ({
|
|
860
|
+
getRowElement,
|
|
861
|
+
focusItem(id, options) {
|
|
862
|
+
const row = getRowElement(id);
|
|
863
|
+
if (!row) return false;
|
|
864
|
+
row.focus(options);
|
|
865
|
+
return true;
|
|
866
|
+
},
|
|
867
|
+
scrollToItem(id, options = { block: "nearest", inline: "nearest" }) {
|
|
868
|
+
const row = getRowElement(id);
|
|
869
|
+
if (!row || typeof row.scrollIntoView !== "function") return false;
|
|
870
|
+
row.scrollIntoView(options);
|
|
871
|
+
return true;
|
|
872
|
+
}
|
|
873
|
+
}), [getRowElement]);
|
|
782
874
|
const siblingOrderMap = useMemo2(
|
|
783
875
|
() => buildSiblingOrderMap(treeData),
|
|
784
876
|
[treeData]
|
|
@@ -789,19 +881,18 @@ function TreeSelect({
|
|
|
789
881
|
}, [maxDepth]);
|
|
790
882
|
const resolvedLevelConfig = useCallback2(
|
|
791
883
|
(depth) => {
|
|
792
|
-
|
|
793
|
-
const
|
|
794
|
-
const perLevel = ((_a2 = levelConfig == null ? void 0 : levelConfig.levels) == null ? void 0 : _a2[depth]) ?? {};
|
|
884
|
+
const defaults = levelConfig?.default ?? {};
|
|
885
|
+
const perLevel = levelConfig?.levels?.[depth] ?? {};
|
|
795
886
|
const editable = perLevel.editable ?? defaults.editable ?? isEditable;
|
|
796
887
|
const actions = {
|
|
797
|
-
addChild:
|
|
798
|
-
addSibling:
|
|
799
|
-
delete:
|
|
888
|
+
addChild: perLevel.actions?.addChild ?? defaults.actions?.addChild ?? editable,
|
|
889
|
+
addSibling: perLevel.actions?.addSibling ?? defaults.actions?.addSibling ?? editable,
|
|
890
|
+
delete: perLevel.actions?.delete ?? defaults.actions?.delete ?? (editable && allowDelete)
|
|
800
891
|
};
|
|
801
892
|
const tooltips = {
|
|
802
|
-
addChild:
|
|
803
|
-
addSibling:
|
|
804
|
-
delete:
|
|
893
|
+
addChild: perLevel.tooltips?.addChild ?? defaults.tooltips?.addChild ?? addChildTooltip,
|
|
894
|
+
addSibling: perLevel.tooltips?.addSibling ?? defaults.tooltips?.addSibling ?? addSiblingTooltip,
|
|
895
|
+
delete: perLevel.tooltips?.delete ?? defaults.tooltips?.delete ?? deleteTooltip
|
|
805
896
|
};
|
|
806
897
|
return { editable, actions, tooltips };
|
|
807
898
|
},
|
|
@@ -821,7 +912,7 @@ function TreeSelect({
|
|
|
821
912
|
(updater) => {
|
|
822
913
|
if (isExpandedControlled) {
|
|
823
914
|
const next = updater(new Set(expandedIds));
|
|
824
|
-
onExpandedChange
|
|
915
|
+
onExpandedChange?.(next);
|
|
825
916
|
return;
|
|
826
917
|
}
|
|
827
918
|
setExpandedIds((prev) => updater(new Set(prev)));
|
|
@@ -830,7 +921,7 @@ function TreeSelect({
|
|
|
830
921
|
);
|
|
831
922
|
const emitDataChange = useCallback2(
|
|
832
923
|
(nextTree, meta) => {
|
|
833
|
-
onDataChange
|
|
924
|
+
onDataChange?.(nextTree, meta);
|
|
834
925
|
},
|
|
835
926
|
[onDataChange]
|
|
836
927
|
);
|
|
@@ -844,17 +935,16 @@ function TreeSelect({
|
|
|
844
935
|
return prev;
|
|
845
936
|
});
|
|
846
937
|
}, [updateExpandedIds]);
|
|
847
|
-
const
|
|
938
|
+
const handleRowSelection = useCallback2(
|
|
848
939
|
(event, id) => {
|
|
849
|
-
var _a2, _b2;
|
|
850
940
|
if (!allowMultiDrag) return;
|
|
851
941
|
const hasMultiKey = event.metaKey || event.ctrlKey;
|
|
852
|
-
const parentId =
|
|
942
|
+
const parentId = itemById.get(id)?.parentId ?? null;
|
|
853
943
|
const siblingOrder = siblingOrderMap.get(parentId) ?? [];
|
|
854
944
|
if (event.shiftKey) {
|
|
855
945
|
const anchorIndex = siblingOrder.indexOf(lastSelectedId);
|
|
856
946
|
const targetIndex = siblingOrder.indexOf(id);
|
|
857
|
-
if (anchorIndex < 0 || targetIndex < 0 || (
|
|
947
|
+
if (anchorIndex < 0 || targetIndex < 0 || (itemById.get(lastSelectedId ?? "")?.parentId ?? null) !== parentId) {
|
|
858
948
|
setSelectedIds(/* @__PURE__ */ new Set([id]));
|
|
859
949
|
setLastSelectedId(id);
|
|
860
950
|
return;
|
|
@@ -876,10 +966,9 @@ function TreeSelect({
|
|
|
876
966
|
return;
|
|
877
967
|
}
|
|
878
968
|
setSelectedIds((prev) => {
|
|
879
|
-
var _a3;
|
|
880
969
|
if (prev.size) {
|
|
881
970
|
const firstSelected = prev.values().next().value;
|
|
882
|
-
const selectedParent =
|
|
971
|
+
const selectedParent = itemById.get(firstSelected)?.parentId ?? null;
|
|
883
972
|
if (selectedParent !== parentId) {
|
|
884
973
|
return /* @__PURE__ */ new Set([id]);
|
|
885
974
|
}
|
|
@@ -906,6 +995,16 @@ function TreeSelect({
|
|
|
906
995
|
siblingOrderMap
|
|
907
996
|
]
|
|
908
997
|
);
|
|
998
|
+
const activateItem = useCallback2((event, id, inputSource) => {
|
|
999
|
+
if (event.defaultPrevented || event.repeat || inputSource === "pointer" && event.button !== 0 || hasActivationModifier(event) || isTreeRowControl(event)) return;
|
|
1000
|
+
const item = itemById.get(id);
|
|
1001
|
+
if (item && !activeId) onNodeActivate?.({ ...item, event, inputSource });
|
|
1002
|
+
}, [activeId, itemById, onNodeActivate]);
|
|
1003
|
+
const handleRowClick = useCallback2((event, id) => {
|
|
1004
|
+
if (event.defaultPrevented || isTreeRowControl(event)) return;
|
|
1005
|
+
handleRowSelection(event, id);
|
|
1006
|
+
activateItem(event, id, "pointer");
|
|
1007
|
+
}, [activateItem, handleRowSelection]);
|
|
909
1008
|
const draggedRootIds = useMemo2(
|
|
910
1009
|
() => getDraggedRootIds({
|
|
911
1010
|
activeId,
|
|
@@ -937,13 +1036,11 @@ function TreeSelect({
|
|
|
937
1036
|
setHorizontalOffset(event.delta.x);
|
|
938
1037
|
}, []);
|
|
939
1038
|
const handleDragOver = useCallback2((event) => {
|
|
940
|
-
|
|
941
|
-
if ((_a2 = event.over) == null ? void 0 : _a2.id) {
|
|
1039
|
+
if (event.over?.id) {
|
|
942
1040
|
setOverId(normaliseId(event.over.id));
|
|
943
1041
|
}
|
|
944
1042
|
}, []);
|
|
945
1043
|
const handleDragEnd = useCallback2(() => {
|
|
946
|
-
var _a2;
|
|
947
1044
|
const projection = getDropProjection({
|
|
948
1045
|
items: visibleItems,
|
|
949
1046
|
activeId,
|
|
@@ -959,7 +1056,7 @@ function TreeSelect({
|
|
|
959
1056
|
return;
|
|
960
1057
|
}
|
|
961
1058
|
const { parentId, siblingIndex, projectedDepth } = projection;
|
|
962
|
-
const sourceParentId =
|
|
1059
|
+
const sourceParentId = itemById.get(draggedRootIds[0] ?? activeId)?.parentId ?? null;
|
|
963
1060
|
const { nextNodes, removedNodes } = removeNodes(treeData, draggedRootIds);
|
|
964
1061
|
if (!removedNodes.length) {
|
|
965
1062
|
setActiveId(null);
|
|
@@ -1072,8 +1169,7 @@ function TreeSelect({
|
|
|
1072
1169
|
);
|
|
1073
1170
|
const startEdit = useCallback2(
|
|
1074
1171
|
(id) => {
|
|
1075
|
-
|
|
1076
|
-
const currentLabel = ((_b2 = (_a2 = itemById.get(id)) == null ? void 0 : _a2.node) == null ? void 0 : _b2.label) ?? ((_d = (_c = itemById.get(id)) == null ? void 0 : _c.node) == null ? void 0 : _d.id) ?? "";
|
|
1172
|
+
const currentLabel = itemById.get(id)?.node?.label ?? itemById.get(id)?.node?.id ?? "";
|
|
1077
1173
|
setEditingId(id);
|
|
1078
1174
|
setDraftLabel(String(currentLabel));
|
|
1079
1175
|
},
|
|
@@ -1101,6 +1197,21 @@ function TreeSelect({
|
|
|
1101
1197
|
const cancelEdit = useCallback2(() => {
|
|
1102
1198
|
setEditingId(null);
|
|
1103
1199
|
}, []);
|
|
1200
|
+
const renderLabelContent = useCallback2(
|
|
1201
|
+
(item) => {
|
|
1202
|
+
if (!item) return null;
|
|
1203
|
+
if (!renderLabel) return item.node.label ?? item.id;
|
|
1204
|
+
return renderLabel({
|
|
1205
|
+
...item,
|
|
1206
|
+
isExpanded: expandedIds.has(item.id),
|
|
1207
|
+
isCurrent: currentId != null && normaliseId(currentId) === item.id,
|
|
1208
|
+
isSelected: selectedIds.has(item.id),
|
|
1209
|
+
isFocused: focusWithinId === item.id,
|
|
1210
|
+
getRowElement: () => null
|
|
1211
|
+
});
|
|
1212
|
+
},
|
|
1213
|
+
[currentId, expandedIds, focusWithinId, renderLabel, selectedIds]
|
|
1214
|
+
);
|
|
1104
1215
|
const indicator = useMemo2(() => {
|
|
1105
1216
|
if (!allowDragAndDrop) return null;
|
|
1106
1217
|
return getDropIndicator({
|
|
@@ -1122,7 +1233,61 @@ function TreeSelect({
|
|
|
1122
1233
|
visibleItems,
|
|
1123
1234
|
maxDepth
|
|
1124
1235
|
]);
|
|
1125
|
-
|
|
1236
|
+
const rowContext = (item) => ({
|
|
1237
|
+
...item,
|
|
1238
|
+
isExpanded: expandedIds.has(item.id),
|
|
1239
|
+
isCurrent: currentId != null && normaliseId(currentId) === item.id,
|
|
1240
|
+
isSelected: selectedIds.has(item.id),
|
|
1241
|
+
isFocused: focusWithinId === item.id,
|
|
1242
|
+
getRowElement: () => getRowElement(item.id)
|
|
1243
|
+
});
|
|
1244
|
+
const rowAccessibility = (item) => ({
|
|
1245
|
+
role: "treeitem",
|
|
1246
|
+
"aria-label": getTreeItemLabel(item.node, getItemLabel),
|
|
1247
|
+
"aria-level": item.depth + 1,
|
|
1248
|
+
"aria-expanded": item.hasChildren ? expandedIds.has(item.id) : void 0,
|
|
1249
|
+
"aria-selected": allowMultiDrag ? selectedIds.has(item.id) : void 0,
|
|
1250
|
+
"aria-current": currentId != null && normaliseId(currentId) === item.id ? "page" : void 0,
|
|
1251
|
+
"aria-posinset": (siblingOrderMap.get(item.parentId) || []).indexOf(item.id) + 1,
|
|
1252
|
+
"aria-setsize": (siblingOrderMap.get(item.parentId) || []).length,
|
|
1253
|
+
tabIndex: (focusedId && itemById.has(focusedId) ? focusedId : visibleItems[0]?.id) === item.id ? 0 : -1,
|
|
1254
|
+
onFocus: () => {
|
|
1255
|
+
setFocusedId(item.id);
|
|
1256
|
+
setFocusWithinId(item.id);
|
|
1257
|
+
},
|
|
1258
|
+
onBlur: (event) => {
|
|
1259
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setFocusWithinId(null);
|
|
1260
|
+
},
|
|
1261
|
+
onKeyDown: (event) => {
|
|
1262
|
+
if (event.target !== event.currentTarget || event.defaultPrevented) return;
|
|
1263
|
+
if (event.repeat && (event.key === "Enter" || event.key === " ")) {
|
|
1264
|
+
event.preventDefault();
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
const index = visibleItems.findIndex((row) => row.id === item.id);
|
|
1268
|
+
let target;
|
|
1269
|
+
if (event.key === "ArrowDown") target = visibleItems[Math.min(index + 1, visibleItems.length - 1)];
|
|
1270
|
+
if (event.key === "ArrowUp") target = visibleItems[Math.max(index - 1, 0)];
|
|
1271
|
+
if (event.key === "Home") target = visibleItems[0];
|
|
1272
|
+
if (event.key === "End") target = visibleItems[visibleItems.length - 1];
|
|
1273
|
+
if (event.key === "ArrowRight" && item.hasChildren) {
|
|
1274
|
+
if (!expandedIds.has(item.id)) toggleExpanded(item.id);
|
|
1275
|
+
else target = visibleItems[index + 1];
|
|
1276
|
+
}
|
|
1277
|
+
if (event.key === "ArrowLeft") {
|
|
1278
|
+
if (item.hasChildren && expandedIds.has(item.id)) toggleExpanded(item.id);
|
|
1279
|
+
else target = itemById.get(item.parentId);
|
|
1280
|
+
}
|
|
1281
|
+
if (event.key === "F2" && resolvedLevelConfig(item.depth).editable) startEdit(item.id);
|
|
1282
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
1283
|
+
handleRowSelection(event, item.id);
|
|
1284
|
+
activateItem(event, item.id, "keyboard");
|
|
1285
|
+
}
|
|
1286
|
+
if (["ArrowDown", "ArrowUp", "ArrowRight", "ArrowLeft", "Home", "End", "F2", " ", "Enter"].includes(event.key)) event.preventDefault();
|
|
1287
|
+
if (target) getRowElement(target.id)?.focus();
|
|
1288
|
+
}
|
|
1289
|
+
});
|
|
1290
|
+
return /* @__PURE__ */ jsx3(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsx3("div", { ...rootProps, "data-data-component": "tree-select", style: { position: "relative", padding: 8, ...rootProps.style }, children: allowDragAndDrop ? /* @__PURE__ */ jsxs2(
|
|
1126
1291
|
DndContext,
|
|
1127
1292
|
{
|
|
1128
1293
|
sensors,
|
|
@@ -1133,24 +1298,24 @@ function TreeSelect({
|
|
|
1133
1298
|
onDragEnd: handleDragEnd,
|
|
1134
1299
|
onDragCancel: handleDragCancel,
|
|
1135
1300
|
children: [
|
|
1136
|
-
/* @__PURE__ */ jsx3(SortableContext, { items: visibleItems.map((item) => item.id), children: /* @__PURE__ */ jsxs2("div", { ref: listRef, style: { position: "relative" }, children: [
|
|
1301
|
+
/* @__PURE__ */ jsx3(SortableContext, { items: visibleItems.map((item) => item.id), children: /* @__PURE__ */ jsxs2("div", { ref: listRef, role: "tree", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-multiselectable": allowMultiDrag || void 0, style: { position: "relative" }, children: [
|
|
1137
1302
|
indicator ? /* @__PURE__ */ jsx3(
|
|
1138
1303
|
"div",
|
|
1139
1304
|
{
|
|
1305
|
+
className: "bg-bg-primary-base",
|
|
1140
1306
|
style: {
|
|
1141
1307
|
position: "absolute",
|
|
1142
1308
|
top: indicator.top,
|
|
1143
1309
|
left: indicator.left,
|
|
1144
1310
|
right: 8,
|
|
1145
1311
|
height: 2,
|
|
1146
|
-
background: "#0f62fe",
|
|
1147
1312
|
borderRadius: 999
|
|
1148
1313
|
}
|
|
1149
1314
|
}
|
|
1150
1315
|
) : null,
|
|
1151
1316
|
visibleItems.map((item) => {
|
|
1152
1317
|
const levelSettings = resolvedLevelConfig(item.depth);
|
|
1153
|
-
const additionalActionsMenu = renderAdditionalActionsMenu
|
|
1318
|
+
const additionalActionsMenu = renderAdditionalActionsMenu?.({
|
|
1154
1319
|
id: item.id,
|
|
1155
1320
|
node: item.node,
|
|
1156
1321
|
depth: item.depth,
|
|
@@ -1165,6 +1330,12 @@ function TreeSelect({
|
|
|
1165
1330
|
onToggle: toggleExpanded,
|
|
1166
1331
|
onRowClick: handleRowClick,
|
|
1167
1332
|
rowRefs,
|
|
1333
|
+
onRowSelect: handleRowSelection,
|
|
1334
|
+
rowAccessibility: rowAccessibility(item),
|
|
1335
|
+
labelContext: rowContext(item),
|
|
1336
|
+
accessibleLabel: getTreeItemLabel(item.node, getItemLabel),
|
|
1337
|
+
rowClassName: typeof rowClassName === "function" ? rowClassName(rowContext(item)) : rowClassName,
|
|
1338
|
+
labelClassName: typeof labelClassName === "function" ? labelClassName(rowContext(item)) : labelClassName,
|
|
1168
1339
|
isActive: activeId === item.id,
|
|
1169
1340
|
isSelected: selectedIds.has(item.id),
|
|
1170
1341
|
isGroupDragging: Boolean(
|
|
@@ -1189,7 +1360,8 @@ function TreeSelect({
|
|
|
1189
1360
|
canDelete: levelSettings.actions.delete,
|
|
1190
1361
|
onDelete: handleDelete,
|
|
1191
1362
|
deleteTooltip: levelSettings.tooltips.delete,
|
|
1192
|
-
additionalActionsMenu
|
|
1363
|
+
additionalActionsMenu,
|
|
1364
|
+
renderLabel
|
|
1193
1365
|
},
|
|
1194
1366
|
item.id
|
|
1195
1367
|
);
|
|
@@ -1198,20 +1370,27 @@ function TreeSelect({
|
|
|
1198
1370
|
/* @__PURE__ */ jsx3(DragOverlay, { children: activeId ? /* @__PURE__ */ jsx3(
|
|
1199
1371
|
"div",
|
|
1200
1372
|
{
|
|
1201
|
-
className: "border border-border-subtle bg-bg-base text-fg-base",
|
|
1373
|
+
className: "border border-border-subtle bg-bg-base text-fg-base shadow-lg",
|
|
1202
1374
|
style: {
|
|
1203
1375
|
padding: "6px 8px",
|
|
1204
|
-
borderRadius: 6
|
|
1205
|
-
boxShadow: "0 8px 24px rgba(0, 0, 0, 0.12)"
|
|
1376
|
+
borderRadius: 6
|
|
1206
1377
|
},
|
|
1207
|
-
children: draggedRootIds.length > 1 ? `${draggedRootIds.length} items` :
|
|
1378
|
+
children: draggedRootIds.length > 1 ? `${draggedRootIds.length} items` : /* @__PURE__ */ jsx3(
|
|
1379
|
+
"span",
|
|
1380
|
+
{
|
|
1381
|
+
className: "inline-flex items-center gap-6",
|
|
1382
|
+
children: renderLabelContent(
|
|
1383
|
+
visibleItems.find((item) => item.id === activeId)
|
|
1384
|
+
) ?? activeId
|
|
1385
|
+
}
|
|
1386
|
+
)
|
|
1208
1387
|
}
|
|
1209
1388
|
) : null })
|
|
1210
1389
|
]
|
|
1211
1390
|
}
|
|
1212
|
-
) : /* @__PURE__ */ jsx3("div", { ref: listRef, style: { position: "relative" }, children: visibleItems.map((item) => {
|
|
1391
|
+
) : /* @__PURE__ */ jsx3("div", { ref: listRef, role: "tree", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-multiselectable": allowMultiDrag || void 0, style: { position: "relative" }, children: visibleItems.map((item) => {
|
|
1213
1392
|
const levelSettings = resolvedLevelConfig(item.depth);
|
|
1214
|
-
const additionalActionsMenu = renderAdditionalActionsMenu
|
|
1393
|
+
const additionalActionsMenu = renderAdditionalActionsMenu?.({
|
|
1215
1394
|
id: item.id,
|
|
1216
1395
|
node: item.node,
|
|
1217
1396
|
depth: item.depth,
|
|
@@ -1226,7 +1405,15 @@ function TreeSelect({
|
|
|
1226
1405
|
isExpanded: expandedIds.has(item.id),
|
|
1227
1406
|
onToggle: toggleExpanded,
|
|
1228
1407
|
onRowClick: handleRowClick,
|
|
1229
|
-
rowRef:
|
|
1408
|
+
rowRef: (row) => {
|
|
1409
|
+
if (row) rowRefs.current.set(item.id, row);
|
|
1410
|
+
else rowRefs.current.delete(item.id);
|
|
1411
|
+
},
|
|
1412
|
+
rowAccessibility: rowAccessibility(item),
|
|
1413
|
+
labelContext: rowContext(item),
|
|
1414
|
+
accessibleLabel: getTreeItemLabel(item.node, getItemLabel),
|
|
1415
|
+
rowClassName: typeof rowClassName === "function" ? rowClassName(rowContext(item)) : rowClassName,
|
|
1416
|
+
labelClassName: typeof labelClassName === "function" ? labelClassName(rowContext(item)) : labelClassName,
|
|
1230
1417
|
isDraggable: false,
|
|
1231
1418
|
isDragging: false,
|
|
1232
1419
|
isSelected: selectedIds.has(item.id),
|
|
@@ -1249,12 +1436,16 @@ function TreeSelect({
|
|
|
1249
1436
|
canDelete: levelSettings.actions.delete,
|
|
1250
1437
|
onDelete: handleDelete,
|
|
1251
1438
|
deleteTooltip: levelSettings.tooltips.delete,
|
|
1252
|
-
additionalActionsMenu
|
|
1439
|
+
additionalActionsMenu,
|
|
1440
|
+
renderLabel
|
|
1253
1441
|
},
|
|
1254
1442
|
item.id
|
|
1255
1443
|
);
|
|
1256
1444
|
}) }) }) });
|
|
1257
|
-
}
|
|
1445
|
+
});
|
|
1446
|
+
|
|
1447
|
+
// src/index.ts
|
|
1448
|
+
var TreeSelect3 = TreeSelect;
|
|
1258
1449
|
export {
|
|
1259
|
-
TreeSelect
|
|
1450
|
+
TreeSelect3 as TreeSelect
|
|
1260
1451
|
};
|