@rowakit/table 0.3.0 → 0.5.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 +137 -1414
- package/dist/index.cjs +1256 -428
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -4
- package/dist/index.d.ts +49 -4
- package/dist/index.js +1258 -430
- 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
|
-
import { useState, useRef, useEffect } from 'react';
|
|
2
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
1
|
+
import { useState, useRef, useEffect, useMemo } from 'react';
|
|
2
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
// src/column-helpers.ts
|
|
5
5
|
function text(field, options) {
|
|
@@ -103,6 +103,834 @@ var col = {
|
|
|
103
103
|
actions,
|
|
104
104
|
custom
|
|
105
105
|
};
|
|
106
|
+
function useColumnResizing(columns) {
|
|
107
|
+
const [columnWidths, setColumnWidths] = useState({});
|
|
108
|
+
const resizeRafRef = useRef(null);
|
|
109
|
+
const resizePendingRef = useRef(null);
|
|
110
|
+
const tableRef = useRef(null);
|
|
111
|
+
const isResizingRef = useRef(false);
|
|
112
|
+
const lastResizeEndTsRef = useRef(0);
|
|
113
|
+
const resizingColIdRef = useRef(null);
|
|
114
|
+
const scheduleColumnWidthUpdate = (colId, width) => {
|
|
115
|
+
resizePendingRef.current = { colId, width };
|
|
116
|
+
if (resizeRafRef.current != null) return;
|
|
117
|
+
resizeRafRef.current = requestAnimationFrame(() => {
|
|
118
|
+
resizeRafRef.current = null;
|
|
119
|
+
const pending = resizePendingRef.current;
|
|
120
|
+
if (!pending) return;
|
|
121
|
+
handleColumnResize(pending.colId, pending.width);
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
const handleColumnResize = (columnId, newWidth) => {
|
|
125
|
+
const minWidth = columns.find((c) => c.id === columnId)?.minWidth ?? 80;
|
|
126
|
+
const maxWidth = columns.find((c) => c.id === columnId)?.maxWidth;
|
|
127
|
+
let finalWidth = Math.max(minWidth, newWidth);
|
|
128
|
+
if (maxWidth) {
|
|
129
|
+
finalWidth = Math.min(finalWidth, maxWidth);
|
|
130
|
+
}
|
|
131
|
+
if (columnWidths[columnId] === finalWidth) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
setColumnWidths((prev) => ({
|
|
135
|
+
...prev,
|
|
136
|
+
[columnId]: finalWidth
|
|
137
|
+
}));
|
|
138
|
+
};
|
|
139
|
+
const autoFitColumnWidth = (columnId) => {
|
|
140
|
+
const tableEl = tableRef.current;
|
|
141
|
+
if (!tableEl) return;
|
|
142
|
+
const th = tableEl.querySelector(`th[data-col-id="${columnId}"]`);
|
|
143
|
+
if (!th) return;
|
|
144
|
+
const tds = Array.from(
|
|
145
|
+
tableEl.querySelectorAll(`td[data-col-id="${columnId}"]`)
|
|
146
|
+
);
|
|
147
|
+
const headerW = th.scrollWidth;
|
|
148
|
+
const cellsMaxW = tds.reduce((max, td) => Math.max(max, td.scrollWidth), 0);
|
|
149
|
+
const padding = 24;
|
|
150
|
+
const raw = Math.max(headerW, cellsMaxW) + padding;
|
|
151
|
+
const colDef = columns.find((c) => c.id === columnId);
|
|
152
|
+
const minW = colDef?.minWidth ?? 80;
|
|
153
|
+
const maxW = colDef?.maxWidth ?? 600;
|
|
154
|
+
const finalW = Math.max(minW, Math.min(raw, maxW));
|
|
155
|
+
setColumnWidths((prev) => ({ ...prev, [columnId]: finalW }));
|
|
156
|
+
};
|
|
157
|
+
const startColumnResize = (e, columnId) => {
|
|
158
|
+
e.preventDefault();
|
|
159
|
+
e.stopPropagation();
|
|
160
|
+
if (e.detail === 2) {
|
|
161
|
+
autoFitColumnWidth(columnId);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (e.pointerType === "mouse" && e.buttons !== 1) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const target = e.currentTarget;
|
|
168
|
+
const pointerId = e.pointerId;
|
|
169
|
+
try {
|
|
170
|
+
target.setPointerCapture(pointerId);
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
isResizingRef.current = true;
|
|
174
|
+
resizingColIdRef.current = columnId;
|
|
175
|
+
const startX = e.clientX;
|
|
176
|
+
const th = target.parentElement;
|
|
177
|
+
let startWidth = columnWidths[columnId] ?? th.offsetWidth;
|
|
178
|
+
const MIN_DRAG_WIDTH = 80;
|
|
179
|
+
if (startWidth < MIN_DRAG_WIDTH) {
|
|
180
|
+
const nextTh = th.nextElementSibling;
|
|
181
|
+
if (nextTh && nextTh.offsetWidth >= 50) {
|
|
182
|
+
startWidth = nextTh.offsetWidth;
|
|
183
|
+
} else {
|
|
184
|
+
startWidth = 100;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
document.body.classList.add("rowakit-resizing");
|
|
188
|
+
const handlePointerMove = (moveEvent) => {
|
|
189
|
+
const delta = moveEvent.clientX - startX;
|
|
190
|
+
const newWidth = startWidth + delta;
|
|
191
|
+
scheduleColumnWidthUpdate(columnId, newWidth);
|
|
192
|
+
};
|
|
193
|
+
const cleanupResize = () => {
|
|
194
|
+
target.removeEventListener("pointermove", handlePointerMove);
|
|
195
|
+
target.removeEventListener("pointerup", handlePointerUp);
|
|
196
|
+
target.removeEventListener("pointercancel", handlePointerCancel);
|
|
197
|
+
document.body.classList.remove("rowakit-resizing");
|
|
198
|
+
isResizingRef.current = false;
|
|
199
|
+
resizingColIdRef.current = null;
|
|
200
|
+
lastResizeEndTsRef.current = Date.now();
|
|
201
|
+
try {
|
|
202
|
+
target.releasePointerCapture(pointerId);
|
|
203
|
+
} catch {
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
const handlePointerUp = () => {
|
|
207
|
+
cleanupResize();
|
|
208
|
+
};
|
|
209
|
+
const handlePointerCancel = () => {
|
|
210
|
+
cleanupResize();
|
|
211
|
+
};
|
|
212
|
+
target.addEventListener("pointermove", handlePointerMove);
|
|
213
|
+
target.addEventListener("pointerup", handlePointerUp);
|
|
214
|
+
target.addEventListener("pointercancel", handlePointerCancel);
|
|
215
|
+
};
|
|
216
|
+
const handleColumnResizeDoubleClick = (e, columnId) => {
|
|
217
|
+
e.preventDefault();
|
|
218
|
+
e.stopPropagation();
|
|
219
|
+
autoFitColumnWidth(columnId);
|
|
220
|
+
};
|
|
221
|
+
return {
|
|
222
|
+
tableRef,
|
|
223
|
+
columnWidths,
|
|
224
|
+
setColumnWidths,
|
|
225
|
+
startColumnResize,
|
|
226
|
+
handleColumnResizeDoubleClick,
|
|
227
|
+
isResizingRef,
|
|
228
|
+
lastResizeEndTsRef,
|
|
229
|
+
resizingColIdRef
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function useFetcherState(fetcher, query, setQuery) {
|
|
233
|
+
const [dataState, setDataState] = useState({
|
|
234
|
+
state: "idle",
|
|
235
|
+
items: [],
|
|
236
|
+
total: 0
|
|
237
|
+
});
|
|
238
|
+
const requestIdRef = useRef(0);
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
const currentRequestId = ++requestIdRef.current;
|
|
241
|
+
setDataState((prev) => ({ ...prev, state: "loading" }));
|
|
242
|
+
fetcher(query).then((result) => {
|
|
243
|
+
if (currentRequestId !== requestIdRef.current) return;
|
|
244
|
+
if (result.items.length === 0) {
|
|
245
|
+
setDataState({
|
|
246
|
+
state: "empty",
|
|
247
|
+
items: [],
|
|
248
|
+
total: result.total
|
|
249
|
+
});
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
setDataState({
|
|
253
|
+
state: "success",
|
|
254
|
+
items: result.items,
|
|
255
|
+
total: result.total
|
|
256
|
+
});
|
|
257
|
+
}).catch((error) => {
|
|
258
|
+
if (currentRequestId !== requestIdRef.current) return;
|
|
259
|
+
setDataState({
|
|
260
|
+
state: "error",
|
|
261
|
+
items: [],
|
|
262
|
+
total: 0,
|
|
263
|
+
error: error instanceof Error ? error.message : "Failed to load data"
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
}, [fetcher, query]);
|
|
267
|
+
const handleRetry = () => {
|
|
268
|
+
setQuery({ ...query });
|
|
269
|
+
};
|
|
270
|
+
return {
|
|
271
|
+
dataState,
|
|
272
|
+
setDataState,
|
|
273
|
+
handleRetry,
|
|
274
|
+
isLoading: dataState.state === "loading",
|
|
275
|
+
isError: dataState.state === "error",
|
|
276
|
+
isEmpty: dataState.state === "empty"
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function validateViewName(name) {
|
|
280
|
+
const trimmed = name.trim();
|
|
281
|
+
if (trimmed.length === 0) {
|
|
282
|
+
return { valid: false, error: "Name cannot be empty" };
|
|
283
|
+
}
|
|
284
|
+
if (trimmed.length > 40) {
|
|
285
|
+
return { valid: false, error: "Name cannot exceed 40 characters" };
|
|
286
|
+
}
|
|
287
|
+
const invalidChars = /[/\\?%*:|"<>\x00-\x1f\x7f]/;
|
|
288
|
+
if (invalidChars.test(trimmed)) {
|
|
289
|
+
return { valid: false, error: "Name contains invalid characters" };
|
|
290
|
+
}
|
|
291
|
+
return { valid: true };
|
|
292
|
+
}
|
|
293
|
+
function getSavedViewsIndex() {
|
|
294
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
const indexStr = localStorage.getItem("rowakit-views-index");
|
|
299
|
+
if (indexStr) {
|
|
300
|
+
const index = JSON.parse(indexStr);
|
|
301
|
+
if (Array.isArray(index)) {
|
|
302
|
+
return index;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} catch {
|
|
306
|
+
}
|
|
307
|
+
const rebuilt = [];
|
|
308
|
+
try {
|
|
309
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
310
|
+
const key = localStorage.key(i);
|
|
311
|
+
if (key?.startsWith("rowakit-view-")) {
|
|
312
|
+
const name = key.substring("rowakit-view-".length);
|
|
313
|
+
rebuilt.push({ name, updatedAt: Date.now() });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
} catch {
|
|
317
|
+
}
|
|
318
|
+
return rebuilt;
|
|
319
|
+
}
|
|
320
|
+
function setSavedViewsIndex(index) {
|
|
321
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
localStorage.setItem("rowakit-views-index", JSON.stringify(index));
|
|
326
|
+
} catch {
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function loadSavedViewsFromStorage() {
|
|
330
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
331
|
+
return [];
|
|
332
|
+
}
|
|
333
|
+
const index = getSavedViewsIndex();
|
|
334
|
+
const views = [];
|
|
335
|
+
for (const entry of index) {
|
|
336
|
+
try {
|
|
337
|
+
const viewStr = localStorage.getItem(`rowakit-view-${entry.name}`);
|
|
338
|
+
if (viewStr) {
|
|
339
|
+
const state = JSON.parse(viewStr);
|
|
340
|
+
views.push({ name: entry.name, state });
|
|
341
|
+
}
|
|
342
|
+
} catch {
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return views;
|
|
346
|
+
}
|
|
347
|
+
function useSavedViews(options) {
|
|
348
|
+
const [savedViews, setSavedViews] = useState([]);
|
|
349
|
+
const [showSaveViewForm, setShowSaveViewForm] = useState(false);
|
|
350
|
+
const [saveViewInput, setSaveViewInput] = useState("");
|
|
351
|
+
const [saveViewError, setSaveViewError] = useState("");
|
|
352
|
+
const [overwriteConfirmName, setOverwriteConfirmName] = useState(null);
|
|
353
|
+
useEffect(() => {
|
|
354
|
+
if (!options.enableSavedViews) return;
|
|
355
|
+
setSavedViews(loadSavedViewsFromStorage());
|
|
356
|
+
}, [options.enableSavedViews]);
|
|
357
|
+
const saveCurrentView = (name) => {
|
|
358
|
+
const viewState = {
|
|
359
|
+
page: options.query.page,
|
|
360
|
+
pageSize: options.query.pageSize,
|
|
361
|
+
sort: options.query.sort,
|
|
362
|
+
filters: options.query.filters,
|
|
363
|
+
columnWidths: options.enableColumnResizing ? options.columnWidths : void 0
|
|
364
|
+
};
|
|
365
|
+
setSavedViews((prev) => {
|
|
366
|
+
const filtered = prev.filter((v) => v.name !== name);
|
|
367
|
+
return [...filtered, { name, state: viewState }];
|
|
368
|
+
});
|
|
369
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
370
|
+
try {
|
|
371
|
+
localStorage.setItem(`rowakit-view-${name}`, JSON.stringify(viewState));
|
|
372
|
+
const index = getSavedViewsIndex();
|
|
373
|
+
const filtered = index.filter((v) => v.name !== name);
|
|
374
|
+
filtered.push({ name, updatedAt: Date.now() });
|
|
375
|
+
setSavedViewsIndex(filtered);
|
|
376
|
+
} catch {
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
const loadSavedView = (name) => {
|
|
381
|
+
const view = savedViews.find((v) => v.name === name);
|
|
382
|
+
if (!view) return;
|
|
383
|
+
const { state } = view;
|
|
384
|
+
options.setQuery({
|
|
385
|
+
page: state.page,
|
|
386
|
+
pageSize: state.pageSize,
|
|
387
|
+
sort: state.sort,
|
|
388
|
+
filters: state.filters
|
|
389
|
+
});
|
|
390
|
+
options.setFilters(state.filters ?? {});
|
|
391
|
+
if (state.columnWidths && options.enableColumnResizing) {
|
|
392
|
+
options.setColumnWidths(state.columnWidths);
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
const deleteSavedView = (name) => {
|
|
396
|
+
setSavedViews((prev) => prev.filter((v) => v.name !== name));
|
|
397
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
398
|
+
try {
|
|
399
|
+
localStorage.removeItem(`rowakit-view-${name}`);
|
|
400
|
+
const index = getSavedViewsIndex();
|
|
401
|
+
const filtered = index.filter((v) => v.name !== name);
|
|
402
|
+
setSavedViewsIndex(filtered);
|
|
403
|
+
} catch {
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
const resetTableState = () => {
|
|
408
|
+
options.setQuery({
|
|
409
|
+
page: 1,
|
|
410
|
+
pageSize: options.defaultPageSize
|
|
411
|
+
});
|
|
412
|
+
options.setFilters({});
|
|
413
|
+
options.setColumnWidths({});
|
|
414
|
+
};
|
|
415
|
+
const shouldShowReset = useMemo(() => {
|
|
416
|
+
if (!options.enableSavedViews) return false;
|
|
417
|
+
return Boolean(options.query.page > 1 || options.query.sort || options.query.filters && Object.keys(options.query.filters).length > 0);
|
|
418
|
+
}, [options.enableSavedViews, options.query.page, options.query.sort, options.query.filters]);
|
|
419
|
+
const openSaveViewForm = () => {
|
|
420
|
+
setShowSaveViewForm(true);
|
|
421
|
+
setSaveViewInput("");
|
|
422
|
+
setSaveViewError("");
|
|
423
|
+
setOverwriteConfirmName(null);
|
|
424
|
+
};
|
|
425
|
+
const cancelSaveViewForm = () => {
|
|
426
|
+
setShowSaveViewForm(false);
|
|
427
|
+
setSaveViewInput("");
|
|
428
|
+
setSaveViewError("");
|
|
429
|
+
setOverwriteConfirmName(null);
|
|
430
|
+
};
|
|
431
|
+
const onSaveViewInputChange = (e) => {
|
|
432
|
+
setSaveViewInput(e.target.value);
|
|
433
|
+
setSaveViewError("");
|
|
434
|
+
};
|
|
435
|
+
const attemptSave = () => {
|
|
436
|
+
const validation = validateViewName(saveViewInput);
|
|
437
|
+
if (!validation.valid) {
|
|
438
|
+
setSaveViewError(validation.error || "Invalid name");
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
const trimmed = saveViewInput.trim();
|
|
442
|
+
if (savedViews.some((v) => v.name === trimmed)) {
|
|
443
|
+
setOverwriteConfirmName(trimmed);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
saveCurrentView(trimmed);
|
|
447
|
+
cancelSaveViewForm();
|
|
448
|
+
};
|
|
449
|
+
const onSaveViewInputKeyDown = (e) => {
|
|
450
|
+
if (e.key !== "Enter") return;
|
|
451
|
+
attemptSave();
|
|
452
|
+
};
|
|
453
|
+
const confirmOverwrite = () => {
|
|
454
|
+
if (!overwriteConfirmName) return;
|
|
455
|
+
saveCurrentView(overwriteConfirmName);
|
|
456
|
+
cancelSaveViewForm();
|
|
457
|
+
};
|
|
458
|
+
const cancelOverwrite = () => {
|
|
459
|
+
setOverwriteConfirmName(null);
|
|
460
|
+
};
|
|
461
|
+
return {
|
|
462
|
+
savedViews,
|
|
463
|
+
showSaveViewForm,
|
|
464
|
+
saveViewInput,
|
|
465
|
+
saveViewError,
|
|
466
|
+
overwriteConfirmName,
|
|
467
|
+
openSaveViewForm,
|
|
468
|
+
cancelSaveViewForm,
|
|
469
|
+
onSaveViewInputChange,
|
|
470
|
+
onSaveViewInputKeyDown,
|
|
471
|
+
attemptSave,
|
|
472
|
+
confirmOverwrite,
|
|
473
|
+
cancelOverwrite,
|
|
474
|
+
loadSavedView,
|
|
475
|
+
deleteSavedView,
|
|
476
|
+
resetTableState,
|
|
477
|
+
shouldShowReset
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// src/hooks/useSortingState.ts
|
|
482
|
+
function useSortingState(query, setQuery) {
|
|
483
|
+
const handleSort = (field, isMultiSort = false) => {
|
|
484
|
+
setQuery((prev) => {
|
|
485
|
+
const currentSorts = prev.sorts || [];
|
|
486
|
+
const existingSort = currentSorts.find((s) => s.field === field);
|
|
487
|
+
if (!isMultiSort) {
|
|
488
|
+
if (existingSort?.priority === 0) {
|
|
489
|
+
if (existingSort.direction === "asc") {
|
|
490
|
+
return {
|
|
491
|
+
...prev,
|
|
492
|
+
sorts: [{ field, direction: "desc", priority: 0 }],
|
|
493
|
+
page: 1
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
const { sorts: _removed, ...rest } = prev;
|
|
497
|
+
return {
|
|
498
|
+
...rest,
|
|
499
|
+
page: 1
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
return {
|
|
503
|
+
...prev,
|
|
504
|
+
sorts: [{ field, direction: "asc", priority: 0 }],
|
|
505
|
+
page: 1
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
if (existingSort) {
|
|
509
|
+
const newSorts2 = currentSorts.map((s) => {
|
|
510
|
+
if (s.field === field) {
|
|
511
|
+
const newDirection = s.direction === "asc" ? "desc" : "asc";
|
|
512
|
+
return { ...s, direction: newDirection };
|
|
513
|
+
}
|
|
514
|
+
return s;
|
|
515
|
+
});
|
|
516
|
+
return { ...prev, sorts: newSorts2, page: 1 };
|
|
517
|
+
}
|
|
518
|
+
const nextPriority = currentSorts.length;
|
|
519
|
+
const newSorts = [...currentSorts, { field, direction: "asc", priority: nextPriority }];
|
|
520
|
+
return { ...prev, sorts: newSorts, page: 1 };
|
|
521
|
+
});
|
|
522
|
+
};
|
|
523
|
+
const getSortIndicator = (field) => {
|
|
524
|
+
const sorts = query.sorts || [];
|
|
525
|
+
const sort = sorts.find((s) => s.field === field);
|
|
526
|
+
if (!sort) {
|
|
527
|
+
return "";
|
|
528
|
+
}
|
|
529
|
+
const directionIcon = sort.direction === "asc" ? " \u2191" : " \u2193";
|
|
530
|
+
const priorityLabel = sort.priority === 0 ? "" : ` [${sort.priority + 1}]`;
|
|
531
|
+
return directionIcon + priorityLabel;
|
|
532
|
+
};
|
|
533
|
+
const getSortPriority = (field) => {
|
|
534
|
+
const sorts = query.sorts || [];
|
|
535
|
+
const sort = sorts.find((s) => s.field === field);
|
|
536
|
+
return sort ? sort.priority : null;
|
|
537
|
+
};
|
|
538
|
+
return { handleSort, getSortIndicator, getSortPriority };
|
|
539
|
+
}
|
|
540
|
+
function parseUrlState(params, defaultPageSize, pageSizeOptions) {
|
|
541
|
+
const pageStr = params.get("page");
|
|
542
|
+
let page = 1;
|
|
543
|
+
if (pageStr) {
|
|
544
|
+
const parsed = parseInt(pageStr, 10);
|
|
545
|
+
page = !isNaN(parsed) && parsed >= 1 ? parsed : 1;
|
|
546
|
+
}
|
|
547
|
+
const pageSizeStr = params.get("pageSize");
|
|
548
|
+
let pageSize = defaultPageSize;
|
|
549
|
+
if (pageSizeStr) {
|
|
550
|
+
const parsed = parseInt(pageSizeStr, 10);
|
|
551
|
+
if (!isNaN(parsed) && parsed >= 1) {
|
|
552
|
+
if (pageSizeOptions && pageSizeOptions.length > 0) {
|
|
553
|
+
pageSize = pageSizeOptions.includes(parsed) ? parsed : defaultPageSize;
|
|
554
|
+
} else {
|
|
555
|
+
pageSize = parsed;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
const result = { page, pageSize };
|
|
560
|
+
const sortsStr = params.get("sorts");
|
|
561
|
+
if (sortsStr) {
|
|
562
|
+
try {
|
|
563
|
+
const parsed = JSON.parse(sortsStr);
|
|
564
|
+
if (Array.isArray(parsed) && parsed.every((s) => typeof s.field === "string" && (s.direction === "asc" || s.direction === "desc") && typeof s.priority === "number")) {
|
|
565
|
+
result.sorts = parsed;
|
|
566
|
+
}
|
|
567
|
+
} catch {
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (!result.sorts) {
|
|
571
|
+
const sortField = params.get("sortField");
|
|
572
|
+
const sortDir = params.get("sortDirection");
|
|
573
|
+
if (sortField && (sortDir === "asc" || sortDir === "desc")) {
|
|
574
|
+
result.sort = { field: sortField, direction: sortDir };
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
const filtersStr = params.get("filters");
|
|
578
|
+
if (filtersStr) {
|
|
579
|
+
try {
|
|
580
|
+
const parsed = JSON.parse(filtersStr);
|
|
581
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
582
|
+
result.filters = parsed;
|
|
583
|
+
}
|
|
584
|
+
} catch {
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
const widthsStr = params.get("columnWidths");
|
|
588
|
+
if (widthsStr) {
|
|
589
|
+
try {
|
|
590
|
+
const parsed = JSON.parse(widthsStr);
|
|
591
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
592
|
+
const widths = {};
|
|
593
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
594
|
+
if (typeof value === "number" && value > 0) {
|
|
595
|
+
widths[key] = value;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
if (Object.keys(widths).length > 0) {
|
|
599
|
+
result.columnWidths = widths;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
} catch {
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return result;
|
|
606
|
+
}
|
|
607
|
+
function serializeUrlState(query, filters, columnWidths, defaultPageSize, enableColumnResizing) {
|
|
608
|
+
const params = new URLSearchParams();
|
|
609
|
+
params.set("page", String(query.page));
|
|
610
|
+
if (query.pageSize !== defaultPageSize) {
|
|
611
|
+
params.set("pageSize", String(query.pageSize));
|
|
612
|
+
}
|
|
613
|
+
if (query.sorts && query.sorts.length > 0) {
|
|
614
|
+
params.set("sorts", JSON.stringify(query.sorts));
|
|
615
|
+
} else if (query.sort) {
|
|
616
|
+
params.set("sortField", query.sort.field);
|
|
617
|
+
params.set("sortDirection", query.sort.direction);
|
|
618
|
+
}
|
|
619
|
+
if (filters && Object.keys(filters).length > 0) {
|
|
620
|
+
const nonEmptyFilters = Object.fromEntries(
|
|
621
|
+
Object.entries(filters).filter(([, v]) => v !== void 0)
|
|
622
|
+
);
|
|
623
|
+
if (Object.keys(nonEmptyFilters).length > 0) {
|
|
624
|
+
params.set("filters", JSON.stringify(nonEmptyFilters));
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (enableColumnResizing && Object.keys(columnWidths).length > 0) {
|
|
628
|
+
params.set("columnWidths", JSON.stringify(columnWidths));
|
|
629
|
+
}
|
|
630
|
+
return params.toString();
|
|
631
|
+
}
|
|
632
|
+
function useUrlSync({
|
|
633
|
+
syncToUrl,
|
|
634
|
+
enableColumnResizing,
|
|
635
|
+
defaultPageSize,
|
|
636
|
+
pageSizeOptions,
|
|
637
|
+
columns,
|
|
638
|
+
query,
|
|
639
|
+
setQuery,
|
|
640
|
+
filters,
|
|
641
|
+
setFilters,
|
|
642
|
+
columnWidths,
|
|
643
|
+
setColumnWidths
|
|
644
|
+
}) {
|
|
645
|
+
const didHydrateUrlRef = useRef(false);
|
|
646
|
+
const didSkipInitialUrlSyncRef = useRef(false);
|
|
647
|
+
const urlSyncDebounceRef = useRef(null);
|
|
648
|
+
useEffect(() => {
|
|
649
|
+
if (!syncToUrl) {
|
|
650
|
+
didSkipInitialUrlSyncRef.current = false;
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
if (!didSkipInitialUrlSyncRef.current) {
|
|
654
|
+
didSkipInitialUrlSyncRef.current = true;
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
if (urlSyncDebounceRef.current) {
|
|
658
|
+
clearTimeout(urlSyncDebounceRef.current);
|
|
659
|
+
urlSyncDebounceRef.current = null;
|
|
660
|
+
}
|
|
661
|
+
const urlStr = serializeUrlState(query, filters, columnWidths, defaultPageSize, enableColumnResizing);
|
|
662
|
+
const qs = urlStr ? `?${urlStr}` : "";
|
|
663
|
+
window.history.replaceState(null, "", `${window.location.pathname}${qs}${window.location.hash}`);
|
|
664
|
+
}, [
|
|
665
|
+
query,
|
|
666
|
+
filters,
|
|
667
|
+
syncToUrl,
|
|
668
|
+
enableColumnResizing,
|
|
669
|
+
defaultPageSize,
|
|
670
|
+
columnWidths
|
|
671
|
+
]);
|
|
672
|
+
useEffect(() => {
|
|
673
|
+
if (!syncToUrl || !enableColumnResizing) return;
|
|
674
|
+
if (!didSkipInitialUrlSyncRef.current) return;
|
|
675
|
+
if (urlSyncDebounceRef.current) {
|
|
676
|
+
clearTimeout(urlSyncDebounceRef.current);
|
|
677
|
+
}
|
|
678
|
+
urlSyncDebounceRef.current = setTimeout(() => {
|
|
679
|
+
const urlStr = serializeUrlState(query, filters, columnWidths, defaultPageSize, enableColumnResizing);
|
|
680
|
+
const qs = urlStr ? `?${urlStr}` : "";
|
|
681
|
+
window.history.replaceState(null, "", `${window.location.pathname}${qs}${window.location.hash}`);
|
|
682
|
+
urlSyncDebounceRef.current = null;
|
|
683
|
+
}, 150);
|
|
684
|
+
return () => {
|
|
685
|
+
if (urlSyncDebounceRef.current) {
|
|
686
|
+
clearTimeout(urlSyncDebounceRef.current);
|
|
687
|
+
urlSyncDebounceRef.current = null;
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
}, [
|
|
691
|
+
columnWidths,
|
|
692
|
+
syncToUrl,
|
|
693
|
+
enableColumnResizing,
|
|
694
|
+
query,
|
|
695
|
+
filters,
|
|
696
|
+
defaultPageSize
|
|
697
|
+
]);
|
|
698
|
+
useEffect(() => {
|
|
699
|
+
if (!syncToUrl) {
|
|
700
|
+
didHydrateUrlRef.current = false;
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
if (didHydrateUrlRef.current) return;
|
|
704
|
+
didHydrateUrlRef.current = true;
|
|
705
|
+
const params = new URLSearchParams(window.location.search);
|
|
706
|
+
const parsed = parseUrlState(params, defaultPageSize, pageSizeOptions);
|
|
707
|
+
setQuery({
|
|
708
|
+
page: parsed.page,
|
|
709
|
+
pageSize: parsed.pageSize,
|
|
710
|
+
sort: parsed.sort,
|
|
711
|
+
sorts: parsed.sorts,
|
|
712
|
+
filters: parsed.filters
|
|
713
|
+
});
|
|
714
|
+
if (parsed.filters) {
|
|
715
|
+
setFilters(parsed.filters);
|
|
716
|
+
}
|
|
717
|
+
if (parsed.columnWidths && enableColumnResizing) {
|
|
718
|
+
const clamped = {};
|
|
719
|
+
for (const [colId, rawWidth] of Object.entries(parsed.columnWidths)) {
|
|
720
|
+
const widthNum = typeof rawWidth === "number" ? rawWidth : Number(rawWidth);
|
|
721
|
+
if (!Number.isFinite(widthNum)) continue;
|
|
722
|
+
const colDef = columns.find((c) => c.id === colId);
|
|
723
|
+
if (!colDef) continue;
|
|
724
|
+
const minW = colDef.minWidth ?? 80;
|
|
725
|
+
const maxW = colDef.maxWidth;
|
|
726
|
+
let finalW = Math.max(minW, widthNum);
|
|
727
|
+
if (maxW != null) {
|
|
728
|
+
finalW = Math.min(finalW, maxW);
|
|
729
|
+
}
|
|
730
|
+
clamped[colId] = finalW;
|
|
731
|
+
}
|
|
732
|
+
setColumnWidths(clamped);
|
|
733
|
+
}
|
|
734
|
+
}, [
|
|
735
|
+
syncToUrl,
|
|
736
|
+
defaultPageSize,
|
|
737
|
+
enableColumnResizing,
|
|
738
|
+
pageSizeOptions,
|
|
739
|
+
columns,
|
|
740
|
+
setQuery,
|
|
741
|
+
setFilters,
|
|
742
|
+
setColumnWidths
|
|
743
|
+
]);
|
|
744
|
+
}
|
|
745
|
+
var FOCUSABLE_SELECTORS = [
|
|
746
|
+
"button",
|
|
747
|
+
"[href]",
|
|
748
|
+
"input",
|
|
749
|
+
"select",
|
|
750
|
+
"textarea",
|
|
751
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
752
|
+
].join(",");
|
|
753
|
+
function useFocusTrap(ref, options = {}) {
|
|
754
|
+
const { onEscape, autoFocus = true } = options;
|
|
755
|
+
const firstFocusableRef = useRef(null);
|
|
756
|
+
const lastFocusableRef = useRef(null);
|
|
757
|
+
useEffect(() => {
|
|
758
|
+
const modalEl = ref.current;
|
|
759
|
+
if (!modalEl) return;
|
|
760
|
+
const getFocusableElements = () => {
|
|
761
|
+
const elements = Array.from(modalEl.querySelectorAll(FOCUSABLE_SELECTORS));
|
|
762
|
+
return elements.filter((el) => !el.hasAttribute("disabled") && el.offsetParent !== null);
|
|
763
|
+
};
|
|
764
|
+
let focusableElements = getFocusableElements();
|
|
765
|
+
if (focusableElements.length === 0) return;
|
|
766
|
+
firstFocusableRef.current = focusableElements[0] || null;
|
|
767
|
+
lastFocusableRef.current = focusableElements[focusableElements.length - 1] || null;
|
|
768
|
+
if (autoFocus && firstFocusableRef.current) {
|
|
769
|
+
firstFocusableRef.current.focus();
|
|
770
|
+
}
|
|
771
|
+
const handleKeyDown = (e) => {
|
|
772
|
+
focusableElements = getFocusableElements();
|
|
773
|
+
if (focusableElements.length === 0) return;
|
|
774
|
+
const activeEl = document.activeElement;
|
|
775
|
+
const firstEl = focusableElements[0] || null;
|
|
776
|
+
const lastEl = focusableElements[focusableElements.length - 1] || null;
|
|
777
|
+
if (e.key === "Escape") {
|
|
778
|
+
e.preventDefault();
|
|
779
|
+
onEscape?.();
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
if (e.key === "Tab") {
|
|
783
|
+
if (e.shiftKey) {
|
|
784
|
+
if (activeEl === firstEl && lastEl) {
|
|
785
|
+
e.preventDefault();
|
|
786
|
+
lastEl.focus();
|
|
787
|
+
}
|
|
788
|
+
} else {
|
|
789
|
+
if (activeEl === lastEl && firstEl) {
|
|
790
|
+
e.preventDefault();
|
|
791
|
+
firstEl.focus();
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
modalEl.addEventListener("keydown", handleKeyDown);
|
|
797
|
+
return () => {
|
|
798
|
+
modalEl.removeEventListener("keydown", handleKeyDown);
|
|
799
|
+
};
|
|
800
|
+
}, [ref, onEscape, autoFocus]);
|
|
801
|
+
}
|
|
802
|
+
function RowSelectionHeaderCell(props) {
|
|
803
|
+
const checkboxRef = useRef(null);
|
|
804
|
+
useEffect(() => {
|
|
805
|
+
if (!checkboxRef.current) return;
|
|
806
|
+
checkboxRef.current.indeterminate = props.indeterminate;
|
|
807
|
+
}, [props.indeterminate]);
|
|
808
|
+
return /* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsx(
|
|
809
|
+
"input",
|
|
810
|
+
{
|
|
811
|
+
ref: checkboxRef,
|
|
812
|
+
type: "checkbox",
|
|
813
|
+
"aria-label": "Select all rows",
|
|
814
|
+
disabled: props.disabled,
|
|
815
|
+
checked: props.checked,
|
|
816
|
+
onChange: (e) => props.onChange(e.target.checked)
|
|
817
|
+
}
|
|
818
|
+
) });
|
|
819
|
+
}
|
|
820
|
+
function RowSelectionCell(props) {
|
|
821
|
+
return /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsx(
|
|
822
|
+
"input",
|
|
823
|
+
{
|
|
824
|
+
type: "checkbox",
|
|
825
|
+
"aria-label": `Select row ${props.rowKey}`,
|
|
826
|
+
disabled: props.disabled,
|
|
827
|
+
checked: props.checked,
|
|
828
|
+
onChange: (e) => props.onChange(e.target.checked)
|
|
829
|
+
}
|
|
830
|
+
) });
|
|
831
|
+
}
|
|
832
|
+
function BulkActionBar(props) {
|
|
833
|
+
if (props.selectedCount <= 0) return null;
|
|
834
|
+
return /* @__PURE__ */ jsxs("div", { className: "rowakit-bulk-action-bar", children: [
|
|
835
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
836
|
+
props.selectedCount,
|
|
837
|
+
" selected"
|
|
838
|
+
] }),
|
|
839
|
+
props.actions.map((action) => /* @__PURE__ */ jsx(
|
|
840
|
+
"button",
|
|
841
|
+
{
|
|
842
|
+
type: "button",
|
|
843
|
+
className: "rowakit-button rowakit-button-secondary",
|
|
844
|
+
onClick: () => props.onActionClick(action.id),
|
|
845
|
+
children: action.label
|
|
846
|
+
},
|
|
847
|
+
action.id
|
|
848
|
+
))
|
|
849
|
+
] });
|
|
850
|
+
}
|
|
851
|
+
function downloadBlob(blob, filename) {
|
|
852
|
+
if (typeof window === "undefined") return;
|
|
853
|
+
if (typeof URL === "undefined" || typeof URL.createObjectURL !== "function") return;
|
|
854
|
+
const url = URL.createObjectURL(blob);
|
|
855
|
+
try {
|
|
856
|
+
const a = document.createElement("a");
|
|
857
|
+
a.href = url;
|
|
858
|
+
a.download = filename;
|
|
859
|
+
a.rel = "noopener noreferrer";
|
|
860
|
+
a.click();
|
|
861
|
+
} finally {
|
|
862
|
+
URL.revokeObjectURL(url);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
function openUrl(url) {
|
|
866
|
+
if (typeof window === "undefined") return;
|
|
867
|
+
if (typeof window.open === "function") {
|
|
868
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
try {
|
|
872
|
+
window.location.assign(url);
|
|
873
|
+
} catch {
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
function ExportButton(props) {
|
|
877
|
+
const [isExporting, setIsExporting] = useState(false);
|
|
878
|
+
const [error, setError] = useState(null);
|
|
879
|
+
const onClick = async () => {
|
|
880
|
+
if (isExporting) return;
|
|
881
|
+
setIsExporting(true);
|
|
882
|
+
setError(null);
|
|
883
|
+
try {
|
|
884
|
+
const snapshot = { ...props.query };
|
|
885
|
+
const result = await props.exporter(snapshot);
|
|
886
|
+
if (result instanceof Blob) {
|
|
887
|
+
downloadBlob(result, "rowakit-export.csv");
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
openUrl(result.url);
|
|
891
|
+
} catch (e) {
|
|
892
|
+
setError(e instanceof Error ? e.message : "Export failed");
|
|
893
|
+
} finally {
|
|
894
|
+
setIsExporting(false);
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
return /* @__PURE__ */ jsxs("div", { className: "rowakit-export", children: [
|
|
898
|
+
/* @__PURE__ */ jsx(
|
|
899
|
+
"button",
|
|
900
|
+
{
|
|
901
|
+
type: "button",
|
|
902
|
+
className: "rowakit-button rowakit-button-secondary",
|
|
903
|
+
onClick,
|
|
904
|
+
disabled: isExporting,
|
|
905
|
+
children: isExporting ? "Exporting\u2026" : "Export CSV"
|
|
906
|
+
}
|
|
907
|
+
),
|
|
908
|
+
error && /* @__PURE__ */ jsx("div", { className: "rowakit-export-error", children: error })
|
|
909
|
+
] });
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// src/state/selection.ts
|
|
913
|
+
function toggleSelectionKey(selected, key) {
|
|
914
|
+
if (selected.includes(key)) {
|
|
915
|
+
return selected.filter((k) => k !== key);
|
|
916
|
+
}
|
|
917
|
+
return [...selected, key];
|
|
918
|
+
}
|
|
919
|
+
function isAllSelected(selected, pageKeys) {
|
|
920
|
+
if (pageKeys.length === 0) return false;
|
|
921
|
+
return pageKeys.every((k) => selected.includes(k));
|
|
922
|
+
}
|
|
923
|
+
function isIndeterminate(selected, pageKeys) {
|
|
924
|
+
if (pageKeys.length === 0) return false;
|
|
925
|
+
const selectedCount = pageKeys.filter((k) => selected.includes(k)).length;
|
|
926
|
+
return selectedCount > 0 && selectedCount < pageKeys.length;
|
|
927
|
+
}
|
|
928
|
+
function selectAll(pageKeys) {
|
|
929
|
+
return [...pageKeys];
|
|
930
|
+
}
|
|
931
|
+
function clearSelection() {
|
|
932
|
+
return [];
|
|
933
|
+
}
|
|
106
934
|
function getRowKey(row, rowKey) {
|
|
107
935
|
if (typeof rowKey === "function") {
|
|
108
936
|
return rowKey(row);
|
|
@@ -218,76 +1046,92 @@ function RowaKitTable({
|
|
|
218
1046
|
enableFilters = false,
|
|
219
1047
|
enableColumnResizing = false,
|
|
220
1048
|
syncToUrl = false,
|
|
221
|
-
enableSavedViews = false
|
|
1049
|
+
enableSavedViews = false,
|
|
1050
|
+
enableRowSelection = false,
|
|
1051
|
+
onSelectionChange,
|
|
1052
|
+
bulkActions,
|
|
1053
|
+
exporter
|
|
222
1054
|
}) {
|
|
223
|
-
const [dataState, setDataState] = useState({
|
|
224
|
-
state: "idle",
|
|
225
|
-
items: [],
|
|
226
|
-
total: 0
|
|
227
|
-
});
|
|
228
1055
|
const [query, setQuery] = useState({
|
|
229
1056
|
page: 1,
|
|
230
1057
|
pageSize: defaultPageSize
|
|
231
1058
|
});
|
|
232
1059
|
const [filters, setFilters] = useState({});
|
|
233
|
-
const [columnWidths, setColumnWidths] = useState({});
|
|
234
|
-
const resizeRafRef = useRef(null);
|
|
235
|
-
const resizePendingRef = useRef(null);
|
|
236
|
-
const tableRef = useRef(null);
|
|
237
|
-
const [savedViews, setSavedViews] = useState([]);
|
|
238
1060
|
const [confirmState, setConfirmState] = useState(null);
|
|
239
|
-
const
|
|
1061
|
+
const [selectedKeys, setSelectedKeys] = useState([]);
|
|
1062
|
+
const [bulkConfirmState, setBulkConfirmState] = useState(null);
|
|
1063
|
+
const confirmModalRef = useRef(null);
|
|
1064
|
+
const bulkConfirmModalRef = useRef(null);
|
|
1065
|
+
const {
|
|
1066
|
+
tableRef,
|
|
1067
|
+
columnWidths,
|
|
1068
|
+
setColumnWidths,
|
|
1069
|
+
startColumnResize,
|
|
1070
|
+
handleColumnResizeDoubleClick,
|
|
1071
|
+
isResizingRef,
|
|
1072
|
+
lastResizeEndTsRef,
|
|
1073
|
+
resizingColIdRef
|
|
1074
|
+
} = useColumnResizing(columns);
|
|
1075
|
+
useUrlSync({
|
|
1076
|
+
syncToUrl,
|
|
1077
|
+
enableColumnResizing,
|
|
1078
|
+
defaultPageSize,
|
|
1079
|
+
pageSizeOptions,
|
|
1080
|
+
columns,
|
|
1081
|
+
query,
|
|
1082
|
+
setQuery,
|
|
1083
|
+
filters,
|
|
1084
|
+
setFilters,
|
|
1085
|
+
columnWidths,
|
|
1086
|
+
setColumnWidths
|
|
1087
|
+
});
|
|
1088
|
+
const { dataState, handleRetry, isLoading, isError, isEmpty } = useFetcherState(fetcher, query, setQuery);
|
|
1089
|
+
const { handleSort, getSortIndicator } = useSortingState(query, setQuery);
|
|
1090
|
+
const pageRowKeys = dataState.items.map((row) => getRowKey(row, rowKey));
|
|
1091
|
+
const headerChecked = isAllSelected(selectedKeys, pageRowKeys);
|
|
1092
|
+
const headerIndeterminate = isIndeterminate(selectedKeys, pageRowKeys);
|
|
240
1093
|
useEffect(() => {
|
|
241
|
-
if (!
|
|
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);
|
|
248
|
-
}
|
|
249
|
-
if (query.filters && Object.keys(query.filters).length > 0) {
|
|
250
|
-
params.set("filters", JSON.stringify(query.filters));
|
|
251
|
-
}
|
|
252
|
-
if (enableColumnResizing && Object.keys(columnWidths).length > 0) {
|
|
253
|
-
params.set("columnWidths", JSON.stringify(columnWidths));
|
|
254
|
-
}
|
|
255
|
-
window.history.replaceState(null, "", `?${params.toString()}`);
|
|
256
|
-
}, [query, columnWidths, syncToUrl, enableColumnResizing]);
|
|
1094
|
+
if (!enableRowSelection) return;
|
|
1095
|
+
setSelectedKeys(clearSelection());
|
|
1096
|
+
}, [enableRowSelection, query.page, dataState.items]);
|
|
257
1097
|
useEffect(() => {
|
|
258
|
-
if (!
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
1098
|
+
if (!enableRowSelection || !onSelectionChange) return;
|
|
1099
|
+
onSelectionChange(selectedKeys);
|
|
1100
|
+
}, [enableRowSelection, onSelectionChange, selectedKeys]);
|
|
1101
|
+
const {
|
|
1102
|
+
savedViews,
|
|
1103
|
+
showSaveViewForm,
|
|
1104
|
+
saveViewInput,
|
|
1105
|
+
saveViewError,
|
|
1106
|
+
overwriteConfirmName,
|
|
1107
|
+
openSaveViewForm,
|
|
1108
|
+
cancelSaveViewForm,
|
|
1109
|
+
onSaveViewInputChange,
|
|
1110
|
+
onSaveViewInputKeyDown,
|
|
1111
|
+
attemptSave,
|
|
1112
|
+
confirmOverwrite,
|
|
1113
|
+
cancelOverwrite,
|
|
1114
|
+
loadSavedView,
|
|
1115
|
+
deleteSavedView,
|
|
1116
|
+
resetTableState
|
|
1117
|
+
} = useSavedViews({
|
|
1118
|
+
enableSavedViews,
|
|
1119
|
+
enableColumnResizing,
|
|
1120
|
+
defaultPageSize,
|
|
1121
|
+
query,
|
|
1122
|
+
setQuery,
|
|
1123
|
+
setFilters,
|
|
1124
|
+
columnWidths,
|
|
1125
|
+
setColumnWidths
|
|
1126
|
+
});
|
|
1127
|
+
useFocusTrap(confirmModalRef, {
|
|
1128
|
+
onEscape: () => setConfirmState(null),
|
|
1129
|
+
autoFocus: true
|
|
1130
|
+
});
|
|
1131
|
+
useFocusTrap(bulkConfirmModalRef, {
|
|
1132
|
+
onEscape: () => setBulkConfirmState(null),
|
|
1133
|
+
autoFocus: true
|
|
1134
|
+
});
|
|
291
1135
|
useEffect(() => {
|
|
292
1136
|
if (!enableFilters) return;
|
|
293
1137
|
const activeFilters = {};
|
|
@@ -295,52 +1139,17 @@ function RowaKitTable({
|
|
|
295
1139
|
for (const [field, value] of Object.entries(filters)) {
|
|
296
1140
|
if (value !== void 0) {
|
|
297
1141
|
activeFilters[field] = value;
|
|
298
|
-
hasFilters = true;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
const filtersToSend = hasFilters ? activeFilters : void 0;
|
|
302
|
-
setQuery((prev) => ({
|
|
303
|
-
...prev,
|
|
304
|
-
filters: filtersToSend,
|
|
305
|
-
page: 1
|
|
306
|
-
// Reset page to 1 when filters change
|
|
307
|
-
}));
|
|
308
|
-
}, [filters, enableFilters]);
|
|
309
|
-
useEffect(() => {
|
|
310
|
-
const currentRequestId = ++requestIdRef.current;
|
|
311
|
-
setDataState((prev) => ({ ...prev, state: "loading" }));
|
|
312
|
-
fetcher(query).then((result) => {
|
|
313
|
-
if (currentRequestId !== requestIdRef.current) {
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
if (result.items.length === 0) {
|
|
317
|
-
setDataState({
|
|
318
|
-
state: "empty",
|
|
319
|
-
items: [],
|
|
320
|
-
total: result.total
|
|
321
|
-
});
|
|
322
|
-
} else {
|
|
323
|
-
setDataState({
|
|
324
|
-
state: "success",
|
|
325
|
-
items: result.items,
|
|
326
|
-
total: result.total
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
}).catch((error) => {
|
|
330
|
-
if (currentRequestId !== requestIdRef.current) {
|
|
331
|
-
return;
|
|
1142
|
+
hasFilters = true;
|
|
332
1143
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
setQuery({ ...query });
|
|
343
|
-
};
|
|
1144
|
+
}
|
|
1145
|
+
const filtersToSend = hasFilters ? activeFilters : void 0;
|
|
1146
|
+
setQuery((prev) => ({
|
|
1147
|
+
...prev,
|
|
1148
|
+
filters: filtersToSend,
|
|
1149
|
+
page: 1
|
|
1150
|
+
// Reset page to 1 when filters change
|
|
1151
|
+
}));
|
|
1152
|
+
}, [filters, enableFilters]);
|
|
344
1153
|
const handlePreviousPage = () => {
|
|
345
1154
|
if (query.page > 1) {
|
|
346
1155
|
setQuery((prev) => ({ ...prev, page: prev.page - 1 }));
|
|
@@ -355,140 +1164,6 @@ function RowaKitTable({
|
|
|
355
1164
|
const handlePageSizeChange = (newPageSize) => {
|
|
356
1165
|
setQuery({ ...query, pageSize: newPageSize, page: 1 });
|
|
357
1166
|
};
|
|
358
|
-
const handleSort = (field) => {
|
|
359
|
-
setQuery((prev) => {
|
|
360
|
-
if (prev.sort?.field !== field) {
|
|
361
|
-
return { ...prev, sort: { field, direction: "asc" }, page: 1 };
|
|
362
|
-
}
|
|
363
|
-
if (prev.sort.direction === "asc") {
|
|
364
|
-
return { ...prev, sort: { field, direction: "desc" }, page: 1 };
|
|
365
|
-
}
|
|
366
|
-
const { sort: _sort, ...rest } = prev;
|
|
367
|
-
return { ...rest, page: 1 };
|
|
368
|
-
});
|
|
369
|
-
};
|
|
370
|
-
const getSortIndicator = (field) => {
|
|
371
|
-
if (!query.sort || query.sort.field !== field) {
|
|
372
|
-
return "";
|
|
373
|
-
}
|
|
374
|
-
return query.sort.direction === "asc" ? " \u2191" : " \u2193";
|
|
375
|
-
};
|
|
376
|
-
const scheduleColumnWidthUpdate = (colId, width) => {
|
|
377
|
-
resizePendingRef.current = { colId, width };
|
|
378
|
-
if (resizeRafRef.current != null) return;
|
|
379
|
-
resizeRafRef.current = requestAnimationFrame(() => {
|
|
380
|
-
resizeRafRef.current = null;
|
|
381
|
-
const pending = resizePendingRef.current;
|
|
382
|
-
if (!pending) return;
|
|
383
|
-
handleColumnResize(pending.colId, pending.width);
|
|
384
|
-
});
|
|
385
|
-
};
|
|
386
|
-
const handleColumnResize = (columnId, newWidth) => {
|
|
387
|
-
const minWidth = columns.find((c) => c.id === columnId)?.minWidth ?? 80;
|
|
388
|
-
const maxWidth = columns.find((c) => c.id === columnId)?.maxWidth;
|
|
389
|
-
let finalWidth = Math.max(minWidth, newWidth);
|
|
390
|
-
if (maxWidth) {
|
|
391
|
-
finalWidth = Math.min(finalWidth, maxWidth);
|
|
392
|
-
}
|
|
393
|
-
setColumnWidths((prev) => ({
|
|
394
|
-
...prev,
|
|
395
|
-
[columnId]: finalWidth
|
|
396
|
-
}));
|
|
397
|
-
};
|
|
398
|
-
const startColumnResize = (e, columnId) => {
|
|
399
|
-
e.preventDefault();
|
|
400
|
-
const startX = e.clientX;
|
|
401
|
-
const th = e.currentTarget.parentElement;
|
|
402
|
-
let startWidth = columnWidths[columnId] ?? th.offsetWidth;
|
|
403
|
-
const MIN_DRAG_WIDTH = 80;
|
|
404
|
-
if (startWidth < MIN_DRAG_WIDTH) {
|
|
405
|
-
const nextTh = th.nextElementSibling;
|
|
406
|
-
if (nextTh && nextTh.offsetWidth >= 50) {
|
|
407
|
-
startWidth = nextTh.offsetWidth;
|
|
408
|
-
} else {
|
|
409
|
-
startWidth = 100;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
document.body.classList.add("rowakit-resizing");
|
|
413
|
-
const handleMouseMove = (moveEvent) => {
|
|
414
|
-
const delta = moveEvent.clientX - startX;
|
|
415
|
-
const newWidth = startWidth + delta;
|
|
416
|
-
scheduleColumnWidthUpdate(columnId, newWidth);
|
|
417
|
-
};
|
|
418
|
-
const handleMouseUp = () => {
|
|
419
|
-
document.removeEventListener("mousemove", handleMouseMove);
|
|
420
|
-
document.removeEventListener("mouseup", handleMouseUp);
|
|
421
|
-
document.body.classList.remove("rowakit-resizing");
|
|
422
|
-
};
|
|
423
|
-
document.addEventListener("mousemove", handleMouseMove);
|
|
424
|
-
document.addEventListener("mouseup", handleMouseUp);
|
|
425
|
-
};
|
|
426
|
-
const handleColumnResizeDoubleClick = (columnId) => {
|
|
427
|
-
const tableEl = tableRef.current;
|
|
428
|
-
if (!tableEl) return;
|
|
429
|
-
const th = tableEl.querySelector(`th[data-col-id="${columnId}"]`);
|
|
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 }));
|
|
440
|
-
};
|
|
441
|
-
const saveCurrentView = (name) => {
|
|
442
|
-
const viewState = {
|
|
443
|
-
page: query.page,
|
|
444
|
-
pageSize: query.pageSize,
|
|
445
|
-
sort: query.sort,
|
|
446
|
-
filters: query.filters,
|
|
447
|
-
columnWidths: enableColumnResizing ? columnWidths : void 0
|
|
448
|
-
};
|
|
449
|
-
setSavedViews((prev) => {
|
|
450
|
-
const filtered = prev.filter((v) => v.name !== name);
|
|
451
|
-
return [...filtered, { name, state: viewState }];
|
|
452
|
-
});
|
|
453
|
-
if (typeof window !== "undefined" && window.localStorage) {
|
|
454
|
-
try {
|
|
455
|
-
localStorage.setItem(`rowakit-view-${name}`, JSON.stringify(viewState));
|
|
456
|
-
} catch {
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
};
|
|
460
|
-
const loadSavedView = (name) => {
|
|
461
|
-
const view = savedViews.find((v) => v.name === name);
|
|
462
|
-
if (!view) return;
|
|
463
|
-
const { state } = view;
|
|
464
|
-
setQuery({
|
|
465
|
-
page: state.page,
|
|
466
|
-
pageSize: state.pageSize,
|
|
467
|
-
sort: state.sort,
|
|
468
|
-
filters: state.filters
|
|
469
|
-
});
|
|
470
|
-
setFilters(state.filters ?? {});
|
|
471
|
-
if (state.columnWidths && enableColumnResizing) {
|
|
472
|
-
setColumnWidths(state.columnWidths);
|
|
473
|
-
}
|
|
474
|
-
};
|
|
475
|
-
const deleteSavedView = (name) => {
|
|
476
|
-
setSavedViews((prev) => prev.filter((v) => v.name !== name));
|
|
477
|
-
if (typeof window !== "undefined" && window.localStorage) {
|
|
478
|
-
try {
|
|
479
|
-
localStorage.removeItem(`rowakit-view-${name}`);
|
|
480
|
-
} catch {
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
};
|
|
484
|
-
const resetTableState = () => {
|
|
485
|
-
setQuery({
|
|
486
|
-
page: 1,
|
|
487
|
-
pageSize: defaultPageSize
|
|
488
|
-
});
|
|
489
|
-
setFilters({});
|
|
490
|
-
setColumnWidths({});
|
|
491
|
-
};
|
|
492
1167
|
const transformFilterValueForColumn = (column, value) => {
|
|
493
1168
|
if (!value || column?.kind !== "number") {
|
|
494
1169
|
return value;
|
|
@@ -533,29 +1208,100 @@ function RowaKitTable({
|
|
|
533
1208
|
const handleClearAllFilters = () => {
|
|
534
1209
|
setFilters({});
|
|
535
1210
|
};
|
|
536
|
-
const isLoading = dataState.state === "loading";
|
|
537
|
-
const isError = dataState.state === "error";
|
|
538
|
-
const isEmpty = dataState.state === "empty";
|
|
539
1211
|
const totalPages = Math.ceil(dataState.total / query.pageSize);
|
|
540
1212
|
const canGoPrevious = query.page > 1 && !isLoading;
|
|
541
1213
|
const canGoNext = query.page < totalPages && !isLoading;
|
|
1214
|
+
const tableColumnCount = columns.length + (enableRowSelection ? 1 : 0);
|
|
542
1215
|
const hasActiveFilters = enableFilters && Object.values(filters).some((v) => v !== void 0);
|
|
543
|
-
|
|
1216
|
+
const containerClass = [
|
|
1217
|
+
"rowakit-table",
|
|
1218
|
+
enableColumnResizing ? "rowakit-layout-fixed" : "",
|
|
1219
|
+
className
|
|
1220
|
+
].filter(Boolean).join(" ");
|
|
1221
|
+
return /* @__PURE__ */ jsxs("div", { className: containerClass, children: [
|
|
1222
|
+
enableRowSelection && bulkActions && bulkActions.length > 0 && selectedKeys.length > 0 && /* @__PURE__ */ jsx(
|
|
1223
|
+
BulkActionBar,
|
|
1224
|
+
{
|
|
1225
|
+
selectedCount: selectedKeys.length,
|
|
1226
|
+
actions: bulkActions,
|
|
1227
|
+
onActionClick: (actionId) => {
|
|
1228
|
+
const action = bulkActions.find((a) => a.id === actionId);
|
|
1229
|
+
if (!action) return;
|
|
1230
|
+
const snapshot = [...selectedKeys];
|
|
1231
|
+
if (action.confirm) {
|
|
1232
|
+
setBulkConfirmState({ action, selectedKeys: snapshot });
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
action.onClick(snapshot);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
),
|
|
1239
|
+
exporter && /* @__PURE__ */ jsx(ExportButton, { exporter, query }),
|
|
544
1240
|
enableSavedViews && /* @__PURE__ */ jsxs("div", { className: "rowakit-saved-views-group", children: [
|
|
545
|
-
/* @__PURE__ */ jsx(
|
|
1241
|
+
!showSaveViewForm ? /* @__PURE__ */ jsx(
|
|
546
1242
|
"button",
|
|
547
1243
|
{
|
|
548
|
-
onClick:
|
|
549
|
-
const name = typeof window !== "undefined" ? window.prompt("Enter view name:") : null;
|
|
550
|
-
if (name) {
|
|
551
|
-
saveCurrentView(name);
|
|
552
|
-
}
|
|
553
|
-
},
|
|
1244
|
+
onClick: openSaveViewForm,
|
|
554
1245
|
className: "rowakit-saved-view-button",
|
|
555
1246
|
type: "button",
|
|
556
1247
|
children: "Save View"
|
|
557
1248
|
}
|
|
558
|
-
),
|
|
1249
|
+
) : /* @__PURE__ */ jsx("div", { className: "rowakit-save-view-form", children: overwriteConfirmName ? /* @__PURE__ */ jsxs("div", { className: "rowakit-save-view-confirm", children: [
|
|
1250
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
1251
|
+
'View "',
|
|
1252
|
+
overwriteConfirmName,
|
|
1253
|
+
'" already exists. Overwrite?'
|
|
1254
|
+
] }),
|
|
1255
|
+
/* @__PURE__ */ jsx(
|
|
1256
|
+
"button",
|
|
1257
|
+
{
|
|
1258
|
+
onClick: confirmOverwrite,
|
|
1259
|
+
className: "rowakit-saved-view-button",
|
|
1260
|
+
type: "button",
|
|
1261
|
+
children: "Overwrite"
|
|
1262
|
+
}
|
|
1263
|
+
),
|
|
1264
|
+
/* @__PURE__ */ jsx(
|
|
1265
|
+
"button",
|
|
1266
|
+
{
|
|
1267
|
+
onClick: cancelOverwrite,
|
|
1268
|
+
className: "rowakit-saved-view-button",
|
|
1269
|
+
type: "button",
|
|
1270
|
+
children: "Cancel"
|
|
1271
|
+
}
|
|
1272
|
+
)
|
|
1273
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1274
|
+
/* @__PURE__ */ jsx(
|
|
1275
|
+
"input",
|
|
1276
|
+
{
|
|
1277
|
+
type: "text",
|
|
1278
|
+
value: saveViewInput,
|
|
1279
|
+
onChange: onSaveViewInputChange,
|
|
1280
|
+
onKeyDown: onSaveViewInputKeyDown,
|
|
1281
|
+
placeholder: "Enter view name...",
|
|
1282
|
+
className: "rowakit-save-view-input"
|
|
1283
|
+
}
|
|
1284
|
+
),
|
|
1285
|
+
saveViewError && /* @__PURE__ */ jsx("div", { className: "rowakit-save-view-error", children: saveViewError }),
|
|
1286
|
+
/* @__PURE__ */ jsx(
|
|
1287
|
+
"button",
|
|
1288
|
+
{
|
|
1289
|
+
onClick: attemptSave,
|
|
1290
|
+
className: "rowakit-saved-view-button",
|
|
1291
|
+
type: "button",
|
|
1292
|
+
children: "Save"
|
|
1293
|
+
}
|
|
1294
|
+
),
|
|
1295
|
+
/* @__PURE__ */ jsx(
|
|
1296
|
+
"button",
|
|
1297
|
+
{
|
|
1298
|
+
onClick: cancelSaveViewForm,
|
|
1299
|
+
className: "rowakit-saved-view-button",
|
|
1300
|
+
type: "button",
|
|
1301
|
+
children: "Cancel"
|
|
1302
|
+
}
|
|
1303
|
+
)
|
|
1304
|
+
] }) }),
|
|
559
1305
|
savedViews.map((view) => /* @__PURE__ */ jsxs("div", { className: "rowakit-saved-view-item", children: [
|
|
560
1306
|
/* @__PURE__ */ jsx(
|
|
561
1307
|
"button",
|
|
@@ -598,159 +1344,143 @@ function RowaKitTable({
|
|
|
598
1344
|
) }),
|
|
599
1345
|
/* @__PURE__ */ jsxs("table", { ref: tableRef, children: [
|
|
600
1346
|
/* @__PURE__ */ jsxs("thead", { children: [
|
|
601
|
-
/* @__PURE__ */
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
const isResizable = enableColumnResizing && column.kind !== "actions";
|
|
605
|
-
const actualWidth = columnWidths[column.id] ?? column.width;
|
|
606
|
-
return /* @__PURE__ */ jsxs(
|
|
607
|
-
"th",
|
|
1347
|
+
/* @__PURE__ */ jsxs("tr", { children: [
|
|
1348
|
+
enableRowSelection && /* @__PURE__ */ jsx(
|
|
1349
|
+
RowSelectionHeaderCell,
|
|
608
1350
|
{
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
1351
|
+
checked: headerChecked,
|
|
1352
|
+
indeterminate: headerIndeterminate,
|
|
1353
|
+
disabled: isLoading || pageRowKeys.length === 0,
|
|
1354
|
+
onChange: (checked) => {
|
|
1355
|
+
if (checked) {
|
|
1356
|
+
setSelectedKeys(selectAll(pageRowKeys));
|
|
1357
|
+
} else {
|
|
1358
|
+
setSelectedKeys(clearSelection());
|
|
617
1359
|
}
|
|
618
|
-
} : void 0,
|
|
619
|
-
"aria-sort": isSortable && query.sort?.field === String(field) ? query.sort.direction === "asc" ? "ascending" : "descending" : void 0,
|
|
620
|
-
style: {
|
|
621
|
-
width: actualWidth ? `${actualWidth}px` : void 0,
|
|
622
|
-
textAlign: column.align,
|
|
623
|
-
position: isResizable ? "relative" : void 0
|
|
624
|
-
},
|
|
625
|
-
className: column.truncate && !isResizable ? "rowakit-cell-truncate" : void 0,
|
|
626
|
-
children: [
|
|
627
|
-
getHeaderLabel(column),
|
|
628
|
-
isSortable && getSortIndicator(String(field)),
|
|
629
|
-
isResizable && /* @__PURE__ */ jsx(
|
|
630
|
-
"div",
|
|
631
|
-
{
|
|
632
|
-
className: "rowakit-column-resize-handle",
|
|
633
|
-
onMouseDown: (e) => startColumnResize(e, column.id),
|
|
634
|
-
onDoubleClick: () => handleColumnResizeDoubleClick(column.id),
|
|
635
|
-
title: "Drag to resize | Double-click to auto-fit content"
|
|
636
|
-
}
|
|
637
|
-
)
|
|
638
|
-
]
|
|
639
|
-
},
|
|
640
|
-
column.id
|
|
641
|
-
);
|
|
642
|
-
}) }),
|
|
643
|
-
enableFilters && /* @__PURE__ */ jsx("tr", { className: "rowakit-table-filter-row", children: columns.map((column) => {
|
|
644
|
-
const field = column.kind === "actions" || column.kind === "custom" ? "" : String(column.field);
|
|
645
|
-
const canFilter = field && column.kind !== "actions";
|
|
646
|
-
if (!canFilter) {
|
|
647
|
-
return /* @__PURE__ */ jsx("th", {}, column.id);
|
|
648
|
-
}
|
|
649
|
-
const filterValue = filters[field];
|
|
650
|
-
if (column.kind === "badge") {
|
|
651
|
-
const options = column.map ? Object.keys(column.map) : [];
|
|
652
|
-
return /* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsxs(
|
|
653
|
-
"select",
|
|
654
|
-
{
|
|
655
|
-
className: "rowakit-filter-select",
|
|
656
|
-
value: filterValue?.op === "equals" ? String(filterValue.value ?? "") : "",
|
|
657
|
-
onChange: (e) => {
|
|
658
|
-
const value = e.target.value;
|
|
659
|
-
if (value === "") {
|
|
660
|
-
handleClearFilter(field);
|
|
661
|
-
} else {
|
|
662
|
-
handleFilterChange(field, { op: "equals", value });
|
|
663
|
-
}
|
|
664
|
-
},
|
|
665
|
-
children: [
|
|
666
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "All" }),
|
|
667
|
-
options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt, children: opt }, opt))
|
|
668
|
-
]
|
|
669
1360
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
1361
|
+
}
|
|
1362
|
+
),
|
|
1363
|
+
columns.map((column) => {
|
|
1364
|
+
const isSortable = column.kind !== "actions" && (column.kind === "custom" ? false : column.sortable === true);
|
|
1365
|
+
const field = column.kind === "actions" ? "" : column.kind === "custom" ? column.field : column.field;
|
|
1366
|
+
const isResizable = enableColumnResizing && column.kind !== "actions";
|
|
1367
|
+
const actualWidth = columnWidths[column.id] ?? column.width;
|
|
1368
|
+
return /* @__PURE__ */ jsxs(
|
|
1369
|
+
"th",
|
|
675
1370
|
{
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
1371
|
+
"data-col-id": column.id,
|
|
1372
|
+
onClick: isSortable ? (e) => {
|
|
1373
|
+
if (isResizingRef.current) return;
|
|
1374
|
+
if (Date.now() - lastResizeEndTsRef.current < 150) return;
|
|
1375
|
+
const isMultiSort = e.ctrlKey || e.metaKey;
|
|
1376
|
+
handleSort(String(field), isMultiSort);
|
|
1377
|
+
} : void 0,
|
|
1378
|
+
role: isSortable ? "button" : void 0,
|
|
1379
|
+
tabIndex: isSortable ? 0 : void 0,
|
|
1380
|
+
onKeyDown: isSortable ? (e) => {
|
|
1381
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1382
|
+
e.preventDefault();
|
|
1383
|
+
const isMultiSort = e.shiftKey;
|
|
1384
|
+
handleSort(String(field), isMultiSort);
|
|
684
1385
|
}
|
|
1386
|
+
} : void 0,
|
|
1387
|
+
"aria-sort": isSortable && (query.sorts?.find((s) => s.field === String(field))?.priority === 0 || query.sort?.field === String(field)) ? (query.sorts?.find((s) => s.field === String(field))?.direction ?? query.sort?.direction) === "asc" ? "ascending" : "descending" : void 0,
|
|
1388
|
+
style: {
|
|
1389
|
+
width: actualWidth != null ? `${actualWidth}px` : void 0,
|
|
1390
|
+
textAlign: column.align,
|
|
1391
|
+
position: isResizable ? "relative" : void 0
|
|
685
1392
|
},
|
|
1393
|
+
className: [
|
|
1394
|
+
column.truncate ? "rowakit-cell-truncate" : "",
|
|
1395
|
+
resizingColIdRef.current === column.id ? "resizing" : ""
|
|
1396
|
+
// PRD-01
|
|
1397
|
+
].filter(Boolean).join(" ") || void 0,
|
|
686
1398
|
children: [
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
/* @__PURE__ */ jsx(
|
|
1399
|
+
getHeaderLabel(column),
|
|
1400
|
+
isSortable && getSortIndicator(String(field)),
|
|
1401
|
+
isResizable && /* @__PURE__ */ jsx(
|
|
1402
|
+
"div",
|
|
1403
|
+
{
|
|
1404
|
+
className: "rowakit-column-resize-handle",
|
|
1405
|
+
onPointerDown: (e) => startColumnResize(e, column.id),
|
|
1406
|
+
onDoubleClick: (e) => handleColumnResizeDoubleClick(e, column.id),
|
|
1407
|
+
title: "Drag to resize | Double-click to auto-fit content"
|
|
1408
|
+
}
|
|
1409
|
+
)
|
|
690
1410
|
]
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
1411
|
+
},
|
|
1412
|
+
column.id
|
|
1413
|
+
);
|
|
1414
|
+
})
|
|
1415
|
+
] }),
|
|
1416
|
+
enableFilters && /* @__PURE__ */ jsxs("tr", { className: "rowakit-table-filter-row", children: [
|
|
1417
|
+
enableRowSelection && /* @__PURE__ */ jsx("th", {}),
|
|
1418
|
+
columns.map((column) => {
|
|
1419
|
+
const field = column.kind === "actions" || column.kind === "custom" ? "" : String(column.field);
|
|
1420
|
+
const canFilter = field && column.kind !== "actions";
|
|
1421
|
+
if (!canFilter) {
|
|
1422
|
+
return /* @__PURE__ */ jsx("th", {}, column.id);
|
|
1423
|
+
}
|
|
1424
|
+
const filterValue = filters[field];
|
|
1425
|
+
if (column.kind === "badge") {
|
|
1426
|
+
const options = column.map ? Object.keys(column.map) : [];
|
|
1427
|
+
return /* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsxs(
|
|
1428
|
+
"select",
|
|
700
1429
|
{
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
placeholder: "From",
|
|
704
|
-
value: fromValue,
|
|
1430
|
+
className: "rowakit-filter-select",
|
|
1431
|
+
value: filterValue?.op === "equals" ? String(filterValue.value ?? "") : "",
|
|
705
1432
|
onChange: (e) => {
|
|
706
|
-
const
|
|
707
|
-
|
|
708
|
-
if (!from && !to) {
|
|
1433
|
+
const value = e.target.value;
|
|
1434
|
+
if (value === "") {
|
|
709
1435
|
handleClearFilter(field);
|
|
710
1436
|
} else {
|
|
711
|
-
handleFilterChange(field, { op: "
|
|
1437
|
+
handleFilterChange(field, { op: "equals", value });
|
|
712
1438
|
}
|
|
713
|
-
}
|
|
1439
|
+
},
|
|
1440
|
+
children: [
|
|
1441
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "All" }),
|
|
1442
|
+
options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt, children: opt }, opt))
|
|
1443
|
+
]
|
|
714
1444
|
}
|
|
715
|
-
),
|
|
716
|
-
|
|
717
|
-
|
|
1445
|
+
) }, column.id);
|
|
1446
|
+
}
|
|
1447
|
+
if (column.kind === "boolean") {
|
|
1448
|
+
return /* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsxs(
|
|
1449
|
+
"select",
|
|
718
1450
|
{
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
placeholder: "To",
|
|
722
|
-
value: toValue,
|
|
1451
|
+
className: "rowakit-filter-select",
|
|
1452
|
+
value: filterValue?.op === "equals" && typeof filterValue.value === "boolean" ? String(filterValue.value) : "",
|
|
723
1453
|
onChange: (e) => {
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
if (!from && !to) {
|
|
1454
|
+
const value = e.target.value;
|
|
1455
|
+
if (value === "") {
|
|
727
1456
|
handleClearFilter(field);
|
|
728
1457
|
} else {
|
|
729
|
-
handleFilterChange(field, { op: "
|
|
1458
|
+
handleFilterChange(field, { op: "equals", value: value === "true" });
|
|
730
1459
|
}
|
|
731
|
-
}
|
|
1460
|
+
},
|
|
1461
|
+
children: [
|
|
1462
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "All" }),
|
|
1463
|
+
/* @__PURE__ */ jsx("option", { value: "true", children: "True" }),
|
|
1464
|
+
/* @__PURE__ */ jsx("option", { value: "false", children: "False" })
|
|
1465
|
+
]
|
|
732
1466
|
}
|
|
733
|
-
)
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
const toValue = filterValue?.op === "range" ? String(filterValue.value.to ?? "") : "";
|
|
740
|
-
const showRangeUI = !filterValue || filterValue.op === "range";
|
|
741
|
-
if (showRangeUI) {
|
|
742
|
-
return /* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsxs("div", { className: "rowakit-filter-number-range", children: [
|
|
1467
|
+
) }, column.id);
|
|
1468
|
+
}
|
|
1469
|
+
if (column.kind === "date") {
|
|
1470
|
+
const fromValue = filterValue?.op === "range" ? filterValue.value.from ?? "" : "";
|
|
1471
|
+
const toValue = filterValue?.op === "range" ? filterValue.value.to ?? "" : "";
|
|
1472
|
+
return /* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsxs("div", { className: "rowakit-filter-date-range", children: [
|
|
743
1473
|
/* @__PURE__ */ jsx(
|
|
744
1474
|
"input",
|
|
745
1475
|
{
|
|
746
|
-
type: "
|
|
1476
|
+
type: "date",
|
|
747
1477
|
className: "rowakit-filter-input",
|
|
748
|
-
placeholder: "
|
|
1478
|
+
placeholder: "From",
|
|
749
1479
|
value: fromValue,
|
|
750
1480
|
onChange: (e) => {
|
|
751
|
-
const from = e.target.value
|
|
752
|
-
const to = toValue
|
|
753
|
-
if (from
|
|
1481
|
+
const from = e.target.value || void 0;
|
|
1482
|
+
const to = toValue || void 0;
|
|
1483
|
+
if (!from && !to) {
|
|
754
1484
|
handleClearFilter(field);
|
|
755
1485
|
} else {
|
|
756
1486
|
handleFilterChange(field, { op: "range", value: { from, to } });
|
|
@@ -761,14 +1491,14 @@ function RowaKitTable({
|
|
|
761
1491
|
/* @__PURE__ */ jsx(
|
|
762
1492
|
"input",
|
|
763
1493
|
{
|
|
764
|
-
type: "
|
|
1494
|
+
type: "date",
|
|
765
1495
|
className: "rowakit-filter-input",
|
|
766
|
-
placeholder: "
|
|
1496
|
+
placeholder: "To",
|
|
767
1497
|
value: toValue,
|
|
768
1498
|
onChange: (e) => {
|
|
769
|
-
const to = e.target.value
|
|
770
|
-
const from = fromValue
|
|
771
|
-
if (from
|
|
1499
|
+
const to = e.target.value || void 0;
|
|
1500
|
+
const from = fromValue || void 0;
|
|
1501
|
+
if (!from && !to) {
|
|
772
1502
|
handleClearFilter(field);
|
|
773
1503
|
} else {
|
|
774
1504
|
handleFilterChange(field, { op: "range", value: { from, to } });
|
|
@@ -778,39 +1508,85 @@ function RowaKitTable({
|
|
|
778
1508
|
)
|
|
779
1509
|
] }) }, column.id);
|
|
780
1510
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
1511
|
+
const isNumberColumn = column.kind === "number";
|
|
1512
|
+
if (isNumberColumn) {
|
|
1513
|
+
const fromValue = filterValue?.op === "range" ? String(filterValue.value.from ?? "") : filterValue?.op === "equals" && typeof filterValue.value === "number" ? String(filterValue.value) : "";
|
|
1514
|
+
const toValue = filterValue?.op === "range" ? String(filterValue.value.to ?? "") : "";
|
|
1515
|
+
const showRangeUI = !filterValue || filterValue.op === "range";
|
|
1516
|
+
if (showRangeUI) {
|
|
1517
|
+
return /* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsxs("div", { className: "rowakit-filter-number-range", children: [
|
|
1518
|
+
/* @__PURE__ */ jsx(
|
|
1519
|
+
"input",
|
|
1520
|
+
{
|
|
1521
|
+
type: "number",
|
|
1522
|
+
className: "rowakit-filter-input",
|
|
1523
|
+
placeholder: "Min",
|
|
1524
|
+
value: fromValue,
|
|
1525
|
+
onChange: (e) => {
|
|
1526
|
+
const from = e.target.value ? Number(e.target.value) : void 0;
|
|
1527
|
+
const to = toValue ? Number(toValue) : void 0;
|
|
1528
|
+
if (from === void 0 && to === void 0) {
|
|
1529
|
+
handleClearFilter(field);
|
|
1530
|
+
} else {
|
|
1531
|
+
handleFilterChange(field, { op: "range", value: { from, to } });
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
),
|
|
1536
|
+
/* @__PURE__ */ jsx(
|
|
1537
|
+
"input",
|
|
1538
|
+
{
|
|
1539
|
+
type: "number",
|
|
1540
|
+
className: "rowakit-filter-input",
|
|
1541
|
+
placeholder: "Max",
|
|
1542
|
+
value: toValue,
|
|
1543
|
+
onChange: (e) => {
|
|
1544
|
+
const to = e.target.value ? Number(e.target.value) : void 0;
|
|
1545
|
+
const from = fromValue ? Number(fromValue) : void 0;
|
|
1546
|
+
if (from === void 0 && to === void 0) {
|
|
1547
|
+
handleClearFilter(field);
|
|
1548
|
+
} else {
|
|
1549
|
+
handleFilterChange(field, { op: "range", value: { from, to } });
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
)
|
|
1554
|
+
] }) }, column.id);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
return /* @__PURE__ */ jsx("th", { children: /* @__PURE__ */ jsx(
|
|
1558
|
+
"input",
|
|
1559
|
+
{
|
|
1560
|
+
type: isNumberColumn ? "number" : "text",
|
|
1561
|
+
className: "rowakit-filter-input",
|
|
1562
|
+
placeholder: `Filter ${getHeaderLabel(column)}...`,
|
|
1563
|
+
value: filterValue?.op === "contains" ? filterValue.value : filterValue?.op === "equals" && typeof filterValue.value === "string" ? filterValue.value : filterValue?.op === "equals" && typeof filterValue.value === "number" ? String(filterValue.value) : "",
|
|
1564
|
+
onChange: (e) => {
|
|
1565
|
+
const rawValue = e.target.value;
|
|
1566
|
+
if (rawValue === "") {
|
|
798
1567
|
handleClearFilter(field);
|
|
1568
|
+
} else if (isNumberColumn) {
|
|
1569
|
+
const numValue = Number(rawValue);
|
|
1570
|
+
if (!isNaN(numValue)) {
|
|
1571
|
+
handleFilterChange(field, { op: "equals", value: numValue });
|
|
1572
|
+
} else {
|
|
1573
|
+
handleClearFilter(field);
|
|
1574
|
+
}
|
|
1575
|
+
} else {
|
|
1576
|
+
handleFilterChange(field, { op: "contains", value: rawValue });
|
|
799
1577
|
}
|
|
800
|
-
} else {
|
|
801
|
-
handleFilterChange(field, { op: "contains", value: rawValue });
|
|
802
1578
|
}
|
|
803
1579
|
}
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
|
|
1580
|
+
) }, column.id);
|
|
1581
|
+
})
|
|
1582
|
+
] })
|
|
807
1583
|
] }),
|
|
808
1584
|
/* @__PURE__ */ jsxs("tbody", { children: [
|
|
809
|
-
isLoading && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsxs("td", { colSpan:
|
|
1585
|
+
isLoading && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsxs("td", { colSpan: tableColumnCount, className: "rowakit-table-loading", children: [
|
|
810
1586
|
/* @__PURE__ */ jsx("div", { className: "rowakit-table-loading-spinner" }),
|
|
811
1587
|
/* @__PURE__ */ jsx("span", { children: "Loading..." })
|
|
812
1588
|
] }) }),
|
|
813
|
-
isError && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsxs("td", { colSpan:
|
|
1589
|
+
isError && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsxs("td", { colSpan: tableColumnCount, className: "rowakit-table-error", children: [
|
|
814
1590
|
/* @__PURE__ */ jsx("div", { className: "rowakit-table-error-message", children: dataState.error ?? "An error occurred" }),
|
|
815
1591
|
/* @__PURE__ */ jsx(
|
|
816
1592
|
"button",
|
|
@@ -822,29 +1598,42 @@ function RowaKitTable({
|
|
|
822
1598
|
}
|
|
823
1599
|
)
|
|
824
1600
|
] }) }),
|
|
825
|
-
isEmpty && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan:
|
|
1601
|
+
isEmpty && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: tableColumnCount, className: "rowakit-table-empty", children: "No data" }) }),
|
|
826
1602
|
dataState.state === "success" && dataState.items.map((row) => {
|
|
827
1603
|
const key = getRowKey(row, rowKey);
|
|
828
|
-
return /* @__PURE__ */
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
column.truncate ? "rowakit-cell-truncate" : ""
|
|
832
|
-
].filter(Boolean).join(" ") || void 0;
|
|
833
|
-
const actualWidth = columnWidths[column.id];
|
|
834
|
-
return /* @__PURE__ */ jsx(
|
|
835
|
-
"td",
|
|
1604
|
+
return /* @__PURE__ */ jsxs("tr", { children: [
|
|
1605
|
+
enableRowSelection && /* @__PURE__ */ jsx(
|
|
1606
|
+
RowSelectionCell,
|
|
836
1607
|
{
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
1608
|
+
rowKey: key,
|
|
1609
|
+
disabled: isLoading,
|
|
1610
|
+
checked: selectedKeys.includes(key),
|
|
1611
|
+
onChange: () => {
|
|
1612
|
+
setSelectedKeys((prev) => toggleSelectionKey(prev, key));
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
),
|
|
1616
|
+
columns.map((column) => {
|
|
1617
|
+
const cellClass = [
|
|
1618
|
+
column.kind === "number" ? "rowakit-cell-number" : "",
|
|
1619
|
+
column.truncate ? "rowakit-cell-truncate" : ""
|
|
1620
|
+
].filter(Boolean).join(" ") || void 0;
|
|
1621
|
+
const actualWidth = columnWidths[column.id] ?? column.width;
|
|
1622
|
+
return /* @__PURE__ */ jsx(
|
|
1623
|
+
"td",
|
|
1624
|
+
{
|
|
1625
|
+
"data-col-id": column.id,
|
|
1626
|
+
className: cellClass,
|
|
1627
|
+
style: {
|
|
1628
|
+
width: actualWidth != null ? `${actualWidth}px` : void 0,
|
|
1629
|
+
textAlign: column.align || (column.kind === "number" ? "right" : void 0)
|
|
1630
|
+
},
|
|
1631
|
+
children: renderCell(column, row, isLoading, setConfirmState)
|
|
842
1632
|
},
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
}) }, key);
|
|
1633
|
+
column.id
|
|
1634
|
+
);
|
|
1635
|
+
})
|
|
1636
|
+
] }, key);
|
|
848
1637
|
})
|
|
849
1638
|
] })
|
|
850
1639
|
] }),
|
|
@@ -899,6 +1688,7 @@ function RowaKitTable({
|
|
|
899
1688
|
confirmState && /* @__PURE__ */ jsx(
|
|
900
1689
|
"div",
|
|
901
1690
|
{
|
|
1691
|
+
ref: confirmModalRef,
|
|
902
1692
|
className: "rowakit-modal-backdrop",
|
|
903
1693
|
onClick: () => setConfirmState(null),
|
|
904
1694
|
role: "dialog",
|
|
@@ -936,13 +1726,51 @@ function RowaKitTable({
|
|
|
936
1726
|
] })
|
|
937
1727
|
] })
|
|
938
1728
|
}
|
|
1729
|
+
),
|
|
1730
|
+
bulkConfirmState && /* @__PURE__ */ jsx(
|
|
1731
|
+
"div",
|
|
1732
|
+
{
|
|
1733
|
+
ref: bulkConfirmModalRef,
|
|
1734
|
+
className: "rowakit-modal-backdrop",
|
|
1735
|
+
onClick: () => setBulkConfirmState(null),
|
|
1736
|
+
role: "dialog",
|
|
1737
|
+
"aria-modal": "true",
|
|
1738
|
+
"aria-labelledby": "bulk-confirm-dialog-title",
|
|
1739
|
+
children: /* @__PURE__ */ jsxs("div", { className: "rowakit-modal", onClick: (e) => e.stopPropagation(), children: [
|
|
1740
|
+
/* @__PURE__ */ jsx("h2", { id: "bulk-confirm-dialog-title", className: "rowakit-modal-title", children: bulkConfirmState.action.confirm?.title ?? "Confirm Action" }),
|
|
1741
|
+
/* @__PURE__ */ jsx("p", { className: "rowakit-modal-content", children: bulkConfirmState.action.confirm?.description ?? "Are you sure you want to perform this action? This action cannot be undone." }),
|
|
1742
|
+
/* @__PURE__ */ jsxs("div", { className: "rowakit-modal-actions", children: [
|
|
1743
|
+
/* @__PURE__ */ jsx(
|
|
1744
|
+
"button",
|
|
1745
|
+
{
|
|
1746
|
+
onClick: () => setBulkConfirmState(null),
|
|
1747
|
+
className: "rowakit-button rowakit-button-secondary",
|
|
1748
|
+
type: "button",
|
|
1749
|
+
children: "Cancel"
|
|
1750
|
+
}
|
|
1751
|
+
),
|
|
1752
|
+
/* @__PURE__ */ jsx(
|
|
1753
|
+
"button",
|
|
1754
|
+
{
|
|
1755
|
+
onClick: () => {
|
|
1756
|
+
bulkConfirmState.action.onClick(bulkConfirmState.selectedKeys);
|
|
1757
|
+
setBulkConfirmState(null);
|
|
1758
|
+
},
|
|
1759
|
+
className: "rowakit-button rowakit-button-danger",
|
|
1760
|
+
type: "button",
|
|
1761
|
+
children: "Confirm"
|
|
1762
|
+
}
|
|
1763
|
+
)
|
|
1764
|
+
] })
|
|
1765
|
+
] })
|
|
1766
|
+
}
|
|
939
1767
|
)
|
|
940
1768
|
] });
|
|
941
1769
|
}
|
|
942
1770
|
var SmartTable = RowaKitTable;
|
|
943
1771
|
|
|
944
1772
|
// src/index.ts
|
|
945
|
-
var VERSION = "0.
|
|
1773
|
+
var VERSION = "0.5.0" ;
|
|
946
1774
|
|
|
947
1775
|
export { RowaKitTable, SmartTable, VERSION, col };
|
|
948
1776
|
//# sourceMappingURL=index.js.map
|