@djangocfg/ui-tools 2.1.315 → 2.1.317

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/TreeRoot-R6XVHYQK.mjs +4 -0
  2. package/dist/{TreeRoot-DO33TIS5.mjs.map → TreeRoot-R6XVHYQK.mjs.map} +1 -1
  3. package/dist/TreeRoot-RAMQSBMO.cjs +19 -0
  4. package/dist/{TreeRoot-NJOZ2DMV.cjs.map → TreeRoot-RAMQSBMO.cjs.map} +1 -1
  5. package/dist/{chunk-MA552EWC.cjs → chunk-44ZTWYAF.cjs} +139 -111
  6. package/dist/chunk-44ZTWYAF.cjs.map +1 -0
  7. package/dist/chunk-KR6B3LVY.mjs +59 -0
  8. package/dist/chunk-KR6B3LVY.mjs.map +1 -0
  9. package/dist/{chunk-E5BP4IXF.mjs → chunk-NTJL2SXK.mjs} +139 -111
  10. package/dist/chunk-NTJL2SXK.mjs.map +1 -0
  11. package/dist/chunk-YXBOAGIM.cjs +63 -0
  12. package/dist/chunk-YXBOAGIM.cjs.map +1 -0
  13. package/dist/file-icon/index.cjs +117 -0
  14. package/dist/file-icon/index.cjs.map +1 -0
  15. package/dist/file-icon/index.d.cts +69 -0
  16. package/dist/file-icon/index.d.ts +69 -0
  17. package/dist/file-icon/index.mjs +112 -0
  18. package/dist/file-icon/index.mjs.map +1 -0
  19. package/dist/index.cjs +140 -180
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +5 -433
  22. package/dist/index.d.ts +5 -433
  23. package/dist/index.mjs +7 -56
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/tree/index.cjs +152 -0
  26. package/dist/tree/index.cjs.map +1 -0
  27. package/dist/tree/index.d.cts +278 -0
  28. package/dist/tree/index.d.ts +278 -0
  29. package/dist/tree/index.mjs +5 -0
  30. package/dist/tree/index.mjs.map +1 -0
  31. package/dist/types-Cclwv4Hl.d.cts +198 -0
  32. package/dist/types-Cclwv4Hl.d.ts +198 -0
  33. package/package.json +16 -10
  34. package/src/tools/FileIcon/FileIcon.tsx +91 -0
  35. package/src/tools/FileIcon/index.ts +9 -0
  36. package/src/tools/FileIcon/loader.ts +47 -0
  37. package/src/tools/FileIcon/treeAdapter.tsx +41 -0
  38. package/src/tools/Tree/README.md +56 -0
  39. package/src/tools/Tree/Tree.story.tsx +48 -0
  40. package/src/tools/Tree/TreeRoot.tsx +15 -5
  41. package/src/tools/Tree/components/TreeRow.tsx +17 -18
  42. package/src/tools/Tree/context/TreeContext.tsx +10 -2
  43. package/src/tools/Tree/hooks/useTreeKeyboard.ts +133 -99
  44. package/src/tools/Tree/index.tsx +2 -0
  45. package/src/tools/Tree/types.ts +36 -2
  46. package/dist/TreeRoot-DO33TIS5.mjs +0 -4
  47. package/dist/TreeRoot-NJOZ2DMV.cjs +0 -19
  48. package/dist/chunk-E5BP4IXF.mjs.map +0 -1
  49. package/dist/chunk-MA552EWC.cjs.map +0 -1
