@getdom/studio 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/LICENSE.md +109 -0
  2. package/README.md +44 -0
  3. package/SKILL.md +83 -0
  4. package/package.json +73 -0
  5. package/public/llms.txt +57 -0
  6. package/public/skills.sh +11 -0
  7. package/src/pages/_layout/componentCatalog.generated.js +4514 -0
  8. package/src/pages/_layout/componentInspector.js +60 -0
  9. package/src/pages/_layout/componentManager.js +192 -0
  10. package/src/pages/_layout/componentRuntimeRecords.js +49 -0
  11. package/src/pages/_layout/inspector/componentRegistry.js +639 -0
  12. package/src/pages/_layout/inspector/serialize.js +134 -0
  13. package/src/pages/_layout/inspector/useInspector.js +293 -0
  14. package/src/pages/components/accordion/DomAccordion.vue +39 -0
  15. package/src/pages/components/breadcrumbs/DomBreadcrumbs.vue +347 -0
  16. package/src/pages/components/button/DomButton.vue +132 -0
  17. package/src/pages/components/command-palette/DomCommandPalette.vue +482 -0
  18. package/src/pages/components/context-menu/DomContextMenu.vue +178 -0
  19. package/src/pages/components/dialog/DomDialog.vue +271 -0
  20. package/src/pages/components/dialog/DomDialogSchemaForm.vue +72 -0
  21. package/src/pages/components/dialog/DomDialogStack.vue +289 -0
  22. package/src/pages/components/dialog/useDialogs.js +253 -0
  23. package/src/pages/components/drawer/DomDrawer.vue +159 -0
  24. package/src/pages/components/dropdown/DomDropdown.vue +261 -0
  25. package/src/pages/components/icon-button/DomIconButton.vue +155 -0
  26. package/src/pages/components/lightbox/DomLightbox.vue +1561 -0
  27. package/src/pages/components/menu/DomMenu.vue +359 -0
  28. package/src/pages/components/menu/DomMenuItem.vue +233 -0
  29. package/src/pages/components/menu/DomMenuLabel.vue +19 -0
  30. package/src/pages/components/menu/DomMenuSeparator.vue +14 -0
  31. package/src/pages/components/menu/menuContext.js +11 -0
  32. package/src/pages/components/menu/menuPlacement.js +26 -0
  33. package/src/pages/components/menu/submenu.js +58 -0
  34. package/src/pages/components/pagination/DomPagination.vue +265 -0
  35. package/src/pages/components/playground/DomRenderer.vue +48 -0
  36. package/src/pages/components/popover/DomPopover.vue +244 -0
  37. package/src/pages/components/profile-menu/DomProfileMenu.vue +408 -0
  38. package/src/pages/components/profile-menu/DomProfileMenuItemContent.vue +45 -0
  39. package/src/pages/components/scroll-menu/DomScrollMenu.vue +430 -0
  40. package/src/pages/components/scroll-menu/DomScrollMenuItem.vue +212 -0
  41. package/src/pages/components/scroll-menu/scrollMenuContext.js +22 -0
  42. package/src/pages/components/splitter-panel/DomSplitterPanel.vue +283 -0
  43. package/src/pages/components/stepper/DomStepper.vue +386 -0
  44. package/src/pages/components/tabs/DomTabs.vue +149 -0
  45. package/src/pages/components/theme-switcher/DomThemeSwitcher.vue +226 -0
  46. package/src/pages/components/toast/DomToastItem.vue +52 -0
  47. package/src/pages/components/toast/DomToastStack.vue +768 -0
  48. package/src/pages/components/toast/useToasts.js +237 -0
  49. package/src/pages/components/toolbar/DomToolbar.vue +158 -0
  50. package/src/pages/components/tooltip/DomTooltip.vue +69 -0
  51. package/src/pages/components/tooltip/vDomTooltip.js +106 -0
  52. package/src/pages/components/tree-view/DomTreeView.vue +961 -0
  53. package/src/pages/components/tree-view/treeSchemas.js +129 -0
  54. package/src/pages/components/tree-view/treeUtils.js +228 -0
  55. package/src/pages/data/_shared/calculateVirtualWindow.js +101 -0
  56. package/src/pages/data/_shared/resourceRecord.js +29 -0
  57. package/src/pages/data/activity-timeline/DomActivityTimeline.vue +417 -0
  58. package/src/pages/data/agent-chat/DomAgentChatShell.vue +565 -0
  59. package/src/pages/data/agent-chat/DomAgentConversation.vue +737 -0
  60. package/src/pages/data/agent-chat/DomAgentConversationList.vue +319 -0
  61. package/src/pages/data/agent-chat/examples/agentChatMockApi.js +620 -0
  62. package/src/pages/data/agent-chat/examples/useAgentChatDemoStore.js +730 -0
  63. package/src/pages/data/data-grid/DataGridVirtualRows.vue +220 -0
  64. package/src/pages/data/data-grid/DomDataGrid.vue +1643 -0
  65. package/src/pages/data/document-editor/DomDocumentEditor.vue +3030 -0
  66. package/src/pages/data/document-editor/documentEditorCodeBlock.js +282 -0
  67. package/src/pages/data/document-editor/examples/documentEditorMockApi.js +272 -0
  68. package/src/pages/data/document-editor/examples/useDocumentEditorDemoStore.js +284 -0
  69. package/src/pages/data/filter-bar/DomFilterBar.vue +533 -0
  70. package/src/pages/data/kanban/DomKanban.vue +1703 -0
  71. package/src/pages/data/kanban/kanbanApi.js +80 -0
  72. package/src/pages/data/media-browser/DomMediaBrowser.vue +2344 -0
  73. package/src/pages/data/media-browser/examples/mediaBrowserMockApi.js +653 -0
  74. package/src/pages/data/media-browser/examples/useMediaBrowserDemoStore.js +1157 -0
  75. package/src/pages/data/month-calendar/DomMonthCalendar.vue +1718 -0
  76. package/src/pages/data/week-planner/DomWeekPlanner.vue +2308 -0
  77. package/src/pages/dev/code-block/DomCodeBlock.vue +263 -0
  78. package/src/pages/dev/json-viewer/DomJsonViewer.vue +962 -0
  79. package/src/pages/forms/_shared/DomFieldLoadingSpinner.vue +12 -0
  80. package/src/pages/forms/_shared/options.js +61 -0
  81. package/src/pages/forms/_shared/useOptionMenu.js +59 -0
  82. package/src/pages/forms/autocomplete/DomAutocomplete.vue +180 -0
  83. package/src/pages/forms/calendar/DomCalendar.vue +442 -0
  84. package/src/pages/forms/checkbox/DomCheckbox.vue +69 -0
  85. package/src/pages/forms/class-toggle-input/DomClassToggleInput.vue +316 -0
  86. package/src/pages/forms/code-input/DomCodeInput.vue +202 -0
  87. package/src/pages/forms/color-input/DomColorInput.vue +118 -0
  88. package/src/pages/forms/combobox/DomCombobox.vue +237 -0
  89. package/src/pages/forms/confirmation-code-input/DomConfirmationCodeInput.vue +345 -0
  90. package/src/pages/forms/date-picker/DomDatePicker.vue +299 -0
  91. package/src/pages/forms/date-time-picker/DomDateTimePicker.vue +630 -0
  92. package/src/pages/forms/email-input/DomEmailInput.vue +55 -0
  93. package/src/pages/forms/field/DomField.vue +267 -0
  94. package/src/pages/forms/field/fieldLayout.js +1 -0
  95. package/src/pages/forms/field/fieldProps.js +114 -0
  96. package/src/pages/forms/field/layouts/DomGridFieldLayout.vue +38 -0
  97. package/src/pages/forms/field/useField.js +343 -0
  98. package/src/pages/forms/field/validators.js +364 -0
  99. package/src/pages/forms/file-upload/DomFileUpload.vue +361 -0
  100. package/src/pages/forms/form/DomForm.vue +985 -0
  101. package/src/pages/forms/form/adapters/fieldBuilder.js +45 -0
  102. package/src/pages/forms/form/adapters/jsonSchema.js +264 -0
  103. package/src/pages/forms/form/adapters/shared.js +53 -0
  104. package/src/pages/forms/form/adapters/zod.js +235 -0
  105. package/src/pages/forms/form/formApi.js +38 -0
  106. package/src/pages/forms/form/formDefinition.js +392 -0
  107. package/src/pages/forms/form/schemaAdapter.js +54 -0
  108. package/src/pages/forms/icon-selector/DomIconSelector.vue +460 -0
  109. package/src/pages/forms/icon-selector/iconSelectorItems.js +490 -0
  110. package/src/pages/forms/input-group/DomInputGroup.vue +232 -0
  111. package/src/pages/forms/json-input/DomJsonInput.vue +227 -0
  112. package/src/pages/forms/json-list-input/DomJsonListInput.vue +206 -0
  113. package/src/pages/forms/listbox/DomListbox.vue +105 -0
  114. package/src/pages/forms/money-input/DomMoneyInput.vue +147 -0
  115. package/src/pages/forms/native-select/DomNativeSelect.vue +67 -0
  116. package/src/pages/forms/number-input/DomNumberInput.vue +47 -0
  117. package/src/pages/forms/password-input/DomPasswordInput.vue +218 -0
  118. package/src/pages/forms/password-input/pwnedPasswords.js +47 -0
  119. package/src/pages/forms/password-strength/DomPasswordStrength.vue +128 -0
  120. package/src/pages/forms/password-strength/passwordStrength.js +92 -0
  121. package/src/pages/forms/position-input/DomPositionInput.vue +83 -0
  122. package/src/pages/forms/radio-group/DomRadioGroup.vue +102 -0
  123. package/src/pages/forms/range-input/DomRangeInput.vue +296 -0
  124. package/src/pages/forms/select/DomSelect.vue +479 -0
  125. package/src/pages/forms/select-input/DomSelectInput.vue +155 -0
  126. package/src/pages/forms/tag-combobox/DomTagCombobox.vue +358 -0
  127. package/src/pages/forms/text-input/DomTextInput.vue +48 -0
  128. package/src/pages/forms/textarea-composer/DomTextareaComposer.vue +234 -0
  129. package/src/pages/forms/textarea-input/DomTextareaInput.vue +86 -0
  130. package/src/pages/forms/textarea-input/useTextareaAutosize.js +147 -0
  131. package/src/pages/forms/time-selector/DomTimeSelector.vue +389 -0
  132. package/src/pages/forms/time-selector/timeUtils.js +194 -0
  133. package/src/pages/forms/toggle/DomToggle.vue +158 -0
  134. package/src/pages/forms/toggle-button/DomToggleButton.vue +88 -0
  135. package/src/pages/forms/toggle-button-group/DomToggleButtonGroup.vue +340 -0
  136. package/src/pages/forms/url-input/DomUrlInput.vue +55 -0
  137. package/src/pages/forms/wysiwyg/DomWysiwyg.vue +204 -0
  138. package/src/pages/lib/css/color.js +245 -0
  139. package/src/pages/lib/headless/accordion.d.ts +7 -0
  140. package/src/pages/lib/headless/accordion.js +246 -0
  141. package/src/pages/lib/headless/autocomplete.d.ts +4 -0
  142. package/src/pages/lib/headless/autocomplete.js +33 -0
  143. package/src/pages/lib/headless/base.d.ts +25 -0
  144. package/src/pages/lib/headless/base.js +127 -0
  145. package/src/pages/lib/headless/checkbox.d.ts +19 -0
  146. package/src/pages/lib/headless/checkbox.js +99 -0
  147. package/src/pages/lib/headless/combobox.d.ts +7 -0
  148. package/src/pages/lib/headless/combobox.js +488 -0
  149. package/src/pages/lib/headless/dialog.d.ts +23 -0
  150. package/src/pages/lib/headless/dialog.js +384 -0
  151. package/src/pages/lib/headless/drawer.d.ts +21 -0
  152. package/src/pages/lib/headless/drawer.js +682 -0
  153. package/src/pages/lib/headless/dropdown.d.ts +19 -0
  154. package/src/pages/lib/headless/dropdown.js +412 -0
  155. package/src/pages/lib/headless/floating-effect.js +118 -0
  156. package/src/pages/lib/headless/floating.d.ts +34 -0
  157. package/src/pages/lib/headless/floating.js +382 -0
  158. package/src/pages/lib/headless/index.d.ts +19 -0
  159. package/src/pages/lib/headless/index.js +19 -0
  160. package/src/pages/lib/headless/listbox.d.ts +19 -0
  161. package/src/pages/lib/headless/listbox.js +126 -0
  162. package/src/pages/lib/headless/menu.d.ts +18 -0
  163. package/src/pages/lib/headless/menu.js +117 -0
  164. package/src/pages/lib/headless/popover-panel.d.ts +18 -0
  165. package/src/pages/lib/headless/popover-panel.js +309 -0
  166. package/src/pages/lib/headless/popover.d.ts +19 -0
  167. package/src/pages/lib/headless/popover.js +391 -0
  168. package/src/pages/lib/headless/radio-group.d.ts +19 -0
  169. package/src/pages/lib/headless/radio-group.js +100 -0
  170. package/src/pages/lib/headless/tabs.d.ts +18 -0
  171. package/src/pages/lib/headless/tabs.js +119 -0
  172. package/src/pages/lib/headless/toast.d.ts +13 -0
  173. package/src/pages/lib/headless/toast.js +65 -0
  174. package/src/pages/lib/headless/toggle.d.ts +19 -0
  175. package/src/pages/lib/headless/toggle.js +162 -0
  176. package/src/pages/lib/headless/tooltip-controller.d.ts +22 -0
  177. package/src/pages/lib/headless/tooltip-controller.js +549 -0
  178. package/src/pages/lib/headless/tooltip.d.ts +19 -0
  179. package/src/pages/lib/headless/tooltip.js +153 -0
  180. package/src/pages/lib/headless/transitions.d.ts +5 -0
  181. package/src/pages/lib/headless/transitions.js +89 -0
  182. package/src/pages/lib/vue/_internal/DomLiquidGlassSurface.vue +310 -0
  183. package/src/pages/lib/vue/_internal/liquidGlass.js +112 -0
  184. package/src/pages/lib/vue/_internal/liquidGlassConfig.js +10 -0
  185. package/src/pages/lib/vue/codeHighlighter.js +132 -0
  186. package/src/pages/lib/vue/floatingTeleportTarget.js +23 -0
  187. package/src/pages/lib/vue/index.d.ts +6806 -0
  188. package/src/pages/lib/vue/index.js +218 -0
  189. package/src/pages/lib/vue/useTheme.js +116 -0
  190. package/src/pages/mobile/action-sheet/DomActionSheet.vue +186 -0
  191. package/src/pages/mobile/bottom-nav/DomAppBottomNav.vue +94 -0
  192. package/src/pages/mobile/list-item/DomAppListItem.vue +90 -0
  193. package/src/pages/mobile/safe-area/DomSafeArea.vue +39 -0
  194. package/src/pages/mobile/shell/DomAppShell.vue +51 -0
  195. package/src/pages/mobile/stack/AppStackScreen.vue +63 -0
  196. package/src/pages/mobile/stack/DomAppStack.vue +711 -0
  197. package/src/pages/mobile/top-bar/DomAppTopBar.vue +67 -0
  198. package/src/pages/visual/alert/DomAlert.vue +363 -0
  199. package/src/pages/visual/avatar/DomAvatar.vue +110 -0
  200. package/src/pages/visual/avatar-stack/DomAvatarStack.vue +89 -0
  201. package/src/pages/visual/badge/DomBadge.vue +105 -0
  202. package/src/pages/visual/card/DomCard.vue +65 -0
  203. package/src/pages/visual/card-rail/DomCardRail.vue +99 -0
  204. package/src/pages/visual/empty-state/DomEmptyState.vue +88 -0
  205. package/src/pages/visual/progress/DomProgress.vue +212 -0
  206. package/src/pages/visual/progress/DomRadialProgress.vue +186 -0
  207. package/src/pages/visual/progress/DomSegmentedProgress.vue +350 -0
  208. package/src/pages/visual/skeleton/DomSkeleton.vue +174 -0
  209. package/src/pages/visual/spinner/DomSpinner.vue +122 -0
  210. package/src/pages/visual/status-pill/DomStatusPill.vue +75 -0
  211. package/src/pages/visual/text-diff/DomTextDiff.vue +577 -0
  212. package/src/pages/visual/text-diff/textDiff.js +300 -0
  213. package/src/style.css +446 -0
