@urbicon-ui/blocks 6.21.0 → 6.21.2

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 (142) hide show
  1. package/dist/components/Calendar/calendar.variants.d.ts +132 -419
  2. package/dist/components/CommandPalette/commandPalette.variants.d.ts +24 -51
  3. package/dist/components/CompositionBar/CompositionBar.svelte +6 -1
  4. package/dist/components/CompositionBar/composition-bar.variants.d.ts +23 -83
  5. package/dist/components/CompositionBar/composition-bar.variants.js +9 -3
  6. package/dist/components/EmptyState/emptyState.variants.d.ts +11 -27
  7. package/dist/components/FileUpload/fileUpload.variants.d.ts +24 -93
  8. package/dist/components/Guide/GuidePanel.svelte +38 -0
  9. package/dist/components/Guide/guide.variants.d.ts +69 -124
  10. package/dist/components/Guide/guide.variants.js +6 -1
  11. package/dist/components/NumberInput/NumberInput.svelte +197 -0
  12. package/dist/components/NumberInput/NumberInput.svelte.d.ts +4 -0
  13. package/dist/components/NumberInput/index.d.ts +55 -0
  14. package/dist/components/NumberInput/index.js +1 -0
  15. package/dist/components/Planner/planner.variants.d.ts +23 -94
  16. package/dist/components/Sankey/sankey.variants.d.ts +15 -48
  17. package/dist/components/SidebarLayout/sidebar-layout.variants.d.ts +10 -38
  18. package/dist/components/SidebarLayout/sidebar-layout.variants.js +12 -2
  19. package/dist/components/ThemeSwitcher/themeSwitcher.variants.d.ts +9 -36
  20. package/dist/components/index.d.ts +2 -0
  21. package/dist/components/index.js +1 -0
  22. package/dist/i18n/index.d.ts +8 -372
  23. package/dist/internal/charts/variants.d.ts +18 -31
  24. package/dist/primitives/Accordion/Accordion.svelte +11 -0
  25. package/dist/primitives/Accordion/AccordionItem.svelte +9 -3
  26. package/dist/primitives/Accordion/accordion.variants.d.ts +12 -43
  27. package/dist/primitives/Accordion/accordion.variants.js +6 -2
  28. package/dist/primitives/Accordion/index.d.ts +12 -0
  29. package/dist/primitives/Alert/alert.variants.d.ts +21 -62
  30. package/dist/primitives/Avatar/avatar.variants.d.ts +18 -138
  31. package/dist/primitives/Badge/Badge.svelte +12 -4
  32. package/dist/primitives/Badge/badge.variants.d.ts +19 -92
  33. package/dist/primitives/Badge/badge.variants.js +11 -4
  34. package/dist/primitives/Badge/index.d.ts +26 -6
  35. package/dist/primitives/Breadcrumb/Breadcrumb.svelte +11 -6
  36. package/dist/primitives/Breadcrumb/breadcrumb.variants.d.ts +13 -40
  37. package/dist/primitives/Breadcrumb/index.d.ts +2 -2
  38. package/dist/primitives/Button/button.variants.d.ts +19 -98
  39. package/dist/primitives/Button/button.variants.js +13 -9
  40. package/dist/primitives/ButtonGroup/buttongroup.variants.d.ts +9 -29
  41. package/dist/primitives/ButtonGroup/buttongroup.variants.js +3 -1
  42. package/dist/primitives/Card/card.variants.d.ts +14 -81
  43. package/dist/primitives/Checkbox/Checkbox.svelte +3 -3
  44. package/dist/primitives/Checkbox/checkbox.variants.d.ts +18 -77
  45. package/dist/primitives/Collapsible/Collapsible.svelte +10 -0
  46. package/dist/primitives/Collapsible/collapsible.variants.d.ts +11 -40
  47. package/dist/primitives/Collapsible/collapsible.variants.js +7 -2
  48. package/dist/primitives/Collapsible/index.d.ts +16 -0
  49. package/dist/primitives/Combobox/Combobox.svelte +478 -104
  50. package/dist/primitives/Combobox/combobox.variants.d.ts +112 -94
  51. package/dist/primitives/Combobox/combobox.variants.js +94 -13
  52. package/dist/primitives/Combobox/index.d.ts +166 -51
  53. package/dist/primitives/ConfirmDialog/index.d.ts +19 -0
  54. package/dist/primitives/Dialog/Dialog.svelte +95 -10
  55. package/dist/primitives/Dialog/dialog.variants.d.ts +15 -54
  56. package/dist/primitives/Dialog/index.d.ts +12 -1
  57. package/dist/primitives/Drawer/Drawer.svelte +23 -11
  58. package/dist/primitives/Drawer/drawer.variants.d.ts +22 -49
  59. package/dist/primitives/Drawer/drawer.variants.js +45 -10
  60. package/dist/primitives/Drawer/index.d.ts +13 -4
  61. package/dist/primitives/FormField/FormField.svelte +6 -6
  62. package/dist/primitives/FormField/index.d.ts +13 -4
  63. package/dist/primitives/Input/Input.svelte +8 -4
  64. package/dist/primitives/Input/input.variants.d.ts +32 -139
  65. package/dist/primitives/Input/input.variants.js +12 -10
  66. package/dist/primitives/JourneyTimeline/journey-timeline.variants.d.ts +55 -197
  67. package/dist/primitives/JourneyTimeline/journey-timeline.variants.js +4 -1
  68. package/dist/primitives/Menu/Menu.svelte +66 -4
  69. package/dist/primitives/Menu/index.d.ts +25 -0
  70. package/dist/primitives/Menu/menu.variants.d.ts +29 -93
  71. package/dist/primitives/Pagination/Pagination.svelte +28 -13
  72. package/dist/primitives/Pagination/index.d.ts +44 -0
  73. package/dist/primitives/Pagination/pagination.variants.d.ts +16 -54
  74. package/dist/primitives/Pagination/pagination.variants.js +16 -14
  75. package/dist/primitives/Popover/Popover.svelte +11 -1
  76. package/dist/primitives/Popover/index.d.ts +3 -3
  77. package/dist/primitives/Popover/popover.variants.d.ts +4 -2
  78. package/dist/primitives/Progress/progress.variants.d.ts +19 -83
  79. package/dist/primitives/Progress/progress.variants.js +2 -1
  80. package/dist/primitives/RadioGroup/RadioGroup.svelte +3 -3
  81. package/dist/primitives/RadioGroup/radioGroup.variants.d.ts +28 -109
  82. package/dist/primitives/SegmentGroup/SegmentGroup.svelte +14 -6
  83. package/dist/primitives/SegmentGroup/segmentgroup.variants.d.ts +12 -53
  84. package/dist/primitives/SegmentGroup/segmentgroup.variants.js +10 -10
  85. package/dist/primitives/Select/Select.svelte +21 -11
  86. package/dist/primitives/Select/index.d.ts +7 -0
  87. package/dist/primitives/Select/select.variants.d.ts +61 -150
  88. package/dist/primitives/Select/select.variants.js +24 -4
  89. package/dist/primitives/Separator/separator.variants.d.ts +4 -2
  90. package/dist/primitives/Sidebar/Sidebar.svelte +10 -15
  91. package/dist/primitives/Sidebar/sidebar.variants.d.ts +11 -29
  92. package/dist/primitives/Skeleton/skeleton.variants.d.ts +9 -36
  93. package/dist/primitives/Slider/Slider.svelte +4 -8
  94. package/dist/primitives/Slider/slider.variants.d.ts +37 -136
  95. package/dist/primitives/Slider/slider.variants.js +8 -5
  96. package/dist/primitives/Spinner/spinner.variants.d.ts +38 -135
  97. package/dist/primitives/Stepper/stepper.variants.d.ts +32 -130
  98. package/dist/primitives/Tab/Tab.svelte +14 -6
  99. package/dist/primitives/Tab/tab.variants.d.ts +17 -78
  100. package/dist/primitives/Textarea/Textarea.svelte +4 -5
  101. package/dist/primitives/Textarea/textarea.variants.d.ts +33 -118
  102. package/dist/primitives/Textarea/textarea.variants.js +17 -12
  103. package/dist/primitives/Toast/Toaster.svelte +58 -5
  104. package/dist/primitives/Toast/index.d.ts +39 -0
  105. package/dist/primitives/Toast/toast.store.svelte.d.ts +25 -1
  106. package/dist/primitives/Toast/toast.store.svelte.js +91 -1
  107. package/dist/primitives/Toast/toast.variants.d.ts +40 -78
  108. package/dist/primitives/Toast/toast.variants.js +15 -0
  109. package/dist/primitives/Toggle/Toggle.svelte +17 -3
  110. package/dist/primitives/Toggle/index.d.ts +8 -2
  111. package/dist/primitives/Toggle/toggle.variants.d.ts +24 -71
  112. package/dist/primitives/Toggle/toggle.variants.js +26 -0
  113. package/dist/primitives/Toolbar/toolbar.variants.d.ts +9 -60
  114. package/dist/primitives/Tooltip/Tooltip.svelte +58 -21
  115. package/dist/primitives/Tooltip/Tooltip.svelte.d.ts +1 -1
  116. package/dist/primitives/Tooltip/index.d.ts +45 -3
  117. package/dist/primitives/Tooltip/tooltip.variants.d.ts +14 -53
  118. package/dist/primitives/Tooltip/tooltip.variants.js +9 -3
  119. package/dist/primitives/index.d.ts +3 -3
  120. package/dist/style/foundation.css +4 -1
  121. package/dist/style/interaction.css +39 -11
  122. package/dist/style/semantic.css +2 -2
  123. package/dist/style/themes/index.css +1 -1
  124. package/dist/translations/de.d.ts +3 -0
  125. package/dist/translations/de.js +4 -1
  126. package/dist/translations/en.d.ts +3 -0
  127. package/dist/translations/en.js +4 -1
  128. package/dist/utils/guide.svelte.d.ts +8 -2
  129. package/dist/utils/guide.svelte.js +86 -5
  130. package/dist/utils/index.d.ts +2 -1
  131. package/dist/utils/index.js +5 -0
  132. package/dist/utils/overlay.d.ts +33 -4
  133. package/dist/utils/overlay.js +61 -18
  134. package/dist/utils/roving.d.ts +15 -0
  135. package/dist/utils/roving.js +44 -0
  136. package/dist/utils/use-floating-panel.svelte.d.ts +4 -1
  137. package/dist/utils/use-floating-panel.svelte.js +23 -1
  138. package/dist/utils/use-form-field.svelte.d.ts +9 -9
  139. package/dist/utils/use-form-field.svelte.js +12 -12
  140. package/dist/utils/variants.d.ts +60 -16
  141. package/dist/utils/variants.js +424 -123
  142. package/package.json +9 -6
