react-glide-table 2.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { W as CellEditType, c as CellRenderer, C as CellKind, a as CellRenderContext, i as DataTableClassNames, R as RowSelectionMode, g as ColumnFreezeOffset, p as SearchResultItem, q as SearchStatus, l as DataTableProps, k as DataTableLabels, j as DataTableCopyActions, P as PasteMode, o as RowsPastePayload } from './types-DJOlsDL8.js';
2
- export { B as BuiltinCellKind, b as CellRenderFn, d as ColumnFreezeColumnInput, e as ColumnFreezeEdgeSide, f as ColumnFreezeMeta, h as ColumnFreezeSide, D as DEFAULT_DATA_TABLE_LABELS, I as INLINE_SEARCH_MAX_RESULTS, S as SearchCorpusRow, t as buildColumnFreezeOffsets, u as buildFlatSearchCorpus, v as buildSearchMatchKey, w as buildSearchMatchKeys, x as buildTreeSearchCorpus, y as cellValueToSearchText, z as collectAncestorKeysToExpand, A as collectSearchMatchesInRange, E as createSearchRegex, F as escapeSearchRegex, G as formatSearchResultLabel, H as getColumnFreezeEdgeAttr, J as getColumnFreezeStyle, K as mapSearchResultToVisibleItem, L as mapSearchResultsToVisibleKeys, M as nextSearchIndex, N as nextSearchStride, O as previousSearchIndex, Q as resolveColumnFreezeSide, U as resolveDataTableLabels, V as resolveHeaderFreezeOffset } from './types-DJOlsDL8.js';
3
- import { Row, ColumnDef, Table, CellContext, Updater, RowSelectionState } from '@tanstack/react-table';
4
- export { ColumnDef, ColumnResizeMode, ColumnSizingState, RowSelectionState } from '@tanstack/react-table';
1
+ import { W as CellEditType, c as CellRenderer, C as CellKind, a as CellRenderContext, i as DataTableClassNames, R as RowSelectionMode, g as ColumnFreezeOffset, p as SearchResultItem, q as SearchStatus, l as DataTableProps, k as DataTableLabels, j as DataTableCopyActions, P as PasteMode, o as RowsPastePayload } from './types-CMUGtH-c.js';
2
+ export { B as BuiltinCellKind, b as CellRenderFn, d as ColumnFreezeColumnInput, e as ColumnFreezeEdgeSide, f as ColumnFreezeMeta, h as ColumnFreezeSide, D as DEFAULT_DATA_TABLE_LABELS, I as INLINE_SEARCH_MAX_RESULTS, S as SearchCorpusRow, t as buildColumnFreezeOffsets, u as buildFlatSearchCorpus, v as buildSearchMatchKey, w as buildSearchMatchKeys, x as buildTreeSearchCorpus, y as cellValueToSearchText, z as collectAncestorKeysToExpand, A as collectSearchMatchesInRange, E as createSearchRegex, F as escapeSearchRegex, G as formatSearchResultLabel, H as getColumnFreezeEdgeAttr, J as getColumnFreezeStyle, K as mapSearchResultToVisibleItem, L as mapSearchResultsToVisibleKeys, M as nextSearchIndex, N as nextSearchStride, O as previousSearchIndex, Q as resolveColumnFreezeSide, U as resolveDataTableLabels, V as resolveHeaderFreezeOffset } from './types-CMUGtH-c.js';
3
+ import { Row, ColumnDef, CellContext, Table, Cell, ColumnOrderState, Updater, RowSelectionState } from '@tanstack/react-table';
4
+ export { ColumnDef, ColumnOrderState, ColumnResizeMode, ColumnSizingState, RowSelectionState } from '@tanstack/react-table';
5
5
  import { Virtualizer, VirtualItem } from '@tanstack/react-virtual';
6
6
  import * as react from 'react';
7
- import { RefObject, CSSProperties } from 'react';
7
+ import { RefObject, CSSProperties, PointerEvent } from 'react';
8
8
 
9
9
  /** Neutral default field names for tree conversion */
10
10
  declare const DEFAULT_TREE_ID_FIELD = "id";
@@ -197,6 +197,19 @@ type DataTableRowContextValue = {
197
197
  };
198
198
  };
199
199
 
