@placeholderco/placeholder-ui 1.0.12 → 2.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 (278) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +134 -130
  3. package/dist/app.css +178 -0
  4. package/dist/bootstrap.min.css +3600 -0
  5. package/dist/cards/GitCard.svelte +115 -0
  6. package/dist/cards/GitCard.svelte.d.ts +22 -0
  7. package/dist/display/Avatar.svelte +29 -17
  8. package/dist/display/Avatar.svelte.d.ts +12 -2
  9. package/dist/display/LinkCollection.svelte +40 -18
  10. package/dist/display/LinkCollection.svelte.d.ts +9 -2
  11. package/dist/display/Paper.svelte +31 -19
  12. package/dist/display/Paper.svelte.d.ts +12 -3
  13. package/dist/display/alert/Alert.svelte +129 -0
  14. package/dist/display/alert/Alert.svelte.d.ts +15 -0
  15. package/dist/display/alert/AlertVariant.d.ts +1 -0
  16. package/dist/display/alert/AlertVariant.js +1 -0
  17. package/dist/display/filetree/FileTree.svelte +259 -0
  18. package/dist/display/filetree/FileTree.svelte.d.ts +76 -0
  19. package/dist/display/filetree/FileTreeItem.svelte +396 -0
  20. package/dist/display/filetree/FileTreeItem.svelte.d.ts +40 -0
  21. package/dist/display/filetree/FileTreeNode.d.ts +46 -0
  22. package/dist/display/filetree/FileTreeNode.js +115 -0
  23. package/dist/display/filetree/index.d.ts +5 -0
  24. package/dist/display/filetree/index.js +3 -0
  25. package/dist/editors/CodeMirror.svelte +306 -0
  26. package/dist/editors/CodeMirror.svelte.d.ts +31 -0
  27. package/dist/editors/tiptap/LinkActionMenu.svelte +307 -0
  28. package/dist/editors/tiptap/LinkActionMenu.svelte.d.ts +8 -0
  29. package/dist/editors/tiptap/TipTap.svelte +856 -0
  30. package/dist/editors/tiptap/TipTap.svelte.d.ts +54 -0
  31. package/dist/editors/tiptap/TipTapBubble.svelte +790 -0
  32. package/dist/editors/tiptap/TipTapBubble.svelte.d.ts +44 -0
  33. package/dist/editors/tiptap/collaboration.d.ts +55 -0
  34. package/dist/editors/tiptap/collaboration.js +1 -0
  35. package/dist/editors/tiptap/extensions/Alert.d.ts +18 -0
  36. package/dist/editors/tiptap/extensions/Alert.js +72 -0
  37. package/dist/editors/tiptap/extensions/CodeBlockEditor.d.ts +21 -0
  38. package/dist/editors/tiptap/extensions/CodeBlockEditor.js +326 -0
  39. package/dist/editors/tiptap/extensions/Div.d.ts +19 -0
  40. package/dist/editors/tiptap/extensions/Div.js +89 -0
  41. package/dist/editors/tiptap/extensions/Span.d.ts +5 -0
  42. package/dist/editors/tiptap/extensions/Span.js +56 -0
  43. package/dist/editors/tiptap/extensions/codeBlockEditor.css +236 -0
  44. package/dist/editors/tiptap/extensions/codeBlockLanguages.d.ts +17 -0
  45. package/dist/editors/tiptap/extensions/codeBlockLanguages.js +124 -0
  46. package/dist/editors/tiptap/extensions/index.d.ts +7 -0
  47. package/dist/editors/tiptap/extensions/index.js +5 -0
  48. package/dist/editors/tiptap/toolbar/AlertButton.svelte +261 -0
  49. package/dist/editors/tiptap/toolbar/AlertButton.svelte.d.ts +7 -0
  50. package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte +18 -0
  51. package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte.d.ts +7 -0
  52. package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte +18 -0
  53. package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte.d.ts +7 -0
  54. package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte +18 -0
  55. package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte.d.ts +7 -0
  56. package/dist/editors/tiptap/toolbar/AlignRightButton.svelte +18 -0
  57. package/dist/editors/tiptap/toolbar/AlignRightButton.svelte.d.ts +7 -0
  58. package/dist/editors/tiptap/toolbar/BoldButton.svelte +18 -0
  59. package/dist/editors/tiptap/toolbar/BoldButton.svelte.d.ts +7 -0
  60. package/dist/editors/tiptap/toolbar/BulletListButton.svelte +18 -0
  61. package/dist/editors/tiptap/toolbar/BulletListButton.svelte.d.ts +7 -0
  62. package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte +18 -0
  63. package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte.d.ts +7 -0
  64. package/dist/editors/tiptap/toolbar/CodeButton.svelte +18 -0
  65. package/dist/editors/tiptap/toolbar/CodeButton.svelte.d.ts +7 -0
  66. package/dist/editors/tiptap/toolbar/ColorButton.svelte +299 -0
  67. package/dist/editors/tiptap/toolbar/ColorButton.svelte.d.ts +20 -0
  68. package/dist/editors/tiptap/toolbar/DocumentButton.svelte +58 -0
  69. package/dist/editors/tiptap/toolbar/DocumentButton.svelte.d.ts +9 -0
  70. package/dist/editors/tiptap/toolbar/HeadingButton.svelte +30 -0
  71. package/dist/editors/tiptap/toolbar/HeadingButton.svelte.d.ts +8 -0
  72. package/dist/editors/tiptap/toolbar/HtmlButton.svelte +68 -0
  73. package/dist/editors/tiptap/toolbar/HtmlButton.svelte.d.ts +7 -0
  74. package/dist/editors/tiptap/toolbar/ImageButton.svelte +54 -0
  75. package/dist/editors/tiptap/toolbar/ImageButton.svelte.d.ts +8 -0
  76. package/dist/editors/tiptap/toolbar/ItalicButton.svelte +18 -0
  77. package/dist/editors/tiptap/toolbar/ItalicButton.svelte.d.ts +7 -0
  78. package/dist/editors/tiptap/toolbar/LinkButton.svelte +257 -0
  79. package/dist/editors/tiptap/toolbar/LinkButton.svelte.d.ts +7 -0
  80. package/dist/editors/tiptap/toolbar/OrderedListButton.svelte +18 -0
  81. package/dist/editors/tiptap/toolbar/OrderedListButton.svelte.d.ts +7 -0
  82. package/dist/editors/tiptap/toolbar/RedoButton.svelte +18 -0
  83. package/dist/editors/tiptap/toolbar/RedoButton.svelte.d.ts +7 -0
  84. package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte +18 -0
  85. package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte.d.ts +7 -0
  86. package/dist/editors/tiptap/toolbar/SubscriptButton.svelte +18 -0
  87. package/dist/editors/tiptap/toolbar/SubscriptButton.svelte.d.ts +7 -0
  88. package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte +18 -0
  89. package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte.d.ts +7 -0
  90. package/dist/editors/tiptap/toolbar/TableButton.svelte +258 -0
  91. package/dist/editors/tiptap/toolbar/TableButton.svelte.d.ts +7 -0
  92. package/dist/editors/tiptap/toolbar/ToolbarButton.svelte +84 -0
  93. package/dist/editors/tiptap/toolbar/ToolbarButton.svelte.d.ts +10 -0
  94. package/dist/editors/tiptap/toolbar/UnderlineButton.svelte +18 -0
  95. package/dist/editors/tiptap/toolbar/UnderlineButton.svelte.d.ts +7 -0
  96. package/dist/editors/tiptap/toolbar/UndoButton.svelte +18 -0
  97. package/dist/editors/tiptap/toolbar/UndoButton.svelte.d.ts +7 -0
  98. package/dist/form/Autocomplete.svelte +86 -135
  99. package/dist/form/Autocomplete.svelte.d.ts +6 -8
  100. package/dist/form/AutocompleteMulti.svelte +385 -227
  101. package/dist/form/AutocompleteMulti.svelte.d.ts +59 -10
  102. package/dist/form/Checkbox.svelte +59 -54
  103. package/dist/form/Checkbox.svelte.d.ts +14 -5
  104. package/dist/form/Chips.svelte +29 -13
  105. package/dist/form/Chips.svelte.d.ts +16 -2
  106. package/dist/form/ColorPicker.svelte +957 -0
  107. package/dist/form/ColorPicker.svelte.d.ts +78 -0
  108. package/dist/form/ComboBox.svelte +30 -20
  109. package/dist/form/ComboBox.svelte.d.ts +1 -1
  110. package/dist/form/ComboBoxItemBuilder.svelte +161 -188
  111. package/dist/form/ComboBoxItemBuilder.svelte.d.ts +19 -2
  112. package/dist/form/ComboBoxMulti.svelte +38 -24
  113. package/dist/form/ComboBoxMulti.svelte.d.ts +1 -1
  114. package/dist/form/CronBuilder.svelte +84 -67
  115. package/dist/form/CronBuilder.svelte.d.ts +16 -3
  116. package/dist/form/DatePicker.svelte +148 -195
  117. package/dist/form/DateRangePicker.svelte +101 -98
  118. package/dist/form/DateTimePicker.svelte +167 -208
  119. package/dist/form/DateTimePicker.svelte.d.ts +10 -2
  120. package/dist/form/FormGroup.svelte +33 -10
  121. package/dist/form/FormGroup.svelte.d.ts +12 -2
  122. package/dist/form/Number.svelte +70 -47
  123. package/dist/form/Number.svelte.d.ts +36 -6
  124. package/dist/form/Radio.svelte +18 -18
  125. package/dist/form/RadioGroup.svelte +23 -12
  126. package/dist/form/RadioGroup.svelte.d.ts +13 -2
  127. package/dist/form/SegmentedControl.svelte +76 -56
  128. package/dist/form/SegmentedControl.svelte.d.ts +9 -4
  129. package/dist/form/Select.svelte +150 -233
  130. package/dist/form/Select.svelte.d.ts +29 -5
  131. package/dist/form/SelectMulti.svelte +163 -277
  132. package/dist/form/SelectMulti.svelte.d.ts +36 -7
  133. package/dist/form/Slider.svelte +183 -269
  134. package/dist/form/Slider.svelte.d.ts +32 -17
  135. package/dist/form/StringArrayBuilder.svelte +249 -0
  136. package/dist/form/StringArrayBuilder.svelte.d.ts +15 -0
  137. package/dist/form/Switch.svelte +164 -106
  138. package/dist/form/Switch.svelte.d.ts +22 -3
  139. package/dist/form/TextArea.svelte +49 -29
  140. package/dist/form/TextArea.svelte.d.ts +23 -3
  141. package/dist/form/Textbox.svelte +69 -68
  142. package/dist/form/Textbox.svelte.d.ts +32 -4
  143. package/dist/form/TimePicker.svelte +42 -47
  144. package/dist/form/TimePicker.svelte.d.ts +12 -2
  145. package/dist/form/comboBoxCore.svelte.d.ts +99 -0
  146. package/dist/form/comboBoxCore.svelte.js +243 -0
  147. package/dist/icon/Icon.svelte +8 -3
  148. package/dist/icon/Icon.svelte.d.ts +8 -3
  149. package/dist/icon/file-type-bmp.svg +1 -0
  150. package/dist/icon/file-type-csv.svg +1 -0
  151. package/dist/icon/file-type-doc.svg +1 -0
  152. package/dist/icon/file-type-docx.svg +1 -0
  153. package/dist/icon/file-type-jpg.svg +1 -0
  154. package/dist/icon/file-type-pdf.svg +1 -0
  155. package/dist/icon/file-type-png.svg +1 -0
  156. package/dist/icon/file-type-ppt.svg +1 -0
  157. package/dist/icon/file-type-svg.svg +1 -0
  158. package/dist/icon/file-type-txt.svg +1 -0
  159. package/dist/icon/file-type-xls.svg +1 -0
  160. package/dist/icon/file-type-zip.svg +1 -0
  161. package/dist/icon/index.d.ts +166 -151
  162. package/dist/icon/index.js +166 -152
  163. package/dist/icon/logo-placeholder.svg +6 -0
  164. package/dist/icon/source-code.svg +1 -0
  165. package/dist/icon/sun-moon.svg +1 -0
  166. package/dist/icon/table.svg +6 -0
  167. package/dist/index.d.ts +146 -82
  168. package/dist/index.js +77 -78
  169. package/dist/layout/AppShell.svelte +12 -162
  170. package/dist/layout/AppShell.svelte.d.ts +6 -27
  171. package/dist/layout/CustomNavbar.svelte +9 -7
  172. package/dist/layout/CustomNavbar.svelte.d.ts +4 -2
  173. package/dist/layout/Navbar.svelte +161 -129
  174. package/dist/layout/Navbar.svelte.d.ts +29 -15
  175. package/dist/layout/NavbarItemDisplay.svelte +2 -2
  176. package/dist/layout/NavbarItemView.svelte +182 -0
  177. package/dist/layout/NavbarItemView.svelte.d.ts +10 -0
  178. package/dist/layout/Sidenav.svelte +280 -341
  179. package/dist/layout/Sidenav.svelte.d.ts +36 -4
  180. package/dist/models/ComboBoxItem.d.ts +2 -0
  181. package/dist/models/Employee.d.ts +53 -0
  182. package/dist/models/Employee.js +23 -0
  183. package/dist/models/Hyperlink.d.ts +3 -1
  184. package/dist/models/LinkCollectionItem.d.ts +3 -1
  185. package/dist/models/NavbarItem.d.ts +6 -2
  186. package/dist/theme.svelte.d.ts +0 -1
  187. package/dist/theme.svelte.js +46 -58
  188. package/dist/ui/Accordion.svelte +81 -242
  189. package/dist/ui/Accordion.svelte.d.ts +37 -14
  190. package/dist/ui/AccordionItem.svelte +303 -0
  191. package/dist/ui/AccordionItem.svelte.d.ts +21 -0
  192. package/dist/ui/ActionIcon.svelte +25 -21
  193. package/dist/ui/ActionIcon.svelte.d.ts +15 -2
  194. package/dist/ui/Badge.svelte +163 -171
  195. package/dist/ui/Badge.svelte.d.ts +13 -5
  196. package/dist/ui/Button.svelte +178 -205
  197. package/dist/ui/Button.svelte.d.ts +19 -2
  198. package/dist/ui/ButtonVariant.d.ts +15 -1
  199. package/dist/ui/ContextMenu.svelte +146 -0
  200. package/dist/ui/ContextMenu.svelte.d.ts +37 -0
  201. package/dist/ui/ContextMenuList.svelte +188 -0
  202. package/dist/ui/ContextMenuList.svelte.d.ts +12 -0
  203. package/dist/ui/Dialog.svelte +183 -111
  204. package/dist/ui/Dialog.svelte.d.ts +21 -4
  205. package/dist/ui/Drawer.svelte +237 -184
  206. package/dist/ui/Drawer.svelte.d.ts +20 -2
  207. package/dist/ui/Dropdown.svelte +24 -14
  208. package/dist/ui/Dropdown.svelte.d.ts +11 -2
  209. package/dist/ui/Dropzone.svelte +159 -57
  210. package/dist/ui/Dropzone.svelte.d.ts +29 -2
  211. package/dist/ui/Link.svelte +12 -4
  212. package/dist/ui/Link.svelte.d.ts +8 -2
  213. package/dist/ui/Loader.svelte +23 -18
  214. package/dist/ui/Loader.svelte.d.ts +5 -2
  215. package/dist/ui/Logo.svelte +37 -0
  216. package/dist/ui/Logo.svelte.d.ts +16 -0
  217. package/dist/ui/MultiSortable.svelte +287 -0
  218. package/dist/ui/MultiSortable.svelte.d.ts +50 -0
  219. package/dist/ui/Pagination.svelte +15 -9
  220. package/dist/ui/Pagination.svelte.d.ts +6 -2
  221. package/dist/ui/Popover.svelte +43 -65
  222. package/dist/ui/Popover.svelte.d.ts +14 -5
  223. package/dist/ui/ProgressBar.svelte +28 -30
  224. package/dist/ui/ProgressBarVariant.d.ts +7 -1
  225. package/dist/ui/SegmentedControl.svelte +0 -0
  226. package/dist/ui/SegmentedControl.svelte.d.ts +26 -0
  227. package/dist/ui/Sortable.svelte +163 -0
  228. package/dist/ui/Sortable.svelte.d.ts +47 -0
  229. package/dist/ui/Table.svelte +383 -80
  230. package/dist/ui/Table.svelte.d.ts +43 -2
  231. package/dist/ui/Tabs.svelte +22 -18
  232. package/dist/ui/Tabs.svelte.d.ts +6 -2
  233. package/dist/ui/ThemeSwitcher.svelte +26 -7
  234. package/dist/ui/ThemeSwitcher.svelte.d.ts +4 -2
  235. package/dist/ui/Tooltip.svelte +312 -180
  236. package/dist/ui/Tooltip.svelte.d.ts +4 -3
  237. package/dist/uiTheme.svelte.d.ts +86 -0
  238. package/dist/uiTheme.svelte.js +214 -0
  239. package/dist/util/DateFunctions.js +1 -1
  240. package/dist/util/DragDrop.d.ts +50 -0
  241. package/dist/util/DragDrop.js +154 -0
  242. package/dist/util/NavigateTo.js +2 -2
  243. package/dist/util/dayjs.d.ts +4 -0
  244. package/dist/util/dayjs.js +15 -0
  245. package/package.json +66 -26
  246. package/dist/display/Alert.svelte +0 -179
  247. package/dist/display/Alert.svelte.d.ts +0 -13
  248. package/dist/form/FileInput.svelte +0 -235
  249. package/dist/form/FileInput.svelte.d.ts +0 -23
  250. package/dist/form/PasswordInput.svelte +0 -253
  251. package/dist/form/PasswordInput.svelte.d.ts +0 -34
  252. package/dist/form/Rating.svelte +0 -235
  253. package/dist/form/Rating.svelte.d.ts +0 -21
  254. package/dist/styles/components.css +0 -199
  255. package/dist/styles/dark.css +0 -146
  256. package/dist/styles/index.css +0 -116
  257. package/dist/styles/reset.css +0 -110
  258. package/dist/styles/semantic.css +0 -86
  259. package/dist/styles/tokens.css +0 -203
  260. package/dist/styles/utilities.css +0 -523
  261. package/dist/ui/Breadcrumbs.svelte +0 -131
  262. package/dist/ui/Breadcrumbs.svelte.d.ts +0 -16
  263. package/dist/ui/LoadingOverlay.svelte +0 -53
  264. package/dist/ui/LoadingOverlay.svelte.d.ts +0 -11
  265. package/dist/ui/Progress.svelte +0 -191
  266. package/dist/ui/Progress.svelte.d.ts +0 -15
  267. package/dist/ui/RingProgress.svelte +0 -141
  268. package/dist/ui/RingProgress.svelte.d.ts +0 -18
  269. package/dist/ui/Skeleton.svelte +0 -85
  270. package/dist/ui/Skeleton.svelte.d.ts +0 -12
  271. package/dist/ui/Stepper.svelte +0 -355
  272. package/dist/ui/Stepper.svelte.d.ts +0 -24
  273. package/dist/ui/Timeline.svelte +0 -225
  274. package/dist/ui/Timeline.svelte.d.ts +0 -23
  275. /package/dist/{actions → util}/ClickOutside.d.ts +0 -0
  276. /package/dist/{actions → util}/ClickOutside.js +0 -0
  277. /package/dist/{transitions → util}/Transitions.d.ts +0 -0
  278. /package/dist/{transitions → util}/Transitions.js +0 -0