@@ -9,53 +9,27 @@ export interface ComboboxOption<T extends SelectValue = string> {
9
9
  value: T;
10
10
  disabled?: boolean;
11
11
  }
12
+ /** A labelled group of combobox options (parity with Select's `groups`). */
13
+ export interface ComboboxGroup<T extends SelectValue = string> {
14
+ label: string;
15
+ options: ComboboxOption<T>[];
16
+ }
12
17
  /**
13
- * @description Searchable menu (autocomplete) combining a text input with a
14
- * filterable option list. Implements the ARIA combobox pattern with
15
- * keyboard navigation, custom filtering, and two-way bindable state.
16
- *
17
- * @tag form
18
- * @related Select
19
- * @related Input
20
- * @related Menu
21
- *
22
- * @example
23
- * ```svelte
24
- * <Combobox
25
- * options={[
26
- * { label: 'Apple', value: 'apple' },
27
- * { label: 'Banana', value: 'banana' },
28
- * { label: 'Cherry', value: 'cherry' }
29
- * ]}
30
- * bind:value={selected}
31
- * placeholder="Search fruit…"
32
- * />
33
- * ```
34
- *
35
- * @example Numeric IDs — the generic value type keeps `value` typed as `number`
36
- * ```svelte
37
- * <Combobox
38
- * options={tenants.map((t) => ({ label: t.name, value: t.id }))}
39
- * bind:value={tenantId}
40
- * />
41
- * ```
42
- *
43
- * @example
44
- * ```svelte
45
- * <Combobox
46
- * options={countries}
47
- * bind:value={country}
48
- * filter={(option, query) => option.label.toLowerCase().startsWith(query.toLowerCase())}
49
- * clearable
50
- * size="lg"
51
- * />
52
- * ```
18
+ * Shared props for both single- and multi-select Combobox modes. The
19
+ * mode-specific shapes (`value`, `onValueChange`, and the multi-only tag props)
20
+ * live on {@link ComboboxSingleProps} / {@link ComboboxMultipleProps} and narrow
21
+ * against `multiple`.
53
22
  */
