@vesture/react 0.2.3 → 0.2.5

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.js CHANGED
@@ -76,7 +76,7 @@ var Select = forwardRef5(function Select2({ invalid, className, children, ...res
76
76
  });
77
77
 
78
78
  // src/components/Checkbox/Checkbox.tsx
79
- import { forwardRef as forwardRef6 } from "react";
79
+ import { forwardRef as forwardRef6, useEffect, useRef } from "react";
80
80
 
81
81
  // src/components/Checkbox/Checkbox.css.ts
82
82
  var box = "Checkbox_box__7029a22";
@@ -85,11 +85,32 @@ var wrapper2 = "Checkbox_wrapper__7029a20";
85
85
 
86
86
  // src/components/Checkbox/Checkbox.tsx
87
87
  import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
88
- var Checkbox = forwardRef6(function Checkbox2({ label: label3, className, ...rest }, ref) {
88
+ var Checkbox = forwardRef6(function Checkbox2({ label: label4, className, indeterminate = false, ...rest }, ref) {
89
+ const inputRef = useRef(null);
90
+ useEffect(() => {
91
+ if (inputRef.current) {
92
+ inputRef.current.indeterminate = indeterminate;
93
+ }
94
+ }, [indeterminate]);
89
95
  return /* @__PURE__ */ jsxs3("label", { className: [wrapper2, className].filter(Boolean).join(" "), children: [
90
- /* @__PURE__ */ jsx6("input", { ref, type: "checkbox", className: hiddenInput, ...rest }),
96
+ /* @__PURE__ */ jsx6(
97
+ "input",
98
+ {
99
+ ref: (el) => {
100
+ inputRef.current = el;
101
+ if (typeof ref === "function") {
102
+ ref(el);
103
+ } else if (ref) {
104
+ ref.current = el;
105
+ }
106
+ },
107
+ type: "checkbox",
108
+ className: hiddenInput,
109
+ ...rest
110
+ }
111
+ ),
91
112
  /* @__PURE__ */ jsx6("span", { className: box, "aria-hidden": "true" }),
92
- label3
113
+ label4
93
114
  ] });
94
115
  });
95
116
 
@@ -103,11 +124,11 @@ var wrapper3 = "Radio_wrapper__gzk3vv0";
103
124
 
104
125
  // src/components/Radio/Radio.tsx
105
126
  import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
106
- var Radio = forwardRef7(function Radio2({ label: label3, className, ...rest }, ref) {
127
+ var Radio = forwardRef7(function Radio2({ label: label4, className, ...rest }, ref) {
107
128
  return /* @__PURE__ */ jsxs4("label", { className: [wrapper3, className].filter(Boolean).join(" "), children: [
108
129
  /* @__PURE__ */ jsx7("input", { ref, type: "radio", className: hiddenInput2, ...rest }),
109
130
  /* @__PURE__ */ jsx7("span", { className: circle, "aria-hidden": "true" }),
110
- label3
131
+ label4
111
132
  ] });
112
133
  });
113
134
 
@@ -121,11 +142,11 @@ var wrapper4 = "Switch_wrapper__1yxysfy0";
121
142
 
122
143
  // src/components/Switch/Switch.tsx
123
144
  import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
124
- var Switch = forwardRef8(function Switch2({ label: label3, className, ...rest }, ref) {
145
+ var Switch = forwardRef8(function Switch2({ label: label4, className, ...rest }, ref) {
125
146
  return /* @__PURE__ */ jsxs5("label", { className: [wrapper4, className].filter(Boolean).join(" "), children: [
126
147
  /* @__PURE__ */ jsx8("input", { ref, type: "checkbox", role: "switch", className: hiddenInput3, ...rest }),
127
148
  /* @__PURE__ */ jsx8("span", { className: track, "aria-hidden": "true" }),
128
- label3
149
+ label4
129
150
  ] });
130
151
  });
131
152
 
@@ -200,7 +221,7 @@ var Divider = forwardRef11(function Divider2({ orientation: orientationProp = "h
200
221
  });
201
222
 
202
223
  // src/components/Tooltip/Tooltip.tsx
203
- import { cloneElement, isValidElement, useRef, useState } from "react";
224
+ import { cloneElement, isValidElement, useRef as useRef2, useState } from "react";
204
225
  import {
205
226
  FloatingArrow,
206
227
  FloatingPortal,
@@ -226,7 +247,7 @@ var tooltip = "Tooltip_tooltip__foawl10";
226
247
  import { Fragment, jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
227
248
  function Tooltip({ content, placement = "top", children }) {
228
249
  const [open, setOpen] = useState(false);
229
- const arrowRef = useRef(null);
250
+ const arrowRef = useRef2(null);
230
251
  const { refs, floatingStyles, context } = useFloating({
231
252
  open,
232
253
  onOpenChange: setOpen,
@@ -444,7 +465,7 @@ function useToast() {
444
465
  }
445
466
 
446
467
  // src/components/DropdownMenu/DropdownMenu.tsx
447
- import { cloneElement as cloneElement3, isValidElement as isValidElement3, useRef as useRef2, useState as useState4 } from "react";
468
+ import { cloneElement as cloneElement3, isValidElement as isValidElement3, useRef as useRef3, useState as useState4 } from "react";
448
469
  import {
449
470
  FloatingFocusManager as FloatingFocusManager3,
450
471
  FloatingPortal as FloatingPortal4,
@@ -485,7 +506,7 @@ function DropdownMenu({
485
506
  }) {
486
507
  const [open, setOpen] = useState4(false);
487
508
  const [activeIndex, setActiveIndex] = useState4(null);
488
- const elementsRef = useRef2([]);
509
+ const elementsRef = useRef3([]);
489
510
  const { refs, floatingStyles, context } = useFloating4({
490
511
  open,
491
512
  onOpenChange: setOpen,
@@ -1027,7 +1048,7 @@ var srOnly = "Spinner_srOnly__1hs5uji5";
1027
1048
  import { jsx as jsx32 } from "react/jsx-runtime";
1028
1049
  function Spinner({
1029
1050
  size: sizeProp = "md",
1030
- label: label3 = "Loading",
1051
+ label: label4 = "Loading",
1031
1052
  className,
1032
1053
  ...rest
1033
1054
  }) {
@@ -1035,10 +1056,10 @@ function Spinner({
1035
1056
  "div",
1036
1057
  {
1037
1058
  role: "status",
1038
- "aria-label": label3,
1059
+ "aria-label": label4,
1039
1060
  className: [spinner, size2[sizeProp], className].filter(Boolean).join(" "),
1040
1061
  ...rest,
1041
- children: /* @__PURE__ */ jsx32("span", { className: srOnly, children: label3 })
1062
+ children: /* @__PURE__ */ jsx32("span", { className: srOnly, children: label4 })
1042
1063
  }
1043
1064
  );
1044
1065
  }
@@ -1079,7 +1100,7 @@ function Alert({
1079
1100
  }
1080
1101
 
1081
1102
  // src/components/DataGrid/DataGrid.tsx
1082
- import { useCallback as useCallback2, useEffect, useMemo as useMemo2, useRef as useRef3, useState as useState8 } from "react";
1103
+ import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef4, useState as useState8 } from "react";
1083
1104
 
1084
1105
  // src/components/DataGrid/DataGrid.css.ts
1085
1106
  var actionsCell = "DataGrid_actionsCell__7xivx7g";
@@ -1116,7 +1137,7 @@ var ACTIONS_COLUMN_WIDTH = 72;
1116
1137
  var FILTER_DEBOUNCE_MS = 200;
1117
1138
  function useDebouncedValue(value, delay) {
1118
1139
  const [debounced, setDebounced] = useState8(value);
1119
- useEffect(() => {
1140
+ useEffect2(() => {
1120
1141
  const timer = setTimeout(() => setDebounced(value), delay);
1121
1142
  return () => clearTimeout(timer);
1122
1143
  }, [value, delay]);
@@ -1129,17 +1150,17 @@ function FilterTextInput({
1129
1150
  }) {
1130
1151
  const [draft, setDraft] = useState8(value);
1131
1152
  const debounced = useDebouncedValue(draft, FILTER_DEBOUNCE_MS);
1132
- const onChangeRef = useRef3(onChange);
1153
+ const onChangeRef = useRef4(onChange);
1133
1154
  onChangeRef.current = onChange;
1134
- const mounted = useRef3(false);
1135
- useEffect(() => {
1155
+ const mounted = useRef4(false);
1156
+ useEffect2(() => {
1136
1157
  if (!mounted.current) {
1137
1158
  mounted.current = true;
1138
1159
  return;
1139
1160
  }
1140
1161
  onChangeRef.current(debounced);
1141
1162
  }, [debounced]);
1142
- useEffect(() => {
1163
+ useEffect2(() => {
1143
1164
  setDraft(value);
1144
1165
  }, [value]);
1145
1166
  return /* @__PURE__ */ jsx34(
@@ -1158,11 +1179,11 @@ function compareValues(a, b) {
1158
1179
  }
1159
1180
  return String(a).localeCompare(String(b));
1160
1181
  }
1161
- function getCellValue(column, row2) {
1182
+ function getCellValue(column, row3) {
1162
1183
  if (column.accessor) {
1163
- return column.accessor(row2);
1184
+ return column.accessor(row3);
1164
1185
  }
1165
- return row2[column.key];
1186
+ return row3[column.key];
1166
1187
  }
1167
1188
  function DataGrid({
1168
1189
  columns,
@@ -1244,11 +1265,11 @@ function DataGrid({
1244
1265
  return data;
1245
1266
  }
1246
1267
  return data.filter(
1247
- (row2) => filters.every((f) => {
1268
+ (row3) => filters.every((f) => {
1248
1269
  if (!f.value) return true;
1249
1270
  const column = columns.find((c) => c.key === f.key);
1250
1271
  if (!column) return true;
1251
- const cellValue = String(getCellValue(column, row2) ?? "").toLowerCase();
1272
+ const cellValue = String(getCellValue(column, row3) ?? "").toLowerCase();
1252
1273
  return cellValue.includes(f.value.toLowerCase());
1253
1274
  })
1254
1275
  );
@@ -1274,8 +1295,8 @@ function DataGrid({
1274
1295
  for (const column of columns) {
1275
1296
  if (!column.filterable || column.filterType !== "select") continue;
1276
1297
  const values = /* @__PURE__ */ new Set();
1277
- for (const row2 of data) {
1278
- values.add(String(getCellValue(column, row2) ?? ""));
1298
+ for (const row3 of data) {
1299
+ values.add(String(getCellValue(column, row3) ?? ""));
1279
1300
  }
1280
1301
  map[column.key] = Array.from(values).sort();
1281
1302
  }
@@ -1301,7 +1322,7 @@ function DataGrid({
1301
1322
  }
1302
1323
  setSelectedIds(next);
1303
1324
  };
1304
- const resizeState = useRef3(null);
1325
+ const resizeState = useRef4(null);
1305
1326
  const handleResizeStart = useCallback2(
1306
1327
  (event, column) => {
1307
1328
  if (column.resizable === false) return;
@@ -1713,7 +1734,7 @@ function DataGrid({
1713
1734
  }
1714
1735
 
1715
1736
  // src/components/Calendar/Calendar.tsx
1716
- import { useEffect as useEffect2, useMemo as useMemo3, useRef as useRef4, useState as useState9 } from "react";
1737
+ import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef5, useState as useState9 } from "react";
1717
1738
 
1718
1739
  // src/components/Calendar/Calendar.css.ts
1719
1740
  var dayButton = "Calendar_dayButton__1uy43my8";
@@ -1852,15 +1873,15 @@ function Calendar({
1852
1873
  const [rovingDate, setRovingDate] = useState9(
1853
1874
  () => getFocusableDate(month, value, minDate, maxDate, isDateDisabled)
1854
1875
  );
1855
- const shouldFocusRef = useRef4(false);
1856
- const dayRefs = useRef4(/* @__PURE__ */ new Map());
1857
- useEffect2(() => {
1876
+ const shouldFocusRef = useRef5(false);
1877
+ const dayRefs = useRef5(/* @__PURE__ */ new Map());
1878
+ useEffect3(() => {
1858
1879
  setRovingDate((prev) => {
1859
1880
  if (isSameMonth(prev, month)) return prev;
1860
1881
  return getFocusableDate(month, value, minDate, maxDate, isDateDisabled);
1861
1882
  });
1862
1883
  }, [month]);
1863
- useEffect2(() => {
1884
+ useEffect3(() => {
1864
1885
  if (!shouldFocusRef.current) return;
1865
1886
  shouldFocusRef.current = false;
1866
1887
  dayRefs.current.get(dateKey(rovingDate))?.focus();
@@ -2027,7 +2048,7 @@ function Calendar({
2027
2048
  }
2028
2049
 
2029
2050
  // src/components/DatePicker/DatePicker.tsx
2030
- import { useEffect as useEffect3, useRef as useRef5, useState as useState10 } from "react";
2051
+ import { useEffect as useEffect4, useRef as useRef6, useState as useState10 } from "react";
2031
2052
  import {
2032
2053
  FloatingFocusManager as FloatingFocusManager4,
2033
2054
  FloatingPortal as FloatingPortal5,
@@ -2141,11 +2162,11 @@ function DatePicker({
2141
2162
  const [inputText, setInputText] = useState10(
2142
2163
  () => value ? formatDate(value, locale) : ""
2143
2164
  );
2144
- const inputRef = useRef5(null);
2145
- useEffect3(() => {
2165
+ const inputRef = useRef6(null);
2166
+ useEffect4(() => {
2146
2167
  setInputText(value ? formatDate(value, locale) : "");
2147
2168
  }, [value, locale]);
2148
- useEffect3(() => {
2169
+ useEffect4(() => {
2149
2170
  if (open && value) {
2150
2171
  setMonth(startOfMonth2(value));
2151
2172
  }
@@ -2293,7 +2314,7 @@ function DatePicker({
2293
2314
  }
2294
2315
 
2295
2316
  // src/components/DateRangePicker/DateRangePicker.tsx
2296
- import { useEffect as useEffect4, useState as useState11 } from "react";
2317
+ import { useEffect as useEffect5, useState as useState11 } from "react";
2297
2318
  import {
2298
2319
  FloatingFocusManager as FloatingFocusManager5,
2299
2320
  FloatingPortal as FloatingPortal6,
@@ -2418,13 +2439,13 @@ function DateRangePicker({
2418
2439
  const [endText, setEndText] = useState11(
2419
2440
  () => value.end ? formatDate2(value.end, locale) : ""
2420
2441
  );
2421
- useEffect4(() => {
2442
+ useEffect5(() => {
2422
2443
  setStartText(value.start ? formatDate2(value.start, locale) : "");
2423
2444
  }, [value.start, locale]);
2424
- useEffect4(() => {
2445
+ useEffect5(() => {
2425
2446
  setEndText(value.end ? formatDate2(value.end, locale) : "");
2426
2447
  }, [value.end, locale]);
2427
- useEffect4(() => {
2448
+ useEffect5(() => {
2428
2449
  if (open && value.start) {
2429
2450
  setMonth(startOfMonth3(value.start));
2430
2451
  }
@@ -2621,7 +2642,7 @@ function DateRangePicker({
2621
2642
  }
2622
2643
 
2623
2644
  // src/components/NumberInput/NumberInput.tsx
2624
- import { forwardRef as forwardRef14, useEffect as useEffect5, useRef as useRef6, useState as useState12 } from "react";
2645
+ import { forwardRef as forwardRef14, useEffect as useEffect6, useRef as useRef7, useState as useState12 } from "react";
2625
2646
  import { useMergeRefs as useMergeRefs5 } from "@floating-ui/react";
2626
2647
 
2627
2648
  // src/components/NumberInput/NumberInput.css.ts
@@ -2666,10 +2687,10 @@ var NumberInput = forwardRef14(function NumberInput2({
2666
2687
  onChange?.(next);
2667
2688
  };
2668
2689
  const [inputText, setInputText] = useState12(() => formatValue(value));
2669
- useEffect5(() => {
2690
+ useEffect6(() => {
2670
2691
  setInputText(formatValue(value));
2671
2692
  }, [value]);
2672
- const inputRef = useRef6(null);
2693
+ const inputRef = useRef7(null);
2673
2694
  const mergedRef = useMergeRefs5([inputRef, ref]);
2674
2695
  const commitTypedValue = () => {
2675
2696
  const trimmed = inputText.trim();
@@ -2780,7 +2801,7 @@ var NumberInput = forwardRef14(function NumberInput2({
2780
2801
  });
2781
2802
 
2782
2803
  // src/components/Slider/Slider.tsx
2783
- import { useRef as useRef7, useState as useState13 } from "react";
2804
+ import { useRef as useRef8, useState as useState13 } from "react";
2784
2805
 
2785
2806
  // src/components/Slider/Slider.css.ts
2786
2807
  var range2 = "Slider_range__2gl56i2";
@@ -2824,10 +2845,10 @@ function Slider({
2824
2845
  }
2825
2846
  onChange?.(next);
2826
2847
  };
2827
- const trackRef = useRef7(null);
2828
- const thumbRefs = useRef7({ single: null, 0: null, 1: null });
2829
- const dragThumbIndex = useRef7(null);
2830
- const isDragging = useRef7(false);
2848
+ const trackRef = useRef8(null);
2849
+ const thumbRefs = useRef8({ single: null, 0: null, 1: null });
2850
+ const dragThumbIndex = useRef8(null);
2851
+ const isDragging = useRef8(false);
2831
2852
  const valueFromClientX = (clientX) => {
2832
2853
  const trackEl = trackRef.current;
2833
2854
  if (!trackEl) return min;
@@ -2910,7 +2931,7 @@ function Slider({
2910
2931
  event.preventDefault();
2911
2932
  setThumbValue(index, next);
2912
2933
  };
2913
- const renderThumb = (index, current2, label3) => /* @__PURE__ */ jsx39(
2934
+ const renderThumb = (index, current2, label4) => /* @__PURE__ */ jsx39(
2914
2935
  "span",
2915
2936
  {
2916
2937
  ref: (node) => {
@@ -2918,7 +2939,7 @@ function Slider({
2918
2939
  },
2919
2940
  role: "slider",
2920
2941
  tabIndex: disabled ? -1 : 0,
2921
- "aria-label": label3,
2942
+ "aria-label": label4,
2922
2943
  "aria-valuemin": min,
2923
2944
  "aria-valuemax": max,
2924
2945
  "aria-valuenow": current2,
@@ -2954,7 +2975,7 @@ function Slider({
2954
2975
  }
2955
2976
 
2956
2977
  // src/components/Combobox/Combobox.tsx
2957
- import { useEffect as useEffect6, useId as useId2, useRef as useRef8, useState as useState14 } from "react";
2978
+ import { useEffect as useEffect7, useId as useId2, useRef as useRef9, useState as useState14 } from "react";
2958
2979
  import {
2959
2980
  FloatingPortal as FloatingPortal7,
2960
2981
  autoUpdate as autoUpdate6,
@@ -3021,12 +3042,12 @@ function Combobox({
3021
3042
  const [open, setOpen] = useState14(false);
3022
3043
  const [inputText, setInputText] = useState14(() => multiple ? "" : labelFor(options, singleValue));
3023
3044
  const [activeIndex, setActiveIndex] = useState14(null);
3024
- useEffect6(() => {
3045
+ useEffect7(() => {
3025
3046
  if (!multiple) {
3026
3047
  setInputText(labelFor(options, singleValue));
3027
3048
  }
3028
3049
  }, [singleValue, multiple]);
3029
- const inputRef = useRef8(null);
3050
+ const inputRef = useRef9(null);
3030
3051
  const listboxId = useId2();
3031
3052
  const filteredOptions = filterOptions(options, multiple ? inputText : open ? inputText : "");
3032
3053
  const { refs, floatingStyles, context } = useFloating7({
@@ -3206,8 +3227,386 @@ function Combobox({
3206
3227
  ] });
3207
3228
  }
3208
3229
 
3230
+ // src/components/TreeView/TreeView.tsx
3231
+ import { useEffect as useEffect8, useMemo as useMemo4, useRef as useRef10, useState as useState15 } from "react";
3232
+ import { vars as vars2 } from "@vesture/tokens";
3233
+
3234
+ // src/components/TreeView/TreeView.css.ts
3235
+ var checkboxCell2 = "TreeView_checkboxCell__1c2l28y6";
3236
+ var chevron2 = "TreeView_chevron__1c2l28y4";
3237
+ var label3 = "TreeView_label__1c2l28y7";
3238
+ var loadingSlot = "TreeView_loadingSlot__1c2l28y5";
3239
+ var root4 = "TreeView_root__1c2l28y1";
3240
+ var row2 = "TreeView_row__1c2l28y2";
3241
+ var rowContent = "TreeView_rowContent__1c2l28y3";
3242
+ var scroller = "TreeView_scroller__1c2l28y0";
3243
+
3244
+ // src/components/TreeView/TreeView.tsx
3245
+ import { jsx as jsx41, jsxs as jsxs24 } from "react/jsx-runtime";
3246
+ function getChildren(node, loadedChildren) {
3247
+ return node.children ?? loadedChildren.get(node.id);
3248
+ }
3249
+ function collectDescendantIds(node, loadedChildren, acc) {
3250
+ const children = getChildren(node, loadedChildren);
3251
+ if (!children) return;
3252
+ for (const child of children) {
3253
+ acc.add(child.id);
3254
+ collectDescendantIds(child, loadedChildren, acc);
3255
+ }
3256
+ }
3257
+ function hasAnyDescendantSelected(node, loadedChildren, selected) {
3258
+ const children = getChildren(node, loadedChildren);
3259
+ if (!children) return false;
3260
+ for (const child of children) {
3261
+ if (selected.has(child.id)) return true;
3262
+ if (hasAnyDescendantSelected(child, loadedChildren, selected)) return true;
3263
+ }
3264
+ return false;
3265
+ }
3266
+ function reconcileAncestors(nodeId, next, nodeIndex, loadedChildren) {
3267
+ let parentId = nodeIndex.get(nodeId)?.parentId ?? null;
3268
+ while (parentId) {
3269
+ const entry = nodeIndex.get(parentId);
3270
+ if (!entry) break;
3271
+ const children = getChildren(entry.node, loadedChildren) ?? [];
3272
+ const allChecked = children.length > 0 && children.every((c) => next.has(c.id));
3273
+ if (allChecked) {
3274
+ next.add(parentId);
3275
+ } else {
3276
+ next.delete(parentId);
3277
+ }
3278
+ parentId = entry.parentId;
3279
+ }
3280
+ }
3281
+ function TreeView({
3282
+ nodes,
3283
+ expanded: controlledExpanded,
3284
+ defaultExpanded,
3285
+ onExpandedChange,
3286
+ selected: controlledSelected,
3287
+ defaultSelected,
3288
+ onSelectedChange,
3289
+ selectable = "none",
3290
+ onLoadChildren,
3291
+ height = 320,
3292
+ rowHeight = 32
3293
+ }) {
3294
+ const [uncontrolledExpanded, setUncontrolledExpanded] = useState15(
3295
+ () => defaultExpanded ?? /* @__PURE__ */ new Set()
3296
+ );
3297
+ const expandedSet = controlledExpanded !== void 0 ? controlledExpanded : uncontrolledExpanded;
3298
+ const setExpandedSet = (next) => {
3299
+ if (controlledExpanded === void 0) {
3300
+ setUncontrolledExpanded(next);
3301
+ }
3302
+ onExpandedChange?.(next);
3303
+ };
3304
+ const [uncontrolledSelected, setUncontrolledSelected] = useState15(
3305
+ () => defaultSelected ?? /* @__PURE__ */ new Set()
3306
+ );
3307
+ const selectedSet = controlledSelected !== void 0 ? controlledSelected : uncontrolledSelected;
3308
+ const setSelectedSet = (next) => {
3309
+ if (controlledSelected === void 0) {
3310
+ setUncontrolledSelected(next);
3311
+ }
3312
+ onSelectedChange?.(next);
3313
+ };
3314
+ const selectedSetRef = useRef10(selectedSet);
3315
+ selectedSetRef.current = selectedSet;
3316
+ const [loadedChildren, setLoadedChildren] = useState15(
3317
+ () => /* @__PURE__ */ new Map()
3318
+ );
3319
+ const [loadingIds, setLoadingIds] = useState15(() => /* @__PURE__ */ new Set());
3320
+ const attemptedLoadRef = useRef10(/* @__PURE__ */ new Set());
3321
+ const [scrollTop, setScrollTop] = useState15(0);
3322
+ const [focusedId, setFocusedId] = useState15(
3323
+ () => nodes[0]?.id ?? null
3324
+ );
3325
+ const shouldFocusRef = useRef10(false);
3326
+ const containerRef = useRef10(null);
3327
+ const rowRefs = useRef10(/* @__PURE__ */ new Map());
3328
+ const nodeIndex = useMemo4(() => {
3329
+ const map = /* @__PURE__ */ new Map();
3330
+ function walk(list4, parentId) {
3331
+ for (const node of list4) {
3332
+ map.set(node.id, { node, parentId });
3333
+ const children = getChildren(node, loadedChildren);
3334
+ if (children) walk(children, node.id);
3335
+ }
3336
+ }
3337
+ walk(nodes, null);
3338
+ return map;
3339
+ }, [nodes, loadedChildren]);
3340
+ const flatNodes = useMemo4(() => {
3341
+ const result = [];
3342
+ function walk(list4, depth) {
3343
+ for (const node of list4) {
3344
+ const children = getChildren(node, loadedChildren);
3345
+ const nodeHasChildren = children ? children.length > 0 : node.hasChildren === true;
3346
+ const isExpanded = expandedSet.has(node.id);
3347
+ result.push({ node, depth, hasChildren: nodeHasChildren, isExpanded });
3348
+ if (isExpanded && children) {
3349
+ walk(children, depth + 1);
3350
+ }
3351
+ }
3352
+ }
3353
+ walk(nodes, 0);
3354
+ return result;
3355
+ }, [nodes, expandedSet, loadedChildren]);
3356
+ useEffect8(() => {
3357
+ if (focusedId !== null && flatNodes.some((f) => f.node.id === focusedId)) {
3358
+ return;
3359
+ }
3360
+ setFocusedId(flatNodes[0]?.node.id ?? null);
3361
+ }, [flatNodes, focusedId]);
3362
+ useEffect8(() => {
3363
+ if (!shouldFocusRef.current || focusedId === null) return;
3364
+ shouldFocusRef.current = false;
3365
+ rowRefs.current.get(focusedId)?.focus();
3366
+ }, [focusedId, scrollTop]);
3367
+ async function loadChildrenFor(node) {
3368
+ if (!onLoadChildren) return;
3369
+ if (attemptedLoadRef.current.has(node.id)) return;
3370
+ attemptedLoadRef.current.add(node.id);
3371
+ setLoadingIds((prev) => new Set(prev).add(node.id));
3372
+ try {
3373
+ const loaded = await onLoadChildren(node);
3374
+ setLoadedChildren((prev) => new Map(prev).set(node.id, loaded));
3375
+ if (selectable === "multi" && selectedSetRef.current.has(node.id)) {
3376
+ setSelectedSet(
3377
+ (() => {
3378
+ const next = new Set(selectedSetRef.current);
3379
+ for (const child of loaded) next.add(child.id);
3380
+ return next;
3381
+ })()
3382
+ );
3383
+ }
3384
+ } finally {
3385
+ setLoadingIds((prev) => {
3386
+ const next = new Set(prev);
3387
+ next.delete(node.id);
3388
+ return next;
3389
+ });
3390
+ }
3391
+ }
3392
+ function expandNode(node) {
3393
+ const next = new Set(expandedSet);
3394
+ next.add(node.id);
3395
+ setExpandedSet(next);
3396
+ const children = getChildren(node, loadedChildren);
3397
+ if (node.hasChildren === true && children === void 0) {
3398
+ void loadChildrenFor(node);
3399
+ }
3400
+ }
3401
+ function collapseNode(node) {
3402
+ const next = new Set(expandedSet);
3403
+ next.delete(node.id);
3404
+ setExpandedSet(next);
3405
+ }
3406
+ function toggleExpand(flat) {
3407
+ if (flat.isExpanded) {
3408
+ collapseNode(flat.node);
3409
+ } else {
3410
+ expandNode(flat.node);
3411
+ }
3412
+ }
3413
+ function toggleSelect(node) {
3414
+ if (selectable === "none") return;
3415
+ if (selectable === "single") {
3416
+ const isSelected = selectedSet.has(node.id);
3417
+ setSelectedSet(isSelected ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([node.id]));
3418
+ return;
3419
+ }
3420
+ const next = new Set(selectedSet);
3421
+ const willSelect = !next.has(node.id);
3422
+ const descendants = /* @__PURE__ */ new Set();
3423
+ collectDescendantIds(node, loadedChildren, descendants);
3424
+ if (willSelect) {
3425
+ next.add(node.id);
3426
+ descendants.forEach((id) => next.add(id));
3427
+ } else {
3428
+ next.delete(node.id);
3429
+ descendants.forEach((id) => next.delete(id));
3430
+ }
3431
+ reconcileAncestors(node.id, next, nodeIndex, loadedChildren);
3432
+ setSelectedSet(next);
3433
+ }
3434
+ function moveFocusTo(id) {
3435
+ const index = flatNodes.findIndex((f) => f.node.id === id);
3436
+ if (index === -1) return;
3437
+ const rowTop = index * rowHeight;
3438
+ const rowBottom = rowTop + rowHeight;
3439
+ let scrolled = false;
3440
+ if (containerRef.current) {
3441
+ if (rowTop < containerRef.current.scrollTop) {
3442
+ containerRef.current.scrollTop = rowTop;
3443
+ scrolled = true;
3444
+ } else if (rowBottom > containerRef.current.scrollTop + height) {
3445
+ containerRef.current.scrollTop = rowBottom - height;
3446
+ scrolled = true;
3447
+ }
3448
+ }
3449
+ setFocusedId(id);
3450
+ const target = rowRefs.current.get(id);
3451
+ if (target && !scrolled) {
3452
+ target.focus();
3453
+ } else {
3454
+ shouldFocusRef.current = true;
3455
+ }
3456
+ }
3457
+ function handleKeyDown(event, flat) {
3458
+ const { node, depth, hasChildren, isExpanded } = flat;
3459
+ const index = flatNodes.findIndex((f) => f.node.id === node.id);
3460
+ switch (event.key) {
3461
+ case "ArrowDown": {
3462
+ event.preventDefault();
3463
+ const next = flatNodes[index + 1];
3464
+ if (next) moveFocusTo(next.node.id);
3465
+ break;
3466
+ }
3467
+ case "ArrowUp": {
3468
+ event.preventDefault();
3469
+ const prev = flatNodes[index - 1];
3470
+ if (prev) moveFocusTo(prev.node.id);
3471
+ break;
3472
+ }
3473
+ case "Home": {
3474
+ event.preventDefault();
3475
+ if (flatNodes[0]) moveFocusTo(flatNodes[0].node.id);
3476
+ break;
3477
+ }
3478
+ case "End": {
3479
+ event.preventDefault();
3480
+ const last = flatNodes[flatNodes.length - 1];
3481
+ if (last) moveFocusTo(last.node.id);
3482
+ break;
3483
+ }
3484
+ case "ArrowRight": {
3485
+ if (!hasChildren) break;
3486
+ event.preventDefault();
3487
+ if (!isExpanded) {
3488
+ expandNode(node);
3489
+ } else {
3490
+ const next = flatNodes[index + 1];
3491
+ if (next && next.depth === depth + 1) moveFocusTo(next.node.id);
3492
+ }
3493
+ break;
3494
+ }
3495
+ case "ArrowLeft": {
3496
+ event.preventDefault();
3497
+ if (isExpanded && hasChildren) {
3498
+ collapseNode(node);
3499
+ } else {
3500
+ const parentId = nodeIndex.get(node.id)?.parentId;
3501
+ if (parentId) moveFocusTo(parentId);
3502
+ }
3503
+ break;
3504
+ }
3505
+ case " ":
3506
+ case "Spacebar": {
3507
+ if (selectable === "none") break;
3508
+ event.preventDefault();
3509
+ toggleSelect(node);
3510
+ break;
3511
+ }
3512
+ default:
3513
+ return;
3514
+ }
3515
+ }
3516
+ const totalHeight = flatNodes.length * rowHeight;
3517
+ const overscan = 5;
3518
+ const visibleCount = Math.ceil(height / rowHeight) + overscan * 2;
3519
+ const startIndex = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);
3520
+ const endIndex = Math.min(flatNodes.length, startIndex + visibleCount);
3521
+ const visibleFlatNodes = flatNodes.slice(startIndex, endIndex);
3522
+ return /* @__PURE__ */ jsx41(
3523
+ "div",
3524
+ {
3525
+ ref: containerRef,
3526
+ className: scroller,
3527
+ style: { height },
3528
+ onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
3529
+ role: "tree",
3530
+ children: /* @__PURE__ */ jsx41("div", { className: root4, style: { height: totalHeight }, children: visibleFlatNodes.map((flat, i) => {
3531
+ const index = startIndex + i;
3532
+ const { node, depth, hasChildren, isExpanded } = flat;
3533
+ const isLoading = loadingIds.has(node.id);
3534
+ const isSelected = selectedSet.has(node.id);
3535
+ const isIndeterminate = selectable === "multi" && !isSelected && hasAnyDescendantSelected(node, loadedChildren, selectedSet);
3536
+ return /* @__PURE__ */ jsx41(
3537
+ "div",
3538
+ {
3539
+ ref: (el) => {
3540
+ if (el) {
3541
+ rowRefs.current.set(node.id, el);
3542
+ } else {
3543
+ rowRefs.current.delete(node.id);
3544
+ }
3545
+ },
3546
+ className: row2,
3547
+ style: { top: index * rowHeight, height: rowHeight },
3548
+ role: "treeitem",
3549
+ "aria-expanded": hasChildren ? isExpanded : void 0,
3550
+ "aria-level": depth + 1,
3551
+ "aria-selected": selectable !== "none" ? isSelected : void 0,
3552
+ tabIndex: node.id === focusedId ? 0 : -1,
3553
+ onFocus: () => setFocusedId(node.id),
3554
+ onKeyDown: (e) => handleKeyDown(e, flat),
3555
+ onClick: () => setFocusedId(node.id),
3556
+ children: /* @__PURE__ */ jsxs24(
3557
+ "div",
3558
+ {
3559
+ className: rowContent,
3560
+ style: { paddingLeft: `calc(${depth} * ${vars2.space.lg})` },
3561
+ children: [
3562
+ hasChildren ? isLoading ? /* @__PURE__ */ jsx41("span", { className: loadingSlot, children: /* @__PURE__ */ jsx41(Spinner, { size: "sm" }) }) : /* @__PURE__ */ jsx41(
3563
+ "button",
3564
+ {
3565
+ type: "button",
3566
+ className: chevron2,
3567
+ "data-expanded": isExpanded || void 0,
3568
+ onMouseDown: (e) => e.preventDefault(),
3569
+ onClick: (e) => {
3570
+ e.stopPropagation();
3571
+ toggleExpand(flat);
3572
+ },
3573
+ "aria-hidden": "true",
3574
+ tabIndex: -1,
3575
+ children: "\u203A"
3576
+ }
3577
+ ) : /* @__PURE__ */ jsx41("span", { className: chevron2, "aria-hidden": "true" }),
3578
+ selectable === "multi" ? /* @__PURE__ */ jsx41(
3579
+ "span",
3580
+ {
3581
+ className: checkboxCell2,
3582
+ onMouseDown: (e) => e.preventDefault(),
3583
+ onClick: (e) => e.stopPropagation(),
3584
+ children: /* @__PURE__ */ jsx41(
3585
+ Checkbox,
3586
+ {
3587
+ checked: isSelected,
3588
+ indeterminate: isIndeterminate,
3589
+ onChange: () => toggleSelect(node),
3590
+ tabIndex: -1,
3591
+ "aria-label": `Select ${typeof node.label === "string" ? node.label : node.id}`
3592
+ }
3593
+ )
3594
+ }
3595
+ ) : null,
3596
+ /* @__PURE__ */ jsx41("span", { className: label3, "data-selected": isSelected || void 0, children: node.label })
3597
+ ]
3598
+ }
3599
+ )
3600
+ },
3601
+ node.id
3602
+ );
3603
+ }) })
3604
+ }
3605
+ );
3606
+ }
3607
+
3209
3608
  // src/index.ts
3210
- import { vars as vars2, defaultThemeClass, defaultThemeVars } from "@vesture/tokens";
3609
+ import { vars as vars3, defaultThemeClass, defaultThemeVars } from "@vesture/tokens";
3211
3610
  export {
3212
3611
  Accordion2 as Accordion,
3213
3612
  AccordionContent,
@@ -3249,9 +3648,10 @@ export {
3249
3648
  Textarea,
3250
3649
  ToastProvider,
3251
3650
  Tooltip,
3651
+ TreeView,
3252
3652
  defaultThemeClass,
3253
3653
  defaultThemeVars,
3254
3654
  useToast,
3255
- vars2 as vars
3655
+ vars3 as vars
3256
3656
  };
3257
3657
  //# sourceMappingURL=index.js.map