@threadlabs/looma 0.6.4 → 0.7.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 (259) hide show
  1. package/components/ui-button/ui-button.html +103 -81
  2. package/components/ui-checkbox/ui-checkbox.html +4 -1
  3. package/components/ui-combobox/ui-combobox.html +40 -3
  4. package/components/ui-combobox/ui-combobox.js +27 -5
  5. package/components/ui-select/ui-select.html +3 -5
  6. package/dist/index.js +1 -1
  7. package/package.json +2 -2
  8. package/styles/ui-affordance-scope.css +1 -1
  9. package/styles/ui-avatar-group.css +1 -1
  10. package/styles/ui-avatar.css +1 -1
  11. package/styles/ui-badge.css +1 -1
  12. package/styles/ui-button.css +112 -80
  13. package/styles/ui-callout.css +1 -1
  14. package/styles/ui-checkbox.css +5 -2
  15. package/styles/ui-cluster.css +1 -1
  16. package/styles/ui-combobox.css +38 -1
  17. package/styles/ui-container.css +1 -1
  18. package/styles/ui-context-menu.css +1 -1
  19. package/styles/ui-dialog.css +1 -1
  20. package/styles/ui-disclosure.css +1 -1
  21. package/styles/ui-editable.css +1 -1
  22. package/styles/ui-editor-insert-table-grid.css +1 -1
  23. package/styles/ui-editor-mention-menu.css +1 -1
  24. package/styles/ui-editor-slash-menu.css +1 -1
  25. package/styles/ui-editor-table-context-menu.css +1 -1
  26. package/styles/ui-editor-table-overlay.css +1 -1
  27. package/styles/ui-editor-table-toolbar.css +1 -1
  28. package/styles/ui-editor-toolbar.css +1 -1
  29. package/styles/ui-floating-action-button.css +1 -1
  30. package/styles/ui-form-field.css +1 -1
  31. package/styles/ui-grid.css +1 -1
  32. package/styles/ui-icon-button.css +1 -1
  33. package/styles/ui-icon.css +1 -1
  34. package/styles/ui-input.css +1 -1
  35. package/styles/ui-menu-item.css +1 -1
  36. package/styles/ui-menu.css +1 -1
  37. package/styles/ui-popover.css +1 -1
  38. package/styles/ui-radio-group.css +1 -1
  39. package/styles/ui-radio.css +1 -1
  40. package/styles/ui-reel.css +1 -1
  41. package/styles/ui-search-result-row.css +1 -1
  42. package/styles/ui-search-shell.css +1 -1
  43. package/styles/ui-select.css +4 -5
  44. package/styles/ui-separator.css +1 -1
  45. package/styles/ui-sidebar.css +1 -1
  46. package/styles/ui-stack.css +1 -1
  47. package/styles/ui-switch.css +1 -1
  48. package/styles/ui-switcher.css +1 -1
  49. package/styles/ui-tabs.css +1 -1
  50. package/styles/ui-textarea.css +1 -1
  51. package/styles/ui-toast-region.css +1 -1
  52. package/styles/ui-tooltip.css +1 -1
  53. package/styles/ui-top-bar.css +1 -1
  54. package/styles/ui-tree-item.css +1 -1
  55. package/styles/ui-tree.css +1 -1
  56. package/theme-dark.css +6 -30
  57. package/theme-high-contrast.css +30 -20
  58. package/theme-light.css +2 -16
  59. package/tokens.css +33 -24
  60. package/vanilla/UiAffordanceScope.d.ts +1 -1
  61. package/vanilla/UiAffordanceScope.js +1 -1
  62. package/vanilla/UiAvatar.d.ts +1 -1
  63. package/vanilla/UiAvatar.js +1 -1
  64. package/vanilla/UiAvatarGroup.d.ts +1 -1
  65. package/vanilla/UiAvatarGroup.js +1 -1
  66. package/vanilla/UiBadge.d.ts +1 -1
  67. package/vanilla/UiBadge.js +1 -1
  68. package/vanilla/UiButton.d.ts +2 -2
  69. package/vanilla/UiButton.js +2 -2
  70. package/vanilla/UiCallout.d.ts +1 -1
  71. package/vanilla/UiCallout.js +1 -1
  72. package/vanilla/UiCheckbox.d.ts +1 -1
  73. package/vanilla/UiCheckbox.js +1 -1
  74. package/vanilla/UiCluster.d.ts +1 -1
  75. package/vanilla/UiCluster.js +1 -1
  76. package/vanilla/UiCombobox.d.ts +1 -1
  77. package/vanilla/UiCombobox.js +2 -2
  78. package/vanilla/UiContainer.d.ts +1 -1
  79. package/vanilla/UiContainer.js +1 -1
  80. package/vanilla/UiContextMenu.d.ts +1 -1
  81. package/vanilla/UiContextMenu.js +1 -1
  82. package/vanilla/UiDialog.d.ts +1 -1
  83. package/vanilla/UiDialog.js +1 -1
  84. package/vanilla/UiDisclosure.d.ts +1 -1
  85. package/vanilla/UiDisclosure.js +1 -1
  86. package/vanilla/UiEditable.d.ts +1 -1
  87. package/vanilla/UiEditable.js +1 -1
  88. package/vanilla/UiEditorInsertTableGrid.d.ts +1 -1
  89. package/vanilla/UiEditorInsertTableGrid.js +1 -1
  90. package/vanilla/UiEditorMentionMenu.d.ts +1 -1
  91. package/vanilla/UiEditorMentionMenu.js +1 -1
  92. package/vanilla/UiEditorSlashMenu.d.ts +1 -1
  93. package/vanilla/UiEditorSlashMenu.js +1 -1
  94. package/vanilla/UiEditorTableContextMenu.d.ts +1 -1
  95. package/vanilla/UiEditorTableContextMenu.js +1 -1
  96. package/vanilla/UiEditorTableOverlay.d.ts +1 -1
  97. package/vanilla/UiEditorTableOverlay.js +1 -1
  98. package/vanilla/UiEditorTableToolbar.d.ts +1 -1
  99. package/vanilla/UiEditorTableToolbar.js +1 -1
  100. package/vanilla/UiEditorToolbar.d.ts +1 -1
  101. package/vanilla/UiEditorToolbar.js +1 -1
  102. package/vanilla/UiFloatingActionButton.d.ts +1 -1
  103. package/vanilla/UiFloatingActionButton.js +1 -1
  104. package/vanilla/UiFormField.d.ts +1 -1
  105. package/vanilla/UiFormField.js +1 -1
  106. package/vanilla/UiGrid.d.ts +1 -1
  107. package/vanilla/UiGrid.js +1 -1
  108. package/vanilla/UiIcon.d.ts +1 -1
  109. package/vanilla/UiIcon.js +1 -1
  110. package/vanilla/UiIconButton.d.ts +1 -1
  111. package/vanilla/UiIconButton.js +1 -1
  112. package/vanilla/UiInput.d.ts +1 -1
  113. package/vanilla/UiInput.js +1 -1
  114. package/vanilla/UiMenu.d.ts +1 -1
  115. package/vanilla/UiMenu.js +1 -1
  116. package/vanilla/UiMenuItem.d.ts +1 -1
  117. package/vanilla/UiMenuItem.js +1 -1
  118. package/vanilla/UiPopover.d.ts +1 -1
  119. package/vanilla/UiPopover.js +1 -1
  120. package/vanilla/UiRadio.d.ts +1 -1
  121. package/vanilla/UiRadio.js +1 -1
  122. package/vanilla/UiRadioGroup.d.ts +1 -1
  123. package/vanilla/UiRadioGroup.js +1 -1
  124. package/vanilla/UiReel.d.ts +1 -1
  125. package/vanilla/UiReel.js +1 -1
  126. package/vanilla/UiSearchResultRow.d.ts +1 -1
  127. package/vanilla/UiSearchResultRow.js +1 -1
  128. package/vanilla/UiSearchShell.d.ts +1 -1
  129. package/vanilla/UiSearchShell.js +1 -1
  130. package/vanilla/UiSelect.d.ts +1 -2
  131. package/vanilla/UiSelect.js +4 -7
  132. package/vanilla/UiSeparator.d.ts +1 -1
  133. package/vanilla/UiSeparator.js +1 -1
  134. package/vanilla/UiSidebar.d.ts +1 -1
  135. package/vanilla/UiSidebar.js +1 -1
  136. package/vanilla/UiStack.d.ts +1 -1
  137. package/vanilla/UiStack.js +1 -1
  138. package/vanilla/UiSwitch.d.ts +1 -1
  139. package/vanilla/UiSwitch.js +1 -1
  140. package/vanilla/UiSwitcher.d.ts +1 -1
  141. package/vanilla/UiSwitcher.js +1 -1
  142. package/vanilla/UiTabs.d.ts +1 -1
  143. package/vanilla/UiTabs.js +1 -1
  144. package/vanilla/UiTextarea.d.ts +1 -1
  145. package/vanilla/UiTextarea.js +1 -1
  146. package/vanilla/UiToastRegion.d.ts +1 -1
  147. package/vanilla/UiToastRegion.js +1 -1
  148. package/vanilla/UiTooltip.d.ts +1 -1
  149. package/vanilla/UiTooltip.js +1 -1
  150. package/vanilla/UiTopBar.d.ts +1 -1
  151. package/vanilla/UiTopBar.js +1 -1
  152. package/vanilla/UiTree.d.ts +1 -1
  153. package/vanilla/UiTree.js +1 -1
  154. package/vanilla/UiTreeItem.d.ts +1 -1
  155. package/vanilla/UiTreeItem.js +1 -1
  156. package/vue/UiAffordanceScope.vue +8 -84
  157. package/vue/UiAvatar.d.ts +1 -1
  158. package/vue/UiAvatar.vue +9 -106
  159. package/vue/UiAvatarGroup.vue +9 -96
  160. package/vue/UiBadge.vue +1 -1
  161. package/vue/UiButton.d.ts +2 -2
  162. package/vue/UiButton.vue +104 -97
  163. package/vue/UiCallout.vue +1 -1
  164. package/vue/UiCheckbox.vue +26 -124
  165. package/vue/UiCluster.vue +1 -1
  166. package/vue/UiCombobox.d.ts +1 -1
  167. package/vue/UiCombobox.vue +270 -479
  168. package/vue/UiContainer.vue +1 -1
  169. package/vue/UiContextMenu.vue +34 -141
  170. package/vue/UiDialog.vue +25 -126
  171. package/vue/UiDisclosure.vue +26 -138
  172. package/vue/UiEditable.vue +46 -158
  173. package/vue/UiEditorInsertTableGrid.vue +19 -138
  174. package/vue/UiEditorMentionMenu.vue +17 -124
  175. package/vue/UiEditorSlashMenu.vue +17 -109
  176. package/vue/UiEditorTableContextMenu.vue +34 -122
  177. package/vue/UiEditorTableOverlay.vue +61 -167
  178. package/vue/UiEditorTableToolbar.vue +34 -142
  179. package/vue/UiEditorToolbar.vue +1 -1
  180. package/vue/UiFloatingActionButton.vue +1 -1
  181. package/vue/UiFormField.vue +7 -79
  182. package/vue/UiGrid.vue +1 -1
  183. package/vue/UiIcon.vue +8 -84
  184. package/vue/UiIconButton.vue +7 -79
  185. package/vue/UiInput.vue +7 -79
  186. package/vue/UiMenu.vue +42 -127
  187. package/vue/UiMenuItem.vue +1 -1
  188. package/vue/UiPopover.vue +25 -121
  189. package/vue/UiRadio.vue +22 -123
  190. package/vue/UiRadioGroup.d.ts +1 -1
  191. package/vue/UiRadioGroup.vue +32 -119
  192. package/vue/UiReel.vue +1 -1
  193. package/vue/UiSearchResultRow.vue +1 -1
  194. package/vue/UiSearchShell.vue +25 -121
  195. package/vue/UiSelect.d.ts +0 -2
  196. package/vue/UiSelect.vue +9 -85
  197. package/vue/UiSeparator.vue +1 -1
  198. package/vue/UiSidebar.vue +32 -133
  199. package/vue/UiStack.vue +1 -1
  200. package/vue/UiSwitch.vue +22 -123
  201. package/vue/UiSwitcher.vue +1 -1
  202. package/vue/UiTabs.vue +24 -128
  203. package/vue/UiTextarea.vue +7 -79
  204. package/vue/UiToastRegion.vue +37 -126
  205. package/vue/UiTooltip.vue +25 -124
  206. package/vue/UiTopBar.vue +1 -1
  207. package/vue/UiTree.vue +56 -137
  208. package/vue/UiTreeItem.vue +28 -144
  209. package/vue/chunks/UiAffordanceScope.js +9 -78
  210. package/vue/chunks/UiAvatar.js +13 -89
  211. package/vue/chunks/UiAvatarGroup.js +10 -80
  212. package/vue/chunks/UiBadge.js +1 -1
  213. package/vue/chunks/UiButton.js +3 -3
  214. package/vue/chunks/UiCallout.js +1 -1
  215. package/vue/chunks/UiCheckbox.js +17 -95
  216. package/vue/chunks/UiCluster.js +1 -1
  217. package/vue/chunks/UiCombobox.js +115 -326
  218. package/vue/chunks/UiContainer.js +1 -1
  219. package/vue/chunks/UiContextMenu.js +21 -113
  220. package/vue/chunks/UiDialog.js +20 -99
  221. package/vue/chunks/UiDisclosure.js +22 -112
  222. package/vue/chunks/UiEditable.js +32 -128
  223. package/vue/chunks/UiEditorInsertTableGrid.js +22 -116
  224. package/vue/chunks/UiEditorMentionMenu.js +18 -116
  225. package/vue/chunks/UiEditorSlashMenu.js +15 -101
  226. package/vue/chunks/UiEditorTableContextMenu.js +12 -91
  227. package/vue/chunks/UiEditorTableOverlay.js +29 -120
  228. package/vue/chunks/UiEditorTableToolbar.js +16 -111
  229. package/vue/chunks/UiEditorToolbar.js +1 -1
  230. package/vue/chunks/UiFloatingActionButton.js +1 -1
  231. package/vue/chunks/UiFormField.js +8 -73
  232. package/vue/chunks/UiGrid.js +1 -1
  233. package/vue/chunks/UiIcon.js +9 -78
  234. package/vue/chunks/UiIconButton.js +8 -73
  235. package/vue/chunks/UiInput.js +8 -73
  236. package/vue/chunks/UiMenu.js +25 -111
  237. package/vue/chunks/UiMenuItem.js +1 -1
  238. package/vue/chunks/UiPopover.js +18 -105
  239. package/vue/chunks/UiRadio.js +17 -95
  240. package/vue/chunks/UiRadioGroup.js +23 -105
  241. package/vue/chunks/UiReel.js +1 -1
  242. package/vue/chunks/UiSearchResultRow.js +1 -1
  243. package/vue/chunks/UiSearchShell.js +17 -94
  244. package/vue/chunks/UiSelect.js +8 -79
  245. package/vue/chunks/UiSeparator.js +1 -1
  246. package/vue/chunks/UiSidebar.js +23 -108
  247. package/vue/chunks/UiStack.js +1 -1
  248. package/vue/chunks/UiSwitch.js +17 -95
  249. package/vue/chunks/UiSwitcher.js +1 -1
  250. package/vue/chunks/UiTabs.js +22 -103
  251. package/vue/chunks/UiTextarea.js +8 -73
  252. package/vue/chunks/UiToastRegion.js +27 -112
  253. package/vue/chunks/UiTooltip.js +18 -106
  254. package/vue/chunks/UiTopBar.js +1 -1
  255. package/vue/chunks/UiTree.js +31 -109
  256. package/vue/chunks/UiTreeItem.js +29 -119
  257. package/vue/chunks/host.js +101 -0
  258. package/vue/components.css +844 -797
  259. package/vue/host.ts +154 -0