54
- export interface ComboboxProps<T extends SelectValue = string> extends ComboboxVariants, Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
23
+ interface ComboboxBaseProps<T extends SelectValue = string> extends ComboboxVariants, Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
55
24
  /** Array of selectable options. Each needs a unique `value`. */
56
- options: ComboboxOption<T>[];
57
- /** Currently selected value. Supports `bind:value` for two-way binding. */
58
- value?: T | null;
25
+ options?: ComboboxOption<T>[];
26
+ /**
27
+ * Grouped options with section labels, at parity with Select. Takes precedence
28
+ * over `options` when set. Filtering runs per group; groups whose options all
29
+ * filter out are hidden, and keyboard navigation flows across the flattened,
30
+ * still-visible options exactly as it does for a flat list.
31
+ */
32
+ groups?: ComboboxGroup<T>[];
59
33
  /** Current search query text. Supports `bind:query` for external control (e.g. server-side filtering). */
60
34
  query?: string;
61
35
  /** Placeholder shown when the input is empty. @default 'Search…' */
@@ -66,22 +40,49 @@ export interface ComboboxProps<T extends SelectValue = string> extends ComboboxV
66
40
  helper?: string;
67
41
  /** Error message — replaces helper text and flags the field as invalid. */
68
42
  error?: string;
69
- /** Marks the field as required. Adds the asterisk on the label. @default false */
43
+ /**
44
+ * Marks the field as required. Adds the asterisk on the label. @default false
45
+ *
46
+ * In multi-select mode this is visual only — the transient search input
47
+ * carries no native `required` (it is cleared after each pick), so enforce a
48
+ * minimum selection in your submit handler.
49
+ */
70
50
  required?: boolean;
71
- /** Custom filter replacing the built-in case-insensitive label match. Return `true` to include an option. */
51
+ /** Custom filter replacing the built-in case-insensitive label match. Return `true` to include an option. Ignored when `queryFn` is set (the server filters). */
72
52
  filter?: (option: ComboboxOption<T>, query: string) => boolean;
