@bmsuisse/datagrid 0.30.0 → 0.31.1

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
@@ -653,11 +653,24 @@ function toDate(value) {
653
653
  }
654
654
  return null;
655
655
  }
656
+ var SWISS_CURRENCY_MARKER = /^(?:chf|s?fr\.?)\s+|\s+(?:chf|s?fr\.?)$/gi;
657
+ var SWISS_THOUSANDS_SEPARATOR = /(\d)['’](\d{3})/;
658
+ function stripSwissNumberFormatting(raw) {
659
+ const withoutCurrency = raw.replace(SWISS_CURRENCY_MARKER, "");
660
+ let stripped = withoutCurrency;
661
+ while (SWISS_THOUSANDS_SEPARATOR.test(stripped)) {
662
+ stripped = stripped.replace(SWISS_THOUSANDS_SEPARATOR, "$1$2");
663
+ }
664
+ return stripped;
665
+ }
656
666
  function toNumber(value) {
657
667
  if (typeof value === "number") return Number.isNaN(value) ? null : value;
658
668
  if (typeof value === "string" && value.trim() !== "") {
659
- const parsed = Number(value);
660
- return Number.isNaN(parsed) ? null : parsed;
669
+ const trimmed = value.trim();
670
+ const parsed = Number(trimmed);
671
+ if (!Number.isNaN(parsed)) return parsed;
672
+ const swiss = Number(stripSwissNumberFormatting(trimmed));
673
+ return Number.isNaN(swiss) ? null : swiss;
661
674
  }
662
675
  return null;
663
676
  }
@@ -1464,25 +1477,36 @@ function EnumEditor({
1464
1477
  value,
1465
1478
  onChange,
1466
1479
  error,
1467
- autoFocus
1480
+ autoFocus,
1481
+ open,
1482
+ onOpenChange
1468
1483
  }) {
1469
1484
  const errorId = editErrorId(rowId, column.id);
1470
1485
  return /* @__PURE__ */ jsxs11("div", { children: [
1471
- /* @__PURE__ */ jsxs11(Select, { value: value === null || value === void 0 ? "" : String(value), onValueChange: onChange, children: [
1472
- /* @__PURE__ */ jsx18(
1473
- SelectTrigger,
1474
- {
1475
- className: "h-8",
1476
- "aria-label": `Edit ${column.header}`,
1477
- "aria-invalid": error ? true : void 0,
1478
- "aria-describedby": error ? errorId : void 0,
1479
- "data-testid": `edit-${rowId}-${column.id}`,
1480
- autoFocus,
1481
- children: /* @__PURE__ */ jsx18(SelectValue, {})
1482
- }
1483
- ),
1484
- /* @__PURE__ */ jsx18(SelectContent, { children: column.options.map((option) => /* @__PURE__ */ jsx18(SelectItem, { value: option.value, children: option.label }, option.value)) })
1485
- ] }),
1486
+ /* @__PURE__ */ jsxs11(
1487
+ Select,
1488
+ {
1489
+ value: value === null || value === void 0 ? "" : String(value),
1490
+ onValueChange: onChange,
1491
+ open,
1492
+ onOpenChange,
1493
+ children: [
1494
+ /* @__PURE__ */ jsx18(
1495
+ SelectTrigger,
1496
+ {
1497
+ className: "h-8",
1498
+ "aria-label": `Edit ${column.header}`,
1499
+ "aria-invalid": error ? true : void 0,
1500
+ "aria-describedby": error ? errorId : void 0,
1501
+ "data-testid": `edit-${rowId}-${column.id}`,
1502
+ autoFocus,
1503
+ children: /* @__PURE__ */ jsx18(SelectValue, {})
1504
+ }
1505
+ ),
1506
+ /* @__PURE__ */ jsx18(SelectContent, { children: column.options.map((option) => /* @__PURE__ */ jsx18(SelectItem, { value: option.value, children: option.label }, option.value)) })
1507
+ ]
1508
+ }
1509
+ ),
1486
1510
  error && /* @__PURE__ */ jsx18(EditFieldError, { id: errorId, message: error })
1487
1511
  ] });
1488
1512
  }
@@ -2169,6 +2193,8 @@ function useCellEditingState(cellEditing, getRowId) {
2169
2193
  return override.value;
2170
2194
  }
2171
2195
  const ctx = cellEditing ? {
2196
+ alwaysEdit: cellEditing.alwaysEdit ?? false,
2197
+ disabled: cellEditing.disabled ?? false,
2172
2198
  editingCell,
2173
2199
  initialText,
2174
2200
  hasDraft,
@@ -2182,6 +2208,7 @@ function useCellEditingState(cellEditing, getRowId) {
2182
2208
  onCommitEdit,
2183
2209
  onCommitValue,
2184
2210
  onSetError,
2211
+ applyChange: (change) => applyChanges([change]),
2185
2212
  consumeJustCommitted
2186
2213
  } : void 0;
2187
2214
  const ctxRef = useRef8(void 0);
@@ -2341,7 +2368,12 @@ function matchEnumOption(options, raw) {
2341
2368
  const lowered = trimmed.toLowerCase();
2342
2369
  return options.find((option) => option.label.toLowerCase() === lowered)?.value;
2343
2370
  }
2371
+ var EXCEL_ERROR_STRINGS = /* @__PURE__ */ new Set(["#N/A", "#DIV/0!", "#REF!", "#VALUE!", "#NAME?", "#NULL!", "#NUM!", "#SPILL!", "#CALC!"]);
2372
+ function isExcelErrorString(raw) {
2373
+ return EXCEL_ERROR_STRINGS.has(raw.trim());
2374
+ }
2344
2375
  function coerceValueForColumn(column, raw) {
2376
+ if (column.type !== "string" && isExcelErrorString(raw)) return void 0;
2345
2377
  switch (column.type) {
2346
2378
  case "string":
2347
2379
  return { value: raw };
@@ -2400,7 +2432,8 @@ function rangeToTsv(range, rows, rowIds, columns, columnIds, getRowId) {
2400
2432
  return lines.join("\n");
2401
2433
  }
2402
2434
  function parseTsv(text) {
2403
- const normalized = text.replace(/\r\n/g, "\n");
2435
+ const withoutBom = text.startsWith("\uFEFF") ? text.slice(1) : text;
2436
+ const normalized = withoutBom.replace(/\r\n/g, "\n");
2404
2437
  const rows = [];
2405
2438
  let row = [];
2406
2439
  let field = "";
@@ -2770,12 +2803,123 @@ import { BarsArrowDownIcon, BarsArrowUpIcon, FunnelIcon as FunnelIcon7 } from "@
2770
2803
  import { columnResizingFeature, columnSizingFeature, flexRender, tableFeatures, useTable } from "@tanstack/react-table";
2771
2804
  import { useVirtualizer } from "@tanstack/react-virtual";
2772
2805
  import { ChevronRight, Loader2 } from "lucide-react";
2773
- import { Fragment as Fragment3, useEffect as useEffect9, useMemo as useMemo8, useRef as useRef11, useState as useState13 } from "react";
2806
+ import { Fragment as Fragment3, useEffect as useEffect10, useMemo as useMemo8, useRef as useRef11, useState as useState14 } from "react";
2807
+
2808
+ // src/cell-editing/renderAlwaysEditCell.tsx
2809
+ import {
2810
+ useEffect as useEffect7,
2811
+ useState as useState10
2812
+ } from "react";
2813
+ import { jsx as jsx32 } from "react/jsx-runtime";
2814
+ function AlwaysEditCell(props) {
2815
+ const { column, row, rawValue, ctx, atomicGesture } = props;
2816
+ const rowId = ctx.getRowId(row);
2817
+ const resolvedValue = ctx.resolveValue(rowId, column.id, rawValue);
2818
+ const [draft, setDraft] = useState10(void 0);
2819
+ const [enumOpen, setEnumOpen] = useState10(false);
2820
+ const isBuiltinEnum = column.type === "enum" && !column.renderEditCell;
2821
+ const shouldOpenEnumNow = isBuiltinEnum && (atomicGesture?.shouldOpenEnum(rowId, column.id) ?? false);
2822
+ useEffect7(() => {
2823
+ if (shouldOpenEnumNow) setEnumOpen(true);
2824
+ }, [shouldOpenEnumNow]);
2825
+ if (!isEditable(column, row)) {
2826
+ return /* @__PURE__ */ jsx32("span", { "data-testid": `cell-${rowId}-${column.id}`, className: "block w-full", children: column.cell ? column.cell(resolvedValue, row) : defaultFormat(column, resolvedValue) });
2827
+ }
2828
+ const isAtomic = isAtomicEditorType(column.type);
2829
+ const error = ctx.getError(rowId, column.id);
2830
+ const draftIsFresh = draft !== void 0 && resolvedValue === draft.baseline;
2831
+ const currentValue = draftIsFresh ? draft.value : resolvedValue;
2832
+ function validate(candidate) {
2833
+ return column.validateEdit?.(candidate, row);
2834
+ }
2835
+ function commit(value) {
2836
+ ctx.applyChange({ rowId, row, columnId: column.id, previousValue: resolvedValue, value });
2837
+ setDraft(void 0);
2838
+ }
2839
+ const onChange = (next) => {
2840
+ const message = validate(next);
2841
+ ctx.onSetError(rowId, column.id, message);
2842
+ if (isAtomic && !message) {
2843
+ commit(next);
2844
+ return;
2845
+ }
2846
+ setDraft({ value: next, baseline: resolvedValue });
2847
+ };
2848
+ function handleKeyDown(event) {
2849
+ if (event.key === "Escape") {
2850
+ event.stopPropagation();
2851
+ ctx.onSetError(rowId, column.id, void 0);
2852
+ setDraft(void 0);
2853
+ return;
2854
+ }
2855
+ const isMultilineNewline = column.type === "string" && column.multiline && event.key === "Enter" && event.shiftKey;
2856
+ if (isMultilineNewline) return;
2857
+ if (isAtomic || event.key !== "Enter" && event.key !== "Tab") return;
2858
+ if (!draftIsFresh) return;
2859
+ const message = validate(draft.value);
2860
+ if (message) {
2861
+ event.stopPropagation();
2862
+ return;
2863
+ }
2864
+ commit(draft.value);
2865
+ }
2866
+ function handleBlur() {
2867
+ if (isAtomic || !draftIsFresh) return;
2868
+ if (validate(draft.value)) setDraft(void 0);
2869
+ else commit(draft.value);
2870
+ }
2871
+ function suppressNative(event) {
2872
+ event.stopPropagation();
2873
+ event.nativeEvent.stopImmediatePropagation();
2874
+ return true;
2875
+ }
2876
+ function handleModifierCapture(event) {
2877
+ if (!isAtomic || isBuiltinEnum) return false;
2878
+ if (!(event.shiftKey || event.ctrlKey || event.metaKey)) return false;
2879
+ return suppressNative(event);
2880
+ }
2881
+ function handleEnumOpenCapture(event) {
2882
+ if (!isBuiltinEnum || enumOpen) return false;
2883
+ return suppressNative(event);
2884
+ }
2885
+ function handlePointerDownCapture(event) {
2886
+ const suppressed = handleModifierCapture(event) || handleEnumOpenCapture(event);
2887
+ if (suppressed) atomicGesture?.registerSelection(rowId, column.id, event.shiftKey);
2888
+ }
2889
+ function handleClickCapture(event) {
2890
+ handleModifierCapture(event);
2891
+ handleEnumOpenCapture(event);
2892
+ }
2893
+ return /* @__PURE__ */ jsx32(
2894
+ "fieldset",
2895
+ {
2896
+ disabled: ctx.disabled,
2897
+ onKeyDown: handleKeyDown,
2898
+ onBlur: handleBlur,
2899
+ onPointerDownCapture: handlePointerDownCapture,
2900
+ onClickCapture: handleClickCapture,
2901
+ className: "contents",
2902
+ children: column.type === "enum" && !column.renderEditCell ? /* @__PURE__ */ jsx32(
2903
+ EnumEditor,
2904
+ {
2905
+ column,
2906
+ rowId,
2907
+ value: currentValue,
2908
+ onChange,
2909
+ error,
2910
+ autoFocus: false,
2911
+ open: enumOpen,
2912
+ onOpenChange: setEnumOpen
2913
+ }
2914
+ ) : column.renderEditCell ? column.renderEditCell(currentValue, row, onChange, error, false) : renderDefaultEditWidget(column, rowId, currentValue, onChange, error, false)
2915
+ }
2916
+ );
2917
+ }
2774
2918
 
2775
2919
  // src/hooks/useGroupExpansion.ts
2776
- import { useCallback as useCallback3, useState as useState10 } from "react";
2920
+ import { useCallback as useCallback3, useState as useState11 } from "react";
2777
2921
  function useGroupExpansion(defaultGroupsExpanded, controlledExpandedGroups, onExpandedGroupsChange) {
2778
- const [internalExpandedGroups, setInternalExpandedGroups] = useState10({});
2922
+ const [internalExpandedGroups, setInternalExpandedGroups] = useState11({});
2779
2923
  const expandedGroups = controlledExpandedGroups ?? internalExpandedGroups;
2780
2924
  const updateExpandedGroups = useCallback3(
2781
2925
  (next) => {
@@ -2798,10 +2942,10 @@ function useGroupExpansion(defaultGroupsExpanded, controlledExpandedGroups, onEx
2798
2942
  }
2799
2943
 
2800
2944
  // src/hooks/useStickyGroupHeaderTop.ts
2801
- import { useLayoutEffect as useLayoutEffect3, useRef as useRef9, useState as useState11 } from "react";
2945
+ import { useLayoutEffect as useLayoutEffect3, useRef as useRef9, useState as useState12 } from "react";
2802
2946
  function useStickyGroupHeaderTop(enabled) {
2803
2947
  const theadRef = useRef9(null);
2804
- const [groupHeaderTop, setGroupHeaderTop] = useState11(0);
2948
+ const [groupHeaderTop, setGroupHeaderTop] = useState12(0);
2805
2949
  useLayoutEffect3(() => {
2806
2950
  if (!enabled) return;
2807
2951
  const theadEl = theadRef.current;
@@ -2865,15 +3009,15 @@ function processClientData(data, state) {
2865
3009
  }
2866
3010
 
2867
3011
  // src/grid/useGridState.ts
2868
- import { useCallback as useCallback4, useEffect as useEffect8, useMemo as useMemo7, useState as useState12 } from "react";
3012
+ import { useCallback as useCallback4, useEffect as useEffect9, useMemo as useMemo7, useState as useState13 } from "react";
2869
3013
 
2870
3014
  // src/hooks/useDebouncedCallback.ts
2871
- import { useEffect as useEffect7, useMemo as useMemo6, useRef as useRef10 } from "react";
3015
+ import { useEffect as useEffect8, useMemo as useMemo6, useRef as useRef10 } from "react";
2872
3016
  function useDebouncedCallback(callback, delayMs) {
2873
3017
  const callbackRef = useRef10(callback);
2874
3018
  callbackRef.current = callback;
2875
3019
  const timeoutRef = useRef10(void 0);
2876
- useEffect7(() => {
3020
+ useEffect8(() => {
2877
3021
  return () => {
2878
3022
  clearTimeout(timeoutRef.current);
2879
3023
  };
@@ -2902,10 +3046,12 @@ function nextSortDir(current, descFirst) {
2902
3046
  if (current === first) return second;
2903
3047
  return void 0;
2904
3048
  }
2905
- function useGridState(dataSource, initialState, externalState) {
2906
- const [state, setState] = useState12(
2907
- () => externalState ?? { ...DEFAULT_STATE, ...initialState }
2908
- );
3049
+ function useGridState(dataSource, initialState, externalState, showPagination = true) {
3050
+ const [state, setState] = useState13(() => {
3051
+ if (externalState) return externalState;
3052
+ const pageSize = initialState?.pageSize ?? (showPagination ? DEFAULT_STATE.pageSize : Number.MAX_SAFE_INTEGER);
3053
+ return { ...DEFAULT_STATE, ...initialState, pageSize };
3054
+ });
2909
3055
  const notifyRaw = useCallback4(
2910
3056
  (next) => {
2911
3057
  if (dataSource.mode === "server") dataSource.onStateChange(next);
@@ -2923,7 +3069,7 @@ function useGridState(dataSource, initialState, externalState) {
2923
3069
  },
2924
3070
  [notifyRaw, cancelDebouncedNotify]
2925
3071
  );
2926
- useEffect8(() => {
3072
+ useEffect9(() => {
2927
3073
  if (!externalState) return;
2928
3074
  cancelDebouncedNotify();
2929
3075
  setState(externalState);
@@ -2992,12 +3138,16 @@ function useGridState(dataSource, initialState, externalState) {
2992
3138
  }
2993
3139
 
2994
3140
  // src/grid/DataGrid.tsx
2995
- import { Fragment as Fragment4, jsx as jsx32, jsxs as jsxs20 } from "react/jsx-runtime";
3141
+ import { Fragment as Fragment4, jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
2996
3142
  var gridTableFeatures = tableFeatures({ columnSizingFeature, columnResizingFeature });
2997
3143
  var DEFAULT_COLUMN_SIZE = 150;
2998
3144
  var DETAIL_COLUMN_WIDTH = 44;
2999
3145
  var ROW_ACTIONS_COLUMN_WIDTH = 44;
3000
3146
  var EMPTY_ID_ARRAY = [];
3147
+ function isNativeTextFieldTarget(event) {
3148
+ const tag = event.target?.tagName;
3149
+ return tag === "INPUT" || tag === "TEXTAREA";
3150
+ }
3001
3151
  var STRUCTURAL_ZEBRA_BG_CLASS = "bg-[color-mix(in_srgb,var(--color-foreground)_5%,var(--color-background))]";
3002
3152
  function pinnedOffsets(visibleColumns, side, getSize, leadingOffset) {
3003
3153
  const offsets = /* @__PURE__ */ new Map();
@@ -3024,13 +3174,26 @@ function computeHeaderRuns(columns) {
3024
3174
  }
3025
3175
  return runs;
3026
3176
  }
3027
- function toTanstackColumns(columns, editingCtxRef, cellEditingCtxRef) {
3177
+ function toTanstackColumns(columns, editingCtxRef, cellEditingCtxRef, atomicGestureRef) {
3028
3178
  return columns.map((column) => ({
3029
3179
  id: column.id,
3030
3180
  header: column.header,
3031
3181
  size: column.width,
3032
3182
  accessorFn: (row) => getColumnValue(column, row),
3033
- cell: (info) => cellEditingCtxRef.current ? renderCellModeCell(column, info.row.original, info.getValue(), cellEditingCtxRef.current) : renderEditableCell(column, info.row.original, info.getValue(), editingCtxRef.current)
3183
+ cell: (info) => {
3184
+ const cellEditingCtx = cellEditingCtxRef.current;
3185
+ if (!cellEditingCtx) return renderEditableCell(column, info.row.original, info.getValue(), editingCtxRef.current);
3186
+ return cellEditingCtx.alwaysEdit ? /* @__PURE__ */ jsx33(
3187
+ AlwaysEditCell,
3188
+ {
3189
+ column,
3190
+ row: info.row.original,
3191
+ rawValue: info.getValue(),
3192
+ ctx: cellEditingCtx,
3193
+ atomicGesture: atomicGestureRef.current
3194
+ }
3195
+ ) : renderCellModeCell(column, info.row.original, info.getValue(), cellEditingCtx);
3196
+ }
3034
3197
  }));
3035
3198
  }
3036
3199
  function DataGrid({
@@ -3069,21 +3232,22 @@ function DataGrid({
3069
3232
  const { state, filtersByColumn, setColumnFilter, toggleSort, setPage } = useGridState(
3070
3233
  dataSource,
3071
3234
  initialState,
3072
- gridState
3235
+ gridState,
3236
+ showPagination
3073
3237
  );
3074
- const [internalSelectedIds, setInternalSelectedIds] = useState13(/* @__PURE__ */ new Set());
3238
+ const [internalSelectedIds, setInternalSelectedIds] = useState14(/* @__PURE__ */ new Set());
3075
3239
  const selectedIds = controlledSelectedIds ?? internalSelectedIds;
3076
3240
  function updateSelectedIds(next) {
3077
3241
  setInternalSelectedIds(next);
3078
3242
  onSelectedIdsChange?.(next);
3079
3243
  }
3080
- const [internalColumnSizing, setInternalColumnSizing] = useState13({});
3244
+ const [internalColumnSizing, setInternalColumnSizing] = useState14({});
3081
3245
  const columnSizing = controlledColumnSizing ?? internalColumnSizing;
3082
3246
  function updateColumnSizing(next) {
3083
3247
  setInternalColumnSizing(next);
3084
3248
  onColumnSizingChange?.(next);
3085
3249
  }
3086
- const [expandedIds, setExpandedIds] = useState13(/* @__PURE__ */ new Set());
3250
+ const [expandedIds, setExpandedIds] = useState14(/* @__PURE__ */ new Set());
3087
3251
  function toggleRowExpanded(id) {
3088
3252
  setExpandedIds((prev) => {
3089
3253
  const next = new Set(prev);
@@ -3105,6 +3269,7 @@ function DataGrid({
3105
3269
  hasCellEditing ? cellEditing : void 0,
3106
3270
  getRowId
3107
3271
  );
3272
+ const atomicGestureRef = useRef11(void 0);
3108
3273
  const visibleColumns = useVisibleColumns(columns, columnVisibility);
3109
3274
  const serverRowCount = dataSource.mode === "server" ? dataSource.rowCount : void 0;
3110
3275
  const { rows, rowCount } = useMemo8(() => {
@@ -3131,7 +3296,7 @@ function DataGrid({
3131
3296
  updateSelectedIds(next);
3132
3297
  }
3133
3298
  const tanstackColumns = useMemo8(
3134
- () => toTanstackColumns(visibleColumns, editingCtxRef, cellEditingCtxRef),
3299
+ () => toTanstackColumns(visibleColumns, editingCtxRef, cellEditingCtxRef, atomicGestureRef),
3135
3300
  [visibleColumns]
3136
3301
  );
3137
3302
  const columnById = useMemo8(() => {
@@ -3258,6 +3423,18 @@ function DataGrid({
3258
3423
  return map;
3259
3424
  }, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
3260
3425
  const tableRows = table.getRowModel().rows;
3426
+ const truncationWarnedRef = useRef11(false);
3427
+ useEffect10(() => {
3428
+ if (process.env.NODE_ENV === "production") return;
3429
+ if (showPagination) return;
3430
+ if (truncationWarnedRef.current) return;
3431
+ if (!gridState && initialState?.pageSize !== void 0) return;
3432
+ if (tableRows.length >= rowCount) return;
3433
+ truncationWarnedRef.current = true;
3434
+ console.warn(
3435
+ `[DataGrid] showPagination={false} but only ${tableRows.length} of ${rowCount} row(s) are rendered, with no pagination UI to reach the rest. Pass an explicit pageSize (via initialState or gridState) that covers every row you want visible, or a dataSource that returns all of them.`
3436
+ );
3437
+ }, [showPagination, gridState, initialState?.pageSize, tableRows.length, rowCount]);
3261
3438
  const groupedBuckets = useMemo8(
3262
3439
  () => groupBy ? groupRows(tableRows, (row) => groupBy(row.original)) : void 0,
3263
3440
  [tableRows, groupBy]
@@ -3290,7 +3467,7 @@ function DataGrid({
3290
3467
  const paddingBottom = virtualItems.length > 0 ? virtualizer.getTotalSize() - virtualItems[virtualItems.length - 1].end : 0;
3291
3468
  const lastVisibleIndex = virtualItems.length > 0 ? virtualItems[virtualItems.length - 1].index : -1;
3292
3469
  const notifiedForLengthRef = useRef11(-1);
3293
- useEffect9(() => {
3470
+ useEffect10(() => {
3294
3471
  if (!virtualize?.onEndReached) return;
3295
3472
  if (lastVisibleIndex < 0 || lastVisibleIndex < flatItems.length - 1) return;
3296
3473
  if (virtualize.hasMore === false) return;
@@ -3316,15 +3493,42 @@ function DataGrid({
3316
3493
  if (index !== void 0) virtualizer.scrollToIndex(index);
3317
3494
  }
3318
3495
  });
3496
+ const clickCellRef = useRef11(null);
3497
+ const dragMovedRef = useRef11(false);
3498
+ const [pendingOpenEnumCell, setPendingOpenEnumCell] = useState14(void 0);
3499
+ atomicGestureRef.current = {
3500
+ shouldOpenEnum: (rowId, columnId) => pendingOpenEnumCell?.rowId === rowId && pendingOpenEnumCell?.columnId === columnId,
3501
+ // Mirrors exactly what `handleCellMouseDown` below does for every other
3502
+ // cell, minus the click-to-edit-only tail end (irrelevant here — always
3503
+ // invoked under `alwaysEdit`, same as that function's own early return
3504
+ // for it) — see `AtomicGestureContext.registerSelection`'s own doc for
3505
+ // why `AlwaysEditCell` can't just rely on that function running on its
3506
+ // own for a gesture it suppresses.
3507
+ registerSelection: (rowId, columnId, extend) => {
3508
+ cellSelection.startSelection({ rowId, columnId }, { extend });
3509
+ clickCellRef.current = { rowId, columnId, extend, wasEditing: false };
3510
+ dragMovedRef.current = false;
3511
+ }
3512
+ };
3513
+ function beginEditFromClick(cell) {
3514
+ if (!cellEditingCtx) return;
3515
+ if (cellEditingCtx.editingCell?.rowId === cell.rowId && cellEditingCtx.editingCell.columnId === cell.columnId) return;
3516
+ const column = columnById.get(cell.columnId);
3517
+ const row = findRowById(cell.rowId);
3518
+ if (!column || row === void 0 || !isEditable(column, row)) return;
3519
+ cellEditingCtx.onBeginEdit(cell);
3520
+ }
3319
3521
  const pendingDragCellRef = useRef11(null);
3320
3522
  const dragRafRef = useRef11(null);
3321
- useEffect9(() => {
3523
+ useEffect10(() => {
3322
3524
  if (!cellSelection.isDragging) return;
3323
3525
  function handleMouseMove(event) {
3324
3526
  const cellEl = event.target?.closest("[data-cell-row]");
3325
3527
  const rowId = cellEl?.dataset.cellRow;
3326
3528
  const columnId = cellEl?.dataset.cellCol;
3327
3529
  if (rowId === void 0 || columnId === void 0) return;
3530
+ const anchor = clickCellRef.current;
3531
+ if (anchor && (rowId !== anchor.rowId || columnId !== anchor.columnId)) dragMovedRef.current = true;
3328
3532
  pendingDragCellRef.current = { rowId, columnId };
3329
3533
  if (dragRafRef.current !== null) return;
3330
3534
  dragRafRef.current = requestAnimationFrame(() => {
@@ -3334,6 +3538,17 @@ function DataGrid({
3334
3538
  }
3335
3539
  function handleMouseUp() {
3336
3540
  cellSelection.endDrag();
3541
+ const clickCell = clickCellRef.current;
3542
+ clickCellRef.current = null;
3543
+ if (!clickCell || clickCell.extend || clickCell.wasEditing || dragMovedRef.current) return;
3544
+ if (cellEditingCtx?.alwaysEdit) {
3545
+ const column = columnById.get(clickCell.columnId);
3546
+ if (column?.type === "enum" && !column.renderEditCell) {
3547
+ setPendingOpenEnumCell({ rowId: clickCell.rowId, columnId: clickCell.columnId });
3548
+ }
3549
+ return;
3550
+ }
3551
+ beginEditFromClick(clickCell);
3337
3552
  }
3338
3553
  window.addEventListener("mousemove", handleMouseMove);
3339
3554
  window.addEventListener("mouseup", handleMouseUp);
@@ -3346,7 +3561,7 @@ function DataGrid({
3346
3561
  }, [cellSelection.isDragging, cellSelection.updateDrag, cellSelection.endDrag]);
3347
3562
  const pendingFillCellRef = useRef11(null);
3348
3563
  const fillRafRef = useRef11(null);
3349
- useEffect9(() => {
3564
+ useEffect10(() => {
3350
3565
  if (!cellSelection.isFillDragging) return;
3351
3566
  function handleMouseMove(event) {
3352
3567
  const cellEl = event.target?.closest("[data-cell-row]");
@@ -3383,7 +3598,7 @@ function DataGrid({
3383
3598
  };
3384
3599
  }, [cellSelection.isFillDragging, cellSelection.updateFillDrag, cellSelection.endFillDrag]);
3385
3600
  const wasEditingCellRef = useRef11(void 0);
3386
- useEffect9(() => {
3601
+ useEffect10(() => {
3387
3602
  const wasEditing = wasEditingCellRef.current !== void 0;
3388
3603
  wasEditingCellRef.current = cellEditingCtx?.editingCell;
3389
3604
  if (!wasEditing || cellEditingCtx?.editingCell) return;
@@ -3399,6 +3614,11 @@ function DataGrid({
3399
3614
  const columnId = cellEl?.dataset.cellCol;
3400
3615
  if (rowId === void 0 || columnId === void 0) return;
3401
3616
  cellSelection.startSelection({ rowId, columnId }, { extend: event.shiftKey });
3617
+ const wasEditing = cellEditingCtx?.editingCell?.rowId === rowId && cellEditingCtx.editingCell.columnId === columnId;
3618
+ clickCellRef.current = { rowId, columnId, extend: event.shiftKey, wasEditing };
3619
+ dragMovedRef.current = false;
3620
+ if (cellEditingCtx?.alwaysEdit) return;
3621
+ if (wasEditing) return;
3402
3622
  scrollRef.current?.focus();
3403
3623
  }
3404
3624
  const CELL_NAV_KEYS = {
@@ -3417,6 +3637,7 @@ function DataGrid({
3417
3637
  }
3418
3638
  function handleCellKeyDown(event) {
3419
3639
  if (!hasCellEditing || !cellEditingCtx) return;
3640
+ if (cellEditingCtx.alwaysEdit) return;
3420
3641
  if (cellEditingCtx.editingCell && !cellEditingCtx.consumeJustCommitted()) return;
3421
3642
  const direction = CELL_NAV_KEYS[event.key];
3422
3643
  if (direction) {
@@ -3445,18 +3666,15 @@ function DataGrid({
3445
3666
  );
3446
3667
  }
3447
3668
  function handleCellDoubleClick(event) {
3448
- if (!hasCellEditing || !cellEditingCtx) return;
3669
+ if (!hasCellEditing || !cellEditingCtx || cellEditingCtx.alwaysEdit) return;
3449
3670
  const cellEl = event.target.closest("[data-cell-row]");
3450
3671
  const rowId = cellEl?.dataset.cellRow;
3451
3672
  const columnId = cellEl?.dataset.cellCol;
3452
3673
  if (rowId === void 0 || columnId === void 0) return;
3453
- const column = columnById.get(columnId);
3454
- const row = findRowById(rowId);
3455
- if (!column || row === void 0 || !isEditable(column, row)) return;
3456
- cellEditingCtx.onBeginEdit({ rowId, columnId });
3674
+ beginEditFromClick({ rowId, columnId });
3457
3675
  }
3458
3676
  function handleCopy(event) {
3459
- if (!hasCellEditing || !cellSelection.selection || cellEditingCtx?.editingCell) return;
3677
+ if (!hasCellEditing || !cellSelection.selection || cellEditingCtx?.editingCell || isNativeTextFieldTarget(event)) return;
3460
3678
  event.preventDefault();
3461
3679
  const text = rangeToTsv(cellSelection.selection, rows, cellEditingRowIds, visibleColumns, cellEditingColumnIds, getRowId);
3462
3680
  event.clipboardData.setData("text/plain", text);
@@ -3473,7 +3691,7 @@ function DataGrid({
3473
3691
  return { rowId, row: rowData, columnId, previousValue: getColumnValue(column, rowData), value: coerced.value };
3474
3692
  }
3475
3693
  function handlePaste(event) {
3476
- if (!hasCellEditing || !cellSelection.selection || cellEditingCtx?.editingCell) return;
3694
+ if (!hasCellEditing || !cellSelection.selection || cellEditingCtx?.editingCell || isNativeTextFieldTarget(event)) return;
3477
3695
  const text = event.clipboardData.getData("text/plain");
3478
3696
  event.preventDefault();
3479
3697
  const normalized = normalizeRange(cellSelection.selection, cellEditingRowIndex, buildIndexMap(cellEditingColumnIds));
@@ -3512,12 +3730,12 @@ function DataGrid({
3512
3730
  "data-testid": rowTestId,
3513
3731
  className: rowProps?.className ? cn("divide-x divide-border", rowProps.className, isOddRow && "bg-foreground/5") : `divide-x divide-border${isOddRow ? " bg-foreground/5" : ""}`,
3514
3732
  children: [
3515
- showDetailColumn && /* @__PURE__ */ jsx32(
3733
+ showDetailColumn && /* @__PURE__ */ jsx33(
3516
3734
  "td",
3517
3735
  {
3518
3736
  className: isOddRow ? detailCellProps.classNameOdd : detailCellProps.className,
3519
3737
  style: detailCellProps.style,
3520
- children: /* @__PURE__ */ jsx32(
3738
+ children: /* @__PURE__ */ jsx33(
3521
3739
  Button,
3522
3740
  {
3523
3741
  type: "button",
@@ -3529,7 +3747,7 @@ function DataGrid({
3529
3747
  stopRowClick(event);
3530
3748
  toggleRowExpanded(row.id);
3531
3749
  },
3532
- children: /* @__PURE__ */ jsx32(
3750
+ children: /* @__PURE__ */ jsx33(
3533
3751
  ChevronRight,
3534
3752
  {
3535
3753
  className: `h-4 w-4 transition-transform${isExpanded ? " rotate-90" : ""}`,
@@ -3540,12 +3758,12 @@ function DataGrid({
3540
3758
  )
3541
3759
  }
3542
3760
  ),
3543
- showSelectionColumn && /* @__PURE__ */ jsx32(
3761
+ showSelectionColumn && /* @__PURE__ */ jsx33(
3544
3762
  "td",
3545
3763
  {
3546
3764
  className: isOddRow ? selectionCellProps.classNameOdd : selectionCellProps.className,
3547
3765
  style: selectionCellProps.style,
3548
- children: /* @__PURE__ */ jsx32(
3766
+ children: /* @__PURE__ */ jsx33(
3549
3767
  "input",
3550
3768
  {
3551
3769
  type: "checkbox",
@@ -3559,7 +3777,7 @@ function DataGrid({
3559
3777
  ),
3560
3778
  row.getAllCells().map((cell) => {
3561
3779
  const cellProps = bodyCellPropsByColumn.get(cell.column.id);
3562
- return /* @__PURE__ */ jsx32(
3780
+ return /* @__PURE__ */ jsx33(
3563
3781
  "td",
3564
3782
  {
3565
3783
  style: cellProps?.style,
@@ -3571,23 +3789,23 @@ function DataGrid({
3571
3789
  cell.id
3572
3790
  );
3573
3791
  }),
3574
- showRowActionsColumn && rowActions && /* @__PURE__ */ jsx32(
3792
+ showRowActionsColumn && rowActions && /* @__PURE__ */ jsx33(
3575
3793
  "td",
3576
3794
  {
3577
3795
  className: isOddRow ? rowActionsCellProps.classNameOdd : rowActionsCellProps.className,
3578
3796
  style: rowActionsCellProps.style,
3579
- children: /* @__PURE__ */ jsx32(ActionsMenu, { items: rowActions, ctx: { row: row.original }, triggerLabel: `Row actions for ${row.id}` })
3797
+ children: /* @__PURE__ */ jsx33(ActionsMenu, { items: rowActions, ctx: { row: row.original }, triggerLabel: `Row actions for ${row.id}` })
3580
3798
  }
3581
3799
  )
3582
3800
  ]
3583
3801
  }
3584
3802
  ),
3585
- showDetailColumn && isExpanded && renderDetail && /* @__PURE__ */ jsx32("tr", { "data-testid": `${rowTestId}-detail`, children: /* @__PURE__ */ jsx32("td", { colSpan: totalColumnCount, className: "border-b border-border p-2", children: renderDetail(row.original) }) })
3803
+ showDetailColumn && isExpanded && renderDetail && /* @__PURE__ */ jsx33("tr", { "data-testid": `${rowTestId}-detail`, children: /* @__PURE__ */ jsx33("td", { colSpan: totalColumnCount, className: "border-b border-border p-2", children: renderDetail(row.original) }) })
3586
3804
  ] }, row.id);
3587
3805
  }
3588
3806
  function renderGroupHeaderTbody(key, rows2, measureRef, dataIndex) {
3589
3807
  const expanded = isGroupExpanded(key);
3590
- return /* @__PURE__ */ jsx32("tbody", { ref: measureRef, "data-index": dataIndex, children: /* @__PURE__ */ jsx32("tr", { "data-testid": `group-header-${key}`, children: /* @__PURE__ */ jsx32(
3808
+ return /* @__PURE__ */ jsx33("tbody", { ref: measureRef, "data-index": dataIndex, children: /* @__PURE__ */ jsx33("tr", { "data-testid": `group-header-${key}`, children: /* @__PURE__ */ jsx33(
3591
3809
  "td",
3592
3810
  {
3593
3811
  colSpan: totalColumnCount,
@@ -3601,7 +3819,7 @@ function DataGrid({
3601
3819
  onClick: () => toggleGroupExpanded(key),
3602
3820
  "aria-expanded": expanded,
3603
3821
  children: [
3604
- /* @__PURE__ */ jsx32(
3822
+ /* @__PURE__ */ jsx33(
3605
3823
  ChevronRight,
3606
3824
  {
3607
3825
  className: `h-4 w-4 shrink-0 transition-transform${expanded ? " rotate-90" : ""}`,
@@ -3641,7 +3859,7 @@ function DataGrid({
3641
3859
  // `overflow-x-auto` div scrolling horizontally) fighting over which one
3642
3860
  // a sticky header/pinned column is actually stuck relative to.
3643
3861
  /* @__PURE__ */ jsxs20("div", { className: "flex h-full min-h-0 flex-col gap-2", "data-testid": "datagrid", children: [
3644
- editing && pendingEdits.size > 0 && /* @__PURE__ */ jsx32(
3862
+ editing && pendingEdits.size > 0 && /* @__PURE__ */ jsx33(
3645
3863
  EditingBar,
3646
3864
  {
3647
3865
  editing,
@@ -3656,15 +3874,15 @@ function DataGrid({
3656
3874
  selectedRows.length,
3657
3875
  " selected"
3658
3876
  ] }),
3659
- /* @__PURE__ */ jsx32(ActionsMenu, { items: headerActions, ctx: { selectedRows }, triggerLabel: "Bulk actions" })
3877
+ /* @__PURE__ */ jsx33(ActionsMenu, { items: headerActions, ctx: { selectedRows }, triggerLabel: "Bulk actions" })
3660
3878
  ] }),
3661
3879
  /* @__PURE__ */ jsxs20("div", { className: "relative min-h-0 flex-1", children: [
3662
- loading && /* @__PURE__ */ jsx32(
3880
+ loading && /* @__PURE__ */ jsx33(
3663
3881
  "div",
3664
3882
  {
3665
3883
  "data-testid": "datagrid-loading-overlay",
3666
3884
  className: "absolute inset-0 z-30 flex items-center justify-center bg-background/60",
3667
- children: /* @__PURE__ */ jsx32(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground", "aria-hidden": true })
3885
+ children: /* @__PURE__ */ jsx33(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground", "aria-hidden": true })
3668
3886
  }
3669
3887
  ),
3670
3888
  /* @__PURE__ */ jsxs20(
@@ -3703,8 +3921,8 @@ function DataGrid({
3703
3921
  onClick: (event) => toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true),
3704
3922
  children: [
3705
3923
  headerContent,
3706
- sortEntry?.dir === "asc" && /* @__PURE__ */ jsx32(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
3707
- sortEntry?.dir === "desc" && /* @__PURE__ */ jsx32(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
3924
+ sortEntry?.dir === "asc" && /* @__PURE__ */ jsx33(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
3925
+ sortEntry?.dir === "desc" && /* @__PURE__ */ jsx33(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
3708
3926
  ]
3709
3927
  }
3710
3928
  ) : (
@@ -3713,10 +3931,10 @@ function DataGrid({
3713
3931
  // silently breaking any interactive content a column's own `renderHeader`
3714
3932
  // embeds directly (bare filter inputs, "select/reject all" buttons) —
3715
3933
  // clicks never reach them at all, even though nothing looks wrong visually.
3716
- /* @__PURE__ */ jsx32("div", { className: "flex items-center gap-1 px-1", children: headerContent })
3934
+ /* @__PURE__ */ jsx33("div", { className: "flex items-center gap-1 px-1", children: headerContent })
3717
3935
  ),
3718
3936
  filterable && /* @__PURE__ */ jsxs20(Popover, { children: [
3719
- /* @__PURE__ */ jsx32(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx32(Button, { variant: "ghost", size: "icon", "aria-label": `Filter ${column.header}`, children: /* @__PURE__ */ jsx32(
3937
+ /* @__PURE__ */ jsx33(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx33(Button, { variant: "ghost", size: "icon", "aria-label": `Filter ${column.header}`, children: /* @__PURE__ */ jsx33(
3720
3938
  FunnelIcon7,
3721
3939
  {
3722
3940
  className: cn(
@@ -3726,10 +3944,10 @@ function DataGrid({
3726
3944
  "aria-hidden": true
3727
3945
  }
3728
3946
  ) }) }),
3729
- /* @__PURE__ */ jsx32(PopoverContent, { children: renderFilterWidget(column) })
3947
+ /* @__PURE__ */ jsx33(PopoverContent, { children: renderFilterWidget(column) })
3730
3948
  ] })
3731
3949
  ] }),
3732
- enableColumnResizing && /* @__PURE__ */ jsx32(
3950
+ enableColumnResizing && /* @__PURE__ */ jsx33(
3733
3951
  "div",
3734
3952
  {
3735
3953
  "data-testid": `resize-handle-${column.id}`,
@@ -3742,7 +3960,7 @@ function DataGrid({
3742
3960
  }
3743
3961
  return /* @__PURE__ */ jsxs20(Fragment3, { children: [
3744
3962
  hasHeaderGroups && /* @__PURE__ */ jsxs20("tr", { "data-testid": "header-group-row", className: "divide-x divide-border", children: [
3745
- showDetailColumn && /* @__PURE__ */ jsx32(
3963
+ showDetailColumn && /* @__PURE__ */ jsx33(
3746
3964
  "th",
3747
3965
  {
3748
3966
  rowSpan: 2,
@@ -3751,13 +3969,13 @@ function DataGrid({
3751
3969
  "aria-hidden": true
3752
3970
  }
3753
3971
  ),
3754
- showSelectionColumn && /* @__PURE__ */ jsx32(
3972
+ showSelectionColumn && /* @__PURE__ */ jsx33(
3755
3973
  "th",
3756
3974
  {
3757
3975
  rowSpan: 2,
3758
3976
  className: selectionHeaderCellProps.className,
3759
3977
  style: selectionHeaderCellProps.style,
3760
- children: /* @__PURE__ */ jsx32(
3978
+ children: /* @__PURE__ */ jsx33(
3761
3979
  "input",
3762
3980
  {
3763
3981
  type: "checkbox",
@@ -3769,7 +3987,7 @@ function DataGrid({
3769
3987
  }
3770
3988
  ),
3771
3989
  headerRuns.map(
3772
- (run) => run.kind === "single" ? renderLeafHeaderCell(run.column, 2) : /* @__PURE__ */ jsx32(
3990
+ (run) => run.kind === "single" ? renderLeafHeaderCell(run.column, 2) : /* @__PURE__ */ jsx33(
3773
3991
  "th",
3774
3992
  {
3775
3993
  colSpan: run.columns.length,
@@ -3779,7 +3997,7 @@ function DataGrid({
3779
3997
  `group-${run.label}-${run.columns[0].id}`
3780
3998
  )
3781
3999
  ),
3782
- showRowActionsColumn && /* @__PURE__ */ jsx32(
4000
+ showRowActionsColumn && /* @__PURE__ */ jsx33(
3783
4001
  "th",
3784
4002
  {
3785
4003
  rowSpan: 2,
@@ -3790,7 +4008,7 @@ function DataGrid({
3790
4008
  )
3791
4009
  ] }, `${headerGroup.id}-groups`),
3792
4010
  /* @__PURE__ */ jsxs20("tr", { className: "divide-x divide-border", children: [
3793
- !hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx32(
4011
+ !hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx33(
3794
4012
  "th",
3795
4013
  {
3796
4014
  className: detailHeaderCellProps.className,
@@ -3798,12 +4016,12 @@ function DataGrid({
3798
4016
  "aria-hidden": true
3799
4017
  }
3800
4018
  ),
3801
- !hasHeaderGroups && showSelectionColumn && /* @__PURE__ */ jsx32(
4019
+ !hasHeaderGroups && showSelectionColumn && /* @__PURE__ */ jsx33(
3802
4020
  "th",
3803
4021
  {
3804
4022
  className: selectionHeaderCellProps.className,
3805
4023
  style: selectionHeaderCellProps.style,
3806
- children: /* @__PURE__ */ jsx32(
4024
+ children: /* @__PURE__ */ jsx33(
3807
4025
  "input",
3808
4026
  {
3809
4027
  type: "checkbox",
@@ -3819,7 +4037,7 @@ function DataGrid({
3819
4037
  if (!column || hasHeaderGroups && !column.headerGroup) return null;
3820
4038
  return renderLeafHeaderCell(column);
3821
4039
  }),
3822
- !hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */ jsx32(
4040
+ !hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */ jsx33(
3823
4041
  "th",
3824
4042
  {
3825
4043
  className: rowActionsHeaderCellProps.className,
@@ -3831,8 +4049,8 @@ function DataGrid({
3831
4049
  ] }, headerGroup.id);
3832
4050
  }),
3833
4051
  hasFilterRow && /* @__PURE__ */ jsxs20("tr", { "data-testid": "filter-row", className: "divide-x divide-border", children: [
3834
- showDetailColumn && /* @__PURE__ */ jsx32("th", { className: detailHeaderCellProps.className, style: detailHeaderCellProps.style }),
3835
- showSelectionColumn && /* @__PURE__ */ jsx32(
4052
+ showDetailColumn && /* @__PURE__ */ jsx33("th", { className: detailHeaderCellProps.className, style: detailHeaderCellProps.style }),
4053
+ showSelectionColumn && /* @__PURE__ */ jsx33(
3836
4054
  "th",
3837
4055
  {
3838
4056
  className: selectionHeaderCellProps.className,
@@ -3841,9 +4059,9 @@ function DataGrid({
3841
4059
  ),
3842
4060
  visibleColumns.map((column) => {
3843
4061
  const cellProps = filterHeaderCellPropsByColumn.get(column.id);
3844
- return /* @__PURE__ */ jsx32("th", { style: cellProps?.style, className: cellProps?.className, children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null }, column.id);
4062
+ return /* @__PURE__ */ jsx33("th", { style: cellProps?.style, className: cellProps?.className, children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null }, column.id);
3845
4063
  }),
3846
- showRowActionsColumn && /* @__PURE__ */ jsx32(
4064
+ showRowActionsColumn && /* @__PURE__ */ jsx33(
3847
4065
  "th",
3848
4066
  {
3849
4067
  className: rowActionsHeaderCellProps.className,
@@ -3852,8 +4070,8 @@ function DataGrid({
3852
4070
  )
3853
4071
  ] })
3854
4072
  ] }),
3855
- rows.length === 0 ? /* @__PURE__ */ jsx32("tbody", { children: /* @__PURE__ */ jsx32("tr", { children: /* @__PURE__ */ jsx32("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: loading ? "Loading..." : "No results." }) }) }) : shouldVirtualize ? /* @__PURE__ */ jsxs20(Fragment4, { children: [
3856
- paddingTop > 0 && /* @__PURE__ */ jsx32("tbody", { children: /* @__PURE__ */ jsx32("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx32("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }) }),
4073
+ rows.length === 0 ? /* @__PURE__ */ jsx33("tbody", { children: /* @__PURE__ */ jsx33("tr", { children: /* @__PURE__ */ jsx33("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: loading ? "Loading..." : "No results." }) }) }) : shouldVirtualize ? /* @__PURE__ */ jsxs20(Fragment4, { children: [
4074
+ paddingTop > 0 && /* @__PURE__ */ jsx33("tbody", { children: /* @__PURE__ */ jsx33("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx33("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }) }),
3857
4075
  virtualItems.map(
3858
4076
  (virtualItem) => renderFlatItem(
3859
4077
  flatItems[virtualItem.index],
@@ -3861,10 +4079,10 @@ function DataGrid({
3861
4079
  (el) => virtualizer.measureElement(el)
3862
4080
  )
3863
4081
  ),
3864
- paddingBottom > 0 && /* @__PURE__ */ jsx32("tbody", { children: /* @__PURE__ */ jsx32("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx32("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingBottom } }) }) })
4082
+ paddingBottom > 0 && /* @__PURE__ */ jsx33("tbody", { children: /* @__PURE__ */ jsx33("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx33("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingBottom } }) }) })
3865
4083
  ] }) : groupedBuckets ? groupedBuckets.map((bucket) => renderGroupedBucket(bucket)) : tableRows.map((row) => renderRow(row))
3866
4084
  ] }),
3867
- hasCellEditing && /* @__PURE__ */ jsx32(
4085
+ hasCellEditing && /* @__PURE__ */ jsx33(
3868
4086
  SelectionOverlay,
3869
4087
  {
3870
4088
  containerRef: scrollRef,
@@ -3890,7 +4108,7 @@ function DataGrid({
3890
4108
  ")"
3891
4109
  ] }),
3892
4110
  /* @__PURE__ */ jsxs20("div", { className: "flex gap-2", children: [
3893
- /* @__PURE__ */ jsx32(
4111
+ /* @__PURE__ */ jsx33(
3894
4112
  Button,
3895
4113
  {
3896
4114
  variant: "outline",
@@ -3900,7 +4118,7 @@ function DataGrid({
3900
4118
  children: "Previous"
3901
4119
  }
3902
4120
  ),
3903
- /* @__PURE__ */ jsx32(
4121
+ /* @__PURE__ */ jsx33(
3904
4122
  Button,
3905
4123
  {
3906
4124
  variant: "outline",
@@ -3919,7 +4137,7 @@ function DataGrid({
3919
4137
  // src/tree/TreeDataGrid.tsx
3920
4138
  import { useVirtualizer as useVirtualizer2 } from "@tanstack/react-virtual";
3921
4139
  import { AlertCircle, ChevronDown as ChevronDown2, ChevronRight as ChevronRight2, Loader2 as Loader22 } from "lucide-react";
3922
- import { Fragment as Fragment5, useEffect as useEffect10, useMemo as useMemo10, useRef as useRef12 } from "react";
4140
+ import { Fragment as Fragment5, useEffect as useEffect11, useMemo as useMemo10, useRef as useRef12 } from "react";
3923
4141
 
3924
4142
  // src/tree/flattenTree.ts
3925
4143
  function flattenTree(roots, accessors, expanded, childrenOverride) {
@@ -3964,7 +4182,7 @@ function computeSelectionStates(roots, accessors, childrenOverride, selectedIds)
3964
4182
  }
3965
4183
 
3966
4184
  // src/tree/useTreeState.ts
3967
- import { useCallback as useCallback5, useMemo as useMemo9, useState as useState14 } from "react";
4185
+ import { useCallback as useCallback5, useMemo as useMemo9, useState as useState15 } from "react";
3968
4186
  function useTreeState({
3969
4187
  data,
3970
4188
  getRowId,
@@ -3976,7 +4194,7 @@ function useTreeState({
3976
4194
  childrenMap: controlledChildrenMap,
3977
4195
  onChildrenMapChange
3978
4196
  }) {
3979
- const [internalExpanded, setInternalExpanded] = useState14({});
4197
+ const [internalExpanded, setInternalExpanded] = useState15({});
3980
4198
  const expanded = controlledExpanded ?? internalExpanded;
3981
4199
  const setExpanded = useCallback5(
3982
4200
  (updater) => {
@@ -3988,7 +4206,7 @@ function useTreeState({
3988
4206
  },
3989
4207
  [controlledExpanded, onExpandedChange]
3990
4208
  );
3991
- const [internalChildrenMap, setInternalChildrenMap] = useState14(/* @__PURE__ */ new Map());
4209
+ const [internalChildrenMap, setInternalChildrenMap] = useState15(/* @__PURE__ */ new Map());
3992
4210
  const childrenMap = controlledChildrenMap ?? internalChildrenMap;
3993
4211
  const setChildrenMap = useCallback5(
3994
4212
  (updater) => {
@@ -4000,8 +4218,8 @@ function useTreeState({
4000
4218
  },
4001
4219
  [controlledChildrenMap, onChildrenMapChange]
4002
4220
  );
4003
- const [loadingIds, setLoadingIds] = useState14(/* @__PURE__ */ new Set());
4004
- const [errorIds, setErrorIds] = useState14(/* @__PURE__ */ new Map());
4221
+ const [loadingIds, setLoadingIds] = useState15(/* @__PURE__ */ new Set());
4222
+ const [errorIds, setErrorIds] = useState15(/* @__PURE__ */ new Map());
4005
4223
  const resolveHasChildren = useMemo9(
4006
4224
  () => hasChildren ?? ((row) => (getChildren(row)?.length ?? 0) > 0),
4007
4225
  [hasChildren, getChildren]
@@ -4081,7 +4299,7 @@ function useTreeState({
4081
4299
  }
4082
4300
 
4083
4301
  // src/tree/TreeDataGrid.tsx
4084
- import { Fragment as Fragment6, jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
4302
+ import { Fragment as Fragment6, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
4085
4303
  var EMPTY_SELECTED_IDS = /* @__PURE__ */ new Set();
4086
4304
  var UNCHECKED_STATE = { checked: false, indeterminate: false };
4087
4305
  function renderCell(column, row, editingCtx) {
@@ -4098,7 +4316,7 @@ function TreeCell({
4098
4316
  editingCtx
4099
4317
  }) {
4100
4318
  return /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-1", style: { paddingLeft: flatRow.depth * indentSize }, children: [
4101
- flatRow.hasChildren ? /* @__PURE__ */ jsx33(
4319
+ flatRow.hasChildren ? /* @__PURE__ */ jsx34(
4102
4320
  "button",
4103
4321
  {
4104
4322
  type: "button",
@@ -4110,14 +4328,14 @@ function TreeCell({
4110
4328
  "aria-label": flatRow.isExpanded ? "Collapse" : "Expand",
4111
4329
  "aria-expanded": flatRow.isExpanded,
4112
4330
  className: "flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground disabled:opacity-50",
4113
- children: isLoading ? /* @__PURE__ */ jsx33(Loader22, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": "true" }) : flatRow.isExpanded ? /* @__PURE__ */ jsx33(ChevronDown2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx33(ChevronRight2, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
4331
+ children: isLoading ? /* @__PURE__ */ jsx34(Loader22, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": "true" }) : flatRow.isExpanded ? /* @__PURE__ */ jsx34(ChevronDown2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx34(ChevronRight2, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
4114
4332
  }
4115
- ) : /* @__PURE__ */ jsx33("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
4116
- /* @__PURE__ */ jsx33("span", { className: "min-w-0 flex-1 truncate", children: renderCell(column, flatRow.row, editingCtx) }),
4333
+ ) : /* @__PURE__ */ jsx34("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
4334
+ /* @__PURE__ */ jsx34("span", { className: "min-w-0 flex-1 truncate", children: renderCell(column, flatRow.row, editingCtx) }),
4117
4335
  flatRow.isExpanded && error ? /* @__PURE__ */ jsxs21("span", { className: "ml-1 flex shrink-0 items-center gap-1 text-xs text-destructive", children: [
4118
- /* @__PURE__ */ jsx33(AlertCircle, { className: "h-3 w-3", "aria-hidden": "true" }),
4336
+ /* @__PURE__ */ jsx34(AlertCircle, { className: "h-3 w-3", "aria-hidden": "true" }),
4119
4337
  "Failed to load.",
4120
- /* @__PURE__ */ jsx33("button", { type: "button", className: "underline underline-offset-2", onClick: onRetry, children: "Retry" })
4338
+ /* @__PURE__ */ jsx34("button", { type: "button", className: "underline underline-offset-2", onClick: onRetry, children: "Retry" })
4121
4339
  ] }) : null
4122
4340
  ] });
4123
4341
  }
@@ -4171,7 +4389,7 @@ function TreeDataGrid({
4171
4389
  onChildrenMapChange
4172
4390
  });
4173
4391
  const didAutoExpand = useRef12(false);
4174
- useEffect10(() => {
4392
+ useEffect11(() => {
4175
4393
  if (didAutoExpand.current) return;
4176
4394
  didAutoExpand.current = true;
4177
4395
  if (initialExpandedLevel > 0) void expandToLevel(initialExpandedLevel);
@@ -4272,9 +4490,9 @@ function TreeDataGrid({
4272
4490
  isOddRow && "bg-foreground/5"
4273
4491
  ) : `border-b border-border divide-x divide-border${isOddRow ? " bg-foreground/5" : ""}`,
4274
4492
  children: [
4275
- showSelectionColumn && /* @__PURE__ */ jsx33("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
4493
+ showSelectionColumn && /* @__PURE__ */ jsx34("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
4276
4494
  const state = resolveSelectionState(flatRow.row, flatRow.id);
4277
- return /* @__PURE__ */ jsx33(
4495
+ return /* @__PURE__ */ jsx34(
4278
4496
  Checkbox,
4279
4497
  {
4280
4498
  "aria-label": `Select row ${flatRow.id}`,
@@ -4285,7 +4503,7 @@ function TreeDataGrid({
4285
4503
  }
4286
4504
  );
4287
4505
  })() }),
4288
- visibleColumns.map((column) => /* @__PURE__ */ jsx33("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx33(
4506
+ visibleColumns.map((column) => /* @__PURE__ */ jsx34("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx34(
4289
4507
  TreeCell,
4290
4508
  {
4291
4509
  flatRow,
@@ -4298,7 +4516,7 @@ function TreeDataGrid({
4298
4516
  editingCtx: editingState.ctx
4299
4517
  }
4300
4518
  ) : renderCell(column, flatRow.row, editingState.ctx) }, column.id)),
4301
- showRowActionsColumn && rowActions && /* @__PURE__ */ jsx33("td", { className: "p-2", children: /* @__PURE__ */ jsx33(
4519
+ showRowActionsColumn && rowActions && /* @__PURE__ */ jsx34("td", { className: "p-2", children: /* @__PURE__ */ jsx34(
4302
4520
  ActionsMenu,
4303
4521
  {
4304
4522
  items: rowActions,
@@ -4315,7 +4533,7 @@ function TreeDataGrid({
4315
4533
  const paddingTop = virtualItems.length > 0 ? virtualItems[0].start : 0;
4316
4534
  const paddingBottom = virtualItems.length > 0 ? virtualizer.getTotalSize() - virtualItems[virtualItems.length - 1].end : 0;
4317
4535
  return /* @__PURE__ */ jsxs21("div", { className: "flex flex-col gap-2", "data-testid": "tree-datagrid", children: [
4318
- editing && editingState.pendingEdits.size > 0 && /* @__PURE__ */ jsx33(
4536
+ editing && editingState.pendingEdits.size > 0 && /* @__PURE__ */ jsx34(
4319
4537
  EditingBar,
4320
4538
  {
4321
4539
  editing,
@@ -4326,15 +4544,15 @@ function TreeDataGrid({
4326
4544
  testIdPrefix: "tree-datagrid"
4327
4545
  }
4328
4546
  ),
4329
- /* @__PURE__ */ jsx33(
4547
+ /* @__PURE__ */ jsx34(
4330
4548
  "div",
4331
4549
  {
4332
4550
  ref: scrollRef,
4333
4551
  className: shouldVirtualize ? "overflow-y-auto" : void 0,
4334
4552
  style: shouldVirtualize ? { maxHeight: maxBodyHeight } : void 0,
4335
4553
  children: /* @__PURE__ */ jsxs21("table", { className: "w-full border-collapse text-sm", children: [
4336
- /* @__PURE__ */ jsx33("thead", { ref: theadRef, children: /* @__PURE__ */ jsxs21("tr", { className: "divide-x divide-border", children: [
4337
- showSelectionColumn && /* @__PURE__ */ jsx33("th", { className: "border-b border-border p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: /* @__PURE__ */ jsx33(
4554
+ /* @__PURE__ */ jsx34("thead", { ref: theadRef, children: /* @__PURE__ */ jsxs21("tr", { className: "divide-x divide-border", children: [
4555
+ showSelectionColumn && /* @__PURE__ */ jsx34("th", { className: "border-b border-border p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: /* @__PURE__ */ jsx34(
4338
4556
  Checkbox,
4339
4557
  {
4340
4558
  "aria-label": "Select all visible rows",
@@ -4342,7 +4560,7 @@ function TreeDataGrid({
4342
4560
  onCheckedChange: toggleAllVisibleSelected
4343
4561
  }
4344
4562
  ) }),
4345
- visibleColumns.map((column) => /* @__PURE__ */ jsx33(
4563
+ visibleColumns.map((column) => /* @__PURE__ */ jsx34(
4346
4564
  "th",
4347
4565
  {
4348
4566
  style: column.width ? { width: column.width } : void 0,
@@ -4351,12 +4569,12 @@ function TreeDataGrid({
4351
4569
  },
4352
4570
  column.id
4353
4571
  )),
4354
- showRowActionsColumn && /* @__PURE__ */ jsx33("th", { className: "border-b border-border p-2", "aria-label": "Row actions" })
4572
+ showRowActionsColumn && /* @__PURE__ */ jsx34("th", { className: "border-b border-border p-2", "aria-label": "Row actions" })
4355
4573
  ] }) }),
4356
- /* @__PURE__ */ jsx33("tbody", { children: data.length === 0 ? /* @__PURE__ */ jsx33("tr", { children: /* @__PURE__ */ jsx33("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: "No results." }) }) : groupedBuckets ? groupedBuckets.map((bucket) => {
4574
+ /* @__PURE__ */ jsx34("tbody", { children: data.length === 0 ? /* @__PURE__ */ jsx34("tr", { children: /* @__PURE__ */ jsx34("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: "No results." }) }) : groupedBuckets ? groupedBuckets.map((bucket) => {
4357
4575
  const groupExpanded = isGroupExpanded(bucket.key);
4358
4576
  return /* @__PURE__ */ jsxs21(Fragment5, { children: [
4359
- /* @__PURE__ */ jsx33("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx33(
4577
+ /* @__PURE__ */ jsx34("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx34(
4360
4578
  "td",
4361
4579
  {
4362
4580
  colSpan: totalColumnCount,
@@ -4373,7 +4591,7 @@ function TreeDataGrid({
4373
4591
  onClick: () => toggleGroupExpanded(bucket.key),
4374
4592
  "aria-expanded": groupExpanded,
4375
4593
  children: [
4376
- /* @__PURE__ */ jsx33(
4594
+ /* @__PURE__ */ jsx34(
4377
4595
  ChevronRight2,
4378
4596
  {
4379
4597
  className: `h-4 w-4 shrink-0 transition-transform${groupExpanded ? " rotate-90" : ""}`,
@@ -4392,11 +4610,11 @@ function TreeDataGrid({
4392
4610
  })()
4393
4611
  ] }, `group-${bucket.key}`);
4394
4612
  }) : shouldVirtualize ? /* @__PURE__ */ jsxs21(Fragment6, { children: [
4395
- paddingTop > 0 && /* @__PURE__ */ jsx33("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx33("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }),
4613
+ paddingTop > 0 && /* @__PURE__ */ jsx34("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx34("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }),
4396
4614
  virtualItems.map(
4397
4615
  (virtualItem) => renderRow(flatRows[virtualItem.index], virtualItem.index, (el) => virtualizer.measureElement(el))
4398
4616
  ),
4399
- paddingBottom > 0 && /* @__PURE__ */ jsx33("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx33(
4617
+ paddingBottom > 0 && /* @__PURE__ */ jsx34("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx34(
4400
4618
  "td",
4401
4619
  {
4402
4620
  colSpan: totalColumnCount,