200
+ /**
201
+ * TanStack `CellContext` with a guaranteed `update` commit helper.
202
+ * Prefer this over bare `cell.getContext()`, which does not include `update` at runtime.
203
+ */
204
+ type CellContextWithUpdate<TData, TValue> = CellContext<TData, TValue> & {
205
+ update: (next: TValue) => void;
206
+ };
207
+ /**
208
+ * Injects `update` into a TanStack `CellContext` so `ColumnDef.cell` can commit
209
+ * through `onCellChange` / `onDataChange` the same way compound `Column.render` does.
210
+ */
211
+ declare function withCellUpdate<TData extends Record<string, unknown>, TValue>(context: CellContext<TData, TValue>, commitValue: (rowId: string, columnId: string, value: unknown) => boolean): CellContextWithUpdate<TData, TValue>;
212
+
200
213
  type UseInlineSearchOptions = {
201
214
  enabled?: boolean;
202
215
  rowCount: number;
@@ -248,6 +261,7 @@ type UseGlideTableResult<T extends Record<string, unknown>> = {
248
261
  selectionLabel: DataTableLabels["selection"];
249
262
  enableCellSelection: boolean;
250
263
  enableColumnResize: boolean;
264
+ enableColumnReorder: boolean;
251
265
  enableColumnFreeze: boolean;
252
266
  enableInlineSearch: boolean;
253
267
  shouldVirtualize: boolean;
@@ -258,8 +272,14 @@ type UseGlideTableResult<T extends Record<string, unknown>> = {
258
272
  paddingTop: number;
259
273
  paddingBottom: number;
260
274
  rowContextValue: DataTableRowContextValue;
275
+ /**
276
+ * TanStack `CellContext` with `update` injected for custom `ColumnDef.cell` renders.
277
+ * Prefer this over `cell.getContext()` when calling `flexRender` yourself.
278
+ */
279
+ getCellContext: <TValue>(cell: Cell<T, TValue>) => CellContextWithUpdate<T, TValue>;
261
280
  handleToggleSelect: (row: Row<T>) => void;
262
281
  clearHover: () => void;
282
+ setColumnOrder: (next: ColumnOrderState) => void;
263
283
  copySelection: DataTableCopyActions["copySelection"];
264
284
  inlineSearch: {
265
285
  showSearch: boolean;
@@ -321,6 +341,38 @@ declare function getColumnSizeStyle(size: number, options?: {
321
341
  lockMax?: boolean;
322
342
  }): CSSProperties | undefined;
323
343
 
344
+ type ColumnDropEdge = "before" | "after";
345
+ declare function collectLeafColumnIds<T>(columns: readonly ColumnDef<T, unknown>[]): string[];
346
+ /** Keep known ids in the given order and append any new leaves at the end. */
347
+ declare function resolveLeafColumnOrder<T>(columns: readonly ColumnDef<T, unknown>[], order: readonly string[] | undefined): string[];
348
+ /**
349
+ * Reorder nested `ColumnDef`s by leaf id list.
350
+ * Consecutive leaves that still share a parent group stay wrapped together;
351
+ * interleaved groups split (Glide-style).
352
+ */
353
+ declare function applyLeafColumnOrder<T>(columns: readonly ColumnDef<T, unknown>[], order: readonly string[] | undefined): ColumnDef<T, unknown>[];
354
+ declare function moveColumnIds(order: readonly string[], fromIds: readonly string[], targetIds: readonly string[], edge: ColumnDropEdge): string[];
355
+ declare function resolveDropEdge(clientX: number, rect: Pick<DOMRect, "left" | "width">): ColumnDropEdge;
356
+
357
+ type ColumnReorderDropTarget = {
358
+ columnId: string;
359
+ edge: ColumnDropEdge;
360
+ };
361
+ declare function useColumnReorder(options: {
362
+ enabled: boolean;
363
+ columnOrder: readonly string[];
364
+ onColumnOrderChange: (next: string[]) => void;
365
+ }): {
366
+ isReordering: boolean;
367
+ draggingColumnId: string | null;
368
+ dropTarget: ColumnReorderDropTarget | null;
369
+ onHeaderPointerDown: (event: PointerEvent<HTMLTableCellElement>, meta: {
370
+ columnId: string;
371
+ leafIds: string[];
372
+ canDrag: boolean;
373
+ }) => void;
374
+ };
375
+
324
376
  type ParsedClipboardTSV = {
325
377
  values: string[][];
326
378
  /**
@@ -445,4 +497,4 @@ declare const useConvertTreeData: <T extends Record<string, unknown>>({ data, en
445
497
  declare function resolveRowSelection(mode: RowSelectionMode, controlledSelection: RowSelectionState | undefined, internalSelection: RowSelectionState): RowSelectionState;
446
498
  declare function applySelectionUpdater(mode: RowSelectionMode, updater: Updater<RowSelectionState>, previous: RowSelectionState): RowSelectionState;
447
499
 
448
- export { BUILTIN_CELL_RENDERERS, CELL_SELECTION_EDGES_CLASS, CellKind, CellRenderContext, CellRenderer, type CellRendererRegistry, type CellSelectionBounds, ColumnFreezeOffset, type ColumnRowSpanMap, type CopyRowEntry, type CopySelectionMode, DEFAULT_TREE_CHILDREN_FIELD, DEFAULT_TREE_ID_FIELD, DEFAULT_TREE_PARENT_ID_FIELD, DEFAULT_TREE_QTY_FIELD, DataTableCopyActions, DataTableLabels, DataTableProps, type DragState, type EditingCell, PasteMode, ResolvedTableCell, RowSelectionMode, type RowSpanInfo, RowsPastePayload, SearchResultItem, SearchStatus, type TreeRow, type UseConvertTreeDataParams, type UseGlideTableOptions, type UseGlideTableResult, type UseInlineSearchOptions, type UseInlineSearchResult, applyCellEdit, applyFillData, applySelectionUpdater, buildColumnRowSpanMap, buildRowsPastePayload, canExpandRow, collectCopyRowEntries, collectCopyRows, collectFillChanges, collectRowSpanColumns, commitCellValue, createCellRendererRegistry, flattenSubtreeRows, formatCellValue, formatDefaultCellValue, getCellEditDraftValue, getCellSelectionEdgeStyle, getColumnEditType, getColumnSizeStyle, getRowIndexInMergedCell, hasCellSelectionEdges, isCellInSelection, isColumnEditable, isEditablePasteTarget, measureMergedSpanRowHeights, parseCellEditValue, parseClipboardTSV, parseClipboardTSVWithDepths, resolveCellRenderer, resolvePasteColumnIds, resolveRowSelection, resolveRowSpanAt, rowRangeToHeightRatios, serializeCopyRowsToTSV, serializeSelectionToTSV, toggleExpandedRowId, useCellEdit, useCellSelection, useConvertTreeData, useGlideTable, useInlineSearch, writeSelectionToClipboard };
500
+ export { BUILTIN_CELL_RENDERERS, CELL_SELECTION_EDGES_CLASS, type CellContextWithUpdate, CellKind, CellRenderContext, CellRenderer, type CellRendererRegistry, type CellSelectionBounds, type ColumnDropEdge, ColumnFreezeOffset, type ColumnRowSpanMap, type CopyRowEntry, type CopySelectionMode, DEFAULT_TREE_CHILDREN_FIELD, DEFAULT_TREE_ID_FIELD, DEFAULT_TREE_PARENT_ID_FIELD, DEFAULT_TREE_QTY_FIELD, DataTableCopyActions, DataTableLabels, DataTableProps, type DragState, type EditingCell, PasteMode, ResolvedTableCell, RowSelectionMode, type RowSpanInfo, RowsPastePayload, SearchResultItem, SearchStatus, type TreeRow, type UseConvertTreeDataParams, type UseGlideTableOptions, type UseGlideTableResult, type UseInlineSearchOptions, type UseInlineSearchResult, applyCellEdit, applyFillData, applyLeafColumnOrder, applySelectionUpdater, buildColumnRowSpanMap, buildRowsPastePayload, canExpandRow, collectCopyRowEntries, collectCopyRows, collectFillChanges, collectLeafColumnIds, collectRowSpanColumns, commitCellValue, createCellRendererRegistry, flattenSubtreeRows, formatCellValue, formatDefaultCellValue, getCellEditDraftValue, getCellSelectionEdgeStyle, getColumnEditType, getColumnSizeStyle, getRowIndexInMergedCell, hasCellSelectionEdges, isCellInSelection, isColumnEditable, isEditablePasteTarget, measureMergedSpanRowHeights, moveColumnIds, parseCellEditValue, parseClipboardTSV, parseClipboardTSVWithDepths, resolveCellRenderer, resolveDropEdge, resolveLeafColumnOrder, resolvePasteColumnIds, resolveRowSelection, resolveRowSpanAt, rowRangeToHeightRatios, serializeCopyRowsToTSV, serializeSelectionToTSV, toggleExpandedRowId, useCellEdit, useCellSelection, useColumnReorder, useConvertTreeData, useGlideTable, useInlineSearch, withCellUpdate, writeSelectionToClipboard };
package/dist/core.js CHANGED
@@ -6,6 +6,7 @@ var DEFAULT_DATA_TABLE_LABELS = {
6
6
  expandRow: "Expand row",
7
7
  collapseRow: "Collapse row",
8
8
  resizeColumn: "Resize column",
9
+ reorderColumn: "Reorder column",
9
10
  searchPlaceholder: "Search\u2026",
10
11
  searchResultHint: "Type to search",
11
12
  searchPrevious: "Previous result",
@@ -47,6 +48,7 @@ var DATA_TABLE_VIRTUAL_OVERSCAN = 8;
47
48
  var DATA_TABLE_COLUMN_SIZE = 150;
48
49
  var DATA_TABLE_COLUMN_MIN_SIZE = 40;
49
50
  var DATA_TABLE_COLUMN_MAX_SIZE = 800;
51
+ var DATA_TABLE_COLUMN_REORDER_THRESHOLD = 4;
50
52
 
51
53
  // src/components/ui/table/features/cell-edit/useCellEdit.ts
52
54
  import { useCallback, useEffect, useRef, useState } from "react";
@@ -420,6 +422,16 @@ function formatDefaultCellValue(value) {
420
422
  return String(value);
421
423
  }
422
424
 
425
+ // src/components/ui/table/features/cell-render/withCellUpdate.ts
426
+ function withCellUpdate(context, commitValue) {
427
+ return {
428
+ ...context,
429
+ update: (next) => {
430
+ commitValue(context.row.id, context.column.id, next);
431
+ }
432
+ };
433
+ }
434
+
423
435
  // src/components/ui/table/features/cell-selection/useCellSelection.ts
424
436
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
425
437
 
@@ -1302,6 +1314,129 @@ function useCellSelection({
1302
1314
  };
1303
1315
  }
1304
1316
 
1317
+ // src/components/ui/table/features/column-reorder/columnReorder.ts
1318
+ function getColumnDefId(column) {
1319
+ if (column.id != null && column.id !== "") return column.id;
1320
+ if ("accessorKey" in column && column.accessorKey != null) {
1321
+ return String(column.accessorKey);
1322
+ }
1323
+ return void 0;
1324
+ }
1325
+ function getColumnDefChildren(column) {
1326
+ if (!("columns" in column) || !Array.isArray(column.columns)) return void 0;
1327
+ if (column.columns.length === 0) return void 0;
1328
+ return column.columns;
1329
+ }
1330
+ function collectLeafColumnIds(columns) {
1331
+ const ids = [];
1332
+ for (const column of columns) {
1333
+ const children = getColumnDefChildren(column);
1334
+ if (children) {
1335
+ ids.push(...collectLeafColumnIds(children));
1336
+ continue;
1337
+ }
1338
+ const id = getColumnDefId(column);
1339
+ if (id) ids.push(id);
1340
+ }
1341
+ return ids;
1342
+ }
1343
+ function areColumnOrdersEqual(left, right) {
1344
+ if (left.length !== right.length) return false;
1345
+ return left.every((id, index) => id === right[index]);
1346
+ }
1347
+ function resolveLeafColumnOrder(columns, order) {
1348
+ const leafIds = collectLeafColumnIds(columns);
1349
+ if (!order?.length) return leafIds;
1350
+ const leafSet = new Set(leafIds);
1351
+ const seen = /* @__PURE__ */ new Set();
1352
+ const next = order.filter((id) => {
1353
+ if (!leafSet.has(id) || seen.has(id)) return false;
1354
+ seen.add(id);
1355
+ return true;
1356
+ });
1357
+ for (const id of leafIds) {
1358
+ if (!seen.has(id)) next.push(id);
1359
+ }
1360
+ return next;
1361
+ }
1362
+ function flattenColumnSlots(columns, group) {
1363
+ const slots = [];
1364
+ for (const column of columns) {
1365
+ const id = getColumnDefId(column);
1366
+ const children = getColumnDefChildren(column);
1367
+ if (children) {
1368
+ const nestedGroup = id ? { id, def: column } : group;
1369
+ slots.push(...flattenColumnSlots(children, nestedGroup));
1370
+ continue;
1371
+ }
1372
+ if (!id) continue;
1373
+ slots.push({ id, def: column, group });
1374
+ }
1375
+ return slots;
1376
+ }
1377
+ function rebuildColumnTree(slots) {
1378
+ const result = [];
1379
+ let index = 0;
1380
+ while (index < slots.length) {
1381
+ const slot = slots[index];
1382
+ if (!slot.group) {
1383
+ result.push(slot.def);
1384
+ index += 1;
1385
+ continue;
1386
+ }
1387
+ const groupId = slot.group.id;
1388
+ const children = [];
1389
+ while (index < slots.length && slots[index]?.group?.id === groupId) {
1390
+ children.push(slots[index].def);
1391
+ index += 1;
1392
+ }
1393
+ const firstChildId = children[0] ? getColumnDefId(children[0]) : groupId;
1394
+ result.push({
1395
+ ...slot.group.def,
1396
+ id: `${groupId}::${firstChildId}`,
1397
+ columns: children
1398
+ });
1399
+ }
1400
+ return result;
1401
+ }
1402
+ function applyLeafColumnOrder(columns, order) {
1403
+ const resolved = resolveLeafColumnOrder(columns, order);
1404
+ const defaultOrder = collectLeafColumnIds(columns);
1405
+ if (areColumnOrdersEqual(resolved, defaultOrder)) {
1406
+ return columns;
1407
+ }
1408
+ const byId = new Map(
1409
+ flattenColumnSlots(columns).map((slot) => [
1410
+ slot.id,
1411
+ slot
1412
+ ])
1413
+ );
1414
+ const ordered = [];
1415
+ for (const id of resolved) {
1416
+ const slot = byId.get(id);
1417
+ if (slot) ordered.push(slot);
1418
+ }
1419
+ return rebuildColumnTree(ordered);
1420
+ }
1421
+ function moveColumnIds(order, fromIds, targetIds, edge) {
1422
+ if (fromIds.length === 0 || targetIds.length === 0) return [...order];
1423
+ const fromSet = new Set(fromIds);
1424
+ if (targetIds.some((id) => fromSet.has(id))) return [...order];
1425
+ const rest = order.filter((id) => !fromSet.has(id));
1426
+ const anchorId = edge === "before" ? targetIds[0] : targetIds[targetIds.length - 1];
1427
+ const anchorIndex = rest.indexOf(anchorId);
1428
+ if (anchorIndex < 0) return [...order];
1429
+ const insertAt = edge === "before" ? anchorIndex : anchorIndex + 1;
1430
+ return [...rest.slice(0, insertAt), ...fromIds, ...rest.slice(insertAt)];
1431
+ }
1432
+ function resolveDropEdge(clientX, rect) {
1433
+ return clientX < rect.left + rect.width / 2 ? "before" : "after";
1434
+ }
1435
+ function parseReorderIds(value) {
1436
+ if (!value) return [];
1437
+ return value.split(",").filter(Boolean);
1438
+ }
1439
+
1305
1440
  // src/components/ui/table/features/column-freeze/columnFreeze.ts
1306
1441
  var HEADER_Z_BASE = 30;
1307
1442
  var BODY_Z_BASE = 5;
@@ -2184,6 +2319,9 @@ function useGlideTable(options) {
2184
2319
  columnSizing: controlledColumnSizing,
2185
2320
  onColumnSizingChange,
2186
2321
  columnResizeMode = "onChange",
2322
+ enableColumnReorder = false,
2323
+ columnOrder: controlledColumnOrder,
2324
+ onColumnOrderChange,
2187
2325
  enableColumnFreeze = false,
2188
2326
  enableInlineSearch = false,
2189
2327
  showSearch,
@@ -2206,6 +2344,7 @@ function useGlideTable(options) {
2206
2344
  const resolvedEnableSubtreeCopy = enableSubtreeCopy ?? enableExpand;
2207
2345
  const [internalRowSelection, setInternalRowSelection] = useState4({});
2208
2346
  const [internalColumnSizing, setInternalColumnSizing] = useState4({});
2347
+ const [internalColumnOrder, setInternalColumnOrder] = useState4([]);
2209
2348
  const [internalExpandedRows, setInternalExpandedRows] = useState4(
2210
2349
  () => /* @__PURE__ */ new Set()
2211
2350
  );
@@ -2226,6 +2365,21 @@ function useGlideTable(options) {
2226
2365
  internalRowSelection
2227
2366
  );
2228
2367
  const columnSizing = controlledColumnSizing ?? internalColumnSizing;
2368
+ const columnOrder = controlledColumnOrder ?? internalColumnOrder;
2369
+ const tableColumns = useMemo3(() => {
2370
+ if (!enableColumnReorder) return columns;
2371
+ return applyLeafColumnOrder(columns, columnOrder);
2372
+ }, [columnOrder, columns, enableColumnReorder]);
2373
+ const setColumnOrder = useCallback4(
2374
+ (next) => {
2375
+ if (onColumnOrderChange) {
2376
+ onColumnOrderChange(next);
2377
+ return;
2378
+ }
2379
+ setInternalColumnOrder(next);
2380
+ },
2381
+ [onColumnOrderChange]
2382
+ );
2229
2383
  const expandedRows = controlledExpandedRows ?? internalExpandedRows;
2230
2384
  const handleExpandedRowsChange = useCallback4(
2231
2385
  (next) => {
@@ -2250,7 +2404,7 @@ function useGlideTable(options) {
2250
2404
  });
2251
2405
  const table = useReactTable({
2252
2406
  data: tableData,
2253
- columns,
2407
+ columns: tableColumns,
2254
2408
  ...enableColumnResize ? {
2255
2409
  defaultColumn: {
2256
2410
  minSize: DATA_TABLE_COLUMN_MIN_SIZE,
@@ -2406,6 +2560,10 @@ function useGlideTable(options) {
2406
2560
  }),
2407
2561
  [onCellChange, onDataChange, rows, tableData]
2408
2562
  );
2563
+ const getCellContext = useCallback4(
2564
+ (cell) => withCellUpdate(cell.getContext(), commitRenderedCellValue),
2565
+ [commitRenderedCellValue]
2566
+ );
2409
2567
  const handleCellMouseDownWithCommit = useCallback4(
2410
2568
  (rowIndex, colIndex, options2) => {
2411
2569
  const isSameEditingCell = editingCell?.rowIndex === rowIndex && editingCell?.colIndex === colIndex;
@@ -2728,6 +2886,7 @@ function useGlideTable(options) {
2728
2886
  selectionLabel: labels.selection,
2729
2887
  enableCellSelection,
2730
2888
  enableColumnResize,
2889
+ enableColumnReorder,
2731
2890
  enableColumnFreeze,
2732
2891
  enableInlineSearch,
2733
2892
  shouldVirtualize,
@@ -2738,8 +2897,10 @@ function useGlideTable(options) {
2738
2897
  paddingTop,
2739
2898
  paddingBottom,
2740
2899
  rowContextValue,
2900
+ getCellContext,
2741
2901
  handleToggleSelect,
2742
2902
  clearHover,
2903
+ setColumnOrder,
2743
2904
  copySelection: stableCopySelection,
2744
2905
  inlineSearch: {
2745
2906
  showSearch: inlineSearch.showSearch,
@@ -2818,6 +2979,219 @@ function getColumnSizeStyle(size, options) {
2818
2979
  ...lockMax ? { maxWidth: size } : {}
2819
2980
  };
2820
2981
  }
2982
+
2983
+ // src/components/ui/table/features/column-reorder/useColumnReorder.ts
2984
+ import {
2985
+ useCallback as useCallback6,
2986
+ useEffect as useEffect6,
2987
+ useRef as useRef6,
2988
+ useState as useState5
2989
+ } from "react";
2990
+ function hitTestReorderHeader(table, clientX, clientY) {
2991
+ const headers = Array.from(
2992
+ table.querySelectorAll(
2993
+ "thead th[data-column-id][data-reorder-ids]"
2994
+ )
2995
+ );
2996
+ const containing = headers.find((element) => {
2997
+ const rect = element.getBoundingClientRect();
2998
+ return clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom;
2999
+ });
3000
+ if (containing) {
3001
+ return {
3002
+ columnId: containing.dataset.columnId ?? "",
3003
+ edge: resolveDropEdge(clientX, containing.getBoundingClientRect())
3004
+ };
3005
+ }
3006
+ const leaves = headers.filter(
3007
+ (element) => element.hasAttribute("data-reorder-leaf")
3008
+ );
3009
+ let match;
3010
+ for (const element of leaves) {
3011
+ const rect = element.getBoundingClientRect();
3012
+ if (clientX >= rect.left && clientX <= rect.right) {
3013
+ match = element;
3014
+ break;
3015
+ }
3016
+ }
3017
+ if (!match && leaves.length > 0) {
3018
+ const first = leaves[0].getBoundingClientRect();
3019
+ const last = leaves[leaves.length - 1].getBoundingClientRect();
3020
+ if (clientX < first.left) match = leaves[0];
3021
+ else if (clientX > last.right) match = leaves[leaves.length - 1];
3022
+ }
3023
+ if (!match) return null;
3024
+ return {
3025
+ columnId: match.dataset.columnId ?? "",
3026
+ edge: resolveDropEdge(clientX, match.getBoundingClientRect())
3027
+ };
3028
+ }
3029
+ function readTargetIds(table, columnId) {
3030
+ const element = table.querySelector(
3031
+ `thead th[data-column-id="${CSS.escape(columnId)}"]`
3032
+ );
3033
+ return parseReorderIds(element?.getAttribute("data-reorder-ids"));
3034
+ }
3035
+ function useColumnReorder(options) {
3036
+ const { enabled, columnOrder, onColumnOrderChange } = options;
3037
+ const sessionRef = useRef6(null);
3038
+ const columnOrderRef = useRef6(columnOrder);
3039
+ const onColumnOrderChangeRef = useRef6(onColumnOrderChange);
3040
+ const [draggingColumnId, setDraggingColumnId] = useState5(null);
3041
+ const [dropTarget, setDropTarget] = useState5(
3042
+ null
3043
+ );
3044
+ const dropTargetRef = useRef6(dropTarget);
3045
+ const previousUserSelectRef = useRef6(null);
3046
+ columnOrderRef.current = columnOrder;
3047
+ onColumnOrderChangeRef.current = onColumnOrderChange;
3048
+ dropTargetRef.current = dropTarget;
3049
+ const resetDrag = useCallback6(() => {
3050
+ sessionRef.current = null;
3051
+ setDraggingColumnId(null);
3052
+ setDropTarget(null);
3053
+ const backup = previousUserSelectRef.current;
3054
+ previousUserSelectRef.current = null;
3055
+ if (backup) {
3056
+ if (backup.value) {
3057
+ document.body.style.setProperty("user-select", backup.value, backup.priority);
3058
+ } else {
3059
+ document.body.style.removeProperty("user-select");
3060
+ }
3061
+ return;
3062
+ }
3063
+ document.body.style.removeProperty("user-select");
3064
+ }, []);
3065
+ useEffect6(() => {
3066
+ if (!enabled) resetDrag();
3067
+ }, [enabled, resetDrag]);
3068
+ useEffect6(() => {
3069
+ return () => {
3070
+ resetDrag();
3071
+ };
3072
+ }, [resetDrag]);
3073
+ const onHeaderPointerDown = useCallback6(
3074
+ (event, meta) => {
3075
+ if (!enabled || !meta.canDrag) return;
3076
+ if (event.button !== 0) return;
3077
+ if (event.pointerType === "mouse" && event.ctrlKey) return;
3078
+ const target = event.target;
3079
+ if (target instanceof Element && target.closest("[data-table-disable-cell-selection]")) {
3080
+ return;
3081
+ }
3082
+ const table = event.currentTarget.closest("table");
3083
+ if (!(table instanceof HTMLTableElement)) return;
3084
+ sessionRef.current = {
3085
+ pointerId: event.pointerId,
3086
+ startX: event.clientX,
3087
+ startY: event.clientY,
3088
+ columnId: meta.columnId,
3089
+ fromIds: meta.leafIds,
3090
+ table,
3091
+ active: false
3092
+ };
3093
+ },
3094
+ [enabled]
3095
+ );
3096
+ useEffect6(() => {
3097
+ if (!enabled) return;
3098
+ const onPointerMove = (event) => {
3099
+ const session = sessionRef.current;
3100
+ if (!session || event.pointerId !== session.pointerId) return;
3101
+ const deltaX = event.clientX - session.startX;
3102
+ const deltaY = event.clientY - session.startY;
3103
+ const distance = Math.hypot(deltaX, deltaY);
3104
+ if (!session.active) {
3105
+ if (distance < DATA_TABLE_COLUMN_REORDER_THRESHOLD) return;
3106
+ session.active = true;
3107
+ if (!previousUserSelectRef.current) {
3108
+ previousUserSelectRef.current = {
3109
+ value: document.body.style.getPropertyValue("user-select"),
3110
+ priority: document.body.style.getPropertyPriority("user-select")
3111
+ };
3112
+ }
3113
+ document.body.style.setProperty("user-select", "none");
3114
+ setDraggingColumnId(session.columnId);
3115
+ }
3116
+ event.preventDefault();
3117
+ const nextTarget = hitTestReorderHeader(
3118
+ session.table,
3119
+ event.clientX,
3120
+ event.clientY
3121
+ );
3122
+ if (!nextTarget || !nextTarget.columnId) {
3123
+ setDropTarget(null);
3124
+ return;
3125
+ }
3126
+ const targetIds = readTargetIds(session.table, nextTarget.columnId);
3127
+ const fromSet = new Set(session.fromIds);
3128
+ if (targetIds.some((id) => fromSet.has(id))) {
3129
+ setDropTarget(null);
3130
+ return;
3131
+ }
3132
+ setDropTarget((previous) => {
3133
+ if (previous?.columnId === nextTarget.columnId && previous.edge === nextTarget.edge) {
3134
+ return previous;
3135
+ }
3136
+ return nextTarget;
3137
+ });
3138
+ };
3139
+ const onPointerUp = (event) => {
3140
+ const session = sessionRef.current;
3141
+ if (!session || event.pointerId !== session.pointerId) {
3142
+ return;
3143
+ }
3144
+ if (session.active) {
3145
+ event.preventDefault();
3146
+ const target = dropTargetRef.current;
3147
+ if (target) {
3148
+ const targetIds = readTargetIds(session.table, target.columnId);
3149
+ const next = moveColumnIds(
3150
+ columnOrderRef.current,
3151
+ session.fromIds,
3152
+ targetIds,
3153
+ target.edge
3154
+ );
3155
+ onColumnOrderChangeRef.current(next);
3156
+ }
3157
+ const suppressClick = (clickEvent) => {
3158
+ clickEvent.preventDefault();
3159
+ clickEvent.stopPropagation();
3160
+ document.removeEventListener("click", suppressClick, true);
3161
+ };
3162
+ document.addEventListener("click", suppressClick, true);
3163
+ window.setTimeout(() => {
3164
+ document.removeEventListener("click", suppressClick, true);
3165
+ }, 0);
3166
+ }
3167
+ resetDrag();
3168
+ };
3169
+ const onPointerCancel = (event) => {
3170
+ const session = sessionRef.current;
3171
+ if (!session || event.pointerId !== session.pointerId) {
3172
+ return;
3173
+ }
3174
+ if (session.active) {
3175
+ event.preventDefault();
3176
+ }
3177
+ resetDrag();
3178
+ };
3179
+ document.addEventListener("pointermove", onPointerMove);
3180
+ document.addEventListener("pointerup", onPointerUp);
3181
+ document.addEventListener("pointercancel", onPointerCancel);
3182
+ return () => {
3183
+ document.removeEventListener("pointermove", onPointerMove);
3184
+ document.removeEventListener("pointerup", onPointerUp);
3185
+ document.removeEventListener("pointercancel", onPointerCancel);
3186
+ };
3187
+ }, [enabled, resetDrag]);
3188
+ return {
3189
+ isReordering: draggingColumnId != null,
3190
+ draggingColumnId,
3191
+ dropTarget,
3192
+ onHeaderPointerDown
3193
+ };
3194
+ }
2821
3195
  export {
2822
3196
  BUILTIN_CELL_RENDERERS,
2823
3197
  CELL_SELECTION_EDGES_CLASS,
@@ -2830,6 +3204,7 @@ export {
2830
3204
  ResolvedTableCell,
2831
3205
  applyCellEdit,
2832
3206
  applyFillData,
3207
+ applyLeafColumnOrder,
2833
3208
  applySelectionUpdater,
2834
3209
  buildColumnFreezeOffsets,
2835
3210
  buildColumnRowSpanMap,
@@ -2844,6 +3219,7 @@ export {
2844
3219
  collectCopyRowEntries,
2845
3220
  collectCopyRows,
2846
3221
  collectFillChanges,
3222
+ collectLeafColumnIds,
2847
3223
  collectRowSpanColumns,
2848
3224
  collectSearchMatchesInRange,
2849
3225
  commitCellValue,
@@ -2868,6 +3244,7 @@ export {
2868
3244
  mapSearchResultToVisibleItem,
2869
3245
  mapSearchResultsToVisibleKeys,
2870
3246
  measureMergedSpanRowHeights,
3247
+ moveColumnIds,
2871
3248
  nextSearchIndex,
2872
3249
  nextSearchStride,
2873
3250
  parseCellEditValue,
@@ -2877,7 +3254,9 @@ export {
2877
3254
  resolveCellRenderer,
2878
3255
  resolveColumnFreezeSide,
2879
3256
  resolveDataTableLabels,
3257
+ resolveDropEdge,
2880
3258
  resolveHeaderFreezeOffset,
3259
+ resolveLeafColumnOrder,
2881
3260
  resolvePasteColumnIds,
2882
3261
  resolveRowSelection,
2883
3262
  resolveRowSpanAt,
@@ -2887,8 +3266,10 @@ export {
2887
3266
  toggleExpandedRowId,
2888
3267
  useCellEdit,
2889
3268
  useCellSelection,
3269
+ useColumnReorder,
2890
3270
  useConvertTreeData,
2891
3271
  useGlideTable,
2892
3272
  useInlineSearch,
3273
+ withCellUpdate,
2893
3274
  writeSelectionToClipboard
2894
3275
  };