53
+ /**
54
+ * Server-side search (analogous to the Table remote-mode API). When set, the
55
+ * Combobox stops filtering client-side and instead calls `queryFn` — debounced
56
+ * by {@link debounceMs} — on each query change, replacing the option list with
57
+ * the resolved result. The `AbortSignal` is aborted when a newer query
58
+ * supersedes an in-flight request, so a slow stale response never clobbers a
59
+ * fresh one. Aborted rejections are swallowed; other rejections leave the
60
+ * previous options in place (and warn in dev). `options`/`groups` are ignored
61
+ * in this mode. The selected option's label is cached so it survives result
62
+ * sets that no longer contain it.
63
+ */
64
+ queryFn?: (query: string, signal: AbortSignal) => Promise<ComboboxOption<T>[]>;
65
+ /** Debounce applied to `queryFn` in milliseconds. @default 250 */
66
+ debounceMs?: number;
67
+ /** Text shown in the listbox while an async `queryFn` request is in flight. @default 'Loading…' */
68
+ loadingText?: string;
73
69
  /** Show a clear button when a value is selected. Click or press Escape to reset. @default false */
74
70
  clearable?: boolean;
75
71
  /** Disable the entire combobox. @default false */
76
72
  disabled?: boolean;
77
- /** Shared `name` for a hidden input for native form submission. */
73
+ /** Shared `name` for a hidden input for native form submission. In multi-select mode one hidden input is emitted per selected value. */
78
74
  name?: string;
79
75
  /** Text displayed when the filter produces no matches. @default 'No results found' */
80
76
  noResultsText?: string;
81
- /** Fires after the selected value changes. Receives the new value or `null` on clear. */
82
- onValueChange?: (value: T | null) => void;
83
77
  /** Controls the open state of the listbox. Supports `bind:open`. @default false */
84
78
  open?: boolean;
79
+ /**
80
+ * Fires when the listbox opens or closes from user interaction (focus,
81
+ * typing, chevron toggle, selection, Escape, outside click). Receives the
82
+ * new open state — use it e.g. to lazy-load options on first open. Not
83
+ * called when the consumer writes `bind:open` directly.
84
+ */
85
+ onOpenChange?: (open: boolean) => void;
85
86
  /** Custom option renderer. Receives the option and whether it is selected. */
86
87
  customOption?: Snippet<[ComboboxOption<T>, boolean]>;
87
88
  /**
@@ -115,8 +116,9 @@ export interface ComboboxProps<T extends SelectValue = string> extends ComboboxV
115
116
  /** Remove all default tv() classes — only user-provided classes apply. @default false */
116
117
  unstyled?: boolean;
117
118
  /** Per-slot class overrides merged with tv() styles. Slots: base | label | requiredMark |
118
- * inputWrapper | input | message | hint | listbox | option | optionActive | optionSelected |
119
- * noResults | clear | chevron */
119
+ * inputWrapper | input | message | helper | listbox | option | optionActive | optionSelected |
120
+ * group | groupLabel | loading | noResults | clear | chevron | control | search | tag |
121
+ * tagLabel | tagRemove */
120
122
  slotClasses?: Partial<Record<ComboboxSlots, string>>;
121
123
  /**
122
124
  * Apply a named preset registered via `<BlocksProvider presets={{ Combobox: {...} }}>`.
@@ -128,6 +130,119 @@ export interface ComboboxProps<T extends SelectValue = string> extends ComboboxV
128
130
  /** Deterministic HTML `id` for the component. Auto-generated when omitted. */
129
131
  id?: string;
130
132
  }
