@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.js
CHANGED
|
@@ -26,10 +26,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
));
|
|
27
27
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
28
|
|
|
29
|
-
// src/index.
|
|
29
|
+
// src/index.ts
|
|
30
30
|
var index_exports = {};
|
|
31
31
|
__export(index_exports, {
|
|
32
|
-
TreeSelect: () =>
|
|
32
|
+
TreeSelect: () => TreeSelect3
|
|
33
33
|
});
|
|
34
34
|
module.exports = __toCommonJS(index_exports);
|
|
35
35
|
|
|
@@ -47,6 +47,21 @@ var import_icons = require("@edux-design/icons");
|
|
|
47
47
|
var import_menus = require("@edux-design/menus");
|
|
48
48
|
var import_tooltips = require("@edux-design/tooltips");
|
|
49
49
|
|
|
50
|
+
// src/elements/treeSelectInteraction.js
|
|
51
|
+
function isTreeRowControl(event) {
|
|
52
|
+
const control = event.target?.closest?.(
|
|
53
|
+
'button, input, textarea, select, a[href], [contenteditable="true"], [role="button"], [role="checkbox"], [role="menuitem"], [tabindex]:not([tabindex="-1"]), [data-tree-interactive]'
|
|
54
|
+
);
|
|
55
|
+
return Boolean(control && control !== event.currentTarget && event.currentTarget.contains(control));
|
|
56
|
+
}
|
|
57
|
+
function hasActivationModifier(event) {
|
|
58
|
+
return event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
|
|
59
|
+
}
|
|
60
|
+
function getTreeItemLabel(node, getItemLabel) {
|
|
61
|
+
if (getItemLabel) return getItemLabel(node);
|
|
62
|
+
return typeof node.label === "string" || typeof node.label === "number" ? String(node.label) : String(node.id);
|
|
63
|
+
}
|
|
64
|
+
|
|
50
65
|
// src/elements/treeSelectConstants.js
|
|
51
66
|
var HANDLE_WIDTH = 28;
|
|
52
67
|
var TOGGLE_WIDTH = 28;
|
|
@@ -55,6 +70,8 @@ var INDICATOR_INSET = HANDLE_WIDTH + TOGGLE_WIDTH + 12;
|
|
|
55
70
|
|
|
56
71
|
// src/elements/TreeRow.jsx
|
|
57
72
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
73
|
+
var DRAG_ROW_BACKGROUND = "color-mix(in srgb, var(--color-fg-base) 4%, transparent)";
|
|
74
|
+
var SELECTED_ROW_BACKGROUND = "color-mix(in srgb, var(--color-primary-600) 8%, transparent)";
|
|
58
75
|
var TreeRow = ({
|
|
59
76
|
item,
|
|
60
77
|
depth,
|
|
@@ -66,6 +83,11 @@ var TreeRow = ({
|
|
|
66
83
|
dragAttributes,
|
|
67
84
|
dragRef,
|
|
68
85
|
rowRef,
|
|
86
|
+
rowAccessibility,
|
|
87
|
+
labelContext,
|
|
88
|
+
accessibleLabel,
|
|
89
|
+
rowClassName,
|
|
90
|
+
labelClassName,
|
|
69
91
|
isDragging,
|
|
70
92
|
isSelected,
|
|
71
93
|
onRowClick,
|
|
@@ -87,13 +109,23 @@ var TreeRow = ({
|
|
|
87
109
|
canDelete,
|
|
88
110
|
onDelete,
|
|
89
111
|
deleteTooltip,
|
|
90
|
-
additionalActionsMenu
|
|
112
|
+
additionalActionsMenu,
|
|
113
|
+
renderLabel
|
|
91
114
|
}) => {
|
|
92
115
|
const toggleLabel = isExpanded ? "Collapse item" : "Expand item";
|
|
93
116
|
const label = item.node.label ?? item.node.id;
|
|
117
|
+
const labelContent = renderLabel ? renderLabel({
|
|
118
|
+
id: item.id,
|
|
119
|
+
node: item.node,
|
|
120
|
+
depth,
|
|
121
|
+
parentId: item.parentId,
|
|
122
|
+
hasChildren: item.hasChildren,
|
|
123
|
+
isExpanded,
|
|
124
|
+
...labelContext
|
|
125
|
+
}) : label;
|
|
94
126
|
const showToggle = item.hasChildren;
|
|
95
127
|
const [additionalMenuOpen, setAdditionalMenuOpen] = import_react.default.useState(false);
|
|
96
|
-
const rowBackground = isDragging ?
|
|
128
|
+
const rowBackground = isDragging ? DRAG_ROW_BACKGROUND : isSelected ? SELECTED_ROW_BACKGROUND : void 0;
|
|
97
129
|
const rowPaddingLeft = depth * INDENT_WIDTH + 8;
|
|
98
130
|
const hasAdditionalActionsMenu = Boolean(additionalActionsMenu);
|
|
99
131
|
const showActions = canAddChild || canAddSibling || canDelete || hasAdditionalActionsMenu;
|
|
@@ -114,9 +146,12 @@ var TreeRow = ({
|
|
|
114
146
|
"div",
|
|
115
147
|
{
|
|
116
148
|
ref: rowRef,
|
|
117
|
-
|
|
149
|
+
...rowAccessibility,
|
|
150
|
+
onClick: (event) => onRowClick?.(event, item.id),
|
|
118
151
|
"data-tree-row": item.id,
|
|
119
|
-
|
|
152
|
+
"data-selected": isSelected || void 0,
|
|
153
|
+
"data-current": labelContext?.isCurrent || void 0,
|
|
154
|
+
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(" "),
|
|
120
155
|
style: {
|
|
121
156
|
display: "flex",
|
|
122
157
|
alignItems: "center",
|
|
@@ -135,7 +170,7 @@ var TreeRow = ({
|
|
|
135
170
|
...dragAttributes,
|
|
136
171
|
...dragHandleProps,
|
|
137
172
|
ref: dragRef,
|
|
138
|
-
"aria-label":
|
|
173
|
+
"aria-label": `Move ${accessibleLabel}`,
|
|
139
174
|
onClick: (event) => event.stopPropagation(),
|
|
140
175
|
className: "border border-border-subtle bg-bg-base text-fg-base",
|
|
141
176
|
style: {
|
|
@@ -155,7 +190,8 @@ var TreeRow = ({
|
|
|
155
190
|
"button",
|
|
156
191
|
{
|
|
157
192
|
type: "button",
|
|
158
|
-
"aria-label": toggleLabel
|
|
193
|
+
"aria-label": `${toggleLabel}: ${accessibleLabel}`,
|
|
194
|
+
"aria-expanded": isExpanded,
|
|
159
195
|
onClick: () => onToggle(item.id),
|
|
160
196
|
style: {
|
|
161
197
|
width: 24,
|
|
@@ -177,6 +213,8 @@ var TreeRow = ({
|
|
|
177
213
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
178
214
|
import_forms.Checkbox,
|
|
179
215
|
{
|
|
216
|
+
"aria-label": `${toggleLabel}: ${accessibleLabel}`,
|
|
217
|
+
"aria-expanded": isExpanded,
|
|
180
218
|
checked: false,
|
|
181
219
|
plus: !isExpanded,
|
|
182
220
|
indeterminate: isExpanded,
|
|
@@ -188,19 +226,21 @@ var TreeRow = ({
|
|
|
188
226
|
"input",
|
|
189
227
|
{
|
|
190
228
|
type: "text",
|
|
229
|
+
"aria-label": `Edit ${accessibleLabel}`,
|
|
230
|
+
onClick: (event) => event.stopPropagation(),
|
|
191
231
|
value: draftLabel,
|
|
192
232
|
autoFocus: true,
|
|
193
233
|
"data-tree-input": item.id,
|
|
194
234
|
onChange: (event) => onDraftChange(event.target.value),
|
|
195
235
|
onBlur: onCommitEdit,
|
|
196
236
|
onKeyDown: (event) => {
|
|
237
|
+
event.stopPropagation();
|
|
197
238
|
if (event.key === "Enter") onCommitEdit();
|
|
198
239
|
if (event.key === "Escape") onCancelEdit();
|
|
199
240
|
},
|
|
200
241
|
style: {
|
|
201
242
|
fontSize: 14,
|
|
202
243
|
border: "none",
|
|
203
|
-
outline: "none",
|
|
204
244
|
background: "transparent",
|
|
205
245
|
padding: 0
|
|
206
246
|
}
|
|
@@ -209,17 +249,32 @@ var TreeRow = ({
|
|
|
209
249
|
"span",
|
|
210
250
|
{
|
|
211
251
|
onClick: (event) => {
|
|
212
|
-
if (!isEditable) return;
|
|
252
|
+
if (!isEditable || event.defaultPrevented || isTreeRowControl(event)) return;
|
|
213
253
|
event.stopPropagation();
|
|
214
|
-
onStartEdit
|
|
254
|
+
onStartEdit?.();
|
|
215
255
|
},
|
|
256
|
+
role: isEditable ? "button" : void 0,
|
|
257
|
+
tabIndex: isEditable ? 0 : void 0,
|
|
258
|
+
"aria-label": isEditable ? `Edit ${accessibleLabel}` : void 0,
|
|
259
|
+
onKeyDown: isEditable ? (event) => {
|
|
260
|
+
if (event.target !== event.currentTarget) return;
|
|
261
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
262
|
+
event.preventDefault();
|
|
263
|
+
event.stopPropagation();
|
|
264
|
+
onStartEdit?.();
|
|
265
|
+
}
|
|
266
|
+
} : void 0,
|
|
216
267
|
"data-tree-label": item.id,
|
|
268
|
+
className: [renderLabel ? "inline-flex items-center gap-6" : "", labelClassName].filter(Boolean).join(" "),
|
|
217
269
|
style: {
|
|
218
270
|
fontSize: 14,
|
|
271
|
+
display: "inline-flex",
|
|
272
|
+
alignItems: "center",
|
|
273
|
+
minHeight: isEditable ? 24 : void 0,
|
|
219
274
|
cursor: isEditable ? "pointer" : "default",
|
|
220
|
-
|
|
275
|
+
...!renderLabel ? labelStyle : {}
|
|
221
276
|
},
|
|
222
|
-
children:
|
|
277
|
+
children: labelContent
|
|
223
278
|
}
|
|
224
279
|
),
|
|
225
280
|
showActions ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
@@ -237,7 +292,7 @@ var TreeRow = ({
|
|
|
237
292
|
isBare: true,
|
|
238
293
|
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.Plus, { "aria-hidden": "true" }),
|
|
239
294
|
"aria-label": "Add child",
|
|
240
|
-
onClick: () => onAddChild
|
|
295
|
+
onClick: () => onAddChild?.(item.id)
|
|
241
296
|
}
|
|
242
297
|
) }),
|
|
243
298
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tooltips.TooltipContent, { children: addChildTooltip ?? "Add child" })
|
|
@@ -250,7 +305,7 @@ var TreeRow = ({
|
|
|
250
305
|
isBare: true,
|
|
251
306
|
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.List, { "aria-hidden": "true" }),
|
|
252
307
|
"aria-label": "Add sibling",
|
|
253
|
-
onClick: () => onAddSibling
|
|
308
|
+
onClick: () => onAddSibling?.(item.id)
|
|
254
309
|
}
|
|
255
310
|
) }),
|
|
256
311
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tooltips.TooltipContent, { children: addSiblingTooltip ?? "Add sibling" })
|
|
@@ -263,7 +318,7 @@ var TreeRow = ({
|
|
|
263
318
|
isBare: true,
|
|
264
319
|
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.Close, { "aria-hidden": "true" }),
|
|
265
320
|
"aria-label": "Delete item",
|
|
266
|
-
onClick: () => onDelete
|
|
321
|
+
onClick: () => onDelete?.(item.id)
|
|
267
322
|
}
|
|
268
323
|
) }),
|
|
269
324
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tooltips.TooltipContent, { children: deleteTooltip ?? "Delete item" })
|
|
@@ -304,6 +359,12 @@ var TreeSortableRow = ({
|
|
|
304
359
|
onToggle,
|
|
305
360
|
onRowClick,
|
|
306
361
|
rowRefs,
|
|
362
|
+
rowAccessibility,
|
|
363
|
+
labelContext,
|
|
364
|
+
accessibleLabel,
|
|
365
|
+
rowClassName,
|
|
366
|
+
labelClassName,
|
|
367
|
+
onRowSelect,
|
|
307
368
|
isActive,
|
|
308
369
|
isSelected,
|
|
309
370
|
isGroupDragging,
|
|
@@ -326,7 +387,8 @@ var TreeSortableRow = ({
|
|
|
326
387
|
canDelete,
|
|
327
388
|
onDelete,
|
|
328
389
|
deleteTooltip,
|
|
329
|
-
additionalActionsMenu
|
|
390
|
+
additionalActionsMenu,
|
|
391
|
+
renderLabel
|
|
330
392
|
}) => {
|
|
331
393
|
const {
|
|
332
394
|
attributes,
|
|
@@ -350,12 +412,11 @@ var TreeSortableRow = ({
|
|
|
350
412
|
() => ({
|
|
351
413
|
...listeners,
|
|
352
414
|
onPointerDown: (event) => {
|
|
353
|
-
|
|
354
|
-
(
|
|
355
|
-
onRowClick == null ? void 0 : onRowClick(event, item.id);
|
|
415
|
+
listeners?.onPointerDown?.(event);
|
|
416
|
+
onRowSelect?.(event, item.id);
|
|
356
417
|
}
|
|
357
418
|
}),
|
|
358
|
-
[item.id, listeners,
|
|
419
|
+
[item.id, listeners, onRowSelect]
|
|
359
420
|
);
|
|
360
421
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
361
422
|
TreeRow,
|
|
@@ -369,6 +430,11 @@ var TreeSortableRow = ({
|
|
|
369
430
|
dragAttributes: attributes,
|
|
370
431
|
dragRef: setActivatorNodeRef,
|
|
371
432
|
rowRef: rowRefCallback,
|
|
433
|
+
rowAccessibility,
|
|
434
|
+
labelContext,
|
|
435
|
+
accessibleLabel,
|
|
436
|
+
rowClassName,
|
|
437
|
+
labelClassName,
|
|
372
438
|
isDragging: isDragging || isActive || isGroupDragging,
|
|
373
439
|
isSelected,
|
|
374
440
|
isEditable,
|
|
@@ -390,7 +456,8 @@ var TreeSortableRow = ({
|
|
|
390
456
|
canDelete,
|
|
391
457
|
onDelete,
|
|
392
458
|
deleteTooltip,
|
|
393
|
-
additionalActionsMenu
|
|
459
|
+
additionalActionsMenu,
|
|
460
|
+
renderLabel
|
|
394
461
|
}
|
|
395
462
|
);
|
|
396
463
|
};
|
|
@@ -442,11 +509,10 @@ var collectDraggedIds = (items, rootIds) => {
|
|
|
442
509
|
var getSelectedRootIds = (items, selectedIds) => {
|
|
443
510
|
const itemById = new Map(items.map((item) => [item.id, item]));
|
|
444
511
|
return items.filter((item) => selectedIds.has(item.id)).filter((item) => {
|
|
445
|
-
|
|
446
|
-
let parentId = (_a = itemById.get(item.id)) == null ? void 0 : _a.parentId;
|
|
512
|
+
let parentId = itemById.get(item.id)?.parentId;
|
|
447
513
|
while (parentId) {
|
|
448
514
|
if (selectedIds.has(parentId)) return false;
|
|
449
|
-
parentId =
|
|
515
|
+
parentId = itemById.get(parentId)?.parentId;
|
|
450
516
|
}
|
|
451
517
|
return true;
|
|
452
518
|
}).map((item) => item.id);
|
|
@@ -746,7 +812,7 @@ var getDropIndicator = ({
|
|
|
746
812
|
|
|
747
813
|
// src/elements/TreeSelect.jsx
|
|
748
814
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
749
|
-
|
|
815
|
+
var TreeSelect = (0, import_react3.forwardRef)(function TreeSelect2({
|
|
750
816
|
data,
|
|
751
817
|
onDataChange,
|
|
752
818
|
allowDragAndDrop = false,
|
|
@@ -765,9 +831,17 @@ function TreeSelect({
|
|
|
765
831
|
defaultExpanded,
|
|
766
832
|
expanded,
|
|
767
833
|
onExpandedChange,
|
|
768
|
-
renderAdditionalActionsMenu
|
|
769
|
-
|
|
770
|
-
|
|
834
|
+
renderAdditionalActionsMenu,
|
|
835
|
+
renderLabel,
|
|
836
|
+
onNodeActivate,
|
|
837
|
+
currentId,
|
|
838
|
+
getItemLabel,
|
|
839
|
+
rowClassName,
|
|
840
|
+
labelClassName,
|
|
841
|
+
"aria-label": ariaLabel = "Tree",
|
|
842
|
+
"aria-labelledby": ariaLabelledBy,
|
|
843
|
+
...rootProps
|
|
844
|
+
}, forwardedRef) {
|
|
771
845
|
const [uncontrolledExpandedIds, setExpandedIds] = (0, import_react3.useState)(
|
|
772
846
|
() => getExpandedIds(data ?? [], defaultExpanded)
|
|
773
847
|
);
|
|
@@ -777,6 +851,8 @@ function TreeSelect({
|
|
|
777
851
|
const [horizontalOffset, setHorizontalOffset] = (0, import_react3.useState)(0);
|
|
778
852
|
const [selectedIds, setSelectedIds] = (0, import_react3.useState)(() => /* @__PURE__ */ new Set());
|
|
779
853
|
const [lastSelectedId, setLastSelectedId] = (0, import_react3.useState)(null);
|
|
854
|
+
const [focusedId, setFocusedId] = (0, import_react3.useState)(null);
|
|
855
|
+
const [focusWithinId, setFocusWithinId] = (0, import_react3.useState)(null);
|
|
780
856
|
const [editingId, setEditingId] = (0, import_react3.useState)(null);
|
|
781
857
|
const [draftLabel, setDraftLabel] = (0, import_react3.useState)("");
|
|
782
858
|
const idCounterRef = (0, import_react3.useRef)(0);
|
|
@@ -796,6 +872,22 @@ function TreeSelect({
|
|
|
796
872
|
() => new Map(visibleItems.map((item) => [item.id, item])),
|
|
797
873
|
[visibleItems]
|
|
798
874
|
);
|
|
875
|
+
const getRowElement = (0, import_react3.useCallback)((id) => rowRefs.current.get(normaliseId(id)) ?? null, []);
|
|
876
|
+
(0, import_react3.useImperativeHandle)(forwardedRef, () => ({
|
|
877
|
+
getRowElement,
|
|
878
|
+
focusItem(id, options) {
|
|
879
|
+
const row = getRowElement(id);
|
|
880
|
+
if (!row) return false;
|
|
881
|
+
row.focus(options);
|
|
882
|
+
return true;
|
|
883
|
+
},
|
|
884
|
+
scrollToItem(id, options = { block: "nearest", inline: "nearest" }) {
|
|
885
|
+
const row = getRowElement(id);
|
|
886
|
+
if (!row || typeof row.scrollIntoView !== "function") return false;
|
|
887
|
+
row.scrollIntoView(options);
|
|
888
|
+
return true;
|
|
889
|
+
}
|
|
890
|
+
}), [getRowElement]);
|
|
799
891
|
const siblingOrderMap = (0, import_react3.useMemo)(
|
|
800
892
|
() => buildSiblingOrderMap(treeData),
|
|
801
893
|
[treeData]
|
|
@@ -806,19 +898,18 @@ function TreeSelect({
|
|
|
806
898
|
}, [maxDepth]);
|
|
807
899
|
const resolvedLevelConfig = (0, import_react3.useCallback)(
|
|
808
900
|
(depth) => {
|
|
809
|
-
|
|
810
|
-
const
|
|
811
|
-
const perLevel = ((_a2 = levelConfig == null ? void 0 : levelConfig.levels) == null ? void 0 : _a2[depth]) ?? {};
|
|
901
|
+
const defaults = levelConfig?.default ?? {};
|
|
902
|
+
const perLevel = levelConfig?.levels?.[depth] ?? {};
|
|
812
903
|
const editable = perLevel.editable ?? defaults.editable ?? isEditable;
|
|
813
904
|
const actions = {
|
|
814
|
-
addChild:
|
|
815
|
-
addSibling:
|
|
816
|
-
delete:
|
|
905
|
+
addChild: perLevel.actions?.addChild ?? defaults.actions?.addChild ?? editable,
|
|
906
|
+
addSibling: perLevel.actions?.addSibling ?? defaults.actions?.addSibling ?? editable,
|
|
907
|
+
delete: perLevel.actions?.delete ?? defaults.actions?.delete ?? (editable && allowDelete)
|
|
817
908
|
};
|
|
818
909
|
const tooltips = {
|
|
819
|
-
addChild:
|
|
820
|
-
addSibling:
|
|
821
|
-
delete:
|
|
910
|
+
addChild: perLevel.tooltips?.addChild ?? defaults.tooltips?.addChild ?? addChildTooltip,
|
|
911
|
+
addSibling: perLevel.tooltips?.addSibling ?? defaults.tooltips?.addSibling ?? addSiblingTooltip,
|
|
912
|
+
delete: perLevel.tooltips?.delete ?? defaults.tooltips?.delete ?? deleteTooltip
|
|
822
913
|
};
|
|
823
914
|
return { editable, actions, tooltips };
|
|
824
915
|
},
|
|
@@ -838,7 +929,7 @@ function TreeSelect({
|
|
|
838
929
|
(updater) => {
|
|
839
930
|
if (isExpandedControlled) {
|
|
840
931
|
const next = updater(new Set(expandedIds));
|
|
841
|
-
onExpandedChange
|
|
932
|
+
onExpandedChange?.(next);
|
|
842
933
|
return;
|
|
843
934
|
}
|
|
844
935
|
setExpandedIds((prev) => updater(new Set(prev)));
|
|
@@ -847,7 +938,7 @@ function TreeSelect({
|
|
|
847
938
|
);
|
|
848
939
|
const emitDataChange = (0, import_react3.useCallback)(
|
|
849
940
|
(nextTree, meta) => {
|
|
850
|
-
onDataChange
|
|
941
|
+
onDataChange?.(nextTree, meta);
|
|
851
942
|
},
|
|
852
943
|
[onDataChange]
|
|
853
944
|
);
|
|
@@ -861,17 +952,16 @@ function TreeSelect({
|
|
|
861
952
|
return prev;
|
|
862
953
|
});
|
|
863
954
|
}, [updateExpandedIds]);
|
|
864
|
-
const
|
|
955
|
+
const handleRowSelection = (0, import_react3.useCallback)(
|
|
865
956
|
(event, id) => {
|
|
866
|
-
var _a2, _b2;
|
|
867
957
|
if (!allowMultiDrag) return;
|
|
868
958
|
const hasMultiKey = event.metaKey || event.ctrlKey;
|
|
869
|
-
const parentId =
|
|
959
|
+
const parentId = itemById.get(id)?.parentId ?? null;
|
|
870
960
|
const siblingOrder = siblingOrderMap.get(parentId) ?? [];
|
|
871
961
|
if (event.shiftKey) {
|
|
872
962
|
const anchorIndex = siblingOrder.indexOf(lastSelectedId);
|
|
873
963
|
const targetIndex = siblingOrder.indexOf(id);
|
|
874
|
-
if (anchorIndex < 0 || targetIndex < 0 || (
|
|
964
|
+
if (anchorIndex < 0 || targetIndex < 0 || (itemById.get(lastSelectedId ?? "")?.parentId ?? null) !== parentId) {
|
|
875
965
|
setSelectedIds(/* @__PURE__ */ new Set([id]));
|
|
876
966
|
setLastSelectedId(id);
|
|
877
967
|
return;
|
|
@@ -893,10 +983,9 @@ function TreeSelect({
|
|
|
893
983
|
return;
|
|
894
984
|
}
|
|
895
985
|
setSelectedIds((prev) => {
|
|
896
|
-
var _a3;
|
|
897
986
|
if (prev.size) {
|
|
898
987
|
const firstSelected = prev.values().next().value;
|
|
899
|
-
const selectedParent =
|
|
988
|
+
const selectedParent = itemById.get(firstSelected)?.parentId ?? null;
|
|
900
989
|
if (selectedParent !== parentId) {
|
|
901
990
|
return /* @__PURE__ */ new Set([id]);
|
|
902
991
|
}
|
|
@@ -923,6 +1012,16 @@ function TreeSelect({
|
|
|
923
1012
|
siblingOrderMap
|
|
924
1013
|
]
|
|
925
1014
|
);
|
|
1015
|
+
const activateItem = (0, import_react3.useCallback)((event, id, inputSource) => {
|
|
1016
|
+
if (event.defaultPrevented || event.repeat || inputSource === "pointer" && event.button !== 0 || hasActivationModifier(event) || isTreeRowControl(event)) return;
|
|
1017
|
+
const item = itemById.get(id);
|
|
1018
|
+
if (item && !activeId) onNodeActivate?.({ ...item, event, inputSource });
|
|
1019
|
+
}, [activeId, itemById, onNodeActivate]);
|
|
1020
|
+
const handleRowClick = (0, import_react3.useCallback)((event, id) => {
|
|
1021
|
+
if (event.defaultPrevented || isTreeRowControl(event)) return;
|
|
1022
|
+
handleRowSelection(event, id);
|
|
1023
|
+
activateItem(event, id, "pointer");
|
|
1024
|
+
}, [activateItem, handleRowSelection]);
|
|
926
1025
|
const draggedRootIds = (0, import_react3.useMemo)(
|
|
927
1026
|
() => getDraggedRootIds({
|
|
928
1027
|
activeId,
|
|
@@ -954,13 +1053,11 @@ function TreeSelect({
|
|
|
954
1053
|
setHorizontalOffset(event.delta.x);
|
|
955
1054
|
}, []);
|
|
956
1055
|
const handleDragOver = (0, import_react3.useCallback)((event) => {
|
|
957
|
-
|
|
958
|
-
if ((_a2 = event.over) == null ? void 0 : _a2.id) {
|
|
1056
|
+
if (event.over?.id) {
|
|
959
1057
|
setOverId(normaliseId(event.over.id));
|
|
960
1058
|
}
|
|
961
1059
|
}, []);
|
|
962
1060
|
const handleDragEnd = (0, import_react3.useCallback)(() => {
|
|
963
|
-
var _a2;
|
|
964
1061
|
const projection = getDropProjection({
|
|
965
1062
|
items: visibleItems,
|
|
966
1063
|
activeId,
|
|
@@ -976,7 +1073,7 @@ function TreeSelect({
|
|
|
976
1073
|
return;
|
|
977
1074
|
}
|
|
978
1075
|
const { parentId, siblingIndex, projectedDepth } = projection;
|
|
979
|
-
const sourceParentId =
|
|
1076
|
+
const sourceParentId = itemById.get(draggedRootIds[0] ?? activeId)?.parentId ?? null;
|
|
980
1077
|
const { nextNodes, removedNodes } = removeNodes(treeData, draggedRootIds);
|
|
981
1078
|
if (!removedNodes.length) {
|
|
982
1079
|
setActiveId(null);
|
|
@@ -1089,8 +1186,7 @@ function TreeSelect({
|
|
|
1089
1186
|
);
|
|
1090
1187
|
const startEdit = (0, import_react3.useCallback)(
|
|
1091
1188
|
(id) => {
|
|
1092
|
-
|
|
1093
|
-
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) ?? "";
|
|
1189
|
+
const currentLabel = itemById.get(id)?.node?.label ?? itemById.get(id)?.node?.id ?? "";
|
|
1094
1190
|
setEditingId(id);
|
|
1095
1191
|
setDraftLabel(String(currentLabel));
|
|
1096
1192
|
},
|
|
@@ -1118,6 +1214,21 @@ function TreeSelect({
|
|
|
1118
1214
|
const cancelEdit = (0, import_react3.useCallback)(() => {
|
|
1119
1215
|
setEditingId(null);
|
|
1120
1216
|
}, []);
|
|
1217
|
+
const renderLabelContent = (0, import_react3.useCallback)(
|
|
1218
|
+
(item) => {
|
|
1219
|
+
if (!item) return null;
|
|
1220
|
+
if (!renderLabel) return item.node.label ?? item.id;
|
|
1221
|
+
return renderLabel({
|
|
1222
|
+
...item,
|
|
1223
|
+
isExpanded: expandedIds.has(item.id),
|
|
1224
|
+
isCurrent: currentId != null && normaliseId(currentId) === item.id,
|
|
1225
|
+
isSelected: selectedIds.has(item.id),
|
|
1226
|
+
isFocused: focusWithinId === item.id,
|
|
1227
|
+
getRowElement: () => null
|
|
1228
|
+
});
|
|
1229
|
+
},
|
|
1230
|
+
[currentId, expandedIds, focusWithinId, renderLabel, selectedIds]
|
|
1231
|
+
);
|
|
1121
1232
|
const indicator = (0, import_react3.useMemo)(() => {
|
|
1122
1233
|
if (!allowDragAndDrop) return null;
|
|
1123
1234
|
return getDropIndicator({
|
|
@@ -1139,7 +1250,61 @@ function TreeSelect({
|
|
|
1139
1250
|
visibleItems,
|
|
1140
1251
|
maxDepth
|
|
1141
1252
|
]);
|
|
1142
|
-
|
|
1253
|
+
const rowContext = (item) => ({
|
|
1254
|
+
...item,
|
|
1255
|
+
isExpanded: expandedIds.has(item.id),
|
|
1256
|
+
isCurrent: currentId != null && normaliseId(currentId) === item.id,
|
|
1257
|
+
isSelected: selectedIds.has(item.id),
|
|
1258
|
+
isFocused: focusWithinId === item.id,
|
|
1259
|
+
getRowElement: () => getRowElement(item.id)
|
|
1260
|
+
});
|
|
1261
|
+
const rowAccessibility = (item) => ({
|
|
1262
|
+
role: "treeitem",
|
|
1263
|
+
"aria-label": getTreeItemLabel(item.node, getItemLabel),
|
|
1264
|
+
"aria-level": item.depth + 1,
|
|
1265
|
+
"aria-expanded": item.hasChildren ? expandedIds.has(item.id) : void 0,
|
|
1266
|
+
"aria-selected": allowMultiDrag ? selectedIds.has(item.id) : void 0,
|
|
1267
|
+
"aria-current": currentId != null && normaliseId(currentId) === item.id ? "page" : void 0,
|
|
1268
|
+
"aria-posinset": (siblingOrderMap.get(item.parentId) || []).indexOf(item.id) + 1,
|
|
1269
|
+
"aria-setsize": (siblingOrderMap.get(item.parentId) || []).length,
|
|
1270
|
+
tabIndex: (focusedId && itemById.has(focusedId) ? focusedId : visibleItems[0]?.id) === item.id ? 0 : -1,
|
|
1271
|
+
onFocus: () => {
|
|
1272
|
+
setFocusedId(item.id);
|
|
1273
|
+
setFocusWithinId(item.id);
|
|
1274
|
+
},
|
|
1275
|
+
onBlur: (event) => {
|
|
1276
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setFocusWithinId(null);
|
|
1277
|
+
},
|
|
1278
|
+
onKeyDown: (event) => {
|
|
1279
|
+
if (event.target !== event.currentTarget || event.defaultPrevented) return;
|
|
1280
|
+
if (event.repeat && (event.key === "Enter" || event.key === " ")) {
|
|
1281
|
+
event.preventDefault();
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
const index = visibleItems.findIndex((row) => row.id === item.id);
|
|
1285
|
+
let target;
|
|
1286
|
+
if (event.key === "ArrowDown") target = visibleItems[Math.min(index + 1, visibleItems.length - 1)];
|
|
1287
|
+
if (event.key === "ArrowUp") target = visibleItems[Math.max(index - 1, 0)];
|
|
1288
|
+
if (event.key === "Home") target = visibleItems[0];
|
|
1289
|
+
if (event.key === "End") target = visibleItems[visibleItems.length - 1];
|
|
1290
|
+
if (event.key === "ArrowRight" && item.hasChildren) {
|
|
1291
|
+
if (!expandedIds.has(item.id)) toggleExpanded(item.id);
|
|
1292
|
+
else target = visibleItems[index + 1];
|
|
1293
|
+
}
|
|
1294
|
+
if (event.key === "ArrowLeft") {
|
|
1295
|
+
if (item.hasChildren && expandedIds.has(item.id)) toggleExpanded(item.id);
|
|
1296
|
+
else target = itemById.get(item.parentId);
|
|
1297
|
+
}
|
|
1298
|
+
if (event.key === "F2" && resolvedLevelConfig(item.depth).editable) startEdit(item.id);
|
|
1299
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
1300
|
+
handleRowSelection(event, item.id);
|
|
1301
|
+
activateItem(event, item.id, "keyboard");
|
|
1302
|
+
}
|
|
1303
|
+
if (["ArrowDown", "ArrowUp", "ArrowRight", "ArrowLeft", "Home", "End", "F2", " ", "Enter"].includes(event.key)) event.preventDefault();
|
|
1304
|
+
if (target) getRowElement(target.id)?.focus();
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_tooltips2.TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ...rootProps, "data-data-component": "tree-select", style: { position: "relative", padding: 8, ...rootProps.style }, children: allowDragAndDrop ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1143
1308
|
import_core.DndContext,
|
|
1144
1309
|
{
|
|
1145
1310
|
sensors,
|
|
@@ -1150,24 +1315,24 @@ function TreeSelect({
|
|
|
1150
1315
|
onDragEnd: handleDragEnd,
|
|
1151
1316
|
onDragCancel: handleDragCancel,
|
|
1152
1317
|
children: [
|
|
1153
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sortable2.SortableContext, { items: visibleItems.map((item) => item.id), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { ref: listRef, style: { position: "relative" }, children: [
|
|
1318
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sortable2.SortableContext, { items: visibleItems.map((item) => item.id), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { ref: listRef, role: "tree", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-multiselectable": allowMultiDrag || void 0, style: { position: "relative" }, children: [
|
|
1154
1319
|
indicator ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1155
1320
|
"div",
|
|
1156
1321
|
{
|
|
1322
|
+
className: "bg-bg-primary-base",
|
|
1157
1323
|
style: {
|
|
1158
1324
|
position: "absolute",
|
|
1159
1325
|
top: indicator.top,
|
|
1160
1326
|
left: indicator.left,
|
|
1161
1327
|
right: 8,
|
|
1162
1328
|
height: 2,
|
|
1163
|
-
background: "#0f62fe",
|
|
1164
1329
|
borderRadius: 999
|
|
1165
1330
|
}
|
|
1166
1331
|
}
|
|
1167
1332
|
) : null,
|
|
1168
1333
|
visibleItems.map((item) => {
|
|
1169
1334
|
const levelSettings = resolvedLevelConfig(item.depth);
|
|
1170
|
-
const additionalActionsMenu = renderAdditionalActionsMenu
|
|
1335
|
+
const additionalActionsMenu = renderAdditionalActionsMenu?.({
|
|
1171
1336
|
id: item.id,
|
|
1172
1337
|
node: item.node,
|
|
1173
1338
|
depth: item.depth,
|
|
@@ -1182,6 +1347,12 @@ function TreeSelect({
|
|
|
1182
1347
|
onToggle: toggleExpanded,
|
|
1183
1348
|
onRowClick: handleRowClick,
|
|
1184
1349
|
rowRefs,
|
|
1350
|
+
onRowSelect: handleRowSelection,
|
|
1351
|
+
rowAccessibility: rowAccessibility(item),
|
|
1352
|
+
labelContext: rowContext(item),
|
|
1353
|
+
accessibleLabel: getTreeItemLabel(item.node, getItemLabel),
|
|
1354
|
+
rowClassName: typeof rowClassName === "function" ? rowClassName(rowContext(item)) : rowClassName,
|
|
1355
|
+
labelClassName: typeof labelClassName === "function" ? labelClassName(rowContext(item)) : labelClassName,
|
|
1185
1356
|
isActive: activeId === item.id,
|
|
1186
1357
|
isSelected: selectedIds.has(item.id),
|
|
1187
1358
|
isGroupDragging: Boolean(
|
|
@@ -1206,7 +1377,8 @@ function TreeSelect({
|
|
|
1206
1377
|
canDelete: levelSettings.actions.delete,
|
|
1207
1378
|
onDelete: handleDelete,
|
|
1208
1379
|
deleteTooltip: levelSettings.tooltips.delete,
|
|
1209
|
-
additionalActionsMenu
|
|
1380
|
+
additionalActionsMenu,
|
|
1381
|
+
renderLabel
|
|
1210
1382
|
},
|
|
1211
1383
|
item.id
|
|
1212
1384
|
);
|
|
@@ -1215,20 +1387,27 @@ function TreeSelect({
|
|
|
1215
1387
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_core.DragOverlay, { children: activeId ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1216
1388
|
"div",
|
|
1217
1389
|
{
|
|
1218
|
-
className: "border border-border-subtle bg-bg-base text-fg-base",
|
|
1390
|
+
className: "border border-border-subtle bg-bg-base text-fg-base shadow-lg",
|
|
1219
1391
|
style: {
|
|
1220
1392
|
padding: "6px 8px",
|
|
1221
|
-
borderRadius: 6
|
|
1222
|
-
boxShadow: "0 8px 24px rgba(0, 0, 0, 0.12)"
|
|
1393
|
+
borderRadius: 6
|
|
1223
1394
|
},
|
|
1224
|
-
children: draggedRootIds.length > 1 ? `${draggedRootIds.length} items` :
|
|
1395
|
+
children: draggedRootIds.length > 1 ? `${draggedRootIds.length} items` : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1396
|
+
"span",
|
|
1397
|
+
{
|
|
1398
|
+
className: "inline-flex items-center gap-6",
|
|
1399
|
+
children: renderLabelContent(
|
|
1400
|
+
visibleItems.find((item) => item.id === activeId)
|
|
1401
|
+
) ?? activeId
|
|
1402
|
+
}
|
|
1403
|
+
)
|
|
1225
1404
|
}
|
|
1226
1405
|
) : null })
|
|
1227
1406
|
]
|
|
1228
1407
|
}
|
|
1229
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: listRef, style: { position: "relative" }, children: visibleItems.map((item) => {
|
|
1408
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: listRef, role: "tree", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-multiselectable": allowMultiDrag || void 0, style: { position: "relative" }, children: visibleItems.map((item) => {
|
|
1230
1409
|
const levelSettings = resolvedLevelConfig(item.depth);
|
|
1231
|
-
const additionalActionsMenu = renderAdditionalActionsMenu
|
|
1410
|
+
const additionalActionsMenu = renderAdditionalActionsMenu?.({
|
|
1232
1411
|
id: item.id,
|
|
1233
1412
|
node: item.node,
|
|
1234
1413
|
depth: item.depth,
|
|
@@ -1243,7 +1422,15 @@ function TreeSelect({
|
|
|
1243
1422
|
isExpanded: expandedIds.has(item.id),
|
|
1244
1423
|
onToggle: toggleExpanded,
|
|
1245
1424
|
onRowClick: handleRowClick,
|
|
1246
|
-
rowRef:
|
|
1425
|
+
rowRef: (row) => {
|
|
1426
|
+
if (row) rowRefs.current.set(item.id, row);
|
|
1427
|
+
else rowRefs.current.delete(item.id);
|
|
1428
|
+
},
|
|
1429
|
+
rowAccessibility: rowAccessibility(item),
|
|
1430
|
+
labelContext: rowContext(item),
|
|
1431
|
+
accessibleLabel: getTreeItemLabel(item.node, getItemLabel),
|
|
1432
|
+
rowClassName: typeof rowClassName === "function" ? rowClassName(rowContext(item)) : rowClassName,
|
|
1433
|
+
labelClassName: typeof labelClassName === "function" ? labelClassName(rowContext(item)) : labelClassName,
|
|
1247
1434
|
isDraggable: false,
|
|
1248
1435
|
isDragging: false,
|
|
1249
1436
|
isSelected: selectedIds.has(item.id),
|
|
@@ -1266,12 +1453,16 @@ function TreeSelect({
|
|
|
1266
1453
|
canDelete: levelSettings.actions.delete,
|
|
1267
1454
|
onDelete: handleDelete,
|
|
1268
1455
|
deleteTooltip: levelSettings.tooltips.delete,
|
|
1269
|
-
additionalActionsMenu
|
|
1456
|
+
additionalActionsMenu,
|
|
1457
|
+
renderLabel
|
|
1270
1458
|
},
|
|
1271
1459
|
item.id
|
|
1272
1460
|
);
|
|
1273
1461
|
}) }) }) });
|
|
1274
|
-
}
|
|
1462
|
+
});
|
|
1463
|
+
|
|
1464
|
+
// src/index.ts
|
|
1465
|
+
var TreeSelect3 = TreeSelect;
|
|
1275
1466
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1276
1467
|
0 && (module.exports = {
|
|
1277
1468
|
TreeSelect
|