@threadlabs/looma 0.37.0 → 0.37.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/components/shared/icons.js +3 -0
  2. package/components/ui-button/ui-button.html +3 -12
  3. package/components/ui-card/ui-card.html +5 -5
  4. package/components/ui-checkbox/ui-checkbox.html +1 -7
  5. package/components/ui-color-picker/ui-color-picker.html +0 -9
  6. package/components/ui-combobox/ui-combobox.html +2 -14
  7. package/components/ui-cycle-button/ui-cycle-button.html +41 -0
  8. package/components/ui-cycle-button/ui-cycle-button.js +19 -0
  9. package/components/ui-dialog/ui-dialog.html +1 -1
  10. package/components/ui-icon/ui-icon.html +3 -0
  11. package/components/ui-icon-button/ui-icon-button.html +10 -14
  12. package/components/ui-input/ui-input.html +4 -19
  13. package/components/ui-input-group/ui-input-group.html +0 -10
  14. package/components/ui-menu/ui-menu.html +1 -1
  15. package/components/ui-popover/ui-popover.html +1 -1
  16. package/components/ui-radio/ui-radio.html +1 -7
  17. package/components/ui-select/ui-select.html +4 -10
  18. package/components/ui-switch/ui-switch.html +1 -7
  19. package/components/ui-tooltip/ui-tooltip.html +1 -1
  20. package/dist/index.js +47 -46
  21. package/docs/README.md +48 -7
  22. package/docs/card.html +2 -1
  23. package/docs/catalog.json +224 -49
  24. package/docs/customizer-render.mjs +7 -6
  25. package/docs/customizer.js +13 -19
  26. package/docs/theme.mjs +3 -3
  27. package/editor/icons.d.ts +3 -0
  28. package/editor/index.js +4 -1
  29. package/package.json +2 -2
  30. package/styles/ui-button.css +2 -14
  31. package/styles/ui-card.css +1 -1
  32. package/styles/ui-checkbox.css +0 -9
  33. package/styles/ui-color-picker.css +0 -13
  34. package/styles/ui-combobox.css +2 -28
  35. package/styles/ui-cycle-button.css +36 -0
  36. package/styles/ui-dialog.css +2 -2
  37. package/styles/ui-icon-button.css +6 -13
  38. package/styles/ui-input-group.css +0 -13
  39. package/styles/ui-input.css +2 -18
  40. package/styles/ui-menu.css +1 -1
  41. package/styles/ui-popover.css +1 -1
  42. package/styles/ui-radio.css +0 -9
  43. package/styles/ui-select.css +3 -10
  44. package/styles/ui-switch.css +0 -9
  45. package/styles/ui-tooltip.css +1 -1
  46. package/theme-system.css +14 -14
  47. package/tokens.css +2 -10
  48. package/vanilla/UiCycleButton.d.ts +29 -0
  49. package/vanilla/UiCycleButton.js +85 -0
  50. package/vanilla/UiIcon.js +1 -1
  51. package/vanilla/index.js +1 -0
  52. package/vue/UiButton.vue +2 -11
  53. package/vue/UiCard.vue +5 -3
  54. package/vue/UiCheckbox.vue +1 -7
  55. package/vue/UiColorPicker.vue +0 -9
  56. package/vue/UiCombobox.vue +3 -27
  57. package/vue/UiCycleButton.d.ts +69 -0
  58. package/vue/UiCycleButton.js +2 -0
  59. package/vue/UiCycleButton.vue +403 -0
  60. package/vue/UiDialog.vue +6 -2
  61. package/vue/UiIcon.vue +22 -0
  62. package/vue/UiIconButton.vue +2 -16
  63. package/vue/UiInput.vue +2 -23
  64. package/vue/UiInputGroup.vue +0 -10
  65. package/vue/UiMenu.vue +3 -1
  66. package/vue/UiPopover.vue +3 -1
  67. package/vue/UiRadio.vue +1 -7
  68. package/vue/UiSelect.vue +3 -9
  69. package/vue/UiSwitch.vue +1 -7
  70. package/vue/UiTooltip.vue +3 -1
  71. package/vue/chunks/UiAffordanceScope.js +1 -1
  72. package/vue/chunks/UiAvatar.js +1 -1
  73. package/vue/chunks/UiAvatarGroup.js +1 -1
  74. package/vue/chunks/UiBadge.js +1 -1
  75. package/vue/chunks/UiBreadcrumbItem.js +1 -1
  76. package/vue/chunks/UiBreadcrumbs.js +1 -1
  77. package/vue/chunks/UiButton.js +3 -3
  78. package/vue/chunks/UiCallout.js +1 -1
  79. package/vue/chunks/UiCard.js +4 -4
  80. package/vue/chunks/UiCheckbox.js +3 -3
  81. package/vue/chunks/UiCluster.js +1 -1
  82. package/vue/chunks/UiColorPicker.js +2 -2
  83. package/vue/chunks/UiCombobox.js +3 -3
  84. package/vue/chunks/UiContainer.js +1 -1
  85. package/vue/chunks/UiContextMenu.js +1 -1
  86. package/vue/chunks/UiCover.js +1 -1
  87. package/vue/chunks/UiCurveEditor.js +1 -1
  88. package/vue/chunks/UiCycleButton.js +352 -0
  89. package/vue/chunks/UiDescriptionItem.js +1 -1
  90. package/vue/chunks/UiDescriptionList.js +1 -1
  91. package/vue/chunks/UiDialog.js +2 -2
  92. package/vue/chunks/UiDisclosure.js +1 -1
  93. package/vue/chunks/UiEditable.js +1 -1
  94. package/vue/chunks/UiEditorBlockActions.js +1 -1
  95. package/vue/chunks/UiEditorChipEditor.js +1 -1
  96. package/vue/chunks/UiEditorCodeLanguage.js +1 -1
  97. package/vue/chunks/UiEditorFootnoteEditor.js +1 -1
  98. package/vue/chunks/UiEditorImageToolbar.js +1 -1
  99. package/vue/chunks/UiEditorInsertTableGrid.js +1 -1
  100. package/vue/chunks/UiEditorLinkCard.js +1 -1
  101. package/vue/chunks/UiEditorLinkForm.js +1 -1
  102. package/vue/chunks/UiEditorMentionMenu.js +1 -1
  103. package/vue/chunks/UiEditorMentionMenuItem.js +1 -1
  104. package/vue/chunks/UiEditorSlashMenu.js +1 -1
  105. package/vue/chunks/UiEditorSlashMenuGroup.js +1 -1
  106. package/vue/chunks/UiEditorSlashMenuItem.js +1 -1
  107. package/vue/chunks/UiEditorSurface.js +1 -1
  108. package/vue/chunks/UiEditorTableContextMenu.js +1 -1
  109. package/vue/chunks/UiEditorTableOverlay.js +1 -1
  110. package/vue/chunks/UiEditorTableToolbar.js +1 -1
  111. package/vue/chunks/UiEditorToolbar.js +1 -1
  112. package/vue/chunks/UiFloatingActionButton.js +1 -1
  113. package/vue/chunks/UiFormField.js +1 -1
  114. package/vue/chunks/UiGrid.js +1 -1
  115. package/vue/chunks/UiIcon.js +70 -2
  116. package/vue/chunks/UiIconButton.js +4 -4
  117. package/vue/chunks/UiImage.js +1 -1
  118. package/vue/chunks/UiInput.js +3 -3
  119. package/vue/chunks/UiInputGroup.js +2 -2
  120. package/vue/chunks/UiLightEditor.js +1 -1
  121. package/vue/chunks/UiList.js +1 -1
  122. package/vue/chunks/UiListItem.js +1 -1
  123. package/vue/chunks/UiListbox.js +1 -1
  124. package/vue/chunks/UiMenu.js +2 -2
  125. package/vue/chunks/UiMenuGroup.js +1 -1
  126. package/vue/chunks/UiMenuItem.js +1 -1
  127. package/vue/chunks/UiMeter.js +1 -1
  128. package/vue/chunks/UiNavItem.js +1 -1
  129. package/vue/chunks/UiNumberInput.js +1 -1
  130. package/vue/chunks/UiPageHeader.js +1 -1
  131. package/vue/chunks/UiPopover.js +2 -2
  132. package/vue/chunks/UiRadio.js +3 -3
  133. package/vue/chunks/UiRadioGroup.js +1 -1
  134. package/vue/chunks/UiReel.js +1 -1
  135. package/vue/chunks/UiScrollArea.js +1 -1
  136. package/vue/chunks/UiSearchResultRow.js +1 -1
  137. package/vue/chunks/UiSearchShell.js +1 -1
  138. package/vue/chunks/UiSection.js +1 -1
  139. package/vue/chunks/UiSelect.js +3 -3
  140. package/vue/chunks/UiSeparator.js +1 -1
  141. package/vue/chunks/UiSidebar.js +1 -1
  142. package/vue/chunks/UiSlider.js +1 -1
  143. package/vue/chunks/UiSpinner.js +1 -1
  144. package/vue/chunks/UiStack.js +1 -1
  145. package/vue/chunks/UiStatusMessage.js +1 -1
  146. package/vue/chunks/UiSwatch.js +1 -1
  147. package/vue/chunks/UiSwitch.js +3 -3
  148. package/vue/chunks/UiSwitcher.js +1 -1
  149. package/vue/chunks/UiTable.js +1 -1
  150. package/vue/chunks/UiTabs.js +1 -1
  151. package/vue/chunks/UiText.js +1 -1
  152. package/vue/chunks/UiTextarea.js +1 -1
  153. package/vue/chunks/UiToast.js +1 -1
  154. package/vue/chunks/UiToastRegion.js +1 -1
  155. package/vue/chunks/UiTooltip.js +2 -2
  156. package/vue/chunks/UiTree.js +1 -1
  157. package/vue/chunks/UiTreeItem.js +1 -1
  158. package/vue/chunks/props.js +12 -1
  159. package/vue/components.css +74 -179
  160. package/vue/editor/index.js +68 -0
  161. package/vue/index.d.ts +1 -0
  162. package/vue/index.js +2 -1
