@dryui/ui 1.2.0 → 1.3.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 (74) hide show
  1. package/dist/accordion/accordion-button-trigger.svelte +6 -0
  2. package/dist/alert/alert.svelte +2 -1
  3. package/dist/alert-dialog/alert-dialog-content.svelte +9 -69
  4. package/dist/alpha-slider/alpha-slider-input.svelte +4 -0
  5. package/dist/avatar/avatar.svelte +3 -4
  6. package/dist/backdrop/backdrop.svelte +12 -9
  7. package/dist/badge/badge.svelte +84 -72
  8. package/dist/button/button.svelte +3 -6
  9. package/dist/button-group/button-group.svelte +2 -2
  10. package/dist/calendar/calendar-button-grid.svelte +1 -1
  11. package/dist/card/card-root.svelte +9 -4
  12. package/dist/chip-group/chip-group-root.svelte +2 -2
  13. package/dist/code-block/code-block-button.svelte +6 -2
  14. package/dist/context-menu/context-menu-content.svelte +12 -79
  15. package/dist/diagram/diagram.svelte +3 -4
  16. package/dist/dialog/dialog-content.svelte +5 -103
  17. package/dist/drag-and-drop/drag-and-drop-item.svelte +1 -1
  18. package/dist/drawer/drawer-dialog-content.svelte +5 -155
  19. package/dist/dropdown-menu/dropdown-menu-content.svelte +14 -71
  20. package/dist/heading/heading.svelte +7 -6
  21. package/dist/hover-card/hover-card-content.svelte +3 -12
  22. package/dist/icon/icon.svelte +2 -2
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.js +1 -1
  25. package/dist/input/input.svelte +3 -3
  26. package/dist/internal/calendar-grid-button.svelte +29 -26
  27. package/dist/internal/calendar-grid-button.svelte.d.ts +1 -0
  28. package/dist/internal/close-button-base.svelte +15 -2
  29. package/dist/internal/close-button-base.svelte.d.ts +1 -0
  30. package/dist/internal/modal-content.svelte +331 -0
  31. package/dist/internal/modal-content.svelte.d.ts +26 -0
  32. package/dist/link-preview/link-preview-content.svelte +3 -11
  33. package/dist/logo-mark/logo-mark.svelte +2 -3
  34. package/dist/mask-reveal/mask-reveal.svelte +17 -14
  35. package/dist/mega-menu/mega-menu-panel.svelte +14 -2
  36. package/dist/mega-menu/mega-menu-panel.svelte.d.ts +1 -0
  37. package/dist/menubar/menubar-content.svelte +21 -81
  38. package/dist/noise/noise.svelte +1 -0
  39. package/dist/option-picker/option-picker-preview.svelte +11 -10
  40. package/dist/popover/popover-content.svelte +10 -36
  41. package/dist/progress/progress.svelte +2 -2
  42. package/dist/prompt-input/index.d.ts +13 -1
  43. package/dist/prompt-input/prompt-input-button-textarea.svelte +13 -1
  44. package/dist/prompt-input/prompt-input-button-textarea.svelte.d.ts +3 -0
  45. package/dist/shimmer/shimmer.svelte +28 -3
  46. package/dist/slider/slider-input.svelte +3 -4
  47. package/dist/spacer/spacer.svelte +2 -2
  48. package/dist/spinner/spinner.svelte +2 -2
  49. package/dist/tabs/tabs-content.svelte +1 -1
  50. package/dist/tabs/tabs-list.svelte +1 -0
  51. package/dist/tag/tag-button.svelte +2 -3
  52. package/dist/text/text.svelte +4 -15
  53. package/dist/themes/dark.css +40 -24
  54. package/dist/themes/default.css +16 -12
  55. package/dist/themes/token-scope.d.ts +1 -0
  56. package/dist/themes/token-scope.js +1 -0
  57. package/dist/timeline/timeline-icon.svelte +11 -1
  58. package/dist/timeline/timeline-item.svelte +18 -5
  59. package/dist/timeline/timeline-root.svelte +3 -10
  60. package/dist/toast/toast-close-button.svelte +19 -7
  61. package/dist/toast/toast-provider.svelte +22 -1
  62. package/dist/toggle/toggle-button.svelte +7 -3
  63. package/dist/toggle-group/toggle-group-root.svelte +2 -2
  64. package/dist/tooltip/tooltip-content.svelte +8 -32
  65. package/dist/tour/tour-root.css +26 -26
  66. package/dist/tree/tree-item-label.svelte +8 -14
  67. package/dist/tree/tree-item-label.svelte.d.ts +2 -2
  68. package/dist/typography/heading.svelte +7 -6
  69. package/dist/typography/text.svelte +4 -3
  70. package/package.json +6 -2
  71. package/skills/dryui/SKILL.md +3 -3
  72. package/skills/dryui/rules/native-web-transitions.md +2 -2
  73. package/dist/themes/use-theme-override.svelte.d.ts +0 -1
  74. package/dist/themes/use-theme-override.svelte.js +0 -1
