boneless-table 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,982 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __defProps = Object.defineProperties;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
11
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
12
+ var __spreadValues = (a, b) => {
13
+ for (var prop in b || (b = {}))
14
+ if (__hasOwnProp.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ if (__getOwnPropSymbols)
17
+ for (var prop of __getOwnPropSymbols(b)) {
18
+ if (__propIsEnum.call(b, prop))
19
+ __defNormalProp(a, prop, b[prop]);
20
+ }
21
+ return a;
22
+ };
23
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
24
+ var __objRest = (source, exclude) => {
25
+ var target = {};
26
+ for (var prop in source)
27
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
28
+ target[prop] = source[prop];
29
+ if (source != null && __getOwnPropSymbols)
30
+ for (var prop of __getOwnPropSymbols(source)) {
31
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
32
+ target[prop] = source[prop];
33
+ }
34
+ return target;
35
+ };
36
+ var __export = (target, all) => {
37
+ for (var name in all)
38
+ __defProp(target, name, { get: all[name], enumerable: true });
39
+ };
40
+ var __copyProps = (to, from, except, desc) => {
41
+ if (from && typeof from === "object" || typeof from === "function") {
42
+ for (let key of __getOwnPropNames(from))
43
+ if (!__hasOwnProp.call(to, key) && key !== except)
44
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
45
+ }
46
+ return to;
47
+ };
48
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
49
+
50
+ // src/index.ts
51
+ var index_exports = {};
52
+ __export(index_exports, {
53
+ BonelessTable: () => BonelessTable,
54
+ ColumnFilter: () => ColumnFilter,
55
+ ColumnMenu: () => ColumnMenu,
56
+ TableSkeleton: () => TableSkeleton,
57
+ compactValue: () => compactValue,
58
+ createColumnHelper: () => import_react_table2.createColumnHelper,
59
+ defaultBonelessTableSettings: () => defaultBonelessTableSettings,
60
+ defineColumns: () => defineColumns,
61
+ flexRender: () => import_react_table2.flexRender,
62
+ getColumnIds: () => getColumnIds,
63
+ getCoreRowModel: () => import_react_table2.getCoreRowModel,
64
+ getExpandedRowModel: () => import_react_table2.getExpandedRowModel,
65
+ getFilteredRowModel: () => import_react_table2.getFilteredRowModel,
66
+ getSortedRowModel: () => import_react_table2.getSortedRowModel,
67
+ mergeBonelessTableSettings: () => mergeBonelessTableSettings,
68
+ resolveColumnSettings: () => resolveColumnSettings,
69
+ resolveColumns: () => resolveColumns,
70
+ resolveRowHref: () => resolveRowHref,
71
+ resolveRowLinkLabel: () => resolveRowLinkLabel,
72
+ useReactTable: () => import_react_table2.useReactTable,
73
+ withDefaultCells: () => withDefaultCells
74
+ });
75
+ module.exports = __toCommonJS(index_exports);
76
+
77
+ // src/boneless-table.tsx
78
+ var import_react_table = require("@tanstack/react-table");
79
+ var import_react_virtual = require("@tanstack/react-virtual");
80
+ var import_react3 = require("react");
81
+
82
+ // src/column-filter.tsx
83
+ var import_react = require("react");
84
+ var import_jsx_runtime = require("react/jsx-runtime");
85
+ function ColumnFilter({
86
+ column,
87
+ settings,
88
+ className,
89
+ icon,
90
+ debounceMs = 0
91
+ }) {
92
+ var _a, _b;
93
+ const value = String((_a = column.getFilterValue()) != null ? _a : "");
94
+ const [inputValue, setInputValue] = (0, import_react.useState)(value);
95
+ (0, import_react.useEffect)(() => {
96
+ setInputValue(value);
97
+ }, [value]);
98
+ (0, import_react.useEffect)(() => {
99
+ if (settings.type !== "text" || inputValue === value) return;
100
+ if (debounceMs <= 0) {
101
+ column.setFilterValue(inputValue || void 0);
102
+ return;
103
+ }
104
+ const timer = window.setTimeout(
105
+ () => column.setFilterValue(inputValue || void 0),
106
+ debounceMs
107
+ );
108
+ return () => window.clearTimeout(timer);
109
+ }, [column, debounceMs, inputValue, settings.type, value]);
110
+ if (settings.type === "select") {
111
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
112
+ "select",
113
+ {
114
+ className,
115
+ "data-slot": "filter-select",
116
+ "aria-label": `Filter ${column.id}`,
117
+ value,
118
+ onChange: (event) => column.setFilterValue(event.target.value || void 0),
119
+ children: [
120
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: "All" }),
121
+ (_b = settings.options) == null ? void 0 : _b.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option, children: option }, option))
122
+ ]
123
+ }
124
+ );
125
+ }
126
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className, "data-slot": "filter-text", children: [
127
+ icon,
128
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
129
+ "input",
130
+ {
131
+ "data-slot": "filter-input",
132
+ "aria-label": `Filter ${column.id}`,
133
+ value: inputValue,
134
+ placeholder: "Filter",
135
+ onChange: (event) => setInputValue(event.target.value)
136
+ }
137
+ )
138
+ ] });
139
+ }
140
+
141
+ // src/column-menu.tsx
142
+ var import_react2 = require("react");
143
+ var import_jsx_runtime2 = require("react/jsx-runtime");
144
+ function ColumnMenu({
145
+ table,
146
+ onClose,
147
+ onMoveColumn,
148
+ className,
149
+ icons,
150
+ id = "boneless-table-column-menu",
151
+ triggerRef
152
+ }) {
153
+ var _a;
154
+ const menuRef = (0, import_react2.useRef)(null);
155
+ const closeButtonRef = (0, import_react2.useRef)(null);
156
+ (0, import_react2.useEffect)(() => {
157
+ var _a2;
158
+ (_a2 = closeButtonRef.current) == null ? void 0 : _a2.focus();
159
+ function dismissOnOutsideClick(event) {
160
+ var _a3, _b;
161
+ const target = event.target;
162
+ if (!((_a3 = menuRef.current) == null ? void 0 : _a3.contains(target)) && !((_b = triggerRef == null ? void 0 : triggerRef.current) == null ? void 0 : _b.contains(target))) onClose();
163
+ }
164
+ document.addEventListener("mousedown", dismissOnOutsideClick);
165
+ return () => document.removeEventListener("mousedown", dismissOnOutsideClick);
166
+ }, [onClose]);
167
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
168
+ "div",
169
+ {
170
+ className,
171
+ "data-slot": "column-menu",
172
+ ref: menuRef,
173
+ id,
174
+ role: "dialog",
175
+ "aria-modal": "false",
176
+ "aria-labelledby": `${id}-title`,
177
+ onKeyDown: (event) => {
178
+ if (event.key === "Escape") {
179
+ event.preventDefault();
180
+ onClose();
181
+ }
182
+ },
183
+ children: [
184
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { "data-slot": "column-menu-header", children: [
185
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { id: `${id}-title`, children: "Configure columns" }),
186
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
187
+ "button",
188
+ {
189
+ "data-slot": "column-menu-close",
190
+ onClick: onClose,
191
+ "aria-label": "Close column settings",
192
+ ref: closeButtonRef,
193
+ children: (_a = icons == null ? void 0 : icons.close) != null ? _a : "Close"
194
+ }
195
+ )
196
+ ] }),
197
+ table.getAllLeafColumns().map((column) => {
198
+ var _a2, _b, _c, _d;
199
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { "data-slot": "column-menu-item", children: [
200
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { "data-slot": "column-menu-label", children: [
201
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
202
+ "input",
203
+ {
204
+ type: "checkbox",
205
+ checked: column.getIsVisible(),
206
+ onChange: column.getToggleVisibilityHandler()
207
+ }
208
+ ),
209
+ (_b = (_a2 = column.columnDef.meta) == null ? void 0 : _a2.columnLabel) != null ? _b : typeof column.columnDef.header === "string" || typeof column.columnDef.header === "number" ? String(column.columnDef.header) : column.id
210
+ ] }),
211
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { "data-slot": "column-menu-actions", children: [
212
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
213
+ "button",
214
+ {
215
+ "data-slot": "column-menu-move-earlier",
216
+ onClick: () => onMoveColumn(column.id, -1),
217
+ title: "Move column earlier",
218
+ children: (_c = icons == null ? void 0 : icons.moveEarlier) != null ? _c : "Earlier"
219
+ }
220
+ ),
221
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
222
+ "button",
223
+ {
224
+ "data-slot": "column-menu-move-later",
225
+ onClick: () => onMoveColumn(column.id, 1),
226
+ title: "Move column later",
227
+ children: (_d = icons == null ? void 0 : icons.moveLater) != null ? _d : "Later"
228
+ }
229
+ )
230
+ ] })
231
+ ] }, column.id);
232
+ })
233
+ ]
234
+ }
235
+ );
236
+ }
237
+
238
+ // src/class-names.ts
239
+ function cn(...classNames) {
240
+ return classNames.filter(Boolean).join(" ");
241
+ }
242
+
243
+ // src/settings.ts
244
+ var defaultBonelessTableSettings = {
245
+ columnDefaults: {
246
+ sizing: { minPx: 120, flex: 1 },
247
+ align: "left",
248
+ borders: "none",
249
+ sorting: { enabled: true, reveal: "hover" }
250
+ },
251
+ interactions: { filterReveal: "hover", horizontalOverflow: "auto" }
252
+ };
253
+ function mergeBonelessTableSettings(override, base = defaultBonelessTableSettings) {
254
+ var _a, _b, _c, _d, _e, _f;
255
+ if (!override) return base;
256
+ return {
257
+ columnDefaults: {
258
+ sizing: __spreadValues(__spreadValues({}, base.columnDefaults.sizing), (_a = override.columnDefaults) == null ? void 0 : _a.sizing),
259
+ align: (_c = (_b = override.columnDefaults) == null ? void 0 : _b.align) != null ? _c : base.columnDefaults.align,
260
+ borders: (_e = (_d = override.columnDefaults) == null ? void 0 : _d.borders) != null ? _e : base.columnDefaults.borders,
261
+ sorting: __spreadValues(__spreadValues({}, base.columnDefaults.sorting), (_f = override.columnDefaults) == null ? void 0 : _f.sorting)
262
+ },
263
+ interactions: __spreadValues(__spreadValues({}, base.interactions), override.interactions)
264
+ };
265
+ }
266
+ function resolveColumnSettings(settings, column) {
267
+ var _a, _b, _c;
268
+ const configured = (_a = column.columnDef.meta) == null ? void 0 : _a.bonelessTable;
269
+ const configuredSizing = configured == null ? void 0 : configured.sizing;
270
+ return {
271
+ sizing: configuredSizing && "widthPx" in configuredSizing ? { widthPx: configuredSizing.widthPx } : __spreadValues(__spreadValues({}, settings.columnDefaults.sizing), configuredSizing),
272
+ align: (_b = configured == null ? void 0 : configured.align) != null ? _b : settings.columnDefaults.align,
273
+ borders: (_c = configured == null ? void 0 : configured.borders) != null ? _c : settings.columnDefaults.borders,
274
+ sorting: __spreadValues(__spreadValues({}, settings.columnDefaults.sorting), configured == null ? void 0 : configured.sorting),
275
+ filtering: configured == null ? void 0 : configured.filtering,
276
+ valueDisplay: configured == null ? void 0 : configured.valueDisplay
277
+ };
278
+ }
279
+ function compactValue(value, settings) {
280
+ var _a;
281
+ const text = value == null ? "" : String(value);
282
+ const display = settings == null ? void 0 : settings.valueDisplay;
283
+ if (!display || text.length <= display.truncateAt) return text;
284
+ return `${text.slice(0, display.truncateAt).trimEnd()}${(_a = display.suffix) != null ? _a : "..."}`;
285
+ }
286
+
287
+ // src/columns.ts
288
+ function defineColumns(columns) {
289
+ return columns;
290
+ }
291
+ function resolveColumns(columns) {
292
+ return columns.map((column) => {
293
+ if ("key" in column && column.key) {
294
+ const _a = column, { key } = _a, definition = __objRest(_a, ["key"]);
295
+ return __spreadProps(__spreadValues({}, definition), { accessorKey: key });
296
+ }
297
+ return column;
298
+ });
299
+ }
300
+ function withDefaultCells(columns) {
301
+ return columns.map((column) => {
302
+ if (column.cell) return column;
303
+ return __spreadProps(__spreadValues({}, column), {
304
+ cell: ({ getValue, column: tableColumn }) => {
305
+ var _a;
306
+ const text = compactValue(getValue(), (_a = tableColumn.columnDef.meta) == null ? void 0 : _a.bonelessTable);
307
+ return text;
308
+ }
309
+ });
310
+ });
311
+ }
312
+ function getColumnIds(columns) {
313
+ return columns.map((column, index) => {
314
+ if ("id" in column && column.id) return column.id;
315
+ if ("key" in column && column.key != null) return String(column.key);
316
+ if ("accessorKey" in column && column.accessorKey != null) {
317
+ return String(column.accessorKey);
318
+ }
319
+ return String(index);
320
+ });
321
+ }
322
+
323
+ // src/table-skeleton.tsx
324
+ var import_jsx_runtime3 = require("react/jsx-runtime");
325
+ function TableSkeleton({
326
+ columnCount,
327
+ rows,
328
+ label = "Loading rows",
329
+ classNames = {}
330
+ }) {
331
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { "data-slot": "skeleton", className: classNames.skeleton, "aria-busy": "true", children: Array.from({ length: rows }, (_, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
332
+ "div",
333
+ {
334
+ className: classNames.row,
335
+ "data-slot": "skeleton-row",
336
+ role: "status",
337
+ "aria-label": label,
338
+ style: { display: "grid", gridTemplateColumns: "var(--boneless-table-grid)" },
339
+ children: Array.from({ length: columnCount }, (_2, columnIndex) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: cn(classNames.cell), "data-slot": "skeleton-cell", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "data-slot": "skeleton-line" }) }, columnIndex))
340
+ },
341
+ rowIndex
342
+ )) });
343
+ }
344
+
345
+ // src/row-link.ts
346
+ function readPath(row, path) {
347
+ return path.split(".").reduce((value, key) => {
348
+ if (value === null || typeof value !== "object") return void 0;
349
+ return value[key];
350
+ }, row);
351
+ }
352
+ function resolveRowHref(link, row) {
353
+ if (typeof link.href === "function") return link.href(row);
354
+ let hasMissingValue = false;
355
+ const href = link.href.replace(/\{(?:row\.)?([\w.]+)\}/g, (token, path) => {
356
+ const value = readPath(row, path);
357
+ if (value === void 0 || value === null) {
358
+ hasMissingValue = true;
359
+ return token;
360
+ }
361
+ return encodeURIComponent(String(value));
362
+ });
363
+ return hasMissingValue ? void 0 : href;
364
+ }
365
+ function resolveRowLinkLabel(link, row) {
366
+ return typeof link.label === "function" ? link.label(row) : link.label;
367
+ }
368
+
369
+ // src/boneless-table.tsx
370
+ var import_jsx_runtime4 = require("react/jsx-runtime");
371
+ function moveItem(order, id, direction) {
372
+ const index = order.indexOf(id);
373
+ const target = index + direction;
374
+ if (index < 0 || target < 0 || target >= order.length) return order;
375
+ const next = [...order];
376
+ [next[index], next[target]] = [next[target], next[index]];
377
+ return next;
378
+ }
379
+ function renderSlot(slot, table, fallback) {
380
+ if (slot === false) return null;
381
+ if (typeof slot === "function") return slot(table);
382
+ if (slot && slot !== true) return slot;
383
+ return fallback;
384
+ }
385
+ function BonelessTable(_a) {
386
+ var _b = _a, {
387
+ data,
388
+ columns,
389
+ settings: settingsOverride,
390
+ className,
391
+ classNames = {},
392
+ icons,
393
+ tree: treeOption,
394
+ isLoading = false,
395
+ isFetchingMore = false,
396
+ skeletonRows = 8,
397
+ skeletonLabel = "Loading rows",
398
+ onNearEnd,
399
+ canLoadMore = true,
400
+ nearEndOffset = 220,
401
+ filterDebounceMs = 150,
402
+ scroller = "auto",
403
+ toolbar = true,
404
+ toolbarLayout,
405
+ footer = true,
406
+ totalCount,
407
+ resultLabel = "rows",
408
+ resultHint,
409
+ onReset,
410
+ onRowClick,
411
+ scrollToTopOn,
412
+ rowLink,
413
+ emptyState,
414
+ virtualization = {}
415
+ } = _b, tableOptions = __objRest(_b, [
416
+ "data",
417
+ "columns",
418
+ "settings",
419
+ "className",
420
+ "classNames",
421
+ "icons",
422
+ "tree",
423
+ "isLoading",
424
+ "isFetchingMore",
425
+ "skeletonRows",
426
+ "skeletonLabel",
427
+ "onNearEnd",
428
+ "canLoadMore",
429
+ "nearEndOffset",
430
+ "filterDebounceMs",
431
+ "scroller",
432
+ "toolbar",
433
+ "toolbarLayout",
434
+ "footer",
435
+ "totalCount",
436
+ "resultLabel",
437
+ "resultHint",
438
+ "onReset",
439
+ "onRowClick",
440
+ "scrollToTopOn",
441
+ "rowLink",
442
+ "emptyState",
443
+ "virtualization"
444
+ ]);
445
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _l, _m, _n, _o, _p, _q, _r, _s;
446
+ const settings = (0, import_react3.useMemo)(() => mergeBonelessTableSettings(settingsOverride), [settingsOverride]);
447
+ const resolvedColumns = (0, import_react3.useMemo)(() => withDefaultCells(resolveColumns(columns)), [columns]);
448
+ const defaultOrder = (0, import_react3.useMemo)(() => getColumnIds(resolvedColumns), [resolvedColumns]);
449
+ const scrollerRef = (0, import_react3.useRef)(null);
450
+ const headerScrollerRef = (0, import_react3.useRef)(null);
451
+ const columnsButtonRef = (0, import_react3.useRef)(null);
452
+ const nearEndTriggeredRef = (0, import_react3.useRef)(false);
453
+ const columnMenuId = (0, import_react3.useId)();
454
+ const [showColumns, setShowColumns] = (0, import_react3.useState)(false);
455
+ const [headerScrollbarGutter, setHeaderScrollbarGutter] = (0, import_react3.useState)(0);
456
+ const [sorting, setSorting] = (0, import_react3.useState)((_b2 = (_a2 = tableOptions.initialState) == null ? void 0 : _a2.sorting) != null ? _b2 : []);
457
+ const [columnFilters, setColumnFilters] = (0, import_react3.useState)(
458
+ (_d = (_c = tableOptions.initialState) == null ? void 0 : _c.columnFilters) != null ? _d : []
459
+ );
460
+ const [columnVisibility, setColumnVisibility] = (0, import_react3.useState)(
461
+ (_f = (_e = tableOptions.initialState) == null ? void 0 : _e.columnVisibility) != null ? _f : {}
462
+ );
463
+ const [columnOrder, setColumnOrder] = (0, import_react3.useState)(
464
+ (_h = (_g = tableOptions.initialState) == null ? void 0 : _g.columnOrder) != null ? _h : defaultOrder
465
+ );
466
+ const [expanded, setExpanded] = (0, import_react3.useState)((_j = (_i = tableOptions.initialState) == null ? void 0 : _i.expanded) != null ? _j : {});
467
+ const _k = tableOptions, {
468
+ state: controlledState,
469
+ onSortingChange,
470
+ onColumnFiltersChange,
471
+ onColumnVisibilityChange,
472
+ onColumnOrderChange,
473
+ onExpandedChange,
474
+ getCoreRowModel: getCoreRowModelOption,
475
+ getSortedRowModel: getSortedRowModelOption,
476
+ getFilteredRowModel: getFilteredRowModelOption,
477
+ getSubRows,
478
+ manualSorting,
479
+ manualFiltering,
480
+ initialState
481
+ } = _k, restTableOptions = __objRest(_k, [
482
+ "state",
483
+ "onSortingChange",
484
+ "onColumnFiltersChange",
485
+ "onColumnVisibilityChange",
486
+ "onColumnOrderChange",
487
+ "onExpandedChange",
488
+ "getCoreRowModel",
489
+ "getSortedRowModel",
490
+ "getFilteredRowModel",
491
+ "getSubRows",
492
+ "manualSorting",
493
+ "manualFiltering",
494
+ "initialState"
495
+ ]);
496
+ (0, import_react3.useEffect)(() => {
497
+ var _a3;
498
+ if (scrollToTopOn !== void 0) (_a3 = scrollerRef.current) == null ? void 0 : _a3.scrollTo({ top: 0 });
499
+ }, [scrollToTopOn]);
500
+ (0, import_react3.useEffect)(() => {
501
+ const scroller2 = scrollerRef.current;
502
+ if (!scroller2) return;
503
+ const syncHeaderScrollbarGutter = () => {
504
+ setHeaderScrollbarGutter(Math.max(0, scroller2.offsetWidth - scroller2.clientWidth));
505
+ };
506
+ syncHeaderScrollbarGutter();
507
+ if (typeof ResizeObserver === "undefined") return;
508
+ const observer = new ResizeObserver(syncHeaderScrollbarGutter);
509
+ observer.observe(scroller2);
510
+ return () => observer.disconnect();
511
+ }, []);
512
+ const table = (0, import_react_table.useReactTable)(__spreadProps(__spreadValues({}, restTableOptions), {
513
+ data,
514
+ columns: resolvedColumns,
515
+ initialState,
516
+ state: __spreadValues({
517
+ sorting,
518
+ columnFilters,
519
+ columnVisibility,
520
+ columnOrder,
521
+ expanded
522
+ }, controlledState),
523
+ onSortingChange: onSortingChange != null ? onSortingChange : setSorting,
524
+ onColumnFiltersChange: onColumnFiltersChange != null ? onColumnFiltersChange : setColumnFilters,
525
+ onColumnVisibilityChange: onColumnVisibilityChange != null ? onColumnVisibilityChange : setColumnVisibility,
526
+ onColumnOrderChange: onColumnOrderChange != null ? onColumnOrderChange : setColumnOrder,
527
+ onExpandedChange: onExpandedChange != null ? onExpandedChange : setExpanded,
528
+ getCoreRowModel: getCoreRowModelOption != null ? getCoreRowModelOption : (0, import_react_table.getCoreRowModel)(),
529
+ getSortedRowModel: getSortedRowModelOption != null ? getSortedRowModelOption : manualSorting ? void 0 : (0, import_react_table.getSortedRowModel)(),
530
+ getFilteredRowModel: getFilteredRowModelOption != null ? getFilteredRowModelOption : manualFiltering ? void 0 : (0, import_react_table.getFilteredRowModel)(),
531
+ getExpandedRowModel: treeOption ? (0, import_react_table.getExpandedRowModel)() : void 0,
532
+ getSubRows,
533
+ manualSorting,
534
+ manualFiltering
535
+ }));
536
+ const visibleColumns = table.getVisibleLeafColumns();
537
+ const allColumns = table.getAllFlatColumns();
538
+ const columnSettingsById = (0, import_react3.useMemo)(
539
+ () => new Map(allColumns.map((column) => [column.id, resolveColumnSettings(settings, column)])),
540
+ [allColumns, settings]
541
+ );
542
+ const getColumnSettings = (column) => {
543
+ var _a3;
544
+ return (_a3 = columnSettingsById.get(column.id)) != null ? _a3 : resolveColumnSettings(settings, column);
545
+ };
546
+ const grid = visibleColumns.map((column) => {
547
+ const columnSettings = getColumnSettings(column);
548
+ if ("widthPx" in columnSettings.sizing) return `${columnSettings.sizing.widthPx}px`;
549
+ return `minmax(${columnSettings.sizing.minPx}px, ${columnSettings.sizing.flex}fr)`;
550
+ }).join(" ");
551
+ const minWidth = visibleColumns.reduce((width, column) => {
552
+ const { sizing } = getColumnSettings(column);
553
+ return width + ("widthPx" in sizing ? sizing.widthPx : sizing.minPx);
554
+ }, 0);
555
+ const gridStyle = {
556
+ "--boneless-table-grid": grid,
557
+ minWidth: `${minWidth}px`
558
+ };
559
+ const tree = treeOption === true ? {} : treeOption === false ? void 0 : treeOption;
560
+ const treeColumnId = (_m = tree == null ? void 0 : tree.columnId) != null ? _m : (_l = visibleColumns[0]) == null ? void 0 : _l.id;
561
+ const treeIndentPx = (_n = tree == null ? void 0 : tree.indentPx) != null ? _n : 16;
562
+ const preExpandedRowModel = tree ? table.getPreExpandedRowModel() : void 0;
563
+ const expandableRows = (0, import_react3.useMemo)(
564
+ () => {
565
+ var _a3;
566
+ return (_a3 = preExpandedRowModel == null ? void 0 : preExpandedRowModel.flatRows.filter((row) => row.getCanExpand())) != null ? _a3 : [];
567
+ },
568
+ [preExpandedRowModel]
569
+ );
570
+ const allTreeRowsExpanded = (0, import_react3.useMemo)(
571
+ () => expandableRows.length > 0 && expandableRows.every((row) => row.getIsExpanded()),
572
+ [expandableRows, expanded]
573
+ );
574
+ (0, import_react3.useEffect)(() => {
575
+ if (!isFetchingMore || !canLoadMore) nearEndTriggeredRef.current = false;
576
+ }, [canLoadMore, isFetchingMore]);
577
+ function handleScroll() {
578
+ const element = scrollerRef.current;
579
+ if (!element) return;
580
+ if (headerScrollerRef.current) headerScrollerRef.current.scrollLeft = element.scrollLeft;
581
+ const isNearEnd = element.scrollHeight - element.scrollTop - element.clientHeight < nearEndOffset;
582
+ if (!isNearEnd) {
583
+ nearEndTriggeredRef.current = false;
584
+ return;
585
+ }
586
+ if (!onNearEnd || !canLoadMore || isFetchingMore || nearEndTriggeredRef.current) return;
587
+ nearEndTriggeredRef.current = true;
588
+ onNearEnd();
589
+ }
590
+ function resetTable() {
591
+ if (onReset) return onReset();
592
+ table.resetSorting();
593
+ table.resetColumnFilters();
594
+ table.resetColumnVisibility();
595
+ table.setColumnOrder(defaultOrder);
596
+ table.resetExpanded();
597
+ }
598
+ function moveColumn(id, direction) {
599
+ const current = table.getState().columnOrder;
600
+ table.setColumnOrder(moveItem(current.length ? current : defaultOrder, id, direction));
601
+ }
602
+ const shown = table.getRowModel().rows.length;
603
+ const total = totalCount != null ? totalCount : shown;
604
+ const rows = table.getRowModel().rows;
605
+ const isVirtualized = scroller === "fill" && virtualization !== false;
606
+ const rowVirtualizer = (0, import_react_virtual.useVirtualizer)({
607
+ count: rows.length,
608
+ getScrollElement: () => scrollerRef.current,
609
+ estimateSize: () => {
610
+ var _a3;
611
+ return virtualization === false ? 58 : (_a3 = virtualization.estimateSize) != null ? _a3 : 58;
612
+ },
613
+ overscan: isVirtualized ? (_o = virtualization.overscan) != null ? _o : 5 : 0,
614
+ enabled: isVirtualized,
615
+ initialRect: { width: 0, height: 600 }
616
+ });
617
+ const estimatedRowSize = virtualization === false ? 58 : (_p = virtualization.estimateSize) != null ? _p : 58;
618
+ const virtualRows = !isVirtualized ? rows.map((row, index) => ({ index, start: index * 58, key: row.id })) : rowVirtualizer.getVirtualItems().length > 0 ? rowVirtualizer.getVirtualItems() : rows.slice(0, Math.ceil(600 / estimatedRowSize) + ((_q = virtualization.overscan) != null ? _q : 5)).map((row, index) => ({
619
+ index,
620
+ start: index * estimatedRowSize,
621
+ key: row.id
622
+ }));
623
+ function closeColumnMenu() {
624
+ var _a3;
625
+ setShowColumns(false);
626
+ (_a3 = columnsButtonRef.current) == null ? void 0 : _a3.focus();
627
+ }
628
+ const toolbarSummary = /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: classNames.toolbarSummary, "data-slot": "toolbar-summary", children: [
629
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: total.toLocaleString() }),
630
+ " ",
631
+ resultLabel,
632
+ resultHint ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: classNames.toolbarHint, "data-slot": "toolbar-hint", children: resultHint }) : null
633
+ ] });
634
+ const toolbarActions = /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: classNames.toolbarActions, "data-slot": "toolbar-actions", children: [
635
+ tree && expandableRows.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
636
+ "button",
637
+ {
638
+ "data-slot": "tree-toggle-all",
639
+ onClick: () => table.toggleAllRowsExpanded(!allTreeRowsExpanded),
640
+ children: [
641
+ allTreeRowsExpanded ? icons == null ? void 0 : icons.collapse : icons == null ? void 0 : icons.expand,
642
+ allTreeRowsExpanded ? (_r = tree.collapseAllLabel) != null ? _r : "Collapse all" : (_s = tree.expandAllLabel) != null ? _s : "Expand all"
643
+ ]
644
+ }
645
+ ) : null,
646
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { "data-slot": "reset", onClick: resetTable, title: "Reset table settings", children: [
647
+ icons == null ? void 0 : icons.reset,
648
+ "Reset"
649
+ ] }),
650
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { "data-slot": "column-settings", children: [
651
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
652
+ "button",
653
+ {
654
+ "data-slot": "columns-button",
655
+ onClick: () => setShowColumns((value) => !value),
656
+ "aria-expanded": showColumns,
657
+ "aria-haspopup": "dialog",
658
+ "aria-controls": columnMenuId,
659
+ ref: columnsButtonRef,
660
+ children: [
661
+ icons == null ? void 0 : icons.columns,
662
+ "Columns"
663
+ ]
664
+ }
665
+ ),
666
+ showColumns ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
667
+ ColumnMenu,
668
+ {
669
+ table,
670
+ onClose: closeColumnMenu,
671
+ onMoveColumn: moveColumn,
672
+ className: classNames.columnMenu,
673
+ icons,
674
+ id: columnMenuId,
675
+ triggerRef: columnsButtonRef
676
+ }
677
+ ) : null
678
+ ] })
679
+ ] });
680
+ const defaultToolbar = toolbarLayout ? toolbarLayout({ table, summary: toolbarSummary, actions: toolbarActions }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: classNames.toolbar, "data-slot": "toolbar", children: [
681
+ toolbarSummary,
682
+ toolbarActions
683
+ ] });
684
+ const defaultFooter = /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("footer", { className: classNames.footer, "data-slot": "footer", children: [
685
+ "Showing ",
686
+ shown.toLocaleString(),
687
+ " of ",
688
+ total.toLocaleString(),
689
+ " ",
690
+ resultLabel
691
+ ] });
692
+ const rootStyle = scroller === "fill" ? { display: "flex", flexDirection: "column", minHeight: 0 } : void 0;
693
+ const scrollerStyle = __spreadValues({
694
+ overflowX: settings.interactions.horizontalOverflow
695
+ }, scroller === "fill" ? { flex: "1 1 auto", minHeight: 0, overflowY: "auto" } : {});
696
+ const tableStyle = __spreadValues({
697
+ "--boneless-table-grid": grid
698
+ }, scroller === "fill" ? { display: "flex", flexDirection: "column", flex: "1 1 auto", minHeight: 0 } : {});
699
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: cn(className, classNames.root), "data-slot": "root", style: rootStyle, children: [
700
+ renderSlot(toolbar, table, defaultToolbar),
701
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
702
+ "div",
703
+ {
704
+ className: classNames.table,
705
+ "data-slot": "table",
706
+ style: tableStyle,
707
+ "aria-busy": isLoading || isFetchingMore,
708
+ role: "table",
709
+ children: [
710
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
711
+ "div",
712
+ {
713
+ "data-slot": "header-scroller",
714
+ ref: headerScrollerRef,
715
+ style: {
716
+ flex: "0 0 auto",
717
+ minWidth: 0,
718
+ overflow: "hidden",
719
+ paddingInlineEnd: headerScrollbarGutter || void 0
720
+ },
721
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
722
+ "div",
723
+ {
724
+ className: classNames.headerGroup,
725
+ "data-slot": "header-group",
726
+ role: "rowgroup",
727
+ style: gridStyle,
728
+ children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
729
+ "div",
730
+ {
731
+ className: classNames.headerRow,
732
+ "data-slot": "header-row",
733
+ role: "row",
734
+ style: { display: "grid", gridTemplateColumns: "var(--boneless-table-grid)" },
735
+ children: headerGroup.headers.map((header) => {
736
+ var _a3;
737
+ const columnSettings = getColumnSettings(header.column);
738
+ const direction = header.column.getIsSorted();
739
+ const isSortable = !header.isPlaceholder && header.column.getCanSort() && columnSettings.sorting.enabled;
740
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
741
+ "div",
742
+ {
743
+ className: classNames.header,
744
+ "data-slot": "header",
745
+ "data-align": columnSettings.align,
746
+ "data-borders": columnSettings.borders,
747
+ role: "columnheader",
748
+ "aria-colspan": header.colSpan > 1 ? header.colSpan : void 0,
749
+ "aria-sort": direction === "asc" ? "ascending" : direction === "desc" ? "descending" : isSortable ? "none" : void 0,
750
+ style: { gridColumn: `span ${header.colSpan}` },
751
+ children: [
752
+ header.isPlaceholder ? null : isSortable ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
753
+ "button",
754
+ {
755
+ className: classNames.headerButton,
756
+ "data-align": columnSettings.align,
757
+ "data-slot": "header-button",
758
+ onClick: header.column.getToggleSortingHandler(),
759
+ children: [
760
+ (0, import_react_table.flexRender)(header.column.columnDef.header, header.getContext()),
761
+ (icons == null ? void 0 : icons.sort) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
762
+ "span",
763
+ {
764
+ className: classNames.sortIndicator,
765
+ "data-align": columnSettings.align,
766
+ "data-slot": "sort-indicator",
767
+ "data-reveal": columnSettings.sorting.reveal,
768
+ children: icons.sort(direction)
769
+ }
770
+ ) : null
771
+ ]
772
+ }
773
+ ) : (0, import_react_table.flexRender)(header.column.columnDef.header, header.getContext()),
774
+ !header.isPlaceholder && columnSettings.filtering ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
775
+ "div",
776
+ {
777
+ className: classNames.filter,
778
+ "data-slot": "filter",
779
+ "data-reveal": (_a3 = columnSettings.filtering.reveal) != null ? _a3 : settings.interactions.filterReveal,
780
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
781
+ ColumnFilter,
782
+ {
783
+ column: header.column,
784
+ settings: columnSettings.filtering,
785
+ icon: icons == null ? void 0 : icons.search,
786
+ debounceMs: filterDebounceMs
787
+ }
788
+ )
789
+ }
790
+ ) : null
791
+ ]
792
+ },
793
+ header.id
794
+ );
795
+ })
796
+ },
797
+ headerGroup.id
798
+ ))
799
+ }
800
+ )
801
+ }
802
+ ),
803
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
804
+ "div",
805
+ {
806
+ className: classNames.scroller,
807
+ "data-slot": "scroller",
808
+ ref: scrollerRef,
809
+ onScroll: handleScroll,
810
+ style: scrollerStyle,
811
+ children: [
812
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
813
+ TableSkeleton,
814
+ {
815
+ columnCount: visibleColumns.length,
816
+ rows: skeletonRows,
817
+ label: skeletonLabel,
818
+ classNames
819
+ }
820
+ ) : rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: classNames.emptyState, "data-slot": "empty-state", role: "status", children: emptyState ? typeof emptyState === "function" ? emptyState(table) : emptyState : table.getState().columnFilters.length > 0 ? "No rows match the current filters." : "No rows to display." }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
821
+ "div",
822
+ {
823
+ className: classNames.rowGroup,
824
+ "data-slot": "row-group",
825
+ style: __spreadProps(__spreadValues({}, gridStyle), {
826
+ height: `${isVirtualized ? rowVirtualizer.getTotalSize() : "auto"}`,
827
+ position: "relative"
828
+ }),
829
+ role: "rowgroup",
830
+ children: virtualRows.map((virtualRow) => {
831
+ var _a3;
832
+ const row = rows[virtualRow.index];
833
+ const href = rowLink ? resolveRowHref(rowLink, row.original) : void 0;
834
+ const RowLink = rowLink == null ? void 0 : rowLink.component;
835
+ const rowContent = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
836
+ "div",
837
+ {
838
+ className: classNames.row,
839
+ "data-slot": "row",
840
+ role: "row",
841
+ style: { display: "grid", gridTemplateColumns: "var(--boneless-table-grid)" },
842
+ tabIndex: onRowClick && !href ? 0 : void 0,
843
+ onClick: (event) => onRowClick == null ? void 0 : onRowClick(row.original, event),
844
+ onKeyDown: (event) => {
845
+ if (!onRowClick || href || event.key !== "Enter" && event.key !== " ")
846
+ return;
847
+ event.preventDefault();
848
+ onRowClick(row.original, event);
849
+ },
850
+ children: row.getVisibleCells().map((cell) => {
851
+ var _a4, _b3, _c2;
852
+ const columnSettings = getColumnSettings(cell.column);
853
+ const title = columnSettings.valueDisplay != null ? String((_a4 = cell.getValue()) != null ? _a4 : "") : void 0;
854
+ const isTreeCell = Boolean(tree && cell.column.id === treeColumnId);
855
+ const canExpand = row.getCanExpand();
856
+ const cellContent = (0, import_react_table.flexRender)(cell.column.columnDef.cell, cell.getContext());
857
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
858
+ "div",
859
+ {
860
+ className: classNames.cell,
861
+ "data-slot": "cell",
862
+ "data-align": columnSettings.align,
863
+ "data-borders": columnSettings.borders,
864
+ role: "cell",
865
+ title,
866
+ children: isTreeCell ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
867
+ "div",
868
+ {
869
+ "data-slot": "tree-content",
870
+ style: { paddingInlineStart: `${row.depth * treeIndentPx}px` },
871
+ children: [
872
+ canExpand ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
873
+ "button",
874
+ {
875
+ "data-slot": "tree-row-toggle",
876
+ type: "button",
877
+ "aria-expanded": row.getIsExpanded(),
878
+ "aria-label": `${row.getIsExpanded() ? "Collapse" : "Expand"} row`,
879
+ onClick: (event) => {
880
+ event.stopPropagation();
881
+ row.toggleExpanded();
882
+ },
883
+ children: row.getIsExpanded() ? (_b3 = icons == null ? void 0 : icons.collapse) != null ? _b3 : "\u2212" : (_c2 = icons == null ? void 0 : icons.expand) != null ? _c2 : "+"
884
+ }
885
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "data-slot": "tree-row-toggle-spacer", "aria-hidden": "true" }),
886
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "data-slot": "tree-value", children: cellContent })
887
+ ]
888
+ }
889
+ ) : cellContent
890
+ },
891
+ cell.id
892
+ );
893
+ })
894
+ }
895
+ );
896
+ const rowStyle = !isVirtualized ? void 0 : {
897
+ position: "absolute",
898
+ top: 0,
899
+ left: 0,
900
+ width: "100%",
901
+ transform: `translateY(${virtualRow.start}px)`
902
+ };
903
+ if (!href || !rowLink || !RowLink || tree && row.getCanExpand())
904
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
905
+ "div",
906
+ {
907
+ "data-index": isVirtualized ? virtualRow.index : void 0,
908
+ ref: isVirtualized ? rowVirtualizer.measureElement : void 0,
909
+ style: rowStyle,
910
+ children: rowContent
911
+ },
912
+ row.id
913
+ );
914
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
915
+ "div",
916
+ {
917
+ "data-index": isVirtualized ? virtualRow.index : void 0,
918
+ ref: isVirtualized ? rowVirtualizer.measureElement : void 0,
919
+ style: rowStyle,
920
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
921
+ RowLink,
922
+ {
923
+ className: (_a3 = classNames.rowLink) != null ? _a3 : "",
924
+ href,
925
+ "aria-label": resolveRowLinkLabel(rowLink, row.original),
926
+ target: rowLink.target,
927
+ rel: rowLink.rel,
928
+ children: rowContent
929
+ }
930
+ )
931
+ },
932
+ row.id
933
+ );
934
+ })
935
+ }
936
+ ),
937
+ isFetchingMore ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
938
+ TableSkeleton,
939
+ {
940
+ columnCount: visibleColumns.length,
941
+ rows: 3,
942
+ label: skeletonLabel,
943
+ classNames
944
+ }
945
+ ) : null
946
+ ]
947
+ }
948
+ )
949
+ ]
950
+ }
951
+ ),
952
+ renderSlot(footer, table, defaultFooter)
953
+ ] });
954
+ }
955
+
956
+ // src/index.ts
957
+ var import_react_table2 = require("@tanstack/react-table");
958
+ // Annotate the CommonJS export names for ESM import in node:
959
+ 0 && (module.exports = {
960
+ BonelessTable,
961
+ ColumnFilter,
962
+ ColumnMenu,
963
+ TableSkeleton,
964
+ compactValue,
965
+ createColumnHelper,
966
+ defaultBonelessTableSettings,
967
+ defineColumns,
968
+ flexRender,
969
+ getColumnIds,
970
+ getCoreRowModel,
971
+ getExpandedRowModel,
972
+ getFilteredRowModel,
973
+ getSortedRowModel,
974
+ mergeBonelessTableSettings,
975
+ resolveColumnSettings,
976
+ resolveColumns,
977
+ resolveRowHref,
978
+ resolveRowLinkLabel,
979
+ useReactTable,
980
+ withDefaultCells
981
+ });
982
+ //# sourceMappingURL=index.js.map