@threadlabs/looma 0.17.0 → 0.17.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 (229) hide show
  1. package/components/ui-avatar-group/ui-avatar-group.html +8 -5
  2. package/components/ui-avatar-group/ui-avatar-group.js +3 -3
  3. package/components/ui-badge/ui-badge.html +10 -5
  4. package/components/ui-breadcrumb-item/ui-breadcrumb-item.html +2 -1
  5. package/components/ui-button/ui-button.html +33 -26
  6. package/components/ui-button/ui-button.js +2 -2
  7. package/components/ui-callout/ui-callout.html +3 -1
  8. package/components/ui-card/ui-card.html +5 -3
  9. package/components/ui-checkbox/ui-checkbox.html +36 -8
  10. package/components/ui-combobox/ui-combobox.html +72 -45
  11. package/components/ui-combobox/ui-combobox.js +7 -2
  12. package/components/ui-context-menu/ui-context-menu.html +2 -2
  13. package/components/ui-description-item/ui-description-item.html +1 -1
  14. package/components/ui-description-list/ui-description-list.html +2 -0
  15. package/components/ui-dialog/ui-dialog.html +3 -23
  16. package/components/ui-disclosure/ui-disclosure.html +2 -2
  17. package/components/ui-editor-insert-table-grid/ui-editor-insert-table-grid.html +2 -2
  18. package/components/ui-editor-mention-menu/ui-editor-mention-menu.html +3 -3
  19. package/components/ui-editor-slash-menu/ui-editor-slash-menu.html +4 -4
  20. package/components/ui-editor-slash-menu-item/ui-editor-slash-menu-item.html +6 -1
  21. package/components/ui-editor-table-context-menu/ui-editor-table-context-menu.html +2 -2
  22. package/components/ui-editor-table-toolbar/ui-editor-table-toolbar.html +4 -4
  23. package/components/ui-editor-toolbar/ui-editor-toolbar.html +5 -5
  24. package/components/ui-floating-action-button/ui-floating-action-button.html +4 -2
  25. package/components/ui-icon-button/ui-icon-button.html +57 -17
  26. package/components/ui-icon-button/ui-icon-button.js +1 -1
  27. package/components/ui-list/ui-list.html +46 -6
  28. package/components/ui-list-item/ui-list-item.html +1 -1
  29. package/components/ui-menu/ui-menu.html +4 -3
  30. package/components/ui-menu/ui-menu.js +9 -3
  31. package/components/ui-menu-item/ui-menu-item.html +43 -6
  32. package/components/ui-meter/ui-meter.html +4 -1
  33. package/components/ui-nav-item/ui-nav-item.html +0 -1
  34. package/components/ui-page-header/ui-page-header.html +1 -0
  35. package/components/ui-popover/ui-popover.html +1 -1
  36. package/components/ui-radio/ui-radio.html +5 -2
  37. package/components/ui-scroll-area/ui-scroll-area.html +9 -14
  38. package/components/ui-scroll-area/ui-scroll-area.js +32 -0
  39. package/components/ui-search-result-row/ui-search-result-row.html +5 -3
  40. package/components/ui-search-shell/ui-search-shell.html +8 -8
  41. package/components/ui-section/ui-section.html +5 -3
  42. package/components/ui-separator/ui-separator.html +2 -2
  43. package/components/ui-sidebar/ui-sidebar.html +2 -2
  44. package/components/ui-spinner/ui-spinner.html +28 -8
  45. package/components/ui-status-message/ui-status-message.html +1 -1
  46. package/components/ui-switch/ui-switch.html +17 -8
  47. package/components/ui-table/ui-table.html +1 -1
  48. package/components/ui-tabs/ui-tabs.html +21 -12
  49. package/components/ui-toast/ui-toast.html +1 -1
  50. package/components/ui-toast-region/ui-toast-region.html +1 -1
  51. package/components/ui-tooltip/ui-tooltip.html +12 -12
  52. package/components/ui-tooltip/ui-tooltip.js +1 -1
  53. package/components/ui-top-bar/ui-top-bar.html +1 -1
  54. package/components/ui-tree/ui-tree.js +4 -1
  55. package/components/ui-tree-item/ui-tree-item.html +2 -1
  56. package/dist/index.js +15 -14
  57. package/editor/chunks/extensions.js +12 -2
  58. package/editor/extensions/preset.d.ts +6 -1
  59. package/package.json +1 -1
  60. package/styles/ui-avatar-group.css +8 -5
  61. package/styles/ui-badge.css +10 -5
  62. package/styles/ui-breadcrumb-item.css +2 -1
  63. package/styles/ui-button.css +19 -25
  64. package/styles/ui-callout.css +3 -1
  65. package/styles/ui-card.css +4 -2
  66. package/styles/ui-checkbox.css +38 -8
  67. package/styles/ui-combobox.css +27 -8
  68. package/styles/ui-context-menu.css +2 -2
  69. package/styles/ui-description-item.css +1 -1
  70. package/styles/ui-description-list.css +2 -0
  71. package/styles/ui-dialog.css +1 -22
  72. package/styles/ui-disclosure.css +2 -2
  73. package/styles/ui-editor-insert-table-grid.css +2 -2
  74. package/styles/ui-editor-mention-menu.css +3 -3
  75. package/styles/ui-editor-slash-menu-item.css +6 -1
  76. package/styles/ui-editor-slash-menu.css +4 -4
  77. package/styles/ui-editor-table-context-menu.css +2 -2
  78. package/styles/ui-editor-table-toolbar.css +4 -4
  79. package/styles/ui-editor-toolbar.css +5 -5
  80. package/styles/ui-floating-action-button.css +3 -1
  81. package/styles/ui-icon-button.css +53 -18
  82. package/styles/ui-list-item.css +1 -1
  83. package/styles/ui-list.css +41 -6
  84. package/styles/ui-menu-item.css +43 -5
  85. package/styles/ui-menu.css +3 -3
  86. package/styles/ui-meter.css +4 -1
  87. package/styles/ui-nav-item.css +0 -1
  88. package/styles/ui-page-header.css +1 -0
  89. package/styles/ui-popover.css +1 -1
  90. package/styles/ui-radio.css +5 -2
  91. package/styles/ui-scroll-area.css +10 -16
  92. package/styles/ui-search-result-row.css +3 -2
  93. package/styles/ui-search-shell.css +7 -7
  94. package/styles/ui-section.css +5 -3
  95. package/styles/ui-separator.css +2 -2
  96. package/styles/ui-sidebar.css +2 -2
  97. package/styles/ui-spinner.css +27 -6
  98. package/styles/ui-status-message.css +1 -1
  99. package/styles/ui-switch.css +17 -8
  100. package/styles/ui-table.css +1 -1
  101. package/styles/ui-tabs.css +60 -51
  102. package/styles/ui-toast-region.css +1 -1
  103. package/styles/ui-toast.css +1 -1
  104. package/styles/ui-tooltip.css +12 -12
  105. package/styles/ui-top-bar.css +1 -1
  106. package/styles/ui-tree-item.css +2 -1
  107. package/theme-dark.css +4 -4
  108. package/theme-light.css +3 -3
  109. package/tokens.css +9 -6
  110. package/vanilla/UiButton.d.ts +1 -0
  111. package/vanilla/UiButton.js +17 -19
  112. package/vanilla/UiCombobox.js +162 -159
  113. package/vanilla/UiDialog.js +2 -2
  114. package/vanilla/UiIconButton.d.ts +1 -0
  115. package/vanilla/UiIconButton.js +1 -4
  116. package/vanilla/UiList.d.ts +3 -1
  117. package/vanilla/UiList.js +36 -19
  118. package/vanilla/UiMenu.d.ts +1 -0
  119. package/vanilla/UiMenu.js +1 -1
  120. package/vanilla/UiMenuItem.js +10 -13
  121. package/vanilla/UiScrollArea.js +11 -6
  122. package/vanilla/UiSearchShell.js +2 -2
  123. package/vanilla/UiSpinner.js +2 -4
  124. package/vue/UiAvatarGroup.vue +8 -5
  125. package/vue/UiBadge.vue +10 -5
  126. package/vue/UiBreadcrumbItem.vue +2 -1
  127. package/vue/UiButton.d.ts +2 -0
  128. package/vue/UiButton.vue +31 -28
  129. package/vue/UiCallout.vue +3 -2
  130. package/vue/UiCard.vue +4 -2
  131. package/vue/UiCheckbox.vue +36 -8
  132. package/vue/UiCombobox.d.ts +1 -1
  133. package/vue/UiCombobox.vue +55 -35
  134. package/vue/UiContextMenu.vue +2 -2
  135. package/vue/UiDescriptionItem.vue +1 -1
  136. package/vue/UiDescriptionList.vue +2 -0
  137. package/vue/UiDialog.d.ts +3 -3
  138. package/vue/UiDialog.vue +3 -27
  139. package/vue/UiDisclosure.vue +2 -2
  140. package/vue/UiEditorInsertTableGrid.vue +2 -2
  141. package/vue/UiEditorMentionMenu.d.ts +1 -1
  142. package/vue/UiEditorMentionMenu.vue +4 -3
  143. package/vue/UiEditorSlashMenu.vue +6 -4
  144. package/vue/UiEditorSlashMenuItem.vue +6 -1
  145. package/vue/UiEditorTableContextMenu.vue +2 -2
  146. package/vue/UiEditorTableToolbar.vue +4 -4
  147. package/vue/UiEditorToolbar.vue +5 -5
  148. package/vue/UiFloatingActionButton.vue +3 -1
  149. package/vue/UiIconButton.d.ts +2 -0
  150. package/vue/UiIconButton.vue +61 -19
  151. package/vue/UiList.d.ts +7 -1
  152. package/vue/UiList.vue +57 -6
  153. package/vue/UiListItem.vue +1 -1
  154. package/vue/UiMenu.d.ts +2 -0
  155. package/vue/UiMenu.vue +6 -3
  156. package/vue/UiMenuItem.d.ts +3 -3
  157. package/vue/UiMenuItem.vue +47 -7
  158. package/vue/UiMeter.vue +4 -1
  159. package/vue/UiNavItem.vue +0 -1
  160. package/vue/UiPageHeader.vue +1 -0
  161. package/vue/UiPopover.vue +1 -1
  162. package/vue/UiRadio.vue +5 -2
  163. package/vue/UiScrollArea.vue +24 -18
  164. package/vue/UiSearchResultRow.vue +3 -2
  165. package/vue/UiSearchShell.vue +8 -8
  166. package/vue/UiSection.vue +5 -3
  167. package/vue/UiSeparator.vue +2 -2
  168. package/vue/UiSidebar.vue +2 -2
  169. package/vue/UiSpinner.vue +28 -7
  170. package/vue/UiStatusMessage.vue +1 -1
  171. package/vue/UiSwitch.vue +20 -9
  172. package/vue/UiTable.vue +1 -1
  173. package/vue/UiTabs.vue +25 -12
  174. package/vue/UiToast.vue +1 -1
  175. package/vue/UiToastRegion.vue +1 -1
  176. package/vue/UiTooltip.vue +12 -12
  177. package/vue/UiTopBar.vue +1 -1
  178. package/vue/UiTreeItem.vue +2 -1
  179. package/vue/chunks/UiAvatarGroup.js +1 -1
  180. package/vue/chunks/UiBadge.js +1 -1
  181. package/vue/chunks/UiBreadcrumbItem.js +1 -1
  182. package/vue/chunks/UiButton.js +28 -23
  183. package/vue/chunks/UiCallout.js +1 -1
  184. package/vue/chunks/UiCard.js +1 -1
  185. package/vue/chunks/UiCheckbox.js +1 -1
  186. package/vue/chunks/UiCombobox.js +80 -77
  187. package/vue/chunks/UiContextMenu.js +1 -1
  188. package/vue/chunks/UiDescriptionItem.js +1 -1
  189. package/vue/chunks/UiDescriptionList.js +1 -1
  190. package/vue/chunks/UiDialog.js +7 -4
  191. package/vue/chunks/UiDisclosure.js +1 -1
  192. package/vue/chunks/UiEditorInsertTableGrid.js +1 -1
  193. package/vue/chunks/UiEditorMentionMenu.js +1 -1
  194. package/vue/chunks/UiEditorSlashMenu.js +1 -1
  195. package/vue/chunks/UiEditorSlashMenuItem.js +1 -1
  196. package/vue/chunks/UiEditorTableContextMenu.js +1 -1
  197. package/vue/chunks/UiEditorTableToolbar.js +1 -1
  198. package/vue/chunks/UiEditorToolbar.js +1 -1
  199. package/vue/chunks/UiFloatingActionButton.js +1 -1
  200. package/vue/chunks/UiIconButton.js +13 -8
  201. package/vue/chunks/UiList.js +17 -5
  202. package/vue/chunks/UiListItem.js +1 -1
  203. package/vue/chunks/UiMenu.js +10 -2
  204. package/vue/chunks/UiMenuItem.js +1 -4
  205. package/vue/chunks/UiMeter.js +1 -1
  206. package/vue/chunks/UiNavItem.js +1 -1
  207. package/vue/chunks/UiPageHeader.js +1 -1
  208. package/vue/chunks/UiPopover.js +1 -1
  209. package/vue/chunks/UiRadio.js +1 -1
  210. package/vue/chunks/UiScrollArea.js +22 -3
  211. package/vue/chunks/UiSearchResultRow.js +1 -1
  212. package/vue/chunks/UiSearchShell.js +2 -2
  213. package/vue/chunks/UiSection.js +1 -1
  214. package/vue/chunks/UiSeparator.js +1 -1
  215. package/vue/chunks/UiSidebar.js +1 -1
  216. package/vue/chunks/UiSpinner.js +7 -5
  217. package/vue/chunks/UiStatusMessage.js +1 -1
  218. package/vue/chunks/UiSwitch.js +1 -1
  219. package/vue/chunks/UiTable.js +1 -1
  220. package/vue/chunks/UiTabs.js +1 -1
  221. package/vue/chunks/UiToast.js +1 -1
  222. package/vue/chunks/UiToastRegion.js +1 -1
  223. package/vue/chunks/UiTooltip.js +1 -1
  224. package/vue/chunks/UiTopBar.js +1 -1
  225. package/vue/chunks/UiTreeItem.js +1 -1
  226. package/vue/components.css +1231 -1032
  227. package/vue/editor/code-block-view.d.ts +29 -0
  228. package/vue/editor/index.js +70 -4
  229. package/vue/index.js +1 -1