133
+ /**
134
+ * Single-select arm (the default). `value` is `T | null`, and `onValueChange`
135
+ * receives the new value or `null` on clear. Selecting an option closes the
136
+ * listbox and mirrors the picked label back into the input.
137
+ */
138
+ export interface ComboboxSingleProps<T extends SelectValue = string> extends ComboboxBaseProps<T> {
139
+ /** Single-select mode (the default). Omit or set to `false` explicitly. */
140
+ multiple?: false;
141
+ /** Currently selected value. Supports `bind:value` for two-way binding. */
142
+ value?: T | null;
143
+ /** Fires after the selected value changes. Receives the new value or `null` on clear. */
144
+ onValueChange?: (value: T | null) => void;
145
+ /** Disallowed in single mode — only relevant for multi-select. */
146
+ maxItems?: never;
147
+ /** Disallowed in single mode — only relevant for multi-select. */
148
+ onRemoveTag?: never;
149
+ /** Disallowed in single mode — only relevant for multi-select. */
150
+ customTag?: never;
151
+ }
152
+ /**
153
+ * Multi-select arm. `value` is an array of the selected values, rendered as
154
+ * removable tag chips below the search input. Selecting an option adds a tag and
155
+ * keeps the listbox open so several picks flow without re-opening; the search
156
+ * query is cleared after each pick. Backspace on an empty query removes the last
157
+ * tag.
158
+ */
159
+ export interface ComboboxMultipleProps<T extends SelectValue = string> extends ComboboxBaseProps<T> {
160
+ /** Enable multi-select mode. */
161
+ multiple: true;
162
+ /** Currently selected values. Supports `bind:value`. @default [] */
163
+ value?: T[];
164
+ /** Fires after the selection changes. Receives the new array. */
165
+ onValueChange?: (value: T[]) => void;
166
+ /**
167
+ * Cap the number of selected values. Once reached, options that aren't already
168
+ * selected become non-selectable (and are skipped by keyboard navigation) until
169
+ * a tag is removed; already-selected options can still be toggled off.
170
+ */
171
+ maxItems?: number;
172
+ /**
173
+ * Fires when a single value is removed from the selection — via the tag's
174
+ * remove button, Backspace on an empty query, or toggling a selected option
175
+ * off. Receives the removed value. `onValueChange` fires alongside it with the
176
+ * resulting array; use `onRemoveTag` for per-tag side effects (analytics, exit
177
+ * animations). Note: the bulk clear button does NOT fire this per tag — it
178
+ * signals through `onValueChange([])` only.
179
+ */
180
+ onRemoveTag?: (value: T) => void;
181
+ /**
182
+ * Custom tag renderer replacing the default chip. Receives the selected option
183
+ * and a `remove` callback — call it to drop the tag (fires `onRemoveTag` +
184
+ * `onValueChange`). Use it to render a `<Badge>` or any bespoke chip.
185
+ *
186
+ * Positional args: `(option, remove)`.
187
+ */
188
+ customTag?: Snippet<[ComboboxOption<T>, () => void]>;
189
+ }
190
+ /**
191
+ * @description Searchable menu (autocomplete) combining a text input with a
192
+ * filterable option list. Implements the ARIA combobox pattern with keyboard
193
+ * navigation, custom filtering, and two-way bindable state. Single-select by
194
+ * default; pass `multiple` for an array-bound multi-select that renders picks as
195
+ * removable tag chips.
196
+ *
197
+ * @tag form
198
+ * @related Select
199
+ * @related Input
200
+ * @related Menu
201
+ *
202
+ * @example
203
+ * ```svelte
204
+ * <Combobox
205
+ * options={[
206
+ * { label: 'Apple', value: 'apple' },
207
+ * { label: 'Banana', value: 'banana' },
208
+ * { label: 'Cherry', value: 'cherry' }
209
+ * ]}
210
+ * bind:value={selected}
211
+ * placeholder="Search fruit…"
212
+ * />
213
+ * ```
214
+ *
215
+ * @example Numeric IDs — the generic value type keeps `value` typed as `number`
216
+ * ```svelte
217
+ * <Combobox
218
+ * options={tenants.map((t) => ({ label: t.name, value: t.id }))}
219
+ * bind:value={tenantId}
220
+ * />
221
+ * ```
222
+ *
223
+ * @example Multi-select with removable tags
224
+ * ```svelte
225
+ * <Combobox
226
+ * options={skills}
227
+ * multiple
228
+ * bind:value={selectedSkills}
229
+ * maxItems={5}
230
+ * placeholder="Add skills…"
231
+ * />
232
+ * ```
233
+ *
234
+ * @example Grouped options — filtering hides empty groups automatically
235
+ * ```svelte
236
+ * <Combobox
237
+ * groups={[
238
+ * { label: 'Fruit', options: [{ label: 'Apple', value: 'apple' }] },
239
+ * { label: 'Veg', options: [{ label: 'Carrot', value: 'carrot' }] }
240
+ * ]}
241
+ * bind:value={food}
242
+ * />
243
+ * ```
244
+ */
245
+ export type ComboboxProps<T extends SelectValue = string> = ComboboxSingleProps<T> | ComboboxMultipleProps<T>;
131
246
  /** Backwards-compatible alias for {@link ComboboxOption} (legacy name). */
132
247
  export type ComboboxOptionType<T extends SelectValue = string> = ComboboxOption<T>;
133
248
  export { default as Combobox } from './Combobox.svelte';
@@ -1,5 +1,6 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { DialogIntent } from '../Dialog/index.js';
3
+ import type { DialogSlots } from '../Dialog/dialog.variants.js';
3
4
  /**
4
5
  * Intent applied to the confirm button. Reuses the standard intent palette
5
6
  * via {@link DialogIntent} (with `neutral` mapped to `primary` for visual
@@ -88,5 +89,23 @@ export interface ConfirmDialogProps {
88
89
  transitionEasing?: (t: number) => number;
89
90
  /** Optional richer markup rendered below `description`. */
90
91
  children?: Snippet;