@@ -0,0 +1,4 @@
1
+ export { TreeRoot, TreeRoot_default as default } from './chunk-NTJL2SXK.mjs';
2
+ import './chunk-CGILA3WO.mjs';
3
+ //# sourceMappingURL=TreeRoot-R6XVHYQK.mjs.map
4
+ //# sourceMappingURL=TreeRoot-R6XVHYQK.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"TreeRoot-DO33TIS5.mjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"TreeRoot-R6XVHYQK.mjs"}
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var chunk44ZTWYAF_cjs = require('./chunk-44ZTWYAF.cjs');
6
+ require('./chunk-WGEGR3DF.cjs');
7
+
8
+
9
+
10
+ Object.defineProperty(exports, "TreeRoot", {
11
+ enumerable: true,
12
+ get: function () { return chunk44ZTWYAF_cjs.TreeRoot; }
13
+ });
14
+ Object.defineProperty(exports, "default", {
15
+ enumerable: true,
16
+ get: function () { return chunk44ZTWYAF_cjs.TreeRoot_default; }
17
+ });
18
+ //# sourceMappingURL=TreeRoot-RAMQSBMO.cjs.map
19
+ //# sourceMappingURL=TreeRoot-RAMQSBMO.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"TreeRoot-NJOZ2DMV.cjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"TreeRoot-RAMQSBMO.cjs"}
@@ -5,6 +5,7 @@ var React = require('react');
5
5
  var lib = require('@djangocfg/ui-core/lib');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var lucideReact = require('lucide-react');
8
+ var hooks = require('@djangocfg/ui-core/hooks');
8
9
 
9
10
  function _interopNamespace(e) {
10
11
  if (e && e.__esModule) return e;
@@ -293,6 +294,7 @@ function TreeProvider(props) {
293
294
  getItemName,
294
295
  loadChildren,
295
296
  selectionMode = "single",
297
+ activationMode = "single-click",
296
298
  initialExpandedIds,
297
299
  initialSelectedIds,
298
300
  indent,
@@ -488,7 +490,7 @@ function TreeProvider(props) {
488
490
  );
489
491
  }, [loadChildren, state.expanded, nodeById, fetchChildren]);
490
492
  const activate = React.useCallback(
491
- (node) => onActivateRef.current?.(node),
493
+ (node, opts = { preview: false }) => onActivateRef.current?.(node, opts),
492
494
  []
493
495
  );