@@ -0,0 +1,69 @@
1
+ import type { PropType } from 'vue';
2
+ import { type InputBindings } from './props.js';
3
+ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
4
+ disabled: {
5
+ type: PropType<boolean | null>;
6
+ };
7
+ index: {
8
+ type: PropType<number | null>;
9
+ };
10
+ items: {
11
+ type: PropType<readonly ({
12
+ readonly value: string;
13
+ readonly label: string;
14
+ readonly icon: string;
15
+ })[]>;
16
+ };
17
+ label: {
18
+ type: PropType<string | null>;
19
+ };
20
+ size: {
21
+ type: PropType<"sm" | "md" | "lg" | null>;
22
+ };
23
+ variant: {
24
+ type: PropType<"ghost" | "outline" | "solid" | null>;
25
+ };
26
+ "\0htmlNextInputStatus": {
27
+ type: PropType<InputBindings>;
28
+ };
29
+ }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
30
+ change: (event: CustomEvent<{
31
+ readonly value: string;
32
+ readonly index: number;
33
+ }>) => any;
34
+ "update:index": (value: number) => any;
35
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
36
+ disabled: {
37
+ type: PropType<boolean | null>;
38
+ };
39
+ index: {
40
+ type: PropType<number | null>;
41
+ };
42
+ items: {
43
+ type: PropType<readonly ({
44
+ readonly value: string;
45
+ readonly label: string;
46
+ readonly icon: string;
47
+ })[]>;
48
+ };
49
+ label: {
50
+ type: PropType<string | null>;
51
+ };
52
+ size: {
53
+ type: PropType<"sm" | "md" | "lg" | null>;
54
+ };
55
+ variant: {
56
+ type: PropType<"ghost" | "outline" | "solid" | null>;
57
+ };
58
+ "\0htmlNextInputStatus": {
59
+ type: PropType<InputBindings>;
60
+ };
61
+ }>> & Readonly<{
62
+ onChange?: ((event: CustomEvent<{
63
+ readonly value: string;
64
+ readonly index: number;
65
+ }>) => any) | undefined;
66
+ "onUpdate:index"?: ((value: number) => any) | undefined;
67
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
68
+ declare const _default: typeof __VLS_export;
69
+ export default _default;
@@ -0,0 +1,2 @@
1
+ import { t as UiCycleButton_default } from "./chunks/UiCycleButton.js";
2
+ export { UiCycleButton_default as default };
@@ -0,0 +1,403 @@
1
+ <!-- Generated by HTML Next 1.0.0-alpha.69 for Vue 3.5. Do not edit. -->
2
+ <script setup lang="ts">
3
+ import { computed, getCurrentInstance, provide, ref, watchSyncEffect } from 'vue'
4
+ import type { PropType } from 'vue'
5
+ import {
6
+ checkHydratedRoot,
7
+ concat,
8
+ createDispatch,
9
+ cycleCheckedComputed,
10
+ initializedState,
11
+ nativeAttrs,
12
+ useComponentHost,
13
+ useReflectedProp,
14
+ warnUnless,
15
+ } from './host'
16
+ import { acceptsControllerWrite } from './props'
17
+ import {
18
+ BINDING_INPUTS_PROP,
19
+ checkedProp,
20
+ checkPropInput,
21
+ copyStateSeed,
22
+ evaluateInput,
23
+ type InputBindings,
24
+ inputComputed,
25
+ inputDefault,
26
+ inputRead,
27
+ propValidityState,
28
+ READY_INPUT,
29
+ requireInputStatus,
30
+ UNRESOLVED_INPUT,
31
+ vPropValidity,
32
+ vuePropInputs,
33
+ } from './props'
34
+ import UiIcon from './UiIcon.vue'
35
+ import UiIconButton from './UiIconButton.vue'
36
+
37
+ defineOptions({ inheritAttrs: false })
38
+
39
+ const propValidityContract = {
40
+ props: {
41
+ disabled: {
42
+ type: { kind: 'terminal', name: 'boolean' },
43
+ required: false,
44
+ target: { attribute: 'disabled' },
45
+ description: 'Prevents cycling while keeping the selected state visible.',
46
+ default: false,
47
+ },
48
+ index: {
49
+ type: { kind: 'terminal', name: 'number' },
50
+ required: false,
51
+ target: { attribute: 'index' },
52
+ description:
53
+ 'Initial or externally selected zero-based state. The first state is the default.',
54
+ default: 0,
55
+ },
56
+ items: {
57
+ type: {
58
+ kind: 'list',
59
+ item: {
60
+ kind: 'object',
61
+ fields: [{ name: 'value', type: { kind: 'terminal', name: 'string' }, optional: false }, {
62
+ name: 'label',
63
+ type: { kind: 'terminal', name: 'string' },
64
+ optional: false,
65
+ }, { name: 'icon', type: { kind: 'terminal', name: 'string' }, optional: false }],
66
+ open: false,
67
+ },
68
+ },
69
+ required: true,
70
+ target: { attribute: 'items' },
71
+ description:
72
+ 'Ordered states with a value, readable label and Looma icon name. Activation advances to the next state and wraps.',
73
+ },
74
+ label: {
75
+ type: { kind: 'terminal', name: 'string' },
76
+ required: false,
77
+ target: { attribute: 'label' },
78
+ description:
79
+ 'What the button changes, such as Appearance. The selected state is included in its accessible name.',
80
+ default: 'State',
81
+ },
82
+ size: {
83
+ type: { kind: 'terminal', name: 'keyword' },
84
+ required: false,
85
+ target: { attribute: 'size' },
86
+ description: 'Uses IconButton\'s compact visible sizes and adjustable invisible touch area.',
87
+ values: ['sm', 'md', 'lg'],
88
+ default: 'md',
89
+ },
90
+ variant: {
91
+ type: { kind: 'terminal', name: 'keyword' },
92
+ required: false,
93
+ target: { attribute: 'variant' },
94
+ description: 'Uses IconButton\'s standard emphasis.',
95
+ values: ['ghost', 'outline', 'solid'],
96
+ default: 'outline',
97
+ },
98
+ },
99
+ } as const
100
+ const props = defineProps({
101
+ disabled: { type: null as unknown as PropType<boolean | null> },
102
+ index: { type: null as unknown as PropType<number | null> },
103
+ items: {
104
+ type: null as unknown as PropType<
105
+ readonly ({ readonly value: string; readonly label: string; readonly icon: string })[]
106
+ >,
107
+ },
108
+ label: { type: null as unknown as PropType<string | null> },
109
+ size: { type: null as unknown as PropType<'sm' | 'md' | 'lg' | null> },
110
+ variant: { type: null as unknown as PropType<'ghost' | 'outline' | 'solid' | null> },
111
+ [BINDING_INPUTS_PROP]: { type: null as unknown as PropType<InputBindings> },
112
+ })
113
+ const acceptedProps: Record<string, unknown> = {
114
+ disabled: false,
115
+ index: 0,
116
+ items: null,
117
+ label: 'State',
118
+ size: 'md',
119
+ variant: 'outline',
120
+ }
121
+ const inputTracking = vuePropInputs(acceptedProps, {
122
+ disabled: false,
123
+ index: 0,
124
+ label: 'State',
125
+ size: 'md',
126
+ variant: 'outline',
127
+ }, (name, value, source, values) => {
128
+ switch (name) {
129
+ case 'disabled':
130
+ return checkPropInput(value, { kind: 'terminal', name: 'boolean' }, true, true)
131
+ case 'index':
132
+ return checkPropInput(value, { kind: 'terminal', name: 'number' }, true, true)
133
+ case 'items':
134
+ return checkPropInput(
135
+ value,
136
+ {
137
+ kind: 'list',
138
+ item: {
139
+ kind: 'object',
140
+ fields: [
141
+ { name: 'value', type: { kind: 'terminal', name: 'string' }, optional: false },
142
+ { name: 'label', type: { kind: 'terminal', name: 'string' }, optional: false },
143
+ { name: 'icon', type: { kind: 'terminal', name: 'string' }, optional: false },
144
+ ],
145
+ open: false,
146
+ },
147
+ },
148
+ true,
149
+ false,
150
+ )
151
+ case 'label':
152
+ return checkPropInput(value, { kind: 'terminal', name: 'string' }, true, true)
153
+ case 'size':
154
+ return checkPropInput(value, { kind: 'terminal', name: 'keyword' }, true, true)
155
+ case 'variant':
156
+ return checkPropInput(value, { kind: 'terminal', name: 'keyword' }, true, true)
157
+ default:
158
+ return { ok: false }
159
+ }
160
+ }, () => {
161
+ void checkedProps.value
162
+ })
163
+ const propInputs = inputTracking.inputs
164
+ const checkedProps = computed(() => {
165
+ void inputTracking.version.value
166
+ return ({
167
+ disabled: propInputs.value<boolean | null>(
168
+ 'disabled',
169
+ props['disabled'],
170
+ props[BINDING_INPUTS_PROP],
171
+ ) as boolean,
172
+ index: propInputs.value<number | null>(
173
+ 'index',
174
+ props['index'],
175
+ props[BINDING_INPUTS_PROP],
176
+ ) as number,
177
+ items: propInputs.value<
178
+ readonly ({ readonly value: string; readonly label: string; readonly icon: string })[]
179
+ >('items', props['items'], props[BINDING_INPUTS_PROP]),
180
+ label: propInputs.value<string | null>(
181
+ 'label',
182
+ props['label'],
183
+ props[BINDING_INPUTS_PROP],
184
+ ) as string,
185
+ size: propInputs.value<'sm' | 'md' | 'lg' | null>(
186
+ 'size',
187
+ props['size'],
188
+ props[BINDING_INPUTS_PROP],
189
+ ) as 'sm' | 'md' | 'lg',
190
+ variant: propInputs.value<'ghost' | 'outline' | 'solid' | null>(
191
+ 'variant',
192
+ props['variant'],
193
+ props[BINDING_INPUTS_PROP],
194
+ ) as 'ghost' | 'outline' | 'solid',
195
+ })
196
+ })
197
+ const inputStatus = (name: string, handlesError: boolean) => {
198
+ void checkedProps.value
199
+ void inputTracking.version.value
200
+ return requireInputStatus(propInputs.status(name), handlesError)
201
+ }
202
+ void checkedProps.value
203
+ watchSyncEffect(() => {
204
+ void checkedProps.value
205
+ })
206
+ const reflectedProp = useReflectedProp()
207
+ const hydrationInstance = getCurrentInstance()
208
+ const emit = defineEmits<{
209
+ 'update:index': [value: number]
210
+ change: [event: CustomEvent<{ readonly value: string; readonly index: number }>]
211
+ }>()
212
+
213
+ const root = ref<HTMLElement | null>(null)
214
+ const current = initializedState<number>(
215
+ () =>
216
+ evaluateInput(() => {
217
+ return inputRead(checkedProps.value.index, propInputs.status('index'))
218
+ }),
219
+ (next: unknown) => (typeof next === 'number' && Number.isFinite(next)),
220
+ copyStateSeed,
221
+ () =>
222
+ warnUnless(false, 'initializer:current', 'State `current` does not satisfy its declared type.'),
223
+ )
224
+ let selectedPrevious: { value: string; label: string; icon: string } | null = null
225
+ const selected: ReturnType<
226
+ typeof inputComputed<{ value: string; label: string; icon: string } | null>
227
+ > = inputComputed<{ value: string; label: string; icon: string } | null>(
228
+ () => {
229
+ return inputDefault(() =>
230
+ ((objectValue: any, indexValue: any) =>
231
+ objectValue === Symbol.for('html-next.invalid-result')
232
+ || indexValue === Symbol.for('html-next.invalid-result')
233
+ ? Symbol.for('html-next.invalid-result')
234
+ : objectValue[indexValue])(
235
+ inputRead(checkedProps.value.items, propInputs.status('items')),
236
+ inputDefault(() => inputRead(current.value, declarationStatus('current')), () => 0),
237
+ ), () =>
238
+ inputRead(checkedProps.value.items, propInputs.status('items'))[0]) as any
239
+ },
240
+ (read) => {
241
+ const value = cycleCheckedComputed(read)
242
+ return { get: () => value.value }
243
+ },
244
+ next =>
245
+ (next as unknown) === Symbol.for('html-next.invalid-result')
246
+ ? selectedPrevious
247
+ : (selectedPrevious = next),
248
+ )
249
+ let guardedPrevious: any = null
250
+ const guarded = computed(() => {
251
+ const candidate: any = concat(
252
+ checkedProps.value.label,
253
+ ': ',
254
+ selected.get()?.label ?? 'No choices',
255
+ )
256
+ if (candidate === Symbol.for('html-next.invalid-result')) return guardedPrevious
257
+ return guardedPrevious = candidate
258
+ })
259
+ provide('html-next:ui-cycle-button:current', current)
260
+ const declarationStatuses = { current: () => current.status() } as Record<
261
+ string,
262
+ () => typeof READY_INPUT
263
+ >
264
+ const declarationStatus = (name: string) => declarationStatuses[name]!()
265
+ current.start()
266
+ checkHydratedRoot('span', 'ui-cycle-button')
267
+
268
+ const isChangeDetail = (
269
+ detail: unknown,
270
+ ): boolean => (detail !== null && typeof detail === 'object' && !Array.isArray(detail)
271
+ && typeof (detail as Record<string, unknown>)['value'] === 'string'
272
+ && (typeof (detail as Record<string, unknown>)['index'] === 'number'
273
+ && Number.isFinite((detail as Record<string, unknown>)['index']))
274
+ && Object.keys(detail as object).every((key) => ['value', 'index'].includes(key)))
275
+ const dispatch = createDispatch(root, emit as (name: string, detail: unknown) => void, {
276
+ checks: {
277
+ change: isChangeDetail,
278
+ },
279
+ modeled: ['index'],
280
+ })
281
+
282
+ useComponentHost(() => import('../components/ui-cycle-button/ui-cycle-button.js'), {
283
+ root,
284
+ module: true,
285
+ dispatch,
286
+ acceptsState: (name: string, keys: readonly string[], value: unknown) =>
287
+ acceptsControllerWrite(
288
+ value,
289
+ ({ current: { kind: 'terminal', name: 'number' } } as Record<string, any>)[name],
290
+ keys,
291
+ ),
292
+ controllerSource: {
293
+ specifier: '../components/ui-cycle-button/ui-cycle-button.js',
294
+ definition: './components/ui-cycle-button/ui-cycle-button.html',
295
+ },
296
+ props: checkedProps,
297
+ inputs: propInputs,
298
+ inputTracking,
299
+ propInputs: (name: string) => {
300
+ const incoming = hydrationInstance?.vnode.props
301
+ const kebab = name.replace(/[A-Z]/g, (letter) => '-' + letter.toLowerCase())
302
+ return incoming !== null && incoming !== undefined
303
+ ? (incoming[name] ?? incoming[kebab] ?? null)
304
+ : null
305
+ },
306
+ propNames: ['disabled', 'index', 'items', 'label', 'size', 'variant'],
307
+ propValidity: (name: string) =>
308
+ propValidityState({ contract: propValidityContract, values: { ...props } }, name),
309
+ state: { current },
310
+ computed: { selected },
311
+ availability: { current: () => declarationStatus('current'), selected: () => selected.status() },
312
+ })
313
+ </script>
314
+
315
+ <template>
316
+ <span
317
+ class="ui-cycle-button"
318
+ v-bind="nativeAttrs($attrs)"
319
+ data-hn
320
+ v-prop-validity="{ contract: propValidityContract, values: { ...props } }"
321
+ :data-ui-cycle-button-prop-disabled="reflectedProp('disabled', 'disabled', checkedProps.disabled, undefined, false)"
322
+ :data-ui-cycle-button-prop-index="reflectedProp('index', 'index', checkedProps.index, undefined, false)"
323
+ :data-ui-cycle-button-prop-items="reflectedProp('items', 'items', checkedProps.items, undefined, false)"
324
+ :data-ui-cycle-button-prop-label="reflectedProp('label', 'label', checkedProps.label, undefined, false)"
325
+ :data-ui-cycle-button-prop-size="reflectedProp('size', 'size', checkedProps.size, undefined, false)"
326
+ :data-ui-cycle-button-prop-variant="reflectedProp('variant', 'variant', checkedProps.variant, undefined, false)"
327
+ ref="root"
328
+ ><template
329
+ v-for="boundInput in [evaluateInput(
330
+ () =>
331
+ (concat(
332
+ inputRead(checkedProps.label, inputStatus('label', true)),
333
+ ': ',
334
+ inputDefault(() => inputRead(selected.get(), selected.status())?.label, () => 'No choices'),
335
+ )) as any,
336
+ READY_INPUT,
337
+ )]"
338
+ ><template
339
+ v-for="boundInputSnapshot in [evaluateInput(
340
+ () =>
341
+ (inputRead(checkedProps.disabled, inputStatus('disabled', true))
342
+ || inputRead(checkedProps.items, inputStatus('items', true)).length < 2) as any,
343
+ READY_INPUT,
344
+ )]"
345
+ ><UiIconButton
346
+ class="ui-icon-button"
347
+ tone="neutral"
348
+ aria-description="Activate to choose the next state."
349
+ :label="boundInput.value"
350
+ :title="guarded"
351
+ :size="checkedProps.size"
352
+ :variant="checkedProps.variant"
353
+ :disabled="boundInputSnapshot.value"
354
+ v-bind="{
355
+ [BINDING_INPUTS_PROP]: {
356
+ label: () => boundInput,
357
+ size: () => ({ value: checkedProps.size, status: inputStatus('size', true) }),
358
+ variant: () => ({ value: checkedProps.variant, status: inputStatus('variant', true) }),
359
+ disabled: () => boundInputSnapshot,
360
+ },
361
+ }"
362
+ ><template
363
+ v-for="boundInputSnapshot2 in [evaluateInput(
364
+ () => (inputDefault(() => inputRead(selected.get(), selected.status())?.icon, () => '')) as any,
365
+ READY_INPUT,
366
+ )]"
367
+ ><UiIcon
368
+ class="ui-icon"
369
+ :name="boundInputSnapshot2.value"
370
+ v-bind="{ [BINDING_INPUTS_PROP]: { name: () => boundInputSnapshot2 } }"
371
+ /></template></UiIconButton></template></template></span>
372
+ </template>
373
+
374
+ <style scoped>
375
+ @scope (.ui-cycle-button) to ([data-hn]) {
376
+ :global(:scope) {
377
+ display: inline-flex !important;
378
+ align-items: center !important;
379
+ justify-content: center !important;
380
+ gap: 0 !important;
381
+ margin: 0 !important;
382
+ padding: 0 !important;
383
+ vertical-align: middle;
384
+ }
385
+ :global(:scope[hidden]) {
386
+ display: none !important;
387
+ }
388
+ }
389
+ @scope ([data-slotted~="ui-cycle-button"]:not([data-hn])) to ([data-hn], [data-slotted]) {
390
+ :global(:where(.ui-cycle-button):where(:scope, :not([data-hn], [data-slotted]:not([data-slotted~="ui-cycle-button"])))) {
391
+ display: inline-flex !important;
392
+ align-items: center !important;
393
+ justify-content: center !important;
394
+ gap: 0 !important;
395
+ margin: 0 !important;
396
+ padding: 0 !important;
397
+ vertical-align: middle;
398
+ }
399
+ :global(:where(.ui-cycle-button)[hidden]:where(:scope, :not([data-hn], [data-slotted]:not([data-slotted~="ui-cycle-button"])))) {
400
+ display: none !important;
401
+ }
402
+ }
403
+ </style>
package/vue/UiDialog.vue CHANGED
@@ -428,7 +428,9 @@ useComponentHost(() => import('../components/ui-dialog/ui-dialog.js'), {
428
428
  overlay var(--ui-dialog-duration, var(--ui-motion-base)) var(--ui-motion-ease) allow-discrete;
429
429
  }
430
430
  :global(:scope:where([data-hn-ui-dialog])) {
431
- --_ui-material-glassiness: clamp(0, var(--ui-glassiness, 0), 1);
431
+ --_ui-material-glassiness: calc(
432
+ clamp(0, var(--ui-glassiness, 0), 1) + 2 * pow(clamp(0, var(--ui-glassiness, 0), 1), 4)
433
+ );
432
434
  --_ui-material-separation: clamp(0, var(--ui-surface-separation, 0), 1);
433
435
  --_ui-material-active: clamp(
434
436
  0,
@@ -707,7 +709,9 @@ useComponentHost(() => import('../components/ui-dialog/ui-dialog.js'), {
707
709
  overlay var(--ui-dialog-duration, var(--ui-motion-base)) var(--ui-motion-ease) allow-discrete;
708
710
  }
709
711
  :global(:where(.ui-dialog):where(:scope, :not([data-hn], [data-slotted]:not([data-slotted~="ui-dialog"]))):where([data-hn-ui-dialog])) {
710
- --_ui-material-glassiness: clamp(0, var(--ui-glassiness, 0), 1);
712
+ --_ui-material-glassiness: calc(
713
+ clamp(0, var(--ui-glassiness, 0), 1) + 2 * pow(clamp(0, var(--ui-glassiness, 0), 1), 4)
714
+ );
711
715
  --_ui-material-separation: clamp(0, var(--ui-surface-separation, 0), 1);
712
716
  --_ui-material-active: clamp(
713
717
  0,
package/vue/UiIcon.vue CHANGED
@@ -326,6 +326,17 @@ const shapes: ReturnType<typeof inputComputed<any>> = inputComputed<any>(
326
326
  { tag: 'path', d: 'M12 7v4' },
327
327
  ],
328
328
  minus: [{ tag: 'path', d: 'M5 12h14' }],
329
+ monitor: [{ tag: 'rect', width: '20', height: '14', x: '2', y: '3', rx: '2' }, {
330
+ tag: 'line',
331
+ x1: '8',
332
+ x2: '16',
333
+ y1: '21',
334
+ y2: '21',
335
+ }, { tag: 'line', x1: '12', x2: '12', y1: '17', y2: '21' }],
336
+ moon: [{
337
+ tag: 'path',
338
+ d: 'M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401',
339
+ }],
329
340
  'notebook-pen': [
330
341
  { tag: 'path', d: 'M13.4 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-7.4' },
331
342
  { tag: 'path', d: 'M2 6h4' },
@@ -413,6 +424,17 @@ const shapes: ReturnType<typeof inputComputed<any>> = inputComputed<any>(
413
424
  tag: 'path',
414
425
  d: 'M14 12a4 4 0 0 1 0 8H6',
415
426
  }, { tag: 'line', x1: '4', x2: '20', y1: '12', y2: '12' }],
427
+ sun: [
428
+ { tag: 'circle', cx: '12', cy: '12', r: '4' },
429
+ { tag: 'path', d: 'M12 2v2' },
430
+ { tag: 'path', d: 'M12 20v2' },
431
+ { tag: 'path', d: 'm4.93 4.93 1.41 1.41' },
432
+ { tag: 'path', d: 'm17.66 17.66 1.41 1.41' },
433
+ { tag: 'path', d: 'M2 12h2' },
434
+ { tag: 'path', d: 'M20 12h2' },
435
+ { tag: 'path', d: 'm6.34 17.66-1.41 1.41' },
436
+ { tag: 'path', d: 'm19.07 4.93-1.41 1.41' },
437
+ ],
416
438
  superscript: [{ tag: 'path', d: 'm4 19 8-8' }, { tag: 'path', d: 'm12 19-8-8' }, {
417
439
  tag: 'path',
418
440
  d: 'M20 12h-4c0-1.5.442-2 1.5-2.5S20 8.334 20 7.002c0-.472-.17-.93-.484-1.29a2.105 2.105 0 0 0-2.617-.436c-.42.239-.738.614-.899 1.06',
@@ -77,7 +77,7 @@ const propValidityContract = {
77
77
  required: false,
78
78
  target: { attribute: 'data-match-button' },
79
79
  description:
80
- 'Matches a boxed Button of the same size when the two share a flex row. Uses the control-size theme tokens and stretches to the row\'s height, including\n touch-sized controls. Leave it off for compact toolbar and inline icon buttons.',
80
+ 'Matches a boxed Button of the same size when the two share a flex row. Uses the control-size theme tokens and stretches to the row\'s height, on mouse and\n touch alike. Leave it off for compact toolbar and inline icon buttons.',
81
81
  default: false,
82
82
  },
83
83
  pending: {
@@ -116,7 +116,7 @@ const propValidityContract = {
116
116
  required: false,
117
117
  target: { attribute: 'size' },
118
118
  description:
119
- 'Button and icon size together: sm is 1.75rem with a small icon, md is 2rem, and lg is 2.5rem with a large icon. On touch, every size gets an invisible hit\n area of at least 44px.',
119
+ 'Button and icon size together: sm is 1.75rem with a small icon, md is 2rem, and lg is 2.5rem with a large icon. Touch can use an invisible hit area sized\n by the adjustable control target preference.',
120
120
  values: ['sm', 'md', 'lg'],
121
121
  default: 'md',
122
122
  },
@@ -841,20 +841,6 @@ useComponentHost(() => import('../components/ui-icon-button/ui-icon-button.js'),
841
841
  --_size: var(--ui-control-size-lg);
842
842
  }
843
843
 
844
- @media (pointer: coarse) {
845
- :global(:scope[data-ui-icon-button-state~="matchButton"]:where([data-hn-ui-icon-button])) {
846
- inline-size: max(var(--ui-icon-button-size, var(--_size)), var(--ui-control-min-block-size));
847
- min-inline-size: max(
848
- var(--ui-icon-button-size, var(--_size)),
849
- var(--ui-control-min-block-size)
850
- );
851
- min-block-size: max(
852
- var(--ui-icon-button-size, var(--_size)),
853
- var(--ui-control-min-block-size)
854
- );
855
- }
856
- }
857
-
858
844
  :global(:scope[data-ui-icon-button-state~="round"]:where([data-hn-ui-icon-button])) {
859
845
  border-radius: var(--ui-radius-round);
860
846
  }
package/vue/UiInput.vue CHANGED
@@ -81,7 +81,7 @@ const propValidityContract = {
81
81
  required: false,
82
82
  target: { attribute: 'size' },
83
83
  description:
84
- 'Height, padding, and type size, matching Button\'s sizes so a field and a button of one size share a row. sm is the small control height (2rem by default)\n with smaller text, for compact rows such as a table row or a filter bar; md is the standard control height; lg is taller with roomier padding. Under a\n coarse pointer, sm grows to the 44px touch minimum and keeps body-size text. Change it after render through this option; the element\'s size property is\n the native one.',
84
+ 'Height, padding, and type size, matching Button\'s sizes so a field and a button of one size share a row. sm is the small control height (2rem by default)\n with smaller text, for compact rows such as a table row or a filter bar; md is the standard control height; lg is taller with roomier padding. Touch input\n keeps body-size field text without inflating the visible size ladder. Change it after render through this option; the element\'s size property is the\n native one.',
85
85
  values: ['sm', 'md', 'lg'],
86
86
  default: 'md',
87
87
  },
@@ -540,8 +540,7 @@ useComponentHost(() => import('../components/ui-input/ui-input.js'), {
540
540
 
541
541
  /* Each size reads the hook and then an Input Group's default first, so a group, which draws the frame, can zero it. */
542
542
 
543
- /* Under touch a small field is still a touch-sized target, as a small button is, and keeps
544
- body-size text: below 16px, iOS zooms the page into the focused field. */
543
+ /* Touch may use body-size text in a small native field to avoid focus zoom; this does not impose a larger visible control height. */
545
544
 
546
545
  /* Tabbing into a field selects its text; tint that selection softly instead of the page highlight. */
547
546
 
@@ -613,31 +612,11 @@ useComponentHost(() => import('../components/ui-input/ui-input.js'), {
613
612
  font-size: var(--ui-font-size-md);
614
613
  }
615
614
  @media (pointer: coarse) {
616
- :global(:scope) {
617
- min-block-size: var(
618
- --ui-input-min-block-size,
619
- var(
620
- --_ui-default-input-min-block-size,
621
- max(var(--ui-control-size-md), var(--ui-control-min-block-size))
622
- )
623
- );
624
- }
625
-
626
615
  :global(:scope),
627
616
  :global(:scope[data-ui-input-state~="size=sm"]),
628
617
  :global(:scope[data-ui-input-state~="size=lg"]) {
629
618
  font-size: var(--ui-font-size-md);
630
619
  }
631
-
632
- :global(:scope[data-ui-input-state~="size=sm"]) {
633
- min-block-size: var(
634
- --ui-input-min-block-size,
635
- var(
636
- --_ui-default-input-min-block-size,
637
- max(var(--ui-control-size-sm), var(--ui-control-min-block-size))
638
- )
639
- );
640
- }
641
620
  }
642
621
  :global(:scope::selection) {
643
622
  background: color-mix(in srgb, var(--ui-accent-solid) 20%, transparent);
@@ -326,16 +326,6 @@ useComponentHost(() => import('../components/ui-input-group/ui-input-group.js'),
326
326
  min-block-size: var(--ui-control-size-lg);
327
327
  }
328
328
 
329
- @media (pointer: coarse) {
330
- :global(:scope:where([data-hn-ui-input-group])) {
331
- min-block-size: max(var(--ui-control-size-md), var(--ui-control-min-block-size));
332
- }
333
-
334
- :global(:scope:has(.field [data-ui-input-state~="size=sm"]):where([data-hn-ui-input-group])) {
335
- min-block-size: var(--ui-control-min-block-size);
336
- }
337
- }
338
-
339
329
  :global(:scope:hover:not(:has(:is(input, textarea):focus, :is(input, textarea):disabled, :is(input, textarea):read-only, [aria-invalid="true"], :is(:user-invalid, [data-user-invalid]))):where([data-hn-ui-input-group])) {
340
330
  border-color: var(--ui-control-border-hover, var(--ui-text-secondary));
341
331
  }
package/vue/UiMenu.vue CHANGED
@@ -427,7 +427,9 @@ useComponentHost(() => import('../components/ui-menu/ui-menu.js'), {
427
427
  overflow: auto;
428
428
  }
429
429
  :global(:scope:not([inline]) .surface:where([data-hn-ui-menu])) {
430
- --_ui-material-glassiness: clamp(0, var(--ui-glassiness, 0), 1);
430
+ --_ui-material-glassiness: calc(
431
+ clamp(0, var(--ui-glassiness, 0), 1) + 2 * pow(clamp(0, var(--ui-glassiness, 0), 1), 4)
432
+ );
431
433
  --_ui-material-separation: clamp(0, var(--ui-surface-separation, 0), 1);
432
434
  --_ui-material-active: clamp(
433
435
  0,
package/vue/UiPopover.vue CHANGED
@@ -378,7 +378,9 @@ useComponentHost(() => import('../components/ui-popover/ui-popover.js'), {
378
378
  );
379
379
  }
380
380
  :global(.surface:where([data-hn-ui-popover])) {
381
- --_ui-material-glassiness: clamp(0, var(--ui-glassiness, 0), 1);
381
+ --_ui-material-glassiness: calc(
382
+ clamp(0, var(--ui-glassiness, 0), 1) + 2 * pow(clamp(0, var(--ui-glassiness, 0), 1), 4)
383
+ );
382
384
  --_ui-material-separation: clamp(0, var(--ui-surface-separation, 0), 1);
383
385
  --_ui-material-active: clamp(
384
386
  0,