@nuxt/ui 4.11.0 → 4.11.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 (101) hide show
  1. package/README.md +3 -3
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +2 -2
  4. package/dist/runtime/components/Accordion.d.vue.ts +1 -1
  5. package/dist/runtime/components/Accordion.vue.d.ts +1 -1
  6. package/dist/runtime/components/Breadcrumb.d.vue.ts +1 -1
  7. package/dist/runtime/components/Breadcrumb.vue.d.ts +1 -1
  8. package/dist/runtime/components/Carousel.d.vue.ts +7 -7
  9. package/dist/runtime/components/Carousel.vue.d.ts +7 -7
  10. package/dist/runtime/components/ChatMessages.d.vue.ts +2 -2
  11. package/dist/runtime/components/ChatMessages.vue.d.ts +2 -2
  12. package/dist/runtime/components/CheckboxGroup.d.vue.ts +1 -1
  13. package/dist/runtime/components/CheckboxGroup.vue.d.ts +1 -1
  14. package/dist/runtime/components/CommandPalette.d.vue.ts +1 -1
  15. package/dist/runtime/components/CommandPalette.vue.d.ts +1 -1
  16. package/dist/runtime/components/ContextMenu.d.vue.ts +1 -1
  17. package/dist/runtime/components/ContextMenu.vue.d.ts +1 -1
  18. package/dist/runtime/components/DashboardNavbar.vue +1 -1
  19. package/dist/runtime/components/DropdownMenu.d.vue.ts +1 -1
  20. package/dist/runtime/components/DropdownMenu.vue.d.ts +1 -1
  21. package/dist/runtime/components/FooterColumns.d.vue.ts +1 -1
  22. package/dist/runtime/components/FooterColumns.vue.d.ts +1 -1
  23. package/dist/runtime/components/Form.vue +2 -2
  24. package/dist/runtime/components/Input.vue +5 -5
  25. package/dist/runtime/components/InputDate.vue +12 -6
  26. package/dist/runtime/components/InputMenu.d.vue.ts +1 -1
  27. package/dist/runtime/components/InputMenu.vue +18 -7
  28. package/dist/runtime/components/InputMenu.vue.d.ts +1 -1
  29. package/dist/runtime/components/InputNumber.vue +8 -5
  30. package/dist/runtime/components/InputTime.vue +12 -6
  31. package/dist/runtime/components/Link.vue +61 -7
  32. package/dist/runtime/components/LinkBase.vue +3 -1
  33. package/dist/runtime/components/Listbox.d.vue.ts +1 -1
  34. package/dist/runtime/components/Listbox.vue.d.ts +1 -1
  35. package/dist/runtime/components/NavigationMenu.d.vue.ts +1 -1
  36. package/dist/runtime/components/NavigationMenu.vue +10 -10
  37. package/dist/runtime/components/NavigationMenu.vue.d.ts +1 -1
  38. package/dist/runtime/components/PageAnchors.d.vue.ts +1 -1
  39. package/dist/runtime/components/PageAnchors.vue.d.ts +1 -1
  40. package/dist/runtime/components/PageLinks.d.vue.ts +1 -1
  41. package/dist/runtime/components/PageLinks.vue.d.ts +1 -1
  42. package/dist/runtime/components/PinInput.d.vue.ts +3 -1
  43. package/dist/runtime/components/PinInput.vue +15 -7
  44. package/dist/runtime/components/PinInput.vue.d.ts +3 -1
  45. package/dist/runtime/components/ProgressGroup.d.vue.ts +1 -1
  46. package/dist/runtime/components/ProgressGroup.vue.d.ts +1 -1
  47. package/dist/runtime/components/RadioGroup.d.vue.ts +1 -1
  48. package/dist/runtime/components/RadioGroup.vue.d.ts +1 -1
  49. package/dist/runtime/components/Select.d.vue.ts +3 -1
  50. package/dist/runtime/components/Select.vue +2 -0
  51. package/dist/runtime/components/Select.vue.d.ts +3 -1
  52. package/dist/runtime/components/SelectMenu.d.vue.ts +4 -1
  53. package/dist/runtime/components/SelectMenu.vue +24 -4
  54. package/dist/runtime/components/SelectMenu.vue.d.ts +4 -1
  55. package/dist/runtime/components/Slider.vue +1 -1
  56. package/dist/runtime/components/Splitter.d.vue.ts +1 -1
  57. package/dist/runtime/components/Splitter.vue.d.ts +1 -1
  58. package/dist/runtime/components/Stepper.d.vue.ts +1 -1
  59. package/dist/runtime/components/Stepper.vue.d.ts +1 -1
  60. package/dist/runtime/components/Table.vue +29 -7
  61. package/dist/runtime/components/Tabs.d.vue.ts +1 -1
  62. package/dist/runtime/components/Tabs.vue.d.ts +1 -1
  63. package/dist/runtime/components/Textarea.vue +5 -5
  64. package/dist/runtime/components/Timeline.d.vue.ts +1 -1
  65. package/dist/runtime/components/Timeline.vue.d.ts +1 -1
  66. package/dist/runtime/components/Toaster.vue +1 -1
  67. package/dist/runtime/components/Tree.d.vue.ts +1 -1
  68. package/dist/runtime/components/Tree.vue +1 -1
  69. package/dist/runtime/components/Tree.vue.d.ts +1 -1
  70. package/dist/runtime/components/color-mode/ColorModeSelect.vue +1 -0
  71. package/dist/runtime/components/content/ContentNavigation.d.vue.ts +1 -1
  72. package/dist/runtime/components/content/ContentNavigation.vue.d.ts +1 -1
  73. package/dist/runtime/components/content/ContentSurround.d.vue.ts +1 -1
  74. package/dist/runtime/components/content/ContentSurround.vue.d.ts +1 -1
  75. package/dist/runtime/components/content/ContentToc.d.vue.ts +1 -1
  76. package/dist/runtime/components/content/ContentToc.vue.d.ts +1 -1
  77. package/dist/runtime/components/locale/LocaleSelect.vue +1 -0
  78. package/dist/runtime/components/prose/CodeTree.vue +31 -18
  79. package/dist/runtime/composables/useForwardProps.js +2 -2
  80. package/dist/runtime/composables/useResizable.js +1 -1
  81. package/dist/runtime/utils/form.d.ts +1 -1
  82. package/dist/runtime/utils/form.js +3 -1
  83. package/dist/runtime/utils/prefetch.d.ts +5 -0
  84. package/dist/runtime/utils/prefetch.js +45 -0
  85. package/dist/runtime/utils/search.js +8 -6
  86. package/dist/runtime/utils/virtualizer.d.ts +1 -1
  87. package/dist/runtime/utils/virtualizer.js +9 -11
  88. package/dist/runtime/vue/components/color-mode/ColorModeSelect.vue +1 -0
  89. package/dist/runtime/vue/overrides/inertia/LinkBase.vue +3 -1
  90. package/dist/shared/{ui.DNq7CNkE.mjs → ui.oNygBd3N.mjs} +40 -12
  91. package/dist/unplugin.d.mts +2 -1
  92. package/dist/unplugin.mjs +22 -10
  93. package/dist/vite.mjs +1 -1
  94. package/package.json +79 -83
  95. package/cli/commands/make/component.mjs +0 -86
  96. package/cli/commands/make/index.mjs +0 -14
  97. package/cli/commands/make/locale.mjs +0 -93
  98. package/cli/index.mjs +0 -15
  99. package/cli/package.json +0 -13
  100. package/cli/templates.mjs +0 -256
  101. package/cli/utils.mjs +0 -50