494
496
  const value = React.useMemo(
@@ -516,6 +518,7 @@ function TreeProvider(props) {
516
518
  appearance: resolvedAppearance,
517
519
  indent: resolvedAppearance.indent,
518
520
  selectionMode,
521
+ activationMode,
519
522
  enableSearch,
520
523
  showIndentGuides,
521
524
  getItemName,
@@ -547,6 +550,7 @@ function TreeProvider(props) {
547
550
  labels,
548
551
  resolvedAppearance,
549
552
  selectionMode,
553
+ activationMode,
550
554
  enableSearch,
551
555
  showIndentGuides,
552
556
  getItemName,
@@ -645,6 +649,7 @@ function TreeRow({ row, className }) {
645
649
  const ctx = useTreeContext();
646
650
  const {
647
651
  appearance,
652
+ activationMode,
648
653
  showIndentGuides,
649
654
  selected,
650
655
  focused,
@@ -673,28 +678,24 @@ function TreeRow({ row, className }) {
673
678
  isLoading,
674
679
  isMatchingSearch
675
680
  };
676
- const handleActivate = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(() => {
681
+ const handleClick = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((e) => {
677
682
  if (node.disabled) return;
678
683
  setFocus(node.id);
679
684
  select(node.id);
680
- if (isFolder) toggle(node.id);
681
- else activate(node);
682
- }, "handleActivate");
683
- const handleClick = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((e) => {
684
- handleActivate();
685
+ if (isFolder) {
686
+ toggle(node.id);
687
+ } else if (activationMode === "single-click") {
688
+ activate(node, { preview: false });
689
+ } else if (activationMode === "single-click-preview") {
690
+ activate(node, { preview: true });
691
+ }
685
692
  e.currentTarget.scrollIntoView?.({ block: "nearest" });
686
693
  }, "handleClick");
687
694
  const handleDoubleClick = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(() => {
688
695
  if (node.disabled) return;
689
- if (!isFolder) activate(node);
696
+ if (isFolder) return;
697
+ activate(node, { preview: false });
690
698
  }, "handleDoubleClick");
691
- const handleKeyDown = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((e) => {
692
- if (node.disabled) return;
693
- if (e.key === "Enter" || e.key === " ") {
694
- e.preventDefault();
695
- handleActivate();
696
- }
697
- }, "handleKeyDown");
698
699
  const trigger = /* @__PURE__ */ jsxRuntime.jsxs(
699
700
  "div",
700
701
  {
@@ -706,6 +707,7 @@ function TreeRow({ row, className }) {
706
707
  "aria-disabled": node.disabled || void 0,
707
708
  "data-tree-row": "",
708
709
  "data-id": node.id,
710
+ "data-activation-mode": activationMode,
709
711
  "data-selected": isSelected ? "true" : void 0,
710
712
  "data-focused": isFocused && !isSelected ? "true" : void 0,
711
713
  "data-folder": isFolder || void 0,
@@ -718,7 +720,6 @@ function TreeRow({ row, className }) {
718
720
  },
719
721
  onClick: handleClick,
720
722
  onDoubleClick: handleDoubleClick,
721
- onKeyDown: handleKeyDown,
722
723
  onFocus: () => setFocus(node.id),
723
724
  className: lib.cn(
724
725
  "group/row relative flex w-full select-none items-center pr-2 text-left",
@@ -963,7 +964,6 @@ function TreeSearchInput({ className, showMatches = true }) {
963
964
  }
964
965
  chunkWGEGR3DF_cjs.__name(TreeSearchInput, "TreeSearchInput");
965
966
  function useTreeKeyboard({
966
- containerRef,
967
967
  rows,
968
968
  focusedId,
969
969
  enabled = true,
@@ -974,95 +974,115 @@ function useTreeKeyboard({
974
974
  onCollapse,
975
975
  onClearSelection
976
976
  }) {
977
- React.useEffect(() => {
978
- if (!enabled) return;
979
- const target = containerRef.current;
980
- if (!target) return;
981
- const handler = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((e) => {
982
- const tag = e.target?.tagName;
983
- if (tag === "INPUT" || tag === "TEXTAREA") return;
984
- if (e.target?.isContentEditable) return;
985
- if (rows.length === 0) return;
986
- const currentIndex = focusedId ? rows.findIndex((r) => r.node.id === focusedId) : -1;
987
- const current = currentIndex >= 0 ? rows[currentIndex] : null;
988
- switch (e.key) {
989
- case "ArrowDown": {
990
- e.preventDefault();
991
- const next = rows[Math.min(currentIndex + 1, rows.length - 1)] ?? rows[0];
992
- onFocus(next.node.id);
993
- return;
994
- }
995
- case "ArrowUp": {
996
- e.preventDefault();
997
- const prev = rows[Math.max(currentIndex - 1, 0)] ?? rows[0];
998
- onFocus(prev.node.id);
999
- return;
1000
- }
1001
- case "Home": {
1002
- e.preventDefault();
1003
- onFocus(rows[0].node.id);
1004
- return;
1005
- }
1006
- case "End": {
1007
- e.preventDefault();
1008
- onFocus(rows[rows.length - 1].node.id);
1009
- return;
1010
- }
1011
- case "ArrowRight": {
1012
- if (!current) return;
1013
- e.preventDefault();
1014
- if (current.isFolder && !current.isExpanded) {
1015
- onExpand(current.node.id);
1016
- } else if (current.isFolder && current.isExpanded) {
1017
- const next = rows[currentIndex + 1];
1018
- if (next) onFocus(next.node.id);
1019
- }
1020
- return;
1021
- }
1022
- case "ArrowLeft": {
1023
- if (!current) return;
1024
- e.preventDefault();
1025
- if (current.isFolder && current.isExpanded) {
1026
- onCollapse(current.node.id);
1027
- } else if (current.parentId) {
1028
- onFocus(current.parentId);
1029
- }
1030
- return;
1031
- }
1032
- case "Enter":
1033
- case " ": {
1034
- if (!current) return;
1035
- e.preventDefault();
1036
- onSelect(current.node.id);
1037
- if (current.isFolder) {
1038
- if (current.isExpanded) onCollapse(current.node.id);
1039
- else onExpand(current.node.id);
1040
- } else {
1041
- onActivate(current.node.id);
1042
- }
1043
- return;
1044
- }
1045
- case "Escape": {
1046
- e.preventDefault();
1047
- onClearSelection();
1048
- return;
1049
- }
977
+ const rowsRef = React.useRef(rows);
978
+ const focusedIdRef = React.useRef(focusedId);
979
+ rowsRef.current = rows;
980
+ focusedIdRef.current = focusedId;
981
+ const getCurrent = /* @__PURE__ */ chunkWGEGR3DF_cjs.__name(() => {
982
+ const r = rowsRef.current;
983
+ const id = focusedIdRef.current;
984
+ const idx = id ? r.findIndex((x) => x.node.id === id) : -1;
985
+ return { rows: r, idx, current: idx >= 0 ? r[idx] : null };
986
+ }, "getCurrent");
987
+ const refDown = hooks.useHotkey(
988
+ "down",
989
+ () => {
990
+ const { rows: r, idx } = getCurrent();
991
+ if (r.length === 0) return;
992
+ const next = r[Math.min(idx + 1, r.length - 1)] ?? r[0];
993
+ onFocus(next.node.id);
994
+ },
995
+ { enabled, preventDefault: true, description: "Next row" }
996
+ );
997
+ const refUp = hooks.useHotkey(
998
+ "up",
999
+ () => {
1000
+ const { rows: r, idx } = getCurrent();
1001
+ if (r.length === 0) return;
1002
+ const prev = r[Math.max(idx - 1, 0)] ?? r[0];
1003
+ onFocus(prev.node.id);
1004
+ },
1005
+ { enabled, preventDefault: true, description: "Previous row" }
1006
+ );
1007
+ const refHome = hooks.useHotkey(
1008
+ "home",
1009
+ () => {
1010
+ const { rows: r } = getCurrent();
1011
+ if (r.length === 0) return;
1012
+ onFocus(r[0].node.id);
1013
+ },
1014
+ { enabled, preventDefault: true, description: "First row" }
1015
+ );
1016
+ const refEnd = hooks.useHotkey(
1017
+ "end",
1018
+ () => {
1019
+ const { rows: r } = getCurrent();
1020
+ if (r.length === 0) return;
1021
+ onFocus(r[r.length - 1].node.id);
1022
+ },
1023
+ { enabled, preventDefault: true, description: "Last row" }
1024
+ );
1025
+ const refRight = hooks.useHotkey(
1026
+ "right",
1027
+ () => {
1028
+ const { rows: r, idx, current } = getCurrent();
1029
+ if (!current) return;
1030
+ if (current.isFolder && !current.isExpanded) {
1031
+ onExpand(current.node.id);
1032
+ } else if (current.isFolder && current.isExpanded) {
1033
+ const next = r[idx + 1];
1034
+ if (next) onFocus(next.node.id);
1050
1035
  }
1051
- }, "handler");
1052
- target.addEventListener("keydown", handler);
1053
- return () => target.removeEventListener("keydown", handler);
1054
- }, [
1055
- containerRef,
1056
- rows,
1057
- focusedId,
1058
- enabled,
1059
- onFocus,
1060
- onSelect,
1061
- onActivate,
1062
- onExpand,
1063
- onCollapse,
1064
- onClearSelection
1065
- ]);
1036
+ },
1037
+ { enabled, preventDefault: true, description: "Expand / first child" }
1038
+ );
1039
+ const refLeft = hooks.useHotkey(
1040
+ "left",
1041
+ () => {
1042
+ const { current } = getCurrent();
1043
+ if (!current) return;
1044
+ if (current.isFolder && current.isExpanded) {
1045
+ onCollapse(current.node.id);
1046
+ } else if (current.parentId) {
1047
+ onFocus(current.parentId);
1048
+ }
1049
+ },
1050
+ { enabled, preventDefault: true, description: "Collapse / parent" }
1051
+ );
1052
+ const refActivate = hooks.useHotkey(
1053
+ ["enter", "space"],
1054
+ () => {
1055
+ const { current } = getCurrent();
1056
+ if (!current) return;
1057
+ onSelect(current.node.id);
1058
+ if (current.isFolder) {
1059
+ if (current.isExpanded) onCollapse(current.node.id);
1060
+ else onExpand(current.node.id);
1061
+ } else {
1062
+ onActivate(current.node.id);
1063
+ }
1064
+ },
1065
+ { enabled, preventDefault: true, description: "Activate / toggle" }
1066
+ );
1067
+ const refEscape = hooks.useHotkey(
1068
+ "escape",
1069
+ () => onClearSelection(),
1070
+ { enabled, preventDefault: true, description: "Clear selection" }
1071
+ );
1072
+ const ref = React.useCallback(
1073
+ (instance) => {
1074
+ refDown(instance);
1075
+ refUp(instance);
1076
+ refHome(instance);
1077
+ refEnd(instance);
1078
+ refRight(instance);
1079
+ refLeft(instance);
1080
+ refActivate(instance);
1081
+ refEscape(instance);
1082
+ },
1083
+ [refDown, refUp, refHome, refEnd, refRight, refLeft, refActivate, refEscape]
1084
+ );
1085
+ return { ref };
1066
1086
  }
1067
1087
  chunkWGEGR3DF_cjs.__name(useTreeKeyboard, "useTreeKeyboard");
1068
1088
  var FLUSH_MS = 600;
@@ -1128,6 +1148,7 @@ function TreeRoot(props) {
1128
1148
  getItemName,
1129
1149
  loadChildren,
1130
1150
  selectionMode,
1151
+ activationMode,
1131
1152
  initialExpandedIds,
1132
1153
  initialSelectedIds,
1133
1154
  indent,
@@ -1156,6 +1177,7 @@ function TreeRoot(props) {
1156
1177
  getItemName,
1157
1178
  loadChildren,
1158
1179
  selectionMode,
1180
+ activationMode,
1159
1181
  initialExpandedIds,
1160
1182
  initialSelectedIds,
1161
1183
  indent,
@@ -1195,20 +1217,26 @@ function TreeRootShell({
1195
1217
  }) {
1196
1218
  const containerRef = React.useRef(null);
1197
1219
  const ctx = useTreeContext();
1198
- useTreeKeyboard({
1199
- containerRef,
1220
+ const { ref: keyboardRef } = useTreeKeyboard({
1200
1221
  rows: ctx.flatRows,
1201
1222
  focusedId: ctx.focused,
1202
1223
  onFocus: ctx.setFocus,
1203
1224
  onSelect: ctx.select,
1204
1225
  onActivate: /* @__PURE__ */ chunkWGEGR3DF_cjs.__name((id) => {
1205
1226
  const row = ctx.flatRows.find((r) => r.node.id === id);
1206
- if (row) ctx.activate(row.node);
1227
+ if (row) ctx.activate(row.node, { preview: false });
1207
1228
  }, "onActivate"),
1208
1229
  onExpand: ctx.expand,
1209
1230
  onCollapse: ctx.collapse,
1210
1231
  onClearSelection: ctx.clearSelection
1211
1232
  });
1233
+ const setContainerRef = React.useCallback(
1234
+ (instance) => {
1235
+ containerRef.current = instance;
1236
+ keyboardRef(instance);
1237
+ },
1238
+ [keyboardRef]
1239
+ );
1212
1240
  const onTypeAheadMatch = React.useCallback(
1213
1241
  (id) => {
1214
1242
  ctx.setFocus(id);
@@ -1229,7 +1257,7 @@ function TreeRootShell({
1229
1257
  return /* @__PURE__ */ jsxRuntime.jsxs(
1230
1258
  "div",
1231
1259
  {
1232
- ref: containerRef,
1260
+ ref: setContainerRef,
1233
1261
  tabIndex: 0,
1234
1262
  className: lib.cn(
1235
1263
  "group/tree flex h-full w-full flex-col gap-2 outline-none",
@@ -1278,5 +1306,5 @@ exports.useTreeRows = useTreeRows;
1278
1306
  exports.useTreeSearch = useTreeSearch;
1279
1307
  exports.useTreeSelection = useTreeSelection;
1280
1308
  exports.useTreeTypeAhead = useTreeTypeAhead;
1281
- //# sourceMappingURL=chunk-MA552EWC.cjs.map
1282
- //# sourceMappingURL=chunk-MA552EWC.cjs.map
1309
+ //# sourceMappingURL=chunk-44ZTWYAF.cjs.map
1310
+ //# sourceMappingURL=chunk-44ZTWYAF.cjs.map