92
+ /** Extra classes merged onto the dialog element. Forwarded to {@link DialogProps.class}. */
93
+ class?: string;
94
+ /**
95
+ * Strip the underlying Dialog's default styles. Combine with `slotClasses`
96
+ * for a fully custom appearance. @default false
97
+ */
98
+ unstyled?: boolean;
99
+ /**
100
+ * Per-slot class overrides, forwarded to the underlying Dialog.
101
+ * Slots: dialog | backdrop | panel | content | header | title | body | footer.
102
+ */
103
+ slotClasses?: Partial<Record<DialogSlots, string>>;
104
+ /**
105
+ * Apply a named preset registered via `<BlocksProvider presets={{ Dialog: {...} }}>`.
106
+ * Resolved against the `Dialog` component key — ConfirmDialog shares the
107
+ * Dialog preset space instead of introducing a parallel one.
108
+ */
109
+ preset?: string;
91
110
  }
92
111
  export { default as ConfirmDialog } from './ConfirmDialog.svelte';
@@ -3,14 +3,9 @@
3
3
  import { getBlocksConfig, resolveSlotClasses } from '../../provider';
4
4
  import { resolveIcon } from '../../icons';
5
5
  import CloseIconDefault from '../../icons/CloseIcon.svelte';
6
- import { onDestroy } from 'svelte';
6
+ import { onDestroy, tick } from 'svelte';
7
7
  import { fade, scale } from 'svelte/transition';
8
- import {
9
- trapFocus,
10
- showDialogModal,
11
- closeDialogModal,
12
- unlockBodyScroll
13
- } from '../../utils/overlay';
8
+ import { trapFocus, showDialogModal, closeDialogModal } from '../../utils/overlay';
14
9
  import { overlayStack, getOverlayMotion } from '../../utils';
15
10
  import Button from '../Button/Button.svelte';
16
11
  import type { DialogProps } from './index';
@@ -31,6 +26,7 @@
31
26
  closeOnBackdropClick = true,
32
27
  closeOnEscape = true,
33
28
  hideCloseButton = false,
29
+ draggable = false,
34
30
  transitionDuration,
35
31
  transitionEasing,
36
32
  onClose,
@@ -69,9 +65,70 @@
69
65
  let panelEl = $state<HTMLElement>();
70
66
  let isVisible = $state(false);
71
67
  let previouslyFocused: HTMLElement | null = null;
68
+ // Release for the body-scroll lock this instance holds while modal — set by
69
+ // showDialogModal, idempotent, so both teardown paths below may call it.
70
+ let releaseScrollLock: (() => void) | undefined;
72
71
 
73
72
  const structured = $derived(!!title);
74
73
 
74
+ // svelte-ignore state_referenced_locally
75
+ if (import.meta.env?.DEV && draggable && !title) {
76
+ console.warn(
77
+ '[Dialog] `draggable` needs a `title` — the header is the drag handle. Without a title no header renders, so dragging is a no-op.'
78
+ );
79
+ }
80
+
81
+ // Drag offset from the centred position (px). Only ever non-zero while
82
+ // `draggable` and the user has grabbed the header. Reset on each open so a
83
+ // reopened dialog always starts centred.
84
+ let dragX = $state(0);
85
+ let dragY = $state(0);
86
+
87
+ // Attachment wiring the header as a drag handle. Kept off the markup as an
88
+ // `{@attach}` (not inline `onpointer*`) so the static `<header>` doesn't trip
89
+ // the a11y "non-interactive element with handlers needs a role" rule, and so
90
+ // the listeners tear down cleanly when `draggable` flips off. Pointer capture
91
+ // keeps the drag alive when the cursor outruns the header; header buttons
92
+ // (close) are exempted so they still click.
93
+ function draggableHandle(node: HTMLElement) {
94
+ node.style.touchAction = 'none';
95
+ node.style.userSelect = 'none';
96
+ node.style.cursor = 'move';
97
+ let dragging = false;
98
+ let startX = 0;
99
+ let startY = 0;
100
+ const onDown = (event: PointerEvent) => {
101
+ if ((event.target as HTMLElement).closest('button')) return;
102
+ dragging = true;
103
+ startX = event.clientX - dragX;
104
+ startY = event.clientY - dragY;
105
+ node.setPointerCapture(event.pointerId);
106
+ };
107
+ const onMove = (event: PointerEvent) => {
108
+ if (!dragging) return;
109
+ dragX = event.clientX - startX;
110
+ dragY = event.clientY - startY;
111
+ };
112
+ const onUp = (event: PointerEvent) => {
113
+ if (!dragging) return;
114
+ dragging = false;
115
+ node.releasePointerCapture(event.pointerId);
116
+ };
117
+ node.addEventListener('pointerdown', onDown);
118
+ node.addEventListener('pointermove', onMove);
119
+ node.addEventListener('pointerup', onUp);
120
+ return () => {
121
+ node.removeEventListener('pointerdown', onDown);
122
+ node.removeEventListener('pointermove', onMove);
123
+ node.removeEventListener('pointerup', onUp);
124
+ // Undo the inline styles too, so toggling `draggable` off at runtime
125
+ // doesn't leave the header stuck with `cursor: move` / unselectable text.
126
+ node.style.touchAction = '';
127
+ node.style.userSelect = '';
128
+ node.style.cursor = '';
129
+ };
130
+ }
131
+
75
132
  const uid = $props.id();
