tabler-react-2 0.1.153-alpha.2 → 0.1.154
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 +17 -5
- package/dist/table-v2/index.js +328 -0
- package/docs/gatsby-node.js +36 -0
- package/docs/src/components/LoadableTabler.jsx +3 -0
- package/docs/src/components/Tabler.jsx +128 -51
- package/docs/src/config/sidebar.yml +2 -0
- package/docs/src/data/congressPeople.json +88 -0
- package/docs/src/docs/components/table-v2.mdx +367 -0
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -156,7 +156,7 @@ Object.keys(_index15).forEach(function (key) {
|
|
|
156
156
|
}
|
|
157
157
|
});
|
|
158
158
|
});
|
|
159
|
-
var _index16 = require("./
|
|
159
|
+
var _index16 = require("./table-v2/index");
|
|
160
160
|
Object.keys(_index16).forEach(function (key) {
|
|
161
161
|
if (key === "default" || key === "__esModule") return;
|
|
162
162
|
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
@@ -168,7 +168,7 @@ Object.keys(_index16).forEach(function (key) {
|
|
|
168
168
|
}
|
|
169
169
|
});
|
|
170
170
|
});
|
|
171
|
-
var _index17 = require("./
|
|
171
|
+
var _index17 = require("./timeline/index");
|
|
172
172
|
Object.keys(_index17).forEach(function (key) {
|
|
173
173
|
if (key === "default" || key === "__esModule") return;
|
|
174
174
|
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
@@ -180,7 +180,7 @@ Object.keys(_index17).forEach(function (key) {
|
|
|
180
180
|
}
|
|
181
181
|
});
|
|
182
182
|
});
|
|
183
|
-
var _index18 = require("./
|
|
183
|
+
var _index18 = require("./input/index");
|
|
184
184
|
Object.keys(_index18).forEach(function (key) {
|
|
185
185
|
if (key === "default" || key === "__esModule") return;
|
|
186
186
|
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
@@ -192,7 +192,7 @@ Object.keys(_index18).forEach(function (key) {
|
|
|
192
192
|
}
|
|
193
193
|
});
|
|
194
194
|
});
|
|
195
|
-
var _index19 = require("./
|
|
195
|
+
var _index19 = require("./navbar/index");
|
|
196
196
|
Object.keys(_index19).forEach(function (key) {
|
|
197
197
|
if (key === "default" || key === "__esModule") return;
|
|
198
198
|
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
@@ -204,7 +204,7 @@ Object.keys(_index19).forEach(function (key) {
|
|
|
204
204
|
}
|
|
205
205
|
});
|
|
206
206
|
});
|
|
207
|
-
var _index20 = require("./
|
|
207
|
+
var _index20 = require("./segmentedControl/index");
|
|
208
208
|
Object.keys(_index20).forEach(function (key) {
|
|
209
209
|
if (key === "default" || key === "__esModule") return;
|
|
210
210
|
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
@@ -216,5 +216,17 @@ Object.keys(_index20).forEach(function (key) {
|
|
|
216
216
|
}
|
|
217
217
|
});
|
|
218
218
|
});
|
|
219
|
+
var _index21 = require("./offcanvas/index");
|
|
220
|
+
Object.keys(_index21).forEach(function (key) {
|
|
221
|
+
if (key === "default" || key === "__esModule") return;
|
|
222
|
+
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
|
223
|
+
if (key in exports && exports[key] === _index21[key]) return;
|
|
224
|
+
Object.defineProperty(exports, key, {
|
|
225
|
+
enumerable: true,
|
|
226
|
+
get: function get() {
|
|
227
|
+
return _index21[key];
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
});
|
|
219
231
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
|
220
232
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.TableV2 = exports.SortIcon = exports.RangeText = exports.Pager = exports.PageSizeSelect = void 0;
|
|
8
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
9
|
+
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
10
|
+
var _reactTable = require("@tanstack/react-table");
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
12
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
|
13
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
|
|
14
|
+
// TableV2.jsx
|
|
15
|
+
// TanStack Table v8 styled like the Original component's Tabler look.
|
|
16
|
+
// Adds `dense` mode for a compact layout.
|
|
17
|
+
// Controlled (server-side) pagination & sorting. ES modules, named exports, arrow functions only.
|
|
18
|
+
|
|
19
|
+
/** Small utilities */
|
|
20
|
+
|
|
21
|
+
var SortIcon = exports.SortIcon = function SortIcon(_ref) {
|
|
22
|
+
var state = _ref.state;
|
|
23
|
+
if (state === "asc") return /*#__PURE__*/_react["default"].createElement("span", {
|
|
24
|
+
"aria-label": "sorted ascending"
|
|
25
|
+
}, "\u25B2");
|
|
26
|
+
if (state === "desc") return /*#__PURE__*/_react["default"].createElement("span", {
|
|
27
|
+
"aria-label": "sorted descending"
|
|
28
|
+
}, "\u25BC");
|
|
29
|
+
return /*#__PURE__*/_react["default"].createElement("span", {
|
|
30
|
+
className: "text-muted",
|
|
31
|
+
"aria-hidden": true
|
|
32
|
+
}, "\u2195");
|
|
33
|
+
};
|
|
34
|
+
var RangeText = exports.RangeText = function RangeText(_ref2) {
|
|
35
|
+
var page = _ref2.page,
|
|
36
|
+
size = _ref2.size,
|
|
37
|
+
total = _ref2.total,
|
|
38
|
+
dense = _ref2.dense;
|
|
39
|
+
var start = total === 0 ? 0 : (page - 1) * size + 1;
|
|
40
|
+
var end = Math.min(page * size, total);
|
|
41
|
+
return /*#__PURE__*/_react["default"].createElement("span", {
|
|
42
|
+
className: dense ? "ms-2 small text-muted" : "ms-3 text-muted"
|
|
43
|
+
}, start.toLocaleString(), "\u2013", end.toLocaleString(), " of", " ", total.toLocaleString());
|
|
44
|
+
};
|
|
45
|
+
var PageSizeSelect = exports.PageSizeSelect = function PageSizeSelect(_ref3) {
|
|
46
|
+
var value = _ref3.value,
|
|
47
|
+
_onChange = _ref3.onChange,
|
|
48
|
+
_ref3$options = _ref3.options,
|
|
49
|
+
options = _ref3$options === void 0 ? [10, 25, 50, 100] : _ref3$options,
|
|
50
|
+
dense = _ref3.dense;
|
|
51
|
+
return /*#__PURE__*/_react["default"].createElement("select", {
|
|
52
|
+
className: dense ? "form-select form-select-sm" : "form-select",
|
|
53
|
+
value: value,
|
|
54
|
+
onChange: function onChange(e) {
|
|
55
|
+
return _onChange(Number(e.target.value));
|
|
56
|
+
},
|
|
57
|
+
"aria-label": "Rows per page",
|
|
58
|
+
style: {
|
|
59
|
+
width: dense ? 90 : 110
|
|
60
|
+
}
|
|
61
|
+
}, options.map(function (n) {
|
|
62
|
+
return /*#__PURE__*/_react["default"].createElement("option", {
|
|
63
|
+
key: n,
|
|
64
|
+
value: n
|
|
65
|
+
}, n, " / page");
|
|
66
|
+
}));
|
|
67
|
+
};
|
|
68
|
+
var Pager = exports.Pager = function Pager(_ref4) {
|
|
69
|
+
var page = _ref4.page,
|
|
70
|
+
size = _ref4.size,
|
|
71
|
+
total = _ref4.total,
|
|
72
|
+
onPageChange = _ref4.onPageChange,
|
|
73
|
+
disabled = _ref4.disabled,
|
|
74
|
+
_ref4$className = _ref4.className,
|
|
75
|
+
className = _ref4$className === void 0 ? "" : _ref4$className,
|
|
76
|
+
dense = _ref4.dense;
|
|
77
|
+
var totalPages = Math.max(1, Math.ceil(total / Math.max(1, size)));
|
|
78
|
+
var canPrev = page > 1 && !disabled;
|
|
79
|
+
var canNext = page < totalPages && !disabled;
|
|
80
|
+
var go = function go(p) {
|
|
81
|
+
var clamped = Math.min(Math.max(1, p), totalPages);
|
|
82
|
+
if (clamped !== page) onPageChange(clamped);
|
|
83
|
+
};
|
|
84
|
+
return /*#__PURE__*/_react["default"].createElement("div", {
|
|
85
|
+
className: "d-inline-flex align-items-center ".concat(dense ? "gap-2" : "", " ").concat(className)
|
|
86
|
+
}, /*#__PURE__*/_react["default"].createElement("button", {
|
|
87
|
+
className: "btn ".concat(dense ? "btn-sm" : "btn-sm", " ").concat(dense ? "px-2 py-1" : ""),
|
|
88
|
+
onClick: function onClick() {
|
|
89
|
+
return go(Math.max(1, page - 1));
|
|
90
|
+
},
|
|
91
|
+
disabled: !canPrev
|
|
92
|
+
}, "Previous"), /*#__PURE__*/_react["default"].createElement("button", {
|
|
93
|
+
className: "btn ".concat(dense ? "btn-sm ms-1" : "btn-sm ms-2", " ").concat(dense ? "px-2 py-1" : ""),
|
|
94
|
+
onClick: function onClick() {
|
|
95
|
+
return go(Math.min(totalPages, page + 1));
|
|
96
|
+
},
|
|
97
|
+
disabled: !canNext
|
|
98
|
+
}, "Next"), /*#__PURE__*/_react["default"].createElement("span", {
|
|
99
|
+
className: dense ? "ms-2 small text-muted" : "ms-3 text-muted"
|
|
100
|
+
}, "Page ", page, " of ", totalPages));
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/** Main table */
|
|
104
|
+
|
|
105
|
+
var TableV2 = exports.TableV2 = function TableV2(_ref5) {
|
|
106
|
+
var columns = _ref5.columns,
|
|
107
|
+
data = _ref5.data,
|
|
108
|
+
totalRows = _ref5.totalRows,
|
|
109
|
+
page = _ref5.page,
|
|
110
|
+
size = _ref5.size,
|
|
111
|
+
onPageChange = _ref5.onPageChange,
|
|
112
|
+
onSizeChange = _ref5.onSizeChange,
|
|
113
|
+
sorting = _ref5.sorting,
|
|
114
|
+
onSortingChange = _ref5.onSortingChange,
|
|
115
|
+
getRowId = _ref5.getRowId,
|
|
116
|
+
rowSelection = _ref5.rowSelection,
|
|
117
|
+
onRowSelectionChange = _ref5.onRowSelectionChange,
|
|
118
|
+
_ref5$nowrap = _ref5.nowrap,
|
|
119
|
+
nowrap = _ref5$nowrap === void 0 ? false : _ref5$nowrap,
|
|
120
|
+
_ref5$stickyHeader = _ref5.stickyHeader,
|
|
121
|
+
stickyHeader = _ref5$stickyHeader === void 0 ? false : _ref5$stickyHeader,
|
|
122
|
+
_ref5$className = _ref5.className,
|
|
123
|
+
className = _ref5$className === void 0 ? "" : _ref5$className,
|
|
124
|
+
_ref5$tableClassName = _ref5.tableClassName,
|
|
125
|
+
tableClassName = _ref5$tableClassName === void 0 ? "" : _ref5$tableClassName,
|
|
126
|
+
_ref5$paginationClass = _ref5.paginationClassName,
|
|
127
|
+
paginationClassName = _ref5$paginationClass === void 0 ? "" : _ref5$paginationClass,
|
|
128
|
+
_ref5$parentClassName = _ref5.parentClassName,
|
|
129
|
+
parentClassName = _ref5$parentClassName === void 0 ? "" : _ref5$parentClassName,
|
|
130
|
+
_ref5$dense = _ref5.dense,
|
|
131
|
+
dense = _ref5$dense === void 0 ? false : _ref5$dense,
|
|
132
|
+
_ref5$emptyState = _ref5.emptyState,
|
|
133
|
+
emptyState = _ref5$emptyState === void 0 ? "No data" : _ref5$emptyState,
|
|
134
|
+
_ref5$loading = _ref5.loading,
|
|
135
|
+
loading = _ref5$loading === void 0 ? false : _ref5$loading,
|
|
136
|
+
_ref5$showPagination = _ref5.showPagination,
|
|
137
|
+
showPagination = _ref5$showPagination === void 0 ? true : _ref5$showPagination,
|
|
138
|
+
_ref5$pageSizeOptions = _ref5.pageSizeOptions,
|
|
139
|
+
pageSizeOptions = _ref5$pageSizeOptions === void 0 ? [10, 25, 50, 100] : _ref5$pageSizeOptions;
|
|
140
|
+
var table = (0, _reactTable.useReactTable)({
|
|
141
|
+
data: data,
|
|
142
|
+
columns: columns,
|
|
143
|
+
state: {
|
|
144
|
+
sorting: sorting,
|
|
145
|
+
rowSelection: rowSelection !== null && rowSelection !== void 0 ? rowSelection : {}
|
|
146
|
+
},
|
|
147
|
+
getCoreRowModel: (0, _reactTable.getCoreRowModel)(),
|
|
148
|
+
manualSorting: true,
|
|
149
|
+
enableSortingRemoval: false,
|
|
150
|
+
onSortingChange: onSortingChange,
|
|
151
|
+
getRowId: getRowId,
|
|
152
|
+
onRowSelectionChange: onRowSelectionChange
|
|
153
|
+
});
|
|
154
|
+
var headerCell = function headerCell(_ref6) {
|
|
155
|
+
var header = _ref6.header;
|
|
156
|
+
var canSort = header.column.getCanSort();
|
|
157
|
+
var sortState = header.column.getIsSorted();
|
|
158
|
+
var handleSort = function handleSort() {
|
|
159
|
+
if (!canSort) return;
|
|
160
|
+
var id = header.column.id;
|
|
161
|
+
var next = sortState === "asc" ? [{
|
|
162
|
+
id: id,
|
|
163
|
+
desc: true
|
|
164
|
+
}] : sortState === "desc" ? [] : [{
|
|
165
|
+
id: id,
|
|
166
|
+
desc: false
|
|
167
|
+
}];
|
|
168
|
+
onSortingChange(next);
|
|
169
|
+
};
|
|
170
|
+
var thClass = [header.column.columnDef.className || "", canSort ? "sortable" : "", dense ? "py-1" : ""].filter(Boolean).join(" ");
|
|
171
|
+
return /*#__PURE__*/_react["default"].createElement("th", {
|
|
172
|
+
key: header.id,
|
|
173
|
+
className: thClass,
|
|
174
|
+
onClick: canSort && !loading ? handleSort : undefined,
|
|
175
|
+
style: {
|
|
176
|
+
cursor: canSort && !loading ? "pointer" : "default"
|
|
177
|
+
},
|
|
178
|
+
"aria-sort": sortState === "asc" ? "ascending" : sortState === "desc" ? "descending" : "none"
|
|
179
|
+
}, /*#__PURE__*/_react["default"].createElement("span", {
|
|
180
|
+
style: {
|
|
181
|
+
marginRight: 8
|
|
182
|
+
}
|
|
183
|
+
}, (0, _reactTable.flexRender)(header.column.columnDef.header, header.getContext())), canSort && /*#__PURE__*/_react["default"].createElement(SortIcon, {
|
|
184
|
+
state: sortState || false
|
|
185
|
+
}));
|
|
186
|
+
};
|
|
187
|
+
var content = (0, _react.useMemo)(function () {
|
|
188
|
+
if (!loading && data.length === 0) {
|
|
189
|
+
return /*#__PURE__*/_react["default"].createElement("tr", null, /*#__PURE__*/_react["default"].createElement("td", {
|
|
190
|
+
colSpan: table.getAllLeafColumns().length,
|
|
191
|
+
className: "text-center ".concat(dense ? "py-3 small" : "py-3", " text-muted")
|
|
192
|
+
}, typeof emptyState === "function" ? emptyState() : emptyState));
|
|
193
|
+
}
|
|
194
|
+
return table.getRowModel().rows.map(function (row) {
|
|
195
|
+
return /*#__PURE__*/_react["default"].createElement("tr", {
|
|
196
|
+
key: row.id,
|
|
197
|
+
className: [row.getIsSelected() ? "table-active" : "", dense ? "align-middle" : ""].filter(Boolean).join(" ")
|
|
198
|
+
}, row.getVisibleCells().map(function (cell) {
|
|
199
|
+
return /*#__PURE__*/_react["default"].createElement("td", {
|
|
200
|
+
key: cell.id,
|
|
201
|
+
className: [cell.column.columnDef.className || "", dense ? "py-1" : ""].filter(Boolean).join(" ")
|
|
202
|
+
}, (0, _reactTable.flexRender)(cell.column.columnDef.cell, cell.getContext()));
|
|
203
|
+
}));
|
|
204
|
+
});
|
|
205
|
+
}, [data, loading, emptyState, table, dense]);
|
|
206
|
+
var total = totalRows !== null && totalRows !== void 0 ? totalRows : 0;
|
|
207
|
+
return /*#__PURE__*/_react["default"].createElement("div", {
|
|
208
|
+
className: parentClassName
|
|
209
|
+
}, /*#__PURE__*/_react["default"].createElement("div", {
|
|
210
|
+
className: "table-responsive ".concat(nowrap ? "table-nowrap" : "", " ").concat(className)
|
|
211
|
+
}, /*#__PURE__*/_react["default"].createElement("table", {
|
|
212
|
+
className: ["table table-vcenter", stickyHeader ? "sticky-top" : "", dense ? "table-sm" : "", tableClassName].filter(Boolean).join(" ")
|
|
213
|
+
}, /*#__PURE__*/_react["default"].createElement("thead", null, table.getHeaderGroups().map(function (hg) {
|
|
214
|
+
return /*#__PURE__*/_react["default"].createElement("tr", {
|
|
215
|
+
key: hg.id
|
|
216
|
+
}, hg.headers.map(function (h) {
|
|
217
|
+
return headerCell({
|
|
218
|
+
header: h
|
|
219
|
+
});
|
|
220
|
+
}));
|
|
221
|
+
})), /*#__PURE__*/_react["default"].createElement("tbody", null, loading && /*#__PURE__*/_react["default"].createElement("tr", null, /*#__PURE__*/_react["default"].createElement("td", {
|
|
222
|
+
colSpan: table.getAllLeafColumns().length,
|
|
223
|
+
className: "text-center ".concat(dense ? "py-2" : "py-3")
|
|
224
|
+
}, /*#__PURE__*/_react["default"].createElement("span", {
|
|
225
|
+
className: "spinner-border spinner-border-sm me-2",
|
|
226
|
+
role: "status",
|
|
227
|
+
"aria-hidden": true
|
|
228
|
+
}), /*#__PURE__*/_react["default"].createElement("span", {
|
|
229
|
+
className: dense ? "small text-muted" : "text-muted"
|
|
230
|
+
}, "Loading\u2026"))), content))), showPagination && /*#__PURE__*/_react["default"].createElement("div", {
|
|
231
|
+
className: "d-flex justify-content-between align-items-center ".concat(dense ? "mt-1" : "mt-2", " ").concat(paginationClassName)
|
|
232
|
+
}, /*#__PURE__*/_react["default"].createElement("div", {
|
|
233
|
+
className: "d-flex align-items-center ".concat(dense ? "gap-2" : "")
|
|
234
|
+
}, /*#__PURE__*/_react["default"].createElement(Pager, {
|
|
235
|
+
page: page,
|
|
236
|
+
size: size,
|
|
237
|
+
total: total,
|
|
238
|
+
onPageChange: onPageChange,
|
|
239
|
+
disabled: loading,
|
|
240
|
+
dense: dense
|
|
241
|
+
}), /*#__PURE__*/_react["default"].createElement(RangeText, {
|
|
242
|
+
page: page,
|
|
243
|
+
size: size,
|
|
244
|
+
total: total,
|
|
245
|
+
dense: dense
|
|
246
|
+
})), /*#__PURE__*/_react["default"].createElement(PageSizeSelect, {
|
|
247
|
+
value: size,
|
|
248
|
+
onChange: function onChange(n) {
|
|
249
|
+
onSizeChange === null || onSizeChange === void 0 || onSizeChange(n);
|
|
250
|
+
},
|
|
251
|
+
options: pageSizeOptions,
|
|
252
|
+
dense: dense
|
|
253
|
+
})));
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/** PropTypes */
|
|
257
|
+
|
|
258
|
+
SortIcon.propTypes = {
|
|
259
|
+
state: _propTypes["default"].oneOf(["asc", "desc", false])
|
|
260
|
+
};
|
|
261
|
+
RangeText.propTypes = {
|
|
262
|
+
page: _propTypes["default"].number.isRequired,
|
|
263
|
+
size: _propTypes["default"].number.isRequired,
|
|
264
|
+
total: _propTypes["default"].number.isRequired,
|
|
265
|
+
dense: _propTypes["default"].bool
|
|
266
|
+
};
|
|
267
|
+
PageSizeSelect.propTypes = {
|
|
268
|
+
value: _propTypes["default"].number.isRequired,
|
|
269
|
+
onChange: _propTypes["default"].func.isRequired,
|
|
270
|
+
options: _propTypes["default"].arrayOf(_propTypes["default"].number),
|
|
271
|
+
dense: _propTypes["default"].bool
|
|
272
|
+
};
|
|
273
|
+
Pager.propTypes = {
|
|
274
|
+
page: _propTypes["default"].number.isRequired,
|
|
275
|
+
size: _propTypes["default"].number.isRequired,
|
|
276
|
+
total: _propTypes["default"].number.isRequired,
|
|
277
|
+
onPageChange: _propTypes["default"].func.isRequired,
|
|
278
|
+
disabled: _propTypes["default"].bool,
|
|
279
|
+
className: _propTypes["default"].string,
|
|
280
|
+
dense: _propTypes["default"].bool
|
|
281
|
+
};
|
|
282
|
+
TableV2.propTypes = {
|
|
283
|
+
columns: _propTypes["default"].array.isRequired,
|
|
284
|
+
data: _propTypes["default"].array.isRequired,
|
|
285
|
+
totalRows: _propTypes["default"].number.isRequired,
|
|
286
|
+
page: _propTypes["default"].number.isRequired,
|
|
287
|
+
size: _propTypes["default"].number.isRequired,
|
|
288
|
+
onPageChange: _propTypes["default"].func.isRequired,
|
|
289
|
+
onSizeChange: _propTypes["default"].func.isRequired,
|
|
290
|
+
sorting: _propTypes["default"].arrayOf(_propTypes["default"].shape({
|
|
291
|
+
id: _propTypes["default"].string.isRequired,
|
|
292
|
+
desc: _propTypes["default"].bool
|
|
293
|
+
})).isRequired,
|
|
294
|
+
onSortingChange: _propTypes["default"].func.isRequired,
|
|
295
|
+
getRowId: _propTypes["default"].func,
|
|
296
|
+
rowSelection: _propTypes["default"].object,
|
|
297
|
+
onRowSelectionChange: _propTypes["default"].func,
|
|
298
|
+
nowrap: _propTypes["default"].bool,
|
|
299
|
+
stickyHeader: _propTypes["default"].bool,
|
|
300
|
+
className: _propTypes["default"].string,
|
|
301
|
+
tableClassName: _propTypes["default"].string,
|
|
302
|
+
paginationClassName: _propTypes["default"].string,
|
|
303
|
+
parentClassName: _propTypes["default"].string,
|
|
304
|
+
emptyState: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].func]),
|
|
305
|
+
loading: _propTypes["default"].bool,
|
|
306
|
+
showPagination: _propTypes["default"].bool,
|
|
307
|
+
pageSizeOptions: _propTypes["default"].arrayOf(_propTypes["default"].number),
|
|
308
|
+
dense: _propTypes["default"].bool // <<< NEW
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
/** Usage (server-side)
|
|
312
|
+
*
|
|
313
|
+
* <TableV2
|
|
314
|
+
* dense // compact mode
|
|
315
|
+
* columns={columns}
|
|
316
|
+
* data={data}
|
|
317
|
+
* totalRows={total}
|
|
318
|
+
* page={page}
|
|
319
|
+
* size={size}
|
|
320
|
+
* onPageChange={setPage}
|
|
321
|
+
* onSizeChange={(n)=>{ setPage(1); setSize(n); }}
|
|
322
|
+
* sorting={sorting}
|
|
323
|
+
* onSortingChange={(next)=>{ setPage(1); setSorting(next); }}
|
|
324
|
+
* loading={loading}
|
|
325
|
+
* nowrap
|
|
326
|
+
* stickyHeader
|
|
327
|
+
* />
|
|
328
|
+
*/
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
|
|
3
|
+
exports.onCreateWebpackConfig = ({ stage, actions, loaders }) => {
|
|
4
|
+
// Only alias to source during local development unless explicitly enabled
|
|
5
|
+
const enableSourceAlias =
|
|
6
|
+
stage === "develop" || process.env.DOCS_USE_SRC === "1";
|
|
7
|
+
|
|
8
|
+
if (!enableSourceAlias) return;
|
|
9
|
+
|
|
10
|
+
actions.setWebpackConfig({
|
|
11
|
+
resolve: {
|
|
12
|
+
alias: {
|
|
13
|
+
// Use library source directly during docs development
|
|
14
|
+
"tabler-react-2": path.resolve(__dirname, "../src"),
|
|
15
|
+
|
|
16
|
+
// Ensure a single React/DOM instance (avoid hooks mismatch)
|
|
17
|
+
react: path.resolve(__dirname, "node_modules/react"),
|
|
18
|
+
"react-dom": path.resolve(__dirname, "node_modules/react-dom"),
|
|
19
|
+
// Route dependency to the docs install so resolution is stable
|
|
20
|
+
"react-router-dom": path.resolve(
|
|
21
|
+
__dirname,
|
|
22
|
+
"node_modules/react-router-dom"
|
|
23
|
+
),
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
module: {
|
|
27
|
+
rules: [
|
|
28
|
+
{
|
|
29
|
+
test: /\.jsx?$/,
|
|
30
|
+
include: [path.resolve(__dirname, "../src")],
|
|
31
|
+
use: [loaders.js()],
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
};
|
|
@@ -57,6 +57,9 @@ export const Table = loadable(() =>
|
|
|
57
57
|
export const Timeline = loadable(() =>
|
|
58
58
|
import("tabler-react-2").then((mod) => mod.Timeline)
|
|
59
59
|
);
|
|
60
|
+
export const TableV2 = loadable(() =>
|
|
61
|
+
import("tabler-react-2").then((mod) => mod.TableV2)
|
|
62
|
+
);
|
|
60
63
|
|
|
61
64
|
// Util
|
|
62
65
|
const Hr = loadable(() => import("tabler-react-2").then((mod) => mod.Util.Hr));
|
|
@@ -1,63 +1,140 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useState } from "react";
|
|
1
|
+
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import ReactDOM from "react-dom";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const TABLER_CSS_URL =
|
|
5
|
+
"https://cdn.jsdelivr.net/npm/@tabler/core@1.3.0/dist/css/tabler.min.css";
|
|
6
|
+
const TABLER_JS_URL =
|
|
7
|
+
"https://cdn.jsdelivr.net/npm/@tabler/core@1.3.0/dist/js/tabler.min.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* <Tabler>
|
|
11
|
+
* Same public API as before:
|
|
12
|
+
* - children
|
|
13
|
+
* - theme?: "light" | "dark" (defaults to "light")
|
|
14
|
+
* Also accepts className and style for the outer iframe element.
|
|
15
|
+
*/
|
|
16
|
+
export const Tabler = ({
|
|
17
|
+
children,
|
|
18
|
+
theme = "light",
|
|
19
|
+
className,
|
|
20
|
+
style,
|
|
21
|
+
headExtras = "",
|
|
22
|
+
cssUrl = TABLER_CSS_URL,
|
|
23
|
+
jsUrl = TABLER_JS_URL,
|
|
24
|
+
}) => {
|
|
25
|
+
const iframeRef = useRef(null);
|
|
26
|
+
const [mountNode, setMountNode] = useState(null);
|
|
27
|
+
const roRef = useRef(null);
|
|
28
|
+
|
|
29
|
+
const srcDoc = useMemo(() => {
|
|
30
|
+
const t = theme === "dark" ? "dark" : "light";
|
|
31
|
+
return `<!DOCTYPE html>
|
|
32
|
+
<html lang="en" data-bs-theme="${t}">
|
|
33
|
+
<head>
|
|
34
|
+
<meta charset="utf-8" />
|
|
35
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
36
|
+
<link rel="stylesheet" href="${cssUrl}">
|
|
37
|
+
${headExtras}
|
|
38
|
+
<style>
|
|
39
|
+
html, body { margin: 0; padding: 0; }
|
|
40
|
+
body { display: block; background-color: transparent!important; }
|
|
41
|
+
#app { min-height: 0; }
|
|
42
|
+
</style>
|
|
43
|
+
</head>
|
|
44
|
+
<body>
|
|
45
|
+
<div id="app"></div>
|
|
46
|
+
</body>
|
|
47
|
+
</html>`;
|
|
48
|
+
}, [cssUrl, headExtras, theme]);
|
|
8
49
|
|
|
9
50
|
useEffect(() => {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
51
|
+
const iframe = iframeRef.current;
|
|
52
|
+
if (!iframe) return;
|
|
53
|
+
|
|
54
|
+
iframe.srcdoc = srcDoc;
|
|
55
|
+
|
|
56
|
+
const onLoad = () => {
|
|
57
|
+
const doc = iframe.contentDocument;
|
|
58
|
+
const win = iframe.contentWindow;
|
|
59
|
+
if (!doc || !win) return;
|
|
60
|
+
|
|
61
|
+
// Ensure theme attribute is correct
|
|
62
|
+
doc.documentElement.setAttribute(
|
|
63
|
+
"data-bs-theme",
|
|
64
|
+
theme === "dark" ? "dark" : "light"
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const app = doc.getElementById("app");
|
|
68
|
+
setMountNode(app || null);
|
|
69
|
+
|
|
70
|
+
// Inject Tabler JS in iframe context
|
|
71
|
+
const s = doc.createElement("script");
|
|
72
|
+
s.src = jsUrl;
|
|
73
|
+
s.defer = true;
|
|
74
|
+
doc.head.appendChild(s);
|
|
75
|
+
|
|
76
|
+
// Auto-resize to content
|
|
77
|
+
if (roRef.current) {
|
|
33
78
|
try {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// Wrap the code in a function that temporarily sets window.SHADOW_DOC
|
|
38
|
-
const runTablerScript = new Function(
|
|
39
|
-
"shadowRoot",
|
|
40
|
-
`
|
|
41
|
-
window.USE_FALLBACK_ANCHOR = true;
|
|
42
|
-
const originalShadowDoc = window.SHADOW_DOC;
|
|
43
|
-
window.SHADOW_DOC = shadowRoot;
|
|
44
|
-
${scriptText}
|
|
45
|
-
window.SHADOW_DOC = originalShadowDoc;
|
|
46
|
-
`
|
|
47
|
-
);
|
|
48
|
-
runTablerScript(shadowRoot);
|
|
49
|
-
} catch (err) {
|
|
50
|
-
console.error("Failed to load Tabler script:", err);
|
|
51
|
-
}
|
|
79
|
+
roRef.current.disconnect();
|
|
80
|
+
} catch {}
|
|
52
81
|
}
|
|
82
|
+
const ro = new win.ResizeObserver(() => {
|
|
83
|
+
const h =
|
|
84
|
+
doc.documentElement.scrollHeight ||
|
|
85
|
+
doc.body.scrollHeight ||
|
|
86
|
+
app?.scrollHeight ||
|
|
87
|
+
0;
|
|
88
|
+
// iframe.style.height = `${Math.ceil(h) + 1}px`;
|
|
89
|
+
});
|
|
90
|
+
roRef.current = ro;
|
|
91
|
+
ro.observe(doc.documentElement);
|
|
92
|
+
|
|
93
|
+
// Initial sizing tick
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
const h =
|
|
96
|
+
doc.documentElement.scrollHeight ||
|
|
97
|
+
doc.body.scrollHeight ||
|
|
98
|
+
app?.scrollHeight ||
|
|
99
|
+
0;
|
|
100
|
+
iframe.style.height = `${Math.ceil(h) + 1}px`;
|
|
101
|
+
}, 0);
|
|
53
102
|
};
|
|
54
103
|
|
|
55
|
-
|
|
56
|
-
|
|
104
|
+
iframe.addEventListener("load", onLoad);
|
|
105
|
+
return () => {
|
|
106
|
+
iframe.removeEventListener("load", onLoad);
|
|
107
|
+
if (roRef.current) {
|
|
108
|
+
try {
|
|
109
|
+
roRef.current.disconnect();
|
|
110
|
+
} catch {}
|
|
111
|
+
roRef.current = null;
|
|
112
|
+
}
|
|
113
|
+
setMountNode(null);
|
|
114
|
+
};
|
|
115
|
+
}, [srcDoc, jsUrl, theme]);
|
|
116
|
+
|
|
117
|
+
// Keep theme in sync if it changes
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
const doc = iframeRef.current?.contentDocument;
|
|
120
|
+
if (doc) {
|
|
121
|
+
doc.documentElement.setAttribute(
|
|
122
|
+
"data-bs-theme",
|
|
123
|
+
theme === "dark" ? "dark" : "light"
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}, [theme]);
|
|
57
127
|
|
|
58
128
|
return (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
129
|
+
<>
|
|
130
|
+
<iframe
|
|
131
|
+
ref={iframeRef}
|
|
132
|
+
className={className}
|
|
133
|
+
style={{ width: "100%", border: 0, display: "block", ...(style || {}) }}
|
|
134
|
+
// Add sandbox if you want stricter isolation; e.g.:
|
|
135
|
+
// sandbox="allow-scripts allow-same-origin"
|
|
136
|
+
/>
|
|
137
|
+
{mountNode ? ReactDOM.createPortal(children, mountNode) : null}
|
|
138
|
+
</>
|
|
62
139
|
);
|
|
63
140
|
};
|
|
@@ -86,5 +86,93 @@
|
|
|
86
86
|
"district": "3rd"
|
|
87
87
|
},
|
|
88
88
|
"website": "https://susielee.house.gov"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"name": "Ashley Hinson",
|
|
92
|
+
"email": "ashley.hinson@mail.house.gov",
|
|
93
|
+
"party": "republican",
|
|
94
|
+
"region": {
|
|
95
|
+
"state": "Iowa",
|
|
96
|
+
"abbr": "IA",
|
|
97
|
+
"district": "2nd"
|
|
98
|
+
},
|
|
99
|
+
"website": "https://hinson.house.gov"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"name": "Ilhan Omar",
|
|
103
|
+
"email": "ilhan.omar@mail.house.gov",
|
|
104
|
+
"party": "democrat",
|
|
105
|
+
"region": {
|
|
106
|
+
"state": "Minnesota",
|
|
107
|
+
"abbr": "MN",
|
|
108
|
+
"district": "5th"
|
|
109
|
+
},
|
|
110
|
+
"website": "https://omar.house.gov"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"name": "Nancy Mace",
|
|
114
|
+
"email": "nancy.mace@mail.house.gov",
|
|
115
|
+
"party": "republican",
|
|
116
|
+
"region": {
|
|
117
|
+
"state": "South Carolina",
|
|
118
|
+
"abbr": "SC",
|
|
119
|
+
"district": "1st"
|
|
120
|
+
},
|
|
121
|
+
"website": "https://mace.house.gov"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"name": "Maxwell Frost",
|
|
125
|
+
"email": "maxwell.frost@mail.house.gov",
|
|
126
|
+
"party": "democrat",
|
|
127
|
+
"region": {
|
|
128
|
+
"state": "Florida",
|
|
129
|
+
"abbr": "FL",
|
|
130
|
+
"district": "10th"
|
|
131
|
+
},
|
|
132
|
+
"website": "https://frost.house.gov"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"name": "Dan Crenshaw",
|
|
136
|
+
"email": "dan.crenshaw@mail.house.gov",
|
|
137
|
+
"party": "republican",
|
|
138
|
+
"region": {
|
|
139
|
+
"state": "Texas",
|
|
140
|
+
"abbr": "TX",
|
|
141
|
+
"district": "2nd"
|
|
142
|
+
},
|
|
143
|
+
"website": "https://crenshaw.house.gov"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"name": "Ayanna Pressley",
|
|
147
|
+
"email": "ayanna.pressley@mail.house.gov",
|
|
148
|
+
"party": "democrat",
|
|
149
|
+
"region": {
|
|
150
|
+
"state": "Massachusetts",
|
|
151
|
+
"abbr": "MA",
|
|
152
|
+
"district": "7th"
|
|
153
|
+
},
|
|
154
|
+
"website": "https://pressley.house.gov"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"name": "Lauren Boebert",
|
|
158
|
+
"email": "lauren.boebert@mail.house.gov",
|
|
159
|
+
"party": "republican",
|
|
160
|
+
"region": {
|
|
161
|
+
"state": "Colorado",
|
|
162
|
+
"abbr": "CO",
|
|
163
|
+
"district": "3rd"
|
|
164
|
+
},
|
|
165
|
+
"website": "https://boebert.house.gov"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"name": "Ruben Gallego",
|
|
169
|
+
"email": "ruben.gallego@mail.house.gov",
|
|
170
|
+
"party": "democrat",
|
|
171
|
+
"region": {
|
|
172
|
+
"state": "Arizona",
|
|
173
|
+
"abbr": "AZ",
|
|
174
|
+
"district": "3rd"
|
|
175
|
+
},
|
|
176
|
+
"website": "https://gallego.house.gov"
|
|
89
177
|
}
|
|
90
178
|
]
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Tables v2
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
import { Excerpt } from "../../components/Excerpt.jsx";
|
|
6
|
+
import { TableV2 } from "../../components/LoadableTabler.jsx";
|
|
7
|
+
import congressPeople from "../../data/congressPeople.json";
|
|
8
|
+
|
|
9
|
+
Tables v2 is a new data table built on TanStack Table v8 with Tabler styling. It is fully controlled: you pass the current page of `data`, the total row count, and the active `sorting`. This design fits server-side pagination and ordering out of the box.
|
|
10
|
+
|
|
11
|
+
> Note: live previews on this page may require a package version that exports `TableV2`. If you don’t see live examples, the code snippets still illustrate usage.
|
|
12
|
+
|
|
13
|
+
## Signature
|
|
14
|
+
|
|
15
|
+
```jsx
|
|
16
|
+
import { TableV2 } from "tabler-react-2";
|
|
17
|
+
|
|
18
|
+
<TableV2 {...props} />;
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Core props
|
|
22
|
+
|
|
23
|
+
| Prop | Required | Type | Description |
|
|
24
|
+
| ---------------------- | -------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
|
|
25
|
+
| `columns` | Yes | TanStack `ColumnDef[]` | Column definitions using `accessorKey`, `header`, and optional `cell` renderers. |
|
|
26
|
+
| `data` | Yes | `any[]` | Current page rows only. |
|
|
27
|
+
| `totalRows` | Yes | `number` | Total available row count (for page count and range text). |
|
|
28
|
+
| `page` | Yes | `number` | Current page (1-based). |
|
|
29
|
+
| `size` | Yes | `number` | Current page size. |
|
|
30
|
+
| `onPageChange` | Yes | `(page:number) => void` | Called with next 1-based page. |
|
|
31
|
+
| `onSizeChange` | Yes | `(size:number) => void` | Called with next page size. Typically also reset page to 1. |
|
|
32
|
+
| `sorting` | Yes | `{ id:string, desc?:boolean }[]` | Active ordering (TanStack format). Empty array for “no sort”. |
|
|
33
|
+
| `onSortingChange` | Yes | `(next) => void` | Called when the user clicks a sortable header. |
|
|
34
|
+
| `loading` | No | `boolean` | Shows a small spinner and disables pager while loading. |
|
|
35
|
+
| `headerSticky` | No | `boolean` | Makes the header stick to the top of the card. |
|
|
36
|
+
| `emptyState` | No | `string \| () => ReactNode` | What to render when there are no rows. |
|
|
37
|
+
| `getRowId` | No | `(row) => string` | Supply when your rows need a stable custom ID. |
|
|
38
|
+
| `rowSelection` | No | `Record<string, boolean>` | Controlled selection map. |
|
|
39
|
+
| `onRowSelectionChange` | No | `(updater) => void` | Controlled selection callback. |
|
|
40
|
+
| `renderToolbarLeft` | No | `({ page,size,totalRows,sorting }) => ReactNode` | Custom left content in the card header. |
|
|
41
|
+
| `renderToolbarRight` | No | same | Custom right content in the card header. |
|
|
42
|
+
|
|
43
|
+
## Basic usage (client or server data)
|
|
44
|
+
|
|
45
|
+
The table is controlled; keep `page`, `size`, and `sorting` in your component state. The example below uses in-memory data, but the same shape works with server APIs.
|
|
46
|
+
|
|
47
|
+
```jsx
|
|
48
|
+
import { useMemo, useState } from "react";
|
|
49
|
+
import { TableV2 } from "tabler-react-2";
|
|
50
|
+
|
|
51
|
+
const allRows = congressPeople; // e.g., from your API
|
|
52
|
+
|
|
53
|
+
export default function Demo() {
|
|
54
|
+
const [page, setPage] = useState(1);
|
|
55
|
+
const [size, setSize] = useState(5);
|
|
56
|
+
const [sorting, setSorting] = useState([]); // [{ id:'name', desc:false }]
|
|
57
|
+
|
|
58
|
+
const columns = useMemo(
|
|
59
|
+
() => [
|
|
60
|
+
{ accessorKey: "name", header: "Name" },
|
|
61
|
+
{ accessorKey: "party", header: "Party" },
|
|
62
|
+
{ accessorKey: "region.state", header: "State" },
|
|
63
|
+
{
|
|
64
|
+
accessorKey: "email",
|
|
65
|
+
header: "Email",
|
|
66
|
+
cell: ({ getValue }) => (
|
|
67
|
+
<a className="text-reset" href={`mailto:${getValue()}`}>
|
|
68
|
+
{getValue()}
|
|
69
|
+
</a>
|
|
70
|
+
),
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
[]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// client-side slice just for the demo
|
|
77
|
+
const ordered = useMemo(() => {
|
|
78
|
+
if (!sorting.length) return allRows;
|
|
79
|
+
const { id, desc } = sorting[0];
|
|
80
|
+
const val = (row) => id.split(".").reduce((a, k) => a?.[k], row);
|
|
81
|
+
const cmp = (a, b) => (a === b ? 0 : a > b ? 1 : -1);
|
|
82
|
+
const sorted = [...allRows].sort((a, b) => cmp(val(a), val(b)));
|
|
83
|
+
return desc ? sorted.reverse() : sorted;
|
|
84
|
+
}, [sorting]);
|
|
85
|
+
|
|
86
|
+
const start = (page - 1) * size;
|
|
87
|
+
const pageData = ordered.slice(start, start + size);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<TableV2
|
|
91
|
+
columns={columns}
|
|
92
|
+
data={pageData}
|
|
93
|
+
totalRows={allRows.length}
|
|
94
|
+
page={page}
|
|
95
|
+
size={size}
|
|
96
|
+
onPageChange={setPage}
|
|
97
|
+
onSizeChange={(n) => {
|
|
98
|
+
setPage(1);
|
|
99
|
+
setSize(n);
|
|
100
|
+
}}
|
|
101
|
+
sorting={sorting}
|
|
102
|
+
onSortingChange={(next) => {
|
|
103
|
+
setPage(1);
|
|
104
|
+
setSorting(next);
|
|
105
|
+
}}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
<Excerpt>
|
|
112
|
+
{(() => {
|
|
113
|
+
const React = require("react");
|
|
114
|
+
const { useMemo, useState } = React;
|
|
115
|
+
|
|
116
|
+
// Local, fully functional demo state (client-side slice)
|
|
117
|
+
const allRows = congressPeople;
|
|
118
|
+
const [page, setPage] = useState(1);
|
|
119
|
+
const [size, setSize] = useState(10);
|
|
120
|
+
const [sorting, setSorting] = useState([]); // [{ id:'name', desc:false }]
|
|
121
|
+
|
|
122
|
+
const ordered = useMemo(() => {
|
|
123
|
+
if (!sorting.length) return allRows;
|
|
124
|
+
const { id, desc } = sorting[0];
|
|
125
|
+
const val = (row) => id.split(".").reduce((a, k) => a?.[k], row);
|
|
126
|
+
const cmp = (a, b) => (a === b ? 0 : a > b ? 1 : -1);
|
|
127
|
+
const sorted = [...allRows].sort((a, b) => cmp(val(a), val(b)));
|
|
128
|
+
return desc ? sorted.reverse() : sorted;
|
|
129
|
+
}, [sorting]);
|
|
130
|
+
|
|
131
|
+
const start = (page - 1) * size;
|
|
132
|
+
const pageData = ordered.slice(start, start + size);
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<TableV2
|
|
136
|
+
columns={[
|
|
137
|
+
{ accessorKey: "name", header: "Name" },
|
|
138
|
+
{ accessorKey: "party", header: "Party" },
|
|
139
|
+
{ accessorKey: "region.state", header: "State" },
|
|
140
|
+
{
|
|
141
|
+
accessorKey: "email",
|
|
142
|
+
header: "Email",
|
|
143
|
+
cell: ({ getValue }) => (
|
|
144
|
+
<a className="text-reset" href={`mailto:${getValue()}`}>
|
|
145
|
+
{getValue()}
|
|
146
|
+
</a>
|
|
147
|
+
),
|
|
148
|
+
},
|
|
149
|
+
]}
|
|
150
|
+
data={pageData}
|
|
151
|
+
totalRows={allRows.length}
|
|
152
|
+
page={page}
|
|
153
|
+
size={size}
|
|
154
|
+
onPageChange={setPage}
|
|
155
|
+
onSizeChange={(n) => {
|
|
156
|
+
setPage(1);
|
|
157
|
+
setSize(n);
|
|
158
|
+
}}
|
|
159
|
+
sorting={sorting}
|
|
160
|
+
onSortingChange={(next) => {
|
|
161
|
+
setPage(1);
|
|
162
|
+
setSorting(next);
|
|
163
|
+
}}
|
|
164
|
+
loading={false}
|
|
165
|
+
/>
|
|
166
|
+
);
|
|
167
|
+
})()}
|
|
168
|
+
|
|
169
|
+
</Excerpt>
|
|
170
|
+
|
|
171
|
+
## Server-side workflow
|
|
172
|
+
|
|
173
|
+
When fetching from an API, keep the same controlled state, but fetch rows for the current `page`, `size`, and `sorting`.
|
|
174
|
+
|
|
175
|
+
```jsx
|
|
176
|
+
const [page, setPage] = useState(1);
|
|
177
|
+
const [size, setSize] = useState(25);
|
|
178
|
+
const [sorting, setSorting] = useState([]);
|
|
179
|
+
const [rows, setRows] = useState([]);
|
|
180
|
+
const [total, setTotal] = useState(0);
|
|
181
|
+
const [loading, setLoading] = useState(false);
|
|
182
|
+
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
let isMounted = true;
|
|
185
|
+
(async () => {
|
|
186
|
+
setLoading(true);
|
|
187
|
+
try {
|
|
188
|
+
const res = await fetchPeople({ page, size, sorting });
|
|
189
|
+
if (!isMounted) return;
|
|
190
|
+
setRows(res.items);
|
|
191
|
+
setTotal(res.total);
|
|
192
|
+
} finally {
|
|
193
|
+
setLoading(false);
|
|
194
|
+
}
|
|
195
|
+
})();
|
|
196
|
+
return () => {
|
|
197
|
+
isMounted = false;
|
|
198
|
+
};
|
|
199
|
+
}, [page, size, sorting]);
|
|
200
|
+
|
|
201
|
+
<TableV2
|
|
202
|
+
columns={columns}
|
|
203
|
+
data={rows}
|
|
204
|
+
totalRows={total}
|
|
205
|
+
page={page}
|
|
206
|
+
size={size}
|
|
207
|
+
onPageChange={setPage}
|
|
208
|
+
onSizeChange={(n) => {
|
|
209
|
+
setPage(1);
|
|
210
|
+
setSize(n);
|
|
211
|
+
}}
|
|
212
|
+
sorting={sorting}
|
|
213
|
+
onSortingChange={(next) => {
|
|
214
|
+
setPage(1);
|
|
215
|
+
setSorting(next);
|
|
216
|
+
}}
|
|
217
|
+
loading={loading}
|
|
218
|
+
/>;
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Live async demo (simulated API)
|
|
222
|
+
|
|
223
|
+
The preview below simulates a server by sorting and slicing in a faux `fetch` with a 1s delay.
|
|
224
|
+
|
|
225
|
+
<Excerpt>
|
|
226
|
+
{(() => {
|
|
227
|
+
const React = require("react");
|
|
228
|
+
const { useEffect, useMemo, useState } = React;
|
|
229
|
+
|
|
230
|
+
// Simulated API that delays 1s and then returns sorted, paged rows
|
|
231
|
+
const fetchPeople = ({ page, size, sorting }) =>
|
|
232
|
+
new Promise((resolve) => {
|
|
233
|
+
setTimeout(() => {
|
|
234
|
+
const all = congressPeople;
|
|
235
|
+
const total = all.length;
|
|
236
|
+
|
|
237
|
+
// server-side order
|
|
238
|
+
let ordered = all;
|
|
239
|
+
if (sorting?.length) {
|
|
240
|
+
const { id, desc } = sorting[0];
|
|
241
|
+
const val = (row) => id.split(".").reduce((a, k) => a?.[k], row);
|
|
242
|
+
const cmp = (a, b) => (a === b ? 0 : a > b ? 1 : -1);
|
|
243
|
+
const sorted = [...all].sort((a, b) => cmp(val(a), val(b)));
|
|
244
|
+
ordered = desc ? sorted.reverse() : sorted;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// server-side page
|
|
248
|
+
const start = (page - 1) * size;
|
|
249
|
+
const items = ordered.slice(start, start + size);
|
|
250
|
+
resolve({ items, total });
|
|
251
|
+
}, 1000);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const columns = useMemo(
|
|
255
|
+
() => [
|
|
256
|
+
{ accessorKey: "name", header: "Name" },
|
|
257
|
+
{ accessorKey: "party", header: "Party" },
|
|
258
|
+
{ accessorKey: "region.state", header: "State" },
|
|
259
|
+
{
|
|
260
|
+
accessorKey: "email",
|
|
261
|
+
header: "Email",
|
|
262
|
+
cell: ({ getValue }) => (
|
|
263
|
+
<a className="text-reset" href={`mailto:${getValue()}`}>
|
|
264
|
+
{getValue()}
|
|
265
|
+
</a>
|
|
266
|
+
),
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
[]
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
const [page, setPage] = useState(1);
|
|
273
|
+
const [size, setSize] = useState(10);
|
|
274
|
+
const [sorting, setSorting] = useState([]);
|
|
275
|
+
const [rows, setRows] = useState([]);
|
|
276
|
+
const [total, setTotal] = useState(0);
|
|
277
|
+
const [loading, setLoading] = useState(false);
|
|
278
|
+
|
|
279
|
+
useEffect(() => {
|
|
280
|
+
let isMounted = true;
|
|
281
|
+
setLoading(true);
|
|
282
|
+
fetchPeople({ page, size, sorting }).then((res) => {
|
|
283
|
+
if (!isMounted) return;
|
|
284
|
+
setRows(res.items);
|
|
285
|
+
setTotal(res.total);
|
|
286
|
+
setLoading(false);
|
|
287
|
+
});
|
|
288
|
+
return () => {
|
|
289
|
+
isMounted = false;
|
|
290
|
+
};
|
|
291
|
+
}, [page, size, sorting]);
|
|
292
|
+
|
|
293
|
+
return (
|
|
294
|
+
<TableV2
|
|
295
|
+
columns={columns}
|
|
296
|
+
data={rows}
|
|
297
|
+
totalRows={total}
|
|
298
|
+
page={page}
|
|
299
|
+
size={size}
|
|
300
|
+
onPageChange={setPage}
|
|
301
|
+
onSizeChange={(n) => {
|
|
302
|
+
setPage(1);
|
|
303
|
+
setSize(n);
|
|
304
|
+
}}
|
|
305
|
+
sorting={sorting}
|
|
306
|
+
onSortingChange={(next) => {
|
|
307
|
+
setPage(1);
|
|
308
|
+
setSorting(next);
|
|
309
|
+
}}
|
|
310
|
+
loading={loading}
|
|
311
|
+
/>
|
|
312
|
+
);
|
|
313
|
+
})()}
|
|
314
|
+
|
|
315
|
+
</Excerpt>
|
|
316
|
+
|
|
317
|
+
## Extras
|
|
318
|
+
|
|
319
|
+
- Sticky header: pass `headerSticky` to keep headers visible while scrolling.
|
|
320
|
+
- Toolbars: use `renderToolbarLeft`/`renderToolbarRight` to add filters or actions.
|
|
321
|
+
- Row selection: control with `rowSelection` and `onRowSelectionChange`.
|
|
322
|
+
|
|
323
|
+
### Sticky header example
|
|
324
|
+
|
|
325
|
+
```jsx
|
|
326
|
+
<TableV2 {...commonProps} headerSticky />
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Toolbar example
|
|
330
|
+
|
|
331
|
+
```jsx
|
|
332
|
+
<TableV2
|
|
333
|
+
{...commonProps}
|
|
334
|
+
renderToolbarLeft={() => (
|
|
335
|
+
<input className="form-control form-control-sm" placeholder="Search" />
|
|
336
|
+
)}
|
|
337
|
+
renderToolbarRight={() => (
|
|
338
|
+
<button className="btn btn-sm btn-primary">Add</button>
|
|
339
|
+
)}
|
|
340
|
+
/>
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Row selection (minimal)
|
|
344
|
+
|
|
345
|
+
```jsx
|
|
346
|
+
const [rowSelection, setRowSelection] = useState({});
|
|
347
|
+
|
|
348
|
+
<TableV2
|
|
349
|
+
{...commonProps}
|
|
350
|
+
rowSelection={rowSelection}
|
|
351
|
+
onRowSelectionChange={setRowSelection}
|
|
352
|
+
columns={[
|
|
353
|
+
{
|
|
354
|
+
id: "select",
|
|
355
|
+
header: () => null,
|
|
356
|
+
cell: ({ row }) => (
|
|
357
|
+
<input
|
|
358
|
+
type="checkbox"
|
|
359
|
+
checked={row.getIsSelected()}
|
|
360
|
+
onChange={row.getToggleSelectedHandler()}
|
|
361
|
+
/>
|
|
362
|
+
),
|
|
363
|
+
},
|
|
364
|
+
...columns,
|
|
365
|
+
]}
|
|
366
|
+
/>;
|
|
367
|
+
```
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tabler-react-2",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.154",
|
|
4
4
|
"description": "A react implementation of Tabler ui",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "cd demo && yarn start",
|
|
8
8
|
"build:css": "postcss src/**/*.css --dir dist",
|
|
9
|
-
"build": "babel src --out-dir dist && npm run build:css"
|
|
9
|
+
"build": "babel src --out-dir dist && npm run build:css",
|
|
10
|
+
"docs:vanilla": "yarn --cwd docs start",
|
|
11
|
+
"docs": "cpulimit -l 20 -- yarn --cwd docs start"
|
|
10
12
|
},
|
|
11
13
|
"author": "Jack Crane",
|
|
12
14
|
"license": "MIT",
|
|
@@ -15,6 +17,8 @@
|
|
|
15
17
|
"@babel/core": "^7.24.8",
|
|
16
18
|
"@babel/preset-env": "^7.24.8",
|
|
17
19
|
"@babel/preset-react": "^7.24.7",
|
|
20
|
+
"@emotion/react": "^11.14.0",
|
|
21
|
+
"@tanstack/react-table": "^8.21.3",
|
|
18
22
|
"classnames": "^2.5.1",
|
|
19
23
|
"prop-types": "^15.8.1",
|
|
20
24
|
"react": "^18.3.1",
|