@delightstack/components 0.1.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/LICENSE +21 -0
- package/README.md +136 -0
- package/SKILL.md +149 -0
- package/bin/agents.js +63 -0
- package/dist/actions/Alert.svelte +202 -0
- package/dist/actions/Alert.svelte.d.ts +36 -0
- package/dist/actions/Alert.svelte.d.ts.map +1 -0
- package/dist/actions/Button.svelte +1450 -0
- package/dist/actions/Button.svelte.d.ts +56 -0
- package/dist/actions/Button.svelte.d.ts.map +1 -0
- package/dist/actions/ButtonGroup.svelte +111 -0
- package/dist/actions/ButtonGroup.svelte.d.ts +41 -0
- package/dist/actions/ButtonGroup.svelte.d.ts.map +1 -0
- package/dist/actions/CommandPalette.svelte +939 -0
- package/dist/actions/CommandPalette.svelte.d.ts +37 -0
- package/dist/actions/CommandPalette.svelte.d.ts.map +1 -0
- package/dist/actions/ContextMenu.svelte +138 -0
- package/dist/actions/ContextMenu.svelte.d.ts +54 -0
- package/dist/actions/ContextMenu.svelte.d.ts.map +1 -0
- package/dist/actions/Modal.svelte +474 -0
- package/dist/actions/Modal.svelte.d.ts +28 -0
- package/dist/actions/Modal.svelte.d.ts.map +1 -0
- package/dist/actions/Popover.svelte +1214 -0
- package/dist/actions/Popover.svelte.d.ts +31 -0
- package/dist/actions/Popover.svelte.d.ts.map +1 -0
- package/dist/actions/Portal.svelte +80 -0
- package/dist/actions/Portal.svelte.d.ts +17 -0
- package/dist/actions/Portal.svelte.d.ts.map +1 -0
- package/dist/actions/ThemeToggle.svelte +345 -0
- package/dist/actions/ThemeToggle.svelte.d.ts +15 -0
- package/dist/actions/ThemeToggle.svelte.d.ts.map +1 -0
- package/dist/actions/index.d.ts +13 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +10 -0
- package/dist/actions/scrollbar.d.ts +48 -0
- package/dist/actions/scrollbar.d.ts.map +1 -0
- package/dist/actions/scrollbar.js +404 -0
- package/dist/display/Accordion.svelte +586 -0
- package/dist/display/Accordion.svelte.d.ts +41 -0
- package/dist/display/Accordion.svelte.d.ts.map +1 -0
- package/dist/display/Avatar.svelte +527 -0
- package/dist/display/Avatar.svelte.d.ts +22 -0
- package/dist/display/Avatar.svelte.d.ts.map +1 -0
- package/dist/display/AvatarGroup.svelte +298 -0
- package/dist/display/AvatarGroup.svelte.d.ts +31 -0
- package/dist/display/AvatarGroup.svelte.d.ts.map +1 -0
- package/dist/display/Calendar.svelte +1366 -0
- package/dist/display/Calendar.svelte.d.ts +58 -0
- package/dist/display/Calendar.svelte.d.ts.map +1 -0
- package/dist/display/Chart.svelte +1426 -0
- package/dist/display/Chart.svelte.d.ts +35 -0
- package/dist/display/Chart.svelte.d.ts.map +1 -0
- package/dist/display/Code.svelte +780 -0
- package/dist/display/Code.svelte.d.ts +19 -0
- package/dist/display/Code.svelte.d.ts.map +1 -0
- package/dist/display/Comparison.svelte +686 -0
- package/dist/display/Comparison.svelte.d.ts +22 -0
- package/dist/display/Comparison.svelte.d.ts.map +1 -0
- package/dist/display/Counter.svelte +285 -0
- package/dist/display/Counter.svelte.d.ts +21 -0
- package/dist/display/Counter.svelte.d.ts.map +1 -0
- package/dist/display/Expand.svelte +48 -0
- package/dist/display/Expand.svelte.d.ts +9 -0
- package/dist/display/Expand.svelte.d.ts.map +1 -0
- package/dist/display/List.svelte +294 -0
- package/dist/display/List.svelte.d.ts +40 -0
- package/dist/display/List.svelte.d.ts.map +1 -0
- package/dist/display/ListContextReset.svelte +19 -0
- package/dist/display/ListContextReset.svelte.d.ts +7 -0
- package/dist/display/ListContextReset.svelte.d.ts.map +1 -0
- package/dist/display/ListItem.svelte +834 -0
- package/dist/display/ListItem.svelte.d.ts +22 -0
- package/dist/display/ListItem.svelte.d.ts.map +1 -0
- package/dist/display/QR.svelte +1193 -0
- package/dist/display/QR.svelte.d.ts +23 -0
- package/dist/display/QR.svelte.d.ts.map +1 -0
- package/dist/display/SplitPane.svelte +744 -0
- package/dist/display/SplitPane.svelte.d.ts +25 -0
- package/dist/display/SplitPane.svelte.d.ts.map +1 -0
- package/dist/display/Stat.svelte +439 -0
- package/dist/display/Stat.svelte.d.ts +24 -0
- package/dist/display/Stat.svelte.d.ts.map +1 -0
- package/dist/display/Table.svelte +4654 -0
- package/dist/display/Table.svelte.d.ts +249 -0
- package/dist/display/Table.svelte.d.ts.map +1 -0
- package/dist/display/TableCellEditor.svelte +935 -0
- package/dist/display/TableCellEditor.svelte.d.ts +58 -0
- package/dist/display/TableCellEditor.svelte.d.ts.map +1 -0
- package/dist/display/Timeline.svelte +1258 -0
- package/dist/display/Timeline.svelte.d.ts +43 -0
- package/dist/display/Timeline.svelte.d.ts.map +1 -0
- package/dist/display/Tree.svelte +1740 -0
- package/dist/display/Tree.svelte.d.ts +74 -0
- package/dist/display/Tree.svelte.d.ts.map +1 -0
- package/dist/display/Typewriter.svelte +338 -0
- package/dist/display/Typewriter.svelte.d.ts +22 -0
- package/dist/display/Typewriter.svelte.d.ts.map +1 -0
- package/dist/display/index.d.ts +24 -0
- package/dist/display/index.d.ts.map +1 -0
- package/dist/display/index.js +18 -0
- package/dist/feedback/Callout.svelte +529 -0
- package/dist/feedback/Callout.svelte.d.ts +24 -0
- package/dist/feedback/Callout.svelte.d.ts.map +1 -0
- package/dist/feedback/Confetti.svelte +631 -0
- package/dist/feedback/Confetti.svelte.d.ts +90 -0
- package/dist/feedback/Confetti.svelte.d.ts.map +1 -0
- package/dist/feedback/Progress.svelte +382 -0
- package/dist/feedback/Progress.svelte.d.ts +25 -0
- package/dist/feedback/Progress.svelte.d.ts.map +1 -0
- package/dist/feedback/Toast.svelte +967 -0
- package/dist/feedback/Toast.svelte.d.ts +54 -0
- package/dist/feedback/Toast.svelte.d.ts.map +1 -0
- package/dist/feedback/index.d.ts +7 -0
- package/dist/feedback/index.d.ts.map +1 -0
- package/dist/feedback/index.js +4 -0
- package/dist/form/Checkbox.svelte +449 -0
- package/dist/form/Checkbox.svelte.d.ts +27 -0
- package/dist/form/Checkbox.svelte.d.ts.map +1 -0
- package/dist/form/Fieldset.svelte +410 -0
- package/dist/form/Fieldset.svelte.d.ts +22 -0
- package/dist/form/Fieldset.svelte.d.ts.map +1 -0
- package/dist/form/FileUpload.svelte +934 -0
- package/dist/form/FileUpload.svelte.d.ts +41 -0
- package/dist/form/FileUpload.svelte.d.ts.map +1 -0
- package/dist/form/Form.svelte +530 -0
- package/dist/form/Form.svelte.d.ts +120 -0
- package/dist/form/Form.svelte.d.ts.map +1 -0
- package/dist/form/Input.svelte +2858 -0
- package/dist/form/Input.svelte.d.ts +66 -0
- package/dist/form/Input.svelte.d.ts.map +1 -0
- package/dist/form/Radio.svelte +507 -0
- package/dist/form/Radio.svelte.d.ts +39 -0
- package/dist/form/Radio.svelte.d.ts.map +1 -0
- package/dist/form/Range.svelte +912 -0
- package/dist/form/Range.svelte.d.ts +33 -0
- package/dist/form/Range.svelte.d.ts.map +1 -0
- package/dist/form/Rating.svelte +429 -0
- package/dist/form/Rating.svelte.d.ts +28 -0
- package/dist/form/Rating.svelte.d.ts.map +1 -0
- package/dist/form/Select.svelte +1933 -0
- package/dist/form/Select.svelte.d.ts +54 -0
- package/dist/form/Select.svelte.d.ts.map +1 -0
- package/dist/form/Toggle.svelte +645 -0
- package/dist/form/Toggle.svelte.d.ts +50 -0
- package/dist/form/Toggle.svelte.d.ts.map +1 -0
- package/dist/form/index.d.ts +15 -0
- package/dist/form/index.d.ts.map +1 -0
- package/dist/form/index.js +10 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/layout/README.md +172 -0
- package/dist/media/Carousel.svelte +2424 -0
- package/dist/media/Carousel.svelte.d.ts +47 -0
- package/dist/media/Carousel.svelte.d.ts.map +1 -0
- package/dist/media/Gallery.svelte +2881 -0
- package/dist/media/Gallery.svelte.d.ts +82 -0
- package/dist/media/Gallery.svelte.d.ts.map +1 -0
- package/dist/media/Image.svelte +389 -0
- package/dist/media/Image.svelte.d.ts +33 -0
- package/dist/media/Image.svelte.d.ts.map +1 -0
- package/dist/media/PDF.svelte +1793 -0
- package/dist/media/PDF.svelte.d.ts +44 -0
- package/dist/media/PDF.svelte.d.ts.map +1 -0
- package/dist/media/Panorama.svelte +1391 -0
- package/dist/media/Panorama.svelte.d.ts +47 -0
- package/dist/media/Panorama.svelte.d.ts.map +1 -0
- package/dist/media/Video.svelte +2501 -0
- package/dist/media/Video.svelte.d.ts +58 -0
- package/dist/media/Video.svelte.d.ts.map +1 -0
- package/dist/media/carousel.d.ts +211 -0
- package/dist/media/carousel.d.ts.map +1 -0
- package/dist/media/carousel.js +408 -0
- package/dist/media/index.d.ts +11 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media/index.js +5 -0
- package/dist/navigation/BottomSheet.svelte +636 -0
- package/dist/navigation/BottomSheet.svelte.d.ts +27 -0
- package/dist/navigation/BottomSheet.svelte.d.ts.map +1 -0
- package/dist/navigation/Breadcrumbs.svelte +611 -0
- package/dist/navigation/Breadcrumbs.svelte.d.ts +28 -0
- package/dist/navigation/Breadcrumbs.svelte.d.ts.map +1 -0
- package/dist/navigation/Pagination.svelte +641 -0
- package/dist/navigation/Pagination.svelte.d.ts +27 -0
- package/dist/navigation/Pagination.svelte.d.ts.map +1 -0
- package/dist/navigation/Steps.svelte +965 -0
- package/dist/navigation/Steps.svelte.d.ts +43 -0
- package/dist/navigation/Steps.svelte.d.ts.map +1 -0
- package/dist/navigation/Tabs.svelte +698 -0
- package/dist/navigation/Tabs.svelte.d.ts +41 -0
- package/dist/navigation/Tabs.svelte.d.ts.map +1 -0
- package/dist/navigation/index.d.ts +8 -0
- package/dist/navigation/index.d.ts.map +1 -0
- package/dist/navigation/index.js +5 -0
- package/package.json +139 -0
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { ripple } from '@delightstack/utilities';
|
|
3
|
+
|
|
4
|
+
const propId = $props.id();
|
|
5
|
+
let {
|
|
6
|
+
/** Current page number (1-based) */
|
|
7
|
+
page = $bindable(1),
|
|
8
|
+
|
|
9
|
+
/** Total number of pages */
|
|
10
|
+
total_pages,
|
|
11
|
+
|
|
12
|
+
/** Total number of items (used for info display) */
|
|
13
|
+
total_items = undefined as number | undefined,
|
|
14
|
+
|
|
15
|
+
/** Number of items per page */
|
|
16
|
+
page_size = $bindable(10),
|
|
17
|
+
|
|
18
|
+
/** Options for the page size selector */
|
|
19
|
+
page_size_options = [10, 25, 50, 100],
|
|
20
|
+
|
|
21
|
+
/** Simple mode: prev/next with "Page X of Y" */
|
|
22
|
+
simple = false,
|
|
23
|
+
|
|
24
|
+
/** Compact mode: minimal prev/next with "X / Y" */
|
|
25
|
+
compact = false,
|
|
26
|
+
|
|
27
|
+
/** Show a page size selector */
|
|
28
|
+
show_page_size = false,
|
|
29
|
+
|
|
30
|
+
/** Show item range info ("Showing X-Y of Z") */
|
|
31
|
+
show_info = false,
|
|
32
|
+
|
|
33
|
+
/** Number of sibling pages to show around the current page */
|
|
34
|
+
sibling_count = 1,
|
|
35
|
+
|
|
36
|
+
/** Number of pages to always show at the start and end */
|
|
37
|
+
boundary_count = 1,
|
|
38
|
+
|
|
39
|
+
/** Size of the pagination buttons */
|
|
40
|
+
size = '1' as '0' | '1' | '2' | '3',
|
|
41
|
+
|
|
42
|
+
/** Show skeleton placeholder */
|
|
43
|
+
skeleton = false,
|
|
44
|
+
|
|
45
|
+
/** Element ID */
|
|
46
|
+
id = propId,
|
|
47
|
+
|
|
48
|
+
/** Additional CSS classes */
|
|
49
|
+
class: class_name = '',
|
|
50
|
+
|
|
51
|
+
/** Callback when the page changes */
|
|
52
|
+
onchange = undefined as ((detail: { page: number }) => void) | undefined,
|
|
53
|
+
|
|
54
|
+
/** Callback when the page size changes */
|
|
55
|
+
onpagesizechange = undefined as ((detail: { page_size: number }) => void) | undefined,
|
|
56
|
+
}: {
|
|
57
|
+
page?: number;
|
|
58
|
+
total_pages: number;
|
|
59
|
+
total_items?: number;
|
|
60
|
+
page_size?: number;
|
|
61
|
+
page_size_options?: number[];
|
|
62
|
+
simple?: boolean;
|
|
63
|
+
compact?: boolean;
|
|
64
|
+
show_page_size?: boolean;
|
|
65
|
+
show_info?: boolean;
|
|
66
|
+
sibling_count?: number;
|
|
67
|
+
boundary_count?: number;
|
|
68
|
+
size?: '0' | '1' | '2' | '3';
|
|
69
|
+
skeleton?: boolean;
|
|
70
|
+
id?: string;
|
|
71
|
+
class?: string;
|
|
72
|
+
onchange?: (detail: { page: number }) => void;
|
|
73
|
+
onpagesizechange?: (detail: { page_size: number }) => void;
|
|
74
|
+
} = $props();
|
|
75
|
+
|
|
76
|
+
const SIZE_MAP: Record<
|
|
77
|
+
string,
|
|
78
|
+
{ min_width: string; height: string; font_size: string }
|
|
79
|
+
> = {
|
|
80
|
+
'0': {
|
|
81
|
+
min_width: '1.5rem',
|
|
82
|
+
height: '1.5rem',
|
|
83
|
+
font_size: 'var(--text-sm, 0.75rem)',
|
|
84
|
+
},
|
|
85
|
+
'1': { min_width: '2rem', height: '2rem', font_size: 'var(--text-base, 0.875rem)' },
|
|
86
|
+
'2': { min_width: '2.5rem', height: '2.5rem', font_size: 'var(--text-lg, 1rem)' },
|
|
87
|
+
'3': { min_width: '3rem', height: '3rem', font_size: 'var(--text-xl, 1.125rem)' },
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const sizeConfig = $derived(SIZE_MAP[size] || SIZE_MAP['1']);
|
|
91
|
+
|
|
92
|
+
const isFirstPage = $derived(page <= 1);
|
|
93
|
+
const isLastPage = $derived(page >= total_pages);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Ellipsis algorithm: compute the list of page numbers and ellipsis markers.
|
|
97
|
+
* Returns an array of numbers (page numbers) and strings ('...') for gaps.
|
|
98
|
+
*/
|
|
99
|
+
const pageRange = $derived.by(() => {
|
|
100
|
+
if (total_pages <= 0) return [];
|
|
101
|
+
|
|
102
|
+
const range: (number | '...')[] = [];
|
|
103
|
+
|
|
104
|
+
// Compute boundary sets
|
|
105
|
+
const startBoundary = Math.min(boundary_count, total_pages);
|
|
106
|
+
const endBoundaryStart = Math.max(
|
|
107
|
+
total_pages - boundary_count + 1,
|
|
108
|
+
startBoundary + 1,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Compute sibling range around current page
|
|
112
|
+
const siblingStart = Math.max(page - sibling_count, 1);
|
|
113
|
+
const siblingEnd = Math.min(page + sibling_count, total_pages);
|
|
114
|
+
|
|
115
|
+
// Collect all page numbers that should appear
|
|
116
|
+
const pages = new Set<number>();
|
|
117
|
+
|
|
118
|
+
// Add start boundary pages
|
|
119
|
+
for (let i = 1; i <= startBoundary; i++) {
|
|
120
|
+
pages.add(i);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Add end boundary pages
|
|
124
|
+
for (let i = endBoundaryStart; i <= total_pages; i++) {
|
|
125
|
+
pages.add(i);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Add sibling pages (and current page)
|
|
129
|
+
for (let i = siblingStart; i <= siblingEnd; i++) {
|
|
130
|
+
pages.add(i);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Convert to sorted array and insert ellipses for gaps
|
|
134
|
+
const sorted = [...pages].sort((a, b) => a - b);
|
|
135
|
+
|
|
136
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
137
|
+
if (i > 0 && sorted[i] - sorted[i - 1] > 1) {
|
|
138
|
+
range.push('...');
|
|
139
|
+
}
|
|
140
|
+
range.push(sorted[i]);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return range;
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const infoText = $derived.by(() => {
|
|
147
|
+
if (!show_info || total_items === undefined) return '';
|
|
148
|
+
const start = (page - 1) * page_size + 1;
|
|
149
|
+
const end = Math.min(page * page_size, total_items);
|
|
150
|
+
return `Showing ${start}\u2013${end} of ${total_items}`;
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
function goToPage(newPage: number) {
|
|
154
|
+
if (newPage < 1 || newPage > total_pages || newPage === page) return;
|
|
155
|
+
page = newPage;
|
|
156
|
+
onchange?.({ page: newPage });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function handlePrev() {
|
|
160
|
+
goToPage(page - 1);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function handleNext() {
|
|
164
|
+
goToPage(page + 1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function handlePageSizeChange(e: Event) {
|
|
168
|
+
const target = e.target as HTMLSelectElement;
|
|
169
|
+
const newSize = Number(target.value);
|
|
170
|
+
page_size = newSize;
|
|
171
|
+
page = 1;
|
|
172
|
+
onpagesizechange?.({ page_size: newSize });
|
|
173
|
+
onchange?.({ page: 1 });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Skeleton page placeholders mirror the real pageRange when total_pages is
|
|
177
|
+
// already known, so skeleton ↔ loaded swaps without layout shift; otherwise
|
|
178
|
+
// fall back to a typical "1 … 5 [6] 7 … 11" run (7 cells).
|
|
179
|
+
const skeletonPageCount = $derived(total_pages > 0 ? pageRange.length : 7);
|
|
180
|
+
</script>
|
|
181
|
+
|
|
182
|
+
<nav
|
|
183
|
+
{id}
|
|
184
|
+
class={['pagination', class_name].filter(Boolean).join(' ')}
|
|
185
|
+
class:skeleton
|
|
186
|
+
aria-label="Pagination"
|
|
187
|
+
aria-hidden={skeleton || undefined}
|
|
188
|
+
style:--pg-min-width={sizeConfig.min_width}
|
|
189
|
+
style:--pg-height={sizeConfig.height}
|
|
190
|
+
style:--pg-font-size={sizeConfig.font_size}>
|
|
191
|
+
{#if skeleton}
|
|
192
|
+
<!-- Placeholders mirror the real layout per mode (and the optional
|
|
193
|
+
page-size / info slots) so the swap to live controls never shifts. -->
|
|
194
|
+
{@const delay_base = (show_page_size ? 1 : 0) + (show_info ? 1 : 0)}
|
|
195
|
+
{#if show_page_size}
|
|
196
|
+
<span class="page-size">
|
|
197
|
+
<span class="skeleton-bar" style:width="2em" style:--shimmer-delay="0ms"></span>
|
|
198
|
+
<span class="skeleton-select" style:--shimmer-delay="0ms"></span>
|
|
199
|
+
</span>
|
|
200
|
+
{/if}
|
|
201
|
+
{#if show_info}
|
|
202
|
+
<span class="info">
|
|
203
|
+
<span
|
|
204
|
+
class="skeleton-bar"
|
|
205
|
+
style:width="9em"
|
|
206
|
+
style:--shimmer-delay="{(show_page_size ? 1 : 0) * 120}ms">
|
|
207
|
+
</span>
|
|
208
|
+
</span>
|
|
209
|
+
{/if}
|
|
210
|
+
<div class="pagination-controls">
|
|
211
|
+
{#if compact}
|
|
212
|
+
<span class="skeleton-page" style:--shimmer-delay="{delay_base * 120}ms"></span>
|
|
213
|
+
<span class="compact-info">
|
|
214
|
+
<span
|
|
215
|
+
class="skeleton-bar"
|
|
216
|
+
style:width="2.5em"
|
|
217
|
+
style:--shimmer-delay="{(delay_base + 1) * 120}ms">
|
|
218
|
+
</span>
|
|
219
|
+
</span>
|
|
220
|
+
<span class="skeleton-page" style:--shimmer-delay="{(delay_base + 2) * 120}ms">
|
|
221
|
+
</span>
|
|
222
|
+
{:else if simple}
|
|
223
|
+
<span class="skeleton-nav" style:--shimmer-delay="{delay_base * 120}ms"></span>
|
|
224
|
+
<span class="simple-info">
|
|
225
|
+
<span
|
|
226
|
+
class="skeleton-bar"
|
|
227
|
+
style:width="5.5em"
|
|
228
|
+
style:--shimmer-delay="{(delay_base + 1) * 120}ms">
|
|
229
|
+
</span>
|
|
230
|
+
</span>
|
|
231
|
+
<span class="skeleton-nav" style:--shimmer-delay="{(delay_base + 2) * 120}ms">
|
|
232
|
+
</span>
|
|
233
|
+
{:else}
|
|
234
|
+
<span class="skeleton-nav" style:--shimmer-delay="{delay_base * 120}ms"></span>
|
|
235
|
+
{#each { length: skeletonPageCount } as _, i}
|
|
236
|
+
<span
|
|
237
|
+
class="skeleton-page"
|
|
238
|
+
style:--shimmer-delay="{(delay_base + 1 + i) * 120}ms">
|
|
239
|
+
</span>
|
|
240
|
+
{/each}
|
|
241
|
+
<span
|
|
242
|
+
class="skeleton-nav"
|
|
243
|
+
style:--shimmer-delay="{(delay_base + 1 + skeletonPageCount) * 120}ms">
|
|
244
|
+
</span>
|
|
245
|
+
{/if}
|
|
246
|
+
</div>
|
|
247
|
+
{:else}
|
|
248
|
+
{#if show_page_size}
|
|
249
|
+
<label class="page-size">
|
|
250
|
+
<span>Rows</span>
|
|
251
|
+
<select value={String(page_size)} onchange={handlePageSizeChange}>
|
|
252
|
+
{#each page_size_options as opt}
|
|
253
|
+
<option value={String(opt)} selected={opt === page_size}>{opt}</option>
|
|
254
|
+
{/each}
|
|
255
|
+
</select>
|
|
256
|
+
</label>
|
|
257
|
+
{/if}
|
|
258
|
+
|
|
259
|
+
{#if show_info && infoText}
|
|
260
|
+
<span class="info">{infoText}</span>
|
|
261
|
+
{/if}
|
|
262
|
+
|
|
263
|
+
<div class="pagination-controls">
|
|
264
|
+
{#if compact}
|
|
265
|
+
<!-- Compact mode: < 6 / 11 > -->
|
|
266
|
+
<button
|
|
267
|
+
type="button"
|
|
268
|
+
disabled={isFirstPage}
|
|
269
|
+
aria-disabled={isFirstPage}
|
|
270
|
+
aria-label="Go to previous page"
|
|
271
|
+
{@attach ripple({ zIndex: 1 })}
|
|
272
|
+
onclick={handlePrev}>
|
|
273
|
+
<svg width="16" height="16" viewBox="0 0 24 24" aria-hidden="true">
|
|
274
|
+
<path
|
|
275
|
+
d="M15 18l-6-6 6-6"
|
|
276
|
+
stroke="currentColor"
|
|
277
|
+
stroke-width="2"
|
|
278
|
+
stroke-linecap="round"
|
|
279
|
+
stroke-linejoin="round"
|
|
280
|
+
fill="none" />
|
|
281
|
+
</svg>
|
|
282
|
+
</button>
|
|
283
|
+
<span class="compact-info">{page} / {total_pages}</span>
|
|
284
|
+
<button
|
|
285
|
+
type="button"
|
|
286
|
+
disabled={isLastPage}
|
|
287
|
+
aria-disabled={isLastPage}
|
|
288
|
+
aria-label="Go to next page"
|
|
289
|
+
{@attach ripple({ zIndex: 1 })}
|
|
290
|
+
onclick={handleNext}>
|
|
291
|
+
<svg width="16" height="16" viewBox="0 0 24 24" aria-hidden="true">
|
|
292
|
+
<path
|
|
293
|
+
d="M9 18l6-6-6-6"
|
|
294
|
+
stroke="currentColor"
|
|
295
|
+
stroke-width="2"
|
|
296
|
+
stroke-linecap="round"
|
|
297
|
+
stroke-linejoin="round"
|
|
298
|
+
fill="none" />
|
|
299
|
+
</svg>
|
|
300
|
+
</button>
|
|
301
|
+
{:else if simple}
|
|
302
|
+
<!-- Simple mode: < Prev Page 6 of 11 Next > -->
|
|
303
|
+
<button
|
|
304
|
+
type="button"
|
|
305
|
+
disabled={isFirstPage}
|
|
306
|
+
aria-disabled={isFirstPage}
|
|
307
|
+
aria-label="Go to previous page"
|
|
308
|
+
{@attach ripple({ zIndex: 1 })}
|
|
309
|
+
onclick={handlePrev}>
|
|
310
|
+
<svg width="16" height="16" viewBox="0 0 24 24" aria-hidden="true">
|
|
311
|
+
<path
|
|
312
|
+
d="M15 18l-6-6 6-6"
|
|
313
|
+
stroke="currentColor"
|
|
314
|
+
stroke-width="2"
|
|
315
|
+
stroke-linecap="round"
|
|
316
|
+
stroke-linejoin="round"
|
|
317
|
+
fill="none" />
|
|
318
|
+
</svg>
|
|
319
|
+
<span>Prev</span>
|
|
320
|
+
</button>
|
|
321
|
+
<span class="simple-info">Page {page} of {total_pages}</span>
|
|
322
|
+
<button
|
|
323
|
+
type="button"
|
|
324
|
+
disabled={isLastPage}
|
|
325
|
+
aria-disabled={isLastPage}
|
|
326
|
+
aria-label="Go to next page"
|
|
327
|
+
{@attach ripple({ zIndex: 1 })}
|
|
328
|
+
onclick={handleNext}>
|
|
329
|
+
<span>Next</span>
|
|
330
|
+
<svg width="16" height="16" viewBox="0 0 24 24" aria-hidden="true">
|
|
331
|
+
<path
|
|
332
|
+
d="M9 18l6-6-6-6"
|
|
333
|
+
stroke="currentColor"
|
|
334
|
+
stroke-width="2"
|
|
335
|
+
stroke-linecap="round"
|
|
336
|
+
stroke-linejoin="round"
|
|
337
|
+
fill="none" />
|
|
338
|
+
</svg>
|
|
339
|
+
</button>
|
|
340
|
+
{:else}
|
|
341
|
+
<!-- Default mode: < Prev 1 2 ... 5 [6] 7 ... 10 11 Next > -->
|
|
342
|
+
<button
|
|
343
|
+
type="button"
|
|
344
|
+
disabled={isFirstPage}
|
|
345
|
+
aria-disabled={isFirstPage}
|
|
346
|
+
aria-label="Go to previous page"
|
|
347
|
+
{@attach ripple({ zIndex: 1 })}
|
|
348
|
+
onclick={handlePrev}>
|
|
349
|
+
<svg width="16" height="16" viewBox="0 0 24 24" aria-hidden="true">
|
|
350
|
+
<path
|
|
351
|
+
d="M15 18l-6-6 6-6"
|
|
352
|
+
stroke="currentColor"
|
|
353
|
+
stroke-width="2"
|
|
354
|
+
stroke-linecap="round"
|
|
355
|
+
stroke-linejoin="round"
|
|
356
|
+
fill="none" />
|
|
357
|
+
</svg>
|
|
358
|
+
<span>Prev</span>
|
|
359
|
+
</button>
|
|
360
|
+
|
|
361
|
+
{#each pageRange as item}
|
|
362
|
+
{#if item === '...'}
|
|
363
|
+
<span class="ellipsis" aria-hidden="true">…</span>
|
|
364
|
+
{:else}
|
|
365
|
+
<button
|
|
366
|
+
type="button"
|
|
367
|
+
class:current={item === page}
|
|
368
|
+
aria-current={item === page ? 'page' : undefined}
|
|
369
|
+
aria-label="Go to page {item}"
|
|
370
|
+
{@attach ripple({ zIndex: 1 })}
|
|
371
|
+
onclick={() => goToPage(item)}>
|
|
372
|
+
{item}
|
|
373
|
+
</button>
|
|
374
|
+
{/if}
|
|
375
|
+
{/each}
|
|
376
|
+
|
|
377
|
+
<button
|
|
378
|
+
type="button"
|
|
379
|
+
disabled={isLastPage}
|
|
380
|
+
aria-disabled={isLastPage}
|
|
381
|
+
aria-label="Go to next page"
|
|
382
|
+
{@attach ripple({ zIndex: 1 })}
|
|
383
|
+
onclick={handleNext}>
|
|
384
|
+
<span>Next</span>
|
|
385
|
+
<svg width="16" height="16" viewBox="0 0 24 24" aria-hidden="true">
|
|
386
|
+
<path
|
|
387
|
+
d="M9 18l6-6-6-6"
|
|
388
|
+
stroke="currentColor"
|
|
389
|
+
stroke-width="2"
|
|
390
|
+
stroke-linecap="round"
|
|
391
|
+
stroke-linejoin="round"
|
|
392
|
+
fill="none" />
|
|
393
|
+
</svg>
|
|
394
|
+
</button>
|
|
395
|
+
{/if}
|
|
396
|
+
</div>
|
|
397
|
+
{/if}
|
|
398
|
+
</nav>
|
|
399
|
+
|
|
400
|
+
<style>
|
|
401
|
+
.pagination {
|
|
402
|
+
display: flex;
|
|
403
|
+
align-items: center;
|
|
404
|
+
gap: 0.75rem;
|
|
405
|
+
font-size: var(--pg-font-size);
|
|
406
|
+
perspective: 100px;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/* ========== Skeleton ========== */
|
|
410
|
+
.pagination.skeleton {
|
|
411
|
+
pointer-events: none;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/* Every placeholder shape shares the token-driven shimmer surface. */
|
|
415
|
+
.skeleton-nav,
|
|
416
|
+
.skeleton-page,
|
|
417
|
+
.skeleton-select,
|
|
418
|
+
.skeleton-bar {
|
|
419
|
+
position: relative;
|
|
420
|
+
overflow: hidden;
|
|
421
|
+
background: var(--skeleton-bg, rgb(from var(--color-text, #888) r g b / 0.1));
|
|
422
|
+
|
|
423
|
+
&::after {
|
|
424
|
+
content: '';
|
|
425
|
+
position: absolute;
|
|
426
|
+
inset: 0;
|
|
427
|
+
transform: translateX(-100%);
|
|
428
|
+
background-image: linear-gradient(
|
|
429
|
+
105deg,
|
|
430
|
+
transparent 25%,
|
|
431
|
+
var(--skeleton-sheen, rgb(from var(--color-text, #888) r g b / 0.12)) 50%,
|
|
432
|
+
transparent 75%
|
|
433
|
+
);
|
|
434
|
+
animation: delight-skeleton-shimmer var(--skeleton-duration, 2.4s) ease-in-out
|
|
435
|
+
infinite;
|
|
436
|
+
animation-delay: var(--shimmer-delay, 0s);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/* Mirrors the real button's box: --pg-min-width × --pg-height for page
|
|
441
|
+
numbers (and compact icon-only prev/next); the wider prev/next variant
|
|
442
|
+
approximates icon + label + padding. Same radius (+ squircle) as the
|
|
443
|
+
real buttons. */
|
|
444
|
+
.skeleton-nav,
|
|
445
|
+
.skeleton-page,
|
|
446
|
+
.skeleton-select {
|
|
447
|
+
flex-shrink: 0;
|
|
448
|
+
height: var(--pg-height);
|
|
449
|
+
border-radius: var(--radius-md, 0.375rem);
|
|
450
|
+
@supports (corner-shape: squircle) {
|
|
451
|
+
corner-shape: squircle;
|
|
452
|
+
border-radius: calc(var(--radius-md, 0.375rem) * var(--squircle-ratio, 2));
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.skeleton-page {
|
|
457
|
+
width: var(--pg-min-width);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.skeleton-nav {
|
|
461
|
+
width: 4.25em;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.skeleton-select {
|
|
465
|
+
width: 3.25em;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/* Text placeholder: a text-height pill standing in for the info strings. */
|
|
469
|
+
.skeleton-bar {
|
|
470
|
+
display: inline-block;
|
|
471
|
+
vertical-align: middle;
|
|
472
|
+
height: 0.7em;
|
|
473
|
+
border-radius: var(--radius-full, 1e5px);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.pagination-controls {
|
|
477
|
+
display: flex;
|
|
478
|
+
align-items: center;
|
|
479
|
+
gap: 0.25rem;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
button {
|
|
483
|
+
display: inline-flex;
|
|
484
|
+
align-items: center;
|
|
485
|
+
justify-content: center;
|
|
486
|
+
min-width: var(--pg-min-width);
|
|
487
|
+
height: var(--pg-height);
|
|
488
|
+
border-radius: var(--radius-md, 0.375rem);
|
|
489
|
+
@supports (corner-shape: squircle) {
|
|
490
|
+
corner-shape: squircle;
|
|
491
|
+
border-radius: calc(var(--radius-md, 0.375rem) * var(--squircle-ratio, 2));
|
|
492
|
+
}
|
|
493
|
+
border: none;
|
|
494
|
+
background: transparent;
|
|
495
|
+
color: inherit;
|
|
496
|
+
cursor: pointer;
|
|
497
|
+
margin: 0;
|
|
498
|
+
padding: 0 0.375em;
|
|
499
|
+
font: inherit;
|
|
500
|
+
font-size: inherit;
|
|
501
|
+
line-height: 1;
|
|
502
|
+
gap: 0.25em;
|
|
503
|
+
/* Positioned + clipped so the click ripple anchors to (and rounds with) the
|
|
504
|
+
button, matching Button.svelte. */
|
|
505
|
+
position: relative;
|
|
506
|
+
overflow: hidden;
|
|
507
|
+
/* Base governs the OUT transition — the hover background eases away on leave.
|
|
508
|
+
`:hover` below re-declares `transition` without `background`, so the tint
|
|
509
|
+
snaps in instantly (see packages/components/CLAUDE.md). */
|
|
510
|
+
transition:
|
|
511
|
+
background 300ms,
|
|
512
|
+
transform 200ms ease;
|
|
513
|
+
white-space: nowrap;
|
|
514
|
+
|
|
515
|
+
/* Hover tint matches the Table's row-hover wash so it reads clearly on any
|
|
516
|
+
surface. Excludes `.current`: the active page button does nothing on click,
|
|
517
|
+
so it gets no hover affordance. Snaps in (transition drops `background`),
|
|
518
|
+
eases out via the base rule. */
|
|
519
|
+
&:hover:not(:disabled):not(.current) {
|
|
520
|
+
background: light-dark(
|
|
521
|
+
rgb(from var(--color-text, #000) r g b / 0.06),
|
|
522
|
+
rgb(from var(--color-text, #fff) r g b / 0.08)
|
|
523
|
+
);
|
|
524
|
+
transition: transform 200ms ease;
|
|
525
|
+
}
|
|
526
|
+
/* Press: a perspective Z-push that reads as a scale-down toward the button's
|
|
527
|
+
OWN centre. The `perspective()` lives in the button's own transform (not on
|
|
528
|
+
a shared parent), so each button's vanishing point is its own centre rather
|
|
529
|
+
than the container's — same feel as Button.svelte's press. */
|
|
530
|
+
&:active:not(:disabled) {
|
|
531
|
+
transform: perspective(100px)
|
|
532
|
+
translate3d(0, 1px, clamp(-10px, calc(0.2em - 12px), -2px));
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
&:focus-visible {
|
|
536
|
+
outline: 2px solid var(--color-action, #2563eb);
|
|
537
|
+
outline-offset: 1px;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/* The active page button is non-interactive (clicking it is a no-op), so it
|
|
541
|
+
keeps its solid background with no hover change. It still presses + ripples
|
|
542
|
+
for tactile feedback. */
|
|
543
|
+
&.current {
|
|
544
|
+
background: var(--color-action, #2563eb);
|
|
545
|
+
color: var(--color-action-text, white);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
&:disabled {
|
|
549
|
+
opacity: 0.4;
|
|
550
|
+
cursor: not-allowed;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
svg {
|
|
554
|
+
flex-shrink: 0;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.ellipsis {
|
|
559
|
+
display: inline-flex;
|
|
560
|
+
align-items: center;
|
|
561
|
+
justify-content: center;
|
|
562
|
+
min-width: var(--pg-min-width);
|
|
563
|
+
height: var(--pg-height);
|
|
564
|
+
line-height: 1;
|
|
565
|
+
color: var(--color-text-muted, #6b7280);
|
|
566
|
+
user-select: none;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
.simple-info {
|
|
570
|
+
padding: 0 0.75em;
|
|
571
|
+
white-space: nowrap;
|
|
572
|
+
color: var(--color-text-muted, #6b7280);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.compact-info {
|
|
576
|
+
padding: 0 0.25em;
|
|
577
|
+
white-space: nowrap;
|
|
578
|
+
font-variant-numeric: tabular-nums;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
.info {
|
|
582
|
+
white-space: nowrap;
|
|
583
|
+
color: var(--color-text-muted, #6b7280);
|
|
584
|
+
font-size: 0.875em;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.page-size {
|
|
588
|
+
display: inline-flex;
|
|
589
|
+
align-items: center;
|
|
590
|
+
gap: 0.375rem;
|
|
591
|
+
white-space: nowrap;
|
|
592
|
+
font-size: 0.875em;
|
|
593
|
+
color: var(--color-text-muted, #6b7280);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
select {
|
|
597
|
+
appearance: auto;
|
|
598
|
+
border: 1px solid
|
|
599
|
+
light-dark(var(--color-border, #d1d5db), var(--color-border, #4b5563));
|
|
600
|
+
border-radius: var(--radius-md, 0.375rem);
|
|
601
|
+
@supports (corner-shape: squircle) {
|
|
602
|
+
corner-shape: squircle;
|
|
603
|
+
border-radius: calc(var(--radius-md, 0.375rem) * var(--squircle-ratio, 2));
|
|
604
|
+
}
|
|
605
|
+
background: light-dark(var(--color-bg, white), var(--color-bg, #0a0a0a));
|
|
606
|
+
color: inherit;
|
|
607
|
+
font-size: inherit;
|
|
608
|
+
font-family: inherit;
|
|
609
|
+
padding: 0.25em 0.5em;
|
|
610
|
+
height: var(--pg-height);
|
|
611
|
+
cursor: pointer;
|
|
612
|
+
|
|
613
|
+
&:focus-visible {
|
|
614
|
+
outline: 2px solid var(--color-action, #2563eb);
|
|
615
|
+
outline-offset: 1px;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
@keyframes -global-delight-skeleton-shimmer {
|
|
620
|
+
0% {
|
|
621
|
+
transform: translateX(-100%);
|
|
622
|
+
}
|
|
623
|
+
55%,
|
|
624
|
+
100% {
|
|
625
|
+
transform: translateX(100%);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
@media (prefers-reduced-motion: reduce) {
|
|
630
|
+
.skeleton-nav::after,
|
|
631
|
+
.skeleton-page::after,
|
|
632
|
+
.skeleton-select::after,
|
|
633
|
+
.skeleton-bar::after {
|
|
634
|
+
animation: none;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
button {
|
|
638
|
+
transition: none;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
page?: number;
|
|
3
|
+
total_pages: number;
|
|
4
|
+
total_items?: number;
|
|
5
|
+
page_size?: number;
|
|
6
|
+
page_size_options?: number[];
|
|
7
|
+
simple?: boolean;
|
|
8
|
+
compact?: boolean;
|
|
9
|
+
show_page_size?: boolean;
|
|
10
|
+
show_info?: boolean;
|
|
11
|
+
sibling_count?: number;
|
|
12
|
+
boundary_count?: number;
|
|
13
|
+
size?: '0' | '1' | '2' | '3';
|
|
14
|
+
skeleton?: boolean;
|
|
15
|
+
id?: string;
|
|
16
|
+
class?: string;
|
|
17
|
+
onchange?: (detail: {
|
|
18
|
+
page: number;
|
|
19
|
+
}) => void;
|
|
20
|
+
onpagesizechange?: (detail: {
|
|
21
|
+
page_size: number;
|
|
22
|
+
}) => void;
|
|
23
|
+
};
|
|
24
|
+
declare const Pagination: import("svelte").Component<$$ComponentProps, {}, "page" | "page_size">;
|
|
25
|
+
type Pagination = ReturnType<typeof Pagination>;
|
|
26
|
+
export default Pagination;
|
|
27
|
+
//# sourceMappingURL=Pagination.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pagination.svelte.d.ts","sourceRoot":"","sources":["../../src/navigation/Pagination.svelte.ts"],"names":[],"mappings":"AAKC,KAAK,gBAAgB,GAAI;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC9C,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC3D,CAAC;AA8RH,QAAA,MAAM,UAAU,wEAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|