@zvndev/yable-react 0.3.0 → 0.4.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/index.cjs CHANGED
@@ -10,6 +10,37 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
10
  var React3__default = /*#__PURE__*/_interopDefault(React3);
11
11
 
12
12
  // src/index.ts
13
+ var YableContext = React3.createContext({});
14
+ function useYableDefaults() {
15
+ return React3.useContext(YableContext);
16
+ }
17
+ function YableProvider({
18
+ children,
19
+ defaultColumnDef,
20
+ striped,
21
+ stickyHeader,
22
+ bordered,
23
+ compact,
24
+ theme,
25
+ direction,
26
+ ariaLabel
27
+ }) {
28
+ const tableProps = {};
29
+ if (striped !== void 0) tableProps.striped = striped;
30
+ if (stickyHeader !== void 0) tableProps.stickyHeader = stickyHeader;
31
+ if (bordered !== void 0) tableProps.bordered = bordered;
32
+ if (compact !== void 0) tableProps.compact = compact;
33
+ if (theme !== void 0) tableProps.theme = theme;
34
+ if (direction !== void 0) tableProps.direction = direction;
35
+ if (ariaLabel !== void 0) tableProps.ariaLabel = ariaLabel;
36
+ const value = {
37
+ tableProps: Object.keys(tableProps).length > 0 ? tableProps : void 0,
38
+ defaultColumnDef
39
+ };
40
+ return /* @__PURE__ */ jsxRuntime.jsx(YableContext.Provider, { value, children });
41
+ }
42
+
43
+ // src/useTable.ts
13
44
  function shallowEqual(a, b) {
14
45
  if (a === b) return true;
15
46
  const keysA = Object.keys(a);
@@ -22,6 +53,17 @@ function shallowEqual(a, b) {
22
53
  return true;
23
54
  }
24
55
  function useTable(options) {
56
+ const providerDefaults = useYableDefaults();
57
+ const optionsWithDefaults = React3.useMemo(() => {
58
+ if (!providerDefaults.defaultColumnDef) return options;
59
+ return {
60
+ ...options,
61
+ defaultColumnDef: {
62
+ ...providerDefaults.defaultColumnDef,
63
+ ...options.defaultColumnDef
64
+ }
65
+ };
66
+ }, [options, providerDefaults.defaultColumnDef]);
25
67
  const [state, setState] = React3.useState(() => ({
26
68
  sorting: [],
27
69
  columnFilters: [],
@@ -61,14 +103,14 @@ function useTable(options) {
61
103
  }));
62
104
  const stateRef = React3.useRef(state);
63
105
  stateRef.current = state;
64
- const prevOptionsRef = React3.useRef(options);
106
+ const prevOptionsRef = React3.useRef(optionsWithDefaults);
65
107
  const stableOptions = React3.useMemo(() => {
66
- if (shallowEqual(prevOptionsRef.current, options)) {
108
+ if (shallowEqual(prevOptionsRef.current, optionsWithDefaults)) {
67
109
  return prevOptionsRef.current;
68
110
  }
69
- prevOptionsRef.current = options;
70
- return options;
71
- }, [options]);
111
+ prevOptionsRef.current = optionsWithDefaults;
112
+ return optionsWithDefaults;
113
+ }, [optionsWithDefaults]);
72
114
  const onStateChangeRef = React3.useRef(options.onStateChange);
73
115
  onStateChangeRef.current = options.onStateChange;
74
116
  const resolvedState = React3.useMemo(
@@ -78,17 +120,14 @@ function useTable(options) {
78
120
  }),
79
121
  [state, stableOptions.state]
80
122
  );
81
- const onStateChange = React3.useCallback(
82
- (updater) => {
83
- const latest = onStateChangeRef.current;
84
- if (latest) {
85
- latest(updater);
86
- } else {
87
- setState((prev) => yableCore.functionalUpdate(updater, prev));
88
- }
89
- },
90
- []
91
- );
123
+ const onStateChange = React3.useCallback((updater) => {
124
+ const latest = onStateChangeRef.current;
125
+ if (latest) {
126
+ latest(updater);
127
+ } else {
128
+ setState((prev) => yableCore.functionalUpdate(updater, prev));
129
+ }
130
+ }, []);
92
131
  const resolvedOptions = React3.useMemo(
93
132
  () => ({
94
133
  ...stableOptions,
@@ -101,12 +140,14 @@ function useTable(options) {
101
140
  if (!tableRef.current) {
102
141
  tableRef.current = yableCore.createTable(resolvedOptions);
103
142
  } else {
104
- tableRef.current.setOptions((prev) => ({
105
- ...prev,
106
- ...resolvedOptions,
107
- state: resolvedState,
108
- onStateChange
109
- }));
143
+ tableRef.current.setOptions(
144
+ (prev) => ({
145
+ ...prev,
146
+ ...resolvedOptions,
147
+ state: resolvedState,
148
+ onStateChange
149
+ })
150
+ );
110
151
  }
111
152
  React3.useEffect(() => {
112
153
  return () => {
@@ -117,6 +158,122 @@ function useTable(options) {
117
158
  }, []);
118
159
  return tableRef.current;
119
160
  }
161
+ var DEFAULT_PERSISTED_KEYS = [
162
+ "columnVisibility",
163
+ "columnOrder",
164
+ "columnSizing",
165
+ "columnPinning"
166
+ ];
167
+ function resolveStorage(custom) {
168
+ if (custom) return custom;
169
+ if (typeof window !== "undefined") {
170
+ try {
171
+ return window.localStorage;
172
+ } catch {
173
+ return void 0;
174
+ }
175
+ }
176
+ return void 0;
177
+ }
178
+ function pick(obj, keys) {
179
+ const result = {};
180
+ for (const k of keys) {
181
+ if (k in obj) {
182
+ result[k] = obj[k];
183
+ }
184
+ }
185
+ return result;
186
+ }
187
+ function readState(storage, key, version, persistedKeys) {
188
+ if (!storage) return {};
189
+ try {
190
+ const raw = storage.getItem(key);
191
+ if (!raw) return {};
192
+ const envelope = JSON.parse(raw);
193
+ if (envelope.version !== version) {
194
+ storage.removeItem(key);
195
+ return {};
196
+ }
197
+ return pick(envelope.state, persistedKeys);
198
+ } catch {
199
+ if (typeof console !== "undefined") {
200
+ console.warn(`[yable] Failed to read persisted state for key "${key}"`);
201
+ }
202
+ return {};
203
+ }
204
+ }
205
+ function writeState(storage, key, version, state) {
206
+ if (!storage) return;
207
+ try {
208
+ const envelope = { version, state };
209
+ storage.setItem(key, JSON.stringify(envelope));
210
+ } catch {
211
+ if (typeof console !== "undefined") {
212
+ console.warn(`[yable] Failed to persist state for key "${key}" (storage may be full)`);
213
+ }
214
+ }
215
+ }
216
+ function useTablePersistence(options) {
217
+ const {
218
+ key,
219
+ persistedKeys = DEFAULT_PERSISTED_KEYS,
220
+ debounce: debounceMs = 100,
221
+ version = 0,
222
+ storage: customStorage
223
+ } = options;
224
+ const storage = React3.useMemo(() => resolveStorage(customStorage), [customStorage]);
225
+ const initialState = React3.useMemo(
226
+ () => readState(storage, key, version, persistedKeys),
227
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally read only on mount
228
+ []
229
+ );
230
+ const [state, setState] = React3.useState(initialState);
231
+ const timerRef = React3.useRef(null);
232
+ const keyRef = React3.useRef(key);
233
+ keyRef.current = key;
234
+ const versionRef = React3.useRef(version);
235
+ versionRef.current = version;
236
+ const persistedKeysRef = React3.useRef(persistedKeys);
237
+ persistedKeysRef.current = persistedKeys;
238
+ const debounceRef = React3.useRef(debounceMs);
239
+ debounceRef.current = debounceMs;
240
+ const storageRef = React3.useRef(storage);
241
+ storageRef.current = storage;
242
+ const onStateChange = React3.useCallback((updater) => {
243
+ setState((prev) => {
244
+ const next = yableCore.functionalUpdate(updater, prev);
245
+ if (timerRef.current !== null) {
246
+ clearTimeout(timerRef.current);
247
+ }
248
+ timerRef.current = setTimeout(() => {
249
+ const sliced = pick(
250
+ next,
251
+ persistedKeysRef.current
252
+ );
253
+ writeState(storageRef.current, keyRef.current, versionRef.current, sliced);
254
+ timerRef.current = null;
255
+ }, debounceRef.current);
256
+ return next;
257
+ });
258
+ }, []);
259
+ React3.useEffect(() => {
260
+ return () => {
261
+ if (timerRef.current !== null) {
262
+ clearTimeout(timerRef.current);
263
+ }
264
+ };
265
+ }, []);
266
+ const clearPersistedState = React3.useCallback(() => {
267
+ const s = storageRef.current;
268
+ if (s) {
269
+ try {
270
+ s.removeItem(keyRef.current);
271
+ } catch {
272
+ }
273
+ }
274
+ }, []);
275
+ return { initialState, state, onStateChange, clearPersistedState };
276
+ }
120
277
  var EMPTY_RESULT = {
121
278
  virtualRows: [],
122
279
  totalHeight: 0,
@@ -130,11 +287,14 @@ function useVirtualization({
130
287
  overscan = 5,
131
288
  estimateRowHeight: _estimateRowHeight,
132
289
  pretextHeights,
133
- pretextPrefixSums
290
+ pretextPrefixSums,
291
+ columnSizingHash
134
292
  }) {
135
293
  const hasPretextHeights = !!(pretextHeights && pretextPrefixSums && pretextHeights.length >= totalRows);
136
294
  const isFixedHeight = typeof rowHeight === "number" && !hasPretextHeights;
137
295
  const heightCacheRef = React3.useRef(/* @__PURE__ */ new Map());
296
+ const heightCacheVersionRef = React3.useRef(0);
297
+ const [heightCacheVersion, setHeightCacheVersion] = React3.useState(0);
138
298
  const getRowHeight = React3.useCallback(
139
299
  (index) => {
140
300
  if (hasPretextHeights) return pretextHeights[index];
@@ -145,7 +305,9 @@ function useVirtualization({
145
305
  heightCacheRef.current.set(index, height);
146
306
  return height;
147
307
  },
148
- [rowHeight, isFixedHeight, hasPretextHeights, pretextHeights]
308
+ // heightCacheVersion forces a new callback identity after cache invalidation
309
+ // eslint-disable-next-line react-hooks/exhaustive-deps
310
+ [rowHeight, isFixedHeight, hasPretextHeights, pretextHeights, heightCacheVersion]
149
311
  );
150
312
  const [scrollState, setScrollState] = React3.useState({ scrollTop: 0, containerHeight: 0 });
151
313
  const rafRef = React3.useRef(null);
@@ -198,6 +360,18 @@ function useVirtualization({
198
360
  heightCacheRef.current.clear();
199
361
  }
200
362
  }, [totalRows, isFixedHeight]);
363
+ React3.useEffect(() => {
364
+ if (!isFixedHeight && columnSizingHash !== void 0) {
365
+ heightCacheRef.current.clear();
366
+ heightCacheVersionRef.current += 1;
367
+ setHeightCacheVersion(heightCacheVersionRef.current);
368
+ }
369
+ }, [columnSizingHash]);
370
+ const invalidateRowHeights = React3.useCallback(() => {
371
+ heightCacheRef.current.clear();
372
+ heightCacheVersionRef.current += 1;
373
+ setHeightCacheVersion(heightCacheVersionRef.current);
374
+ }, []);
201
375
  const scrollTo = React3.useCallback(
202
376
  (index) => {
203
377
  const container = containerRef.current;
@@ -234,10 +408,18 @@ function useVirtualization({
234
408
  total += getRowHeight(i);
235
409
  }
236
410
  return total;
237
- }, [totalRows, rowHeight, isFixedHeight, getRowHeight, hasPretextHeights, pretextPrefixSums]);
411
+ }, [
412
+ totalRows,
413
+ rowHeight,
414
+ isFixedHeight,
415
+ getRowHeight,
416
+ hasPretextHeights,
417
+ pretextPrefixSums,
418
+ heightCacheVersion
419
+ ]);
238
420
  const { scrollTop, containerHeight } = scrollState;
239
421
  if (totalRows === 0) {
240
- return { ...EMPTY_RESULT, scrollTo };
422
+ return { ...EMPTY_RESULT, scrollTo, invalidateRowHeights };
241
423
  }
242
424
  if (containerHeight === 0) {
243
425
  return {
@@ -245,7 +427,8 @@ function useVirtualization({
245
427
  totalHeight,
246
428
  startIndex: 0,
247
429
  endIndex: 0,
248
- scrollTo
430
+ scrollTo,
431
+ invalidateRowHeights
249
432
  };
250
433
  }
251
434
  let startIndex = 0;
@@ -341,7 +524,8 @@ function useVirtualization({
341
524
  totalHeight,
342
525
  startIndex: overscanStart,
343
526
  endIndex: overscanEnd,
344
- scrollTo
527
+ scrollTo,
528
+ invalidateRowHeights
345
529
  };
346
530
  }
347
531
  function binarySearchOffsets(offsets, target) {
@@ -1387,11 +1571,6 @@ function HeaderCell({
1387
1571
  const handleDragStart = React3.useCallback(
1388
1572
  (e) => {
1389
1573
  if (!canReorder) return;
1390
- const target = e.target;
1391
- if (target && target.closest(".yable-resize-handle")) {
1392
- e.preventDefault();
1393
- return;
1394
- }
1395
1574
  e.stopPropagation();
1396
1575
  e.dataTransfer.effectAllowed = "move";
1397
1576
  try {
@@ -1399,8 +1578,9 @@ function HeaderCell({
1399
1578
  e.dataTransfer.setData("text/plain", column.id);
1400
1579
  } catch {
1401
1580
  }
1581
+ table.setColumnDragActive(true);
1402
1582
  },
1403
- [canReorder, column.id]
1583
+ [canReorder, column.id, table]
1404
1584
  );
1405
1585
  const handleDragOver = React3.useCallback(
1406
1586
  (e) => {
@@ -1429,7 +1609,8 @@ function HeaderCell({
1429
1609
  }, []);
1430
1610
  const handleDragEnd = React3.useCallback(() => {
1431
1611
  setDragOver(null);
1432
- }, []);
1612
+ table.setColumnDragActive(false);
1613
+ }, [table]);
1433
1614
  const handleDrop = React3.useCallback(
1434
1615
  (e) => {
1435
1616
  if (!canReorder) return;
@@ -1439,6 +1620,7 @@ function HeaderCell({
1439
1620
  const rect = e.currentTarget.getBoundingClientRect();
1440
1621
  const insertAfter = e.clientX >= rect.left + rect.width / 2;
1441
1622
  setDragOver(null);
1623
+ table.setColumnDragActive(false);
1442
1624
  if (!sourceId || sourceId === column.id) return;
1443
1625
  const state = table.getState();
1444
1626
  const allLeafs = table.getAllLeafColumns();
@@ -1489,18 +1671,24 @@ function HeaderCell({
1489
1671
  "aria-sort": sortDirection === "asc" ? "ascending" : sortDirection === "desc" ? "descending" : canSort ? "none" : void 0,
1490
1672
  role: "columnheader",
1491
1673
  colSpan: header.colSpan,
1492
- draggable: canReorder || void 0,
1493
1674
  onClick: handleHeaderClick,
1494
- onDragStart: canReorder ? handleDragStart : void 0,
1495
1675
  onDragOver: canReorder ? handleDragOver : void 0,
1496
1676
  onDragLeave: canReorder ? handleDragLeave : void 0,
1497
- onDragEnd: canReorder ? handleDragEnd : void 0,
1498
1677
  onDrop: canReorder ? handleDrop : void 0,
1499
1678
  children: [
1500
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "yable-th-content", children: [
1501
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: headerContent }),
1502
- canSort && /* @__PURE__ */ jsxRuntime.jsx(SortIndicator, { direction: sortDirection, index: sortIndex > 0 ? sortIndex : void 0 })
1503
- ] }),
1679
+ /* @__PURE__ */ jsxRuntime.jsxs(
1680
+ "div",
1681
+ {
1682
+ className: "yable-th-content",
1683
+ draggable: canReorder || void 0,
1684
+ onDragStart: canReorder ? handleDragStart : void 0,
1685
+ onDragEnd: canReorder ? handleDragEnd : void 0,
1686
+ children: [
1687
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: headerContent }),
1688
+ canSort && /* @__PURE__ */ jsxRuntime.jsx(SortIndicator, { direction: sortDirection, index: sortIndex > 0 ? sortIndex : void 0 })
1689
+ ]
1690
+ }
1691
+ ),
1504
1692
  canResize && /* @__PURE__ */ jsxRuntime.jsx(
1505
1693
  "div",
1506
1694
  {
@@ -1508,9 +1696,7 @@ function HeaderCell({
1508
1696
  "data-resizing": column.getIsResizing() || void 0,
1509
1697
  onMouseDown: startResize,
1510
1698
  onTouchStart: startResize,
1511
- onClick: handleResizeClick,
1512
- draggable: false,
1513
- onDragStart: (e) => e.preventDefault()
1699
+ onClick: handleResizeClick
1514
1700
  }
1515
1701
  )
1516
1702
  ]
@@ -1709,6 +1895,11 @@ function TableCell({
1709
1895
  if (!table.getState().cellSelection?.isDragging) return;
1710
1896
  table.endCellRangeSelection();
1711
1897
  }, [table]);
1898
+ const cellClassNameDef = column.columnDef.cellClassName;
1899
+ const userClassName = typeof cellClassNameDef === "function" ? cellClassNameDef(cell.getContext()) : cellClassNameDef;
1900
+ const cellStyleDef = column.columnDef.cellStyle;
1901
+ const userStyle = typeof cellStyleDef === "function" ? cellStyleDef(cell.getContext()) : cellStyleDef;
1902
+ const mergedStyle = userStyle ? { ...style, ...userStyle } : style;
1712
1903
  const classNames = [
1713
1904
  "yable-td",
1714
1905
  isFocused && "yable-cell--focused",
@@ -1716,13 +1907,14 @@ function TableCell({
1716
1907
  selectionEdges?.top && "yable-cell--selection-top",
1717
1908
  selectionEdges?.right && "yable-cell--selection-right",
1718
1909
  selectionEdges?.bottom && "yable-cell--selection-bottom",
1719
- selectionEdges?.left && "yable-cell--selection-left"
1910
+ selectionEdges?.left && "yable-cell--selection-left",
1911
+ userClassName
1720
1912
  ].filter(Boolean).join(" ");
1721
1913
  return /* @__PURE__ */ jsxRuntime.jsxs(
1722
1914
  "td",
1723
1915
  {
1724
1916
  className: classNames,
1725
- style,
1917
+ style: mergedStyle,
1726
1918
  "data-editing": isEditing || void 0,
1727
1919
  "data-focused": isFocused || void 0,
1728
1920
  "data-pinned": pinned || void 0,
@@ -1862,7 +2054,11 @@ var CellErrorBoundary = class extends React3__default.default.Component {
1862
2054
  return this.props.children;
1863
2055
  }
1864
2056
  };
1865
- function TableBody({ table, clickableRows }) {
2057
+ function TableBody({
2058
+ table,
2059
+ clickableRows,
2060
+ colgroup
2061
+ }) {
1866
2062
  const rows = table.getRowModel().rows;
1867
2063
  const visibleColumns = table.getVisibleLeafColumns();
1868
2064
  const activeCell = table.getState().editing.activeCell;
@@ -1879,6 +2075,19 @@ function TableBody({ table, clickableRows }) {
1879
2075
  const estimateRowHeight = options.estimateRowHeight;
1880
2076
  const pretextHeights = options.pretextHeights ?? null;
1881
2077
  const pretextPrefixSums = options.pretextPrefixSums ?? null;
2078
+ const columnSizing = table.getState().columnSizing;
2079
+ const columnSizingHash = React3.useMemo(() => {
2080
+ const entries = Object.entries(columnSizing);
2081
+ if (entries.length === 0) return 0;
2082
+ let h = 0;
2083
+ for (const [key, value] of entries) {
2084
+ for (let i = 0; i < key.length; i++) {
2085
+ h = h * 31 + key.charCodeAt(i) | 0;
2086
+ }
2087
+ h = h * 31 + (value | 0) | 0;
2088
+ }
2089
+ return h;
2090
+ }, [columnSizing]);
1882
2091
  const { virtualRows, totalHeight } = useVirtualization({
1883
2092
  containerRef: scrollContainerRef,
1884
2093
  totalRows: rows.length,
@@ -1886,7 +2095,8 @@ function TableBody({ table, clickableRows }) {
1886
2095
  overscan,
1887
2096
  estimateRowHeight,
1888
2097
  pretextHeights,
1889
- pretextPrefixSums
2098
+ pretextPrefixSums,
2099
+ columnSizingHash
1890
2100
  });
1891
2101
  const cellSelectionKey = cellSelection.range ? `${cellSelection.range.start.rowIndex}:${cellSelection.range.start.columnIndex}:${cellSelection.range.end.rowIndex}:${cellSelection.range.end.columnIndex}:${cellSelection.isDragging ? "dragging" : "idle"}` : `none:${cellSelection.isDragging ? "dragging" : "idle"}`;
1892
2102
  React3.useEffect(() => {
@@ -1937,7 +2147,7 @@ function TableBody({ table, clickableRows }) {
1937
2147
  {
1938
2148
  className: "yable-virtual-spacer",
1939
2149
  style: { height: totalHeight, position: "relative" },
1940
- children: /* @__PURE__ */ jsxRuntime.jsx(
2150
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1941
2151
  "table",
1942
2152
  {
1943
2153
  style: {
@@ -1948,35 +2158,38 @@ function TableBody({ table, clickableRows }) {
1948
2158
  tableLayout: "fixed",
1949
2159
  borderCollapse: "collapse"
1950
2160
  },
1951
- children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: virtualRows.map((vRow) => {
1952
- const row = rows[vRow.index];
1953
- if (!row) return null;
1954
- return /* @__PURE__ */ jsxRuntime.jsx(
1955
- MemoizedTableRow,
1956
- {
1957
- row,
1958
- table,
1959
- rowIndex: vRow.index,
1960
- visibleColumns,
1961
- isSelected: row.getIsSelected(),
1962
- isExpanded: row.getIsExpanded(),
1963
- activeColumnId: activeCell?.rowId === row.id ? activeCell.columnId : void 0,
1964
- focusedColumnIndex: focusedCell?.rowIndex === vRow.index ? focusedCell.columnIndex : null,
1965
- hasFocusedCell: focusedCell !== null,
1966
- cellSelectionKey,
1967
- clickable: clickableRows,
1968
- virtualStyle: {
1969
- position: "absolute",
1970
- top: 0,
1971
- left: 0,
1972
- width: "100%",
1973
- height: vRow.size,
1974
- transform: `translateY(${vRow.start}px)`
1975
- }
1976
- },
1977
- row.id
1978
- );
1979
- }) })
2161
+ children: [
2162
+ colgroup,
2163
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: virtualRows.map((vRow) => {
2164
+ const row = rows[vRow.index];
2165
+ if (!row) return null;
2166
+ return /* @__PURE__ */ jsxRuntime.jsx(
2167
+ MemoizedTableRow,
2168
+ {
2169
+ row,
2170
+ table,
2171
+ rowIndex: vRow.index,
2172
+ visibleColumns,
2173
+ isSelected: row.getIsSelected(),
2174
+ isExpanded: row.getIsExpanded(),
2175
+ activeColumnId: activeCell?.rowId === row.id ? activeCell.columnId : void 0,
2176
+ focusedColumnIndex: focusedCell?.rowIndex === vRow.index ? focusedCell.columnIndex : null,
2177
+ hasFocusedCell: focusedCell !== null,
2178
+ cellSelectionKey,
2179
+ clickable: clickableRows,
2180
+ virtualStyle: {
2181
+ position: "absolute",
2182
+ top: 0,
2183
+ left: 0,
2184
+ width: "100%",
2185
+ height: vRow.size,
2186
+ transform: `translateY(${vRow.start}px)`
2187
+ }
2188
+ },
2189
+ row.id
2190
+ );
2191
+ }) })
2192
+ ]
1980
2193
  }
1981
2194
  )
1982
2195
  }
@@ -3255,11 +3468,11 @@ function filterHeaderGroups(groups, visibleColumnIds) {
3255
3468
  }
3256
3469
  function Table({
3257
3470
  table,
3258
- stickyHeader,
3259
- striped,
3260
- bordered,
3261
- compact,
3262
- theme,
3471
+ stickyHeader: stickyHeaderProp,
3472
+ striped: stripedProp,
3473
+ bordered: borderedProp,
3474
+ compact: compactProp,
3475
+ theme: themeProp,
3263
3476
  clickableRows,
3264
3477
  footer,
3265
3478
  loading,
@@ -3273,7 +3486,7 @@ function Table({
3273
3486
  renderLoading,
3274
3487
  children,
3275
3488
  className,
3276
- direction,
3489
+ direction: directionProp,
3277
3490
  statusBar,
3278
3491
  statusBarPanels,
3279
3492
  sidebar,
@@ -3282,9 +3495,17 @@ function Table({
3282
3495
  floatingFilters,
3283
3496
  columnVirtualization,
3284
3497
  columnVirtualizationOverscan,
3285
- ariaLabel,
3498
+ ariaLabel: ariaLabelProp,
3286
3499
  ...rest
3287
3500
  }) {
3501
+ const { tableProps: providerTableProps } = useYableDefaults();
3502
+ const stickyHeader = stickyHeaderProp ?? providerTableProps?.stickyHeader;
3503
+ const striped = stripedProp ?? providerTableProps?.striped;
3504
+ const bordered = borderedProp ?? providerTableProps?.bordered;
3505
+ const compact = compactProp ?? providerTableProps?.compact;
3506
+ const theme = themeProp ?? providerTableProps?.theme;
3507
+ const direction = directionProp ?? providerTableProps?.direction;
3508
+ const ariaLabel = ariaLabelProp ?? providerTableProps?.ariaLabel;
3288
3509
  const [sidebarOpen, setSidebarOpen] = React3.useState(false);
3289
3510
  const [sidebarPanel, setSidebarPanel] = React3.useState(
3290
3511
  defaultSidebarPanel ?? "columns"
@@ -3395,20 +3616,42 @@ function Table({
3395
3616
  },
3396
3617
  [contextMenu, table]
3397
3618
  );
3398
- const tableNode = /* @__PURE__ */ jsxRuntime.jsxs(
3399
- "table",
3400
- {
3401
- className: "yable-table",
3402
- style: columnVirtualState.isVirtualized ? {
3619
+ const enableRowVirtualization = renderTable.options.enableVirtualization ?? false;
3620
+ const visibleLeafColumns = renderTable.getVisibleLeafColumns();
3621
+ const columnSizing = renderTable.getState().columnSizing;
3622
+ const colgroup = React3.useMemo(() => {
3623
+ if (visibleLeafColumns.length === 0) return null;
3624
+ return /* @__PURE__ */ jsxRuntime.jsx("colgroup", { children: visibleLeafColumns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("col", { style: { width: col.getSize() } }, col.id)) });
3625
+ }, [visibleLeafColumns, columnSizing]);
3626
+ const outerTableStyle = React3.useMemo(() => {
3627
+ if (columnVirtualState.isVirtualized) {
3628
+ return {
3403
3629
  width: columnVirtualState.visibleWidth,
3404
3630
  minWidth: columnVirtualState.visibleWidth,
3405
3631
  marginLeft: columnVirtualState.startOffset,
3406
3632
  tableLayout: "fixed"
3407
- } : void 0,
3633
+ };
3634
+ }
3635
+ if (enableRowVirtualization) {
3636
+ return { tableLayout: "fixed" };
3637
+ }
3638
+ return void 0;
3639
+ }, [
3640
+ columnVirtualState.isVirtualized,
3641
+ columnVirtualState.visibleWidth,
3642
+ columnVirtualState.startOffset,
3643
+ enableRowVirtualization
3644
+ ]);
3645
+ const tableNode = /* @__PURE__ */ jsxRuntime.jsxs(
3646
+ "table",
3647
+ {
3648
+ className: "yable-table",
3649
+ style: outerTableStyle,
3408
3650
  "data-column-virtualized": columnVirtualState.isVirtualized || void 0,
3409
3651
  children: [
3652
+ enableRowVirtualization && colgroup,
3410
3653
  /* @__PURE__ */ jsxRuntime.jsx(TableHeader, { table: renderTable, floatingFilters }),
3411
- /* @__PURE__ */ jsxRuntime.jsx(TableBody, { table: renderTable, clickableRows }),
3654
+ /* @__PURE__ */ jsxRuntime.jsx(TableBody, { table: renderTable, clickableRows, colgroup }),
3412
3655
  footer && /* @__PURE__ */ jsxRuntime.jsx(TableFooter, { table: renderTable })
3413
3656
  ]
3414
3657
  }
@@ -5416,6 +5659,233 @@ function useTheme(options = {}) {
5416
5659
  containerRef
5417
5660
  };
5418
5661
  }
5662
+ function selectColumn(options = {}) {
5663
+ const { id = "_select", size = 40, headerAriaLabel = "Select all rows" } = options;
5664
+ return {
5665
+ id,
5666
+ header: ({ table }) => /* @__PURE__ */ jsxRuntime.jsx(
5667
+ "input",
5668
+ {
5669
+ type: "checkbox",
5670
+ checked: table.getIsAllPageRowsSelected(),
5671
+ ref: (el) => {
5672
+ if (el)
5673
+ el.indeterminate = table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected();
5674
+ },
5675
+ onChange: () => table.toggleAllPageRowsSelected(),
5676
+ "aria-label": headerAriaLabel
5677
+ }
5678
+ ),
5679
+ cell: ({ row }) => /* @__PURE__ */ jsxRuntime.jsx(
5680
+ "input",
5681
+ {
5682
+ type: "checkbox",
5683
+ checked: row.getIsSelected(),
5684
+ disabled: !row.getCanSelect(),
5685
+ onChange: row.getToggleSelectedHandler(),
5686
+ "aria-label": `Select row`
5687
+ }
5688
+ ),
5689
+ size,
5690
+ enableSorting: false,
5691
+ enableColumnFilter: false,
5692
+ enableResizing: false,
5693
+ enableReorder: false,
5694
+ enableHiding: false,
5695
+ lockVisible: true
5696
+ };
5697
+ }
5698
+
5699
+ // src/presets/rowNumberColumn.tsx
5700
+ function rowNumberColumn(options = {}) {
5701
+ const { id = "_rowNumber", header = "#", size = 50, startFrom = 1 } = options;
5702
+ return {
5703
+ id,
5704
+ header,
5705
+ cell: ({ row }) => row.index + startFrom,
5706
+ size,
5707
+ enableSorting: false,
5708
+ enableColumnFilter: false,
5709
+ enableResizing: false,
5710
+ enableReorder: false,
5711
+ lockVisible: true
5712
+ };
5713
+ }
5714
+ function actionsColumn(options) {
5715
+ const { id = "_actions", header = "", size = 100, actions } = options;
5716
+ return {
5717
+ id,
5718
+ header,
5719
+ cell: (ctx) => {
5720
+ const items = typeof actions === "function" ? actions(ctx.row) : actions;
5721
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "yable-cell-actions", children: items.filter((a) => !a.hidden || !a.hidden(ctx.row)).map((action, i) => /* @__PURE__ */ jsxRuntime.jsx(
5722
+ "button",
5723
+ {
5724
+ type: "button",
5725
+ className: "yable-action-btn",
5726
+ disabled: action.disabled?.(ctx.row),
5727
+ onClick: (e) => {
5728
+ e.stopPropagation();
5729
+ action.onClick(ctx.row);
5730
+ },
5731
+ title: action.label,
5732
+ children: action.icon ?? action.label
5733
+ },
5734
+ i
5735
+ )) });
5736
+ },
5737
+ size,
5738
+ enableSorting: false,
5739
+ enableColumnFilter: false,
5740
+ enableResizing: false,
5741
+ enableReorder: false
5742
+ };
5743
+ }
5744
+ function expandColumn(options = {}) {
5745
+ const { id = "_expand", size = 40 } = options;
5746
+ return {
5747
+ id,
5748
+ header: () => null,
5749
+ cell: ({ row }) => {
5750
+ if (!row.getCanExpand()) return null;
5751
+ return /* @__PURE__ */ jsxRuntime.jsx(
5752
+ "button",
5753
+ {
5754
+ type: "button",
5755
+ className: "yable-expand-btn",
5756
+ onClick: (e) => {
5757
+ e.stopPropagation();
5758
+ row.toggleExpanded();
5759
+ },
5760
+ "aria-expanded": row.getIsExpanded(),
5761
+ "aria-label": row.getIsExpanded() ? "Collapse row" : "Expand row",
5762
+ children: /* @__PURE__ */ jsxRuntime.jsx(
5763
+ "span",
5764
+ {
5765
+ className: "yable-expand-icon",
5766
+ style: {
5767
+ display: "inline-block",
5768
+ transform: row.getIsExpanded() ? "rotate(90deg)" : "rotate(0deg)",
5769
+ transition: "transform 150ms ease"
5770
+ },
5771
+ children: "\u25B6"
5772
+ }
5773
+ )
5774
+ }
5775
+ );
5776
+ },
5777
+ size,
5778
+ enableSorting: false,
5779
+ enableColumnFilter: false,
5780
+ enableResizing: false,
5781
+ enableReorder: false,
5782
+ lockVisible: true
5783
+ };
5784
+ }
5785
+ function CellStack({ children, gap = 2 }) {
5786
+ return /* @__PURE__ */ jsxRuntime.jsx(
5787
+ "div",
5788
+ {
5789
+ className: "yable-cell-stack",
5790
+ style: {
5791
+ display: "flex",
5792
+ flexDirection: "column",
5793
+ gap
5794
+ },
5795
+ children
5796
+ }
5797
+ );
5798
+ }
5799
+ function CellRow({ children, gap = 6, align = "center", justify = "start" }) {
5800
+ const justifyMap = {
5801
+ start: "flex-start",
5802
+ center: "center",
5803
+ end: "flex-end",
5804
+ between: "space-between"
5805
+ };
5806
+ return /* @__PURE__ */ jsxRuntime.jsx(
5807
+ "div",
5808
+ {
5809
+ className: "yable-cell-row",
5810
+ style: {
5811
+ display: "flex",
5812
+ flexDirection: "row",
5813
+ alignItems: align === "baseline" ? "baseline" : align === "start" ? "flex-start" : align === "end" ? "flex-end" : "center",
5814
+ justifyContent: justifyMap[justify] || "flex-start",
5815
+ gap
5816
+ },
5817
+ children
5818
+ }
5819
+ );
5820
+ }
5821
+ function CellWithIcon({ icon, children, gap = 6, iconSize }) {
5822
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5823
+ "div",
5824
+ {
5825
+ className: "yable-cell-with-icon",
5826
+ style: {
5827
+ display: "flex",
5828
+ flexDirection: "row",
5829
+ alignItems: "center",
5830
+ gap
5831
+ },
5832
+ children: [
5833
+ /* @__PURE__ */ jsxRuntime.jsx(
5834
+ "span",
5835
+ {
5836
+ className: "yable-cell-icon",
5837
+ style: {
5838
+ display: "inline-flex",
5839
+ alignItems: "center",
5840
+ justifyContent: "center",
5841
+ flexShrink: 0,
5842
+ ...iconSize ? { width: iconSize, height: iconSize } : {}
5843
+ },
5844
+ children: icon
5845
+ }
5846
+ ),
5847
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "yable-cell-icon-content", style: { minWidth: 0 }, children })
5848
+ ]
5849
+ }
5850
+ );
5851
+ }
5852
+ function CellText({
5853
+ children,
5854
+ variant = "primary",
5855
+ bold,
5856
+ truncate,
5857
+ size = "md"
5858
+ }) {
5859
+ const fontSizeMap = { sm: "0.75rem", md: "0.875rem", lg: "1rem" };
5860
+ return /* @__PURE__ */ jsxRuntime.jsx(
5861
+ "span",
5862
+ {
5863
+ className: `yable-cell-text yable-cell-text--${variant}`,
5864
+ style: {
5865
+ fontSize: fontSizeMap[size],
5866
+ fontWeight: bold ? 600 : void 0,
5867
+ color: variant === "secondary" ? "var(--yable-text-secondary, #6b7280)" : variant === "muted" ? "var(--yable-text-muted, #9ca3af)" : void 0,
5868
+ ...truncate ? {
5869
+ overflow: "hidden",
5870
+ textOverflow: "ellipsis",
5871
+ whiteSpace: "nowrap"
5872
+ } : {}
5873
+ },
5874
+ children
5875
+ }
5876
+ );
5877
+ }
5878
+
5879
+ // src/utils/mergeEditChanges.ts
5880
+ function mergeEditChanges(data, changes, getRowId = (_, i) => String(i)) {
5881
+ const changeKeys = Object.keys(changes);
5882
+ if (changeKeys.length === 0) return data;
5883
+ return data.map((row, i) => {
5884
+ const id = getRowId(row, i);
5885
+ const patch = changes[id];
5886
+ return patch ? { ...row, ...patch } : row;
5887
+ });
5888
+ }
5419
5889
 
5420
5890
  Object.defineProperty(exports, "CommitError", {
5421
5891
  enumerable: true,
@@ -5509,10 +5979,14 @@ exports.CellLink = CellLink;
5509
5979
  exports.CellNumeric = CellNumeric;
5510
5980
  exports.CellProgress = CellProgress;
5511
5981
  exports.CellRating = CellRating;
5982
+ exports.CellRow = CellRow;
5512
5983
  exports.CellSelect = CellSelect;
5984
+ exports.CellStack = CellStack;
5513
5985
  exports.CellStatus = CellStatus;
5514
5986
  exports.CellStatusBadge = CellStatusBadge;
5987
+ exports.CellText = CellText;
5515
5988
  exports.CellToggle = CellToggle;
5989
+ exports.CellWithIcon = CellWithIcon;
5516
5990
  exports.ColumnsPanel = ColumnsPanel;
5517
5991
  exports.ContextMenu = ContextMenu;
5518
5992
  exports.ContextMenuItem = ContextMenuItem;
@@ -5544,9 +6018,15 @@ exports.TableHeader = TableHeader;
5544
6018
  exports.TableProvider = TableProvider;
5545
6019
  exports.Tooltip = Tooltip;
5546
6020
  exports.TreeToggle = TreeToggle;
6021
+ exports.YableProvider = YableProvider;
6022
+ exports.actionsColumn = actionsColumn;
6023
+ exports.expandColumn = expandColumn;
5547
6024
  exports.getMeasureRecipeForCellType = getMeasureRecipeForCellType;
5548
6025
  exports.getRegisteredCellTypes = getRegisteredCellTypes;
6026
+ exports.mergeEditChanges = mergeEditChanges;
5549
6027
  exports.resolveMeasureRecipe = resolveMeasureRecipe;
6028
+ exports.rowNumberColumn = rowNumberColumn;
6029
+ exports.selectColumn = selectColumn;
5550
6030
  exports.useAutoMeasurements = useAutoMeasurements;
5551
6031
  exports.useCellFlash = useCellFlash;
5552
6032
  exports.useClipboard = useClipboard;
@@ -5560,9 +6040,11 @@ exports.useRowAnimation = useRowAnimation;
5560
6040
  exports.useRowDrag = useRowDrag;
5561
6041
  exports.useTable = useTable;
5562
6042
  exports.useTableContext = useTableContext;
6043
+ exports.useTablePersistence = useTablePersistence;
5563
6044
  exports.useTableRowHeights = useTableRowHeights;
5564
6045
  exports.useTheme = useTheme;
5565
6046
  exports.useTooltip = useTooltip;
5566
6047
  exports.useVirtualization = useVirtualization;
6048
+ exports.useYableDefaults = useYableDefaults;
5567
6049
  //# sourceMappingURL=index.cjs.map
5568
6050
  //# sourceMappingURL=index.cjs.map