@@ -1,7 +1,8 @@
1
- <!-- Generated by HTML Next 1.0.0-alpha.2 for Vue 3.5. Do not edit. -->
1
+ <!-- Generated by HTML Next 1.0.0-alpha.3 for Vue 3.5. Do not edit. -->
2
2
  <script setup lang="ts">
3
- import { computed, onBeforeUnmount, onMounted, ref, shallowRef, watchEffect } from 'vue'
3
+ import { computed, ref } from 'vue'
4
4
  import * as controllerModule from '../components/ui-radio-group/ui-radio-group.js'
5
+ import { createDispatch, useComponentHost } from './host'
5
6
 
6
7
  defineOptions({ inheritAttrs: false })
7
8
 
@@ -51,125 +52,37 @@ const hostState = computed(() =>
51
52
  ].filter(Boolean).join(' ')
52
53
  )
53
54
 
54
- /** Dispatches a component event to Vue listeners and, for controllers and page code, on the root. */
55
- function dispatch(name: string, detail?: unknown): boolean {
56
- const declared: Record<string, EventInit | undefined> = {
57
- select: { bubbles: true, composed: true, cancelable: false },
58
- change: { bubbles: true, composed: true, cancelable: false },
59
- }
60
- const checks: Record<string, (detail: unknown) => boolean> = {
61
- select: (detail) =>
62
- detail !== null && typeof detail === 'object' && !Array.isArray(detail)
63
- && typeof (detail as Record<string, unknown>)['value'] === 'string'
64
- && typeof (detail as Record<string, unknown>)['previousValue'] === 'string'
65
- && ((detail as Record<string, unknown>)['trigger'] === 'keyboard'
66
- || (detail as Record<string, unknown>)['trigger'] === 'pointer'
67
- || (detail as Record<string, unknown>)['trigger'] === 'programmatic')
68
- && Object.keys(detail as object).every((key) =>
69
- ['value', 'previousValue', 'trigger'].includes(key)
70
- ),
71
- change: (detail) =>
72
- detail !== null && typeof detail === 'object' && !Array.isArray(detail)
73
- && typeof (detail as Record<string, unknown>)['checked'] === 'boolean'
74
- && typeof (detail as Record<string, unknown>)['value'] === 'string'
75
- && ((detail as Record<string, unknown>)['trigger'] === 'keyboard'
76
- || (detail as Record<string, unknown>)['trigger'] === 'pointer'
77
- || (detail as Record<string, unknown>)['trigger'] === 'programmatic')
78
- && Object.keys(detail as object).every((key) =>
79
- ['checked', 'value', 'trigger'].includes(key)
80
- ),
81
- }
82
- if (detail !== undefined && checks[name] !== undefined && !checks[name]!(detail)) {
83
- throw new TypeError(`HR002: Event \`${name}\` detail does not satisfy its declared type.`)
84
- }
85
- const emitEvent = emit as (name: string, detail: unknown) => void
86
- emitEvent(name, detail)
87
- if (detail !== null && typeof detail === 'object' && 'value' in detail) {
88
- emitEvent('update:value', (detail as Record<string, unknown>)['value'])
89
- }
90
- return root.value?.dispatchEvent(
91
- new CustomEvent(name, { bubbles: true, composed: true, ...declared[name], detail }),
92
- ) ?? true
93
- }
94
-
95
- function read(name: string): unknown {
96
- if (name === 'internalValue') return internalValue.value
97
- return (props as Record<string, unknown>)[name]
98
- }
99
-
100
- function write(name: string, value: unknown): boolean {
101
- if (name === 'internalValue') {
102
- internalValue.value = value as never
103
- return true
104
- }
105
- throw new TypeError(`Only declared state is writable; \`${name}\` is not.`)
106
- }
107
-
108
- const stops: Array<() => void> = []
109
- const host = {
110
- get element(): Element {
111
- return root.value as Element
112
- },
113
- state: new Proxy({} as Record<string, unknown>, {
114
- get: (_target, name) => typeof name === 'string' ? read(name) : undefined,
115
- set: (_target, name, value) => typeof name === 'string' && write(name, value),
116
- }),
117
- refs: {} as Readonly<Record<string, Element>>,
118
- elements: new Proxy({} as Record<string, Element | RadioNodeList | undefined>, {
119
- get: (_target, name) => {
120
- if (typeof name !== 'string' || root.value === null) return undefined
121
- const form = root.value instanceof HTMLFormElement
122
- ? root.value
123
- : root.value.querySelector('form')
124
- return form?.elements.namedItem(name)
125
- ?? root.value.querySelector(`[name="${CSS.escape(name)}"]`) ?? undefined
126
- },
127
- }),
128
- signal<T>(initialValue: T) {
129
- const value = shallowRef(initialValue)
130
- return {
131
- get: (): T => value.value,
132
- set: (next: T): void => {
133
- if (!Object.is(value.value, next)) value.value = next
134
- },
135
- update: (next: (current: T) => T): void => {
136
- const updated = next(value.value)
137
- if (!Object.is(value.value, updated)) value.value = updated
138
- },
139
- }
55
+ const isSelectDetail = (detail: unknown): boolean =>
56
+ detail !== null && typeof detail === 'object' && !Array.isArray(detail)
57
+ && typeof (detail as Record<string, unknown>)['value'] === 'string'
58
+ && typeof (detail as Record<string, unknown>)['previousValue'] === 'string'
59
+ && ((detail as Record<string, unknown>)['trigger'] === 'keyboard'
60
+ || (detail as Record<string, unknown>)['trigger'] === 'pointer'
61
+ || (detail as Record<string, unknown>)['trigger'] === 'programmatic')
62
+ && Object.keys(detail as object).every((key) =>
63
+ ['value', 'previousValue', 'trigger'].includes(key)
64
+ )
65
+ const isChangeDetail = (detail: unknown): boolean =>
66
+ detail !== null && typeof detail === 'object' && !Array.isArray(detail)
67
+ && typeof (detail as Record<string, unknown>)['checked'] === 'boolean'
68
+ && typeof (detail as Record<string, unknown>)['value'] === 'string'
69
+ && ((detail as Record<string, unknown>)['trigger'] === 'keyboard'
70
+ || (detail as Record<string, unknown>)['trigger'] === 'pointer'
71
+ || (detail as Record<string, unknown>)['trigger'] === 'programmatic')
72
+ && Object.keys(detail as object).every((key) => ['checked', 'value', 'trigger'].includes(key))
73
+ const dispatch = createDispatch(root, emit as (name: string, detail: unknown) => void, {
74
+ checks: {
75
+ select: isSelectDetail,
76
+ change: isChangeDetail,
140
77
  },
141
- computed<T>(compute: () => T) {
142
- const value = computed(compute)
143
- return { get: (): T => value.value }
144
- },
145
- effect(run: () => void | (() => void)): () => void {
146
- const stop = watchEffect((onCleanup) => {
147
- const cleanup = run()
148
- if (typeof cleanup === 'function') onCleanup(cleanup)
149
- }, { flush: 'post' })
150
- stops.push(stop)
151
- return stop
152
- },
153
- on(event: string, listener: EventListener): () => void {
154
- const element = root.value
155
- element?.addEventListener(event, listener)
156
- const off = (): void => element?.removeEventListener(event, listener)
157
- stops.push(off)
158
- return off
159
- },
160
- dispatch,
161
- }
162
-
163
- let cleanup: void | (() => void)
164
- let ready: Promise<void> | undefined
165
- onMounted(() => {
166
- ready = Promise.resolve(controllerModule.default(host as never)).then((result) => {
167
- cleanup = result
168
- })
78
+ modeled: ['value'],
169
79
  })
170
- onBeforeUnmount(() => {
171
- for (const stop of stops.splice(0)) stop()
172
- if (typeof cleanup === 'function') cleanup()
80
+
81
+ useComponentHost(controllerModule.default, {
82
+ root,
83
+ dispatch,
84
+ props,
85
+ state: { internalValue },
173
86
  })
174
87
  </script>
175
88
 
package/vue/UiReel.vue CHANGED
@@ -1,4 +1,4 @@
1
- <!-- Generated by HTML Next 1.0.0-alpha.2 for Vue 3.5. Do not edit. -->
1
+ <!-- Generated by HTML Next 1.0.0-alpha.3 for Vue 3.5. Do not edit. -->
2
2
  <script setup lang="ts">
3
3
  import { computed } from 'vue'
4
4
 
@@ -1,4 +1,4 @@
1
- <!-- Generated by HTML Next 1.0.0-alpha.2 for Vue 3.5. Do not edit. -->
1
+ <!-- Generated by HTML Next 1.0.0-alpha.3 for Vue 3.5. Do not edit. -->
2
2
  <script setup lang="ts">
3
3
  import { computed } from 'vue'
4
4
 
@@ -1,15 +1,8 @@
1
- <!-- Generated by HTML Next 1.0.0-alpha.2 for Vue 3.5. Do not edit. -->
1
+ <!-- Generated by HTML Next 1.0.0-alpha.3 for Vue 3.5. Do not edit. -->
2
2
  <script setup lang="ts">
3
- import {
4
- computed,
5
- onBeforeUnmount,
6
- onMounted,
7
- ref,
8
- shallowRef,
9
- useTemplateRef,
10
- watchEffect,
11
- } from 'vue'
3
+ import { ref, useTemplateRef } from 'vue'
12
4
  import * as controllerModule from '../components/ui-search-shell/ui-search-shell.js'
5
+ import { createDispatch, useComponentHost } from './host'
13
6
 
14
7
  defineOptions({ inheritAttrs: false })
15
8
 
@@ -42,119 +35,30 @@ const root = ref<HTMLElement | null>(null)
42
35
  const dialogElement = useTemplateRef<HTMLElement>('dialog')
43
36
  const internalOpen = ref(false)
44
37
 
45
- /** Dispatches a component event to Vue listeners and, for controllers and page code, on the root. */
46
- function dispatch(name: string, detail?: unknown): boolean {
47
- const declared: Record<string, EventInit | undefined> = {
48
- close: { bubbles: true, composed: true, cancelable: false },
49
- }
50
- const checks: Record<string, (detail: unknown) => boolean> = {
51
- close: (detail) =>
52
- detail !== null && typeof detail === 'object' && !Array.isArray(detail)
53
- && typeof (detail as Record<string, unknown>)['open'] === 'boolean'
54
- && ((detail as Record<string, unknown>)['reason'] === 'action'
55
- || (detail as Record<string, unknown>)['reason'] === 'programmatic'
56
- || (detail as Record<string, unknown>)['reason'] === 'light-dismiss'
57
- || (detail as Record<string, unknown>)['reason'] === 'escape')
58
- && ((detail as Record<string, unknown>)['trigger'] === 'keyboard'
59
- || (detail as Record<string, unknown>)['trigger'] === 'pointer'
60
- || (detail as Record<string, unknown>)['trigger'] === 'programmatic')
61
- && Object.keys(detail as object).every((key) => ['open', 'reason', 'trigger'].includes(key)),
62
- }
63
- if (detail !== undefined && checks[name] !== undefined && !checks[name]!(detail)) {
64
- throw new TypeError(`HR002: Event \`${name}\` detail does not satisfy its declared type.`)
65
- }
66
- const emitEvent = emit as (name: string, detail: unknown) => void
67
- emitEvent(name, detail)
68
- if (detail !== null && typeof detail === 'object' && 'open' in detail) {
69
- emitEvent('update:open', (detail as Record<string, unknown>)['open'])
70
- }
71
- return root.value?.dispatchEvent(
72
- new CustomEvent(name, { bubbles: true, composed: true, ...declared[name], detail }),
73
- ) ?? true
74
- }
75
-
76
- function read(name: string): unknown {
77
- if (name === 'internalOpen') return internalOpen.value
78
- return (props as Record<string, unknown>)[name]
79
- }
80
-
81
- function write(name: string, value: unknown): boolean {
82
- if (name === 'internalOpen') {
83
- internalOpen.value = value as never
84
- return true
85
- }
86
- throw new TypeError(`Only declared state is writable; \`${name}\` is not.`)
87
- }
88
-
89
- const stops: Array<() => void> = []
90
- const host = {
91
- get element(): Element {
92
- return root.value as Element
38
+ const isCloseDetail = (detail: unknown): boolean =>
39
+ detail !== null && typeof detail === 'object' && !Array.isArray(detail)
40
+ && typeof (detail as Record<string, unknown>)['open'] === 'boolean'
41
+ && ((detail as Record<string, unknown>)['reason'] === 'action'
42
+ || (detail as Record<string, unknown>)['reason'] === 'programmatic'
43
+ || (detail as Record<string, unknown>)['reason'] === 'light-dismiss'
44
+ || (detail as Record<string, unknown>)['reason'] === 'escape')
45
+ && ((detail as Record<string, unknown>)['trigger'] === 'keyboard'
46
+ || (detail as Record<string, unknown>)['trigger'] === 'pointer'
47
+ || (detail as Record<string, unknown>)['trigger'] === 'programmatic')
48
+ && Object.keys(detail as object).every((key) => ['open', 'reason', 'trigger'].includes(key))
49
+ const dispatch = createDispatch(root, emit as (name: string, detail: unknown) => void, {
50
+ checks: {
51
+ close: isCloseDetail,
93
52
  },
94
- state: new Proxy({} as Record<string, unknown>, {
95
- get: (_target, name) => typeof name === 'string' ? read(name) : undefined,
96
- set: (_target, name, value) => typeof name === 'string' && write(name, value),
97
- }),
98
- refs: {
99
- get dialog(): Element {
100
- return dialogElement.value as Element
101
- },
102
- } as Readonly<Record<string, Element>>,
103
- elements: new Proxy({} as Record<string, Element | RadioNodeList | undefined>, {
104
- get: (_target, name) => {
105
- if (typeof name !== 'string' || root.value === null) return undefined
106
- const form = root.value instanceof HTMLFormElement
107
- ? root.value
108
- : root.value.querySelector('form')
109
- return form?.elements.namedItem(name)
110
- ?? root.value.querySelector(`[name="${CSS.escape(name)}"]`) ?? undefined
111
- },
112
- }),
113
- signal<T>(initialValue: T) {
114
- const value = shallowRef(initialValue)
115
- return {
116
- get: (): T => value.value,
117
- set: (next: T): void => {
118
- if (!Object.is(value.value, next)) value.value = next
119
- },
120
- update: (next: (current: T) => T): void => {
121
- const updated = next(value.value)
122
- if (!Object.is(value.value, updated)) value.value = updated
123
- },
124
- }
125
- },
126
- computed<T>(compute: () => T) {
127
- const value = computed(compute)
128
- return { get: (): T => value.value }
129
- },
130
- effect(run: () => void | (() => void)): () => void {
131
- const stop = watchEffect((onCleanup) => {
132
- const cleanup = run()
133
- if (typeof cleanup === 'function') onCleanup(cleanup)
134
- }, { flush: 'post' })
135
- stops.push(stop)
136
- return stop
137
- },
138
- on(event: string, listener: EventListener): () => void {
139
- const element = root.value
140
- element?.addEventListener(event, listener)
141
- const off = (): void => element?.removeEventListener(event, listener)
142
- stops.push(off)
143
- return off
144
- },
145
- dispatch,
146
- }
147
-
148
- let cleanup: void | (() => void)
149
- let ready: Promise<void> | undefined
150
- onMounted(() => {
151
- ready = Promise.resolve(controllerModule.default(host as never)).then((result) => {
152
- cleanup = result
153
- })
53
+ modeled: ['open'],
154
54
  })
155
- onBeforeUnmount(() => {
156
- for (const stop of stops.splice(0)) stop()
157
- if (typeof cleanup === 'function') cleanup()
55
+
56
+ useComponentHost(controllerModule.default, {
57
+ root,
58
+ dispatch,
59
+ props,
60
+ refs: { dialog: dialogElement },
61
+ state: { internalOpen },
158
62
  })
159
63
  </script>
160
64
 
package/vue/UiSelect.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  type __VLS_Props = {
2
2
  disabled?: boolean;
3
3
  invalid?: boolean;
4
- multiple?: boolean;
5
4
  required?: boolean;
6
5
  value?: string;
7
6
  modelValue?: string | null;
@@ -18,7 +17,6 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
18
17
  invalid: boolean;
19
18
  disabled: boolean;
20
19
  required: boolean;
21
- multiple: boolean;
22
20
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
23
21
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
24
22
  declare const _default: typeof __VLS_export;
package/vue/UiSelect.vue CHANGED
@@ -1,7 +1,8 @@
1
- <!-- Generated by HTML Next 1.0.0-alpha.2 for Vue 3.5. Do not edit. -->
1
+ <!-- Generated by HTML Next 1.0.0-alpha.3 for Vue 3.5. Do not edit. -->
2
2
  <script setup lang="ts">
3
- import { computed, onBeforeUnmount, onMounted, ref, shallowRef, watchEffect } from 'vue'
3
+ import { computed, ref } from 'vue'
4
4
  import * as controllerModule from '../components/ui-select/ui-select.js'
5
+ import { createDispatch, useComponentHost } from './host'
5
6
 
6
7
  defineOptions({ inheritAttrs: false })
7
8
 
@@ -9,7 +10,6 @@ const props = withDefaults(
9
10
  defineProps<{
10
11
  disabled?: boolean
11
12
  invalid?: boolean
12
- multiple?: boolean
13
13
  required?: boolean
14
14
  value?: string
15
15
  modelValue?: string | null
@@ -17,7 +17,6 @@ const props = withDefaults(
17
17
  {
18
18
  disabled: false,
19
19
  invalid: false,
20
- multiple: false,
21
20
  required: false,
22
21
  },
23
22
  )
@@ -31,85 +30,12 @@ const model = computed({
31
30
 
32
31
  const root = ref<HTMLElement | null>(null)
33
32
 
34
- /** Dispatches a component event to Vue listeners and, for controllers and page code, on the root. */
35
- function dispatch(name: string, detail?: unknown): boolean {
36
- return root.value?.dispatchEvent(new CustomEvent(name, { bubbles: true, composed: true, detail }))
37
- ?? true
38
- }
39
-
40
- function read(name: string): unknown {
41
- return (props as Record<string, unknown>)[name]
42
- }
33
+ const dispatch = createDispatch(root)
43
34
 
44
- function write(name: string, value: unknown): boolean {
45
- throw new TypeError(`Only declared state is writable; \`${name}\` is not.`)
46
- }
47
-
48
- const stops: Array<() => void> = []
49
- const host = {
50
- get element(): Element {
51
- return root.value as Element
52
- },
53
- state: new Proxy({} as Record<string, unknown>, {
54
- get: (_target, name) => typeof name === 'string' ? read(name) : undefined,
55
- set: (_target, name, value) => typeof name === 'string' && write(name, value),
56
- }),
57
- refs: {} as Readonly<Record<string, Element>>,
58
- elements: new Proxy({} as Record<string, Element | RadioNodeList | undefined>, {
59
- get: (_target, name) => {
60
- if (typeof name !== 'string' || root.value === null) return undefined
61
- const form = root.value instanceof HTMLFormElement
62
- ? root.value
63
- : root.value.querySelector('form')
64
- return form?.elements.namedItem(name)
65
- ?? root.value.querySelector(`[name="${CSS.escape(name)}"]`) ?? undefined
66
- },
67
- }),
68
- signal<T>(initialValue: T) {
69
- const value = shallowRef(initialValue)
70
- return {
71
- get: (): T => value.value,
72
- set: (next: T): void => {
73
- if (!Object.is(value.value, next)) value.value = next
74
- },
75
- update: (next: (current: T) => T): void => {
76
- const updated = next(value.value)
77
- if (!Object.is(value.value, updated)) value.value = updated
78
- },
79
- }
80
- },
81
- computed<T>(compute: () => T) {
82
- const value = computed(compute)
83
- return { get: (): T => value.value }
84
- },
85
- effect(run: () => void | (() => void)): () => void {
86
- const stop = watchEffect((onCleanup) => {
87
- const cleanup = run()
88
- if (typeof cleanup === 'function') onCleanup(cleanup)
89
- }, { flush: 'post' })
90
- stops.push(stop)
91
- return stop
92
- },
93
- on(event: string, listener: EventListener): () => void {
94
- const element = root.value
95
- element?.addEventListener(event, listener)
96
- const off = (): void => element?.removeEventListener(event, listener)
97
- stops.push(off)
98
- return off
99
- },
35
+ useComponentHost(controllerModule.default, {
36
+ root,
100
37
  dispatch,
101
- }
102
-
103
- let cleanup: void | (() => void)
104
- let ready: Promise<void> | undefined
105
- onMounted(() => {
106
- ready = Promise.resolve(controllerModule.default(host as never)).then((result) => {
107
- cleanup = result
108
- })
109
- })
110
- onBeforeUnmount(() => {
111
- for (const stop of stops.splice(0)) stop()
112
- if (typeof cleanup === 'function') cleanup()
38
+ props,
113
39
  })
114
40
  </script>
115
41
 
@@ -119,7 +45,6 @@ onBeforeUnmount(() => {
119
45
  v-bind="$attrs"
120
46
  :disabled="disabled"
121
47
  :required="required"
122
- :multiple="multiple"
123
48
  :aria-invalid="invalid"
124
49
  v-model="model"
125
50
  ref="root"
@@ -151,9 +76,9 @@ onBeforeUnmount(() => {
151
76
  background-color var(--ui-motion-fast) var(--ui-motion-ease);
152
77
  }
153
78
 
154
- /* Single selects draw their own chevron: the native caret cannot be inset or sized consistently.
79
+ /* The select draws its own chevron: the native caret cannot be inset or sized consistently.
155
80
  * The root stays a native <s-lect>, so the icon is a background image rather than an element. */
156
- [data-component~="ui-select"]:not([multiple]) {
81
+ [data-component~="ui-select"] {
157
82
  appearance: none;
158
83
  padding-inline-end: calc(var(--ui-space-3) + 1.5rem);
159
84
  background-image: var(
@@ -198,7 +123,6 @@ onBeforeUnmount(() => {
198
123
  cursor: not-allowed;
199
124
  }
200
125
 
201
- [data-component~="ui-select"][multiple],
202
126
  [data-component~="ui-select"][size]:not([size="1"]) {
203
127
  min-block-size: 7rem;
204
128
  padding: var(--ui-space-2);
@@ -1,4 +1,4 @@
1
- <!-- Generated by HTML Next 1.0.0-alpha.2 for Vue 3.5. Do not edit. -->
1
+ <!-- Generated by HTML Next 1.0.0-alpha.3 for Vue 3.5. Do not edit. -->
2
2
  <script setup lang="ts">
3
3
  defineOptions({ inheritAttrs: false })
4
4