@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,474 @@
1
+ <script lang="ts" module>
2
+ let lastClickedElement = undefined as HTMLElement | undefined;
3
+ let lastClickedListenerCount = 0;
4
+ function onDocumentClick(event: MouseEvent) {
5
+ lastClickedElement = event.target as HTMLElement;
6
+ }
7
+ /** Listen for the last clicked element while at least one modal is mounted. Returns a cleanup */
8
+ function listenForLastClickedElement() {
9
+ if (!document) return;
10
+ lastClickedListenerCount++;
11
+ if (lastClickedListenerCount === 1) {
12
+ document.addEventListener('pointerdown', onDocumentClick);
13
+ }
14
+ return () => {
15
+ lastClickedListenerCount--;
16
+ if (lastClickedListenerCount === 0) {
17
+ document.removeEventListener('pointerdown', onDocumentClick);
18
+ }
19
+ };
20
+ }
21
+ </script>
22
+
23
+ <script lang="ts">
24
+ import { crossfade, fade, scale } from 'svelte/transition';
25
+ import { quartOut } from 'svelte/easing';
26
+ import { type Snippet } from 'svelte';
27
+ import { focusTrap, generateID, ripple } from '@delightstack/utilities';
28
+ import Button from './Button.svelte';
29
+ import { scrollbar } from './scrollbar';
30
+
31
+ let {
32
+ /** Title text displayed as the dialog header */
33
+ title = '',
34
+
35
+ /** Determines whether the dialog is open or not */
36
+ open = $bindable(false) as boolean,
37
+
38
+ /** Determines whether the dialog can be conventially closed using the escape key or backdrop click. */
39
+ closable = true,
40
+
41
+ /** Whether the close icon should be hidden or not */
42
+ disable_close_icon = false,
43
+
44
+ /** The ID of the modal - used to set/unset transition targets automatically */
45
+ modal_id = '',
46
+
47
+ /** The CSS string width of the modal (when on desktop) */
48
+ width = '',
49
+
50
+ /** The CSS string height of the modal (when on desktop) */
51
+ height = '',
52
+
53
+ /** The CSS string maximum width of the modal */
54
+ max_width = 'calc(100vw - 2rem)',
55
+
56
+ /** The CSS string maximum height of the modal */
57
+ max_height = 'calc(100svh - 2rem)',
58
+
59
+ /** The element that the modal will be animated from when opening */
60
+ transition_target = undefined as HTMLElement | Element | undefined,
61
+
62
+ /** The css style string added to the component from the parent */
63
+ style = '',
64
+
65
+ /** Specifies a custom class name for the dialog */
66
+ class: class_name = '',
67
+
68
+ /** The snippet used to render the modal body */
69
+ children = undefined as undefined | Snippet,
70
+
71
+ /** The snippet used to render the header bar */
72
+ header = undefined as undefined | Snippet,
73
+
74
+ /** The snippet used to render a child at the start of the header bar. Can't be used if 'header' is supplied */
75
+ header_start = undefined as undefined | Snippet,
76
+
77
+ /** The snippet used to render a child at the end of the header bar. Can't be used if 'header' is supplied */
78
+ header_end = undefined as undefined | Snippet,
79
+
80
+ /** The snippet used to render the modal footer */
81
+ footer = undefined as undefined | Snippet,
82
+
83
+ /** The snippet used to render the modal footer at the start. Can't be used if 'footer' is supplied */
84
+ footer_start = undefined as undefined | Snippet,
85
+
86
+ /** The snippet used to render the modal footer at the end. Can't be used if 'footer' is supplied */
87
+ footer_end = undefined as undefined | Snippet,
88
+
89
+ /** The function to call when the dialog is closed. If false is returned, the modal will not be closed */
90
+ onclose = undefined as undefined | (() => boolean | undefined | void),
91
+
92
+ /** The function to call when the dialog is opened */
93
+ onopen = undefined as undefined | (() => void),
94
+
95
+ /** The function to call when the backdrop is clicked */
96
+ onbackdropclick = undefined as undefined | (() => void),
97
+
98
+ ...rest
99
+ } = $props();
100
+
101
+ const titleId = `modal-title-${generateID({ length: 6 })}`;
102
+ const bodyId = `modal-body-${generateID({ length: 6 })}`;
103
+ const easing = (t: number, factor = 0.5) => quartOut(t) * factor + (1 - factor);
104
+ let _open = $state(open);
105
+ $effect(() => listenForLastClickedElement());
106
+ $effect(() => {
107
+ if (open === _open) return;
108
+ const target = transition_target || lastClickedElement;
109
+ if (target && open) send(target, { key: 'modal' });
110
+ _open = open;
111
+ });
112
+
113
+ // Setup the send/receive animation so the modal body can be animated into existence
114
+ const [send, receive] = crossfade({
115
+ duration: 300,
116
+ easing,
117
+ fallback: (node) => {
118
+ const style = getComputedStyle(node);
119
+ const transform = style.transform === 'none' ? '' : style.transform;
120
+ return {
121
+ duration: 300,
122
+ easing,
123
+ css: (t) => `transform: ${transform} translateZ(0px) scale(${t}); opacity: ${t}`,
124
+ };
125
+ },
126
+ });
127
+
128
+ function close() {
129
+ if (closable && _open) {
130
+ const accepted = onclose?.() ?? true;
131
+ if (accepted) {
132
+ _open = false;
133
+ open = false;
134
+ }
135
+ }
136
+ }
137
+ function handleEscapeKey({ key }: KeyboardEvent) {
138
+ if (key === 'Escape') close();
139
+ }
140
+
141
+ function mountModal(node: HTMLElement) {
142
+ if (onopen) onopen();
143
+ if (transition_target) transition_target = undefined;
144
+ return {
145
+ destroy: () => {
146
+ if (_open) return;
147
+ if (onclose) onclose();
148
+ },
149
+ };
150
+ }
151
+ </script>
152
+
153
+ <svelte:window onkeyup={handleEscapeKey} />
154
+
155
+ {#if _open}
156
+ <div
157
+ class={['modal', class_name].filter(Boolean).join(' ')}
158
+ {style}
159
+ in:receive={{ key: 'modal' }}
160
+ out:scale={{ duration: 100, start: 0.75 }}
161
+ role="dialog"
162
+ aria-modal="true"
163
+ aria-labelledby={titleId}
164
+ aria-describedby={bodyId}
165
+ {@attach focusTrap({
166
+ escapeDeactivates: false,
167
+ allowOutsideClick: true,
168
+ returnFocusOnDeactivate: true,
169
+ initialFocus: false,
170
+ })}
171
+ use:mountModal
172
+ {...rest}>
173
+ <div
174
+ class="body"
175
+ class:has-footer={!!(footer || footer_start || footer_end)}
176
+ id={bodyId}
177
+ style:width
178
+ style:height
179
+ style:max-width={max_width}
180
+ style:max-height={max_height}
181
+ {@attach scrollbar()}>
182
+ {#if (closable && !disable_close_icon) || title || header || header_start || header_end}
183
+ <header
184
+ class:bar={title || header || header_start || header_end}
185
+ class:no-close={!closable || disable_close_icon}>
186
+ {#if closable && !disable_close_icon}
187
+ <div class="close">
188
+ <Button transparent icon onclick={close} size="0">
189
+ <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
190
+ <path
191
+ d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
192
+ </svg>
193
+ </Button>
194
+ </div>
195
+ {/if}
196
+ {#if title}<h2>{title}</h2>{/if}
197
+ {#if header}
198
+ {@render header()}
199
+ {:else}
200
+ {#if header_start}{@render header_start()}{/if}
201
+ <div class="spacer"></div>
202
+ {#if header_end}{@render header_end()}{/if}
203
+ {/if}
204
+ </header>
205
+ {/if}
206
+ {#if children}{@render children()}{/if}
207
+ {#if footer || footer_start || footer_end}
208
+ <footer>
209
+ {#if footer}
210
+ {@render footer()}
211
+ {:else}
212
+ {#if footer_start}{@render footer_start()}{/if}
213
+ <div class="spacer"></div>
214
+ {#if footer_end}{@render footer_end()}{/if}
215
+ {/if}
216
+ </footer>
217
+ {/if}
218
+ </div>
219
+ <div class="modal-fg"></div>
220
+ </div>
221
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
222
+ <div
223
+ class="modal-bg"
224
+ style:cursor={closable ? 'pointer' : ''}
225
+ style:pointer-events={closable ? '' : 'none'}
226
+ role="button"
227
+ tabindex="-1"
228
+ {@attach ripple()}
229
+ onclick={() => {
230
+ if (onbackdropclick) onbackdropclick();
231
+ close();
232
+ }}
233
+ in:fade={{ duration: 250, delay: 50 }}
234
+ out:fade={{ duration: 120 }}>
235
+ </div>
236
+ {/if}
237
+
238
+ <style>
239
+ :global(html:has(.modal)) {
240
+ overflow: hidden;
241
+ }
242
+ :global(::view-transition-old(modal-fg)),
243
+ :global(::view-transition-new(modal-fg)) {
244
+ /* Prevent the default animation,
245
+ so both views remain opacity:1 throughout the transition */
246
+ animation: none;
247
+ /* Use normal blending,
248
+ so the new view sits on top and obscures the old view */
249
+ mix-blend-mode: normal;
250
+ /* Make the height the same as the group,
251
+ meaning the view size might not match its aspect-ratio. */
252
+ height: 100%;
253
+ /* Clip any overflow of the view */
254
+ overflow: clip;
255
+ }
256
+ .modal {
257
+ /* panel sits one above the backdrop, which is at --layer-modal */
258
+ --layer: calc(var(--layer-modal) + 1);
259
+ /* Private tokens: the panel wants a bigger radius/shadow than the shared
260
+ defaults, but redefining --radius-lg/--shadow-md here would leak into
261
+ every component rendered inside the modal. The radius is clamped so an
262
+ over-rounded --radius-2xl can't turn this large panel into a blob — see
263
+ --radius-cap. Every panel surface (.body/.modal-fg/.close) inherits it. */
264
+ --_radius: min(var(--radius-2xl), var(--radius-cap, 40px));
265
+ --_shadow: var(--shadow-lg);
266
+ display: grid;
267
+ position: fixed;
268
+ z-index: var(--layer);
269
+ top: 0;
270
+ left: 0;
271
+ bottom: 0;
272
+ right: 0;
273
+ grid-template-columns: 100%;
274
+ grid-template-rows: 100%;
275
+ width: 100%;
276
+ height: 100%;
277
+ align-content: center;
278
+ justify-content: center;
279
+ pointer-events: none;
280
+
281
+ @media (min-width: 768px) {
282
+ overflow: hidden;
283
+ grid-template-rows: max-content;
284
+ grid-template-columns: max-content;
285
+ border-radius: var(--_radius);
286
+ @supports (corner-shape: squircle) {
287
+ corner-shape: squircle;
288
+ border-radius: calc(var(--_radius) * var(--squircle-ratio, 2));
289
+ }
290
+ }
291
+
292
+ header {
293
+ display: flex;
294
+ align-items: center;
295
+ position: absolute;
296
+ bottom: 0;
297
+ left: 0;
298
+ background-color: var(--color-bg);
299
+ z-index: 2;
300
+ gap: 0.5rem;
301
+ padding: 0.5rem 0.5rem 0.5rem 0;
302
+ overflow-x: auto;
303
+ @media (max-width: 767px) {
304
+ width: 100%;
305
+ :global(> *) {
306
+ flex-shrink: 0;
307
+ }
308
+ h2 {
309
+ font-size: 1.15rem;
310
+ }
311
+ }
312
+ @media (min-width: 768px) {
313
+ &.bar {
314
+ padding: 0;
315
+ position: sticky;
316
+ margin: -1.5rem -1rem 0.5rem -1.25rem;
317
+ height: 4rem;
318
+ top: calc(-2rem - 1px);
319
+ bottom: unset;
320
+ left: unset;
321
+ overflow-x: hidden;
322
+ /* Without a close button the title sits on the edge of the bar;
323
+ pad it so its distance from the panel's left edge matches its
324
+ distance from the top. */
325
+ &.no-close {
326
+ padding-inline-start: 1.25rem;
327
+ /*padding-block-start: 0.5rem;*/
328
+ margin-block-start: -1rem;
329
+ }
330
+ }
331
+ }
332
+ @media (max-width: 767px) {
333
+ &.no-close {
334
+ padding-inline-start: 0.75rem;
335
+ }
336
+ }
337
+ &:not(.bar) {
338
+ background-color: transparent;
339
+ position: sticky;
340
+ left: 0;
341
+ top: -1rem;
342
+ bottom: unset;
343
+ height: 4rem;
344
+ width: 4rem;
345
+ margin: -3rem 0 0 -2rem;
346
+ overflow: hidden;
347
+ @media (min-width: 768px) {
348
+ left: -1rem;
349
+ top: -2rem;
350
+ margin: -2rem 0 0 -2rem;
351
+ }
352
+ }
353
+ .close {
354
+ position: sticky;
355
+ left: 0;
356
+ background-color: var(--color-bg);
357
+ border-radius: var(--_radius);
358
+ @supports (corner-shape: squircle) {
359
+ corner-shape: squircle;
360
+ border-radius: calc(var(--_radius) * var(--squircle-ratio, 2));
361
+ }
362
+ }
363
+ .spacer {
364
+ flex: 1;
365
+ }
366
+ }
367
+ }
368
+ .body {
369
+ grid-column: 1 / 1;
370
+ grid-row: 1 / 1;
371
+ height: 100%;
372
+ z-index: 1;
373
+ padding: 1rem 0.5rem;
374
+ view-transition-name: modal-body;
375
+ overflow-y: auto;
376
+ overflow-x: hidden;
377
+ overscroll-behavior: contain;
378
+ pointer-events: auto;
379
+ /* The overlay scrollbar takes no layout space (the native gutter that
380
+ scrollbar-gutter: stable both-edges used to reserve is gone), so the
381
+ inline padding carries the full edge distance itself. */
382
+ scrollbar-gutter: stable both-edges;
383
+ @media (max-width: 767px) {
384
+ min-width: 100vw;
385
+ padding-bottom: 4rem;
386
+ }
387
+ @media (min-width: 768px) {
388
+ padding: 2rem 2.5rem;
389
+ /* The body is the scroll container, so its own rounded clip is what
390
+ actually crops the sticky header/footer. Match the panel's squircle
391
+ corners — without this their square boxes bleed over the curve. */
392
+ border-radius: var(--_radius);
393
+ @supports (corner-shape: squircle) {
394
+ corner-shape: squircle;
395
+ border-radius: calc(var(--_radius) * var(--squircle-ratio, 2));
396
+ }
397
+ /* The footer carries its own bottom spacing (and must end flush with
398
+ the body's content box for sticky positioning to leave it at the
399
+ bottom edge — a negative margin over padding gets clamped back up,
400
+ overlapping the content above). */
401
+ &.has-footer {
402
+ padding-bottom: 0;
403
+ }
404
+ }
405
+ /* Corner inset for the styled-native fallback (pre-JS / no overlay) */
406
+ --scrollbar-track-inset: calc(var(--_radius, 10px) / 2);
407
+ }
408
+ footer {
409
+ display: flex;
410
+ align-items: center;
411
+ gap: 0.5rem;
412
+ justify-content: flex-end;
413
+ background-color: var(--color-bg);
414
+ border-top: 1px solid var(--color-border, rgb(from var(--color-text) r g b / 0.1));
415
+ /* Full-bleed: negative inline margins cancel the body's padding so the
416
+ divider spans edge to edge, like the header bar. */
417
+ margin: 1rem -0.5rem 0;
418
+ padding: 0.75rem 1rem;
419
+ @media (min-width: 768px) {
420
+ /* The body drops its bottom padding when a footer is present (see
421
+ .body.has-footer), so the footer ends flush with the modal's
422
+ bottom edge and sticks there while long content scrolls underneath.
423
+ Actions sit closer to the edge than body content would (1rem vs the
424
+ body's 2rem) — a chrome row, not content. */
425
+ position: sticky;
426
+ bottom: 0;
427
+ z-index: 2;
428
+ margin: 1.5rem -2.5rem 0;
429
+ /* Inline padding matches the block padding, so the action buttons sit
430
+ the same distance from the bottom and side edges. */
431
+ padding: 1.5rem;
432
+ }
433
+ /* On mobile the footer stays in flow: the header bar is pinned to the
434
+ bottom of the screen there, and the body's 4rem bottom padding keeps
435
+ the footer clear of it. */
436
+ }
437
+ .modal-fg {
438
+ view-transition-name: modal-fg;
439
+ z-index: -1;
440
+ grid-column: 1 / 1;
441
+ grid-row: 1 / 1;
442
+ height: 100%;
443
+ background-color: var(--color-bg);
444
+ z-index: -1;
445
+ box-shadow: var(--_shadow);
446
+ @media (min-width: 768px) {
447
+ border-radius: var(--_radius);
448
+ @supports (corner-shape: squircle) {
449
+ corner-shape: squircle;
450
+ border-radius: calc(var(--_radius) * var(--squircle-ratio, 2));
451
+ }
452
+ }
453
+ }
454
+ .modal-bg {
455
+ --layer: var(--layer-modal);
456
+ position: fixed;
457
+ top: 0;
458
+ bottom: 0;
459
+ right: 0;
460
+ left: 0;
461
+ backdrop-filter: blur(15px);
462
+ z-index: var(--layer);
463
+ &::after {
464
+ content: '';
465
+ background-color: var(--color-text);
466
+ position: absolute;
467
+ top: 0;
468
+ left: 0;
469
+ right: 0;
470
+ bottom: 0;
471
+ opacity: 0.2;
472
+ }
473
+ }
474
+ </style>
@@ -0,0 +1,28 @@
1
+ import { type Snippet } from 'svelte';
2
+ declare const Modal: import("svelte").Component<{
3
+ title?: string;
4
+ open?: boolean;
5
+ closable?: boolean;
6
+ disable_close_icon?: boolean;
7
+ modal_id?: string;
8
+ width?: string;
9
+ height?: string;
10
+ max_width?: string;
11
+ max_height?: string;
12
+ transition_target?: HTMLElement | Element | undefined;
13
+ style?: string;
14
+ class?: string;
15
+ children?: undefined | Snippet;
16
+ header?: undefined | Snippet;
17
+ header_start?: undefined | Snippet;
18
+ header_end?: undefined | Snippet;
19
+ footer?: undefined | Snippet;
20
+ footer_start?: undefined | Snippet;
21
+ footer_end?: undefined | Snippet;
22
+ onclose?: undefined | (() => boolean | undefined | void);
23
+ onopen?: undefined | (() => void);
24
+ onbackdropclick?: undefined | (() => void);
25
+ } & Record<string, any>, {}, "open">;
26
+ type Modal = ReturnType<typeof Modal>;
27
+ export default Modal;
28
+ //# sourceMappingURL=Modal.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Modal.svelte.d.ts","sourceRoot":"","sources":["../../src/actions/Modal.svelte.ts"],"names":[],"mappings":"AA0BA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AAmMtC,QAAA,MAAM,KAAK;YAxL2C,MAAM;WAAS,OAAO;eAAa,OAAO;yBAAuB,OAAO;eAAa,MAAM;YAAU,MAAM;aAAW,MAAM;gBAAc,MAAM;iBAAe,MAAM;wBAAsB,WAAW,GAAG,OAAO,GAAG,SAAS;YAAU,MAAM;YAAU,MAAM;eAAa,SAAS,GAAG,OAAO;aAAW,SAAS,GAAG,OAAO;mBAAiB,SAAS,GAAG,OAAO;iBAAe,SAAS,GAAG,OAAO;aAAW,SAAS,GAAG,OAAO;mBAAiB,SAAS,GAAG,OAAO;iBAAe,SAAS,GAAG,OAAO;cAAY,SAAS,GAAG,CAAC,MAAM,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC;aAAW,SAAS,GAAG,CAAC,MAAM,IAAI,CAAC;sBAAoB,SAAS,GAAG,CAAC,MAAM,IAAI,CAAC;oCAwLhnB,CAAC;AACpD,KAAK,KAAK,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AACtC,eAAe,KAAK,CAAC"}