@@ -3,19 +3,37 @@
3
3
  import { iconX } from '../icon/index.js';
4
4
  import type { Snippet } from 'svelte';
5
5
  import ActionIcon from './ActionIcon.svelte';
6
- import { setContext } from 'svelte';
6
+ import { setContext, untrack } from 'svelte';
7
+
8
+ export interface DialogContext {
9
+ /** Whether the dialog is currently open (reactive) */
10
+ readonly isOpen: boolean;
11
+ /** Register a callback invoked each time the dialog opens */
12
+ onOpen: (callback: () => void) => void;
13
+ /** Register a callback invoked each time the dialog closes */
14
+ onClose: (callback: () => void) => void;
15
+ /** Register a callback invoked only the first time the dialog opens */
16
+ onFirstOpen: (callback: () => void) => void;
17
+ }
7
18
 
8
- interface Props {
19
+ export interface DialogProps {
20
+ /** Whether the dialog is visible (bindable) */
9
21
  show: boolean;
22
+ /** Allow closing by clicking backdrop or pressing Escape */
10
23
  allowImplicitClose?: boolean;
24
+ /** Dialog title displayed in the header */
11
25
  title?: string | undefined;
26
+ /** Render without the Paper wrapper component */
12
27
  noPaper?: boolean;
28
+ /** Prevent content from overflowing the dialog bounds */
13
29
  preventOverflow?: boolean;
30
+ /** Size preset for the dialog width */
14
31
  size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
32
+ /** Additional CSS classes */
15
33
  class?: string;
16
- /** Use space-between layout for footer (useful for left/right button groups) */
17
- footerBetween?: boolean;
34
+ /** Footer content (typically action buttons) */
18
35
  footer?: Snippet;
36
+ /** Main dialog content */
19
37
  children?: Snippet;
20
38
  }
21
39
 
@@ -27,153 +45,178 @@
27
45
  preventOverflow = false,
28
46
  size = 'md',
29
47
  class: classes = '',
30
- footerBetween = false,
31
48
  footer,
32
49
  children
33
- }: Props = $props();
50
+ }: DialogProps = $props();
51
+
52
+ const uid = $props.id();
53
+ const titleId = `${uid}-title`;
34
54
 
