@zvndev/yable-react 0.4.0 → 0.5.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
@@ -1,7 +1,8 @@
1
1
  import { canCellEnterEditMode, functionalUpdate, createTable, getDefaultLocale, getFirstKeyboardCell, getResolvedFocusedCell, detectCellChanges } from '@zvndev/yable-core';
2
2
  export { CommitError, FormulaEngine, FormulaError, PivotEngine, UndoStack, aggregationFns, createColumnHelper, createLocale, createUndoRedoIntegration, en, filterFns, formulaFunctions, functionalUpdate, generatePivotColumnDefs, getDefaultLocale, getInitialPivotState, getPivotRowModel, resetLocale, setDefaultLocale, sortingFns } from '@zvndev/yable-core';
3
- import React3, { createContext, useCallback, useMemo, useContext, useState, useRef, useEffect } from 'react';
3
+ import React4, { createContext, useCallback, useMemo, useContext, useState, useRef, useEffect } from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+ import { createPortal } from 'react-dom';
5
6
  import { getInitialRowDragState, moveRow } from '@zvndev/yable-core/features/rowDragging';
6
7
 
7
8
  // src/index.ts
@@ -11,6 +12,8 @@ function useYableDefaults() {
11
12
  }
12
13
  function YableProvider({
13
14
  children,
15
+ config,
16
+ tableProfile,
14
17
  defaultColumnDef,
15
18
  striped,
16
19
  stickyHeader,
@@ -29,12 +32,86 @@ function YableProvider({
29
32
  if (direction !== void 0) tableProps.direction = direction;
30
33
  if (ariaLabel !== void 0) tableProps.ariaLabel = ariaLabel;
31
34
  const value = {
35
+ config,
36
+ tableProfile,
32
37
  tableProps: Object.keys(tableProps).length > 0 ? tableProps : void 0,
33
38
  defaultColumnDef
34
39
  };
35
40
  return /* @__PURE__ */ jsx(YableContext.Provider, { value, children });
36
41
  }
37
42
 
43
+ // src/config.ts
44
+ function createYableConfig(config) {
45
+ return config;
46
+ }
47
+ function resolveYableProfile(config, profileName = "default") {
48
+ const base = pickProfileFields(config);
49
+ const named = profileName === "default" ? void 0 : config?.profiles?.[profileName];
50
+ return {
51
+ name: profileName,
52
+ table: { ...base.table, ...named?.table },
53
+ columns: {
54
+ default: { ...base.columns?.default, ...named?.columns?.default },
55
+ byId: { ...base.columns?.byId, ...named?.columns?.byId }
56
+ },
57
+ rows: { ...base.rows, ...named?.rows },
58
+ cells: {
59
+ default: { ...base.cells?.default, ...named?.cells?.default },
60
+ named: { ...base.cells?.named, ...named?.cells?.named },
61
+ byColumn: { ...base.cells?.byColumn, ...named?.cells?.byColumn }
62
+ }
63
+ };
64
+ }
65
+ function getYableDefaultColumnDef(profile) {
66
+ const next = {
67
+ ...profile?.columns?.default
68
+ };
69
+ return Object.keys(next).length > 0 ? next : void 0;
70
+ }
71
+ function applyYableConfigToColumns(columns, profile) {
72
+ if (!profile) return columns;
73
+ return columns.map((columnDef) => applyColumnConfig(columnDef, profile));
74
+ }
75
+ function applyColumnConfig(columnDef, profile) {
76
+ const columnId = getColumnId(columnDef);
77
+ const explicit = columnDef;
78
+ const cellNames = normalizeCellConfigNames(explicit.cellConfig);
79
+ const namedCellConfig = cellNames.reduce(
80
+ (acc, name) => ({ ...acc, ...profile.cells?.named?.[name] }),
81
+ {}
82
+ );
83
+ const defaultCell = profile.cells?.default;
84
+ const columnCell = columnId ? profile.cells?.byColumn?.[columnId] : void 0;
85
+ const columnConfig = columnId ? profile.columns?.byId?.[columnId] : void 0;
86
+ const children = "columns" in columnDef && columnDef.columns ? { columns: columnDef.columns.map((child) => applyColumnConfig(child, profile)) } : void 0;
87
+ return {
88
+ ...defaultCell,
89
+ ...namedCellConfig,
90
+ ...columnCell,
91
+ ...columnConfig,
92
+ ...columnDef,
93
+ ...children
94
+ };
95
+ }
96
+ function pickProfileFields(config) {
97
+ if (!config) return {};
98
+ return {
99
+ table: config.table,
100
+ columns: config.columns,
101
+ rows: config.rows,
102
+ cells: config.cells
103
+ };
104
+ }
105
+ function getColumnId(columnDef) {
106
+ if ("id" in columnDef && columnDef.id) return String(columnDef.id);
107
+ if ("accessorKey" in columnDef && columnDef.accessorKey) return String(columnDef.accessorKey);
108
+ return void 0;
109
+ }
110
+ function normalizeCellConfigNames(value) {
111
+ if (!value) return [];
112
+ return Array.isArray(value) ? value : [value];
113
+ }
114
+
38
115
  // src/useTable.ts
39
116
  function shallowEqual(a, b) {
40
117
  if (a === b) return true;
@@ -50,15 +127,30 @@ function shallowEqual(a, b) {
50
127
  function useTable(options) {
51
128
  const providerDefaults = useYableDefaults();
52
129
  const optionsWithDefaults = useMemo(() => {
53
- if (!providerDefaults.defaultColumnDef) return options;
130
+ const profile = resolveYableProfile(
131
+ options.config ?? providerDefaults.config,
132
+ options.configProfile ?? providerDefaults.tableProfile
133
+ );
134
+ const profileDefaultColumnDef = getYableDefaultColumnDef(profile);
135
+ const configuredColumns = applyYableConfigToColumns(options.columns, profile);
136
+ const defaultColumnDef = {
137
+ ...profileDefaultColumnDef,
138
+ ...providerDefaults.defaultColumnDef,
139
+ ...options.defaultColumnDef
140
+ };
54
141
  return {
55
142
  ...options,
56
- defaultColumnDef: {
57
- ...providerDefaults.defaultColumnDef,
58
- ...options.defaultColumnDef
59
- }
143
+ columns: configuredColumns,
144
+ rowClassName: options.rowClassName ?? profile.rows?.className,
145
+ rowStyle: options.rowStyle ?? profile.rows?.style,
146
+ defaultColumnDef: Object.keys(defaultColumnDef).length > 0 ? defaultColumnDef : void 0
60
147
  };
61
- }, [options, providerDefaults.defaultColumnDef]);
148
+ }, [
149
+ options,
150
+ providerDefaults.config,
151
+ providerDefaults.defaultColumnDef,
152
+ providerDefaults.tableProfile
153
+ ]);
62
154
  const [state, setState] = useState(() => ({
63
155
  sorting: [],
64
156
  columnFilters: [],
@@ -144,6 +236,32 @@ function useTable(options) {
144
236
  })
145
237
  );
146
238
  }
239
+ useEffect(() => {
240
+ const table = tableRef.current;
241
+ if (!table) return;
242
+ const unsubscribers = [
243
+ options.onCellClick && table.events.on("cell:click", options.onCellClick),
244
+ options.onCellDoubleClick && table.events.on("cell:dblclick", options.onCellDoubleClick),
245
+ options.onCellContextMenu && table.events.on("cell:contextmenu", options.onCellContextMenu),
246
+ options.onRowClick && table.events.on("row:click", options.onRowClick),
247
+ options.onRowDoubleClick && table.events.on("row:dblclick", options.onRowDoubleClick),
248
+ options.onRowContextMenu && table.events.on("row:contextmenu", options.onRowContextMenu),
249
+ options.onHeaderClick && table.events.on("header:click", options.onHeaderClick),
250
+ options.onHeaderContextMenu && table.events.on("header:contextmenu", options.onHeaderContextMenu)
251
+ ].filter((unsubscribe) => Boolean(unsubscribe));
252
+ return () => {
253
+ unsubscribers.forEach((unsubscribe) => unsubscribe());
254
+ };
255
+ }, [
256
+ options.onCellClick,
257
+ options.onCellContextMenu,
258
+ options.onCellDoubleClick,
259
+ options.onHeaderClick,
260
+ options.onHeaderContextMenu,
261
+ options.onRowClick,
262
+ options.onRowContextMenu,
263
+ options.onRowDoubleClick
264
+ ]);
147
265
  useEffect(() => {
148
266
  return () => {
149
267
  if (tableRef.current) {
@@ -153,6 +271,195 @@ function useTable(options) {
153
271
  }, []);
154
272
  return tableRef.current;
155
273
  }
274
+ function useServerTable({
275
+ fetchData,
276
+ updateRow,
277
+ initialRows = [],
278
+ initialCursor = null,
279
+ initialHasMore = true,
280
+ initialRowCount,
281
+ initialPageCount,
282
+ initialSorting = [],
283
+ initialColumnFilters = [],
284
+ initialGlobalFilter = "",
285
+ initialPagination = { pageIndex: 0, pageSize: 50 },
286
+ autoLoad = true,
287
+ getRowId,
288
+ ...tableOptions
289
+ }) {
290
+ const [rows, setRows] = useState(initialRows);
291
+ const [cursor, setCursor] = useState(initialCursor);
292
+ const [hasMore, setHasMore] = useState(initialHasMore);
293
+ const [rowCount, setRowCount] = useState(initialRowCount);
294
+ const [pageCount, setPageCount] = useState(initialPageCount);
295
+ const [sorting, setSorting] = useState(initialSorting);
296
+ const [columnFilters, setColumnFilters] = useState(initialColumnFilters);
297
+ const [globalFilter, setGlobalFilter] = useState(initialGlobalFilter);
298
+ const [pagination, setPagination] = useState(initialPagination);
299
+ const [loading, setLoading] = useState(false);
300
+ const [error, setError] = useState(null);
301
+ const abortRef = useRef(null);
302
+ const cursorRef = useRef(cursor);
303
+ const fetchDataRef = useRef(fetchData);
304
+ const updateRowRef = useRef(updateRow);
305
+ const resolveRowId = useCallback(
306
+ (row, index) => getRowId?.(row, index) ?? String(row.id ?? index),
307
+ [getRowId]
308
+ );
309
+ const runFetch = useCallback(
310
+ async (mode) => {
311
+ abortRef.current?.abort();
312
+ const abort = new AbortController();
313
+ abortRef.current = abort;
314
+ setLoading(true);
315
+ setError(null);
316
+ try {
317
+ const result = await fetchDataRef.current({
318
+ sorting,
319
+ columnFilters,
320
+ globalFilter,
321
+ pagination,
322
+ cursor: mode === "append" ? cursorRef.current : null,
323
+ signal: abort.signal
324
+ });
325
+ if (abort.signal.aborted) return;
326
+ setRows((prev) => mode === "append" ? [...prev, ...result.rows] : result.rows);
327
+ setCursor(result.cursor ?? null);
328
+ setHasMore((prev) => result.hasMore ?? prev);
329
+ setRowCount((prev) => result.rowCount ?? prev);
330
+ setPageCount((prev) => result.pageCount ?? prev);
331
+ } catch (nextError) {
332
+ if (!abort.signal.aborted) setError(nextError);
333
+ } finally {
334
+ if (!abort.signal.aborted) setLoading(false);
335
+ }
336
+ },
337
+ [columnFilters, globalFilter, pagination, sorting]
338
+ );
339
+ const refresh = useCallback(() => runFetch("replace"), [runFetch]);
340
+ const loadMore = useCallback(async () => {
341
+ if (!hasMore || loading) return;
342
+ await runFetch("append");
343
+ }, [hasMore, loading, runFetch]);
344
+ const patchRow = useCallback(
345
+ async (rowId, patch) => {
346
+ const previousRow = rows.find((row, index) => resolveRowId(row, index) === rowId);
347
+ setRows(
348
+ (prev) => prev.map((row, index) => resolveRowId(row, index) === rowId ? { ...row, ...patch } : row)
349
+ );
350
+ if (!updateRowRef.current) return;
351
+ const abort = new AbortController();
352
+ try {
353
+ const result = await updateRowRef.current({
354
+ rowId,
355
+ patch,
356
+ previousRow,
357
+ signal: abort.signal
358
+ });
359
+ if (!result) return;
360
+ setRows(
361
+ (prev) => prev.map(
362
+ (row, index) => resolveRowId(row, index) === rowId ? { ...row, ...result } : row
363
+ )
364
+ );
365
+ } catch (nextError) {
366
+ setError(nextError);
367
+ if (previousRow) {
368
+ setRows(
369
+ (prev) => prev.map((row, index) => resolveRowId(row, index) === rowId ? previousRow : row)
370
+ );
371
+ }
372
+ }
373
+ },
374
+ [resolveRowId, rows]
375
+ );
376
+ useEffect(() => {
377
+ fetchDataRef.current = fetchData;
378
+ }, [fetchData]);
379
+ useEffect(() => {
380
+ updateRowRef.current = updateRow;
381
+ }, [updateRow]);
382
+ useEffect(() => {
383
+ if (!autoLoad) return;
384
+ void refresh();
385
+ }, [autoLoad, refresh]);
386
+ useEffect(() => {
387
+ cursorRef.current = cursor;
388
+ }, [cursor]);
389
+ useEffect(() => () => abortRef.current?.abort(), []);
390
+ const table = useTable({
391
+ ...tableOptions,
392
+ data: rows,
393
+ getRowId: resolveRowId,
394
+ manualSorting: true,
395
+ manualFiltering: true,
396
+ manualPagination: true,
397
+ rowCount,
398
+ pageCount,
399
+ state: {
400
+ sorting,
401
+ columnFilters,
402
+ globalFilter,
403
+ pagination
404
+ },
405
+ onSortingChange: (updater) => {
406
+ setSorting((prev) => functionalUpdate(updater, prev));
407
+ setCursor(null);
408
+ setHasMore(true);
409
+ },
410
+ onColumnFiltersChange: (updater) => {
411
+ setColumnFilters((prev) => functionalUpdate(updater, prev));
412
+ setCursor(null);
413
+ setHasMore(true);
414
+ },
415
+ onGlobalFilterChange: (updater) => {
416
+ setGlobalFilter((prev) => functionalUpdate(updater, prev));
417
+ setCursor(null);
418
+ setHasMore(true);
419
+ },
420
+ onPaginationChange: (updater) => {
421
+ setPagination((prev) => functionalUpdate(updater, prev));
422
+ setCursor(null);
423
+ setHasMore(true);
424
+ }
425
+ });
426
+ return useMemo(
427
+ () => ({
428
+ table,
429
+ rows,
430
+ loading,
431
+ error,
432
+ cursor,
433
+ hasMore,
434
+ rowCount,
435
+ pageCount,
436
+ sorting,
437
+ columnFilters,
438
+ globalFilter,
439
+ pagination,
440
+ refresh,
441
+ loadMore,
442
+ updateRow: patchRow
443
+ }),
444
+ [
445
+ table,
446
+ rows,
447
+ loading,
448
+ error,
449
+ cursor,
450
+ hasMore,
451
+ rowCount,
452
+ pageCount,
453
+ sorting,
454
+ columnFilters,
455
+ globalFilter,
456
+ pagination,
457
+ refresh,
458
+ loadMore,
459
+ patchRow
460
+ ]
461
+ );
462
+ }
156
463
  var DEFAULT_PERSISTED_KEYS = [
157
464
  "columnVisibility",
158
465
  "columnOrder",
@@ -349,7 +656,7 @@ function useVirtualization({
349
656
  resizeObserver.disconnect();
350
657
  }
351
658
  };
352
- }, [containerRef]);
659
+ }, [containerRef, totalRows]);
353
660
  useEffect(() => {
354
661
  if (!isFixedHeight) {
355
662
  heightCacheRef.current.clear();
@@ -540,14 +847,20 @@ function useColumnVirtualization({
540
847
  containerRef,
541
848
  columns,
542
849
  overscan = 2,
543
- enabled = true
850
+ enabled = true,
851
+ sizingKey
544
852
  }) {
545
853
  const [scrollState, setScrollState] = useState({
546
854
  scrollLeft: 0,
547
855
  containerWidth: 0
548
856
  });
549
857
  const rafRef = useRef(null);
550
- const sizes = useMemo(() => columns.map((column) => column.getSize()), [columns]);
858
+ const sizes = useMemo(
859
+ () => columns.map((column) => column.getSize()),
860
+ // `sizingKey` is an explicit invalidation hook for stable Column objects whose getSize value changed.
861
+ // eslint-disable-next-line react-hooks/exhaustive-deps
862
+ [columns, sizingKey]
863
+ );
551
864
  const offsets = useMemo(() => {
552
865
  const next = new Array(columns.length + 1);
553
866
  next[0] = 0;
@@ -1125,12 +1438,12 @@ function getRegisteredCellTypes() {
1125
1438
 
1126
1439
  // src/hooks/useAutoMeasurements.ts
1127
1440
  var NON_DATA_COLUMN_IDS = /* @__PURE__ */ new Set(["select", "expand", "drag", "actions"]);
1128
- function getColumnId(col) {
1441
+ function getColumnId2(col) {
1129
1442
  const id = col.id ?? col.accessorKey;
1130
1443
  return typeof id === "string" ? id : void 0;
1131
1444
  }
1132
1445
  function defaultShouldMeasure(col) {
1133
- const id = getColumnId(col);
1446
+ const id = getColumnId2(col);
1134
1447
  if (!id) return false;
1135
1448
  if (id.startsWith("_")) return false;
1136
1449
  if (NON_DATA_COLUMN_IDS.has(id)) return false;
@@ -1155,11 +1468,11 @@ function useAutoMeasurements({
1155
1468
  getColumnWidth = defaultGetColumnWidth,
1156
1469
  shouldMeasureColumn = defaultShouldMeasure
1157
1470
  }) {
1158
- const widthKey = columns.map((c) => `${getColumnId(c) ?? "?"}:${getColumnWidth(c)}`).join("|");
1471
+ const widthKey = columns.map((c) => `${getColumnId2(c) ?? "?"}:${getColumnWidth(c)}`).join("|");
1159
1472
  return useMemo(() => {
1160
1473
  const result = [];
1161
1474
  for (const col of columns) {
1162
- const id = getColumnId(col);
1475
+ const id = getColumnId2(col);
1163
1476
  if (!id) continue;
1164
1477
  if (!shouldMeasureColumn(col)) continue;
1165
1478
  const recipe = resolveMeasureRecipe(col, defaultRecipe);
@@ -1478,16 +1791,170 @@ function FloatingFilter({ column }) {
1478
1791
  }
1479
1792
  ) });
1480
1793
  }
1481
- var DRAG_MIME = "application/yable-column";
1794
+ var REORDER_TRANSITION = "transform 180ms cubic-bezier(0.2, 0, 0, 1)";
1795
+ function transformAt(i, d) {
1796
+ if (i === d.fromIndex) return 0;
1797
+ if (d.toIndex > d.fromIndex) return i > d.fromIndex && i < d.toIndex ? -d.width : 0;
1798
+ return i >= d.toIndex && i < d.fromIndex ? d.width : 0;
1799
+ }
1482
1800
  function TableHeader({
1483
1801
  table,
1484
1802
  floatingFilters = false
1485
1803
  }) {
1486
1804
  const headerGroups = table.getHeaderGroups();
1487
1805
  const visibleColumns = table.getVisibleLeafColumns();
1488
- return /* @__PURE__ */ jsxs("thead", { className: "yable-thead", children: [
1489
- headerGroups.map((headerGroup) => /* @__PURE__ */ jsx("tr", { className: "yable-header-row", children: headerGroup.headers.map((header) => /* @__PURE__ */ jsx(HeaderCell, { header, table }, header.id)) }, headerGroup.id)),
1490
- floatingFilters && visibleColumns.length > 0 && /* @__PURE__ */ jsx("tr", { className: "yable-header-row yable-header-row--filters", children: visibleColumns.map((column) => /* @__PURE__ */ jsx(FloatingFilterCell, { column }, `${column.id}-filter`)) })
1806
+ const theadRef = useRef(null);
1807
+ const reorderEndRef = useRef(0);
1808
+ const [drag, setDrag] = useState(null);
1809
+ const commitReorder = useCallback(
1810
+ (d) => {
1811
+ if (d.toIndex === d.fromIndex || d.toIndex === d.fromIndex + 1) return;
1812
+ const order = table.getState().columnOrder;
1813
+ const base = order && order.length > 0 ? [...order] : d.layout.map((l) => l.id);
1814
+ const targetId = d.toIndex < d.layout.length ? d.layout[d.toIndex].id : null;
1815
+ const next = base.filter((id) => id !== d.columnId);
1816
+ let insertAt = targetId ? next.indexOf(targetId) : next.length;
1817
+ if (insertAt === -1) insertAt = next.length;
1818
+ next.splice(insertAt, 0, d.columnId);
1819
+ table.setColumnOrder(next);
1820
+ },
1821
+ [table]
1822
+ );
1823
+ const beginReorder = useCallback(
1824
+ (e, columnId) => {
1825
+ if (e.button !== 0) return;
1826
+ const thead = theadRef.current;
1827
+ if (!thead) return;
1828
+ const startX = e.clientX;
1829
+ const startY = e.clientY;
1830
+ const layout = [];
1831
+ let top = 0;
1832
+ let height = 0;
1833
+ for (const c of visibleColumns) {
1834
+ const th = thead.querySelector(`th[data-column-id="${CSS.escape(c.id)}"]`);
1835
+ if (!th) return;
1836
+ const r = th.getBoundingClientRect();
1837
+ layout.push({ id: c.id, left: r.left, width: r.width });
1838
+ if (c.id === columnId) {
1839
+ top = r.top;
1840
+ height = r.height;
1841
+ }
1842
+ }
1843
+ const fromIndex = layout.findIndex((l) => l.id === columnId);
1844
+ if (fromIndex < 0) return;
1845
+ const src = layout[fromIndex];
1846
+ const bodyRoot = thead.closest("table");
1847
+ const applyBody = (d) => {
1848
+ if (!bodyRoot) return;
1849
+ visibleColumns.forEach((col, i) => {
1850
+ if (col.getIsPinned()) return;
1851
+ const tx = transformAt(i, d);
1852
+ bodyRoot.querySelectorAll(`td[data-column-id="${CSS.escape(col.id)}"]`).forEach((td) => {
1853
+ td.style.transition = REORDER_TRANSITION;
1854
+ td.style.opacity = i === d.fromIndex ? "0" : "";
1855
+ td.style.transform = i !== d.fromIndex && tx ? `translateX(${tx}px)` : "";
1856
+ });
1857
+ });
1858
+ };
1859
+ const clearBody = () => {
1860
+ bodyRoot?.querySelectorAll("td[data-column-id]").forEach((td) => {
1861
+ td.style.transform = "";
1862
+ td.style.transition = "";
1863
+ td.style.opacity = "";
1864
+ });
1865
+ };
1866
+ let started = false;
1867
+ let latest = {
1868
+ columnId,
1869
+ fromIndex,
1870
+ toIndex: fromIndex,
1871
+ pointerX: startX,
1872
+ grabOffsetX: startX - src.left,
1873
+ width: src.width,
1874
+ top,
1875
+ height,
1876
+ layout
1877
+ };
1878
+ const computeToIndex = (x) => {
1879
+ let t = layout.findIndex((l) => x < l.left + l.width / 2);
1880
+ if (t === -1) t = layout.length;
1881
+ return t;
1882
+ };
1883
+ const onMove = (ev) => {
1884
+ if (!started) {
1885
+ if (Math.abs(ev.clientX - startX) < 4 && Math.abs(ev.clientY - startY) < 4) return;
1886
+ started = true;
1887
+ table.setColumnDragActive(true);
1888
+ document.body.style.userSelect = "none";
1889
+ document.body.style.cursor = "grabbing";
1890
+ }
1891
+ latest = { ...latest, pointerX: ev.clientX, toIndex: computeToIndex(ev.clientX) };
1892
+ setDrag(latest);
1893
+ applyBody(latest);
1894
+ };
1895
+ const finish = () => {
1896
+ window.removeEventListener("pointermove", onMove);
1897
+ window.removeEventListener("pointerup", finish);
1898
+ window.removeEventListener("pointercancel", finish);
1899
+ if (started) {
1900
+ commitReorder(latest);
1901
+ reorderEndRef.current = Date.now();
1902
+ table.setColumnDragActive(false);
1903
+ document.body.style.userSelect = "";
1904
+ document.body.style.cursor = "";
1905
+ clearBody();
1906
+ }
1907
+ setDrag(null);
1908
+ };
1909
+ window.addEventListener("pointermove", onMove);
1910
+ window.addEventListener("pointerup", finish);
1911
+ window.addEventListener("pointercancel", finish);
1912
+ },
1913
+ [visibleColumns, table, commitReorder]
1914
+ );
1915
+ const transformFor = useCallback(
1916
+ (columnId) => {
1917
+ if (!drag) return 0;
1918
+ const i = visibleColumns.findIndex((c) => c.id === columnId);
1919
+ if (i < 0) return 0;
1920
+ return transformAt(i, drag);
1921
+ },
1922
+ [drag, visibleColumns]
1923
+ );
1924
+ const dragColumn = drag ? visibleColumns.find((c) => c.id === drag.columnId) : null;
1925
+ return /* @__PURE__ */ jsxs("thead", { className: "yable-thead", ref: theadRef, children: [
1926
+ headerGroups.map((headerGroup) => /* @__PURE__ */ jsx("tr", { className: "yable-header-row", children: headerGroup.headers.map((header) => /* @__PURE__ */ jsx(
1927
+ HeaderCell,
1928
+ {
1929
+ header,
1930
+ table,
1931
+ onReorderPointerDown: beginReorder,
1932
+ dragTransform: transformFor(header.column.id),
1933
+ isDragSource: drag?.columnId === header.column.id,
1934
+ dragActive: drag !== null,
1935
+ reorderEndRef
1936
+ },
1937
+ header.id
1938
+ )) }, headerGroup.id)),
1939
+ floatingFilters && visibleColumns.length > 0 && /* @__PURE__ */ jsx("tr", { className: "yable-header-row yable-header-row--filters", children: visibleColumns.map((column) => /* @__PURE__ */ jsx(FloatingFilterCell, { column }, `${column.id}-filter`)) }),
1940
+ drag && dragColumn && typeof document !== "undefined" && createPortal(
1941
+ /* @__PURE__ */ jsx(
1942
+ "div",
1943
+ {
1944
+ className: "yable-col-drag-ghost",
1945
+ "aria-hidden": "true",
1946
+ style: {
1947
+ position: "fixed",
1948
+ top: drag.top,
1949
+ left: drag.pointerX - drag.grabOffsetX,
1950
+ width: drag.width,
1951
+ height: drag.height
1952
+ },
1953
+ children: typeof dragColumn.columnDef.header === "string" ? dragColumn.columnDef.header : dragColumn.id
1954
+ }
1955
+ ),
1956
+ document.body
1957
+ )
1491
1958
  ] });
1492
1959
  }
1493
1960
  function FloatingFilterCell({
@@ -1516,7 +1983,12 @@ function FloatingFilterCell({
1516
1983
  }
1517
1984
  function HeaderCell({
1518
1985
  header,
1519
- table
1986
+ table,
1987
+ onReorderPointerDown,
1988
+ dragTransform,
1989
+ isDragSource,
1990
+ dragActive,
1991
+ reorderEndRef
1520
1992
  }) {
1521
1993
  const column = header.column;
1522
1994
  const canSort = column.getCanSort();
@@ -1524,6 +1996,7 @@ function HeaderCell({
1524
1996
  const sortIndex = column.getSortIndex();
1525
1997
  const canResize = column.getCanResize();
1526
1998
  const canReorder = column.getCanReorder() && !header.isPlaceholder;
1999
+ const pinned = column.getIsPinned();
1527
2000
  const headerContent = header.isPlaceholder ? null : typeof column.columnDef.header === "function" ? column.columnDef.header(header.getContext()) : column.columnDef.header ?? header.id;
1528
2001
  const style = useMemo(() => {
1529
2002
  const s = {
@@ -1531,18 +2004,19 @@ function HeaderCell({
1531
2004
  minWidth: column.columnDef.minSize,
1532
2005
  maxWidth: column.columnDef.maxSize
1533
2006
  };
1534
- const pinned2 = column.getIsPinned();
1535
- if (pinned2) {
2007
+ if (pinned) {
1536
2008
  s.position = "sticky";
1537
- if (pinned2 === "left") {
2009
+ if (pinned === "left") {
1538
2010
  s.left = header.getStart("left");
1539
2011
  } else {
1540
2012
  s.right = header.getStart("right");
1541
2013
  }
1542
2014
  }
2015
+ if (!pinned && !isDragSource && dragTransform !== 0) {
2016
+ s.transform = `translateX(${dragTransform}px)`;
2017
+ }
1543
2018
  return s;
1544
- }, [header, column]);
1545
- const pinned = column.getIsPinned();
2019
+ }, [header, column, pinned, isDragSource, dragTransform]);
1546
2020
  const lastResizeEndRef = useRef(0);
1547
2021
  const startResize = useCallback(
1548
2022
  (e) => {
@@ -1562,122 +2036,60 @@ function HeaderCell({
1562
2036
  const handleResizeClick = useCallback((e) => {
1563
2037
  e.stopPropagation();
1564
2038
  }, []);
1565
- const [dragOver, setDragOver] = useState(null);
1566
- const handleDragStart = useCallback(
1567
- (e) => {
1568
- if (!canReorder) return;
1569
- e.stopPropagation();
1570
- e.dataTransfer.effectAllowed = "move";
1571
- try {
1572
- e.dataTransfer.setData(DRAG_MIME, column.id);
1573
- e.dataTransfer.setData("text/plain", column.id);
1574
- } catch {
1575
- }
1576
- table.setColumnDragActive(true);
1577
- },
1578
- [canReorder, column.id, table]
1579
- );
1580
- const handleDragOver = useCallback(
1581
- (e) => {
1582
- if (!canReorder) return;
1583
- const types = e.dataTransfer.types;
1584
- let isYableDrag = false;
1585
- for (let i = 0; i < types.length; i++) {
1586
- if (types[i] === DRAG_MIME) {
1587
- isYableDrag = true;
1588
- break;
1589
- }
1590
- }
1591
- if (!isYableDrag) return;
1592
- e.preventDefault();
1593
- e.dataTransfer.dropEffect = "move";
1594
- const rect = e.currentTarget.getBoundingClientRect();
1595
- const midpoint = rect.left + rect.width / 2;
1596
- setDragOver(e.clientX < midpoint ? "left" : "right");
1597
- },
1598
- [canReorder]
1599
- );
1600
- const handleDragLeave = useCallback((e) => {
1601
- const next = e.relatedTarget;
1602
- if (next && e.currentTarget.contains(next)) return;
1603
- setDragOver(null);
1604
- }, []);
1605
- const handleDragEnd = useCallback(() => {
1606
- setDragOver(null);
1607
- table.setColumnDragActive(false);
1608
- }, [table]);
1609
- const handleDrop = useCallback(
2039
+ const handleContentPointerDown = useCallback(
1610
2040
  (e) => {
1611
- if (!canReorder) return;
1612
- e.preventDefault();
1613
- e.stopPropagation();
1614
- const sourceId = e.dataTransfer.getData(DRAG_MIME);
1615
- const rect = e.currentTarget.getBoundingClientRect();
1616
- const insertAfter = e.clientX >= rect.left + rect.width / 2;
1617
- setDragOver(null);
1618
- table.setColumnDragActive(false);
1619
- if (!sourceId || sourceId === column.id) return;
1620
- const state = table.getState();
1621
- const allLeafs = table.getAllLeafColumns();
1622
- const baseOrder = state.columnOrder && state.columnOrder.length > 0 ? state.columnOrder : allLeafs.map((c) => c.id);
1623
- const next = [];
1624
- const seen = /* @__PURE__ */ new Set();
1625
- for (const id of baseOrder) {
1626
- if (allLeafs.some((c) => c.id === id)) {
1627
- next.push(id);
1628
- seen.add(id);
1629
- }
1630
- }
1631
- for (const c of allLeafs) {
1632
- if (!seen.has(c.id)) {
1633
- next.push(c.id);
1634
- seen.add(c.id);
1635
- }
1636
- }
1637
- const fromIdx = next.indexOf(sourceId);
1638
- if (fromIdx === -1) return;
1639
- next.splice(fromIdx, 1);
1640
- let toIdx = next.indexOf(column.id);
1641
- if (toIdx === -1) return;
1642
- if (insertAfter) toIdx += 1;
1643
- next.splice(toIdx, 0, sourceId);
1644
- table.setColumnOrder(next);
2041
+ if (!canReorder || pinned) return;
2042
+ onReorderPointerDown(e, column.id);
1645
2043
  },
1646
- [canReorder, column.id, table]
2044
+ [canReorder, pinned, onReorderPointerDown, column.id]
1647
2045
  );
1648
2046
  const handleHeaderClick = useCallback(
1649
2047
  (e) => {
2048
+ table.events.emit("header:click", {
2049
+ column,
2050
+ header,
2051
+ originalEvent: e
2052
+ });
1650
2053
  if (!canSort) return;
1651
2054
  if (Date.now() - lastResizeEndRef.current < 250) return;
2055
+ if (Date.now() - reorderEndRef.current < 250) return;
1652
2056
  const handler = column.getToggleSortingHandler();
1653
2057
  if (handler) handler(e);
1654
2058
  },
1655
- [canSort, column]
2059
+ [canSort, column, header, table.events, reorderEndRef]
2060
+ );
2061
+ const handleHeaderContextMenu = useCallback(
2062
+ (e) => {
2063
+ table.events.emit("header:contextmenu", {
2064
+ column,
2065
+ header,
2066
+ originalEvent: e
2067
+ });
2068
+ },
2069
+ [column, header, table.events]
1656
2070
  );
1657
2071
  return /* @__PURE__ */ jsxs(
1658
2072
  "th",
1659
2073
  {
1660
2074
  className: "yable-th",
1661
2075
  style,
2076
+ "data-column-id": column.id,
1662
2077
  "data-sortable": canSort || void 0,
1663
2078
  "data-pinned": pinned || void 0,
1664
- "data-reorderable": canReorder || void 0,
1665
- "data-drag-over": dragOver || void 0,
2079
+ "data-reorderable": canReorder && !pinned || void 0,
2080
+ "data-reordering": dragActive && !pinned || void 0,
2081
+ "data-drag-source": isDragSource || void 0,
1666
2082
  "aria-sort": sortDirection === "asc" ? "ascending" : sortDirection === "desc" ? "descending" : canSort ? "none" : void 0,
1667
2083
  role: "columnheader",
1668
2084
  colSpan: header.colSpan,
1669
2085
  onClick: handleHeaderClick,
1670
- onDragOver: canReorder ? handleDragOver : void 0,
1671
- onDragLeave: canReorder ? handleDragLeave : void 0,
1672
- onDrop: canReorder ? handleDrop : void 0,
2086
+ onContextMenu: handleHeaderContextMenu,
1673
2087
  children: [
1674
2088
  /* @__PURE__ */ jsxs(
1675
2089
  "div",
1676
2090
  {
1677
2091
  className: "yable-th-content",
1678
- draggable: canReorder || void 0,
1679
- onDragStart: canReorder ? handleDragStart : void 0,
1680
- onDragEnd: canReorder ? handleDragEnd : void 0,
2092
+ onPointerDown: canReorder && !pinned ? handleContentPointerDown : void 0,
1681
2093
  children: [
1682
2094
  /* @__PURE__ */ jsx("span", { children: headerContent }),
1683
2095
  canSort && /* @__PURE__ */ jsx(SortIndicator, { direction: sortDirection, index: sortIndex > 0 ? sortIndex : void 0 })
@@ -1776,19 +2188,46 @@ function CellStatusBadge(props) {
1776
2188
  }
1777
2189
  );
1778
2190
  }
2191
+ function FillHandle({
2192
+ rowIndex,
2193
+ columnIndex,
2194
+ onMouseDown
2195
+ }) {
2196
+ const handleMouseDown = useCallback(
2197
+ (e) => {
2198
+ e.preventDefault();
2199
+ e.stopPropagation();
2200
+ onMouseDown(rowIndex, columnIndex, e);
2201
+ },
2202
+ [rowIndex, columnIndex, onMouseDown]
2203
+ );
2204
+ return /* @__PURE__ */ jsx(
2205
+ "div",
2206
+ {
2207
+ className: "yable-fill-handle",
2208
+ onMouseDown: handleMouseDown,
2209
+ role: "presentation",
2210
+ "aria-hidden": "true",
2211
+ title: "Drag to fill",
2212
+ children: /* @__PURE__ */ jsx("div", { className: "yable-fill-handle-dot" })
2213
+ }
2214
+ );
2215
+ }
1779
2216
  function TableCell({
1780
2217
  cell,
1781
2218
  table,
1782
2219
  rowIndex,
1783
2220
  columnIndex,
1784
2221
  isFocused,
1785
- isTabStop
2222
+ isTabStop,
2223
+ onFillHandleMouseDown
1786
2224
  }) {
1787
2225
  const column = cell.column;
1788
2226
  const isEditing = cell.getIsEditing();
1789
2227
  const isAlwaysEditable = cell.getIsAlwaysEditable();
1790
2228
  const pinned = column.getIsPinned();
1791
2229
  const keyboardNavigationEnabled = table.options.enableKeyboardNavigation !== false;
2230
+ const cellSelectionEnabled = table.options.enableCellSelection !== false && column.columnDef.enableCellSelection !== false;
1792
2231
  const style = {
1793
2232
  width: column.getSize(),
1794
2233
  minWidth: column.columnDef.minSize,
@@ -1873,6 +2312,7 @@ function TableCell({
1873
2312
  const handleMouseDown = useCallback(
1874
2313
  (e) => {
1875
2314
  if (e.button !== 0) return;
2315
+ if (!cellSelectionEnabled) return;
1876
2316
  const clickTarget = e.target;
1877
2317
  if (isInteractiveClickTarget(clickTarget)) return;
1878
2318
  e.preventDefault();
@@ -1880,12 +2320,13 @@ function TableCell({
1880
2320
  table.startCellRangeSelection({ rowIndex, columnIndex }, { extend: e.shiftKey });
1881
2321
  e.currentTarget.focus({ preventScroll: true });
1882
2322
  },
1883
- [columnIndex, rowIndex, table]
2323
+ [cellSelectionEnabled, columnIndex, rowIndex, table]
1884
2324
  );
1885
2325
  const handleMouseEnter = useCallback(() => {
2326
+ if (!cellSelectionEnabled) return;
1886
2327
  if (!table.getState().cellSelection?.isDragging) return;
1887
2328
  table.updateCellRangeSelection({ rowIndex, columnIndex });
1888
- }, [columnIndex, rowIndex, table]);
2329
+ }, [cellSelectionEnabled, columnIndex, rowIndex, table]);
1889
2330
  const handleMouseUp = useCallback(() => {
1890
2331
  if (!table.getState().cellSelection?.isDragging) return;
1891
2332
  table.endCellRangeSelection();
@@ -1895,6 +2336,7 @@ function TableCell({
1895
2336
  const cellStyleDef = column.columnDef.cellStyle;
1896
2337
  const userStyle = typeof cellStyleDef === "function" ? cellStyleDef(cell.getContext()) : cellStyleDef;
1897
2338
  const mergedStyle = userStyle ? { ...style, ...userStyle } : style;
2339
+ const showFillHandle = isFocused && Boolean(table.options.enableFillHandle) && onFillHandleMouseDown != null;
1898
2340
  const classNames = [
1899
2341
  "yable-td",
1900
2342
  isFocused && "yable-cell--focused",
@@ -1948,6 +2390,14 @@ function TableCell({
1948
2390
  onRetry: () => void table.retryCommit(cell.row.id, column.id),
1949
2391
  onDismiss: () => table.dismissCommit(cell.row.id, column.id)
1950
2392
  }
2393
+ ),
2394
+ showFillHandle && onFillHandleMouseDown && /* @__PURE__ */ jsx(
2395
+ FillHandle,
2396
+ {
2397
+ rowIndex,
2398
+ columnIndex,
2399
+ onMouseDown: onFillHandleMouseDown
2400
+ }
1951
2401
  )
1952
2402
  ]
1953
2403
  }
@@ -1960,7 +2410,7 @@ function isInteractiveClickTarget(element) {
1960
2410
  );
1961
2411
  return interactive !== null;
1962
2412
  }
1963
- var ErrorBoundary = class extends React3.Component {
2413
+ var ErrorBoundary = class extends React4.Component {
1964
2414
  constructor(props) {
1965
2415
  super(props);
1966
2416
  this.state = { hasError: false, error: null };
@@ -2006,7 +2456,7 @@ var ErrorBoundary = class extends React3.Component {
2006
2456
  return this.props.children;
2007
2457
  }
2008
2458
  };
2009
- var CellErrorBoundary = class extends React3.Component {
2459
+ var CellErrorBoundary = class extends React4.Component {
2010
2460
  constructor(props) {
2011
2461
  super(props);
2012
2462
  this.state = { hasError: false, error: null };
@@ -2049,10 +2499,38 @@ var CellErrorBoundary = class extends React3.Component {
2049
2499
  return this.props.children;
2050
2500
  }
2051
2501
  };
2502
+ function MasterDetail({
2503
+ row,
2504
+ table,
2505
+ colSpan,
2506
+ renderDetailPanel,
2507
+ animationClass
2508
+ }) {
2509
+ const renderer = renderDetailPanel ?? table.options.renderDetailPanel;
2510
+ if (!renderer) return null;
2511
+ const content = renderer(row);
2512
+ if (content == null) return null;
2513
+ const classes = [
2514
+ "yable-detail-row",
2515
+ "yable-detail-row--animated",
2516
+ animationClass
2517
+ ].filter(Boolean).join(" ");
2518
+ return /* @__PURE__ */ jsx(
2519
+ "tr",
2520
+ {
2521
+ className: classes,
2522
+ "data-detail-for": row.id,
2523
+ role: "row",
2524
+ "aria-label": `Details for row ${row.id}`,
2525
+ children: /* @__PURE__ */ jsx("td", { className: "yable-detail-cell", colSpan, role: "cell", children: /* @__PURE__ */ jsx("div", { className: "yable-detail-panel", children: /* @__PURE__ */ jsx("div", { className: "yable-detail-panel-inner", children: content }) }) })
2526
+ }
2527
+ );
2528
+ }
2052
2529
  function TableBody({
2053
2530
  table,
2054
2531
  clickableRows,
2055
- colgroup
2532
+ colgroup,
2533
+ onFillHandleMouseDown
2056
2534
  }) {
2057
2535
  const rows = table.getRowModel().rows;
2058
2536
  const visibleColumns = table.getVisibleLeafColumns();
@@ -2062,6 +2540,7 @@ function TableBody({
2062
2540
  range: null,
2063
2541
  isDragging: false
2064
2542
  };
2543
+ const pendingValues = table.getState().editing.pendingValues ?? {};
2065
2544
  const options = table.options;
2066
2545
  const enableVirtualization = options.enableVirtualization ?? false;
2067
2546
  const scrollContainerRef = useRef(null);
@@ -2105,24 +2584,41 @@ function TableBody({
2105
2584
  window.removeEventListener("mouseup", handleWindowMouseUp);
2106
2585
  };
2107
2586
  }, [table]);
2587
+ const renderRow = (row, rowIndex, pinnedPosition) => /* @__PURE__ */ jsx(
2588
+ MemoizedTableRow,
2589
+ {
2590
+ row,
2591
+ table,
2592
+ rowIndex,
2593
+ visibleColumns,
2594
+ isSelected: row.getIsSelected(),
2595
+ isExpanded: row.getIsExpanded(),
2596
+ activeColumnId: activeCell?.rowId === row.id ? activeCell.columnId : void 0,
2597
+ focusedColumnIndex: focusedCell?.rowIndex === rowIndex ? focusedCell.columnIndex : null,
2598
+ hasFocusedCell: focusedCell !== null,
2599
+ cellSelectionKey,
2600
+ pendingValuesKey: getPendingValuesKey(pendingValues[row.id]),
2601
+ clickable: clickableRows,
2602
+ pinnedPosition,
2603
+ onFillHandleMouseDown
2604
+ },
2605
+ row.id
2606
+ );
2108
2607
  if (!enableVirtualization) {
2109
- return /* @__PURE__ */ jsx("tbody", { className: "yable-tbody", children: rows.map((row, rowIndex) => /* @__PURE__ */ jsx(
2110
- MemoizedTableRow,
2111
- {
2112
- row,
2113
- table,
2114
- rowIndex,
2115
- visibleColumns,
2116
- isSelected: row.getIsSelected(),
2117
- isExpanded: row.getIsExpanded(),
2118
- activeColumnId: activeCell?.rowId === row.id ? activeCell.columnId : void 0,
2119
- focusedColumnIndex: focusedCell?.rowIndex === rowIndex ? focusedCell.columnIndex : null,
2120
- hasFocusedCell: focusedCell !== null,
2121
- cellSelectionKey,
2122
- clickable: clickableRows
2123
- },
2124
- row.id
2125
- )) });
2608
+ const rowPinning = table.getState().rowPinning;
2609
+ const hasPinnedRows = (rowPinning.top?.length ?? 0) > 0 || (rowPinning.bottom?.length ?? 0) > 0;
2610
+ if (hasPinnedRows) {
2611
+ const topRows = table.getTopRows();
2612
+ const centerRows = table.getCenterRows();
2613
+ const bottomRows = table.getBottomRows();
2614
+ let visualIndex = 0;
2615
+ return /* @__PURE__ */ jsxs("tbody", { className: "yable-tbody", children: [
2616
+ topRows.map((row) => renderRow(row, visualIndex++, "top")),
2617
+ centerRows.map((row) => renderRow(row, visualIndex++)),
2618
+ bottomRows.map((row) => renderRow(row, visualIndex++, "bottom"))
2619
+ ] });
2620
+ }
2621
+ return /* @__PURE__ */ jsx("tbody", { className: "yable-tbody", children: rows.map((row, rowIndex) => renderRow(row, rowIndex)) });
2126
2622
  }
2127
2623
  const hasPretextData = !!(pretextHeights && pretextPrefixSums);
2128
2624
  const fixedRowHeight = typeof rowHeight === "number" && !hasPretextData ? rowHeight : void 0;
@@ -2171,7 +2667,9 @@ function TableBody({
2171
2667
  focusedColumnIndex: focusedCell?.rowIndex === vRow.index ? focusedCell.columnIndex : null,
2172
2668
  hasFocusedCell: focusedCell !== null,
2173
2669
  cellSelectionKey,
2670
+ pendingValuesKey: getPendingValuesKey(pendingValues[row.id]),
2174
2671
  clickable: clickableRows,
2672
+ onFillHandleMouseDown,
2175
2673
  virtualStyle: {
2176
2674
  position: "absolute",
2177
2675
  top: 0,
@@ -2203,13 +2701,19 @@ function TableRowInner({
2203
2701
  focusedColumnIndex,
2204
2702
  hasFocusedCell,
2205
2703
  cellSelectionKey: _cellSelectionKey,
2704
+ pendingValuesKey: _pendingValuesKey,
2206
2705
  clickable,
2207
- virtualStyle
2706
+ pinnedPosition,
2707
+ virtualStyle,
2708
+ onFillHandleMouseDown
2208
2709
  }) {
2209
2710
  const allCells = row.getAllCells();
2210
2711
  const visibleCells = visibleColumns.map((column) => allCells.find((cell) => cell.column.id === column.id)).filter((cell) => cell != null);
2211
2712
  const handleClick = useCallback(
2212
2713
  (e) => {
2714
+ if (table.options.enableRowClickSelection && row.getCanSelect() && !isInteractiveClickTarget2(e.target)) {
2715
+ row.toggleSelected();
2716
+ }
2213
2717
  if (clickable) {
2214
2718
  table.events.emit("row:click", {
2215
2719
  row,
@@ -2217,7 +2721,7 @@ function TableRowInner({
2217
2721
  });
2218
2722
  }
2219
2723
  },
2220
- [clickable, table.events, row]
2724
+ [clickable, table, row]
2221
2725
  );
2222
2726
  const handleDoubleClick = useCallback(
2223
2727
  (e) => {
@@ -2239,15 +2743,26 @@ function TableRowInner({
2239
2743
  );
2240
2744
  const selectionEnabled = Boolean(table.options.enableRowSelection);
2241
2745
  const expansionEnabled = Boolean(table.options.enableExpanding);
2746
+ const rowClassNameDef = table.options.rowClassName;
2747
+ const userRowClassName = typeof rowClassNameDef === "function" ? rowClassNameDef(row) : rowClassNameDef;
2748
+ const rowStyleDef = table.options.rowStyle;
2749
+ const userRowStyle = typeof rowStyleDef === "function" ? rowStyleDef(row) : rowStyleDef;
2750
+ const mergedRowStyle = userRowStyle ? { ...virtualStyle, ...userRowStyle } : virtualStyle;
2751
+ const rowClassName = [
2752
+ "yable-tr",
2753
+ pinnedPosition && `yable-tr--pinned-${pinnedPosition}`,
2754
+ userRowClassName
2755
+ ].filter(Boolean).join(" ");
2242
2756
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2243
2757
  /* @__PURE__ */ jsx(
2244
2758
  "tr",
2245
2759
  {
2246
- className: "yable-tr",
2247
- style: virtualStyle,
2760
+ className: rowClassName,
2761
+ style: mergedRowStyle,
2248
2762
  "data-selected": isSelected || void 0,
2249
2763
  "data-expanded": isExpanded || void 0,
2250
2764
  "data-clickable": clickable || void 0,
2765
+ "data-pinned-row": pinnedPosition,
2251
2766
  "data-row-id": row.id,
2252
2767
  "data-row-index": rowIndex,
2253
2768
  "aria-selected": selectionEnabled ? isSelected : void 0,
@@ -2270,7 +2785,8 @@ function TableRowInner({
2270
2785
  rowIndex,
2271
2786
  columnIndex,
2272
2787
  isFocused,
2273
- isTabStop
2788
+ isTabStop,
2789
+ onFillHandleMouseDown
2274
2790
  }
2275
2791
  )
2276
2792
  },
@@ -2279,7 +2795,7 @@ function TableRowInner({
2279
2795
  })
2280
2796
  }
2281
2797
  ),
2282
- isExpanded && /* @__PURE__ */ jsx("tr", { className: "yable-expand-row", children: /* @__PURE__ */ jsx("td", { className: "yable-td", colSpan: visibleColumns.length, children: typeof row._renderExpanded === "function" ? row._renderExpanded() : null }) })
2798
+ isExpanded && /* @__PURE__ */ jsx(MasterDetail, { row, table, colSpan: visibleColumns.length })
2283
2799
  ] });
2284
2800
  }
2285
2801
  function areRowPropsEqual(prev, next) {
@@ -2290,10 +2806,12 @@ function areRowPropsEqual(prev, next) {
2290
2806
  if (prev.isSelected !== next.isSelected) return false;
2291
2807
  if (prev.isExpanded !== next.isExpanded) return false;
2292
2808
  if (prev.clickable !== next.clickable) return false;
2809
+ if (prev.pinnedPosition !== next.pinnedPosition) return false;
2293
2810
  if (prev.activeColumnId !== next.activeColumnId) return false;
2294
2811
  if (prev.focusedColumnIndex !== next.focusedColumnIndex) return false;
2295
2812
  if (prev.hasFocusedCell !== next.hasFocusedCell) return false;
2296
2813
  if (prev.cellSelectionKey !== next.cellSelectionKey) return false;
2814
+ if (prev.pendingValuesKey !== next.pendingValuesKey) return false;
2297
2815
  if (prev.virtualStyle !== next.virtualStyle) {
2298
2816
  if (!prev.virtualStyle || !next.virtualStyle) return false;
2299
2817
  if (prev.virtualStyle.transform !== next.virtualStyle.transform) return false;
@@ -2302,7 +2820,17 @@ function areRowPropsEqual(prev, next) {
2302
2820
  if (prev.table !== next.table) return false;
2303
2821
  return true;
2304
2822
  }
2305
- var MemoizedTableRow = React3.memo(TableRowInner, areRowPropsEqual);
2823
+ var MemoizedTableRow = React4.memo(TableRowInner, areRowPropsEqual);
2824
+ function getPendingValuesKey(values) {
2825
+ if (!values) return "";
2826
+ return Object.keys(values).sort().map((key) => `${key}:${String(values[key])}`).join("|");
2827
+ }
2828
+ function isInteractiveClickTarget2(target) {
2829
+ if (!(target instanceof HTMLElement)) return false;
2830
+ return Boolean(
2831
+ target.closest('input, textarea, select, button, a[href], [contenteditable="true"]')
2832
+ );
2833
+ }
2306
2834
  function TableFooter({ table }) {
2307
2835
  const footerGroups = table.getFooterGroups();
2308
2836
  if (!footerGroups.length) return null;
@@ -2510,8 +3038,8 @@ function StatusDivider() {
2510
3038
  return /* @__PURE__ */ jsx("span", { className: "yable-status-bar-divider", "aria-hidden": "true" });
2511
3039
  }
2512
3040
  function PanelGroup({ children }) {
2513
- const items = React3.Children.toArray(children).filter(Boolean);
2514
- return /* @__PURE__ */ jsx(Fragment, { children: items.map((child, i) => /* @__PURE__ */ jsxs(React3.Fragment, { children: [
3041
+ const items = React4.Children.toArray(children).filter(Boolean);
3042
+ return /* @__PURE__ */ jsx(Fragment, { children: items.map((child, i) => /* @__PURE__ */ jsxs(React4.Fragment, { children: [
2515
3043
  i > 0 && /* @__PURE__ */ jsx(StatusDivider, {}),
2516
3044
  child
2517
3045
  ] }, i)) });
@@ -2590,29 +3118,73 @@ function StatusBar({
2590
3118
  ] });
2591
3119
  }
2592
3120
  function SearchIcon() {
2593
- return /* @__PURE__ */ jsxs("svg", { className: "yable-sidebar-search-icon", width: "13", height: "13", viewBox: "0 0 14 14", fill: "none", "aria-hidden": "true", children: [
2594
- /* @__PURE__ */ jsx("circle", { cx: "6.25", cy: "6.25", r: "4.25", stroke: "currentColor", strokeWidth: "1.5" }),
2595
- /* @__PURE__ */ jsx("path", { d: "M9.5 9.5L12.5 12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
2596
- ] });
3121
+ return /* @__PURE__ */ jsxs(
3122
+ "svg",
3123
+ {
3124
+ className: "yable-sidebar-search-icon",
3125
+ width: "13",
3126
+ height: "13",
3127
+ viewBox: "0 0 14 14",
3128
+ fill: "none",
3129
+ "aria-hidden": "true",
3130
+ children: [
3131
+ /* @__PURE__ */ jsx("circle", { cx: "6.25", cy: "6.25", r: "4.25", stroke: "currentColor", strokeWidth: "1.5" }),
3132
+ /* @__PURE__ */ jsx("path", { d: "M9.5 9.5L12.5 12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
3133
+ ]
3134
+ }
3135
+ );
2597
3136
  }
2598
3137
  function VisibilityIcon({ visible }) {
2599
3138
  if (visible) {
2600
3139
  return /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": "true", children: [
2601
- /* @__PURE__ */ jsx("path", { d: "M1 7s2.5-4 6-4 6 4 6 4-2.5 4-6 4-6-4-6-4z", stroke: "currentColor", strokeWidth: "1.2", strokeLinejoin: "round" }),
3140
+ /* @__PURE__ */ jsx(
3141
+ "path",
3142
+ {
3143
+ d: "M1 7s2.5-4 6-4 6 4 6 4-2.5 4-6 4-6-4-6-4z",
3144
+ stroke: "currentColor",
3145
+ strokeWidth: "1.2",
3146
+ strokeLinejoin: "round"
3147
+ }
3148
+ ),
2602
3149
  /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "2", stroke: "currentColor", strokeWidth: "1.2" })
2603
3150
  ] });
2604
3151
  }
2605
3152
  return /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", "aria-hidden": "true", children: [
2606
- /* @__PURE__ */ jsx("path", { d: "M1 7s2.5-4 6-4 6 4 6 4-2.5 4-6 4-6-4-6-4z", stroke: "currentColor", strokeWidth: "1.2", strokeLinejoin: "round", opacity: "0.3" }),
2607
- /* @__PURE__ */ jsx("line", { x1: "2", y1: "2", x2: "12", y2: "12", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round", opacity: "0.5" })
3153
+ /* @__PURE__ */ jsx(
3154
+ "path",
3155
+ {
3156
+ d: "M1 7s2.5-4 6-4 6 4 6 4-2.5 4-6 4-6-4-6-4z",
3157
+ stroke: "currentColor",
3158
+ strokeWidth: "1.2",
3159
+ strokeLinejoin: "round",
3160
+ opacity: "0.3"
3161
+ }
3162
+ ),
3163
+ /* @__PURE__ */ jsx(
3164
+ "line",
3165
+ {
3166
+ x1: "2",
3167
+ y1: "2",
3168
+ x2: "12",
3169
+ y2: "12",
3170
+ stroke: "currentColor",
3171
+ strokeWidth: "1.2",
3172
+ strokeLinecap: "round",
3173
+ opacity: "0.5"
3174
+ }
3175
+ )
2608
3176
  ] });
2609
3177
  }
2610
- function ColumnsPanel({
2611
- table
2612
- }) {
3178
+ function ColumnsPanel({ table }) {
2613
3179
  const [search, setSearch] = useState("");
2614
3180
  const [draggedId, setDraggedId] = useState(null);
2615
- const columns = table.getAllLeafColumns();
3181
+ const allColumns = table.getAllLeafColumns();
3182
+ const columnOrder = table.getState().columnOrder;
3183
+ const columns = columnOrder && columnOrder.length > 0 ? [...allColumns].sort((a, b) => {
3184
+ const ia = columnOrder.indexOf(a.id);
3185
+ const ib = columnOrder.indexOf(b.id);
3186
+ return (ia === -1 ? Number.MAX_SAFE_INTEGER : ia) - (ib === -1 ? Number.MAX_SAFE_INTEGER : ib);
3187
+ }) : allColumns;
2616
3188
  const visibleCount = columns.filter((c) => c.getIsVisible()).length;
2617
3189
  const filteredColumns = search ? columns.filter((col) => {
2618
3190
  const header = typeof col.columnDef.header === "string" ? col.columnDef.header : col.id;
@@ -3039,6 +3611,7 @@ function ContextMenu({
3039
3611
  y,
3040
3612
  onClose,
3041
3613
  table,
3614
+ targetColumnId,
3042
3615
  customItems
3043
3616
  }) {
3044
3617
  const menuRef = useRef(null);
@@ -3082,6 +3655,17 @@ function ContextMenu({
3082
3655
  },
3083
3656
  [onClose]
3084
3657
  );
3658
+ const resolveSortColumn = () => {
3659
+ if (targetColumnId) {
3660
+ const column = table.getColumn(targetColumnId);
3661
+ if (column) return column;
3662
+ }
3663
+ const focused = table.getFocusedCell();
3664
+ if (focused) {
3665
+ return table.getVisibleLeafColumns()[focused.columnIndex];
3666
+ }
3667
+ return void 0;
3668
+ };
3085
3669
  const defaultItems = [
3086
3670
  {
3087
3671
  id: "copy",
@@ -3130,14 +3714,14 @@ function ContextMenu({
3130
3714
  id: "sort-asc",
3131
3715
  label: "Sort Ascending",
3132
3716
  action: () => {
3133
- table.setSorting([]);
3717
+ resolveSortColumn()?.toggleSorting(false);
3134
3718
  }
3135
3719
  },
3136
3720
  {
3137
3721
  id: "sort-desc",
3138
3722
  label: "Sort Descending",
3139
3723
  action: () => {
3140
- table.setSorting([]);
3724
+ resolveSortColumn()?.toggleSorting(true);
3141
3725
  }
3142
3726
  },
3143
3727
  {
@@ -3229,11 +3813,13 @@ function useContextMenu() {
3229
3813
  const [x, setX] = useState(0);
3230
3814
  const [y, setY] = useState(0);
3231
3815
  const [targetTable, setTargetTable] = useState(null);
3816
+ const [targetColumnId, setTargetColumnId] = useState(void 0);
3232
3817
  const open = useCallback(
3233
- (clientX, clientY, table) => {
3818
+ (clientX, clientY, table, columnId) => {
3234
3819
  setX(clientX);
3235
3820
  setY(clientY);
3236
3821
  setTargetTable(table);
3822
+ setTargetColumnId(columnId);
3237
3823
  setIsOpen(true);
3238
3824
  },
3239
3825
  []
@@ -3241,6 +3827,7 @@ function useContextMenu() {
3241
3827
  const close = useCallback(() => {
3242
3828
  setIsOpen(false);
3243
3829
  setTargetTable(null);
3830
+ setTargetColumnId(void 0);
3244
3831
  }, []);
3245
3832
  useEffect(() => {
3246
3833
  if (!isOpen) return;
@@ -3255,7 +3842,7 @@ function useContextMenu() {
3255
3842
  document.removeEventListener("click", handleClick);
3256
3843
  };
3257
3844
  }, [isOpen, close]);
3258
- return { isOpen, x, y, targetTable, open, close };
3845
+ return { isOpen, x, y, targetTable, targetColumnId, open, close };
3259
3846
  }
3260
3847
  function useKeyboardNavigation(table, options = {}) {
3261
3848
  const {
@@ -3455,6 +4042,85 @@ function isEditableTarget(element) {
3455
4042
  }
3456
4043
  return element.isContentEditable;
3457
4044
  }
4045
+ function useFillHandle(table, options = {}) {
4046
+ const { enabled = true } = options;
4047
+ const [dragState, setDragState] = useState({
4048
+ isDragging: false,
4049
+ sourceCell: null,
4050
+ currentCell: null
4051
+ });
4052
+ const dragRef = useRef(dragState);
4053
+ dragRef.current = dragState;
4054
+ const onFillHandleMouseDown = useCallback(
4055
+ (rowIndex, columnIndex, e) => {
4056
+ if (!enabled) return;
4057
+ e.preventDefault();
4058
+ e.stopPropagation();
4059
+ setDragState({
4060
+ isDragging: true,
4061
+ sourceCell: { rowIndex, columnIndex },
4062
+ currentCell: { rowIndex, columnIndex }
4063
+ });
4064
+ },
4065
+ [enabled]
4066
+ );
4067
+ useEffect(() => {
4068
+ if (!dragState.isDragging) return;
4069
+ const handleMouseMove = (e) => {
4070
+ const target = document.elementFromPoint(e.clientX, e.clientY);
4071
+ if (!target) return;
4072
+ const td = target.closest("td[data-column-id]");
4073
+ const tr = target.closest("tr[data-row-id]");
4074
+ if (!td || !tr) return;
4075
+ const columnId = td.getAttribute("data-column-id");
4076
+ const rowId = tr.getAttribute("data-row-id");
4077
+ if (!columnId || !rowId) return;
4078
+ const rows = table.getRowModel().rows;
4079
+ const columns = table.getVisibleLeafColumns();
4080
+ const rowIndex = rows.findIndex((r) => r.id === rowId);
4081
+ const columnIndex = columns.findIndex((c) => c.id === columnId);
4082
+ if (rowIndex === -1 || columnIndex === -1) return;
4083
+ setDragState((prev) => ({
4084
+ ...prev,
4085
+ currentCell: { rowIndex, columnIndex }
4086
+ }));
4087
+ };
4088
+ const handleMouseUp = () => {
4089
+ const current = dragRef.current;
4090
+ if (current.sourceCell && current.currentCell) {
4091
+ const source = current.sourceCell;
4092
+ const target = current.currentCell;
4093
+ if (source.rowIndex !== target.rowIndex || source.columnIndex !== target.columnIndex) {
4094
+ const sourceRange = {
4095
+ startRow: source.rowIndex,
4096
+ startCol: source.columnIndex,
4097
+ endRow: source.rowIndex,
4098
+ endCol: source.columnIndex
4099
+ };
4100
+ const targetRange = {
4101
+ startRow: Math.min(source.rowIndex, target.rowIndex),
4102
+ startCol: Math.min(source.columnIndex, target.columnIndex),
4103
+ endRow: Math.max(source.rowIndex, target.rowIndex),
4104
+ endCol: Math.max(source.columnIndex, target.columnIndex)
4105
+ };
4106
+ table.fillRange(sourceRange, targetRange);
4107
+ }
4108
+ }
4109
+ setDragState({
4110
+ isDragging: false,
4111
+ sourceCell: null,
4112
+ currentCell: null
4113
+ });
4114
+ };
4115
+ document.addEventListener("mousemove", handleMouseMove);
4116
+ document.addEventListener("mouseup", handleMouseUp);
4117
+ return () => {
4118
+ document.removeEventListener("mousemove", handleMouseMove);
4119
+ document.removeEventListener("mouseup", handleMouseUp);
4120
+ };
4121
+ }, [dragState.isDragging, table]);
4122
+ return { dragState, onFillHandleMouseDown };
4123
+ }
3458
4124
  function filterHeaderGroups(groups, visibleColumnIds) {
3459
4125
  return groups.map((group) => ({
3460
4126
  ...group,
@@ -3468,6 +4134,8 @@ function Table({
3468
4134
  bordered: borderedProp,
3469
4135
  compact: compactProp,
3470
4136
  theme: themeProp,
4137
+ config,
4138
+ configProfile,
3471
4139
  clickableRows,
3472
4140
  footer,
3473
4141
  loading,
@@ -3485,7 +4153,7 @@ function Table({
3485
4153
  statusBar,
3486
4154
  statusBarPanels,
3487
4155
  sidebar,
3488
- sidebarPanels = ["columns", "filters"],
4156
+ sidebarPanels,
3489
4157
  defaultSidebarPanel,
3490
4158
  floatingFilters,
3491
4159
  columnVirtualization,
@@ -3493,17 +4161,31 @@ function Table({
3493
4161
  ariaLabel: ariaLabelProp,
3494
4162
  ...rest
3495
4163
  }) {
3496
- const { tableProps: providerTableProps } = useYableDefaults();
3497
- const stickyHeader = stickyHeaderProp ?? providerTableProps?.stickyHeader;
3498
- const striped = stripedProp ?? providerTableProps?.striped;
3499
- const bordered = borderedProp ?? providerTableProps?.bordered;
3500
- const compact = compactProp ?? providerTableProps?.compact;
3501
- const theme = themeProp ?? providerTableProps?.theme;
3502
- const direction = directionProp ?? providerTableProps?.direction;
3503
- const ariaLabel = ariaLabelProp ?? providerTableProps?.ariaLabel;
4164
+ const providerDefaults = useYableDefaults();
4165
+ const { tableProps: providerTableProps } = providerDefaults;
4166
+ const profile = resolveYableProfile(
4167
+ config ?? providerDefaults.config,
4168
+ configProfile ?? providerDefaults.tableProfile
4169
+ );
4170
+ const profileTableProps = profile.table;
4171
+ const stickyHeader = stickyHeaderProp ?? profileTableProps?.stickyHeader ?? providerTableProps?.stickyHeader;
4172
+ const striped = stripedProp ?? profileTableProps?.striped ?? providerTableProps?.striped;
4173
+ const bordered = borderedProp ?? profileTableProps?.bordered ?? providerTableProps?.bordered;
4174
+ const compact = compactProp ?? profileTableProps?.compact ?? providerTableProps?.compact;
4175
+ const theme = themeProp ?? profileTableProps?.theme ?? providerTableProps?.theme;
4176
+ const direction = directionProp ?? profileTableProps?.direction ?? providerTableProps?.direction;
4177
+ const ariaLabel = ariaLabelProp ?? profileTableProps?.ariaLabel ?? providerTableProps?.ariaLabel;
4178
+ const resolvedClickableRows = clickableRows ?? profileTableProps?.clickableRows;
4179
+ const resolvedStatusBar = statusBar ?? profileTableProps?.statusBar;
4180
+ const resolvedSidebar = sidebar ?? profileTableProps?.sidebar;
4181
+ const resolvedSidebarPanels = sidebarPanels ?? profileTableProps?.sidebarPanels ?? ["columns", "filters"];
4182
+ const resolvedDefaultSidebarPanel = defaultSidebarPanel ?? profileTableProps?.defaultSidebarPanel;
4183
+ const resolvedFloatingFilters = floatingFilters ?? profileTableProps?.floatingFilters;
4184
+ const resolvedColumnVirtualization = columnVirtualization ?? profileTableProps?.columnVirtualization;
4185
+ const resolvedColumnVirtualizationOverscan = columnVirtualizationOverscan ?? profileTableProps?.columnVirtualizationOverscan;
3504
4186
  const [sidebarOpen, setSidebarOpen] = useState(false);
3505
4187
  const [sidebarPanel, setSidebarPanel] = useState(
3506
- defaultSidebarPanel ?? "columns"
4188
+ resolvedDefaultSidebarPanel ?? "columns"
3507
4189
  );
3508
4190
  const containerRef = useRef(null);
3509
4191
  const horizontalScrollRef = useRef(null);
@@ -3527,12 +4209,14 @@ function Table({
3527
4209
  const allVisibleColumns = table.getVisibleLeafColumns();
3528
4210
  const hasPinnedColumns = table.getLeftVisibleLeafColumns().length > 0 || table.getRightVisibleLeafColumns().length > 0;
3529
4211
  const hasGroupedHeaders = table.getHeaderGroups().length > 1;
3530
- const canVirtualizeColumns = Boolean(columnVirtualization) && !hasPinnedColumns && !hasGroupedHeaders && allVisibleColumns.length > 0;
4212
+ const canVirtualizeColumns = Boolean(resolvedColumnVirtualization) && !hasPinnedColumns && !hasGroupedHeaders && allVisibleColumns.length > 0;
4213
+ const allVisibleColumnSizeSignature = allVisibleColumns.map((column) => `${column.id}:${column.getSize()}`).join("|");
3531
4214
  const columnVirtualState = useColumnVirtualization({
3532
4215
  containerRef: horizontalScrollRef,
3533
4216
  columns: allVisibleColumns,
3534
- overscan: columnVirtualizationOverscan ?? 2,
3535
- enabled: canVirtualizeColumns
4217
+ overscan: resolvedColumnVirtualizationOverscan ?? 2,
4218
+ enabled: canVirtualizeColumns,
4219
+ sizingKey: allVisibleColumnSizeSignature
3536
4220
  });
3537
4221
  const renderTable = useMemo(() => {
3538
4222
  if (!canVirtualizeColumns || !columnVirtualState.isVirtualized) {
@@ -3560,6 +4244,9 @@ function Table({
3560
4244
  const showColumnVirtualizationShell = canVirtualizeColumns;
3561
4245
  const contextMenu = useContextMenu();
3562
4246
  useKeyboardNavigation(table, { containerRef });
4247
+ const { onFillHandleMouseDown } = useFillHandle(table, {
4248
+ enabled: Boolean(table.options.enableFillHandle)
4249
+ });
3563
4250
  const [announcement, setAnnouncement] = useState("");
3564
4251
  const prevSortingRef = useRef(table.getState().sorting);
3565
4252
  const prevFilterCountRef = useRef(rows.length);
@@ -3607,17 +4294,18 @@ function Table({
3607
4294
  const handleContextMenu = useCallback(
3608
4295
  (e) => {
3609
4296
  e.preventDefault();
3610
- contextMenu.open(e.clientX, e.clientY, table);
4297
+ const targetEl = e.target?.closest?.("[data-column-id]");
4298
+ const targetColumnId = targetEl?.getAttribute("data-column-id") ?? void 0;
4299
+ contextMenu.open(e.clientX, e.clientY, table, targetColumnId);
3611
4300
  },
3612
4301
  [contextMenu, table]
3613
4302
  );
3614
- const enableRowVirtualization = renderTable.options.enableVirtualization ?? false;
3615
4303
  const visibleLeafColumns = renderTable.getVisibleLeafColumns();
3616
- const columnSizing = renderTable.getState().columnSizing;
3617
- const colgroup = useMemo(() => {
3618
- if (visibleLeafColumns.length === 0) return null;
3619
- return /* @__PURE__ */ jsx("colgroup", { children: visibleLeafColumns.map((col) => /* @__PURE__ */ jsx("col", { style: { width: col.getSize() } }, col.id)) });
3620
- }, [visibleLeafColumns, columnSizing]);
4304
+ const visibleColumnTotalSize = visibleLeafColumns.reduce(
4305
+ (sum, column) => sum + column.getSize(),
4306
+ 0
4307
+ );
4308
+ const colgroup = visibleLeafColumns.length === 0 ? null : /* @__PURE__ */ jsx("colgroup", { children: visibleLeafColumns.map((col) => /* @__PURE__ */ jsx("col", { style: { width: col.getSize() } }, col.id)) });
3621
4309
  const outerTableStyle = useMemo(() => {
3622
4310
  if (columnVirtualState.isVirtualized) {
3623
4311
  return {
@@ -3627,15 +4315,15 @@ function Table({
3627
4315
  tableLayout: "fixed"
3628
4316
  };
3629
4317
  }
3630
- if (enableRowVirtualization) {
3631
- return { tableLayout: "fixed" };
3632
- }
3633
- return void 0;
4318
+ return {
4319
+ minWidth: visibleColumnTotalSize || void 0,
4320
+ tableLayout: "fixed"
4321
+ };
3634
4322
  }, [
3635
4323
  columnVirtualState.isVirtualized,
3636
4324
  columnVirtualState.visibleWidth,
3637
4325
  columnVirtualState.startOffset,
3638
- enableRowVirtualization
4326
+ visibleColumnTotalSize
3639
4327
  ]);
3640
4328
  const tableNode = /* @__PURE__ */ jsxs(
3641
4329
  "table",
@@ -3644,9 +4332,17 @@ function Table({
3644
4332
  style: outerTableStyle,
3645
4333
  "data-column-virtualized": columnVirtualState.isVirtualized || void 0,
3646
4334
  children: [
3647
- enableRowVirtualization && colgroup,
3648
- /* @__PURE__ */ jsx(TableHeader, { table: renderTable, floatingFilters }),
3649
- /* @__PURE__ */ jsx(TableBody, { table: renderTable, clickableRows, colgroup }),
4335
+ colgroup,
4336
+ /* @__PURE__ */ jsx(TableHeader, { table: renderTable, floatingFilters: resolvedFloatingFilters }),
4337
+ /* @__PURE__ */ jsx(
4338
+ TableBody,
4339
+ {
4340
+ table: renderTable,
4341
+ clickableRows: resolvedClickableRows,
4342
+ colgroup,
4343
+ onFillHandleMouseDown
4344
+ }
4345
+ ),
3650
4346
  footer && /* @__PURE__ */ jsx(TableFooter, { table: renderTable })
3651
4347
  ]
3652
4348
  }
@@ -3712,18 +4408,43 @@ function Table({
3712
4408
  }
3713
4409
  ))
3714
4410
  ] }),
3715
- sidebar && /* @__PURE__ */ jsx(
4411
+ resolvedSidebar && !sidebarOpen && /* @__PURE__ */ jsx(
4412
+ "button",
4413
+ {
4414
+ type: "button",
4415
+ className: "yable-sidebar-trigger",
4416
+ "aria-label": "Open tool panel",
4417
+ title: "Open tool panel",
4418
+ onClick: () => setSidebarOpen(true),
4419
+ children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
4420
+ /* @__PURE__ */ jsx(
4421
+ "rect",
4422
+ {
4423
+ x: "3",
4424
+ y: "4",
4425
+ width: "18",
4426
+ height: "16",
4427
+ rx: "2",
4428
+ stroke: "currentColor",
4429
+ strokeWidth: "2"
4430
+ }
4431
+ ),
4432
+ /* @__PURE__ */ jsx("line", { x1: "14", y1: "4", x2: "14", y2: "20", stroke: "currentColor", strokeWidth: "2" })
4433
+ ] })
4434
+ }
4435
+ ),
4436
+ resolvedSidebar && /* @__PURE__ */ jsx(
3716
4437
  Sidebar,
3717
4438
  {
3718
4439
  table,
3719
4440
  open: sidebarOpen,
3720
4441
  onClose: () => setSidebarOpen(false),
3721
- panels: sidebarPanels,
4442
+ panels: resolvedSidebarPanels,
3722
4443
  activePanel: sidebarPanel,
3723
4444
  onPanelChange: setSidebarPanel
3724
4445
  }
3725
4446
  ),
3726
- statusBar && /* @__PURE__ */ jsx(StatusBar, { table, panels: statusBarPanels }),
4447
+ resolvedStatusBar && /* @__PURE__ */ jsx(StatusBar, { table, panels: statusBarPanels }),
3727
4448
  children,
3728
4449
  contextMenu.isOpen && /* @__PURE__ */ jsx(
3729
4450
  ContextMenu,
@@ -3731,7 +4452,8 @@ function Table({
3731
4452
  x: contextMenu.x,
3732
4453
  y: contextMenu.y,
3733
4454
  onClose: contextMenu.close,
3734
- table
4455
+ table,
4456
+ targetColumnId: contextMenu.targetColumnId
3735
4457
  }
3736
4458
  ),
3737
4459
  /* @__PURE__ */ jsx(
@@ -4308,110 +5030,6 @@ function isEditableTarget2(el) {
4308
5030
  if (el.isContentEditable) return true;
4309
5031
  return false;
4310
5032
  }
4311
- function useFillHandle(table, options = {}) {
4312
- const { enabled = true } = options;
4313
- const [dragState, setDragState] = useState({
4314
- isDragging: false,
4315
- sourceCell: null,
4316
- currentCell: null
4317
- });
4318
- const dragRef = useRef(dragState);
4319
- dragRef.current = dragState;
4320
- const onFillHandleMouseDown = useCallback(
4321
- (rowIndex, columnIndex, e) => {
4322
- if (!enabled) return;
4323
- e.preventDefault();
4324
- e.stopPropagation();
4325
- setDragState({
4326
- isDragging: true,
4327
- sourceCell: { rowIndex, columnIndex },
4328
- currentCell: { rowIndex, columnIndex }
4329
- });
4330
- },
4331
- [enabled]
4332
- );
4333
- useEffect(() => {
4334
- if (!dragState.isDragging) return;
4335
- const handleMouseMove = (e) => {
4336
- const target = document.elementFromPoint(e.clientX, e.clientY);
4337
- if (!target) return;
4338
- const td = target.closest("td[data-column-id]");
4339
- const tr = target.closest("tr[data-row-id]");
4340
- if (!td || !tr) return;
4341
- const columnId = td.getAttribute("data-column-id");
4342
- const rowId = tr.getAttribute("data-row-id");
4343
- if (!columnId || !rowId) return;
4344
- const rows = table.getRowModel().rows;
4345
- const columns = table.getVisibleLeafColumns();
4346
- const rowIndex = rows.findIndex((r) => r.id === rowId);
4347
- const columnIndex = columns.findIndex((c) => c.id === columnId);
4348
- if (rowIndex === -1 || columnIndex === -1) return;
4349
- setDragState((prev) => ({
4350
- ...prev,
4351
- currentCell: { rowIndex, columnIndex }
4352
- }));
4353
- };
4354
- const handleMouseUp = () => {
4355
- const current = dragRef.current;
4356
- if (current.sourceCell && current.currentCell) {
4357
- const source = current.sourceCell;
4358
- const target = current.currentCell;
4359
- if (source.rowIndex !== target.rowIndex || source.columnIndex !== target.columnIndex) {
4360
- const sourceRange = {
4361
- startRow: source.rowIndex,
4362
- startCol: source.columnIndex,
4363
- endRow: source.rowIndex,
4364
- endCol: source.columnIndex
4365
- };
4366
- const targetRange = {
4367
- startRow: Math.min(source.rowIndex, target.rowIndex),
4368
- startCol: Math.min(source.columnIndex, target.columnIndex),
4369
- endRow: Math.max(source.rowIndex, target.rowIndex),
4370
- endCol: Math.max(source.columnIndex, target.columnIndex)
4371
- };
4372
- table.fillRange(sourceRange, targetRange);
4373
- }
4374
- }
4375
- setDragState({
4376
- isDragging: false,
4377
- sourceCell: null,
4378
- currentCell: null
4379
- });
4380
- };
4381
- document.addEventListener("mousemove", handleMouseMove);
4382
- document.addEventListener("mouseup", handleMouseUp);
4383
- return () => {
4384
- document.removeEventListener("mousemove", handleMouseMove);
4385
- document.removeEventListener("mouseup", handleMouseUp);
4386
- };
4387
- }, [dragState.isDragging, table]);
4388
- return { dragState, onFillHandleMouseDown };
4389
- }
4390
- function FillHandle({
4391
- rowIndex,
4392
- columnIndex,
4393
- onMouseDown
4394
- }) {
4395
- const handleMouseDown = useCallback(
4396
- (e) => {
4397
- e.preventDefault();
4398
- e.stopPropagation();
4399
- onMouseDown(rowIndex, columnIndex, e);
4400
- },
4401
- [rowIndex, columnIndex, onMouseDown]
4402
- );
4403
- return /* @__PURE__ */ jsx(
4404
- "div",
4405
- {
4406
- className: "yable-fill-handle",
4407
- onMouseDown: handleMouseDown,
4408
- role: "presentation",
4409
- "aria-hidden": "true",
4410
- title: "Drag to fill",
4411
- children: /* @__PURE__ */ jsx("div", { className: "yable-fill-handle-dot" })
4412
- }
4413
- );
4414
- }
4415
5033
  function GripIcon() {
4416
5034
  return /* @__PURE__ */ jsxs(
4417
5035
  "svg",
@@ -4659,33 +5277,6 @@ function TreeToggle({
4659
5277
  }
4660
5278
  );
4661
5279
  }
4662
- function MasterDetail({
4663
- row,
4664
- table,
4665
- colSpan,
4666
- renderDetailPanel,
4667
- animationClass
4668
- }) {
4669
- const renderer = renderDetailPanel ?? table.options.renderDetailPanel;
4670
- if (!renderer) return null;
4671
- const content = renderer(row);
4672
- if (content == null) return null;
4673
- const classes = [
4674
- "yable-detail-row",
4675
- "yable-detail-row--animated",
4676
- animationClass
4677
- ].filter(Boolean).join(" ");
4678
- return /* @__PURE__ */ jsx(
4679
- "tr",
4680
- {
4681
- className: classes,
4682
- "data-detail-for": row.id,
4683
- role: "row",
4684
- "aria-label": `Details for row ${row.id}`,
4685
- children: /* @__PURE__ */ jsx("td", { className: "yable-detail-cell", colSpan, role: "cell", children: /* @__PURE__ */ jsx("div", { className: "yable-detail-panel", children: /* @__PURE__ */ jsx("div", { className: "yable-detail-panel-inner", children: content }) }) })
4686
- }
4687
- );
4688
- }
4689
5280
  function ExpandIcon({
4690
5281
  isExpanded,
4691
5282
  onClick,
@@ -5658,10 +6249,11 @@ function selectColumn(options = {}) {
5658
6249
  const { id = "_select", size = 40, headerAriaLabel = "Select all rows" } = options;
5659
6250
  return {
5660
6251
  id,
5661
- header: ({ table }) => /* @__PURE__ */ jsx(
6252
+ header: ({ table }) => /* @__PURE__ */ jsx("label", { className: "yable-checkbox-hitbox", onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsx(
5662
6253
  "input",
5663
6254
  {
5664
6255
  type: "checkbox",
6256
+ className: "yable-checkbox",
5665
6257
  checked: table.getIsAllPageRowsSelected(),
5666
6258
  ref: (el) => {
5667
6259
  if (el)
@@ -5670,19 +6262,21 @@ function selectColumn(options = {}) {
5670
6262
  onChange: () => table.toggleAllPageRowsSelected(),
5671
6263
  "aria-label": headerAriaLabel
5672
6264
  }
5673
- ),
5674
- cell: ({ row }) => /* @__PURE__ */ jsx(
6265
+ ) }),
6266
+ cell: ({ row }) => /* @__PURE__ */ jsx("label", { className: "yable-checkbox-hitbox", onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsx(
5675
6267
  "input",
5676
6268
  {
5677
6269
  type: "checkbox",
6270
+ className: "yable-checkbox",
5678
6271
  checked: row.getIsSelected(),
5679
6272
  disabled: !row.getCanSelect(),
5680
6273
  onChange: row.getToggleSelectedHandler(),
5681
- "aria-label": `Select row`
6274
+ "aria-label": "Select row"
5682
6275
  }
5683
- ),
6276
+ ) }),
5684
6277
  size,
5685
- enableSorting: false,
6278
+ enableSorting: true,
6279
+ sortingFn: (rowA, rowB) => Number(rowA.getIsSelected()) - Number(rowB.getIsSelected()),
5686
6280
  enableColumnFilter: false,
5687
6281
  enableResizing: false,
5688
6282
  enableReorder: false,
@@ -5882,6 +6476,6 @@ function mergeEditChanges(data, changes, getRowId = (_, i) => String(i)) {
5882
6476
  });
5883
6477
  }
5884
6478
 
5885
- export { CellBadge, CellBoolean, CellCheckbox, CellCurrency, CellDate, CellDatePicker, CellErrorBoundary, CellInput, CellLink, CellNumeric, CellProgress, CellRating, CellRow, CellSelect, CellStack, CellStatus, CellStatusBadge, CellText, CellToggle, CellWithIcon, ColumnsPanel, ContextMenu, ContextMenuItem, DEFAULT_TEXT_RECIPE, DragHandle, ErrorBoundary, ExpandIcon, FillHandle, FiltersPanel, FlashCell, FloatingFilter, GlobalFilter, LoadingOverlay, MasterDetail, NoRowsOverlay, Pagination, PivotConfigPanel, PrintLayout, SetFilter, Sidebar, SortIndicator, StatusBar, StatusBarPanelComponent, Table, TableBody, TableCell, TableFooter, TableHeader, TableProvider, Tooltip, TreeToggle, YableProvider, actionsColumn, expandColumn, getMeasureRecipeForCellType, getRegisteredCellTypes, mergeEditChanges, resolveMeasureRecipe, rowNumberColumn, selectColumn, useAutoMeasurements, useCellFlash, useClipboard, useColumnVirtualization, useContextMenu, useFillHandle, useKeyboardNavigation, usePretextMeasurement, usePrintLayout, useRowAnimation, useRowDrag, useTable, useTableContext, useTablePersistence, useTableRowHeights, useTheme, useTooltip, useVirtualization, useYableDefaults };
6479
+ export { CellBadge, CellBoolean, CellCheckbox, CellCurrency, CellDate, CellDatePicker, CellErrorBoundary, CellInput, CellLink, CellNumeric, CellProgress, CellRating, CellRow, CellSelect, CellStack, CellStatus, CellStatusBadge, CellText, CellToggle, CellWithIcon, ColumnsPanel, ContextMenu, ContextMenuItem, DEFAULT_TEXT_RECIPE, DragHandle, ErrorBoundary, ExpandIcon, FillHandle, FiltersPanel, FlashCell, FloatingFilter, GlobalFilter, LoadingOverlay, MasterDetail, NoRowsOverlay, Pagination, PivotConfigPanel, PrintLayout, SetFilter, Sidebar, SortIndicator, StatusBar, StatusBarPanelComponent, Table, TableBody, TableCell, TableFooter, TableHeader, TableProvider, Tooltip, TreeToggle, YableProvider, actionsColumn, applyYableConfigToColumns, createYableConfig, expandColumn, getMeasureRecipeForCellType, getRegisteredCellTypes, getYableDefaultColumnDef, mergeEditChanges, resolveMeasureRecipe, resolveYableProfile, rowNumberColumn, selectColumn, useAutoMeasurements, useCellFlash, useClipboard, useColumnVirtualization, useContextMenu, useFillHandle, useKeyboardNavigation, usePretextMeasurement, usePrintLayout, useRowAnimation, useRowDrag, useServerTable, useTable, useTableContext, useTablePersistence, useTableRowHeights, useTheme, useTooltip, useVirtualization, useYableDefaults };
5886
6480
  //# sourceMappingURL=index.js.map
5887
6481
  //# sourceMappingURL=index.js.map