76
133
  const titleId = $derived(title ? `dialog-title-${uid}` : undefined);
77
134
  const bodyId = `dialog-body-${uid}`;
@@ -120,16 +177,38 @@
120
177
  }
121
178
 
122
179
  function handleOutroEnd() {
123
- closeDialogModal(dialogEl, previouslyFocused);
180
+ closeDialogModal(dialogEl, previouslyFocused, releaseScrollLock);
181
+ releaseScrollLock = undefined;
124
182
  isVisible = false;
125
183
  previouslyFocused = null;
126
184
  }
127
185
 
186
+ // Reset the drag offset whenever the dialog opens so a reopened dialog always
187
+ // starts centred — keyed on `open`, not the `open && !isVisible` transition.
188
+ // Reopening *during* the close outro (isVisible still true) must still recentre;
189
+ // the old transition-guarded reset silently skipped that case, restoring the
190
+ // stale drag position. `open` is constant during a drag, so this never fights
191
+ // the pointer handlers.
192
+ $effect(() => {
193
+ if (open) {
194
+ dragX = 0;
195
+ dragY = 0;
196
+ }
197
+ });
198
+
128
199
  $effect(() => {
129
200
  if (open && !isVisible) {
130
201
  isVisible = true;
131
202
  previouslyFocused = document.activeElement as HTMLElement;
132
- showDialogModal(dialogEl, panelEl);
203
+ // Defer until the `{#if isVisible}` block has rendered so `bind:this` has
204
+ // assigned dialogEl/panelEl — showDialogModal captures the refs by value,
205
+ // so promoting before the bind would silently no-op (never actually modal).
206
+ // The guard covers same-flush teardown: if the component unmounts before
207
+ // the tick resolves, bind:this has nulled dialogEl and onDestroy has
208
+ // already run — a lock taken now could never be released.
209
+ tick().then(() => {
210
+ if (dialogEl) releaseScrollLock = showDialogModal(dialogEl, panelEl);
211
+ });
133
212
  }
134
213
  });
135
214
 
@@ -139,7 +218,11 @@
139
218
  });
140
219
 
141
220
  onDestroy(() => {
142
- unlockBodyScroll();
221
+ // Safety net for destroy-while-open, where no outro runs. Releasing is
222
+ // idempotent and scoped to this instance's own lock, so a regular close
223
+ // (already released in handleOutroEnd) or a never-opened instance make
224
+ // this a no-op — it can never free another overlay's lock.
225
+ releaseScrollLock?.();
143
226
  });
144
227
  </script>
145
228
 
@@ -180,6 +263,7 @@
180
263
  : styles.panel({ class: [slotClasses?.panel, className] })}
181
264
  data-intent={intent}
182
265
  role="document"