35
55
  let dialogElement: HTMLDialogElement | undefined = $state(undefined);
36
-
37
- // Create context for dialog events
38
- let hasOpenedBefore = $state(false);
39
- let dialogContext = $state({
40
- isOpen: show,
41
- onOpen: () => {},
42
- onClose: () => {},
43
- onFirstOpen: () => {}
44
- });
45
56
 
46
- $effect(() => {
47
- dialogContext.isOpen = show;
57
+ // Context so child components can react to dialog state
58
+ const openCallbacks: (() => void)[] = [];
59
+ const closeCallbacks: (() => void)[] = [];
60
+ const firstOpenCallbacks: (() => void)[] = [];
61
+
62
+ setContext<DialogContext>('dialog', {
63
+ get isOpen() {
64
+ return show;
65
+ },
66
+ onOpen: (callback) => openCallbacks.push(callback),
67
+ onClose: (callback) => closeCallbacks.push(callback),
68
+ onFirstOpen: (callback) => firstOpenCallbacks.push(callback)
48
69
  });
49
70
 
50
- // Set context so child components can access dialog state
51
- setContext('dialog', dialogContext);
71
+ // Plain (untracked) flags — only the `show` transition should re-run the effect
72
+ let wasOpen = false;
73
+ let hasOpenedBefore = false;
52
74
 
53
75
  $effect(() => {
54
- if (dialogElement) {
55
- if (show) {
56
- dialogElement.showModal();
57
- dialogContext.onOpen();
76
+ if (!dialogElement) return;
77
+
78
+ if (show && !wasOpen) {
79
+ wasOpen = true;
80
+ dialogElement.showModal();
81
+ untrack(() => {
82
+ for (const callback of openCallbacks) callback();
58
83
  if (!hasOpenedBefore) {
59
84
  hasOpenedBefore = true;
60
- dialogContext.onFirstOpen();
85
+ for (const callback of firstOpenCallbacks) callback();
61
86
  }
62
- } else {
63
- dialogElement.close();
64
- dialogContext.onClose();
65
- }
87
+ });
88
+ } else if (!show && wasOpen) {
89
+ wasOpen = false;
90
+ dialogElement.close();
91
+ untrack(() => {
92
+ for (const callback of closeCallbacks) callback();
93
+ });
66
94
  }
67
95
  });
68
96
 
97
+ // Lock body scroll while open; effects only run in the browser, so this is SSR-safe
69
98
  $effect(() => {
70
- if (!body) return;
71
-
72
99
  if (show) {
73
- body.style.overflow = 'hidden';
74
- } else {
75
- body.style.overflow = '';
100
+ document.body.style.overflow = 'hidden';
101
+ return () => {
102
+ document.body.style.overflow = '';
103
+ };
76
104
  }
77
105
  });
78
106
 
79
- const body = document.querySelector('body');
107
+ function onCancel(e: Event) {
108
+ // Escape closes the native dialog before `close` fires; block it here,
109
+ // otherwise the dialog closes while `show` stays true
110
+ if (!allowImplicitClose) e.preventDefault();
111
+ }
112
+
113
+ function onNativeClose() {
114
+ // The dialog has already closed natively (Escape, form method="dialog");
115
+ // sync `show` unconditionally so it can be reopened
116
+ show = false;
117
+ }
80
118
 
81
- function onClose() {
119
+ function onBackdropMousedown() {
82
120
  if (allowImplicitClose) show = false;
83
121
  }
84
122
 
85
- function cancel(e: Event) {
86
- e.stopImmediatePropagation();
123
+ function stopMousedown(e: Event) {
124
+ // Keep mousedowns inside the dialog from reaching the backdrop handler
125
+ e.stopPropagation();
87
126
  }
88
127
  </script>
89
128
 
90
129
  {#snippet DialogInner()}
91
130
  {#if title}
92
131
  <div class="dialog-title">
93
- <span class="font-bold">{title}</span>
132
+ <span id={titleId} class="font-bold">{title}</span>
94
133
  {#if allowImplicitClose}
95
134
  <ActionIcon
96
- onclick={onClose}
135
+ onclick={() => (show = false)}
97
136
  variant="secondary-subtle"
98
137
  class="flex !p-[0.3rem] mr-1 !leading-0"
99
138
  svg={iconX}
100
139
  size="1rem"
140
+ ariaLabel="Close dialog"
101
141
  />
102
142
  {/if}
103
143
  </div>
104
144
  {/if}
105
- <div
106
- class="dialog-body
107
- {preventOverflow ? 'overflow-y-auto' : 'overflow-visible'}
108
- {!footer && 'no-footer'}"
109
- >
145
+ <div class="dialog-body {preventOverflow ? 'overflow-y-auto' : 'overflow-visible'}">
110
146
  {@render children?.()}
111
147
  </div>
112
148
  {#if footer}
113
- <div class="dialog-footer" class:footer-between={footerBetween}>
149
+ <div class="dialog-footer">
114
150
  {@render footer?.()}
115
151
  </div>
116
152
  {/if}
117
153
  {/snippet}
118
154
 
119
- {#if noPaper}
120
- <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
121
- <dialog
122
- bind:this={dialogElement}
123
- onclose={onClose}
124
- onmousedown={onClose}
125
- class={`dialog ${size}`}
155
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
156
+ <dialog
157
+ bind:this={dialogElement}
158
+ oncancel={onCancel}
159
+ onclose={onNativeClose}
160
+ onmousedown={onBackdropMousedown}
161
+ class={`dialog ${size}`}
162
+ aria-labelledby={title ? titleId : undefined}
163
+ >
164
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
165
+ <div
166
+ class="dialog-inner {noPaper ? `${classes} dialog-no-paper` : ''}"
167
+ onmousedown={stopMousedown}
126
168
  >
127
- <!-- svelte-ignore a11y_click_events_have_key_events -->
128
- <!-- svelte-ignore a11y_no_static_element_interactions -->
129
- <div
130
- class="dialog-inner {classes} dialog-no-paper"
131
- onclick={cancel}
132
- onmousedown={cancel}
133
- onmouseup={cancel}
134
- >
169
+ {#if noPaper}
135
170
  {@render DialogInner()}
136
- </div>
137
- </dialog>
138
- {:else}
139
- <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
140
- <dialog
141
- bind:this={dialogElement}
142
- onclose={onClose}
143
- onmousedown={onClose}
144
- class={`dialog ${size}`}
145
- >
146
- <!-- svelte-ignore a11y_click_events_have_key_events -->
147
- <!-- svelte-ignore a11y_no_static_element_interactions -->
148
- <div class="dialog-inner" onclick={cancel} onmousedown={cancel} onmouseup={cancel}>
149
- <div class="dialog-paper {preventOverflow ? 'overflow-y-auto' : 'overflow-visible'} ">
171
+ {:else}
172
+ <div class="dialog-paper {preventOverflow ? 'overflow-y-auto' : 'overflow-visible'}">
150
173
  <Paper containerClass={classes}>
151
174
  {@render DialogInner()}
152
175
  </Paper>
153
176
  </div>
154
- </div>
155
- </dialog>
156
- {/if}
177
+ {/if}
178
+ </div>
179
+ </dialog>
157
180
 
158
181
  <style>
182
+ /* Animate transform, not margin — the native dialog centers itself via
183
+ `margin: auto`, and a margin animation with `forwards` fill would
184
+ permanently override it and pin the dialog to the top of the viewport */
159
185
  @keyframes fadeIn {
160
186
  from {
161
187
  opacity: 0;
162
- transform: translateY(-1em);
188
+ transform: translateY(-0.5rem);
163
189
  }
164
190
  to {
165
191
  opacity: 1;
166
- transform: translateY(0em);
192
+ transform: translateY(0);
167
193
  }
168
194
  }
169
195
  @keyframes fadeOut {
170
196
  from {
171
197
  opacity: 1;
172
- transform: translateY(0em);
198
+ transform: translateY(0);
199
+ }
200
+ to {
201
+ opacity: 0;
202
+ transform: translateY(-0.5rem);
203
+ }
204
+ }
205
+
206
+ @keyframes fadeInFull {
207
+ from {
208
+ opacity: 0;
209
+ }
210
+ to {
211
+ opacity: 1;
212
+ }
213
+ }
214
+ @keyframes fadeOutFull {
215
+ from {
216
+ opacity: 1;
173
217
  }
174
218
  to {
175
219
  opacity: 0;
176
- transform: translateY(-1em);
177
220
  }
178
221
  }
179
222
 
@@ -196,58 +239,79 @@
196
239
  }
197
240
 
198
241
  dialog {
199
- color: var(--pui-text-primary);
242
+ color: var(--text-color);
200
243
  border: none;
244
+ /* The dialog element itself is focused by showModal(); don't draw a
245
+ focus ring around the whole dialog */
246
+ outline: none;
201
247
  background-color: transparent;
202
248
  overflow: visible;
203
249
  margin: auto;
204
-
205
- animation: fadeOut var(--pui-transition-fast) forwards;
250
+ max-width: min(calc(100vw - 2rem), calc(100% - 6px));
251
+ /* Cap to the visible viewport with equal margins; the flex chain below
252
+ (dialog > inner > paper > body, each min-height: 0) passes this limit
253
+ down so the body shrinks instead of spilling past the bottom edge.
254
+ dvh tracks the visible viewport on iOS Safari (100vh includes the
255
+ area behind the collapsible URL bar); vh is the older-browser fallback */
256
+ max-height: calc(100vh - 2rem);
257
+ max-height: calc(100dvh - 2rem);
258
+
259
+ animation: fadeOut 0.2s forwards;
206
260
  transition:
207
- display var(--pui-transition-fast) allow-discrete,
208
- overlay var(--pui-transition-fast) allow-discrete;
261
+ display 0.2s allow-discrete,
262
+ overlay 0.2s allow-discrete;
209
263
  &::backdrop {
210
- animation: backdropFadeOut var(--pui-transition-fast) forwards;
264
+ animation: backdropFadeOut 0.2s forwards;
211
265
  }
212
266
  &[open] {
213
- animation: fadeIn var(--pui-transition-fast) forwards;
267
+ /* Only when [open] — an unconditional author display would override
268
+ the UA's display: none on closed dialogs and make them visible */
269
+ display: flex;
270
+ flex-direction: column;
271
+ animation: fadeIn 0.2s forwards;
214
272
  &::backdrop {
215
- animation: backdropFadeIn var(--pui-transition-fast) forwards;
273
+ animation: backdropFadeIn 0.2s forwards;
216
274
  }
217
275
  }
218
276
  }
219
277
 
278
+ .dialog-inner {
279
+ display: flex;
280
+ flex-direction: column;
281
+ min-height: 0;
282
+ }
283
+
284
+ /* Paper sits inside the height chain; .paper-body already has an inline
285
+ min-height: 0, so only .paper needs the shrink link */
286
+ .dialog :global(.paper) {
287
+ min-height: 0;
288
+ }
289
+
220
290
  .dialog-title {
221
291
  display: flex;
222
292
  justify-content: space-between;
223
- margin-bottom: var(--pui-spacing-2);
224
- padding-bottom: var(--pui-spacing-1);
225
- border-bottom: 1px solid var(--pui-border-default);
226
- font-weight: var(--pui-font-weight-semibold);
293
+ margin-bottom: 0.5rem;
294
+ padding-bottom: 0.25rem;
295
+ border-bottom: 1px solid var(--border-color);
296
+ font-weight: 600;
297
+ flex-shrink: 0;
227
298
  }
228
299
 
229
300
  .dialog-body {
230
301
  display: flex;
231
302
  flex-direction: column;
232
303
  min-width: 0;
304
+ min-height: 0;
233
305
  flex-shrink: 1;
234
- max-height: calc(100vh - 167px);
235
- }
236
-
237
- .dialog-body.no-footer {
238
- max-height: calc(100vh - 107px);
239
306
  }
240
307
 
241
308
  .dialog-footer {
242
309
  display: flex;
243
- justify-content: flex-end;
244
- padding: var(--pui-spacing-2) 0;
245
- border-top: 1px solid var(--pui-border-default);
246
- gap: var(--pui-spacing-2);
247
- }
248
-
249
- .dialog-footer.footer-between {
250
310
  justify-content: space-between;
311
+ padding-top: 0.5rem;
312
+ border-top: 1px solid var(--border-color);
313
+ gap: 0.5rem;
314
+ flex-shrink: 0;
251
315
  }
252
316
 
253
317
  .overflow-visible {
@@ -259,7 +323,7 @@
259
323
  }
260
324
 
261
325
  .dialog-no-paper {
262
- padding: var(--pui-spacing-4);
326
+ padding: 1rem;
263
327
  }
264
328
 
265
329
  .dialog-paper,
@@ -267,17 +331,18 @@
267
331
  display: flex;
268
332
  flex-direction: column;
269
333
  box-sizing: border-box;
270
- border-radius: var(--pui-radius-base);
271
- background: var(--pui-color-gray-100);
334
+ border-radius: 0.25rem;
335
+ background: #f1f3f5;
336
+ min-height: 0;
272
337
  }
273
338
 
274
339
  :global(.dark .dialog-paper),
275
340
  :global(.dark .dialog-no-paper) {
276
- background-color: var(--pui-color-dark-200);
341
+ background-color: rgb(33, 34, 38);
277
342
  }
278
343
 
279
344
  dialog::backdrop {
280
- background-color: var(--pui-bg-overlay);
345
+ background-color: #0008;
281
346
  }
282
347
 
283
348
  .dialog.sm {
@@ -298,13 +363,20 @@
298
363
 
299
364
  .dialog.full {
300
365
  width: 100%;
366
+ max-width: 100%;
367
+ max-height: 100%;
301
368
  margin-top: auto;
369
+ animation: fadeOutFull 0.2s forwards;
370
+
371
+ &[open] {
372
+ animation: fadeInFull 0.2s forwards;
373
+ }
302
374
  }
303
375
 
304
376
  .dialog.full,
305
377
  .dialog.full .dialog-inner,
306
378
  .dialog.full :global(.paper),
307
- .dialog.full :global(.dialog-paper) {
379
+ .dialog.full .dialog-paper {
308
380
  height: 100%;
309
381
  }
310
382
 
@@ -1,17 +1,34 @@
1
1
  import type { Snippet } from 'svelte';
2
- interface Props {
2
+ export interface DialogContext {
3
+ /** Whether the dialog is currently open (reactive) */
4
+ readonly isOpen: boolean;
5
+ /** Register a callback invoked each time the dialog opens */
6
+ onOpen: (callback: () => void) => void;
7
+ /** Register a callback invoked each time the dialog closes */
8
+ onClose: (callback: () => void) => void;
9
+ /** Register a callback invoked only the first time the dialog opens */
10
+ onFirstOpen: (callback: () => void) => void;
11
+ }
12
+ export interface DialogProps {
13
+ /** Whether the dialog is visible (bindable) */
3
14
  show: boolean;
15
+ /** Allow closing by clicking backdrop or pressing Escape */
4
16
  allowImplicitClose?: boolean;
17
+ /** Dialog title displayed in the header */
5
18
  title?: string | undefined;
19
+ /** Render without the Paper wrapper component */
6
20
  noPaper?: boolean;
21
+ /** Prevent content from overflowing the dialog bounds */
7
22
  preventOverflow?: boolean;
23
+ /** Size preset for the dialog width */
8
24
  size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
25
+ /** Additional CSS classes */
9
26
  class?: string;
10
- /** Use space-between layout for footer (useful for left/right button groups) */
11
- footerBetween?: boolean;
27
+ /** Footer content (typically action buttons) */
12
28
  footer?: Snippet;
29
+ /** Main dialog content */
13
30
  children?: Snippet;
14
31
  }
15
- declare const Dialog: import("svelte").Component<Props, {}, "show">;
32
+ declare const Dialog: import("svelte").Component<DialogProps, {}, "show">;
16
33
  type Dialog = ReturnType<typeof Dialog>;
17
34
  export default Dialog;