@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/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 ? "rgba(0, 0, 0, 0.04)" : isSelected ? "rgba(15, 98, 254, 0.08)" : void 0;
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
- onClick: (event) => onRowClick == null ? void 0 : onRowClick(event, item.id),
132
+ ...rowAccessibility,
133
+ onClick: (event) => onRowClick?.(event, item.id),
101
134
  "data-tree-row": item.id,
102
- className: "transition-colors hover:bg-bg-primary-subtle",
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": "Drag item",
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 == null ? void 0 : 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
- ...labelStyle
258
+ ...!renderLabel ? labelStyle : {}
204
259
  },
205
- children: label
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 == null ? void 0 : onAddChild(item.id)
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 == null ? void 0 : onAddSibling(item.id)
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 == null ? void 0 : onDelete(item.id)
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
- var _a;
337
- (_a = listeners == null ? void 0 : listeners.onPointerDown) == null ? void 0 : _a.call(listeners, event);
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, onRowClick]
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
- var _a, _b;
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 = (_b = itemById.get(parentId)) == null ? void 0 : _b.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
- function TreeSelect({
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
- var _a, _b;
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
- var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
793
- const defaults = (levelConfig == null ? void 0 : levelConfig.default) ?? {};
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: ((_b2 = perLevel.actions) == null ? void 0 : _b2.addChild) ?? ((_c = defaults.actions) == null ? void 0 : _c.addChild) ?? editable,
798
- addSibling: ((_d = perLevel.actions) == null ? void 0 : _d.addSibling) ?? ((_e = defaults.actions) == null ? void 0 : _e.addSibling) ?? editable,
799
- delete: ((_f = perLevel.actions) == null ? void 0 : _f.delete) ?? ((_g = defaults.actions) == null ? void 0 : _g.delete) ?? (editable && allowDelete)
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: ((_h = perLevel.tooltips) == null ? void 0 : _h.addChild) ?? ((_i = defaults.tooltips) == null ? void 0 : _i.addChild) ?? addChildTooltip,
803
- addSibling: ((_j = perLevel.tooltips) == null ? void 0 : _j.addSibling) ?? ((_k = defaults.tooltips) == null ? void 0 : _k.addSibling) ?? addSiblingTooltip,
804
- delete: ((_l = perLevel.tooltips) == null ? void 0 : _l.delete) ?? ((_m = defaults.tooltips) == null ? void 0 : _m.delete) ?? deleteTooltip
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 == null ? void 0 : onExpandedChange(next);
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 == null ? void 0 : onDataChange(nextTree, meta);
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 handleRowClick = useCallback2(
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 = ((_a2 = itemById.get(id)) == null ? void 0 : _a2.parentId) ?? null;
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 || (((_b2 = itemById.get(lastSelectedId ?? "")) == null ? void 0 : _b2.parentId) ?? null) !== parentId) {
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 = ((_a3 = itemById.get(firstSelected)) == null ? void 0 : _a3.parentId) ?? null;
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
- var _a2;
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 = ((_a2 = itemById.get(draggedRootIds[0] ?? activeId)) == null ? void 0 : _a2.parentId) ?? null;
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
- var _a2, _b2, _c, _d;
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
- return /* @__PURE__ */ jsx3(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsx3("div", { style: { position: "relative", padding: 8 }, children: allowDragAndDrop ? /* @__PURE__ */ jsxs2(
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 == null ? void 0 : 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` : ((_b = (_a = visibleItems.find((item) => item.id === activeId)) == null ? void 0 : _a.node) == null ? void 0 : _b.label) ?? activeId
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 == null ? void 0 : 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: void 0,
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
  };