@@ -0,0 +1,237 @@
1
+ import { markRaw, ref, toRaw } from 'vue';
2
+ import DomToastItem from './DomToastItem.vue';
3
+
4
+ /**
5
+ * @typedef {import('vue').Component} ToastComponent
6
+ * @typedef {Record<string, unknown>} ToastProps
7
+ *
8
+ * @typedef {Object} ToastOptions
9
+ * @property {string} [id] Stable toast id. Generated automatically when omitted.
10
+ * @property {'default'|'success'|'danger'|'warning'|string} [tone] Visual tone used by the default toast item.
11
+ * @property {number} [duration] Auto-dismiss delay in milliseconds. Use 0 for manual dismissal.
12
+ * @property {boolean} [dismissible] Set false to hide the default dismiss button.
13
+ * @property {string} [title] Default toast title.
14
+ * @property {string} [description] Default toast description.
15
+ * @property {ToastComponent|string} [component] Component used for a custom toast template.
16
+ * @property {ToastProps} [props] Props passed to a custom toast template.
17
+ *
18
+ * @typedef {Object} ToastApi
19
+ * @property {import('vue').Ref<ToastOptions[]>} toasts Reactive toast array for <DomToastStack>.
20
+ * @property {(input?: string|ToastOptions|ToastComponent, propsOrOverrides?: ToastProps|ToastOptions, options?: ToastOptions) => string} show Show a toast and return its id.
21
+ * @property {(input?: string|ToastOptions|ToastComponent, propsOrOverrides?: ToastProps|ToastOptions, options?: ToastOptions) => string} normal Show a default-tone toast and return its id.
22
+ * @property {(input?: string|ToastOptions|ToastComponent, propsOrOverrides?: ToastProps|ToastOptions, options?: ToastOptions) => string} success Show a success-tone toast and return its id.
23
+ * @property {(input?: string|ToastOptions|ToastComponent, propsOrOverrides?: ToastProps|ToastOptions, options?: ToastOptions) => string} danger Show a danger-tone toast and return its id.
24
+ * @property {(id: string) => void} dismiss Remove a toast by id.
25
+ * @property {() => void} clear Remove all toasts.
26
+ */
27
+
28
+ /**
29
+ * Creates a small controlled toast store for <DomToastStack>.
30
+ *
31
+ * @param {ToastOptions} [defaults] Options applied to every toast shown by this store.
32
+ * @returns {ToastApi}
33
+ */
34
+ export function useToasts(defaults = {}) {
35
+ const toasts = ref([]);
36
+
37
+ /**
38
+ * Show a toast and return its generated id.
39
+ *
40
+ * Plain strings and toast objects render with the default ToastItem:
41
+ * `show('Saved', { description: 'Changes stored.' })`.
42
+ *
43
+ * Vue components render as template toasts:
44
+ * `show(AvatarToast, { name: 'Maya' }, { duration: 0 })`.
45
+ *
46
+ * @param {string|ToastOptions|ToastComponent} [input] Toast text, full toast object, or Vue component.
47
+ * @param {ToastProps|ToastOptions} [propsOrOverrides] Props for component toasts, or option overrides for default toasts.
48
+ * @param {ToastOptions} [options] Options for component toasts.
49
+ * @returns {string}
50
+ */
51
+ function show(input = {}, propsOrOverrides = {}, options = {}) {
52
+ const toast = normalizeToast(input, propsOrOverrides, options, defaults);
53
+ toasts.value = [...toasts.value, toast];
54
+ return toast.id;
55
+ }
56
+
57
+ /**
58
+ * Show a default-tone toast. Supports the same arguments as show().
59
+ *
60
+ * @param {string|ToastOptions|ToastComponent} [input]
61
+ * @param {ToastProps|ToastOptions} [propsOrOverrides]
62
+ * @param {ToastOptions} [options]
63
+ * @returns {string}
64
+ */
65
+ function normal(input, propsOrOverrides, options) {
66
+ return showWithTone('default', input, propsOrOverrides, options);
67
+ }
68
+
69
+ /**
70
+ * Show a success-tone toast. Supports the same arguments as show().
71
+ *
72
+ * @param {string|ToastOptions|ToastComponent} [input]
73
+ * @param {ToastProps|ToastOptions} [propsOrOverrides]
74
+ * @param {ToastOptions} [options]
75
+ * @returns {string}
76
+ */
77
+ function success(input, propsOrOverrides, options) {
78
+ return showWithTone('success', input, propsOrOverrides, options);
79
+ }
80
+
81
+ /**
82
+ * Show a danger-tone toast. Supports the same arguments as show().
83
+ *
84
+ * @param {string|ToastOptions|ToastComponent} [input]
85
+ * @param {ToastProps|ToastOptions} [propsOrOverrides]
86
+ * @param {ToastOptions} [options]
87
+ * @returns {string}
88
+ */
89
+ function danger(input, propsOrOverrides, options) {
90
+ return showWithTone('danger', input, propsOrOverrides, options);
91
+ }
92
+
93
+ /**
94
+ * Remove a toast by id.
95
+ *
96
+ * @param {string} id
97
+ * @returns {void}
98
+ */
99
+ function dismiss(id) {
100
+ toasts.value = toasts.value.filter((toast) => toast.id !== id);
101
+ }
102
+
103
+ /**
104
+ * Remove all visible toasts.
105
+ *
106
+ * @returns {void}
107
+ */
108
+ function clear() {
109
+ toasts.value = [];
110
+ }
111
+
112
+ return {
113
+ toasts,
114
+ show,
115
+ normal,
116
+ success,
117
+ danger,
118
+ dismiss,
119
+ clear,
120
+ };
121
+
122
+ /**
123
+ * Show a toast while forcing a specific tone into the supported argument shapes.
124
+ *
125
+ * @param {string} tone Tone to apply to the toast.
126
+ * @param {string|ToastOptions|ToastComponent} [input] Toast text, full toast object, or Vue component.
127
+ * @param {ToastProps|ToastOptions} [propsOrOverrides] Props for component toasts, or option overrides for default toasts.
128
+ * @param {ToastOptions} [options] Options for component toasts.
129
+ * @returns {string}
130
+ */
131
+ function showWithTone(tone, input, propsOrOverrides = {}, options = {}) {
132
+ const toastOptions = tone === 'default'
133
+ ? { tone, ...options }
134
+ : { ...options, tone };
135
+ const toastOverrides = tone === 'default'
136
+ ? { tone, ...propsOrOverrides }
137
+ : { ...propsOrOverrides, tone };
138
+ if (isToastComponent(input)) return show(input, propsOrOverrides, toastOptions);
139
+ return show(input, toastOverrides);
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Normalizes supported show() inputs into the controlled toast record shape.
145
+ *
146
+ * @param {string|ToastOptions|ToastComponent} input Toast text, full toast object, or Vue component.
147
+ * @param {ToastProps|ToastOptions} propsOrOverrides Props for component toasts, or option overrides for default toasts.
148
+ * @param {ToastOptions} options Options for component toasts.
149
+ * @param {ToastOptions} defaults Options applied to every toast shown by this store.
150
+ * @returns {ToastOptions & { id: string }}
151
+ */
152
+ function normalizeToast(input, propsOrOverrides, options, defaults) {
153
+ if (isToastComponent(input)) {
154
+ return {
155
+ id: createToastId(),
156
+ tone: 'default',
157
+ ...defaults,
158
+ ...options,
159
+ component: rawToastComponent(input),
160
+ props: propsOrOverrides || {},
161
+ };
162
+ }
163
+
164
+ const base = typeof input === 'string' ? { title: input } : { ...input };
165
+ const toast = {
166
+ id: createToastId(),
167
+ tone: 'default',
168
+ ...defaults,
169
+ ...base,
170
+ ...propsOrOverrides,
171
+ };
172
+
173
+ return withDefaultComponent(toast);
174
+ }
175
+
176
+ /**
177
+ * Ensures component references stay out of Vue's reactive proxy conversion.
178
+ *
179
+ * @param {ToastOptions} toast Toast record to normalize.
180
+ * @returns {ToastOptions}
181
+ */
182
+ function withDefaultComponent(toast) {
183
+ if (toast.component) {
184
+ return {
185
+ ...toast,
186
+ component: rawToastComponent(toast.component),
187
+ props: toast.props || {},
188
+ };
189
+ }
190
+ return {
191
+ ...toast,
192
+ component: rawToastComponent(DomToastItem),
193
+ props: {
194
+ title: toast.title,
195
+ description: toast.description,
196
+ tone: toast.tone,
197
+ dismissible: toast.dismissible !== false,
198
+ ...(toast.props || {}),
199
+ },
200
+ };
201
+ }
202
+
203
+ /**
204
+ * Reports whether a value looks like a Vue component that can render a toast.
205
+ *
206
+ * @param {unknown} value Value to inspect.
207
+ * @returns {boolean}
208
+ */
209
+ function isToastComponent(value) {
210
+ return Boolean(
211
+ value
212
+ && (typeof value === 'function'
213
+ || (typeof value === 'object'
214
+ && (value.setup || value.render || value.template || value.__name || value.__file))),
215
+ );
216
+ }
217
+
218
+ /**
219
+ * Marks a Vue component reference as raw before storing it in the reactive toast array.
220
+ *
221
+ * @param {ToastComponent|string} component Component reference to store.
222
+ * @returns {ToastComponent|string}
223
+ */
224
+ function rawToastComponent(component) {
225
+ if (!isToastComponent(component)) return component;
226
+ return markRaw(toRaw(component));
227
+ }
228
+
229
+ /**
230
+ * Creates a stable id for a toast that did not provide one.
231
+ *
232
+ * @returns {string}
233
+ */
234
+ function createToastId() {
235
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) return crypto.randomUUID();
236
+ return `toast-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
237
+ }
@@ -0,0 +1,158 @@
1
+ <script setup>
2
+ import { computed } from 'vue';
3
+
4
+ const toolbarControlHeights = {
5
+ xs: '1.75rem',
6
+ sm: '2rem',
7
+ md: '2.5rem',
8
+ lg: '3rem',
9
+ };
10
+ const toolbarControlRadiusValues = {
11
+ none: '0',
12
+ sm: '0.125rem',
13
+ md: '0.375rem',
14
+ lg: '0.5rem',
15
+ xl: '0.75rem',
16
+ '2xl': '1rem',
17
+ full: '9999px',
18
+ };
19
+ const toolbarSkinClasses = {
20
+ control: 'skin-control rounded-2xl p-1',
21
+ card: 'skin-card rounded-2xl p-1',
22
+ flat: 'bg-transparent',
23
+ none: '',
24
+ };
25
+
26
+ defineOptions({
27
+ __doc: {
28
+ name: 'Toolbar',
29
+ tag: '<DomToolbar>',
30
+ description: 'A compact action surface for icon buttons, segmented controls, filters, and overflow menus.',
31
+ slots: [
32
+ { name: '(default)', description: 'Toolbar controls.' },
33
+ ],
34
+ },
35
+ });
36
+
37
+ const props = defineProps({
38
+ as: {
39
+ type: String,
40
+ default: 'div',
41
+ _edit: { description: 'Element to render.' },
42
+ },
43
+ label: {
44
+ type: String,
45
+ default: 'Toolbar',
46
+ _edit: { description: 'Accessible toolbar label.' },
47
+ },
48
+ orientation: {
49
+ type: String,
50
+ default: 'horizontal',
51
+ _edit: { options: ['horizontal', 'vertical'], description: 'Toolbar direction.' },
52
+ },
53
+ align: {
54
+ type: String,
55
+ default: 'start',
56
+ _edit: { options: ['start', 'center', 'end', 'between'], description: 'Control alignment.' },
57
+ },
58
+ wrap: {
59
+ type: Boolean,
60
+ default: true,
61
+ _edit: { description: 'Allow controls to wrap on narrow screens.' },
62
+ },
63
+ skin: {
64
+ type: [String, Boolean],
65
+ default: null,
66
+ _edit: { options: ['control', 'card', 'flat', 'none', false], description: 'Toolbar surface skin. Use flat when the parent supplies border, padding, or background chrome.' },
67
+ },
68
+ inset: {
69
+ type: Boolean,
70
+ default: false,
71
+ _edit: { description: 'Use card skin instead of control skin. Prefer skin="card" for new usage.' },
72
+ },
73
+ controlSize: {
74
+ type: String,
75
+ default: 'md',
76
+ _edit: { options: ['xs', 'sm', 'md', 'lg'], description: 'Shared toolbar control height for buttons, dropdowns, and select triggers.' },
77
+ },
78
+ controlHeight: {
79
+ type: String,
80
+ default: '',
81
+ _edit: { description: 'Explicit CSS height for toolbar controls. Overrides controlSize when provided.' },
82
+ },
83
+ controlRadius: {
84
+ type: String,
85
+ default: 'full',
86
+ _edit: { options: ['none', 'sm', 'md', 'lg', 'xl', '2xl', 'full'], description: 'Shared corner radius for toolbar buttons, dropdowns, and select triggers.' },
87
+ },
88
+ });
89
+
90
+ /**
91
+ * Resolves the toolbar skin while preserving the older inset prop contract.
92
+ *
93
+ * @returns {'control'|'card'|'flat'|'none'} Resolved toolbar skin.
94
+ */
95
+ function resolveToolbarSkin() {
96
+ if (props.skin === false || props.skin === 'none') return 'none';
97
+ if (props.skin) return props.skin;
98
+ return props.inset ? 'card' : 'control';
99
+ }
100
+
101
+ /**
102
+ * Resolves the CSS height used to normalize controls inside the toolbar.
103
+ *
104
+ * @returns {string} CSS length for toolbar controls.
105
+ */
106
+ function resolveToolbarControlHeight() {
107
+ return props.controlHeight || toolbarControlHeights[props.controlSize] || toolbarControlHeights.md;
108
+ }
109
+
110
+ /**
111
+ * Resolves the CSS border radius used to normalize controls inside the toolbar.
112
+ *
113
+ * @returns {string} CSS radius for toolbar controls.
114
+ */
115
+ function resolveToolbarControlRadius() {
116
+ return toolbarControlRadiusValues[props.controlRadius] || toolbarControlRadiusValues.full;
117
+ }
118
+
119
+ const resolvedSkin = computed(resolveToolbarSkin);
120
+ const classes = computed(() => [
121
+ 'dom-toolbar inline-flex gap-1',
122
+ toolbarSkinClasses[resolvedSkin.value] ?? toolbarSkinClasses.control,
123
+ props.orientation === 'vertical' ? 'flex-col' : 'flex-row items-center',
124
+ props.wrap && props.orientation !== 'vertical' && 'flex-wrap',
125
+ {
126
+ start: 'justify-start',
127
+ center: 'justify-center',
128
+ end: 'justify-end',
129
+ between: 'justify-between',
130
+ }[props.align] || 'justify-start',
131
+ ]);
132
+ const styles = computed(() => ({
133
+ '--dom-toolbar-control-height': resolveToolbarControlHeight(),
134
+ '--dom-toolbar-control-radius': resolveToolbarControlRadius(),
135
+ }));
136
+ </script>
137
+
138
+ <template>
139
+ <component :is="as" role="toolbar" :aria-label="label" :aria-orientation="orientation" :class="classes" :style="styles">
140
+ <slot />
141
+ </component>
142
+ </template>
143
+
144
+ <style scoped>
145
+ .dom-toolbar {
146
+ --dom-toolbar-control-height: 2.5rem;
147
+ --dom-toolbar-control-radius: 9999px;
148
+ }
149
+
150
+ .dom-toolbar :deep(button),
151
+ .dom-toolbar :deep([role="button"]),
152
+ .dom-toolbar :deep([slot="trigger"]),
153
+ .dom-toolbar :deep(.dom-toolbar-control) {
154
+ border-radius: var(--dom-toolbar-control-radius);
155
+ height: var(--dom-toolbar-control-height);
156
+ min-height: var(--dom-toolbar-control-height);
157
+ }
158
+ </style>
@@ -0,0 +1,69 @@
1
+ <script setup>
2
+ import { onMounted } from 'vue';
3
+
4
+ const props = defineProps({
5
+ for: {
6
+ type: String,
7
+ default: '',
8
+ _edit: { description: 'ID of an existing trigger element. Leave empty to wrap the trigger in the default slot.' },
9
+ },
10
+ text: {
11
+ type: String,
12
+ required: true,
13
+ _edit: { description: 'Tooltip body. Keep it short — one line works best.' },
14
+ },
15
+ placement: {
16
+ type: String,
17
+ default: 'top',
18
+ _edit: {
19
+ options: ['top', 'bottom', 'left', 'right'],
20
+ description: 'Position of the bubble relative to the trigger.',
21
+ },
22
+ },
23
+ delay: {
24
+ type: Number,
25
+ default: 120,
26
+ _edit: { description: 'Open delay in milliseconds — stops flicker when sweeping the cursor across.' },
27
+ },
28
+ offset: {
29
+ type: Number,
30
+ default: 6,
31
+ _edit: { description: 'Gap in pixels between the trigger and tooltip.' },
32
+ },
33
+ collisionPadding: {
34
+ type: Number,
35
+ default: 8,
36
+ _edit: { description: 'Viewport padding used when the tooltip flips or shifts.' },
37
+ },
38
+ floatingMode: {
39
+ type: String,
40
+ default: 'viewport',
41
+ _edit: { options: ['viewport', 'anchor'], description: 'viewport keeps the tooltip inside the browser; anchor keeps it attached while scrolling.' },
42
+ },
43
+ });
44
+
45
+ /**
46
+ * Register the headless tooltip element after Vue mounts its target markup.
47
+ *
48
+ * @returns {Promise<void>}
49
+ */
50
+ async function registerTooltipElement() {
51
+ await import('../../lib/headless/tooltip.js');
52
+ }
53
+
54
+ onMounted(registerTooltipElement);
55
+ </script>
56
+
57
+ <template>
58
+ <dom-tooltip
59
+ :for="props.for || undefined"
60
+ :text="text"
61
+ :placement="placement"
62
+ :delay="delay"
63
+ :offset="offset"
64
+ :collision-padding="collisionPadding"
65
+ :floating-mode="floatingMode"
66
+ >
67
+ <slot />
68
+ </dom-tooltip>
69
+ </template>
@@ -0,0 +1,106 @@
1
+ import { attachTooltip } from '../../lib/headless/tooltip-controller.js';
2
+
3
+ const controllers = new WeakMap();
4
+ const placementModifiers = ['top', 'right', 'bottom', 'left'];
5
+
6
+ /**
7
+ * Resolve the first supported placement modifier from a Vue directive binding.
8
+ *
9
+ * @param {Record<string, boolean>} modifiers Vue directive modifiers.
10
+ * @returns {'top' | 'right' | 'bottom' | 'left' | undefined} Requested placement.
11
+ */
12
+ function placementFromModifiers(modifiers) {
13
+ return placementModifiers.find((placement) => modifiers[placement]);
14
+ }
15
+
16
+ /**
17
+ * Normalize a Vue directive binding into the shared tooltip controller shape.
18
+ *
19
+ * String bindings are shorthand for tooltip text. Object bindings may use
20
+ * either `label` or `text`, and falsey or disabled bindings remove behaviour
21
+ * without unmounting the target element.
22
+ *
23
+ * @param {unknown} value Vue directive binding value.
24
+ * @param {Record<string, boolean>} modifiers Vue directive modifiers.
25
+ * @returns {Record<string, unknown> | null} Controller options or null when disabled.
26
+ */
27
+ function normalizeDirectiveValue(value, modifiers = {}) {
28
+ const placement = placementFromModifiers(modifiers);
29
+
30
+ if (typeof value === 'string') {
31
+ if (!value.trim()) return null;
32
+ return {
33
+ text: value,
34
+ ...(placement ? { placement } : {}),
35
+ };
36
+ }
37
+ if (!value || typeof value !== 'object' || value.disabled) return null;
38
+ const text = String(value.text ?? value.label ?? '');
39
+ if (!text.trim()) return null;
40
+ return {
41
+ ...value,
42
+ text,
43
+ ...(placement ? { placement } : {}),
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Attach, update, or remove tooltip behaviour for one directive target.
49
+ *
50
+ * @param {HTMLElement} element Vue directive target element.
51
+ * @param {unknown} value Current directive binding value.
52
+ * @param {Record<string, boolean>} modifiers Vue directive modifiers.
53
+ * @returns {void}
54
+ */
55
+ function syncDirectiveTooltip(element, value, modifiers = {}) {
56
+ const options = normalizeDirectiveValue(value, modifiers);
57
+ const controller = controllers.get(element);
58
+
59
+ if (!options) {
60
+ controller?.destroy();
61
+ controllers.delete(element);
62
+ return;
63
+ }
64
+
65
+ if (controller) {
66
+ controller.update(options);
67
+ return;
68
+ }
69
+
70
+ controllers.set(element, attachTooltip(element, options, element));
71
+ }
72
+
73
+ export const vDomTooltip = {
74
+ /**
75
+ * Register tooltip behaviour directly on the mounted element.
76
+ *
77
+ * @param {HTMLElement} element Vue directive target element.
78
+ * @param {{ value: unknown, modifiers: Record<string, boolean> }} binding Vue directive binding.
79
+ * @returns {void}
80
+ */
81
+ mounted(element, binding) {
82
+ syncDirectiveTooltip(element, binding.value, binding.modifiers);
83
+ },
84
+
85
+ /**
86
+ * Synchronize content, placement, and enabled state after Vue updates.
87
+ *
88
+ * @param {HTMLElement} element Vue directive target element.
89
+ * @param {{ value: unknown, modifiers: Record<string, boolean> }} binding Vue directive binding.
90
+ * @returns {void}
91
+ */
92
+ updated(element, binding) {
93
+ syncDirectiveTooltip(element, binding.value, binding.modifiers);
94
+ },
95
+
96
+ /**
97
+ * Release listeners, ARIA wiring, and the bubble before unmount.
98
+ *
99
+ * @param {HTMLElement} element Vue directive target element.
100
+ * @returns {void}
101
+ */
102
+ beforeUnmount(element) {
103
+ controllers.get(element)?.destroy();
104
+ controllers.delete(element);
105
+ },
106
+ };