266
+ style:translate={dragX !== 0 || dragY !== 0 ? `${dragX}px ${dragY}px` : undefined}
183
267
  transition:scale={{
184
268
  duration: motion.enterDuration,
185
269
  easing: motion.easing,
@@ -193,6 +277,7 @@
193
277
  class={unstyled
194
278
  ? (slotClasses?.header ?? '')
195
279
  : styles.header({ class: slotClasses?.header })}
280
+ {@attach draggable ? draggableHandle : undefined}
196
281
  >
197
282
  <h2
198
283
  class={unstyled
@@ -1,106 +1,67 @@
1
1
  import { type SlotNames, type VariantProps } from '../../utils/variants.js';
2
- export declare const dialogVariants: (props?: import("../../utils/variants.js").TVProps<{
3
- size: {
4
- sm: {
5
- panel: string;
6
- };
7
- md: {
8
- panel: string;
9
- };
10
- lg: {
11
- panel: string;
12
- };
13
- xl: {
14
- panel: string;
15
- };
16
- full: {
17
- panel: string[];
18
- };
19
- fullscreen: {
20
- panel: string[];
21
- };
22
- };
23
- placement: {
24
- center: {
25
- dialog: string;
26
- };
27
- top: {
28
- dialog: string;
29
- };
30
- };
31
- intent: {
32
- neutral: {};
33
- primary: {};
34
- secondary: {};
35
- success: {};
36
- warning: {};
37
- danger: {};
38
- };
39
- }> | undefined) => {
2
+ export declare const dialogVariants: ((props?: {
3
+ size?: "sm" | "md" | "lg" | "xl" | "full" | "fullscreen" | undefined;
4
+ placement?: "top" | "center" | undefined;
5
+ intent?: "primary" | "secondary" | "success" | "warning" | "danger" | "neutral" | undefined;
6
+ } | undefined) => {
40
7
  dialog: (props?: ({
41
8
  size?: "sm" | "md" | "lg" | "xl" | "full" | "fullscreen" | undefined;
42
9
  placement?: "top" | "center" | undefined;
43
10
  intent?: "primary" | "secondary" | "success" | "warning" | "danger" | "neutral" | undefined;
44
11
  } & {
45
- class?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
46
- className?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
12
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
47
13
  }) | undefined) => string;
48
14
  backdrop: (props?: ({
49
15
  size?: "sm" | "md" | "lg" | "xl" | "full" | "fullscreen" | undefined;
50
16
  placement?: "top" | "center" | undefined;
51
17
  intent?: "primary" | "secondary" | "success" | "warning" | "danger" | "neutral" | undefined;
52
18
  } & {
53
- class?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
54
- className?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
19
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
55
20
  }) | undefined) => string;
56
21
  panel: (props?: ({
57
22
  size?: "sm" | "md" | "lg" | "xl" | "full" | "fullscreen" | undefined;
58
23
  placement?: "top" | "center" | undefined;
59
24
  intent?: "primary" | "secondary" | "success" | "warning" | "danger" | "neutral" | undefined;
60
25
  } & {
61
- class?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
62
- className?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
26
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
63
27
  }) | undefined) => string;
64
28
  content: (props?: ({
65
29
  size?: "sm" | "md" | "lg" | "xl" | "full" | "fullscreen" | undefined;
66
30
  placement?: "top" | "center" | undefined;
67
31
  intent?: "primary" | "secondary" | "success" | "warning" | "danger" | "neutral" | undefined;
68
32
  } & {
69
- class?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
70
- className?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
33
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
71
34
  }) | undefined) => string;
72
35
  header: (props?: ({
73
36
  size?: "sm" | "md" | "lg" | "xl" | "full" | "fullscreen" | undefined;
74
37
  placement?: "top" | "center" | undefined;
75
38
  intent?: "primary" | "secondary" | "success" | "warning" | "danger" | "neutral" | undefined;
76
39
  } & {
77
- class?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
78
- className?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
40
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
79
41
  }) | undefined) => string;
80
42
  title: (props?: ({
81
43
  size?: "sm" | "md" | "lg" | "xl" | "full" | "fullscreen" | undefined;
82
44
  placement?: "top" | "center" | undefined;
83
45
  intent?: "primary" | "secondary" | "success" | "warning" | "danger" | "neutral" | undefined;
84
46
  } & {
85
- class?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
86
- className?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
47
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
87
48
  }) | undefined) => string;
88
49
  body: (props?: ({
89
50
  size?: "sm" | "md" | "lg" | "xl" | "full" | "fullscreen" | undefined;
90
51
  placement?: "top" | "center" | undefined;
91
52
  intent?: "primary" | "secondary" | "success" | "warning" | "danger" | "neutral" | undefined;
92
53
  } & {
93
- class?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
94
- className?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
54
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
95
55
  }) | undefined) => string;
96
56
  footer: (props?: ({
97
57
  size?: "sm" | "md" | "lg" | "xl" | "full" | "fullscreen" | undefined;
98
58
  placement?: "top" | "center" | undefined;
99
59
  intent?: "primary" | "secondary" | "success" | "warning" | "danger" | "neutral" | undefined;
100
60
  } & {
101
- class?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
102
- className?: string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | (string | false | /*elided*/ any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined;
61
+ class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
103
62
  }) | undefined) => string;
63
+ }) & {
64
+ readonly config: import("../../utils/variants.js").TVConfig;
104
65
  };
105
66
  export type DialogVariants = VariantProps<typeof dialogVariants>;
106
67
  /** Slot names derived from the `tv()` config above — single source of truth for `slotClasses`. */
@@ -27,7 +27,7 @@ import type { DialogSlots, DialogVariants } from './dialog.variants.js';
27
27
  * </Dialog>
28
28
  * ```
29
29
  */
30
- export interface DialogProps extends Omit<HTMLDialogAttributes, 'children' | 'open'> {
30
+ export interface DialogProps extends Omit<HTMLDialogAttributes, 'children' | 'open' | 'draggable'> {
31
31
  /** Controls whether the dialog is visible. Supports bind:open. */
32
32
  open?: boolean;
33
33
  /** Dialog body content. When `title` is omitted the content fills the entire panel. */
@@ -52,6 +52,17 @@ export interface DialogProps extends Omit<HTMLDialogAttributes, 'children' | 'op
52
52
  closeOnBackdropClick?: boolean;
53
53
  /** Whether pressing Escape dismisses the dialog. @default true */
54
54
  closeOnEscape?: boolean;
55
+ /**
56
+ * Let the user reposition the dialog by dragging its header. Off by default.
57
+ * The structured header (rendered when `title` is set) becomes the drag
58
+ * handle; the close button and any header controls stay clickable, and the
59
+ * offset resets each time the dialog reopens. Most useful for a dialog the
60
+ * user may want to shove aside to see the content behind it. Requires a
61
+ * `title` — without a header there is nothing to grab. Overrides the native
62
+ * HTML `draggable` attribute, which has no meaningful use on a dialog.
63
+ * @default false
64
+ */
65
+ draggable?: boolean;
55
66
  /**
56
67
  * Hides the built-in close button. Only takes effect when `title` is
57
68
  * also set — the close button only renders in the structured (titled)