@makolabs/ripple 1.2.2 → 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.
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../../helper/cls.js';
|
|
3
|
+
import type { ClassValue } from 'tailwind-variants';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Pagination component props
|
|
7
|
+
*/
|
|
8
|
+
export interface PaginationProps {
|
|
9
|
+
/** Current page number (1-indexed). If not provided, component manages state internally. */
|
|
10
|
+
currentPage?: number;
|
|
11
|
+
/** Total number of items across all pages */
|
|
12
|
+
totalItems: number;
|
|
13
|
+
/** Number of items per page */
|
|
14
|
+
pageSize?: number;
|
|
15
|
+
/** Callback when page changes */
|
|
16
|
+
onPageChange?: (page: number) => void;
|
|
17
|
+
/** Callback when page size changes */
|
|
18
|
+
onPageSizeChange?: (pageSize: number) => void;
|
|
19
|
+
/** Callback when first page is clicked */
|
|
20
|
+
onFirstPage?: () => void;
|
|
21
|
+
/** Callback when last page is clicked */
|
|
22
|
+
onLastPage?: () => void;
|
|
23
|
+
/** Whether pagination controls are disabled */
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
/** Show page size selector */
|
|
26
|
+
showPageSize?: boolean;
|
|
27
|
+
/** Available page size options */
|
|
28
|
+
pageSizeOptions?: number[];
|
|
29
|
+
/** Pagination template mode: 'full' shows page numbers, 'compact' shows "Page X of Y" */
|
|
30
|
+
template?: 'full' | 'compact';
|
|
31
|
+
/** Show "Showing X to Y of Z results" text */
|
|
32
|
+
showInfo?: boolean;
|
|
33
|
+
/** Show navigation buttons (first, prev, next, last) */
|
|
34
|
+
showNavigation?: boolean;
|
|
35
|
+
/** Show first/last page buttons */
|
|
36
|
+
showFirstLast?: boolean;
|
|
37
|
+
/** Show page number buttons (only applies when template='full') */
|
|
38
|
+
showPageNumbers?: boolean;
|
|
39
|
+
/** Maximum number of visible page buttons */
|
|
40
|
+
maxVisiblePages?: number;
|
|
41
|
+
/** Always show first and last page numbers when there are many pages */
|
|
42
|
+
alwaysShowFirstLast?: boolean;
|
|
43
|
+
/** Hide pagination when there's only one page */
|
|
44
|
+
hideWhenSinglePage?: boolean;
|
|
45
|
+
/** Hide pagination when there are no items */
|
|
46
|
+
hideWhenNoItems?: boolean;
|
|
47
|
+
/** Custom class for wrapper */
|
|
48
|
+
class?: ClassValue;
|
|
49
|
+
/** Custom class for info text */
|
|
50
|
+
infoClass?: ClassValue;
|
|
51
|
+
/** Custom class for navigation buttons */
|
|
52
|
+
buttonClass?: ClassValue;
|
|
53
|
+
/** Custom class for active page button */
|
|
54
|
+
activeButtonClass?: ClassValue;
|
|
55
|
+
/** Use internal state management (when currentPage is not provided) */
|
|
56
|
+
internalState?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let {
|
|
60
|
+
currentPage: externalCurrentPage,
|
|
61
|
+
totalItems,
|
|
62
|
+
pageSize: externalPageSize,
|
|
63
|
+
onPageChange,
|
|
64
|
+
onPageSizeChange,
|
|
65
|
+
onFirstPage,
|
|
66
|
+
onLastPage,
|
|
67
|
+
disabled = false,
|
|
68
|
+
showPageSize = false,
|
|
69
|
+
pageSizeOptions = [5, 10, 25, 50, 100],
|
|
70
|
+
template = 'full',
|
|
71
|
+
showInfo = true,
|
|
72
|
+
showNavigation = true,
|
|
73
|
+
showFirstLast = true,
|
|
74
|
+
showPageNumbers = true,
|
|
75
|
+
maxVisiblePages = 5,
|
|
76
|
+
alwaysShowFirstLast = true,
|
|
77
|
+
hideWhenSinglePage = true,
|
|
78
|
+
hideWhenNoItems = true,
|
|
79
|
+
class: wrapperClass = '',
|
|
80
|
+
infoClass = '',
|
|
81
|
+
buttonClass = '',
|
|
82
|
+
activeButtonClass = '',
|
|
83
|
+
internalState = false
|
|
84
|
+
}: PaginationProps = $props();
|
|
85
|
+
|
|
86
|
+
// Internal state management
|
|
87
|
+
const defaultPageSize = 10;
|
|
88
|
+
let internalCurrentPage = $state(externalCurrentPage ?? 1);
|
|
89
|
+
let internalPageSize = $state(externalPageSize ?? defaultPageSize);
|
|
90
|
+
|
|
91
|
+
// Use external values if provided, otherwise use internal state or defaults
|
|
92
|
+
const currentPage = $derived(
|
|
93
|
+
externalCurrentPage !== undefined
|
|
94
|
+
? externalCurrentPage
|
|
95
|
+
: internalState
|
|
96
|
+
? internalCurrentPage
|
|
97
|
+
: 1
|
|
98
|
+
);
|
|
99
|
+
const pageSize = $derived(
|
|
100
|
+
externalPageSize !== undefined
|
|
101
|
+
? externalPageSize
|
|
102
|
+
: internalState
|
|
103
|
+
? internalPageSize
|
|
104
|
+
: defaultPageSize
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// Sync external page size changes
|
|
108
|
+
$effect(() => {
|
|
109
|
+
if (externalPageSize !== undefined) {
|
|
110
|
+
internalPageSize = externalPageSize;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Sync external current page changes
|
|
115
|
+
$effect(() => {
|
|
116
|
+
if (externalCurrentPage !== undefined) {
|
|
117
|
+
internalCurrentPage = externalCurrentPage;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Validation: ensure pageSize is valid
|
|
122
|
+
const validPageSize = $derived(Math.max(1, pageSize));
|
|
123
|
+
// Validation: ensure totalItems is non-negative
|
|
124
|
+
const validTotalItems = $derived(Math.max(0, totalItems));
|
|
125
|
+
|
|
126
|
+
// Computed values
|
|
127
|
+
const totalPages = $derived(Math.ceil(validTotalItems / validPageSize));
|
|
128
|
+
const startItem = $derived(
|
|
129
|
+
Math.min((currentPage - 1) * validPageSize + 1, validTotalItems)
|
|
130
|
+
);
|
|
131
|
+
const endItem = $derived(Math.min(currentPage * validPageSize, validTotalItems));
|
|
132
|
+
|
|
133
|
+
// Ensure currentPage is within valid range
|
|
134
|
+
const validCurrentPage = $derived(
|
|
135
|
+
Math.max(1, Math.min(currentPage, totalPages || 1))
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// Navigation functions
|
|
139
|
+
function goToPage(page: number) {
|
|
140
|
+
if (!disabled && page >= 1 && page <= totalPages && page !== validCurrentPage) {
|
|
141
|
+
if (internalState && externalCurrentPage === undefined) {
|
|
142
|
+
internalCurrentPage = page;
|
|
143
|
+
}
|
|
144
|
+
onPageChange?.(page);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function firstPage() {
|
|
149
|
+
if (!disabled && validCurrentPage > 1) {
|
|
150
|
+
const targetPage = 1;
|
|
151
|
+
if (internalState && externalCurrentPage === undefined) {
|
|
152
|
+
internalCurrentPage = targetPage;
|
|
153
|
+
}
|
|
154
|
+
onPageChange?.(targetPage);
|
|
155
|
+
onFirstPage?.();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function lastPage() {
|
|
160
|
+
if (!disabled && validCurrentPage < totalPages) {
|
|
161
|
+
const targetPage = totalPages;
|
|
162
|
+
if (internalState && externalCurrentPage === undefined) {
|
|
163
|
+
internalCurrentPage = targetPage;
|
|
164
|
+
}
|
|
165
|
+
onPageChange?.(targetPage);
|
|
166
|
+
onLastPage?.();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function nextPage() {
|
|
171
|
+
if (!disabled && validCurrentPage < totalPages) {
|
|
172
|
+
const targetPage = validCurrentPage + 1;
|
|
173
|
+
if (internalState && externalCurrentPage === undefined) {
|
|
174
|
+
internalCurrentPage = targetPage;
|
|
175
|
+
}
|
|
176
|
+
onPageChange?.(targetPage);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function prevPage() {
|
|
181
|
+
if (!disabled && validCurrentPage > 1) {
|
|
182
|
+
const targetPage = validCurrentPage - 1;
|
|
183
|
+
if (internalState && externalCurrentPage === undefined) {
|
|
184
|
+
internalCurrentPage = targetPage;
|
|
185
|
+
}
|
|
186
|
+
onPageChange?.(targetPage);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function handlePageSizeChange(event: Event) {
|
|
191
|
+
if (disabled) return;
|
|
192
|
+
const select = event.target as HTMLSelectElement;
|
|
193
|
+
const newPageSize = parseInt(select.value, 10);
|
|
194
|
+
|
|
195
|
+
if (isNaN(newPageSize) || newPageSize < 1) return;
|
|
196
|
+
|
|
197
|
+
// Calculate new total pages
|
|
198
|
+
const newTotalPages = Math.ceil(validTotalItems / newPageSize);
|
|
199
|
+
|
|
200
|
+
// Adjust current page if it would exceed the new total pages
|
|
201
|
+
let adjustedPage = validCurrentPage;
|
|
202
|
+
if (validCurrentPage > newTotalPages) {
|
|
203
|
+
adjustedPage = newTotalPages || 1;
|
|
204
|
+
if (internalState && externalCurrentPage === undefined) {
|
|
205
|
+
internalCurrentPage = adjustedPage;
|
|
206
|
+
}
|
|
207
|
+
onPageChange?.(adjustedPage);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Update page size
|
|
211
|
+
if (internalState && externalPageSize === undefined) {
|
|
212
|
+
internalPageSize = newPageSize;
|
|
213
|
+
}
|
|
214
|
+
onPageSizeChange?.(newPageSize);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Calculate which page numbers to display
|
|
219
|
+
*/
|
|
220
|
+
function getPageNumbers(): number[] {
|
|
221
|
+
if (totalPages <= maxVisiblePages) {
|
|
222
|
+
// Show all pages if total is less than or equal to max
|
|
223
|
+
return Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const pages: number[] = [];
|
|
227
|
+
const halfMax = Math.floor(maxVisiblePages / 2);
|
|
228
|
+
|
|
229
|
+
// Calculate start and end of visible range, centered around current page
|
|
230
|
+
let start = Math.max(1, validCurrentPage - halfMax);
|
|
231
|
+
let end = Math.min(totalPages, start + maxVisiblePages - 1);
|
|
232
|
+
|
|
233
|
+
// Adjust if we're near the end
|
|
234
|
+
if (end - start + 1 < maxVisiblePages) {
|
|
235
|
+
start = Math.max(1, end - maxVisiblePages + 1);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Build page numbers array
|
|
239
|
+
for (let i = start; i <= end; i++) {
|
|
240
|
+
pages.push(i);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return pages;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Determine if we should show pagination
|
|
247
|
+
const shouldShowPagination = $derived(
|
|
248
|
+
!hideWhenNoItems || validTotalItems > 0
|
|
249
|
+
);
|
|
250
|
+
const shouldShowControls = $derived(
|
|
251
|
+
!hideWhenSinglePage || totalPages > 1
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
// Default classes using ripple-ui design system
|
|
255
|
+
const defaultWrapperClass = 'flex items-center justify-between p-4';
|
|
256
|
+
const defaultInfoClass = 'text-default-500 text-sm';
|
|
257
|
+
const defaultButtonClass = 'relative inline-flex items-center rounded-md px-2 py-1 text-sm font-medium';
|
|
258
|
+
const defaultActiveButtonClass = 'bg-primary-100 text-primary-700';
|
|
259
|
+
const defaultInactiveButtonClass = 'text-default-700 hover:bg-default-100';
|
|
260
|
+
const defaultDisabledButtonClass = 'text-default-300 cursor-not-allowed';
|
|
261
|
+
const defaultPageButtonClass = 'relative inline-flex items-center rounded-md px-3 py-1 text-sm font-medium';
|
|
262
|
+
</script>
|
|
263
|
+
|
|
264
|
+
{#if shouldShowPagination}
|
|
265
|
+
<div class={cn(defaultWrapperClass, wrapperClass)}>
|
|
266
|
+
{#if showInfo}
|
|
267
|
+
<div class="flex items-center gap-2">
|
|
268
|
+
{#if showPageSize}
|
|
269
|
+
<div class="flex items-center gap-2">
|
|
270
|
+
<label for="pagination-page-size" class="text-default-500 text-sm">Show</label>
|
|
271
|
+
<select
|
|
272
|
+
id="pagination-page-size"
|
|
273
|
+
class={cn(
|
|
274
|
+
'border-default-200 rounded-md border px-2 py-1 text-sm',
|
|
275
|
+
disabled && 'cursor-not-allowed opacity-50'
|
|
276
|
+
)}
|
|
277
|
+
value={pageSize}
|
|
278
|
+
onchange={handlePageSizeChange}
|
|
279
|
+
disabled={disabled}
|
|
280
|
+
>
|
|
281
|
+
{#each pageSizeOptions as option}
|
|
282
|
+
<option value={option}>{option}</option>
|
|
283
|
+
{/each}
|
|
284
|
+
</select>
|
|
285
|
+
<span class="text-default-500 text-sm">entries</span>
|
|
286
|
+
</div>
|
|
287
|
+
{/if}
|
|
288
|
+
<span class={cn(defaultInfoClass, infoClass)}>
|
|
289
|
+
Showing {startItem} to {endItem} of {validTotalItems} entries
|
|
290
|
+
</span>
|
|
291
|
+
</div>
|
|
292
|
+
{/if}
|
|
293
|
+
|
|
294
|
+
{#if shouldShowControls && showNavigation}
|
|
295
|
+
<div class="flex items-center gap-1">
|
|
296
|
+
{#if showFirstLast}
|
|
297
|
+
<button
|
|
298
|
+
onclick={firstPage}
|
|
299
|
+
disabled={disabled || validCurrentPage === 1}
|
|
300
|
+
class={cn(
|
|
301
|
+
defaultButtonClass,
|
|
302
|
+
buttonClass,
|
|
303
|
+
disabled || validCurrentPage === 1
|
|
304
|
+
? defaultDisabledButtonClass
|
|
305
|
+
: defaultInactiveButtonClass
|
|
306
|
+
)}
|
|
307
|
+
aria-label="First page"
|
|
308
|
+
type="button"
|
|
309
|
+
>
|
|
310
|
+
«
|
|
311
|
+
</button>
|
|
312
|
+
{/if}
|
|
313
|
+
|
|
314
|
+
<button
|
|
315
|
+
onclick={prevPage}
|
|
316
|
+
disabled={disabled || validCurrentPage === 1}
|
|
317
|
+
class={cn(
|
|
318
|
+
defaultButtonClass,
|
|
319
|
+
buttonClass,
|
|
320
|
+
disabled || validCurrentPage === 1
|
|
321
|
+
? defaultDisabledButtonClass
|
|
322
|
+
: defaultInactiveButtonClass
|
|
323
|
+
)}
|
|
324
|
+
aria-label="Previous page"
|
|
325
|
+
type="button"
|
|
326
|
+
>
|
|
327
|
+
‹
|
|
328
|
+
</button>
|
|
329
|
+
|
|
330
|
+
{#if template === 'full' && showPageNumbers}
|
|
331
|
+
{#if totalPages <= maxVisiblePages}
|
|
332
|
+
<!-- Show all pages if total is less than or equal to maxVisiblePages -->
|
|
333
|
+
{#each Array(totalPages) as _, i}
|
|
334
|
+
{@const pageNum = i + 1}
|
|
335
|
+
<button
|
|
336
|
+
onclick={() => goToPage(pageNum)}
|
|
337
|
+
disabled={disabled}
|
|
338
|
+
class={cn(
|
|
339
|
+
defaultPageButtonClass,
|
|
340
|
+
buttonClass,
|
|
341
|
+
validCurrentPage === pageNum
|
|
342
|
+
? cn(defaultActiveButtonClass, activeButtonClass)
|
|
343
|
+
: defaultInactiveButtonClass
|
|
344
|
+
)}
|
|
345
|
+
aria-label="Page {pageNum}"
|
|
346
|
+
aria-current={validCurrentPage === pageNum ? 'page' : undefined}
|
|
347
|
+
type="button"
|
|
348
|
+
>
|
|
349
|
+
{pageNum}
|
|
350
|
+
</button>
|
|
351
|
+
{/each}
|
|
352
|
+
{:else}
|
|
353
|
+
<!-- Smart pagination for many pages -->
|
|
354
|
+
{@const pageNumbers = getPageNumbers()}
|
|
355
|
+
{@const showFirst = alwaysShowFirstLast && pageNumbers[0] > 1}
|
|
356
|
+
{@const showLast = alwaysShowFirstLast && pageNumbers[pageNumbers.length - 1] < totalPages}
|
|
357
|
+
|
|
358
|
+
{#if showFirst}
|
|
359
|
+
<button
|
|
360
|
+
onclick={() => goToPage(1)}
|
|
361
|
+
disabled={disabled}
|
|
362
|
+
class={cn(
|
|
363
|
+
defaultPageButtonClass,
|
|
364
|
+
buttonClass,
|
|
365
|
+
validCurrentPage === 1
|
|
366
|
+
? cn(defaultActiveButtonClass, activeButtonClass)
|
|
367
|
+
: defaultInactiveButtonClass
|
|
368
|
+
)}
|
|
369
|
+
aria-label="Page 1"
|
|
370
|
+
aria-current={validCurrentPage === 1 ? 'page' : undefined}
|
|
371
|
+
type="button"
|
|
372
|
+
>
|
|
373
|
+
1
|
|
374
|
+
</button>
|
|
375
|
+
{#if pageNumbers[0] > 2}
|
|
376
|
+
<span class="px-2 text-sm text-default-400">...</span>
|
|
377
|
+
{/if}
|
|
378
|
+
{/if}
|
|
379
|
+
|
|
380
|
+
{#each pageNumbers as pageNum}
|
|
381
|
+
<button
|
|
382
|
+
onclick={() => goToPage(pageNum)}
|
|
383
|
+
disabled={disabled}
|
|
384
|
+
class={cn(
|
|
385
|
+
defaultPageButtonClass,
|
|
386
|
+
buttonClass,
|
|
387
|
+
validCurrentPage === pageNum
|
|
388
|
+
? cn(defaultActiveButtonClass, activeButtonClass)
|
|
389
|
+
: defaultInactiveButtonClass
|
|
390
|
+
)}
|
|
391
|
+
aria-label="Page {pageNum}"
|
|
392
|
+
aria-current={validCurrentPage === pageNum ? 'page' : undefined}
|
|
393
|
+
type="button"
|
|
394
|
+
>
|
|
395
|
+
{pageNum}
|
|
396
|
+
</button>
|
|
397
|
+
{/each}
|
|
398
|
+
|
|
399
|
+
{#if showLast}
|
|
400
|
+
{#if pageNumbers[pageNumbers.length - 1] < totalPages - 1}
|
|
401
|
+
<span class="px-2 text-sm text-default-400">...</span>
|
|
402
|
+
{/if}
|
|
403
|
+
<button
|
|
404
|
+
onclick={() => goToPage(totalPages)}
|
|
405
|
+
disabled={disabled}
|
|
406
|
+
class={cn(
|
|
407
|
+
defaultPageButtonClass,
|
|
408
|
+
buttonClass,
|
|
409
|
+
validCurrentPage === totalPages
|
|
410
|
+
? cn(defaultActiveButtonClass, activeButtonClass)
|
|
411
|
+
: defaultInactiveButtonClass
|
|
412
|
+
)}
|
|
413
|
+
aria-label="Page {totalPages}"
|
|
414
|
+
aria-current={validCurrentPage === totalPages ? 'page' : undefined}
|
|
415
|
+
type="button"
|
|
416
|
+
>
|
|
417
|
+
{totalPages}
|
|
418
|
+
</button>
|
|
419
|
+
{/if}
|
|
420
|
+
{/if}
|
|
421
|
+
{:else if template === 'compact'}
|
|
422
|
+
<span class="text-default-500 px-2 text-sm">
|
|
423
|
+
Page {validCurrentPage} of {totalPages}
|
|
424
|
+
</span>
|
|
425
|
+
{/if}
|
|
426
|
+
|
|
427
|
+
<button
|
|
428
|
+
onclick={nextPage}
|
|
429
|
+
disabled={disabled || validCurrentPage === totalPages}
|
|
430
|
+
class={cn(
|
|
431
|
+
defaultButtonClass,
|
|
432
|
+
buttonClass,
|
|
433
|
+
disabled || validCurrentPage === totalPages
|
|
434
|
+
? defaultDisabledButtonClass
|
|
435
|
+
: defaultInactiveButtonClass
|
|
436
|
+
)}
|
|
437
|
+
aria-label="Next page"
|
|
438
|
+
type="button"
|
|
439
|
+
>
|
|
440
|
+
›
|
|
441
|
+
</button>
|
|
442
|
+
|
|
443
|
+
{#if showFirstLast}
|
|
444
|
+
<button
|
|
445
|
+
onclick={lastPage}
|
|
446
|
+
disabled={disabled || validCurrentPage === totalPages}
|
|
447
|
+
class={cn(
|
|
448
|
+
defaultButtonClass,
|
|
449
|
+
buttonClass,
|
|
450
|
+
disabled || validCurrentPage === totalPages
|
|
451
|
+
? defaultDisabledButtonClass
|
|
452
|
+
: defaultInactiveButtonClass
|
|
453
|
+
)}
|
|
454
|
+
aria-label="Last page"
|
|
455
|
+
type="button"
|
|
456
|
+
>
|
|
457
|
+
»
|
|
458
|
+
</button>
|
|
459
|
+
{/if}
|
|
460
|
+
</div>
|
|
461
|
+
{/if}
|
|
462
|
+
</div>
|
|
463
|
+
{/if}
|
|
464
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ClassValue } from 'tailwind-variants';
|
|
2
|
+
/**
|
|
3
|
+
* Pagination component props
|
|
4
|
+
*/
|
|
5
|
+
export interface PaginationProps {
|
|
6
|
+
/** Current page number (1-indexed). If not provided, component manages state internally. */
|
|
7
|
+
currentPage?: number;
|
|
8
|
+
/** Total number of items across all pages */
|
|
9
|
+
totalItems: number;
|
|
10
|
+
/** Number of items per page */
|
|
11
|
+
pageSize?: number;
|
|
12
|
+
/** Callback when page changes */
|
|
13
|
+
onPageChange?: (page: number) => void;
|
|
14
|
+
/** Callback when page size changes */
|
|
15
|
+
onPageSizeChange?: (pageSize: number) => void;
|
|
16
|
+
/** Callback when first page is clicked */
|
|
17
|
+
onFirstPage?: () => void;
|
|
18
|
+
/** Callback when last page is clicked */
|
|
19
|
+
onLastPage?: () => void;
|
|
20
|
+
/** Whether pagination controls are disabled */
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
/** Show page size selector */
|
|
23
|
+
showPageSize?: boolean;
|
|
24
|
+
/** Available page size options */
|
|
25
|
+
pageSizeOptions?: number[];
|
|
26
|
+
/** Pagination template mode: 'full' shows page numbers, 'compact' shows "Page X of Y" */
|
|
27
|
+
template?: 'full' | 'compact';
|
|
28
|
+
/** Show "Showing X to Y of Z results" text */
|
|
29
|
+
showInfo?: boolean;
|
|
30
|
+
/** Show navigation buttons (first, prev, next, last) */
|
|
31
|
+
showNavigation?: boolean;
|
|
32
|
+
/** Show first/last page buttons */
|
|
33
|
+
showFirstLast?: boolean;
|
|
34
|
+
/** Show page number buttons (only applies when template='full') */
|
|
35
|
+
showPageNumbers?: boolean;
|
|
36
|
+
/** Maximum number of visible page buttons */
|
|
37
|
+
maxVisiblePages?: number;
|
|
38
|
+
/** Always show first and last page numbers when there are many pages */
|
|
39
|
+
alwaysShowFirstLast?: boolean;
|
|
40
|
+
/** Hide pagination when there's only one page */
|
|
41
|
+
hideWhenSinglePage?: boolean;
|
|
42
|
+
/** Hide pagination when there are no items */
|
|
43
|
+
hideWhenNoItems?: boolean;
|
|
44
|
+
/** Custom class for wrapper */
|
|
45
|
+
class?: ClassValue;
|
|
46
|
+
/** Custom class for info text */
|
|
47
|
+
infoClass?: ClassValue;
|
|
48
|
+
/** Custom class for navigation buttons */
|
|
49
|
+
buttonClass?: ClassValue;
|
|
50
|
+
/** Custom class for active page button */
|
|
51
|
+
activeButtonClass?: ClassValue;
|
|
52
|
+
/** Use internal state management (when currentPage is not provided) */
|
|
53
|
+
internalState?: boolean;
|
|
54
|
+
}
|
|
55
|
+
declare const Pagination: import("svelte").Component<PaginationProps, {}, "">;
|
|
56
|
+
type Pagination = ReturnType<typeof Pagination>;
|
|
57
|
+
export default Pagination;
|
package/dist/index.d.ts
CHANGED
|
@@ -229,6 +229,9 @@ export type TableProps<T extends DataRow = any> = {
|
|
|
229
229
|
onpagesizechange?: (pageSize: number) => void;
|
|
230
230
|
paginationPosition?: 'top' | 'bottom' | 'both';
|
|
231
231
|
paginationTemplate?: 'simple' | 'full';
|
|
232
|
+
title?: string;
|
|
233
|
+
subtitle?: string;
|
|
234
|
+
headerActions?: Snippet;
|
|
232
235
|
};
|
|
233
236
|
export type BreadcrumbItem = {
|
|
234
237
|
label: string;
|
|
@@ -347,6 +350,8 @@ export { default as Card } from './layout/card/Card.svelte';
|
|
|
347
350
|
export { default as MetricCard } from './layout/card/MetricCard.svelte';
|
|
348
351
|
export { default as RankedCard } from './layout/card/RankedCard.svelte';
|
|
349
352
|
export { default as Alert } from './elements/alert/Alert.svelte';
|
|
353
|
+
export { default as Pagination } from './elements/pagination/Pagination.svelte';
|
|
354
|
+
export type { PaginationProps } from './elements/pagination/Pagination.svelte';
|
|
350
355
|
export type TabProps = {
|
|
351
356
|
value: string;
|
|
352
357
|
label: string;
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ export { default as MetricCard } from './layout/card/MetricCard.svelte';
|
|
|
33
33
|
export { default as RankedCard } from './layout/card/RankedCard.svelte';
|
|
34
34
|
// Elements - Alert
|
|
35
35
|
export { default as Alert } from './elements/alert/Alert.svelte';
|
|
36
|
+
export { default as Pagination } from './elements/pagination/Pagination.svelte';
|
|
36
37
|
export { default as Tab } from './layout/tabs/Tab.svelte';
|
|
37
38
|
export { default as TabContent } from './layout/tabs/TabContent.svelte';
|
|
38
39
|
export { default as TabGroup } from './layout/tabs/TabGroup.svelte';
|