@@ -3,16 +3,17 @@ import theme from "#build/ui/link";
3
3
  </script>
4
4
 
5
5
  <script setup>
6
- import { computed } from "vue";
6
+ import { computed, getCurrentInstance, mergeProps, onMounted, onBeforeUnmount } from "vue";
7
7
  import { isEqual } from "ohash/utils";
8
8
  import { useForwardProps, Slot } from "reka-ui";
9
9
  import { defu } from "defu";
10
10
  import { hasProtocol } from "ufo";
11
11
  import { reactiveOmit } from "@vueuse/core";
12
- import { useRoute, useAppConfig, useNuxtApp } from "#imports";
12
+ import { useRoute, useAppConfig, useNuxtApp, onNuxtReady } from "#imports";
13
13
  import { mergeClasses } from "../utils";
14
14
  import { tv } from "../utils/tv";
15
15
  import { isPartiallyEqual } from "../utils/link";
16
+ import { requestIdleCallback, cancelIdleCallback, observeIntersection } from "../utils/prefetch";
16
17
  import ULinkBase from "./LinkBase.vue";
17
18
  defineOptions({ inheritAttrs: false });
18
19
  const props = defineProps({
@@ -126,13 +127,63 @@ function isLinkActive({ route: linkRoute, isActive, isExactActive } = {}) {
126
127
  }
127
128
  return false;
128
129
  }
129
- function resolveLinkClass({ route: route2, isActive, isExactActive } = {}) {
130
+ function resolveLinkClass({ route: route2, isActive, isExactActive, prefetched } = {}) {
130
131
  const active = isLinkActive({ route: route2, isActive, isExactActive });
132
+ const prefetchedClass = prefetched ? props.prefetchedClass : void 0;
131
133
  if (props.raw) {
132
- return [props.class, active ? props.activeClass : props.inactiveClass];
134
+ return [props.class, active ? props.activeClass : props.inactiveClass, prefetchedClass];
133
135
  }
134
- return ui.value({ class: props.class, active, disabled: props.disabled });
136
+ return ui.value({ class: prefetchedClass ? [props.class, prefetchedClass] : props.class, active, disabled: props.disabled });
135
137
  }
138
+ const instance = getCurrentInstance();
139
+ let prefetchApi;
140
+ function onPrefetch() {
141
+ prefetchApi?.prefetch?.(nuxtApp);
142
+ }
143
+ function getPrefetchListeners({ prefetch, shouldPrefetch }, attrs) {
144
+ if (!prefetch || !shouldPrefetch) {
145
+ return void 0;
146
+ }
147
+ prefetchApi = { prefetch, shouldPrefetch };
148
+ if (!shouldPrefetch("interaction")) {
149
+ return void 0;
150
+ }
151
+ return mergeProps(
152
+ { onPointerenter: attrs.onPointerenter, onFocus: attrs.onFocus },
153
+ { onPointerenter: onPrefetch, onFocus: onPrefetch }
154
+ );
155
+ }
156
+ let idleId;
157
+ let unobserve = null;
158
+ let unmounted = false;
159
+ onMounted(() => {
160
+ if (!prefetchApi?.shouldPrefetch?.("visibility")) {
161
+ return;
162
+ }
163
+ const root = instance?.proxy?.$el;
164
+ const el = root instanceof Element ? root : root?.nextElementSibling;
165
+ if (!el) {
166
+ return;
167
+ }
168
+ onNuxtReady(() => {
169
+ if (unmounted) {
170
+ return;
171
+ }
172
+ idleId = requestIdleCallback(() => {
173
+ unobserve = observeIntersection(el, () => {
174
+ unobserve?.();
175
+ unobserve = null;
176
+ onPrefetch();
177
+ });
178
+ });
179
+ });
180
+ });
181
+ onBeforeUnmount(() => {
182
+ unmounted = true;
183
+ cancelIdleCallback(idleId);
184
+ unobserve?.();
185
+ unobserve = null;
186
+ });
136
187
  </script>
137
188
 
138
189
  <template>
@@ -142,6 +193,8 @@ function resolveLinkClass({ route: route2, isActive, isExactActive } = {}) {
142
193
  v-bind="{
143
194
  ...$attrs,
144
195
  ...exact && isExactActive ? { 'aria-current': props.ariaCurrentValue } : {},
196
+ ...rest.prefetched && prefetchedClass ? { class: prefetchedClass } : {},
197
+ ...getPrefetchListeners(rest, $attrs),
145
198
  as,
146
199
  type,
147
200
  disabled,
@@ -166,9 +219,10 @@ function resolveLinkClass({ route: route2, isActive, isExactActive } = {}) {
166
219
  navigate,
167
220
  rel,
168
221
  target: rest.target,
169
- isExternal: rest.isExternal
222
+ isExternal: rest.isExternal,
223
+ ...getPrefetchListeners(rest, $attrs)
170
224
  }"
171
- :class="resolveLinkClass({ route: linkRoute, isActive, isExactActive })"
225
+ :class="resolveLinkClass({ route: linkRoute, isActive, isExactActive, prefetched: rest.prefetched })"
172
226
  >
173
227
  <slot :active="isLinkActive({ route: linkRoute, isActive, isExactActive })" />
174
228
  </ULinkBase>
@@ -22,14 +22,16 @@ function onClickWrapper(e) {
22
22
  e.preventDefault();
23
23
  return;
24
24
  }
25
+ const results = [];
25
26
  if (props.onClick) {
26
27
  for (const onClick of Array.isArray(props.onClick) ? props.onClick : [props.onClick]) {
27
- onClick(e);
28
+ results.push(onClick(e));
28
29
  }
29
30
  }
30
31
  if (props.href && props.navigate && !props.isExternal) {
31
32
  props.navigate(e);
32
33
  }
34
+ return Promise.all(results);
33
35
  }
34
36
  </script>
35
37
 
@@ -27,7 +27,7 @@ export interface ListboxItem {
27
27
  disabled?: boolean;
28
28
  onSelect?: (e: Event) => void;
29
29
  class?: any;
30
- ui?: Pick<Listbox['slots'], 'label' | 'separator' | 'item' | 'itemLeadingIcon' | 'itemLeadingAvatar' | 'itemLeadingAvatarSize' | 'itemLeadingChip' | 'itemLeadingChipSize' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemTrailing' | 'itemTrailingIcon'>;
30
+ ui?: Partial<Pick<Listbox['slots'], 'label' | 'separator' | 'item' | 'itemLeadingIcon' | 'itemLeadingAvatar' | 'itemLeadingAvatarSize' | 'itemLeadingChip' | 'itemLeadingChipSize' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemTrailing' | 'itemTrailingIcon'>>;
31
31
  [key: string]: any;
32
32
  }
33
33
  export interface ListboxProps<T extends ArrayOrNested<ListboxItem> = ArrayOrNested<ListboxItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false, Mod extends Omit<ModelModifiers, 'lazy'> = Omit<ModelModifiers, 'lazy'>> extends Pick<ListboxRootProps, 'by' | 'disabled' | 'highlightOnHover' | 'name' | 'orientation' | 'required' | 'selectionBehavior'> {
@@ -27,7 +27,7 @@ export interface ListboxItem {
27
27
  disabled?: boolean;
28
28
  onSelect?: (e: Event) => void;
29
29
  class?: any;
30
- ui?: Pick<Listbox['slots'], 'label' | 'separator' | 'item' | 'itemLeadingIcon' | 'itemLeadingAvatar' | 'itemLeadingAvatarSize' | 'itemLeadingChip' | 'itemLeadingChipSize' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemTrailing' | 'itemTrailingIcon'>;
30
+ ui?: Partial<Pick<Listbox['slots'], 'label' | 'separator' | 'item' | 'itemLeadingIcon' | 'itemLeadingAvatar' | 'itemLeadingAvatarSize' | 'itemLeadingChip' | 'itemLeadingChipSize' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemTrailing' | 'itemTrailingIcon'>>;
31
31
  [key: string]: any;
32
32
  }
33
33
  export interface ListboxProps<T extends ArrayOrNested<ListboxItem> = ArrayOrNested<ListboxItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false, Mod extends Omit<ModelModifiers, 'lazy'> = Omit<ModelModifiers, 'lazy'>> extends Pick<ListboxRootProps, 'by' | 'disabled' | 'highlightOnHover' | 'name' | 'orientation' | 'required' | 'selectionBehavior'> {
@@ -67,7 +67,7 @@ export interface NavigationMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cu
67
67
  open?: boolean;
68
68
  onSelect?: (e: Event) => void;
69
69
  class?: any;
70
- ui?: Pick<NavigationMenu['slots'], 'item' | 'linkLeadingAvatarSize' | 'linkLeadingAvatar' | 'linkLeadingIcon' | 'linkLeadingChipSize' | 'linkLabel' | 'linkLabelExternalIcon' | 'linkTrailing' | 'linkTrailingBadgeSize' | 'linkTrailingBadge' | 'linkTrailingIcon' | 'label' | 'link' | 'content' | 'childList' | 'childLabel' | 'childItem' | 'childLink' | 'childLinkIcon' | 'childLinkWrapper' | 'childLinkLabel' | 'childLinkLabelExternalIcon' | 'childLinkDescription'>;
70
+ ui?: Partial<Pick<NavigationMenu['slots'], 'item' | 'linkLeadingAvatarSize' | 'linkLeadingAvatar' | 'linkLeadingIcon' | 'linkLeadingChipSize' | 'linkLabel' | 'linkLabelExternalIcon' | 'linkTrailing' | 'linkTrailingBadgeSize' | 'linkTrailingBadge' | 'linkTrailingIcon' | 'label' | 'link' | 'content' | 'childList' | 'childLabel' | 'childItem' | 'childLink' | 'childLinkIcon' | 'childLinkWrapper' | 'childLinkLabel' | 'childLinkLabelExternalIcon' | 'childLinkDescription'>>;
71
71
  [key: string]: any;
72
72
  }
73
73
  type SingleOrMultipleType = 'single' | 'multiple';
@@ -106,13 +106,13 @@ function getAccordionDefaultValue(list, level = 0, listIndex = 0) {
106
106
  }, []);
107
107
  return props.type === "single" ? indexes[0] : indexes;
108
108
  }
109
- function onLinkTrailingClick(e, item) {
109
+ function onLinkTrailingClick(e, item, trailingTrigger) {
110
110
  if (!item.children?.length) {
111
111
  return;
112
112
  }
113
113
  if (props.orientation === "horizontal") {
114
114
  e.preventDefault();
115
- } else if (props.orientation === "vertical" && !props.collapsed) {
115
+ } else if (props.orientation === "vertical" && !props.collapsed && trailingTrigger) {
116
116
  e.preventDefault();
117
117
  e.stopPropagation();
118
118
  }
@@ -120,7 +120,7 @@ function onLinkTrailingClick(e, item) {
120
120
  </script>
121
121
 
122
122
  <template>
123
- <DefineLinkTemplate v-slot="{ item, active, index }">
123
+ <DefineLinkTemplate v-slot="{ item, active, index, trailingTrigger }">
124
124
  <slot :name="item.slot || 'item'" :item="item" :index="index" :active="active" :ui="ui">
125
125
  <slot :name="item.slot ? `${item.slot}-leading` : 'item-leading'" :item="item" :active="active" :index="index" :ui="ui">
126
126
  <UAvatar v-if="item.avatar" :size="item.ui?.linkLeadingAvatarSize || props.ui?.linkLeadingAvatarSize || ui.linkLeadingAvatarSize()" v-bind="item.avatar" data-slot="linkLeadingAvatar" :class="ui.linkLeadingAvatar({ class: [props.ui?.linkLeadingAvatar, item.ui?.linkLeadingAvatar], active, disabled: !!item.disabled })" />
@@ -149,12 +149,12 @@ function onLinkTrailingClick(e, item) {
149
149
  </span>
150
150
 
151
151
  <component
152
- :is="props.orientation === 'vertical' && item.children?.length && !props.collapsed ? AccordionTrigger : 'span'"
152
+ :is="props.orientation === 'vertical' && item.children?.length && !props.collapsed && trailingTrigger ? AccordionTrigger : 'span'"
153
153
  v-if="item.badge || item.badge === 0 || props.orientation === 'horizontal' && (item.children?.length || !!slots[item.slot ? `${item.slot}-content` : 'item-content']) || props.orientation === 'vertical' && item.children?.length || item.trailingIcon || !!slots[item.slot ? `${item.slot}-trailing` : 'item-trailing']"
154
- :as="props.orientation === 'vertical' && item.children?.length && !props.collapsed ? 'span' : void 0"
154
+ :as="props.orientation === 'vertical' && item.children?.length && !props.collapsed && trailingTrigger ? 'span' : void 0"
155
155
  data-slot="linkTrailing"
156
156
  :class="ui.linkTrailing({ class: [props.ui?.linkTrailing, item.ui?.linkTrailing] })"
157
- @click="(e) => onLinkTrailingClick(e, item)"
157
+ @click="(e) => onLinkTrailingClick(e, item, trailingTrigger)"
158
158
  >
159
159
  <slot :name="item.slot ? `${item.slot}-trailing` : 'item-trailing'" :item="item" :active="active" :index="index" :ui="ui">
160
160
  <UBadge
@@ -182,7 +182,7 @@ function onLinkTrailingClick(e, item) {
182
182
  :value="getItemValue(item, index, level, listIndex)"
183
183
  >
184
184
  <div v-if="props.orientation === 'vertical' && item.type === 'label' && !props.collapsed" data-slot="label" :class="ui.label({ class: [props.ui?.label, item.ui?.label, item.class] })">
185
- <ReuseLinkTemplate :item="item" :index="index" />
185
+ <ReuseLinkTemplate :item="item" :index="index" :trailing-trigger="true" />
186
186
  </div>
187
187
  <ULink v-else-if="item.type !== 'label'" v-slot="{ active, ...slotProps }" v-bind="props.orientation === 'vertical' && item.children?.length && !props.collapsed && item.type === 'trigger' ? {} : pickLinkProps(item)" custom>
188
188
  <component
@@ -194,7 +194,7 @@ function onLinkTrailingClick(e, item) {
194
194
  >
195
195
  <UPopover v-if="props.orientation === 'vertical' && props.collapsed && item.children?.length && (!!props.popover || !!item.popover)" v-bind="{ ...popoverProps, ...typeof item.popover === 'boolean' ? {} : item.popover || {} }" :ui="{ content: ui.content({ class: [props.ui?.content, item.ui?.content] }) }">
196
196
  <ULinkBase v-bind="slotProps" data-slot="link" :class="ui.link({ class: [props.ui?.link, item.ui?.link, item.class], active: active || item.active, disabled: !!item.disabled, level: level > 0 })">
197
- <ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" />
197
+ <ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" :trailing-trigger="!!slotProps.href" />
198
198
  </ULinkBase>
199
199
 
200
200
  <template #content="{ close }">
@@ -231,11 +231,11 @@ function onLinkTrailingClick(e, item) {
231
231
  </UPopover>
232
232
  <UTooltip v-else-if="props.orientation === 'vertical' && props.collapsed && (!!props.tooltip || !!item.tooltip) || props.orientation === 'horizontal' && !!item.tooltip" :text="get(item, props.labelKey)" v-bind="{ ...tooltipProps, ...typeof item.tooltip === 'boolean' ? {} : item.tooltip || {} }">
233
233
  <ULinkBase v-bind="slotProps" data-slot="link" :class="ui.link({ class: [props.ui?.link, item.ui?.link, item.class], active: active || item.active, disabled: !!item.disabled, level: level > 0 })">
234
- <ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" />
234
+ <ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" :trailing-trigger="!!slotProps.href" />
235
235
  </ULinkBase>
236
236
  </UTooltip>
237
237
  <ULinkBase v-else v-bind="slotProps" data-slot="link" :class="ui.link({ class: [props.ui?.link, item.ui?.link, item.class], active: active || item.active, disabled: !!item.disabled, level: props.orientation === 'horizontal' || level > 0 })">
238
- <ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" />
238
+ <ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" :trailing-trigger="!!slotProps.href" />
239
239
  </ULinkBase>
240
240
  </component>
241
241
 
@@ -67,7 +67,7 @@ export interface NavigationMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cu
67
67
  open?: boolean;
68
68
  onSelect?: (e: Event) => void;
69
69
  class?: any;
70
- ui?: Pick<NavigationMenu['slots'], 'item' | 'linkLeadingAvatarSize' | 'linkLeadingAvatar' | 'linkLeadingIcon' | 'linkLeadingChipSize' | 'linkLabel' | 'linkLabelExternalIcon' | 'linkTrailing' | 'linkTrailingBadgeSize' | 'linkTrailingBadge' | 'linkTrailingIcon' | 'label' | 'link' | 'content' | 'childList' | 'childLabel' | 'childItem' | 'childLink' | 'childLinkIcon' | 'childLinkWrapper' | 'childLinkLabel' | 'childLinkLabelExternalIcon' | 'childLinkDescription'>;
70
+ ui?: Partial<Pick<NavigationMenu['slots'], 'item' | 'linkLeadingAvatarSize' | 'linkLeadingAvatar' | 'linkLeadingIcon' | 'linkLeadingChipSize' | 'linkLabel' | 'linkLabelExternalIcon' | 'linkTrailing' | 'linkTrailingBadgeSize' | 'linkTrailingBadge' | 'linkTrailingIcon' | 'label' | 'link' | 'content' | 'childList' | 'childLabel' | 'childItem' | 'childLink' | 'childLinkIcon' | 'childLinkWrapper' | 'childLinkLabel' | 'childLinkLabelExternalIcon' | 'childLinkDescription'>>;
71
71
  [key: string]: any;
72
72
  }
73
73
  type SingleOrMultipleType = 'single' | 'multiple';
@@ -12,7 +12,7 @@ export interface PageAnchor extends Omit<LinkProps, 'custom'> {
12
12
  */
13
13
  icon?: IconProps['name'];
14
14
  class?: any;
15
- ui?: Pick<PageAnchors['slots'], 'item' | 'link' | 'linkLabel' | 'linkLabelExternalIcon' | 'linkLeading' | 'linkLeadingIcon'>;
15
+ ui?: Partial<Pick<PageAnchors['slots'], 'item' | 'link' | 'linkLabel' | 'linkLabelExternalIcon' | 'linkLeading' | 'linkLeadingIcon'>>;
16
16
  }
17
17
  export interface PageAnchorsProps<T extends PageAnchor = PageAnchor> {
18
18
  /**
@@ -12,7 +12,7 @@ export interface PageAnchor extends Omit<LinkProps, 'custom'> {
12
12
  */
13
13
  icon?: IconProps['name'];
14
14
  class?: any;
15
- ui?: Pick<PageAnchors['slots'], 'item' | 'link' | 'linkLabel' | 'linkLabelExternalIcon' | 'linkLeading' | 'linkLeadingIcon'>;
15
+ ui?: Partial<Pick<PageAnchors['slots'], 'item' | 'link' | 'linkLabel' | 'linkLabelExternalIcon' | 'linkLeading' | 'linkLeadingIcon'>>;
16
16
  }
17
17
  export interface PageAnchorsProps<T extends PageAnchor = PageAnchor> {
18
18
  /**
@@ -12,7 +12,7 @@ export interface PageLink extends Omit<LinkProps, 'custom'> {
12
12
  */
13
13
  icon?: IconProps['name'];
14
14
  class?: any;
15
- ui?: Pick<PageLinks['slots'], 'item' | 'link' | 'linkLabel' | 'linkLabelExternalIcon' | 'linkLeadingIcon'>;
15
+ ui?: Partial<Pick<PageLinks['slots'], 'item' | 'link' | 'linkLabel' | 'linkLabelExternalIcon' | 'linkLeadingIcon'>>;
16
16
  }
17
17
  export interface PageLinksProps<T extends PageLink = PageLink> {
18
18
  /**
@@ -12,7 +12,7 @@ export interface PageLink extends Omit<LinkProps, 'custom'> {
12
12
  */
13
13
  icon?: IconProps['name'];
14
14
  class?: any;
15
- ui?: Pick<PageLinks['slots'], 'item' | 'link' | 'linkLabel' | 'linkLabelExternalIcon' | 'linkLeadingIcon'>;
15
+ ui?: Partial<Pick<PageLinks['slots'], 'item' | 'link' | 'linkLabel' | 'linkLabelExternalIcon' | 'linkLeadingIcon'>>;
16
16
  }
17
17
  export interface PageLinksProps<T extends PageLink = PageLink> {
18
18
  /**
@@ -46,6 +46,7 @@ export interface PinInputProps<T extends PinInputType = 'text'> extends Pick<Pin
46
46
  export type PinInputEmits<T extends PinInputType = 'text'> = PinInputRootEmits<T> & {
47
47
  change: [event: Event];
48
48
  blur: [event: Event];
49
+ focus: [event: Event];
49
50
  };
50
51
  export interface PinInputSlots {
51
52
  separator?(props: {
@@ -58,6 +59,7 @@ declare const __VLS_export: <T extends PinInputType>(__VLS_props: NonNullable<Aw
58
59
  props: import("vue").PublicProps & __VLS_PrettifyLocal<PinInputProps<T> & {
59
60
  onBlur?: ((event: Event) => any) | undefined;
60
61
  onChange?: ((event: Event) => any) | undefined;
62
+ onFocus?: ((event: Event) => any) | undefined;
61
63
  "onUpdate:modelValue"?: ((value: [T] extends ["number"] ? number[] : string[]) => any) | undefined;
62
64
  onComplete?: ((value: [T] extends ["number"] ? number[] : string[]) => any) | undefined;
63
65
  }> & (typeof globalThis extends {
@@ -68,7 +70,7 @@ declare const __VLS_export: <T extends PinInputType>(__VLS_props: NonNullable<Aw
68
70
  }>) => void;
69
71
  attrs: any;
70
72
  slots: PinInputSlots;
71
- emit: ((evt: "blur", event: Event) => void) & ((evt: "change", event: Event) => void) & ((evt: "update:modelValue", value: [T] extends ["number"] ? number[] : string[]) => void) & ((evt: "complete", value: [T] extends ["number"] ? number[] : string[]) => void);
73
+ emit: ((evt: "blur", event: Event) => void) & ((evt: "change", event: Event) => void) & ((evt: "focus", event: Event) => void) & ((evt: "update:modelValue", value: [T] extends ["number"] ? number[] : string[]) => void) & ((evt: "complete", value: [T] extends ["number"] ? number[] : string[]) => void);
72
74
  }>) => import("vue").VNode & {
73
75
  __ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
74
76
  };
@@ -36,7 +36,7 @@ const _props = defineProps({
36
36
  required: { type: Boolean, required: false },
37
37
  type: { type: null, required: false, default: "text" }
38
38
  });
39
- const emits = defineEmits(["update:modelValue", "complete", "change", "blur"]);
39
+ const emits = defineEmits(["update:modelValue", "complete", "change", "blur", "focus"]);
40
40
  defineSlots();
41
41
  const props = useComponentProps("pinInput", _props);
42
42
  const appConfig = useAppConfig();
@@ -62,11 +62,19 @@ function onComplete(value) {
62
62
  emits("change", event);
63
63
  emitFormChange();
64
64
  }
65
- function onBlur(event) {
66
- if (!event.relatedTarget) {
67
- emits("blur", event);
68
- emitFormBlur();
65
+ function onFocusOut(event) {
66
+ if (event.relatedTarget && event.currentTarget.contains(event.relatedTarget)) {
67
+ return;
69
68
  }
69
+ emits("blur", event);
70
+ emitFormBlur();
71
+ }
72
+ function onFocusIn(event) {
73
+ if (event.relatedTarget && event.currentTarget.contains(event.relatedTarget)) {
74
+ return;
75
+ }
76
+ emits("focus", event);
77
+ emitFormFocus();
70
78
  }
71
79
  function autoFocus() {
72
80
  if (props.autofocus) {
@@ -111,6 +119,8 @@ defineExpose({
111
119
  :class="ui.root({ class: [props.ui?.root, props.class] })"
112
120
  @update:model-value="emitFormInput()"
113
121
  @complete="onComplete"
122
+ @focusout="onFocusOut"
123
+ @focusin="onFocusIn"
114
124
  >
115
125
  <template v-for="(ids, index) in looseToNumber(props.length)" :key="ids">
116
126
  <PinInputInput
@@ -119,8 +129,6 @@ defineExpose({
119
129
  data-slot="base"
120
130
  :class="ui.base({ class: props.ui?.base })"
121
131
  :disabled="disabled"
122
- @blur="onBlur"
123
- @focus="emitFormFocus"
124
132
  />
125
133
  <span
126
134
  v-if="shouldInsertSeparator(index)"
@@ -46,6 +46,7 @@ export interface PinInputProps<T extends PinInputType = 'text'> extends Pick<Pin
46
46
  export type PinInputEmits<T extends PinInputType = 'text'> = PinInputRootEmits<T> & {
47
47
  change: [event: Event];
48
48
  blur: [event: Event];
49
+ focus: [event: Event];
49
50
  };
50
51
  export interface PinInputSlots {
51
52
  separator?(props: {
@@ -58,6 +59,7 @@ declare const __VLS_export: <T extends PinInputType>(__VLS_props: NonNullable<Aw
58
59
  props: import("vue").PublicProps & __VLS_PrettifyLocal<PinInputProps<T> & {
59
60
  onBlur?: ((event: Event) => any) | undefined;
60
61
  onChange?: ((event: Event) => any) | undefined;
62
+ onFocus?: ((event: Event) => any) | undefined;
61
63
  "onUpdate:modelValue"?: ((value: [T] extends ["number"] ? number[] : string[]) => any) | undefined;
62
64
  onComplete?: ((value: [T] extends ["number"] ? number[] : string[]) => any) | undefined;
63
65
  }> & (typeof globalThis extends {
@@ -68,7 +70,7 @@ declare const __VLS_export: <T extends PinInputType>(__VLS_props: NonNullable<Aw
68
70
  }>) => void;
69
71
  attrs: any;
70
72
  slots: PinInputSlots;
71
- emit: ((evt: "blur", event: Event) => void) & ((evt: "change", event: Event) => void) & ((evt: "update:modelValue", value: [T] extends ["number"] ? number[] : string[]) => void) & ((evt: "complete", value: [T] extends ["number"] ? number[] : string[]) => void);
73
+ emit: ((evt: "blur", event: Event) => void) & ((evt: "change", event: Event) => void) & ((evt: "focus", event: Event) => void) & ((evt: "update:modelValue", value: [T] extends ["number"] ? number[] : string[]) => void) & ((evt: "complete", value: [T] extends ["number"] ? number[] : string[]) => void);
72
74
  }>) => import("vue").VNode & {
73
75
  __ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
74
76
  };
@@ -20,7 +20,7 @@ export interface ProgressGroupItem {
20
20
  color?: ProgressGroup['variants']['color'] | (string & {});
21
21
  slot?: string;
22
22
  class?: any;
23
- ui?: Pick<ProgressGroup['slots'], 'segment' | 'indicator' | 'item' | 'itemLeadingIcon' | 'itemLeadingDot' | 'itemLabel' | 'itemTrailing'>;
23
+ ui?: Partial<Pick<ProgressGroup['slots'], 'segment' | 'indicator' | 'item' | 'itemLeadingIcon' | 'itemLeadingDot' | 'itemLabel' | 'itemTrailing'>>;
24
24
  [key: string]: any;
25
25
  }
26
26
  export interface ProgressGroupProps<T extends ProgressGroupItem = ProgressGroupItem> {
@@ -20,7 +20,7 @@ export interface ProgressGroupItem {
20
20
  color?: ProgressGroup['variants']['color'] | (string & {});
21
21
  slot?: string;
22
22
  class?: any;
23
- ui?: Pick<ProgressGroup['slots'], 'segment' | 'indicator' | 'item' | 'itemLeadingIcon' | 'itemLeadingDot' | 'itemLabel' | 'itemTrailing'>;
23
+ ui?: Partial<Pick<ProgressGroup['slots'], 'segment' | 'indicator' | 'item' | 'itemLeadingIcon' | 'itemLeadingDot' | 'itemLabel' | 'itemTrailing'>>;
24
24
  [key: string]: any;
25
25
  }
26
26
  export interface ProgressGroupProps<T extends ProgressGroupItem = ProgressGroupItem> {
@@ -18,7 +18,7 @@ export type RadioGroupItem = RadioGroupValue | {
18
18
  */
19
19
  icon?: IconProps['name'];
20
20
  class?: any;
21
- ui?: Pick<RadioGroup['slots'], 'item' | 'container' | 'base' | 'indicator' | 'wrapper' | 'label' | 'icon' | 'description'>;
21
+ ui?: Partial<Pick<RadioGroup['slots'], 'item' | 'container' | 'base' | 'indicator' | 'wrapper' | 'label' | 'icon' | 'description'>>;
22
22
  [key: string]: any;
23
23
  };
24
24
  export interface RadioGroupProps<T extends RadioGroupItem[] = RadioGroupItem[], VK extends GetItemKeys<T> = 'value'> extends Pick<RadioGroupRootProps, 'disabled' | 'loop' | 'name' | 'required'> {
@@ -18,7 +18,7 @@ export type RadioGroupItem = RadioGroupValue | {
18
18
  */
19
19
  icon?: IconProps['name'];
20
20
  class?: any;
21
- ui?: Pick<RadioGroup['slots'], 'item' | 'container' | 'base' | 'indicator' | 'wrapper' | 'label' | 'icon' | 'description'>;
21
+ ui?: Partial<Pick<RadioGroup['slots'], 'item' | 'container' | 'base' | 'indicator' | 'wrapper' | 'label' | 'icon' | 'description'>>;
22
22
  [key: string]: any;
23
23
  };
24
24
  export interface RadioGroupProps<T extends RadioGroupItem[] = RadioGroupItem[], VK extends GetItemKeys<T> = 'value'> extends Pick<RadioGroupRootProps, 'disabled' | 'loop' | 'name' | 'required'> {
@@ -30,7 +30,7 @@ export type SelectItem = SelectValue | {
30
30
  disabled?: boolean;
31
31
  onSelect?: (e: Event) => void;
32
32
  class?: any;
33
- ui?: Pick<Select['slots'], 'label' | 'separator' | 'item' | 'itemLeadingIcon' | 'itemLeadingAvatarSize' | 'itemLeadingAvatar' | 'itemLeadingChipSize' | 'itemLeadingChip' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemTrailing' | 'itemTrailingIcon'>;
33
+ ui?: Partial<Pick<Select['slots'], 'label' | 'separator' | 'item' | 'itemLeadingIcon' | 'itemLeadingAvatarSize' | 'itemLeadingAvatar' | 'itemLeadingChipSize' | 'itemLeadingChip' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemTrailing' | 'itemTrailingIcon'>>;
34
34
  [key: string]: any;
35
35
  };
36
36
  type ExcludeItem = {
@@ -105,6 +105,8 @@ export interface SelectProps<T extends ArrayOrNested<SelectItem> = ArrayOrNested
105
105
  multiple?: M & boolean;
106
106
  /** Highlight the ring color like a focus state. */
107
107
  highlight?: boolean;
108
+ /** Keep the mobile text size on all breakpoints. */
109
+ fixed?: boolean;
108
110
  autofocus?: boolean;
109
111
  autofocusDelay?: number;
110
112
  class?: any;
@@ -40,6 +40,7 @@ const _props = defineProps({
40
40
  modelModifiers: { type: null, required: false },
41
41
  multiple: { type: Boolean, required: false },
42
42
  highlight: { type: Boolean, required: false },
43
+ fixed: { type: Boolean, required: false },
43
44
  autofocus: { type: Boolean, required: false },
44
45
  autofocusDelay: { type: Number, required: false, default: 0 },
45
46
  class: { type: null, required: false },
@@ -90,6 +91,7 @@ const ui = computed(() => tv({ extend: theme, ...appConfig.ui?.select || {} })({
90
91
  size: size.value,
91
92
  loading: props.loading,
92
93
  highlight: highlight.value,
94
+ fixed: props.fixed,
93
95
  leading: isLeading.value || !!props.avatar || !!slots.leading,
94
96
  trailing: isTrailing.value || !!slots.trailing,
95
97
  fieldGroup: orientation.value,
@@ -30,7 +30,7 @@ export type SelectItem = SelectValue | {
30
30
  disabled?: boolean;
31
31
  onSelect?: (e: Event) => void;
32
32
  class?: any;
33
- ui?: Pick<Select['slots'], 'label' | 'separator' | 'item' | 'itemLeadingIcon' | 'itemLeadingAvatarSize' | 'itemLeadingAvatar' | 'itemLeadingChipSize' | 'itemLeadingChip' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemTrailing' | 'itemTrailingIcon'>;
33
+ ui?: Partial<Pick<Select['slots'], 'label' | 'separator' | 'item' | 'itemLeadingIcon' | 'itemLeadingAvatarSize' | 'itemLeadingAvatar' | 'itemLeadingChipSize' | 'itemLeadingChip' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemTrailing' | 'itemTrailingIcon'>>;
34
34
  [key: string]: any;
35
35
  };
36
36
  type ExcludeItem = {
@@ -105,6 +105,8 @@ export interface SelectProps<T extends ArrayOrNested<SelectItem> = ArrayOrNested
105
105
  multiple?: M & boolean;
106
106
  /** Highlight the ring color like a focus state. */
107
107
  highlight?: boolean;
108
+ /** Keep the mobile text size on all breakpoints. */
109
+ fixed?: boolean;
108
110
  autofocus?: boolean;
109
111
  autofocusDelay?: number;
110
112
  class?: any;
@@ -32,7 +32,7 @@ export type SelectMenuItem = SelectMenuValue | {
32
32
  disabled?: boolean;
33
33
  onSelect?: (e: Event) => void;
34
34
  class?: any;
35
- ui?: Pick<SelectMenu['slots'], 'label' | 'separator' | 'item' | 'itemLeadingIcon' | 'itemLeadingAvatarSize' | 'itemLeadingAvatar' | 'itemLeadingChipSize' | 'itemLeadingChip' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemTrailing' | 'itemTrailingIcon'>;
35
+ ui?: Partial<Pick<SelectMenu['slots'], 'label' | 'separator' | 'item' | 'itemLeadingIcon' | 'itemLeadingAvatarSize' | 'itemLeadingAvatar' | 'itemLeadingChipSize' | 'itemLeadingChip' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemTrailing' | 'itemTrailingIcon'>>;
36
36
  [key: string]: any;
37
37
  };
38
38
  type ExcludeItem = {
@@ -47,6 +47,7 @@ export interface SelectMenuProps<T extends ArrayOrNested<SelectMenuItem> = Array
47
47
  * Whether to display the search input or not.
48
48
  * Can be an object to pass additional props to the input.
49
49
  * `{ placeholder: 'Search...', variant: 'none' }`{lang="ts-type"}
50
+ * Set `autofocus: false` to prevent the search input from being focused when the menu opens (e.g. to avoid opening the virtual keyboard on touch devices).
50
51
  * @defaultValue true
51
52
  */
52
53
  searchInput?: boolean | Omit<InputProps, 'modelValue' | 'defaultValue'>;
@@ -145,6 +146,8 @@ export interface SelectMenuProps<T extends ArrayOrNested<SelectMenuItem> = Array
145
146
  multiple?: M & boolean;
146
147
  /** Highlight the ring color like a focus state. */
147
148
  highlight?: boolean;
149
+ /** Keep the mobile text size on all breakpoints. */
150
+ fixed?: boolean;
148
151
  /**
149
152
  * Determines if custom user input that does not exist in options can be added.
150
153
  * @defaultValue false
@@ -50,6 +50,7 @@ const _props = defineProps({
50
50
  modelModifiers: { type: null, required: false },
51
51
  multiple: { type: Boolean, required: false },
52
52
  highlight: { type: Boolean, required: false },
53
+ fixed: { type: Boolean, required: false },
53
54
  createItem: { type: [Boolean, String, Object], required: false },
54
55
  filterFields: { type: Array, required: false },
55
56
  ignoreFilter: { type: Boolean, required: false },
@@ -92,7 +93,7 @@ const virtualizerProps = toRef(() => {
92
93
  estimateSize: getEstimateSize(filteredItems.value, size.value ?? "md", props.descriptionKey, !!slots["item-description"])
93
94
  });
94
95
  });
95
- const searchInputProps = toRef(() => defu(props.searchInput, { placeholder: t("selectMenu.search"), variant: "none" }));
96
+ const searchInputProps = toRef(() => defu(props.searchInput, { placeholder: t("selectMenu.search"), variant: "none", fixed: props.fixed }));
96
97
  const { emitFormBlur, emitFormFocus, emitFormInput, emitFormChange, size: formFieldSize, color: formFieldColor, id, name, highlight: formFieldHighlight, disabled: formFieldDisabled, ariaAttrs } = useFormField(_props);
97
98
  const { orientation, size: fieldGroupSize } = useFieldGroup(_props);
98
99
  const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(computed(() => ({
@@ -127,6 +128,7 @@ const ui = computed(() => tv({ extend: theme, ...appConfig.ui?.selectMenu || {}
127
128
  size: size.value,
128
129
  loading: props.loading,
129
130
  highlight: highlight.value,
131
+ fixed: props.fixed,
130
132
  leading: isLeading.value || !!props.avatar || !!slots.leading,
131
133
  trailing: isTrailing.value || !!slots.trailing,
132
134
  fieldGroup: orientation.value,
@@ -235,6 +237,14 @@ function onUpdateOpen(value) {
235
237
  clearTimeout(timeoutId);
236
238
  }
237
239
  }
240
+ function onTriggerKeydown(e) {
241
+ if (isOpen.value) {
242
+ return;
243
+ }
244
+ const trigger = e.currentTarget;
245
+ e.preventDefault();
246
+ trigger.click();
247
+ }
238
248
  function onCreate(e) {
239
249
  e.preventDefault();
240
250
  e.stopPropagation();
@@ -260,8 +270,16 @@ function isModelValueEmpty(modelValue) {
260
270
  return modelValue === void 0 || modelValue === null || modelValue === "";
261
271
  }
262
272
  function onClear() {
273
+ if (disabled.value) {
274
+ return;
275
+ }
263
276
  emits("clear");
264
277
  }
278
+ function onMountAutoFocus(event) {
279
+ if (searchInputProps.value.autofocus === false) {
280
+ event.preventDefault();
281
+ }
282
+ }
265
283
  const viewportRef = useTemplateRef("viewportRef");
266
284
  const comboboxRootRef = useTemplateRef("comboboxRootRef");
267
285
  watch(() => props.items, async () => {
@@ -366,7 +384,9 @@ defineExpose({
366
384
  data-slot="base"
367
385
  :class="ui.base({ class: [props.ui?.base, props.class] })"
368
386
  tabindex="0"
369
- v-bind="{ ...$attrs, ...ariaAttrs }"
387
+ v-bind="{ 'aria-label': void 0, ...$attrs, ...ariaAttrs }"
388
+ @keydown.down="onTriggerKeydown"
389
+ @keydown.up="onTriggerKeydown"
370
390
  >
371
391
  <span v-if="isLeading || !!props.avatar || !!slots.leading" data-slot="leading" :class="ui.leading({ class: props.ui?.leading })">
372
392
  <slot name="leading" :model-value="modelValue" :open="open" :ui="ui">
@@ -388,7 +408,7 @@ defineExpose({
388
408
 
389
409
  <span v-if="isTrailing || !!slots.trailing || !!props.clear" data-slot="trailing" :class="ui.trailing({ class: props.ui?.trailing })">
390
410
  <slot name="trailing" :model-value="modelValue" :open="open" :ui="ui">
391
- <ComboboxCancel v-if="!!props.clear && !isModelValueEmpty(modelValue)" as-child>
411
+ <ComboboxCancel v-if="!!props.clear && !disabled && !isModelValueEmpty(modelValue)" as-child>
392
412
  <UButton
393
413
  as="span"
394
414
  :icon="props.clearIcon || appConfig.ui.icons.close"
@@ -412,7 +432,7 @@ defineExpose({
412
432
  <ComboboxPortal v-bind="portalProps">
413
433
  <FieldGroupReset>
414
434
  <ComboboxContent data-slot="content" :class="ui.content({ class: props.ui?.content })" v-bind="contentProps">
415
- <FocusScope trapped data-slot="focusScope" :class="ui.focusScope({ class: props.ui?.focusScope })">
435
+ <FocusScope trapped data-slot="focusScope" :class="ui.focusScope({ class: props.ui?.focusScope })" @mount-auto-focus="onMountAutoFocus">
416
436
  <slot name="content-top" />
417
437
 
418
438
  <ComboboxInput v-if="!!props.searchInput" v-model="searchTerm" :display-value="() => searchTerm" as-child>
@@ -32,7 +32,7 @@ export type SelectMenuItem = SelectMenuValue | {
32
32
  disabled?: boolean;
33
33
  onSelect?: (e: Event) => void;
34
34
  class?: any;
35
- ui?: Pick<SelectMenu['slots'], 'label' | 'separator' | 'item' | 'itemLeadingIcon' | 'itemLeadingAvatarSize' | 'itemLeadingAvatar' | 'itemLeadingChipSize' | 'itemLeadingChip' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemTrailing' | 'itemTrailingIcon'>;
35
+ ui?: Partial<Pick<SelectMenu['slots'], 'label' | 'separator' | 'item' | 'itemLeadingIcon' | 'itemLeadingAvatarSize' | 'itemLeadingAvatar' | 'itemLeadingChipSize' | 'itemLeadingChip' | 'itemWrapper' | 'itemLabel' | 'itemDescription' | 'itemTrailing' | 'itemTrailingIcon'>>;
36
36
  [key: string]: any;
37
37
  };
38
38
  type ExcludeItem = {
@@ -47,6 +47,7 @@ export interface SelectMenuProps<T extends ArrayOrNested<SelectMenuItem> = Array
47
47
  * Whether to display the search input or not.
48
48
  * Can be an object to pass additional props to the input.
49
49
  * `{ placeholder: 'Search...', variant: 'none' }`{lang="ts-type"}
50
+ * Set `autofocus: false` to prevent the search input from being focused when the menu opens (e.g. to avoid opening the virtual keyboard on touch devices).
50
51
  * @defaultValue true
51
52
  */
52
53
  searchInput?: boolean | Omit<InputProps, 'modelValue' | 'defaultValue'>;
@@ -145,6 +146,8 @@ export interface SelectMenuProps<T extends ArrayOrNested<SelectMenuItem> = Array
145
146
  multiple?: M & boolean;
146
147
  /** Highlight the ring color like a focus state. */
147
148
  highlight?: boolean;
149
+ /** Keep the mobile text size on all breakpoints. */
150
+ fixed?: boolean;
148
151
  /**
149
152
  * Determines if custom user input that does not exist in options can be added.
150
153
  * @defaultValue false