@rowakit/table 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/README.md +14 -11
- package/dist/index.cjs +427 -77
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +428 -78
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/styles/table.css +56 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState, useRef, useEffect } from 'react';
|
|
2
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
// src/column-helpers.ts
|
|
5
5
|
function text(field, options) {
|
|
@@ -118,6 +118,158 @@ function getRowKey(row, rowKey) {
|
|
|
118
118
|
function getHeaderLabel(column) {
|
|
119
119
|
return column.header ?? column.id;
|
|
120
120
|
}
|
|
121
|
+
function validateViewName(name) {
|
|
122
|
+
const trimmed = name.trim();
|
|
123
|
+
if (trimmed.length === 0) {
|
|
124
|
+
return { valid: false, error: "Name cannot be empty" };
|
|
125
|
+
}
|
|
126
|
+
if (trimmed.length > 40) {
|
|
127
|
+
return { valid: false, error: "Name cannot exceed 40 characters" };
|
|
128
|
+
}
|
|
129
|
+
const invalidChars = /[/\\?%*:|"<>\x00-\x1f\x7f]/;
|
|
130
|
+
if (invalidChars.test(trimmed)) {
|
|
131
|
+
return { valid: false, error: "Name contains invalid characters" };
|
|
132
|
+
}
|
|
133
|
+
return { valid: true };
|
|
134
|
+
}
|
|
135
|
+
function getSavedViewsIndex() {
|
|
136
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
const indexStr = localStorage.getItem("rowakit-views-index");
|
|
141
|
+
if (indexStr) {
|
|
142
|
+
const index = JSON.parse(indexStr);
|
|
143
|
+
if (Array.isArray(index)) {
|
|
144
|
+
return index;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
}
|
|
149
|
+
const rebuilt = [];
|
|
150
|
+
try {
|
|
151
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
152
|
+
const key = localStorage.key(i);
|
|
153
|
+
if (key?.startsWith("rowakit-view-")) {
|
|
154
|
+
const name = key.substring("rowakit-view-".length);
|
|
155
|
+
rebuilt.push({
|
|
156
|
+
name,
|
|
157
|
+
updatedAt: Date.now()
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
163
|
+
return rebuilt;
|
|
164
|
+
}
|
|
165
|
+
function setSavedViewsIndex(index) {
|
|
166
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
localStorage.setItem("rowakit-views-index", JSON.stringify(index));
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function loadSavedViewsFromStorage() {
|
|
175
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
const index = getSavedViewsIndex();
|
|
179
|
+
const views = [];
|
|
180
|
+
for (const entry of index) {
|
|
181
|
+
try {
|
|
182
|
+
const viewStr = localStorage.getItem(`rowakit-view-${entry.name}`);
|
|
183
|
+
if (viewStr) {
|
|
184
|
+
const state = JSON.parse(viewStr);
|
|
185
|
+
views.push({
|
|
186
|
+
name: entry.name,
|
|
187
|
+
state
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
} catch {
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return views;
|
|
194
|
+
}
|
|
195
|
+
function parseUrlState(params, defaultPageSize, pageSizeOptions) {
|
|
196
|
+
const pageStr = params.get("page");
|
|
197
|
+
let page = 1;
|
|
198
|
+
if (pageStr) {
|
|
199
|
+
const parsed = parseInt(pageStr, 10);
|
|
200
|
+
page = !isNaN(parsed) && parsed >= 1 ? parsed : 1;
|
|
201
|
+
}
|
|
202
|
+
const pageSizeStr = params.get("pageSize");
|
|
203
|
+
let pageSize = defaultPageSize;
|
|
204
|
+
if (pageSizeStr) {
|
|
205
|
+
const parsed = parseInt(pageSizeStr, 10);
|
|
206
|
+
if (!isNaN(parsed) && parsed >= 1) {
|
|
207
|
+
if (pageSizeOptions && pageSizeOptions.length > 0) {
|
|
208
|
+
pageSize = pageSizeOptions.includes(parsed) ? parsed : defaultPageSize;
|
|
209
|
+
} else {
|
|
210
|
+
pageSize = parsed;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const result = { page, pageSize };
|
|
215
|
+
const sortField = params.get("sortField");
|
|
216
|
+
const sortDir = params.get("sortDirection");
|
|
217
|
+
if (sortField && (sortDir === "asc" || sortDir === "desc")) {
|
|
218
|
+
result.sort = { field: sortField, direction: sortDir };
|
|
219
|
+
}
|
|
220
|
+
const filtersStr = params.get("filters");
|
|
221
|
+
if (filtersStr) {
|
|
222
|
+
try {
|
|
223
|
+
const parsed = JSON.parse(filtersStr);
|
|
224
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
225
|
+
result.filters = parsed;
|
|
226
|
+
}
|
|
227
|
+
} catch {
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const widthsStr = params.get("columnWidths");
|
|
231
|
+
if (widthsStr) {
|
|
232
|
+
try {
|
|
233
|
+
const parsed = JSON.parse(widthsStr);
|
|
234
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
235
|
+
const widths = {};
|
|
236
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
237
|
+
if (typeof value === "number" && value > 0) {
|
|
238
|
+
widths[key] = value;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (Object.keys(widths).length > 0) {
|
|
242
|
+
result.columnWidths = widths;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
} catch {
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
function serializeUrlState(query, filters, columnWidths, defaultPageSize, enableColumnResizing) {
|
|
251
|
+
const params = new URLSearchParams();
|
|
252
|
+
params.set("page", String(query.page));
|
|
253
|
+
if (query.pageSize !== defaultPageSize) {
|
|
254
|
+
params.set("pageSize", String(query.pageSize));
|
|
255
|
+
}
|
|
256
|
+
if (query.sort) {
|
|
257
|
+
params.set("sortField", query.sort.field);
|
|
258
|
+
params.set("sortDirection", query.sort.direction);
|
|
259
|
+
}
|
|
260
|
+
if (filters && Object.keys(filters).length > 0) {
|
|
261
|
+
const nonEmptyFilters = Object.fromEntries(
|
|
262
|
+
Object.entries(filters).filter(([, v]) => v !== void 0)
|
|
263
|
+
);
|
|
264
|
+
if (Object.keys(nonEmptyFilters).length > 0) {
|
|
265
|
+
params.set("filters", JSON.stringify(nonEmptyFilters));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (enableColumnResizing && Object.keys(columnWidths).length > 0) {
|
|
269
|
+
params.set("columnWidths", JSON.stringify(columnWidths));
|
|
270
|
+
}
|
|
271
|
+
return params.toString();
|
|
272
|
+
}
|
|
121
273
|
function renderCell(column, row, isLoading, setConfirmState) {
|
|
122
274
|
switch (column.kind) {
|
|
123
275
|
case "text": {
|
|
@@ -234,60 +386,96 @@ function RowaKitTable({
|
|
|
234
386
|
const resizeRafRef = useRef(null);
|
|
235
387
|
const resizePendingRef = useRef(null);
|
|
236
388
|
const tableRef = useRef(null);
|
|
389
|
+
const isResizingRef = useRef(false);
|
|
390
|
+
const lastResizeEndTsRef = useRef(0);
|
|
391
|
+
const resizingColIdRef = useRef(null);
|
|
392
|
+
const didHydrateUrlRef = useRef(false);
|
|
393
|
+
const didSkipInitialUrlSyncRef = useRef(false);
|
|
394
|
+
const urlSyncDebounceRef = useRef(null);
|
|
237
395
|
const [savedViews, setSavedViews] = useState([]);
|
|
396
|
+
const [showSaveViewForm, setShowSaveViewForm] = useState(false);
|
|
397
|
+
const [saveViewInput, setSaveViewInput] = useState("");
|
|
398
|
+
const [saveViewError, setSaveViewError] = useState("");
|
|
399
|
+
const [overwriteConfirmName, setOverwriteConfirmName] = useState(null);
|
|
400
|
+
useEffect(() => {
|
|
401
|
+
if (!enableSavedViews) return;
|
|
402
|
+
const views = loadSavedViewsFromStorage();
|
|
403
|
+
setSavedViews(views);
|
|
404
|
+
}, [enableSavedViews]);
|
|
238
405
|
const [confirmState, setConfirmState] = useState(null);
|
|
239
406
|
const requestIdRef = useRef(0);
|
|
240
407
|
useEffect(() => {
|
|
241
|
-
if (!syncToUrl)
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
params.set("pageSize", String(query.pageSize));
|
|
245
|
-
if (query.sort) {
|
|
246
|
-
params.set("sortField", query.sort.field);
|
|
247
|
-
params.set("sortDirection", query.sort.direction);
|
|
408
|
+
if (!syncToUrl) {
|
|
409
|
+
didSkipInitialUrlSyncRef.current = false;
|
|
410
|
+
return;
|
|
248
411
|
}
|
|
249
|
-
if (
|
|
250
|
-
|
|
412
|
+
if (!didSkipInitialUrlSyncRef.current) {
|
|
413
|
+
didSkipInitialUrlSyncRef.current = true;
|
|
414
|
+
return;
|
|
251
415
|
}
|
|
252
|
-
if (
|
|
253
|
-
|
|
416
|
+
if (urlSyncDebounceRef.current) {
|
|
417
|
+
clearTimeout(urlSyncDebounceRef.current);
|
|
418
|
+
urlSyncDebounceRef.current = null;
|
|
254
419
|
}
|
|
255
|
-
|
|
256
|
-
|
|
420
|
+
const urlStr = serializeUrlState(query, filters, columnWidths, defaultPageSize, enableColumnResizing);
|
|
421
|
+
const qs = urlStr ? `?${urlStr}` : "";
|
|
422
|
+
window.history.replaceState(null, "", `${window.location.pathname}${qs}${window.location.hash}`);
|
|
423
|
+
}, [query, filters, syncToUrl, enableColumnResizing, defaultPageSize, columnWidths]);
|
|
257
424
|
useEffect(() => {
|
|
258
|
-
if (!syncToUrl) return;
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const sortField = params.get("sortField");
|
|
263
|
-
const sortDirection = params.get("sortDirection");
|
|
264
|
-
const filtersStr = params.get("filters");
|
|
265
|
-
const columnWidthsStr = params.get("columnWidths");
|
|
266
|
-
const newQuery = {
|
|
267
|
-
page: Math.max(1, page),
|
|
268
|
-
pageSize: Math.max(1, pageSize)
|
|
269
|
-
};
|
|
270
|
-
if (sortField && sortDirection) {
|
|
271
|
-
newQuery.sort = { field: sortField, direction: sortDirection };
|
|
425
|
+
if (!syncToUrl || !enableColumnResizing) return;
|
|
426
|
+
if (!didSkipInitialUrlSyncRef.current) return;
|
|
427
|
+
if (urlSyncDebounceRef.current) {
|
|
428
|
+
clearTimeout(urlSyncDebounceRef.current);
|
|
272
429
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
430
|
+
urlSyncDebounceRef.current = setTimeout(() => {
|
|
431
|
+
const urlStr = serializeUrlState(query, filters, columnWidths, defaultPageSize, enableColumnResizing);
|
|
432
|
+
const qs = urlStr ? `?${urlStr}` : "";
|
|
433
|
+
window.history.replaceState(null, "", `${window.location.pathname}${qs}${window.location.hash}`);
|
|
434
|
+
urlSyncDebounceRef.current = null;
|
|
435
|
+
}, 150);
|
|
436
|
+
return () => {
|
|
437
|
+
if (urlSyncDebounceRef.current) {
|
|
438
|
+
clearTimeout(urlSyncDebounceRef.current);
|
|
439
|
+
urlSyncDebounceRef.current = null;
|
|
281
440
|
}
|
|
441
|
+
};
|
|
442
|
+
}, [columnWidths, syncToUrl, enableColumnResizing, query, filters, defaultPageSize]);
|
|
443
|
+
useEffect(() => {
|
|
444
|
+
if (!syncToUrl) {
|
|
445
|
+
didHydrateUrlRef.current = false;
|
|
446
|
+
return;
|
|
282
447
|
}
|
|
283
|
-
if (
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
448
|
+
if (didHydrateUrlRef.current) return;
|
|
449
|
+
didHydrateUrlRef.current = true;
|
|
450
|
+
const params = new URLSearchParams(window.location.search);
|
|
451
|
+
const parsed = parseUrlState(params, defaultPageSize, pageSizeOptions);
|
|
452
|
+
setQuery({
|
|
453
|
+
page: parsed.page,
|
|
454
|
+
pageSize: parsed.pageSize,
|
|
455
|
+
sort: parsed.sort,
|
|
456
|
+
filters: parsed.filters
|
|
457
|
+
});
|
|
458
|
+
if (parsed.filters) {
|
|
459
|
+
setFilters(parsed.filters);
|
|
460
|
+
}
|
|
461
|
+
if (parsed.columnWidths && enableColumnResizing) {
|
|
462
|
+
const clamped = {};
|
|
463
|
+
for (const [colId, rawWidth] of Object.entries(parsed.columnWidths)) {
|
|
464
|
+
const widthNum = typeof rawWidth === "number" ? rawWidth : Number(rawWidth);
|
|
465
|
+
if (!Number.isFinite(widthNum)) continue;
|
|
466
|
+
const colDef = columns.find((c) => c.id === colId);
|
|
467
|
+
if (!colDef) continue;
|
|
468
|
+
const minW = colDef.minWidth ?? 80;
|
|
469
|
+
const maxW = colDef.maxWidth;
|
|
470
|
+
let finalW = Math.max(minW, widthNum);
|
|
471
|
+
if (maxW != null) {
|
|
472
|
+
finalW = Math.min(finalW, maxW);
|
|
473
|
+
}
|
|
474
|
+
clamped[colId] = finalW;
|
|
287
475
|
}
|
|
476
|
+
setColumnWidths(clamped);
|
|
288
477
|
}
|
|
289
|
-
|
|
290
|
-
}, [syncToUrl, defaultPageSize, enableColumnResizing]);
|
|
478
|
+
}, [syncToUrl, defaultPageSize, enableColumnResizing, pageSizeOptions, columns]);
|
|
291
479
|
useEffect(() => {
|
|
292
480
|
if (!enableFilters) return;
|
|
293
481
|
const activeFilters = {};
|
|
@@ -390,15 +578,52 @@ function RowaKitTable({
|
|
|
390
578
|
if (maxWidth) {
|
|
391
579
|
finalWidth = Math.min(finalWidth, maxWidth);
|
|
392
580
|
}
|
|
581
|
+
if (columnWidths[columnId] === finalWidth) {
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
393
584
|
setColumnWidths((prev) => ({
|
|
394
585
|
...prev,
|
|
395
586
|
[columnId]: finalWidth
|
|
396
587
|
}));
|
|
397
588
|
};
|
|
589
|
+
const autoFitColumnWidth = (columnId) => {
|
|
590
|
+
const tableEl = tableRef.current;
|
|
591
|
+
if (!tableEl) return;
|
|
592
|
+
const th = tableEl.querySelector(`th[data-col-id="${columnId}"]`);
|
|
593
|
+
if (!th) return;
|
|
594
|
+
const tds = Array.from(
|
|
595
|
+
tableEl.querySelectorAll(`td[data-col-id="${columnId}"]`)
|
|
596
|
+
);
|
|
597
|
+
const headerW = th.scrollWidth;
|
|
598
|
+
const cellsMaxW = tds.reduce((max, td) => Math.max(max, td.scrollWidth), 0);
|
|
599
|
+
const padding = 24;
|
|
600
|
+
const raw = Math.max(headerW, cellsMaxW) + padding;
|
|
601
|
+
const colDef = columns.find((c) => c.id === columnId);
|
|
602
|
+
const minW = colDef?.minWidth ?? 80;
|
|
603
|
+
const maxW = colDef?.maxWidth ?? 600;
|
|
604
|
+
const finalW = Math.max(minW, Math.min(raw, maxW));
|
|
605
|
+
setColumnWidths((prev) => ({ ...prev, [columnId]: finalW }));
|
|
606
|
+
};
|
|
398
607
|
const startColumnResize = (e, columnId) => {
|
|
399
608
|
e.preventDefault();
|
|
609
|
+
e.stopPropagation();
|
|
610
|
+
if (e.detail === 2) {
|
|
611
|
+
autoFitColumnWidth(columnId);
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
if (e.pointerType === "mouse" && e.buttons !== 1) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
const target = e.currentTarget;
|
|
618
|
+
const pointerId = e.pointerId;
|
|
619
|
+
try {
|
|
620
|
+
target.setPointerCapture(pointerId);
|
|
621
|
+
} catch {
|
|
622
|
+
}
|
|
623
|
+
isResizingRef.current = true;
|
|
624
|
+
resizingColIdRef.current = columnId;
|
|
400
625
|
const startX = e.clientX;
|
|
401
|
-
const th =
|
|
626
|
+
const th = target.parentElement;
|
|
402
627
|
let startWidth = columnWidths[columnId] ?? th.offsetWidth;
|
|
403
628
|
const MIN_DRAG_WIDTH = 80;
|
|
404
629
|
if (startWidth < MIN_DRAG_WIDTH) {
|
|
@@ -410,33 +635,38 @@ function RowaKitTable({
|
|
|
410
635
|
}
|
|
411
636
|
}
|
|
412
637
|
document.body.classList.add("rowakit-resizing");
|
|
413
|
-
const
|
|
638
|
+
const handlePointerMove = (moveEvent) => {
|
|
414
639
|
const delta = moveEvent.clientX - startX;
|
|
415
640
|
const newWidth = startWidth + delta;
|
|
416
641
|
scheduleColumnWidthUpdate(columnId, newWidth);
|
|
417
642
|
};
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
643
|
+
const cleanupResize = () => {
|
|
644
|
+
target.removeEventListener("pointermove", handlePointerMove);
|
|
645
|
+
target.removeEventListener("pointerup", handlePointerUp);
|
|
646
|
+
target.removeEventListener("pointercancel", handlePointerCancel);
|
|
421
647
|
document.body.classList.remove("rowakit-resizing");
|
|
648
|
+
isResizingRef.current = false;
|
|
649
|
+
resizingColIdRef.current = null;
|
|
650
|
+
lastResizeEndTsRef.current = Date.now();
|
|
651
|
+
try {
|
|
652
|
+
target.releasePointerCapture(pointerId);
|
|
653
|
+
} catch {
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
const handlePointerUp = () => {
|
|
657
|
+
cleanupResize();
|
|
658
|
+
};
|
|
659
|
+
const handlePointerCancel = () => {
|
|
660
|
+
cleanupResize();
|
|
422
661
|
};
|
|
423
|
-
|
|
424
|
-
|
|
662
|
+
target.addEventListener("pointermove", handlePointerMove);
|
|
663
|
+
target.addEventListener("pointerup", handlePointerUp);
|
|
664
|
+
target.addEventListener("pointercancel", handlePointerCancel);
|
|
425
665
|
};
|
|
426
|
-
const handleColumnResizeDoubleClick = (columnId) => {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
if (!th) return;
|
|
431
|
-
const tds = Array.from(tableEl.querySelectorAll(`td[data-col-id="${columnId}"]`));
|
|
432
|
-
const headerW = th.scrollWidth;
|
|
433
|
-
const cellsMaxW = tds.reduce((max, td) => Math.max(max, td.scrollWidth), 0);
|
|
434
|
-
const padding = 24;
|
|
435
|
-
const raw = Math.max(headerW, cellsMaxW) + padding;
|
|
436
|
-
const minW = columns.find((c) => c.id === columnId)?.minWidth ?? 80;
|
|
437
|
-
const maxW = columns.find((c) => c.id === columnId)?.maxWidth ?? 600;
|
|
438
|
-
const finalW = Math.max(minW, Math.min(raw, maxW));
|
|
439
|
-
setColumnWidths((prev) => ({ ...prev, [columnId]: finalW }));
|
|
666
|
+
const handleColumnResizeDoubleClick = (e, columnId) => {
|
|
667
|
+
e.preventDefault();
|
|
668
|
+
e.stopPropagation();
|
|
669
|
+
autoFitColumnWidth(columnId);
|
|
440
670
|
};
|
|
441
671
|
const saveCurrentView = (name) => {
|
|
442
672
|
const viewState = {
|
|
@@ -453,6 +683,10 @@ function RowaKitTable({
|
|
|
453
683
|
if (typeof window !== "undefined" && window.localStorage) {
|
|
454
684
|
try {
|
|
455
685
|
localStorage.setItem(`rowakit-view-${name}`, JSON.stringify(viewState));
|
|
686
|
+
const index = getSavedViewsIndex();
|
|
687
|
+
const filtered = index.filter((v) => v.name !== name);
|
|
688
|
+
filtered.push({ name, updatedAt: Date.now() });
|
|
689
|
+
setSavedViewsIndex(filtered);
|
|
456
690
|
} catch {
|
|
457
691
|
}
|
|
458
692
|
}
|
|
@@ -477,6 +711,9 @@ function RowaKitTable({
|
|
|
477
711
|
if (typeof window !== "undefined" && window.localStorage) {
|
|
478
712
|
try {
|
|
479
713
|
localStorage.removeItem(`rowakit-view-${name}`);
|
|
714
|
+
const index = getSavedViewsIndex();
|
|
715
|
+
const filtered = index.filter((v) => v.name !== name);
|
|
716
|
+
setSavedViewsIndex(filtered);
|
|
480
717
|
} catch {
|
|
481
718
|
}
|
|
482
719
|
}
|
|
@@ -540,22 +777,127 @@ function RowaKitTable({
|
|
|
540
777
|
const canGoPrevious = query.page > 1 && !isLoading;
|
|
541
778
|
const canGoNext = query.page < totalPages && !isLoading;
|
|
542
779
|
const hasActiveFilters = enableFilters && Object.values(filters).some((v) => v !== void 0);
|
|
543
|
-
|
|
780
|
+
const containerClass = [
|
|
781
|
+
"rowakit-table",
|
|
782
|
+
enableColumnResizing ? "rowakit-layout-fixed" : "",
|
|
783
|
+
className
|
|
784
|
+
].filter(Boolean).join(" ");
|
|
785
|
+
return /* @__PURE__ */ jsxs("div", { className: containerClass, children: [
|
|
544
786
|
enableSavedViews && /* @__PURE__ */ jsxs("div", { className: "rowakit-saved-views-group", children: [
|
|
545
|
-
/* @__PURE__ */ jsx(
|
|
787
|
+
!showSaveViewForm ? /* @__PURE__ */ jsx(
|
|
546
788
|
"button",
|
|
547
789
|
{
|
|
548
790
|
onClick: () => {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
791
|
+
setShowSaveViewForm(true);
|
|
792
|
+
setSaveViewInput("");
|
|
793
|
+
setSaveViewError("");
|
|
794
|
+
setOverwriteConfirmName(null);
|
|
553
795
|
},
|
|
554
796
|
className: "rowakit-saved-view-button",
|
|
555
797
|
type: "button",
|
|
556
798
|
children: "Save View"
|
|
557
799
|
}
|
|
558
|
-
),
|
|
800
|
+
) : /* @__PURE__ */ jsx("div", { className: "rowakit-save-view-form", children: overwriteConfirmName ? /* @__PURE__ */ jsxs("div", { className: "rowakit-save-view-confirm", children: [
|
|
801
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
802
|
+
'View "',
|
|
803
|
+
overwriteConfirmName,
|
|
804
|
+
'" already exists. Overwrite?'
|
|
805
|
+
] }),
|
|
806
|
+
/* @__PURE__ */ jsx(
|
|
807
|
+
"button",
|
|
808
|
+
{
|
|
809
|
+
onClick: () => {
|
|
810
|
+
saveCurrentView(overwriteConfirmName);
|
|
811
|
+
setShowSaveViewForm(false);
|
|
812
|
+
setSaveViewInput("");
|
|
813
|
+
setSaveViewError("");
|
|
814
|
+
setOverwriteConfirmName(null);
|
|
815
|
+
},
|
|
816
|
+
className: "rowakit-saved-view-button",
|
|
817
|
+
type: "button",
|
|
818
|
+
children: "Overwrite"
|
|
819
|
+
}
|
|
820
|
+
),
|
|
821
|
+
/* @__PURE__ */ jsx(
|
|
822
|
+
"button",
|
|
823
|
+
{
|
|
824
|
+
onClick: () => {
|
|
825
|
+
setOverwriteConfirmName(null);
|
|
826
|
+
},
|
|
827
|
+
className: "rowakit-saved-view-button",
|
|
828
|
+
type: "button",
|
|
829
|
+
children: "Cancel"
|
|
830
|
+
}
|
|
831
|
+
)
|
|
832
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
833
|
+
/* @__PURE__ */ jsx(
|
|
834
|
+
"input",
|
|
835
|
+
{
|
|
836
|
+
type: "text",
|
|
837
|
+
value: saveViewInput,
|
|
838
|
+
onChange: (e) => {
|
|
839
|
+
setSaveViewInput(e.target.value);
|
|
840
|
+
setSaveViewError("");
|
|
841
|
+
},
|
|
842
|
+
onKeyDown: (e) => {
|
|
843
|
+
if (e.key === "Enter") {
|
|
844
|
+
const validation = validateViewName(saveViewInput);
|
|
845
|
+
if (!validation.valid) {
|
|
846
|
+
setSaveViewError(validation.error || "Invalid name");
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
if (savedViews.some((v) => v.name === saveViewInput.trim())) {
|
|
850
|
+
setOverwriteConfirmName(saveViewInput.trim());
|
|
851
|
+
} else {
|
|
852
|
+
saveCurrentView(saveViewInput.trim());
|
|
853
|
+
setShowSaveViewForm(false);
|
|
854
|
+
setSaveViewInput("");
|
|
855
|
+
setSaveViewError("");
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
placeholder: "Enter view name...",
|
|
860
|
+
className: "rowakit-save-view-input"
|
|
861
|
+
}
|
|
862
|
+
),
|
|
863
|
+
saveViewError && /* @__PURE__ */ jsx("div", { className: "rowakit-save-view-error", children: saveViewError }),
|
|
864
|
+
/* @__PURE__ */ jsx(
|
|
865
|
+
"button",
|
|
866
|
+
{
|
|
867
|
+
onClick: () => {
|
|
868
|
+
const validation = validateViewName(saveViewInput);
|
|
869
|
+
if (!validation.valid) {
|
|
870
|
+
setSaveViewError(validation.error || "Invalid name");
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
if (savedViews.some((v) => v.name === saveViewInput.trim())) {
|
|
874
|
+
setOverwriteConfirmName(saveViewInput.trim());
|
|
875
|
+
} else {
|
|
876
|
+
saveCurrentView(saveViewInput.trim());
|
|
877
|
+
setShowSaveViewForm(false);
|
|
878
|
+
setSaveViewInput("");
|
|
879
|
+
setSaveViewError("");
|
|
880
|
+
}
|
|
881
|
+
},
|
|
882
|
+
className: "rowakit-saved-view-button",
|
|
883
|
+
type: "button",
|
|
884
|
+
children: "Save"
|
|
885
|
+
}
|
|
886
|
+
),
|
|
887
|
+
/* @__PURE__ */ jsx(
|
|
888
|
+
"button",
|
|
889
|
+
{
|
|
890
|
+
onClick: () => {
|
|
891
|
+
setShowSaveViewForm(false);
|
|
892
|
+
setSaveViewInput("");
|
|
893
|
+
setSaveViewError("");
|
|
894
|
+
},
|
|
895
|
+
className: "rowakit-saved-view-button",
|
|
896
|
+
type: "button",
|
|
897
|
+
children: "Cancel"
|
|
898
|
+
}
|
|
899
|
+
)
|
|
900
|
+
] }) }),
|
|
559
901
|
savedViews.map((view) => /* @__PURE__ */ jsxs("div", { className: "rowakit-saved-view-item", children: [
|
|
560
902
|
/* @__PURE__ */ jsx(
|
|
561
903
|
"button",
|
|
@@ -607,7 +949,11 @@ function RowaKitTable({
|
|
|
607
949
|
"th",
|
|
608
950
|
{
|
|
609
951
|
"data-col-id": column.id,
|
|
610
|
-
onClick: isSortable ? () =>
|
|
952
|
+
onClick: isSortable ? () => {
|
|
953
|
+
if (isResizingRef.current) return;
|
|
954
|
+
if (Date.now() - lastResizeEndTsRef.current < 150) return;
|
|
955
|
+
handleSort(String(field));
|
|
956
|
+
} : void 0,
|
|
611
957
|
role: isSortable ? "button" : void 0,
|
|
612
958
|
tabIndex: isSortable ? 0 : void 0,
|
|
613
959
|
onKeyDown: isSortable ? (e) => {
|
|
@@ -618,11 +964,15 @@ function RowaKitTable({
|
|
|
618
964
|
} : void 0,
|
|
619
965
|
"aria-sort": isSortable && query.sort?.field === String(field) ? query.sort.direction === "asc" ? "ascending" : "descending" : void 0,
|
|
620
966
|
style: {
|
|
621
|
-
width: actualWidth ? `${actualWidth}px` : void 0,
|
|
967
|
+
width: actualWidth != null ? `${actualWidth}px` : void 0,
|
|
622
968
|
textAlign: column.align,
|
|
623
969
|
position: isResizable ? "relative" : void 0
|
|
624
970
|
},
|
|
625
|
-
className:
|
|
971
|
+
className: [
|
|
972
|
+
column.truncate ? "rowakit-cell-truncate" : "",
|
|
973
|
+
resizingColIdRef.current === column.id ? "resizing" : ""
|
|
974
|
+
// PRD-01
|
|
975
|
+
].filter(Boolean).join(" ") || void 0,
|
|
626
976
|
children: [
|
|
627
977
|
getHeaderLabel(column),
|
|
628
978
|
isSortable && getSortIndicator(String(field)),
|
|
@@ -630,8 +980,8 @@ function RowaKitTable({
|
|
|
630
980
|
"div",
|
|
631
981
|
{
|
|
632
982
|
className: "rowakit-column-resize-handle",
|
|
633
|
-
|
|
634
|
-
onDoubleClick: () => handleColumnResizeDoubleClick(column.id),
|
|
983
|
+
onPointerDown: (e) => startColumnResize(e, column.id),
|
|
984
|
+
onDoubleClick: (e) => handleColumnResizeDoubleClick(e, column.id),
|
|
635
985
|
title: "Drag to resize | Double-click to auto-fit content"
|
|
636
986
|
}
|
|
637
987
|
)
|
|
@@ -830,14 +1180,14 @@ function RowaKitTable({
|
|
|
830
1180
|
column.kind === "number" ? "rowakit-cell-number" : "",
|
|
831
1181
|
column.truncate ? "rowakit-cell-truncate" : ""
|
|
832
1182
|
].filter(Boolean).join(" ") || void 0;
|
|
833
|
-
const actualWidth = columnWidths[column.id];
|
|
1183
|
+
const actualWidth = columnWidths[column.id] ?? column.width;
|
|
834
1184
|
return /* @__PURE__ */ jsx(
|
|
835
1185
|
"td",
|
|
836
1186
|
{
|
|
837
1187
|
"data-col-id": column.id,
|
|
838
1188
|
className: cellClass,
|
|
839
1189
|
style: {
|
|
840
|
-
width: actualWidth ? `${actualWidth}px` : void 0,
|
|
1190
|
+
width: actualWidth != null ? `${actualWidth}px` : void 0,
|
|
841
1191
|
textAlign: column.align || (column.kind === "number" ? "right" : void 0)
|
|
842
1192
|
},
|
|
843
1193
|
children: renderCell(column, row, isLoading, setConfirmState)
|
|
@@ -942,7 +1292,7 @@ function RowaKitTable({
|
|
|
942
1292
|
var SmartTable = RowaKitTable;
|
|
943
1293
|
|
|
944
1294
|
// src/index.ts
|
|
945
|
-
var VERSION = "0.
|
|
1295
|
+
var VERSION = "0.4.0";
|
|
946
1296
|
|
|
947
1297
|
export { RowaKitTable, SmartTable, VERSION, col };
|
|
948
1298
|
//# sourceMappingURL=index.js.map
|