package/vue/UiButton.vue CHANGED
@@ -13,6 +13,7 @@ const props = withDefaults(
13
13
  as?: 'button' | 'a'
14
14
  disabled?: boolean
15
15
  href?: string
16
+ loading?: boolean
16
17
  pending?: boolean
17
18
  rel?: string
18
19
  size?: 'sm' | 'md' | 'lg'
@@ -26,6 +27,7 @@ const props = withDefaults(
26
27
  align: 'center',
27
28
  as: 'button',
28
29
  disabled: false,
30
+ loading: false,
29
31
  pending: false,
30
32
  size: 'md',
31
33
  stretch: false,
@@ -45,6 +47,7 @@ const hostState = computed(() =>
45
47
  props.size && `size size=${props.size}`,
46
48
  props.variant && `variant variant=${props.variant}`,
47
49
  props.tone && `tone tone=${props.tone}`,
50
+ props.loading && 'loading',
48
51
  props.pending && 'pending',
49
52
  ].filter(Boolean).join(' ')
50
53
  )
@@ -76,17 +79,17 @@ function attribute(value: unknown): any {
76
79
  v-if="as === 'a'"
77
80
  data-component="ui-button"
78
81
  v-bind="$attrs"
79
- :href="attribute(({ true: null, false: href })[`${disabled || pending}`])"
82
+ :href="attribute(({ true: null, false: href })[`${disabled || loading || pending}`])"
80
83
  :target="target"
81
84
  :rel="rel"
82
- :aria-disabled="typeof (({ true: 'true', false: null })[`${disabled || pending}`]) === 'boolean'
83
- ? String(({ true: 'true', false: null })[`${disabled || pending}`])
84
- : attribute(({ true: 'true', false: null })[`${disabled || pending}`])"
85
- :aria-busy="pending"
86
- :tabindex="attribute(({ true: '0', false: null })[`${pending && !disabled}`])"
85
+ :aria-disabled="typeof (({ true: 'true', false: null })[`${disabled || loading || pending}`]) === 'boolean'
86
+ ? String(({ true: 'true', false: null })[`${disabled || loading || pending}`])
87
+ : attribute(({ true: 'true', false: null })[`${disabled || loading || pending}`])"
88
+ :aria-busy="loading || pending"
89
+ :tabindex="attribute(({ true: '0', false: null })[`${(loading || pending) && !disabled}`])"
87
90
  :data-ui-button-state="hostState || undefined"
88
91
  ref="root"
89
- ><UiSpinner v-if="pending" size="sm" class="pending-spinner" />
92
+ ><span v-if="loading || pending" class="spinner-wrap"><UiSpinner size="sm" /></span>
90
93
  <slot /></a>
91
94
  <button
92
95
  v-else
@@ -94,14 +97,14 @@ function attribute(value: unknown): any {
94
97
  v-bind="$attrs"
95
98
  :type="type"
96
99
  :disabled="disabled"
97
- :aria-disabled="typeof (({ true: 'true', false: null })[`${pending}`]) === 'boolean'
98
- ? String(({ true: 'true', false: null })[`${pending}`])
99
- : attribute(({ true: 'true', false: null })[`${pending}`])"
100
- :aria-busy="pending"
100
+ :aria-disabled="typeof (({ true: 'true', false: null })[`${loading || pending}`]) === 'boolean'
101
+ ? String(({ true: 'true', false: null })[`${loading || pending}`])
102
+ : attribute(({ true: 'true', false: null })[`${loading || pending}`])"
103
+ :aria-busy="loading || pending"
101
104
  :data-ui-button-state="hostState || undefined"
102
105
  ref="root"
103
106
  >
104
- <UiSpinner v-if="pending" size="sm" class="pending-spinner" />
107
+ <span v-if="loading || pending" class="spinner-wrap"><UiSpinner size="sm" /></span>
105
108
  <slot />
106
109
  </button>
107
110
  </template>
@@ -234,12 +237,15 @@ function attribute(value: unknown): any {
234
237
  background-color var(--ui-motion-fast) var(--ui-motion-ease),
235
238
  border-color var(--ui-motion-fast) var(--ui-motion-ease),
236
239
  color var(--ui-motion-fast) var(--ui-motion-ease),
237
- box-shadow var(--ui-motion-fast) var(--ui-motion-ease);
240
+ box-shadow var(--ui-motion-base) var(--ui-motion-ease);
238
241
  margin: 0;
239
242
  }
240
243
 
241
- .pending-spinner {
244
+ .spinner-wrap {
245
+ display: inline-flex;
246
+ flex: none;
242
247
  margin-inline-end: var(--ui-space-2);
248
+ vertical-align: middle;
243
249
  }
244
250
 
245
251
  @supports (text-box-trim: trim-both) {
@@ -371,12 +377,12 @@ function attribute(value: unknown): any {
371
377
  var(--ui-focus-halo);
372
378
  }
373
379
 
374
- /* Disabled keeps the shape and washes out the rest: every variant keeps its own structure, so a
375
- disabled outline is still an outline and a disabled ghost still has no edge. One filter fades
376
- fill, border, and text together (--ui-disabled-filter), and the theme decides which way is
377
- toward the page. The highlight and shadow go, because nothing that cannot be pressed should
378
- look raised. */
379
- [data-component~="ui-button"]:is(:disabled, [aria-disabled="true"]) {
380
+ /* A loading action stays visually active; only an explicitly disabled action takes the
381
+ neutral disabled palette. Its flat surface and muted ink make the state unambiguous. */
382
+ [data-component~="ui-button"]:is(:disabled, [aria-disabled="true"]:not([aria-busy="true"])) {
383
+ border-color: var(--ui-disabled-border);
384
+ background-color: var(--ui-disabled-surface);
385
+ color: var(--ui-disabled-text);
380
386
  filter: var(--ui-button-disabled-filter, var(--ui-disabled-filter));
381
387
  opacity: var(--ui-button-disabled-opacity, 1);
382
388
  cursor: not-allowed;
@@ -384,16 +390,13 @@ function attribute(value: unknown): any {
384
390
  box-shadow: none;
385
391
  }
386
392
 
387
- [data-component~="ui-button"][data-ui-button-state~="pending"]:focus-visible {
393
+ :is([data-component~="ui-button"][data-ui-button-state~="loading"], [data-component~="ui-button"][data-ui-button-state~="pending"]):focus-visible {
388
394
  box-shadow: var(--ui-focus-halo);
389
395
  }
390
396
 
391
- /* Ghost has no hover to fall back on, so disabled is the one state it has to state outright:
392
- it takes a surface rather than reading as plain text. The surface is a wash of the faded tone,
393
- the way hover washes the live one, so it stays light in every tone. An opaque mix toward the
394
- hue came out a mid-grey slab for neutral, whose hue is the ink: louder than the enabled button. */
395
- [data-component~="ui-button"][data-ui-button-state~="variant=ghost"]:is(:disabled, [aria-disabled="true"]) {
396
- background-color: oklch(from var(--_tone) l c h / 14%);
397
+ /* Ghost keeps no border, but gains the same neutral surface as other disabled buttons. */
398
+ [data-component~="ui-button"][data-ui-button-state~="variant=ghost"]:is(:disabled, [aria-disabled="true"]:not([aria-busy="true"])) {
399
+ background-color: var(--ui-disabled-surface);
397
400
  border-color: transparent;
398
401
  }
399
402
 
@@ -456,7 +459,7 @@ html[data-ui-input-modality="touch"]
456
459
  transform: none;
457
460
  }
458
461
 
459
- [data-component~="ui-button"][data-ui-button-state~="variant=link"]:is(:disabled, [aria-disabled="true"]) {
462
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"]:is(:disabled, [aria-disabled="true"]:not([aria-busy="true"])) {
460
463
  background: none;
461
464
  border-color: transparent;
462
465
  }
package/vue/UiCallout.vue CHANGED
@@ -158,8 +158,9 @@ const hostState = computed(() =>
158
158
  gap: var(--ui-callout-gap, calc(var(--ui-space-2) * 0.75));
159
159
  align-items: start;
160
160
  max-width: 100%;
161
- padding: var(--ui-callout-padding-block, var(--ui-space-2))
162
- var(--ui-callout-padding-inline, var(--ui-space-3));
161
+ padding-block: var(--ui-callout-padding-block, var(--ui-space-2));
162
+ padding-inline-start: var(--ui-callout-padding-inline, var(--ui-space-2));
163
+ padding-inline-end: var(--ui-callout-padding-inline, var(--ui-space-3));
163
164
  border: var(--ui-callout-border-width, 1px) solid
164
165
  color-mix(in srgb, var(--ui-callout-border, var(--_callout-border)) 35%, transparent);
165
166
  border-inline-start: var(--ui-callout-leading-border-width, 4px) solid
package/vue/UiCard.vue CHANGED
@@ -69,7 +69,7 @@ const hostState = computed(() =>
69
69
  box-sizing: border-box;
70
70
  min-inline-size: 0;
71
71
  padding: var(--ui-space-5);
72
- border: 1px solid var(--ui-border-default);
72
+ border: 1px solid var(--ui-border-strong);
73
73
  border-radius: var(--ui-radius-lg);
74
74
  background: var(--ui-surface-raised);
75
75
  color: var(--ui-text-primary);
@@ -95,7 +95,9 @@ const hostState = computed(() =>
95
95
  }
96
96
 
97
97
  [data-component~="ui-card"][data-ui-card-state~="tone=danger"] {
98
- border-color: var(--ui-danger);
98
+ border-color: color-mix(in srgb, var(--ui-danger) 45%, var(--ui-border-default));
99
+ border-inline-start: 4px solid var(--ui-danger);
100
+ background: var(--ui-danger-soft);
99
101
  }
100
102
 
101
103
  /* A component that sets its display still honours the hidden attribute on its root. */
@@ -163,10 +163,10 @@ input {
163
163
  inline-size: var(--_ui-checkbox-size, 1.125rem);
164
164
  block-size: var(--_ui-checkbox-size, 1.125rem);
165
165
  margin: 0.125em 0 0;
166
- border: 1px solid var(--ui-control-border-hover, var(--ui-border-strong));
166
+ border: 1px solid var(--ui-control-border, var(--ui-border-strong));
167
167
  border-radius: var(--ui-radius-sm);
168
168
  background: var(--ui-control-surface, var(--ui-surface-default));
169
- box-shadow: inset 0 1px 2px color-mix(in srgb, var(--ui-text-primary) 8%, transparent);
169
+ box-shadow: var(--ui-control-inset);
170
170
  cursor: pointer;
171
171
  transition:
172
172
  background-color var(--ui-motion-fast) var(--ui-motion-ease),
@@ -178,28 +178,30 @@ input::after {
178
178
  content: "";
179
179
  position: absolute;
180
180
  opacity: 0;
181
+ transition: opacity 120ms var(--ui-motion-ease);
181
182
  }
182
183
 
183
184
  input:checked,
184
185
  input:indeterminate {
185
186
  border-color: var(--ui-accent-solid);
186
187
  background: var(--ui-accent-solid);
187
- box-shadow: inset 0 1px 2px color-mix(in srgb, #000 16%, transparent);
188
+ box-shadow: var(--ui-control-inset);
188
189
  }
189
190
 
190
191
  /* Centre the tick's box, then lift it: a rotated L's ink sits ~0.11rem below its box centre. */
191
192
  input:checked::after {
192
193
  inset: 0;
193
194
  margin: auto;
194
- inline-size: 0.32rem;
195
- block-size: 0.58rem;
195
+ inline-size: 0.28rem;
196
+ block-size: 0.5rem;
196
197
  /* Longhands, not `border: solid var(...)`: lowering drops a shorthand that omits a
197
198
  component, which left the tick with no style and no colour. */
198
199
  border-style: solid;
199
200
  border-color: var(--ui-text-on-accent);
200
201
  border-width: 0 0.125rem 0.125rem 0;
201
202
  opacity: 1;
202
- transform: translateY(-0.11rem) rotate(45deg);
203
+ transform: translate(0.07rem, -0.09rem) rotate(45deg);
204
+ animation: ui-checkbox-check 180ms var(--ui-motion-ease-out, cubic-bezier(0.16, 1, 0.3, 1)) both;
203
205
  }
204
206
 
205
207
  input:indeterminate::after {
@@ -211,6 +213,28 @@ input:indeterminate::after {
211
213
  transform: translateY(-50%);
212
214
  }
213
215
 
216
+ @keyframes ui-checkbox-check {
217
+ from {
218
+ opacity: 0;
219
+ transform: translate(0.07rem, -0.09rem) rotate(45deg) scale(0.55);
220
+ }
221
+ 65% {
222
+ opacity: 1;
223
+ transform: translate(0.07rem, -0.09rem) rotate(45deg) scale(1.12);
224
+ }
225
+ to {
226
+ opacity: 1;
227
+ transform: translate(0.07rem, -0.09rem) rotate(45deg) scale(1);
228
+ }
229
+ }
230
+
231
+ @media (prefers-reduced-motion: reduce) {
232
+ input::after {
233
+ transition: none;
234
+ animation: none;
235
+ }
236
+ }
237
+
214
238
  input:hover:enabled {
215
239
  border-color: var(--ui-accent-solid);
216
240
  }
@@ -228,9 +252,13 @@ input:focus-visible {
228
252
  color: var(--ui-disabled-text);
229
253
  cursor: not-allowed;
230
254
  }
255
+ [data-component~="ui-checkbox"][data-ui-checkbox-state~="disabled"] .description {
256
+ color: var(--ui-disabled-text);
257
+ }
231
258
  [data-component~="ui-checkbox"][data-ui-checkbox-state~="disabled"] input {
232
- border-color: var(--ui-border-default);
233
- background: var(--ui-control-surface-disabled, var(--ui-surface-muted));
259
+ border-color: var(--ui-disabled-border);
260
+ background: var(--ui-disabled-surface);
261
+ box-shadow: none;
234
262
  cursor: not-allowed;
235
263
  }
236
264
 
@@ -347,6 +347,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
347
347
  invalid: boolean;
348
348
  size: "sm" | "md" | "lg";
349
349
  disabled: boolean;
350
+ loading: boolean;
350
351
  required: boolean;
351
352
  allowCreate: boolean;
352
353
  allowFreeText: boolean;
@@ -354,7 +355,6 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
354
355
  disclosure: boolean;
355
356
  help: string;
356
357
  labelVisibility: "visible" | "sr-only";
357
- loading: boolean;
358
358
  multiple: boolean;
359
359
  openOnFocus: boolean;
360
360
  placeholder: string;
@@ -802,35 +802,37 @@ defineExpose({
802
802
  *</template></label>
803
803
  <div class="control">
804
804
  <div class="field" ref="field">
805
- <slot name="start" />
806
- <template v-if="multiple"><button
807
- v-for="(item, index) in internalItems"
808
- :key="item.id"
809
- class="item"
810
- type="button"
811
- tabindex="-1"
812
- :data-index="index"
813
- :disabled="disabled || readonly || item.disabled"
814
- :aria-label="`${item.label}, press Delete or Backspace to remove`"
805
+ <div class="entry">
806
+ <slot name="start" />
807
+ <template v-if="multiple"><button
808
+ v-for="(item, index) in internalItems"
809
+ :key="item.id"
810
+ class="item"
811
+ type="button"
812
+ tabindex="-1"
813
+ :data-index="index"
814
+ :disabled="disabled || readonly || item.disabled"
815
+ :aria-label="`${item.label}, press Delete or Backspace to remove`"
816
+ >
817
+ <slot :name="`item-${item.id}`"><UiBadge>{{ item.label }}</UiBadge></slot>
818
+ </button></template>
819
+ <input
820
+ role="combobox"
821
+ aria-autocomplete="list"
822
+ autocomplete="off"
823
+ :id="`${uid}-input`"
824
+ :aria-controls="`${uid}-listbox`"
825
+ :aria-expanded="expanded"
826
+ :aria-invalid="invalid || validationStatus === 'error'"
827
+ :aria-busy="loading || validationStatus === 'pending'"
828
+ :value.prop="display"
829
+ :placeholder="placeholder"
830
+ :disabled="disabled"
831
+ :readonly="readonly"
832
+ :required="required && !(multiple && internalItems[0])"
833
+ ref="input"
815
834
  >
816
- <slot :name="`item-${item.id}`"><UiBadge>{{ item.label }}</UiBadge></slot>
817
- </button></template>
818
- <input
819
- role="combobox"
820
- aria-autocomplete="list"
821
- autocomplete="off"
822
- :id="`${uid}-input`"
823
- :aria-controls="`${uid}-listbox`"
824
- :aria-expanded="expanded"
825
- :aria-invalid="invalid || validationStatus === 'error'"
826
- :aria-busy="loading || validationStatus === 'pending'"
827
- :value.prop="display"
828
- :placeholder="placeholder"
829
- :disabled="disabled"
830
- :readonly="readonly"
831
- :required="required && !(multiple && internalItems[0])"
832
- ref="input"
833
- >
835
+ </div>
834
836
  <input
835
837
  v-if="name && !multiple"
836
838
  type="hidden"
@@ -851,7 +853,7 @@ defineExpose({
851
853
  type="button"
852
854
  data-combobox-action="clear"
853
855
  aria-label="Clear"
854
- :disabled="disabled || readonly"
856
+ :disabled="disabled || readonly || (!raw && (multiple || selected === null))"
855
857
  >
856
858
  <svg
857
859
  class="affordance-icon"
@@ -1113,6 +1115,11 @@ label {
1113
1115
  var(--ui-control-inset),
1114
1116
  var(--ui-control-focus-halo-shadow);
1115
1117
  }
1118
+ .entry {
1119
+ display: flex;
1120
+ flex: 1 1 auto;
1121
+ min-inline-size: 0;
1122
+ }
1116
1123
  /* A ghost icon button: the affordance is the mark, not a box around it. */
1117
1124
  .help {
1118
1125
  flex: none;
@@ -1238,7 +1245,7 @@ input:disabled, button:disabled {
1238
1245
  margin: 0;
1239
1246
  padding: var(--ui-space-1);
1240
1247
  border: 1px solid
1241
- var(--_ui-combobox-popup-border, var(--ui-overlay-border, var(--ui-border-default)));
1248
+ var(--_ui-combobox-popup-border, var(--ui-overlay-border, var(--ui-border-strong)));
1242
1249
  border-radius: var(--_ui-combobox-popup-radius, var(--ui-overlay-radius, var(--ui-radius-md)));
1243
1250
  background: var(
1244
1251
  --_ui-combobox-popup-surface,
@@ -1268,6 +1275,10 @@ input:disabled, button:disabled {
1268
1275
  color: var(--ui-disabled-text);
1269
1276
  cursor: not-allowed;
1270
1277
  }
1278
+ .option[aria-disabled="true"] .primary,
1279
+ .option[aria-disabled="true"] .secondary {
1280
+ color: var(--ui-disabled-text);
1281
+ }
1271
1282
  .primary {
1272
1283
  display: block;
1273
1284
  color: var(--_ui-combobox-option-text, var(--ui-text-primary));
@@ -1365,21 +1376,29 @@ input:disabled, button:disabled {
1365
1376
  );
1366
1377
  }
1367
1378
 
1368
- /* Multiple mode: chips wrap before the cursor on one flexible field line. */
1379
+ /* Multiple mode keeps one control height. Chips and the input scroll within the entry,
1380
+ while the clear and disclosure controls remain fixed at the end of the field. */
1369
1381
  [data-component~="ui-combobox"][data-ui-combobox-state~="multiple"] .field {
1370
- flex-wrap: wrap;
1371
1382
  align-items: center;
1372
- gap: var(--ui-space-1);
1373
1383
  min-block-size: var(--_ui-combobox-item-min-block-size, 2.5rem);
1374
1384
  padding: var(--ui-space-1);
1375
1385
  }
1386
+ [data-component~="ui-combobox"][data-ui-combobox-state~="multiple"] .entry {
1387
+ align-items: center;
1388
+ gap: var(--ui-space-1);
1389
+ overflow-x: auto;
1390
+ scrollbar-width: none;
1391
+ }
1392
+ [data-component~="ui-combobox"][data-ui-combobox-state~="multiple"] .entry::-webkit-scrollbar {
1393
+ display: none;
1394
+ }
1376
1395
  [data-component~="ui-combobox"][data-ui-combobox-state~="multiple"] input {
1377
1396
  flex: 1 0 var(--_ui-combobox-item-input-min-inline-size, 5rem);
1378
1397
  width: auto;
1379
1398
  min-block-size: 1.75rem;
1380
1399
  padding: var(--ui-space-0-5) var(--ui-space-1);
1381
1400
  }
1382
- /* A small multiple field starts at the small control height; chosen items still wrap it taller. */
1401
+ /* Size variants keep their control height as selections are added. */
1383
1402
  [data-component~="ui-combobox"][data-ui-combobox-state~="multiple"][data-component~="ui-combobox"][data-ui-combobox-state~="size=sm"]
1384
1403
  .field {
1385
1404
  min-block-size: 0;
@@ -1402,6 +1421,7 @@ input:disabled, button:disabled {
1402
1421
  .item {
1403
1422
  appearance: none;
1404
1423
  display: inline-flex;
1424
+ flex: none;
1405
1425
  min-inline-size: 0;
1406
1426
  max-inline-size: min(100%, var(--_ui-combobox-item-item-max-inline-size, 12rem));
1407
1427
  overflow: hidden;
@@ -1418,7 +1438,7 @@ input:disabled, button:disabled {
1418
1438
  outline-offset: 1px;
1419
1439
  }
1420
1440
  .item:disabled {
1421
- opacity: 0.6;
1441
+ color: var(--ui-disabled-text);
1422
1442
  }
1423
1443
 
1424
1444
  /* A component that sets its display still honours the hidden attribute on its root. */
@@ -141,7 +141,7 @@ useComponentHost(controllerModule.default, {
141
141
  var(--ui-overlay-surface, var(--ui-surface-elevated))
142
142
  );
143
143
  border: 1px solid
144
- var(--_ui-context-menu-border, var(--ui-overlay-border, var(--ui-border-default)));
144
+ var(--_ui-context-menu-border, var(--ui-overlay-border, var(--ui-border-strong)));
145
145
  border-radius: var(--_ui-context-menu-radius, var(--ui-overlay-radius, var(--ui-radius-lg)));
146
146
  box-shadow: var(
147
147
  --_ui-context-menu-shadow,
@@ -165,7 +165,7 @@ useComponentHost(controllerModule.default, {
165
165
  block-size: 0;
166
166
  margin: var(--ui-space-1) 0;
167
167
  border: 0;
168
- border-block-start: 1px solid var(--ui-border-default);
168
+ border-block-start: 1px solid var(--ui-border-divider);
169
169
  }
170
170
 
171
171
  .menu:popover-open {
@@ -32,7 +32,7 @@ withDefaults(
32
32
  padding: var(--_ui-description-padding, 0);
33
33
  border-width: var(--_ui-description-border-width, 0);
34
34
  border-style: solid;
35
- border-color: var(--ui-border-default);
35
+ border-color: var(--_ui-description-border-color, var(--ui-border-default));
36
36
  border-radius: var(--ui-radius-md);
37
37
  font-size: var(--ui-font-size-sm);
38
38
  }
@@ -70,6 +70,7 @@ const hostState = computed(() =>
70
70
  than that many fit. */
71
71
  [data-component~="ui-description-list"][data-ui-description-list-state~="layout=grid"],
72
72
  [data-component~="ui-description-list"][data-ui-description-list-state~="layout=tiles"] {
73
+ inline-size: 100%;
73
74
  grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--_track)), 1fr));
74
75
  }
75
76
 
@@ -78,6 +79,7 @@ const hostState = computed(() =>
78
79
  --_ui-description-value-size: var(--ui-font-size-lg);
79
80
  --_ui-description-padding: var(--_tile-padding);
80
81
  --_ui-description-border-width: 1px;
82
+ --_ui-description-border-color: var(--ui-border-strong);
81
83
  --_gap: var(--ui-space-3);
82
84
  --_gap-compact: var(--ui-space-2);
83
85
  --_column-gap: var(--_gap);
package/vue/UiDialog.d.ts CHANGED
@@ -8,11 +8,11 @@ type __VLS_Props = {
8
8
  open?: boolean;
9
9
  size?: 'sm' | 'md' | 'lg' | 'fullscreen';
10
10
  };
11
- declare var __VLS_1: {}, __VLS_3: {};
11
+ declare var __VLS_7: {}, __VLS_9: {};
12
12
  type __VLS_Slots = {} & {
13
- default?: (props: typeof __VLS_1) => any;
13
+ default?: (props: typeof __VLS_7) => any;
14
14
  } & {
15
- actions?: (props: typeof __VLS_3) => any;
15
+ actions?: (props: typeof __VLS_9) => any;
16
16
  };
17
17
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
18
18
  close: (detail: {
package/vue/UiDialog.vue CHANGED
@@ -3,6 +3,7 @@
3
3
  import { computed, ref, useTemplateRef } from 'vue'
4
4
  import * as controllerModule from '../components/ui-dialog/ui-dialog.js'
5
5
  import { createDispatch, useComponentHost } from './host'
6
+ import UiScrollArea from './UiScrollArea.vue'
6
7
 
7
8
  defineOptions({ inheritAttrs: false })
8
9
 
@@ -106,7 +107,7 @@ useComponentHost(controllerModule.default, {
106
107
  <h2 v-if="label" class="title">{{ label }}</h2>
107
108
  <button class="close" type="button" aria-label="Close" ref="close"></button>
108
109
  </header>
109
- <div class="body"><slot /></div>
110
+ <UiScrollArea class="body"><slot /></UiScrollArea>
110
111
  <footer><slot name="actions" /></footer>
111
112
  </dialog>
112
113
  </template>
@@ -160,7 +161,7 @@ useComponentHost(controllerModule.default, {
160
161
  overflow: hidden;
161
162
  flex-direction: column;
162
163
  background: var(--ui-dialog-surface, var(--ui-overlay-surface, var(--ui-surface-elevated)));
163
- border: 1px solid var(--ui-dialog-border, var(--ui-overlay-border, var(--ui-border-default)));
164
+ border: 1px solid var(--ui-dialog-border, var(--ui-overlay-border, var(--ui-border-strong)));
164
165
  border-radius: var(--ui-dialog-radius, var(--ui-overlay-radius, var(--ui-radius-dialog)));
165
166
  box-shadow: var(
166
167
  --ui-dialog-shadow,
@@ -215,7 +216,6 @@ header {
215
216
  gap: var(--ui-space-3);
216
217
  flex: none;
217
218
  padding: var(--ui-space-3) var(--ui-space-3) var(--ui-space-3) var(--ui-space-5);
218
- border-block-end: 1px solid var(--ui-border-default);
219
219
  }
220
220
 
221
221
  /* :host-qualified so page heading rules that reach in cannot outrank the dialog chrome. */
@@ -292,30 +292,7 @@ html[data-ui-input-modality="touch"] [data-component~="ui-dialog"] .close {
292
292
  .body {
293
293
  flex: 1 1 auto;
294
294
  min-block-size: 0;
295
- overflow: auto;
296
- overscroll-behavior: contain;
297
295
  padding: var(--ui-space-5);
298
- /* Each edge that hides content fades: the scroll fade every Looma scroller uses (see ui-scroll-area). */
299
- --_ui-scroll-fade-direction: to bottom;
300
- --_ui-scroll-fade-size: var(--ui-scroll-fade-size, 24px);
301
- mask-image: linear-gradient(
302
- var(--_ui-scroll-fade-direction),
303
- transparent,
304
- #000 var(--_ui-scroll-fade-start),
305
- #000 calc(100% - var(--_ui-scroll-fade-end)),
306
- transparent
307
- );
308
- animation:
309
- ui-scroll-fade-start linear,
310
- ui-scroll-fade-start-hold linear,
311
- ui-scroll-fade-end-hold linear,
312
- ui-scroll-fade-end linear;
313
- animation-timeline: scroll(self block);
314
- animation-range:
315
- 0 var(--_ui-scroll-fade-size),
316
- var(--_ui-scroll-fade-size) calc(100% + 1px),
317
- -1px calc(100% - var(--_ui-scroll-fade-size)),
318
- calc(100% - var(--_ui-scroll-fade-size)) 100%;
319
296
  }
320
297
 
321
298
  footer {
@@ -325,7 +302,6 @@ footer {
325
302
  justify-content: flex-end;
326
303
  gap: var(--ui-space-2);
327
304
  padding: var(--ui-space-3) var(--ui-space-5);
328
- border-block-start: 1px solid var(--ui-border-default);
329
305
  }
330
306
 
331
307
  footer:empty {
@@ -166,7 +166,7 @@ function attribute(value: unknown): any {
166
166
  display: block;
167
167
  inline-size: 100%;
168
168
  overflow: clip;
169
- border-block-end: 1px solid var(--ui-border-default);
169
+ border-block-end: 1px solid var(--ui-border-divider);
170
170
  color: var(--ui-text-primary);
171
171
  }
172
172
 
@@ -191,7 +191,7 @@ function attribute(value: unknown): any {
191
191
  }
192
192
 
193
193
  .trigger:hover:not(:disabled) {
194
- background: var(--ui-surface-muted);
194
+ background: color-mix(in srgb, currentColor 6%, transparent);
195
195
  }
196
196
  .trigger:focus-visible {
197
197
  outline: none;
@@ -91,7 +91,7 @@ useComponentHost(controllerModule.default, {
91
91
  [data-component~="ui-editor-insert-table-grid"] {
92
92
  padding: var(--ui-space-3);
93
93
  background: var(--ui-surface);
94
- border: 1px solid var(--ui-border);
94
+ border: 1px solid var(--ui-border-strong);
95
95
  border-radius: var(--ui-radius-md);
96
96
  box-shadow: var(--ui-shadow-lg, 0 10px 15px -3px rgb(0 0 0 / 0.1));
97
97
  }
@@ -140,7 +140,7 @@ useComponentHost(controllerModule.default, {
140
140
  width: 1.25rem;
141
141
  height: 1.25rem;
142
142
  padding: 0;
143
- border: 1px solid var(--ui-border);
143
+ border: 1px solid var(--ui-control-border);
144
144
  border-radius: 2px;
145
145
  background: var(--ui-surface);
146
146
  cursor: pointer;
@@ -50,8 +50,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
50
50
  }) => any) | undefined;
51
51
  }>, {
52
52
  label: string;
53
- open: boolean;
54
53
  loading: boolean;
54
+ open: boolean;
55
55
  query: string;
56
56
  loadingLabel: string;
57
57
  selectedIndex: number;
@@ -118,7 +118,7 @@ useComponentHost(controllerModule.default, {
118
118
  overflow: hidden;
119
119
  flex-direction: column;
120
120
  background: var(--ui-surface);
121
- border: 1px solid var(--ui-border);
121
+ border: 1px solid var(--ui-border-strong);
122
122
  border-radius: var(--ui-radius-dialog);
123
123
  box-shadow: var(--ui-shadow-lg, 0 8px 24px rgb(0 0 0 / 0.12));
124
124
  }
@@ -130,12 +130,13 @@ useComponentHost(controllerModule.default, {
130
130
  gap: var(--ui-space-3);
131
131
  padding: var(--ui-space-2) var(--ui-space-3);
132
132
  color: var(--ui-text-secondary);
133
- border-bottom: 1px solid var(--ui-border);
133
+ border-bottom: 1px solid var(--ui-border-divider);
134
134
  font-size: var(--ui-font-size-xs);
135
135
  font-weight: var(--ui-font-semibold);
136
136
  }
137
137
 
138
- .header:not(:has(> *)), .empty:not(:has(> *)) {
138
+ [data-component~="ui-editor-mention-menu"] > .header:empty,
139
+ [data-component~="ui-editor-mention-menu"] > .empty:empty {
139
140
  display: none;
140
141
  }
141
142