package/dist/index.js CHANGED
@@ -150,5 +150,5 @@ export { ShaderCanvas, PRESETS as SHADER_PRESETS } from './shader-canvas/index.j
150
150
  export { Svg } from './svg/index.js';
151
151
  export { StarRating } from './star-rating/index.js';
152
152
  export { Tag } from './tag/index.js';
153
- export { useThemeOverride } from './themes/use-theme-override.svelte.js';
153
+ export { TokenScope } from './themes/token-scope.js';
154
154
  export { Diagram } from './diagram/index.js';
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { HTMLInputAttributes } from 'svelte/elements';
3
- import { getFormControlCtx } from '@dryui/primitives';
3
+ import { getFormControlCtx, variantAttrs } from '@dryui/primitives';
4
4
 
5
5
  interface Props extends Omit<HTMLInputAttributes, 'size'> {
6
6
  value?: string;
@@ -34,8 +34,7 @@
34
34
  aria-invalid={ctx?.hasError || undefined}
35
35
  aria-errormessage={ctx?.errorMessageId}
36
36
  data-disabled={isDisabled || undefined}
37
- data-size={size}
38
- data-variant={variant !== 'default' ? variant : undefined}
37
+ {...variantAttrs({ size, variant: variant !== 'default' ? variant : undefined })}
39
38
  class={className}
40
39
  {...rest}
41
40
  />
@@ -45,6 +44,7 @@
45
44
  .wrapper {
46
45
  container-type: inline-size;
47
46
  display: grid;
47
+ grid-template-columns: minmax(0, 1fr);
48
48
  }
49
49
 
50
50
  input {
@@ -41,9 +41,10 @@
41
41
 
42
42
  interface Props extends HTMLAttributes<HTMLDivElement> {
43
43
  adapter: CalendarGridAdapter;
44
+ hideHeader?: boolean;
44
45
  }
45
46
 
46
- let { adapter, class: className, ...rest }: Props = $props();
47
+ let { adapter, hideHeader = false, class: className, ...rest }: Props = $props();
47
48
 
48
49
  let containerEl = $state<HTMLDivElement>();
49
50
 
@@ -90,31 +91,33 @@
90
91
 
91
92
  <div {@attach bindContainer} class={className} {...rest} data-calendar-grid>
92
93
  <div role="group" aria-label={monthYearLabel} data-calendar-panel>
93
- <div data-calendar-header>
94
- <Button
95
- variant="trigger"
96
- size="icon-sm"
97
- type="button"
98
- aria-label="Previous month"
99
- disabled={adapter.disabled}
100
- onclick={() => adapter.prevMonth()}
101
- >
102
- &#8249;
103
- </Button>
104
- <span aria-live="polite" aria-atomic="true" data-calendar-heading>
105
- {monthYearLabel}
106
- </span>
107
- <Button
108
- variant="trigger"
109
- size="icon-sm"
110
- type="button"
111
- aria-label="Next month"
112
- disabled={adapter.disabled}
113
- onclick={() => adapter.nextMonth()}
114
- >
115
- &#8250;
116
- </Button>
117
- </div>
94
+ {#if !hideHeader}
95
+ <div data-calendar-header>
96
+ <Button
97
+ variant="trigger"
98
+ size="icon-sm"
99
+ type="button"
100
+ aria-label="Previous month"
101
+ disabled={adapter.disabled}
102
+ onclick={() => adapter.prevMonth()}
103
+ >
104
+ &#8249;
105
+ </Button>
106
+ <span aria-live="polite" aria-atomic="true" data-calendar-heading>
107
+ {monthYearLabel}
108
+ </span>
109
+ <Button
110
+ variant="trigger"
111
+ size="icon-sm"
112
+ type="button"
113
+ aria-label="Next month"
114
+ disabled={adapter.disabled}
115
+ onclick={() => adapter.nextMonth()}
116
+ >
117
+ &#8250;
118
+ </Button>
119
+ </div>
120
+ {/if}
118
121
 
119
122
  <div role="grid" aria-label={monthYearLabel}>
120
123
  <div role="row" data-calendar-row>
@@ -21,6 +21,7 @@ export interface CalendarGridAdapter {
21
21
  import type { HTMLAttributes } from 'svelte/elements';
22
22
  interface Props extends HTMLAttributes<HTMLDivElement> {
23
23
  adapter: CalendarGridAdapter;
24
+ hideHeader?: boolean;
24
25
  }
25
26
  declare const CalendarGridButton: import("svelte").Component<Props, {}, "">;
26
27
  type CalendarGridButton = ReturnType<typeof CalendarGridButton>;
@@ -7,13 +7,26 @@
7
7
  interface Props extends HTMLButtonAttributes {
8
8
  variant?: ButtonVariant;
9
9
  size?: ButtonSize;
10
+ label?: string;
10
11
  children?: Snippet;
11
12
  }
12
13
 
13
- let { variant = 'trigger', size = 'icon-sm', children, ...rest }: Props = $props();
14
+ let {
15
+ variant = 'trigger',
16
+ size = 'icon-sm',
17
+ label = 'Dismiss',
18
+ children,
19
+ ...rest
20
+ }: Props = $props();
14
21
  </script>
15
22
 
16
- <Button {variant} {size} type="button" {...rest}>
23
+ <Button
24
+ {variant}
25
+ {size}
26
+ type="button"
27
+ aria-label={children !== undefined ? undefined : label}
28
+ {...rest}
29
+ >
17
30
  {#if children}
18
31
  {@render children()}
19
32
  {:else}
@@ -4,6 +4,7 @@ import type { ButtonVariant, ButtonSize } from '../button/index.js';
4
4
  interface Props extends HTMLButtonAttributes {
5
5
  variant?: ButtonVariant;
6
6
  size?: ButtonSize;
7
+ label?: string;
7
8
  children?: Snippet;
8
9
  }
9
10
  declare const CloseButtonBase: import("svelte").Component<Props, {}, "">;
@@ -0,0 +1,331 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLDialogAttributes } from 'svelte/elements';
4
+
5
+ type ModalSide = 'center' | 'top' | 'right' | 'bottom' | 'left';
6
+ type ModalVariant = 'dialog' | 'drawer' | 'alert-dialog';
7
+
8
+ interface Props extends HTMLDialogAttributes {
9
+ ctx: { readonly open: boolean; readonly headerId: string; close: () => void };
10
+ /** Presentational variant; drives the scoped style block. */
11
+ variant: ModalVariant;
12
+ /**
13
+ * Positioning hint. `center` stacks the panel in the middle;
14
+ * `top`/`right`/`bottom`/`left` pin it to an edge (drawer-style).
15
+ */
16
+ side?: ModalSide;
17
+ /** When false, clicking the dialog backdrop does not dismiss. */
18
+ dismissOnBackdropClick?: boolean;
19
+ /** When false, pressing Escape does not dismiss. */
20
+ dismissOnEscape?: boolean;
21
+ children: Snippet;
22
+ }
23
+
24
+ let {
25
+ ctx,
26
+ variant,
27
+ side = 'center',
28
+ dismissOnBackdropClick = true,
29
+ dismissOnEscape = true,
30
+ class: className,
31
+ children,
32
+ ...rest
33
+ }: Props = $props();
34
+
35
+ let dialogEl = $state<HTMLDialogElement>();
36
+
37
+ $effect(() => {
38
+ if (!dialogEl) return;
39
+
40
+ if (side !== 'center') {
41
+ // Native <dialog> applies a max-width that leaves a strip beside edge drawers.
42
+ dialogEl.style.setProperty('max-width', 'none');
43
+ }
44
+
45
+ if (ctx.open && !dialogEl.open) {
46
+ dialogEl.showModal();
47
+ }
48
+ if (!ctx.open && dialogEl.open) {
49
+ dialogEl.close();
50
+ }
51
+ });
52
+ </script>
53
+
54
+ <dialog
55
+ bind:this={dialogEl}
56
+ data-modal-content
57
+ data-variant={variant}
58
+ data-side={side}
59
+ data-dialog-content={variant === 'dialog' ? '' : undefined}
60
+ data-drawer-content={variant === 'drawer' ? '' : undefined}
61
+ data-alert-dialog-content={variant === 'alert-dialog' ? '' : undefined}
62
+ aria-labelledby={ctx.headerId || undefined}
63
+ data-state={ctx.open ? 'open' : 'closed'}
64
+ onclose={() => ctx.close()}
65
+ oncancel={dismissOnEscape ? undefined : (e) => e.preventDefault()}
66
+ onclick={dismissOnBackdropClick
67
+ ? (e) => {
68
+ if (e.target === dialogEl) {
69
+ ctx.close();
70
+ }
71
+ }
72
+ : undefined}
73
+ class={className}
74
+ {...rest}
75
+ >
76
+ <div
77
+ data-modal-panel
78
+ data-variant={variant}
79
+ data-side={side}
80
+ data-dialog-panel={variant === 'dialog' ? '' : undefined}
81
+ data-drawer-panel={variant === 'drawer' ? '' : undefined}
82
+ data-alert-dialog-panel={variant === 'alert-dialog' ? '' : undefined}
83
+ >
84
+ {@render children()}
85
+ </div>
86
+ </dialog>
87
+
88
+ <style>
89
+ [data-modal-content] {
90
+ position: fixed;
91
+ inset: 0;
92
+ height: 100vh;
93
+ height: 100dvh;
94
+ max-height: none;
95
+ border: none;
96
+ background: transparent;
97
+ color: var(--dry-color-text-strong);
98
+ padding: 0;
99
+ }
100
+
101
+ [data-modal-content][data-variant]:not([open]) {
102
+ display: none;
103
+ }
104
+
105
+ /* ---------- Dialog (center) ---------- */
106
+
107
+ [data-modal-content][data-variant='dialog'] {
108
+ /* dryui-allow width */
109
+ width: 100vw;
110
+ /* dryui-allow width */
111
+ max-width: none;
112
+ display: grid;
113
+ grid-template-columns: min(90vw, var(--dry-dialog-max-width, 32rem));
114
+ place-content: center;
115
+ place-items: center;
116
+ overflow: visible;
117
+ }
118
+
119
+ [data-modal-content][data-variant='dialog']::backdrop {
120
+ background: var(--dry-overlay-bg, var(--dry-color-overlay-backdrop));
121
+ backdrop-filter: blur(var(--dry-overlay-blur, 12px));
122
+ -webkit-backdrop-filter: blur(var(--dry-overlay-blur, 12px));
123
+ }
124
+
125
+ [data-modal-panel][data-variant='dialog'] {
126
+ --dry-dialog-border: var(--dry-overlay-border, var(--dry-color-stroke-weak));
127
+ --dry-dialog-padding: var(--dry-space-8);
128
+ --dry-radius-nested: max(
129
+ 0px,
130
+ calc(
131
+ var(--dry-dialog-radius, var(--dry-overlay-radius, var(--dry-radius-2xl))) -
132
+ var(--dry-dialog-padding)
133
+ )
134
+ );
135
+
136
+ container-type: inline-size;
137
+ justify-self: stretch;
138
+ border: 1px solid var(--dry-dialog-border);
139
+ border-radius: var(--dry-dialog-radius, var(--dry-overlay-radius, var(--dry-radius-2xl)));
140
+ background: var(--dry-dialog-bg, var(--dry-overlay-bg, var(--dry-color-bg-overlay)));
141
+ color: var(--dry-color-text-strong);
142
+ box-shadow: var(--dry-dialog-shadow, var(--dry-overlay-shadow, var(--dry-shadow-overlay)));
143
+ padding: 0;
144
+ max-block-size: var(--dry-dialog-max-block-size, 85vh);
145
+ display: grid;
146
+ overflow: var(--dry-dialog-overflow, auto);
147
+
148
+ transition:
149
+ opacity var(--dry-duration-normal) var(--dry-ease-spring-snappy),
150
+ transform var(--dry-duration-normal) var(--dry-ease-spring-snappy);
151
+ }
152
+
153
+ [data-modal-content][data-variant='dialog'][data-state='open'] [data-modal-panel] {
154
+ opacity: 1;
155
+ transform: scale(1) translateY(0);
156
+ }
157
+
158
+ @starting-style {
159
+ [data-modal-content][data-variant='dialog'][open] [data-modal-panel] {
160
+ opacity: 0;
161
+ transform: scale(var(--dry-motion-scale-enter)) translateY(var(--dry-motion-distance-sm));
162
+ }
163
+ }
164
+
165
+ /* ---------- AlertDialog (center) ---------- */
166
+
167
+ [data-modal-content][data-variant='alert-dialog'] {
168
+ /* dryui-allow width */
169
+ width: 100vw;
170
+ /* dryui-allow width */
171
+ max-width: none;
172
+ display: grid;
173
+ grid-template-columns: min(90vw, var(--dry-dialog-max-width, 32rem));
174
+ place-content: center;
175
+ place-items: center;
176
+ overflow: visible;
177
+ }
178
+
179
+ [data-modal-content][data-variant='alert-dialog']::backdrop {
180
+ background: var(--dry-overlay-bg, var(--dry-color-overlay-backdrop-strong));
181
+ backdrop-filter: blur(var(--dry-overlay-blur, 4px));
182
+ -webkit-backdrop-filter: blur(var(--dry-overlay-blur, 4px));
183
+ }
184
+
185
+ [data-modal-panel][data-variant='alert-dialog'] {
186
+ --dry-dialog-bg: var(--dry-color-bg-overlay);
187
+ --dry-dialog-border: var(--dry-color-stroke-weak);
188
+ --dry-dialog-radius: var(--dry-radius-xl);
189
+ --dry-dialog-shadow: var(--dry-shadow-overlay);
190
+ --dry-dialog-padding: var(--dry-space-6);
191
+ --dry-dialog-max-width: 32rem;
192
+
193
+ container-type: inline-size;
194
+ justify-self: stretch;
195
+ border: 1px solid var(--dry-dialog-border);
196
+ border-radius: var(--dry-dialog-radius);
197
+ background: var(--dry-dialog-bg);
198
+ color: var(--dry-color-text-strong);
199
+ box-shadow: var(--dry-dialog-shadow);
200
+ padding: 0;
201
+ max-block-size: 85vh;
202
+ display: grid;
203
+ grid-template-rows: max-content minmax(0, 1fr) max-content;
204
+ overflow: hidden;
205
+
206
+ transition:
207
+ opacity var(--dry-duration-normal) var(--dry-ease-spring-snappy),
208
+ transform var(--dry-duration-normal) var(--dry-ease-spring-snappy);
209
+ }
210
+
211
+ [data-modal-content][data-variant='alert-dialog'][data-state='open'] [data-modal-panel] {
212
+ opacity: 1;
213
+ transform: scale(1) translateY(0);
214
+ }
215
+
216
+ @starting-style {
217
+ [data-modal-content][data-variant='alert-dialog'][open] [data-modal-panel] {
218
+ opacity: 0;
219
+ transform: scale(var(--dry-motion-scale-enter)) translateY(var(--dry-motion-distance-sm));
220
+ }
221
+ }
222
+
223
+ /* ---------- Drawer (edge-pinned) ---------- */
224
+
225
+ [data-modal-content][data-variant='drawer'] {
226
+ margin: 0;
227
+ display: grid;
228
+ grid-template-columns: minmax(0, 1fr);
229
+ grid-template-rows: minmax(0, 1fr);
230
+ }
231
+
232
+ [data-modal-content][data-variant='drawer']::backdrop {
233
+ background: var(--dry-overlay-bg, var(--dry-color-overlay-backdrop-strong));
234
+ backdrop-filter: blur(var(--dry-overlay-blur, 8px));
235
+ -webkit-backdrop-filter: blur(var(--dry-overlay-blur, 8px));
236
+ }
237
+
238
+ [data-modal-content][data-variant='drawer'][data-side='right'] {
239
+ grid-template-columns:
240
+ minmax(0, calc(100dvw - var(--dry-drawer-size, 25rem)))
241
+ var(--dry-drawer-size, 25rem);
242
+ }
243
+
244
+ [data-modal-content][data-variant='drawer'][data-side='left'] {
245
+ grid-template-columns:
246
+ var(--dry-drawer-size, 25rem)
247
+ minmax(0, calc(100dvw - var(--dry-drawer-size, 25rem)));
248
+ }
249
+
250
+ [data-modal-content][data-variant='drawer'][data-side='top'] {
251
+ grid-template-rows:
252
+ var(--dry-drawer-size, 25rem)
253
+ minmax(0, calc(100dvh - var(--dry-drawer-size, 25rem)));
254
+ }
255
+
256
+ [data-modal-content][data-variant='drawer'][data-side='bottom'] {
257
+ grid-template-rows:
258
+ minmax(0, calc(100dvh - var(--dry-drawer-size, 25rem)))
259
+ var(--dry-drawer-size, 25rem);
260
+ }
261
+
262
+ [data-modal-panel][data-variant='drawer'] {
263
+ --dry-drawer-bg: var(--dry-color-bg-overlay);
264
+ --dry-drawer-border: var(--dry-color-stroke-weak);
265
+ --dry-drawer-size: 25rem;
266
+ --_drawer-rest-transform: translateX(0);
267
+ --_drawer-enter-transform: translateX(100%);
268
+
269
+ background: var(--dry-drawer-bg);
270
+ color: var(--dry-color-text-strong);
271
+ box-shadow: var(--dry-drawer-shadow, var(--dry-shadow-overlay));
272
+ padding: 0;
273
+ display: grid;
274
+ grid-template-rows: max-content minmax(0, 1fr) max-content;
275
+ overflow: hidden;
276
+ opacity: 1;
277
+ transform: var(--_drawer-rest-transform);
278
+ will-change: transform, opacity;
279
+
280
+ transition:
281
+ transform var(--dry-duration-slow) var(--dry-ease-spring-snappy),
282
+ opacity var(--dry-duration-normal) var(--dry-ease-out);
283
+ }
284
+
285
+ [data-modal-content][data-variant='drawer'][data-side='right'] [data-modal-panel] {
286
+ grid-column: 2;
287
+ height: 100%;
288
+ border-left: 1px solid var(--dry-drawer-border);
289
+ }
290
+
291
+ [data-modal-content][data-variant='drawer'][data-side='left'] [data-modal-panel] {
292
+ --_drawer-enter-transform: translateX(-100%);
293
+ grid-column: 1;
294
+ height: 100%;
295
+ border-right: 1px solid var(--dry-drawer-border);
296
+ }
297
+
298
+ [data-modal-content][data-variant='drawer'][data-side='top'] [data-modal-panel] {
299
+ --_drawer-rest-transform: translateY(0);
300
+ --_drawer-enter-transform: translateY(-100%);
301
+ grid-row: 1;
302
+ height: var(--dry-drawer-size);
303
+ border-bottom: 1px solid var(--dry-drawer-border);
304
+ }
305
+
306
+ [data-modal-content][data-variant='drawer'][data-side='bottom'] [data-modal-panel] {
307
+ --_drawer-rest-transform: translateY(0);
308
+ --_drawer-enter-transform: translateY(100%);
309
+ grid-row: 2;
310
+ height: var(--dry-drawer-size);
311
+ border-top: 1px solid var(--dry-drawer-border);
312
+ }
313
+
314
+ @starting-style {
315
+ [data-modal-content][data-variant='drawer'][open] [data-modal-panel] {
316
+ opacity: 0;
317
+ transform: var(--_drawer-enter-transform);
318
+ }
319
+ }
320
+
321
+ [data-modal-content][data-variant='drawer'][data-state='open'] [data-modal-panel] {
322
+ opacity: 1;
323
+ transform: var(--_drawer-rest-transform);
324
+ }
325
+
326
+ @media (prefers-reduced-motion: reduce) {
327
+ [data-modal-panel] {
328
+ transition: none;
329
+ }
330
+ }
331
+ </style>
@@ -0,0 +1,26 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLDialogAttributes } from 'svelte/elements';
3
+ type ModalSide = 'center' | 'top' | 'right' | 'bottom' | 'left';
4
+ type ModalVariant = 'dialog' | 'drawer' | 'alert-dialog';
5
+ interface Props extends HTMLDialogAttributes {
6
+ ctx: {
7
+ readonly open: boolean;
8
+ readonly headerId: string;
9
+ close: () => void;
10
+ };
11
+ /** Presentational variant; drives the scoped style block. */
12
+ variant: ModalVariant;
13
+ /**
14
+ * Positioning hint. `center` stacks the panel in the middle;
15
+ * `top`/`right`/`bottom`/`left` pin it to an edge (drawer-style).
16
+ */
17
+ side?: ModalSide;
18
+ /** When false, clicking the dialog backdrop does not dismiss. */
19
+ dismissOnBackdropClick?: boolean;
20
+ /** When false, pressing Escape does not dismiss. */
21
+ dismissOnEscape?: boolean;
22
+ children: Snippet;
23
+ }
24
+ declare const ModalContent: import("svelte").Component<Props, {}, "">;
25
+ type ModalContent = ReturnType<typeof ModalContent>;
26
+ export default ModalContent;
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLAttributes } from 'svelte/elements';
4
- import { createPositionedPopover } from '@dryui/primitives';
4
+ import { createAnchoredPopover } from '@dryui/primitives';
5
5
  import type { Placement } from '@dryui/primitives';
6
6
  import { getLinkPreviewCtx } from './context.svelte.js';
7
7
 
@@ -24,21 +24,13 @@
24
24
 
25
25
  let contentEl = $state<HTMLDivElement>();
26
26
 
27
- const popover = createPositionedPopover({
27
+ const popover = createAnchoredPopover({
28
28
  triggerEl: () => ctx.triggerEl,
29
29
  contentEl: () => contentEl ?? null,
30
+ open: () => ctx.open,
30
31
  placement: () => placement,
31
32
  offset: () => offset
32
33
  });
33
-
34
- $effect(() => {
35
- if (!contentEl) return;
36
- if (ctx.open) {
37
- popover.showPopover(contentEl);
38
- } else {
39
- popover.hidePopover(contentEl);
40
- }
41
- });
42
34
  </script>
43
35
 
44
36
  <div
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
+ import { variantAttrs } from '@dryui/primitives';
3
4
 
4
5
  interface Props extends HTMLAttributes<HTMLSpanElement> {
5
6
  src?: string;
@@ -42,9 +43,7 @@
42
43
  role="img"
43
44
  aria-label={alt}
44
45
  data-logo-mark
45
- data-size={size}
46
- data-shape={shape}
47
- data-color={color}
46
+ {...variantAttrs({ size, shape, color })}
48
47
  class={className}
49
48
  {...rest}
50
49
  >
@@ -96,23 +96,26 @@
96
96
  });
97
97
  </script>
98
98
 
99
- <div
100
- {@attach captureElement}
101
- {@attach applyDuration}
102
- class={className}
103
- data-mask-reveal
104
- data-shape={shape}
105
- data-direction={direction}
106
- data-revealed={revealed || undefined}
107
- data-reduced-motion={prefersReducedMotion || undefined}
108
- {...rest}
109
- >
110
- {#if childSnippet}
111
- {@render childSnippet()}
112
- {/if}
99
+ <div {@attach captureElement} class={className} data-mask-reveal-root {...rest}>
100
+ <div
101
+ {@attach applyDuration}
102
+ data-mask-reveal
103
+ data-shape={shape}
104
+ data-direction={direction}
105
+ data-revealed={revealed || undefined}
106
+ data-reduced-motion={prefersReducedMotion || undefined}
107
+ >
108
+ {#if childSnippet}
109
+ {@render childSnippet()}
110
+ {/if}
111
+ </div>
113
112
  </div>
114
113
 
115
114
  <style>
115
+ [data-mask-reveal-root] {
116
+ display: block;
117
+ }
118
+
116
119
  [data-mask-reveal] {
117
120
  --dry-mask-reveal-duration: var(--dry-duration-entrance, 480ms);
118
121
  --dry-mask-reveal-ease: var(--dry-ease-spring-snappy, cubic-bezier(0.16, 1, 0.3, 1));
@@ -7,10 +7,18 @@
7
7
 
8
8
  interface Props extends HTMLAttributes<HTMLDivElement> {
9
9
  fullWidth?: boolean;
10
+ align?: 'start' | 'center' | 'end';
10
11
  children: Snippet;
11
12
  }
12
13
 
13
- let { fullWidth = false, class: className, children, style, ...rest }: Props = $props();
14
+ let {
15
+ fullWidth = false,
16
+ align = 'start',
17
+ class: className,
18
+ children,
19
+ style,
20
+ ...rest
21
+ }: Props = $props();
14
22
 
15
23
  const ctx = getMegaMenuCtx();
16
24
  const itemCtx = getMegaMenuItemCtx();
@@ -20,7 +28,11 @@
20
28
  const popover = createPositionedPopover({
21
29
  triggerEl: () => document.getElementById(itemCtx.triggerId),
22
30
  contentEl: () => panelEl ?? null,
23
- placement: () => 'bottom-start',
31
+ placement: () => {
32
+ if (align === 'center') return 'bottom';
33
+ if (align === 'end') return 'bottom-end';
34
+ return 'bottom-start';
35
+ },
24
36
  offset: () => 4
25
37
  });
26
38
 
@@ -2,6 +2,7 @@ import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
3
  interface Props extends HTMLAttributes<HTMLDivElement> {
4
4
  fullWidth?: boolean;
5
+ align?: 'start' | 'center' | 'end';
5
6
  children: Snippet;
6
7
  }
7
8
  declare const MegaMenuPanel: import("svelte").Component<Props, {}, "">;