@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.
Files changed (195) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +136 -0
  3. package/SKILL.md +149 -0
  4. package/bin/agents.js +63 -0
  5. package/dist/actions/Alert.svelte +202 -0
  6. package/dist/actions/Alert.svelte.d.ts +36 -0
  7. package/dist/actions/Alert.svelte.d.ts.map +1 -0
  8. package/dist/actions/Button.svelte +1450 -0
  9. package/dist/actions/Button.svelte.d.ts +56 -0
  10. package/dist/actions/Button.svelte.d.ts.map +1 -0
  11. package/dist/actions/ButtonGroup.svelte +111 -0
  12. package/dist/actions/ButtonGroup.svelte.d.ts +41 -0
  13. package/dist/actions/ButtonGroup.svelte.d.ts.map +1 -0
  14. package/dist/actions/CommandPalette.svelte +939 -0
  15. package/dist/actions/CommandPalette.svelte.d.ts +37 -0
  16. package/dist/actions/CommandPalette.svelte.d.ts.map +1 -0
  17. package/dist/actions/ContextMenu.svelte +138 -0
  18. package/dist/actions/ContextMenu.svelte.d.ts +54 -0
  19. package/dist/actions/ContextMenu.svelte.d.ts.map +1 -0
  20. package/dist/actions/Modal.svelte +474 -0
  21. package/dist/actions/Modal.svelte.d.ts +28 -0
  22. package/dist/actions/Modal.svelte.d.ts.map +1 -0
  23. package/dist/actions/Popover.svelte +1214 -0
  24. package/dist/actions/Popover.svelte.d.ts +31 -0
  25. package/dist/actions/Popover.svelte.d.ts.map +1 -0
  26. package/dist/actions/Portal.svelte +80 -0
  27. package/dist/actions/Portal.svelte.d.ts +17 -0
  28. package/dist/actions/Portal.svelte.d.ts.map +1 -0
  29. package/dist/actions/ThemeToggle.svelte +345 -0
  30. package/dist/actions/ThemeToggle.svelte.d.ts +15 -0
  31. package/dist/actions/ThemeToggle.svelte.d.ts.map +1 -0
  32. package/dist/actions/index.d.ts +13 -0
  33. package/dist/actions/index.d.ts.map +1 -0
  34. package/dist/actions/index.js +10 -0
  35. package/dist/actions/scrollbar.d.ts +48 -0
  36. package/dist/actions/scrollbar.d.ts.map +1 -0
  37. package/dist/actions/scrollbar.js +404 -0
  38. package/dist/display/Accordion.svelte +586 -0
  39. package/dist/display/Accordion.svelte.d.ts +41 -0
  40. package/dist/display/Accordion.svelte.d.ts.map +1 -0
  41. package/dist/display/Avatar.svelte +527 -0
  42. package/dist/display/Avatar.svelte.d.ts +22 -0
  43. package/dist/display/Avatar.svelte.d.ts.map +1 -0
  44. package/dist/display/AvatarGroup.svelte +298 -0
  45. package/dist/display/AvatarGroup.svelte.d.ts +31 -0
  46. package/dist/display/AvatarGroup.svelte.d.ts.map +1 -0
  47. package/dist/display/Calendar.svelte +1366 -0
  48. package/dist/display/Calendar.svelte.d.ts +58 -0
  49. package/dist/display/Calendar.svelte.d.ts.map +1 -0
  50. package/dist/display/Chart.svelte +1426 -0
  51. package/dist/display/Chart.svelte.d.ts +35 -0
  52. package/dist/display/Chart.svelte.d.ts.map +1 -0
  53. package/dist/display/Code.svelte +780 -0
  54. package/dist/display/Code.svelte.d.ts +19 -0
  55. package/dist/display/Code.svelte.d.ts.map +1 -0
  56. package/dist/display/Comparison.svelte +686 -0
  57. package/dist/display/Comparison.svelte.d.ts +22 -0
  58. package/dist/display/Comparison.svelte.d.ts.map +1 -0
  59. package/dist/display/Counter.svelte +285 -0
  60. package/dist/display/Counter.svelte.d.ts +21 -0
  61. package/dist/display/Counter.svelte.d.ts.map +1 -0
  62. package/dist/display/Expand.svelte +48 -0
  63. package/dist/display/Expand.svelte.d.ts +9 -0
  64. package/dist/display/Expand.svelte.d.ts.map +1 -0
  65. package/dist/display/List.svelte +294 -0
  66. package/dist/display/List.svelte.d.ts +40 -0
  67. package/dist/display/List.svelte.d.ts.map +1 -0
  68. package/dist/display/ListContextReset.svelte +19 -0
  69. package/dist/display/ListContextReset.svelte.d.ts +7 -0
  70. package/dist/display/ListContextReset.svelte.d.ts.map +1 -0
  71. package/dist/display/ListItem.svelte +834 -0
  72. package/dist/display/ListItem.svelte.d.ts +22 -0
  73. package/dist/display/ListItem.svelte.d.ts.map +1 -0
  74. package/dist/display/QR.svelte +1193 -0
  75. package/dist/display/QR.svelte.d.ts +23 -0
  76. package/dist/display/QR.svelte.d.ts.map +1 -0
  77. package/dist/display/SplitPane.svelte +744 -0
  78. package/dist/display/SplitPane.svelte.d.ts +25 -0
  79. package/dist/display/SplitPane.svelte.d.ts.map +1 -0
  80. package/dist/display/Stat.svelte +439 -0
  81. package/dist/display/Stat.svelte.d.ts +24 -0
  82. package/dist/display/Stat.svelte.d.ts.map +1 -0
  83. package/dist/display/Table.svelte +4654 -0
  84. package/dist/display/Table.svelte.d.ts +249 -0
  85. package/dist/display/Table.svelte.d.ts.map +1 -0
  86. package/dist/display/TableCellEditor.svelte +935 -0
  87. package/dist/display/TableCellEditor.svelte.d.ts +58 -0
  88. package/dist/display/TableCellEditor.svelte.d.ts.map +1 -0
  89. package/dist/display/Timeline.svelte +1258 -0
  90. package/dist/display/Timeline.svelte.d.ts +43 -0
  91. package/dist/display/Timeline.svelte.d.ts.map +1 -0
  92. package/dist/display/Tree.svelte +1740 -0
  93. package/dist/display/Tree.svelte.d.ts +74 -0
  94. package/dist/display/Tree.svelte.d.ts.map +1 -0
  95. package/dist/display/Typewriter.svelte +338 -0
  96. package/dist/display/Typewriter.svelte.d.ts +22 -0
  97. package/dist/display/Typewriter.svelte.d.ts.map +1 -0
  98. package/dist/display/index.d.ts +24 -0
  99. package/dist/display/index.d.ts.map +1 -0
  100. package/dist/display/index.js +18 -0
  101. package/dist/feedback/Callout.svelte +529 -0
  102. package/dist/feedback/Callout.svelte.d.ts +24 -0
  103. package/dist/feedback/Callout.svelte.d.ts.map +1 -0
  104. package/dist/feedback/Confetti.svelte +631 -0
  105. package/dist/feedback/Confetti.svelte.d.ts +90 -0
  106. package/dist/feedback/Confetti.svelte.d.ts.map +1 -0
  107. package/dist/feedback/Progress.svelte +382 -0
  108. package/dist/feedback/Progress.svelte.d.ts +25 -0
  109. package/dist/feedback/Progress.svelte.d.ts.map +1 -0
  110. package/dist/feedback/Toast.svelte +967 -0
  111. package/dist/feedback/Toast.svelte.d.ts +54 -0
  112. package/dist/feedback/Toast.svelte.d.ts.map +1 -0
  113. package/dist/feedback/index.d.ts +7 -0
  114. package/dist/feedback/index.d.ts.map +1 -0
  115. package/dist/feedback/index.js +4 -0
  116. package/dist/form/Checkbox.svelte +449 -0
  117. package/dist/form/Checkbox.svelte.d.ts +27 -0
  118. package/dist/form/Checkbox.svelte.d.ts.map +1 -0
  119. package/dist/form/Fieldset.svelte +410 -0
  120. package/dist/form/Fieldset.svelte.d.ts +22 -0
  121. package/dist/form/Fieldset.svelte.d.ts.map +1 -0
  122. package/dist/form/FileUpload.svelte +934 -0
  123. package/dist/form/FileUpload.svelte.d.ts +41 -0
  124. package/dist/form/FileUpload.svelte.d.ts.map +1 -0
  125. package/dist/form/Form.svelte +530 -0
  126. package/dist/form/Form.svelte.d.ts +120 -0
  127. package/dist/form/Form.svelte.d.ts.map +1 -0
  128. package/dist/form/Input.svelte +2858 -0
  129. package/dist/form/Input.svelte.d.ts +66 -0
  130. package/dist/form/Input.svelte.d.ts.map +1 -0
  131. package/dist/form/Radio.svelte +507 -0
  132. package/dist/form/Radio.svelte.d.ts +39 -0
  133. package/dist/form/Radio.svelte.d.ts.map +1 -0
  134. package/dist/form/Range.svelte +912 -0
  135. package/dist/form/Range.svelte.d.ts +33 -0
  136. package/dist/form/Range.svelte.d.ts.map +1 -0
  137. package/dist/form/Rating.svelte +429 -0
  138. package/dist/form/Rating.svelte.d.ts +28 -0
  139. package/dist/form/Rating.svelte.d.ts.map +1 -0
  140. package/dist/form/Select.svelte +1933 -0
  141. package/dist/form/Select.svelte.d.ts +54 -0
  142. package/dist/form/Select.svelte.d.ts.map +1 -0
  143. package/dist/form/Toggle.svelte +645 -0
  144. package/dist/form/Toggle.svelte.d.ts +50 -0
  145. package/dist/form/Toggle.svelte.d.ts.map +1 -0
  146. package/dist/form/index.d.ts +15 -0
  147. package/dist/form/index.d.ts.map +1 -0
  148. package/dist/form/index.js +10 -0
  149. package/dist/index.d.ts +7 -0
  150. package/dist/index.d.ts.map +1 -0
  151. package/dist/index.js +6 -0
  152. package/dist/layout/README.md +172 -0
  153. package/dist/media/Carousel.svelte +2424 -0
  154. package/dist/media/Carousel.svelte.d.ts +47 -0
  155. package/dist/media/Carousel.svelte.d.ts.map +1 -0
  156. package/dist/media/Gallery.svelte +2881 -0
  157. package/dist/media/Gallery.svelte.d.ts +82 -0
  158. package/dist/media/Gallery.svelte.d.ts.map +1 -0
  159. package/dist/media/Image.svelte +389 -0
  160. package/dist/media/Image.svelte.d.ts +33 -0
  161. package/dist/media/Image.svelte.d.ts.map +1 -0
  162. package/dist/media/PDF.svelte +1793 -0
  163. package/dist/media/PDF.svelte.d.ts +44 -0
  164. package/dist/media/PDF.svelte.d.ts.map +1 -0
  165. package/dist/media/Panorama.svelte +1391 -0
  166. package/dist/media/Panorama.svelte.d.ts +47 -0
  167. package/dist/media/Panorama.svelte.d.ts.map +1 -0
  168. package/dist/media/Video.svelte +2501 -0
  169. package/dist/media/Video.svelte.d.ts +58 -0
  170. package/dist/media/Video.svelte.d.ts.map +1 -0
  171. package/dist/media/carousel.d.ts +211 -0
  172. package/dist/media/carousel.d.ts.map +1 -0
  173. package/dist/media/carousel.js +408 -0
  174. package/dist/media/index.d.ts +11 -0
  175. package/dist/media/index.d.ts.map +1 -0
  176. package/dist/media/index.js +5 -0
  177. package/dist/navigation/BottomSheet.svelte +636 -0
  178. package/dist/navigation/BottomSheet.svelte.d.ts +27 -0
  179. package/dist/navigation/BottomSheet.svelte.d.ts.map +1 -0
  180. package/dist/navigation/Breadcrumbs.svelte +611 -0
  181. package/dist/navigation/Breadcrumbs.svelte.d.ts +28 -0
  182. package/dist/navigation/Breadcrumbs.svelte.d.ts.map +1 -0
  183. package/dist/navigation/Pagination.svelte +641 -0
  184. package/dist/navigation/Pagination.svelte.d.ts +27 -0
  185. package/dist/navigation/Pagination.svelte.d.ts.map +1 -0
  186. package/dist/navigation/Steps.svelte +965 -0
  187. package/dist/navigation/Steps.svelte.d.ts +43 -0
  188. package/dist/navigation/Steps.svelte.d.ts.map +1 -0
  189. package/dist/navigation/Tabs.svelte +698 -0
  190. package/dist/navigation/Tabs.svelte.d.ts +41 -0
  191. package/dist/navigation/Tabs.svelte.d.ts.map +1 -0
  192. package/dist/navigation/index.d.ts +8 -0
  193. package/dist/navigation/index.d.ts.map +1 -0
  194. package/dist/navigation/index.js +5 -0
  195. 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">&hellip;</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"}