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