@makolabs/ripple 1.2.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/elements/pagination/Pagination.svelte +464 -0
- package/dist/elements/pagination/Pagination.svelte.d.ts +57 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +1 -0
- package/dist/layout/sidebar/Sidebar.svelte +0 -1
- package/dist/layout/table/Table.svelte +471 -588
- package/dist/layout/table/table.js +9 -9
- package/package.json +1 -1
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
import { cn } from '../../helper/cls.js';
|
|
3
3
|
import { table } from './table.js';
|
|
4
4
|
import type { TableProps, SortDirection, SortState } from '../../index.js';
|
|
5
|
+
import Pagination from '../../elements/pagination/Pagination.svelte';
|
|
6
|
+
import Card from '../../layout/card/Card.svelte';
|
|
5
7
|
|
|
6
8
|
let {
|
|
7
9
|
data = [],
|
|
8
10
|
columns = [],
|
|
9
|
-
bordered
|
|
11
|
+
bordered: externalBordered,
|
|
10
12
|
striped = false,
|
|
11
13
|
pageSize = 10,
|
|
12
14
|
currentPage: externalCurrentPage,
|
|
@@ -36,9 +38,22 @@
|
|
|
36
38
|
showPageSize = false,
|
|
37
39
|
pageSizeOptions = [5, 10, 25, 50, 100],
|
|
38
40
|
paginationPosition = 'bottom',
|
|
39
|
-
paginationTemplate = 'full'
|
|
41
|
+
paginationTemplate = 'full',
|
|
42
|
+
title,
|
|
43
|
+
subtitle,
|
|
44
|
+
headerActions
|
|
40
45
|
}: TableProps<any> = $props();
|
|
41
46
|
|
|
47
|
+
// Determine if we should use Card wrapper
|
|
48
|
+
const hasHeader = $derived(title !== undefined || subtitle !== undefined);
|
|
49
|
+
|
|
50
|
+
// Border logic:
|
|
51
|
+
// - If title/subtitle provided: force bordered=false (no borders when in Card)
|
|
52
|
+
// - If no title/subtitle: use externalBordered prop, defaulting to true
|
|
53
|
+
const bordered = $derived(
|
|
54
|
+
hasHeader ? false : externalBordered !== undefined ? externalBordered : true
|
|
55
|
+
);
|
|
56
|
+
|
|
42
57
|
let sortColumn = $state('');
|
|
43
58
|
let sortDirection = $state<SortDirection>(null);
|
|
44
59
|
let internalCurrentPage = $state(externalCurrentPage || 1);
|
|
@@ -160,631 +175,499 @@
|
|
|
160
175
|
onrowclick?.(row, index);
|
|
161
176
|
}
|
|
162
177
|
|
|
163
|
-
function
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
onpagechange?.(internalCurrentPage);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function goToLastPage() {
|
|
171
|
-
if (internalCurrentPage !== totalPages) {
|
|
172
|
-
internalCurrentPage = totalPages;
|
|
173
|
-
onpagechange?.(internalCurrentPage);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function nextPage() {
|
|
178
|
-
if (internalCurrentPage < totalPages) {
|
|
179
|
-
internalCurrentPage++;
|
|
180
|
-
onpagechange?.(internalCurrentPage);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function prevPage() {
|
|
185
|
-
if (internalCurrentPage > 1) {
|
|
186
|
-
internalCurrentPage--;
|
|
187
|
-
onpagechange?.(internalCurrentPage);
|
|
188
|
-
}
|
|
178
|
+
function handlePageChange(page: number) {
|
|
179
|
+
internalCurrentPage = page;
|
|
180
|
+
onpagechange?.(page);
|
|
189
181
|
}
|
|
190
182
|
|
|
191
|
-
function
|
|
192
|
-
if (page >= 1 && page <= totalPages && page !== internalCurrentPage) {
|
|
193
|
-
internalCurrentPage = page;
|
|
194
|
-
onpagechange?.(internalCurrentPage);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function handlePageSizeChange(event: Event) {
|
|
199
|
-
const select = event.target as HTMLSelectElement;
|
|
200
|
-
const newPageSize = parseInt(select.value, 10);
|
|
183
|
+
function handlePageSizeChange(newPageSize: number) {
|
|
201
184
|
internalPageSize = newPageSize;
|
|
202
185
|
|
|
203
186
|
// Adjust current page if it would exceed the new total pages
|
|
204
187
|
const newTotalPages = Math.ceil(effectiveTotalItems / newPageSize);
|
|
205
188
|
if (internalCurrentPage > newTotalPages) {
|
|
206
189
|
internalCurrentPage = newTotalPages || 1;
|
|
190
|
+
onpagechange?.(internalCurrentPage);
|
|
207
191
|
}
|
|
208
192
|
|
|
209
193
|
onpagesizechange?.(newPageSize);
|
|
210
|
-
onpagechange?.(internalCurrentPage);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function getPageNumbers(): number[] {
|
|
214
|
-
const pages: number[] = [];
|
|
215
|
-
const maxPages = 5;
|
|
216
|
-
const halfMax = Math.floor(maxPages / 2);
|
|
217
|
-
|
|
218
|
-
let start = Math.max(1, internalCurrentPage - halfMax);
|
|
219
|
-
let end = Math.min(totalPages, start + maxPages - 1);
|
|
220
|
-
|
|
221
|
-
if (end - start + 1 < maxPages) {
|
|
222
|
-
start = Math.max(1, end - maxPages + 1);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
for (let i = start; i <= end; i++) {
|
|
226
|
-
pages.push(i);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return pages;
|
|
230
194
|
}
|
|
231
195
|
</script>
|
|
232
196
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
<
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
</select>
|
|
251
|
-
<span class="text-default-500 text-sm">entries</span>
|
|
197
|
+
{#if hasHeader}
|
|
198
|
+
<Card>
|
|
199
|
+
{#snippet custom()}
|
|
200
|
+
<!-- Header Section -->
|
|
201
|
+
<div class="mb-4 border-b border-gray-200 pb-3">
|
|
202
|
+
<div class="flex items-center justify-between">
|
|
203
|
+
<div>
|
|
204
|
+
{#if title}
|
|
205
|
+
<h2 class="text-lg font-semibold text-gray-900">{title}</h2>
|
|
206
|
+
{/if}
|
|
207
|
+
{#if subtitle}
|
|
208
|
+
<p class="mt-1 text-xs text-gray-500">{subtitle}</p>
|
|
209
|
+
{/if}
|
|
210
|
+
</div>
|
|
211
|
+
{#if headerActions}
|
|
212
|
+
<div class="flex items-center">
|
|
213
|
+
{@render headerActions()}
|
|
252
214
|
</div>
|
|
253
215
|
{/if}
|
|
254
|
-
<span class="text-default-500 text-sm">
|
|
255
|
-
Showing {Math.min(
|
|
256
|
-
(internalCurrentPage - 1) * internalPageSize + 1,
|
|
257
|
-
effectiveTotalItems
|
|
258
|
-
)}
|
|
259
|
-
to {Math.min(internalCurrentPage * internalPageSize, effectiveTotalItems)} of {effectiveTotalItems}
|
|
260
|
-
entries
|
|
261
|
-
</span>
|
|
262
|
-
</div>
|
|
263
|
-
|
|
264
|
-
<div class="flex items-center gap-1">
|
|
265
|
-
{#if paginationTemplate === 'full'}
|
|
266
|
-
<!-- First page button -->
|
|
267
|
-
<button
|
|
268
|
-
type="button"
|
|
269
|
-
class={cn(
|
|
270
|
-
'relative inline-flex items-center rounded-md px-2 py-1 text-sm font-medium',
|
|
271
|
-
internalCurrentPage === 1
|
|
272
|
-
? 'text-default-300 cursor-not-allowed'
|
|
273
|
-
: 'text-default-700 hover:bg-default-100'
|
|
274
|
-
)}
|
|
275
|
-
onclick={goToFirstPage}
|
|
276
|
-
disabled={internalCurrentPage === 1}
|
|
277
|
-
aria-label="First page"
|
|
278
|
-
>
|
|
279
|
-
<!-- Double Chevron Left SVG -->
|
|
280
|
-
<svg
|
|
281
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
282
|
-
width="16"
|
|
283
|
-
height="16"
|
|
284
|
-
viewBox="0 0 24 24"
|
|
285
|
-
fill="none"
|
|
286
|
-
stroke="currentColor"
|
|
287
|
-
stroke-width="2"
|
|
288
|
-
stroke-linecap="round"
|
|
289
|
-
stroke-linejoin="round"
|
|
290
|
-
class="h-4 w-4"
|
|
291
|
-
>
|
|
292
|
-
<path d="m11 17-5-5 5-5"></path>
|
|
293
|
-
<path d="m18 17-5-5 5-5"></path>
|
|
294
|
-
</svg>
|
|
295
|
-
</button>
|
|
296
|
-
{/if}
|
|
297
|
-
|
|
298
|
-
<!-- Previous page button -->
|
|
299
|
-
<button
|
|
300
|
-
type="button"
|
|
301
|
-
class={cn(
|
|
302
|
-
'relative inline-flex items-center rounded-md px-2 py-1 text-sm font-medium',
|
|
303
|
-
internalCurrentPage === 1
|
|
304
|
-
? 'text-default-300 cursor-not-allowed'
|
|
305
|
-
: 'text-default-700 hover:bg-default-100'
|
|
306
|
-
)}
|
|
307
|
-
onclick={prevPage}
|
|
308
|
-
disabled={internalCurrentPage === 1}
|
|
309
|
-
aria-label="Previous page"
|
|
310
|
-
>
|
|
311
|
-
<!-- Chevron Left SVG -->
|
|
312
|
-
<svg
|
|
313
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
314
|
-
width="16"
|
|
315
|
-
height="16"
|
|
316
|
-
viewBox="0 0 24 24"
|
|
317
|
-
fill="none"
|
|
318
|
-
stroke="currentColor"
|
|
319
|
-
stroke-width="2"
|
|
320
|
-
stroke-linecap="round"
|
|
321
|
-
stroke-linejoin="round"
|
|
322
|
-
class="h-4 w-4"
|
|
323
|
-
>
|
|
324
|
-
<path d="m15 18-6-6 6-6"></path>
|
|
325
|
-
</svg>
|
|
326
|
-
</button>
|
|
327
|
-
|
|
328
|
-
<!-- Page numbers -->
|
|
329
|
-
{#if paginationTemplate === 'full'}
|
|
330
|
-
{#each getPageNumbers() as pageNum}
|
|
331
|
-
<button
|
|
332
|
-
type="button"
|
|
333
|
-
class={cn(
|
|
334
|
-
'relative inline-flex items-center rounded-md px-3 py-1 text-sm font-medium',
|
|
335
|
-
internalCurrentPage === pageNum
|
|
336
|
-
? 'bg-primary-100 text-primary-700'
|
|
337
|
-
: 'text-default-700 hover:bg-default-100'
|
|
338
|
-
)}
|
|
339
|
-
onclick={() => goToPage(pageNum)}
|
|
340
|
-
aria-label={`Page ${pageNum}`}
|
|
341
|
-
aria-current={internalCurrentPage === pageNum ? 'page' : undefined}
|
|
342
|
-
>
|
|
343
|
-
{pageNum}
|
|
344
|
-
</button>
|
|
345
|
-
{/each}
|
|
346
|
-
{:else}
|
|
347
|
-
<span class="text-default-500 px-2 text-sm">
|
|
348
|
-
Page {internalCurrentPage} of {totalPages}
|
|
349
|
-
</span>
|
|
350
|
-
{/if}
|
|
351
|
-
|
|
352
|
-
<!-- Next page button -->
|
|
353
|
-
<button
|
|
354
|
-
type="button"
|
|
355
|
-
class={cn(
|
|
356
|
-
'relative inline-flex items-center rounded-md px-2 py-1 text-sm font-medium',
|
|
357
|
-
internalCurrentPage === totalPages
|
|
358
|
-
? 'text-default-300 cursor-not-allowed'
|
|
359
|
-
: 'text-default-700 hover:bg-default-100'
|
|
360
|
-
)}
|
|
361
|
-
onclick={nextPage}
|
|
362
|
-
disabled={internalCurrentPage === totalPages}
|
|
363
|
-
aria-label="Next page"
|
|
364
|
-
>
|
|
365
|
-
<!-- Chevron Right SVG -->
|
|
366
|
-
<svg
|
|
367
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
368
|
-
width="16"
|
|
369
|
-
height="16"
|
|
370
|
-
viewBox="0 0 24 24"
|
|
371
|
-
fill="none"
|
|
372
|
-
stroke="currentColor"
|
|
373
|
-
stroke-width="2"
|
|
374
|
-
stroke-linecap="round"
|
|
375
|
-
stroke-linejoin="round"
|
|
376
|
-
class="h-4 w-4"
|
|
377
|
-
>
|
|
378
|
-
<path d="m9 18 6-6-6-6"></path>
|
|
379
|
-
</svg>
|
|
380
|
-
</button>
|
|
381
|
-
|
|
382
|
-
{#if paginationTemplate === 'full'}
|
|
383
|
-
<!-- Last page button -->
|
|
384
|
-
<button
|
|
385
|
-
type="button"
|
|
386
|
-
class={cn(
|
|
387
|
-
'relative inline-flex items-center rounded-md px-2 py-1 text-sm font-medium',
|
|
388
|
-
internalCurrentPage === totalPages
|
|
389
|
-
? 'text-default-300 cursor-not-allowed'
|
|
390
|
-
: 'text-default-700 hover:bg-default-100'
|
|
391
|
-
)}
|
|
392
|
-
onclick={goToLastPage}
|
|
393
|
-
disabled={internalCurrentPage === totalPages}
|
|
394
|
-
aria-label="Last page"
|
|
395
|
-
>
|
|
396
|
-
<!-- Double Chevron Right SVG -->
|
|
397
|
-
<svg
|
|
398
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
399
|
-
width="16"
|
|
400
|
-
height="16"
|
|
401
|
-
viewBox="0 0 24 24"
|
|
402
|
-
fill="none"
|
|
403
|
-
stroke="currentColor"
|
|
404
|
-
stroke-width="2"
|
|
405
|
-
stroke-linecap="round"
|
|
406
|
-
stroke-linejoin="round"
|
|
407
|
-
class="h-4 w-4"
|
|
408
|
-
>
|
|
409
|
-
<path d="m13 17 5-5-5-5"></path>
|
|
410
|
-
<path d="m6 17 5-5-5-5"></path>
|
|
411
|
-
</svg>
|
|
412
|
-
</button>
|
|
413
|
-
{/if}
|
|
414
216
|
</div>
|
|
415
217
|
</div>
|
|
416
|
-
</div>
|
|
417
|
-
{/if}
|
|
418
|
-
|
|
419
|
-
<div class={wrapperClasses}>
|
|
420
|
-
<table class={tableClasses}>
|
|
421
|
-
<thead class={theadClasses}>
|
|
422
|
-
<tr>
|
|
423
|
-
{#if selectable}
|
|
424
|
-
<th class={cn(thClasses, 'text-center')}>
|
|
425
|
-
<input
|
|
426
|
-
type="checkbox"
|
|
427
|
-
onchange={() => {
|
|
428
|
-
if (selected.length === data.length) {
|
|
429
|
-
selected = [];
|
|
430
|
-
} else {
|
|
431
|
-
selected = [...data];
|
|
432
|
-
}
|
|
433
|
-
onselect(selected);
|
|
434
|
-
}}
|
|
435
|
-
checked={selected.length === data.length && data.length > 0}
|
|
436
|
-
aria-label="Select all rows"
|
|
437
|
-
/>
|
|
438
|
-
</th>
|
|
439
|
-
{/if}
|
|
440
218
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
{
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
219
|
+
<!-- Table Content -->
|
|
220
|
+
<div class={baseClasses}>
|
|
221
|
+
{#if showPaginationControls && (paginationPosition === 'top' || paginationPosition === 'both')}
|
|
222
|
+
<div class={footerClasses}>
|
|
223
|
+
<Pagination
|
|
224
|
+
currentPage={internalCurrentPage}
|
|
225
|
+
totalItems={effectiveTotalItems}
|
|
226
|
+
pageSize={internalPageSize}
|
|
227
|
+
onPageChange={handlePageChange}
|
|
228
|
+
onPageSizeChange={handlePageSizeChange}
|
|
229
|
+
{showPageSize}
|
|
230
|
+
{pageSizeOptions}
|
|
231
|
+
template={paginationTemplate === 'full' ? 'full' : 'compact'}
|
|
232
|
+
disabled={loading}
|
|
233
|
+
class={cn(paginationClasses, paginationClass)}
|
|
234
|
+
/>
|
|
235
|
+
</div>
|
|
236
|
+
{/if}
|
|
237
|
+
|
|
238
|
+
<div class={wrapperClasses}>
|
|
239
|
+
<table class={tableClasses}>
|
|
240
|
+
<thead class={theadClasses}>
|
|
241
|
+
<tr>
|
|
242
|
+
{#if selectable}
|
|
243
|
+
<th class={cn(thClasses, 'text-center')}>
|
|
244
|
+
<input
|
|
245
|
+
type="checkbox"
|
|
246
|
+
onchange={() => {
|
|
247
|
+
if (selected.length === data.length) {
|
|
248
|
+
selected = [];
|
|
249
|
+
} else {
|
|
250
|
+
selected = [...data];
|
|
251
|
+
}
|
|
252
|
+
onselect(selected);
|
|
253
|
+
}}
|
|
254
|
+
checked={selected.length === data.length && data.length > 0}
|
|
255
|
+
aria-label="Select all rows"
|
|
256
|
+
/>
|
|
257
|
+
</th>
|
|
258
|
+
{/if}
|
|
259
|
+
|
|
260
|
+
{#each columns as column (column.key)}
|
|
261
|
+
<th
|
|
262
|
+
class={cn(
|
|
263
|
+
thClasses,
|
|
264
|
+
column.align === 'center' && 'text-center',
|
|
265
|
+
column.align === 'right' && 'text-right',
|
|
266
|
+
column.class
|
|
267
|
+
)}
|
|
268
|
+
style={column.width ? `width: ${column.width}` : undefined}
|
|
269
|
+
>
|
|
270
|
+
{#if column.sortable}
|
|
271
|
+
<button
|
|
272
|
+
type="button"
|
|
273
|
+
class={sortButtonBaseClass()}
|
|
274
|
+
onclick={() => toggleSort(column.sortKey || column.key)}
|
|
275
|
+
aria-label={`Sort by ${column.header}`}
|
|
276
|
+
>
|
|
277
|
+
{column.header}
|
|
278
|
+
<span class={sortIconBaseClass()}>
|
|
279
|
+
{#if sortColumn === (column.sortKey || column.key)}
|
|
280
|
+
{#if sortDirection === 'asc'}
|
|
281
|
+
<svg
|
|
282
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
283
|
+
viewBox="0 0 20 20"
|
|
284
|
+
fill="currentColor"
|
|
285
|
+
class="h-4 w-4"
|
|
286
|
+
>
|
|
287
|
+
<path
|
|
288
|
+
d="M10 15a.75.75 0 01-.75-.75V7.612L6.058 10.8a.75.75 0 01-1.061-1.061l3.75-3.75a.75.75 0 011.06 0l3.75 3.75a.75.75 0 11-1.06 1.061L10.75 7.612v6.638A.75.75 0 0110 15z"
|
|
289
|
+
/>
|
|
290
|
+
</svg>
|
|
291
|
+
{:else if sortDirection === 'desc'}
|
|
292
|
+
<svg
|
|
293
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
294
|
+
viewBox="0 0 20 20"
|
|
295
|
+
fill="currentColor"
|
|
296
|
+
class="h-4 w-4"
|
|
297
|
+
>
|
|
298
|
+
<path
|
|
299
|
+
d="M10 5a.75.75 0 01.75.75v6.638l3.192-3.187a.75.75 0 111.06 1.061l-3.75 3.75a.75.75 0 01-1.06 0l-3.75-3.75a.75.75 0 111.06-1.061L9.25 12.389V5.75A.75.75 0 0110 5z"
|
|
300
|
+
/>
|
|
301
|
+
</svg>
|
|
302
|
+
{/if}
|
|
303
|
+
{:else}
|
|
304
|
+
<svg
|
|
305
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
306
|
+
fill="none"
|
|
307
|
+
viewBox="0 0 24 24"
|
|
308
|
+
stroke-width="1.5"
|
|
309
|
+
stroke="currentColor"
|
|
310
|
+
class="h-4 w-4 opacity-40"
|
|
311
|
+
>
|
|
312
|
+
<path
|
|
313
|
+
stroke-linecap="round"
|
|
314
|
+
stroke-linejoin="round"
|
|
315
|
+
d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9"
|
|
316
|
+
/>
|
|
317
|
+
</svg>
|
|
318
|
+
{/if}
|
|
319
|
+
</span>
|
|
320
|
+
</button>
|
|
484
321
|
{:else}
|
|
322
|
+
{column.header}
|
|
323
|
+
{/if}
|
|
324
|
+
</th>
|
|
325
|
+
{/each}
|
|
326
|
+
</tr>
|
|
327
|
+
</thead>
|
|
328
|
+
|
|
329
|
+
<tbody class={tbodyClasses}>
|
|
330
|
+
{#if loading}
|
|
331
|
+
<tr>
|
|
332
|
+
<td
|
|
333
|
+
colspan={selectable ? columns.length + 1 : columns.length}
|
|
334
|
+
class={cn(tdClasses, 'py-8 text-center')}
|
|
335
|
+
>
|
|
336
|
+
<div class="flex justify-center">
|
|
485
337
|
<svg
|
|
338
|
+
class="text-default-500 h-6 w-6 animate-spin"
|
|
486
339
|
xmlns="http://www.w3.org/2000/svg"
|
|
487
340
|
fill="none"
|
|
488
341
|
viewBox="0 0 24 24"
|
|
489
|
-
stroke-width="1.5"
|
|
490
|
-
stroke="currentColor"
|
|
491
|
-
class="h-4 w-4 opacity-40"
|
|
492
342
|
>
|
|
343
|
+
<circle
|
|
344
|
+
class="opacity-25"
|
|
345
|
+
cx="12"
|
|
346
|
+
cy="12"
|
|
347
|
+
r="10"
|
|
348
|
+
stroke="currentColor"
|
|
349
|
+
stroke-width="4"
|
|
350
|
+
></circle>
|
|
493
351
|
<path
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
d="
|
|
497
|
-
|
|
352
|
+
class="opacity-75"
|
|
353
|
+
fill="currentColor"
|
|
354
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
355
|
+
></path>
|
|
498
356
|
</svg>
|
|
499
|
-
|
|
500
|
-
</
|
|
501
|
-
</
|
|
357
|
+
</div>
|
|
358
|
+
</td>
|
|
359
|
+
</tr>
|
|
360
|
+
{:else if getPaginatedData().length === 0}
|
|
361
|
+
<tr>
|
|
362
|
+
<td
|
|
363
|
+
colspan={selectable ? columns.length + 1 : columns.length}
|
|
364
|
+
class={emptyStateClasses}
|
|
365
|
+
>
|
|
366
|
+
No data available
|
|
367
|
+
</td>
|
|
368
|
+
</tr>
|
|
502
369
|
{:else}
|
|
503
|
-
{
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
class="opacity-25"
|
|
526
|
-
cx="12"
|
|
527
|
-
cy="12"
|
|
528
|
-
r="10"
|
|
529
|
-
stroke="currentColor"
|
|
530
|
-
stroke-width="4"
|
|
531
|
-
></circle>
|
|
532
|
-
<path
|
|
533
|
-
class="opacity-75"
|
|
534
|
-
fill="currentColor"
|
|
535
|
-
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
536
|
-
></path>
|
|
537
|
-
</svg>
|
|
538
|
-
</div>
|
|
539
|
-
</td>
|
|
540
|
-
</tr>
|
|
541
|
-
{:else if getPaginatedData().length === 0}
|
|
542
|
-
<tr>
|
|
543
|
-
<td
|
|
544
|
-
colspan={selectable ? columns.length + 1 : columns.length}
|
|
545
|
-
class={emptyStateClasses}
|
|
546
|
-
>
|
|
547
|
-
No data available
|
|
548
|
-
</td>
|
|
549
|
-
</tr>
|
|
550
|
-
{:else}
|
|
551
|
-
{#each getPaginatedData() as row, rowIndex}
|
|
552
|
-
<tr
|
|
553
|
-
class={cn(trClasses, rowclass(row, rowIndex), {
|
|
554
|
-
'bg-primary-100': selectable && isRowSelected(row),
|
|
555
|
-
'cursor-pointer': onrowclick
|
|
556
|
-
})}
|
|
557
|
-
onclick={() => handleRowClick(row, rowIndex)}
|
|
558
|
-
aria-selected={selectable && isRowSelected(row)}
|
|
559
|
-
>
|
|
560
|
-
{#if selectable}
|
|
561
|
-
<td class={cn(tdClasses, 'text-center')}>
|
|
562
|
-
<input
|
|
563
|
-
type="checkbox"
|
|
564
|
-
checked={isRowSelected(row)}
|
|
565
|
-
onclick={(e) => {
|
|
566
|
-
e.stopPropagation(); // Prevent row click
|
|
567
|
-
toggleRowSelection(row);
|
|
568
|
-
}}
|
|
569
|
-
aria-label={`Select row ${rowIndex + 1}`}
|
|
570
|
-
/>
|
|
571
|
-
</td>
|
|
572
|
-
{/if}
|
|
370
|
+
{#each getPaginatedData() as row, rowIndex}
|
|
371
|
+
<tr
|
|
372
|
+
class={cn(trClasses, rowclass(row, rowIndex), {
|
|
373
|
+
'bg-primary-100': selectable && isRowSelected(row),
|
|
374
|
+
'cursor-pointer': onrowclick
|
|
375
|
+
})}
|
|
376
|
+
onclick={() => handleRowClick(row, rowIndex)}
|
|
377
|
+
aria-selected={selectable && isRowSelected(row)}
|
|
378
|
+
>
|
|
379
|
+
{#if selectable}
|
|
380
|
+
<td class={cn(tdClasses, 'text-center')}>
|
|
381
|
+
<input
|
|
382
|
+
type="checkbox"
|
|
383
|
+
checked={isRowSelected(row)}
|
|
384
|
+
onclick={(e) => {
|
|
385
|
+
e.stopPropagation(); // Prevent row click
|
|
386
|
+
toggleRowSelection(row);
|
|
387
|
+
}}
|
|
388
|
+
aria-label={`Select row ${rowIndex + 1}`}
|
|
389
|
+
/>
|
|
390
|
+
</td>
|
|
391
|
+
{/if}
|
|
573
392
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
393
|
+
{#each columns as column (column.key)}
|
|
394
|
+
<td
|
|
395
|
+
class={cn(
|
|
396
|
+
tdClasses,
|
|
397
|
+
column.align === 'center' && 'text-center',
|
|
398
|
+
column.align === 'right' && 'text-right',
|
|
399
|
+
column.class
|
|
400
|
+
)}
|
|
401
|
+
>
|
|
402
|
+
{#if column.cell}
|
|
403
|
+
{@render column.cell(row, column.key, rowIndex)}
|
|
404
|
+
{:else if row[column.key] === undefined || row[column.key] === null}
|
|
405
|
+
<span class="text-default-300">—</span>
|
|
406
|
+
{:else}
|
|
407
|
+
{row[column.key]}
|
|
408
|
+
{/if}
|
|
409
|
+
</td>
|
|
410
|
+
{/each}
|
|
411
|
+
</tr>
|
|
412
|
+
{#if expandedContent}
|
|
413
|
+
<tr class="expandedContent-row">
|
|
414
|
+
<td
|
|
415
|
+
colspan={selectable ? columns.length + 1 : columns.length}
|
|
416
|
+
class="border-0 p-0"
|
|
417
|
+
>
|
|
418
|
+
{@render expandedContent(row)}
|
|
419
|
+
</td>
|
|
420
|
+
</tr>
|
|
589
421
|
{/if}
|
|
590
|
-
</td>
|
|
591
|
-
{/each}
|
|
592
|
-
</tr>
|
|
593
|
-
{#if expandedContent}
|
|
594
|
-
<tr class="expandedContent-row">
|
|
595
|
-
<td colspan={selectable ? columns.length + 1 : columns.length} class="border-0 p-0">
|
|
596
|
-
{@render expandedContent(row)}
|
|
597
|
-
</td>
|
|
598
|
-
</tr>
|
|
599
|
-
{/if}
|
|
600
|
-
{/each}
|
|
601
|
-
{/if}
|
|
602
|
-
</tbody>
|
|
603
|
-
</table>
|
|
604
|
-
</div>
|
|
605
|
-
|
|
606
|
-
{#if showPaginationControls && (paginationPosition === 'bottom' || paginationPosition === 'both')}
|
|
607
|
-
<div class={footerClasses}>
|
|
608
|
-
<div class={paginationClasses}>
|
|
609
|
-
<div class="flex items-center gap-2">
|
|
610
|
-
{#if showPageSize}
|
|
611
|
-
<div class="flex items-center gap-2">
|
|
612
|
-
<label for="table-page-size-bottom" class="text-default-500 text-sm">Show</label>
|
|
613
|
-
<select
|
|
614
|
-
id="table-page-size-bottom"
|
|
615
|
-
class="border-default-200 rounded-md border px-2 py-1 text-sm"
|
|
616
|
-
value={internalPageSize}
|
|
617
|
-
onchange={handlePageSizeChange}
|
|
618
|
-
>
|
|
619
|
-
{#each pageSizeOptions as option}
|
|
620
|
-
<option value={option}>{option}</option>
|
|
621
422
|
{/each}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
{/if}
|
|
626
|
-
<span class="text-default-500 text-sm">
|
|
627
|
-
Showing {Math.min(
|
|
628
|
-
(internalCurrentPage - 1) * internalPageSize + 1,
|
|
629
|
-
effectiveTotalItems
|
|
630
|
-
)}
|
|
631
|
-
to {Math.min(internalCurrentPage * internalPageSize, effectiveTotalItems)} of {effectiveTotalItems}
|
|
632
|
-
entries
|
|
633
|
-
</span>
|
|
423
|
+
{/if}
|
|
424
|
+
</tbody>
|
|
425
|
+
</table>
|
|
634
426
|
</div>
|
|
635
427
|
|
|
636
|
-
|
|
637
|
-
{
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
{
|
|
428
|
+
{#if showPaginationControls && (paginationPosition === 'bottom' || paginationPosition === 'both')}
|
|
429
|
+
<div class={footerClasses}>
|
|
430
|
+
<Pagination
|
|
431
|
+
currentPage={internalCurrentPage}
|
|
432
|
+
totalItems={effectiveTotalItems}
|
|
433
|
+
pageSize={internalPageSize}
|
|
434
|
+
onPageChange={handlePageChange}
|
|
435
|
+
onPageSizeChange={handlePageSizeChange}
|
|
436
|
+
{showPageSize}
|
|
437
|
+
{pageSizeOptions}
|
|
438
|
+
template={paginationTemplate === 'full' ? 'full' : 'compact'}
|
|
439
|
+
disabled={loading}
|
|
440
|
+
class={cn(paginationClasses, paginationClass)}
|
|
441
|
+
/>
|
|
442
|
+
</div>
|
|
443
|
+
{/if}
|
|
444
|
+
</div>
|
|
445
|
+
{/snippet}
|
|
446
|
+
</Card>
|
|
447
|
+
{:else}
|
|
448
|
+
<div class={baseClasses}>
|
|
449
|
+
{#if showPaginationControls && (paginationPosition === 'top' || paginationPosition === 'both')}
|
|
450
|
+
<div class={footerClasses}>
|
|
451
|
+
<Pagination
|
|
452
|
+
currentPage={internalCurrentPage}
|
|
453
|
+
totalItems={effectiveTotalItems}
|
|
454
|
+
pageSize={internalPageSize}
|
|
455
|
+
onPageChange={handlePageChange}
|
|
456
|
+
onPageSizeChange={handlePageSizeChange}
|
|
457
|
+
{showPageSize}
|
|
458
|
+
{pageSizeOptions}
|
|
459
|
+
template={paginationTemplate === 'full' ? 'full' : 'compact'}
|
|
460
|
+
disabled={loading}
|
|
461
|
+
class={cn(paginationClasses, paginationClass)}
|
|
462
|
+
/>
|
|
463
|
+
</div>
|
|
464
|
+
{/if}
|
|
465
|
+
|
|
466
|
+
<div class={wrapperClasses}>
|
|
467
|
+
<table class={tableClasses}>
|
|
468
|
+
<thead class={theadClasses}>
|
|
469
|
+
<tr>
|
|
470
|
+
{#if selectable}
|
|
471
|
+
<th class={cn(thClasses, 'text-center')}>
|
|
472
|
+
<input
|
|
473
|
+
type="checkbox"
|
|
474
|
+
onchange={() => {
|
|
475
|
+
if (selected.length === data.length) {
|
|
476
|
+
selected = [];
|
|
477
|
+
} else {
|
|
478
|
+
selected = [...data];
|
|
479
|
+
}
|
|
480
|
+
onselect(selected);
|
|
481
|
+
}}
|
|
482
|
+
checked={selected.length === data.length && data.length > 0}
|
|
483
|
+
aria-label="Select all rows"
|
|
484
|
+
/>
|
|
485
|
+
</th>
|
|
486
|
+
{/if}
|
|
669
487
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
type="button"
|
|
673
|
-
class={cn(
|
|
674
|
-
'relative inline-flex items-center rounded-md px-2 py-1 text-sm font-medium',
|
|
675
|
-
internalCurrentPage === 1
|
|
676
|
-
? 'text-default-300 cursor-not-allowed'
|
|
677
|
-
: 'text-default-700 hover:bg-default-100'
|
|
678
|
-
)}
|
|
679
|
-
onclick={prevPage}
|
|
680
|
-
disabled={internalCurrentPage === 1}
|
|
681
|
-
aria-label="Previous page"
|
|
682
|
-
>
|
|
683
|
-
<!-- Chevron Left SVG -->
|
|
684
|
-
<svg
|
|
685
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
686
|
-
width="16"
|
|
687
|
-
height="16"
|
|
688
|
-
viewBox="0 0 24 24"
|
|
689
|
-
fill="none"
|
|
690
|
-
stroke="currentColor"
|
|
691
|
-
stroke-width="2"
|
|
692
|
-
stroke-linecap="round"
|
|
693
|
-
stroke-linejoin="round"
|
|
694
|
-
class="h-4 w-4"
|
|
695
|
-
>
|
|
696
|
-
<path d="m15 18-6-6 6-6"></path>
|
|
697
|
-
</svg>
|
|
698
|
-
</button>
|
|
699
|
-
|
|
700
|
-
<!-- Page numbers -->
|
|
701
|
-
{#if paginationTemplate === 'full'}
|
|
702
|
-
{#each getPageNumbers() as pageNum}
|
|
703
|
-
<button
|
|
704
|
-
type="button"
|
|
488
|
+
{#each columns as column (column.key)}
|
|
489
|
+
<th
|
|
705
490
|
class={cn(
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
491
|
+
thClasses,
|
|
492
|
+
column.align === 'center' && 'text-center',
|
|
493
|
+
column.align === 'right' && 'text-right',
|
|
494
|
+
column.class
|
|
710
495
|
)}
|
|
711
|
-
|
|
712
|
-
aria-label={`Page ${pageNum}`}
|
|
713
|
-
aria-current={internalCurrentPage === pageNum ? 'page' : undefined}
|
|
496
|
+
style={column.width ? `width: ${column.width}` : undefined}
|
|
714
497
|
>
|
|
715
|
-
{
|
|
716
|
-
|
|
498
|
+
{#if column.sortable}
|
|
499
|
+
<button
|
|
500
|
+
type="button"
|
|
501
|
+
class={sortButtonBaseClass()}
|
|
502
|
+
onclick={() => toggleSort(column.sortKey || column.key)}
|
|
503
|
+
aria-label={`Sort by ${column.header}`}
|
|
504
|
+
>
|
|
505
|
+
{column.header}
|
|
506
|
+
<span class={sortIconBaseClass()}>
|
|
507
|
+
{#if sortColumn === (column.sortKey || column.key)}
|
|
508
|
+
{#if sortDirection === 'asc'}
|
|
509
|
+
<svg
|
|
510
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
511
|
+
viewBox="0 0 20 20"
|
|
512
|
+
fill="currentColor"
|
|
513
|
+
class="h-4 w-4"
|
|
514
|
+
>
|
|
515
|
+
<path
|
|
516
|
+
d="M10 15a.75.75 0 01-.75-.75V7.612L6.058 10.8a.75.75 0 01-1.061-1.061l3.75-3.75a.75.75 0 011.06 0l3.75 3.75a.75.75 0 11-1.06 1.061L10.75 7.612v6.638A.75.75 0 0110 15z"
|
|
517
|
+
/>
|
|
518
|
+
</svg>
|
|
519
|
+
{:else if sortDirection === 'desc'}
|
|
520
|
+
<svg
|
|
521
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
522
|
+
viewBox="0 0 20 20"
|
|
523
|
+
fill="currentColor"
|
|
524
|
+
class="h-4 w-4"
|
|
525
|
+
>
|
|
526
|
+
<path
|
|
527
|
+
d="M10 5a.75.75 0 01.75.75v6.638l3.192-3.187a.75.75 0 111.06 1.061l-3.75 3.75a.75.75 0 01-1.06 0l-3.75-3.75a.75.75 0 111.06-1.061L9.25 12.389V5.75A.75.75 0 0110 5z"
|
|
528
|
+
/>
|
|
529
|
+
</svg>
|
|
530
|
+
{/if}
|
|
531
|
+
{:else}
|
|
532
|
+
<svg
|
|
533
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
534
|
+
fill="none"
|
|
535
|
+
viewBox="0 0 24 24"
|
|
536
|
+
stroke-width="1.5"
|
|
537
|
+
stroke="currentColor"
|
|
538
|
+
class="h-4 w-4 opacity-40"
|
|
539
|
+
>
|
|
540
|
+
<path
|
|
541
|
+
stroke-linecap="round"
|
|
542
|
+
stroke-linejoin="round"
|
|
543
|
+
d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9"
|
|
544
|
+
/>
|
|
545
|
+
</svg>
|
|
546
|
+
{/if}
|
|
547
|
+
</span>
|
|
548
|
+
</button>
|
|
549
|
+
{:else}
|
|
550
|
+
{column.header}
|
|
551
|
+
{/if}
|
|
552
|
+
</th>
|
|
717
553
|
{/each}
|
|
554
|
+
</tr>
|
|
555
|
+
</thead>
|
|
556
|
+
|
|
557
|
+
<tbody class={tbodyClasses}>
|
|
558
|
+
{#if loading}
|
|
559
|
+
<tr>
|
|
560
|
+
<td
|
|
561
|
+
colspan={selectable ? columns.length + 1 : columns.length}
|
|
562
|
+
class={cn(tdClasses, 'py-8 text-center')}
|
|
563
|
+
>
|
|
564
|
+
<div class="flex justify-center">
|
|
565
|
+
<svg
|
|
566
|
+
class="text-default-500 h-6 w-6 animate-spin"
|
|
567
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
568
|
+
fill="none"
|
|
569
|
+
viewBox="0 0 24 24"
|
|
570
|
+
>
|
|
571
|
+
<circle
|
|
572
|
+
class="opacity-25"
|
|
573
|
+
cx="12"
|
|
574
|
+
cy="12"
|
|
575
|
+
r="10"
|
|
576
|
+
stroke="currentColor"
|
|
577
|
+
stroke-width="4"
|
|
578
|
+
></circle>
|
|
579
|
+
<path
|
|
580
|
+
class="opacity-75"
|
|
581
|
+
fill="currentColor"
|
|
582
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
583
|
+
></path>
|
|
584
|
+
</svg>
|
|
585
|
+
</div>
|
|
586
|
+
</td>
|
|
587
|
+
</tr>
|
|
588
|
+
{:else if getPaginatedData().length === 0}
|
|
589
|
+
<tr>
|
|
590
|
+
<td
|
|
591
|
+
colspan={selectable ? columns.length + 1 : columns.length}
|
|
592
|
+
class={emptyStateClasses}
|
|
593
|
+
>
|
|
594
|
+
No data available
|
|
595
|
+
</td>
|
|
596
|
+
</tr>
|
|
718
597
|
{:else}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
class={cn(
|
|
728
|
-
'relative inline-flex items-center rounded-md px-2 py-1 text-sm font-medium',
|
|
729
|
-
internalCurrentPage === totalPages
|
|
730
|
-
? 'text-default-300 cursor-not-allowed'
|
|
731
|
-
: 'text-default-700 hover:bg-default-100'
|
|
732
|
-
)}
|
|
733
|
-
onclick={nextPage}
|
|
734
|
-
disabled={internalCurrentPage === totalPages}
|
|
735
|
-
aria-label="Next page"
|
|
736
|
-
>
|
|
737
|
-
<!-- Chevron Right SVG -->
|
|
738
|
-
<svg
|
|
739
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
740
|
-
width="16"
|
|
741
|
-
height="16"
|
|
742
|
-
viewBox="0 0 24 24"
|
|
743
|
-
fill="none"
|
|
744
|
-
stroke="currentColor"
|
|
745
|
-
stroke-width="2"
|
|
746
|
-
stroke-linecap="round"
|
|
747
|
-
stroke-linejoin="round"
|
|
748
|
-
class="h-4 w-4"
|
|
749
|
-
>
|
|
750
|
-
<path d="m9 18 6-6-6-6"></path>
|
|
751
|
-
</svg>
|
|
752
|
-
</button>
|
|
753
|
-
|
|
754
|
-
{#if paginationTemplate === 'full'}
|
|
755
|
-
<!-- Last page button -->
|
|
756
|
-
<button
|
|
757
|
-
type="button"
|
|
758
|
-
class={cn(
|
|
759
|
-
'relative inline-flex items-center rounded-md px-2 py-1 text-sm font-medium',
|
|
760
|
-
internalCurrentPage === totalPages
|
|
761
|
-
? 'text-default-300 cursor-not-allowed'
|
|
762
|
-
: 'text-default-700 hover:bg-default-100'
|
|
763
|
-
)}
|
|
764
|
-
onclick={goToLastPage}
|
|
765
|
-
disabled={internalCurrentPage === totalPages}
|
|
766
|
-
aria-label="Last page"
|
|
767
|
-
>
|
|
768
|
-
<!-- Double Chevron Right SVG -->
|
|
769
|
-
<svg
|
|
770
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
771
|
-
width="16"
|
|
772
|
-
height="16"
|
|
773
|
-
viewBox="0 0 24 24"
|
|
774
|
-
fill="none"
|
|
775
|
-
stroke="currentColor"
|
|
776
|
-
stroke-width="2"
|
|
777
|
-
stroke-linecap="round"
|
|
778
|
-
stroke-linejoin="round"
|
|
779
|
-
class="h-4 w-4"
|
|
598
|
+
{#each getPaginatedData() as row, rowIndex}
|
|
599
|
+
<tr
|
|
600
|
+
class={cn(trClasses, rowclass(row, rowIndex), {
|
|
601
|
+
'bg-primary-100': selectable && isRowSelected(row),
|
|
602
|
+
'cursor-pointer': onrowclick
|
|
603
|
+
})}
|
|
604
|
+
onclick={() => handleRowClick(row, rowIndex)}
|
|
605
|
+
aria-selected={selectable && isRowSelected(row)}
|
|
780
606
|
>
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
607
|
+
{#if selectable}
|
|
608
|
+
<td class={cn(tdClasses, 'text-center')}>
|
|
609
|
+
<input
|
|
610
|
+
type="checkbox"
|
|
611
|
+
checked={isRowSelected(row)}
|
|
612
|
+
onclick={(e) => {
|
|
613
|
+
e.stopPropagation(); // Prevent row click
|
|
614
|
+
toggleRowSelection(row);
|
|
615
|
+
}}
|
|
616
|
+
aria-label={`Select row ${rowIndex + 1}`}
|
|
617
|
+
/>
|
|
618
|
+
</td>
|
|
619
|
+
{/if}
|
|
620
|
+
|
|
621
|
+
{#each columns as column (column.key)}
|
|
622
|
+
<td
|
|
623
|
+
class={cn(
|
|
624
|
+
tdClasses,
|
|
625
|
+
column.align === 'center' && 'text-center',
|
|
626
|
+
column.align === 'right' && 'text-right',
|
|
627
|
+
column.class
|
|
628
|
+
)}
|
|
629
|
+
>
|
|
630
|
+
{#if column.cell}
|
|
631
|
+
{@render column.cell(row, column.key, rowIndex)}
|
|
632
|
+
{:else if row[column.key] === undefined || row[column.key] === null}
|
|
633
|
+
<span class="text-default-300">—</span>
|
|
634
|
+
{:else}
|
|
635
|
+
{row[column.key]}
|
|
636
|
+
{/if}
|
|
637
|
+
</td>
|
|
638
|
+
{/each}
|
|
639
|
+
</tr>
|
|
640
|
+
{#if expandedContent}
|
|
641
|
+
<tr class="expandedContent-row">
|
|
642
|
+
<td
|
|
643
|
+
colspan={selectable ? columns.length + 1 : columns.length}
|
|
644
|
+
class="border-0 p-0"
|
|
645
|
+
>
|
|
646
|
+
{@render expandedContent(row)}
|
|
647
|
+
</td>
|
|
648
|
+
</tr>
|
|
649
|
+
{/if}
|
|
650
|
+
{/each}
|
|
785
651
|
{/if}
|
|
786
|
-
</
|
|
787
|
-
</
|
|
652
|
+
</tbody>
|
|
653
|
+
</table>
|
|
788
654
|
</div>
|
|
789
|
-
|
|
790
|
-
|
|
655
|
+
|
|
656
|
+
{#if showPaginationControls && (paginationPosition === 'bottom' || paginationPosition === 'both')}
|
|
657
|
+
<div class={footerClasses}>
|
|
658
|
+
<Pagination
|
|
659
|
+
currentPage={internalCurrentPage}
|
|
660
|
+
totalItems={effectiveTotalItems}
|
|
661
|
+
pageSize={internalPageSize}
|
|
662
|
+
onPageChange={handlePageChange}
|
|
663
|
+
onPageSizeChange={handlePageSizeChange}
|
|
664
|
+
{showPageSize}
|
|
665
|
+
{pageSizeOptions}
|
|
666
|
+
template={paginationTemplate === 'full' ? 'full' : 'compact'}
|
|
667
|
+
disabled={loading}
|
|
668
|
+
class={cn(paginationClasses, paginationClass)}
|
|
669
|
+
/>
|
|
670
|
+
</div>
|
|
671
|
+
{/if}
|
|
672
|
+
</div>
|
|
673
|
+
{/if}
|