@nuxt/ui 3.0.0-alpha.10 → 3.0.0-alpha.12

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 (191) hide show
  1. package/.nuxt/ui/alert.ts +76 -59
  2. package/.nuxt/ui/avatar-group.ts +13 -1
  3. package/.nuxt/ui/avatar.ts +14 -2
  4. package/.nuxt/ui/badge.ts +102 -65
  5. package/.nuxt/ui/button-group.ts +13 -0
  6. package/.nuxt/ui/button.ts +124 -93
  7. package/.nuxt/ui/calendar.ts +20 -2
  8. package/.nuxt/ui/carousel.ts +5 -3
  9. package/.nuxt/ui/checkbox.ts +27 -9
  10. package/.nuxt/ui/chip.ts +36 -7
  11. package/.nuxt/ui/color-picker.ts +9 -1
  12. package/.nuxt/ui/context-menu.ts +31 -13
  13. package/.nuxt/ui/drawer.ts +11 -4
  14. package/.nuxt/ui/dropdown-menu.ts +31 -13
  15. package/.nuxt/ui/form-field.ts +9 -1
  16. package/.nuxt/ui/input-menu.ts +110 -75
  17. package/.nuxt/ui/input-number.ts +82 -51
  18. package/.nuxt/ui/input.ts +88 -53
  19. package/.nuxt/ui/kbd.ts +14 -2
  20. package/.nuxt/ui/navigation-menu.ts +193 -79
  21. package/.nuxt/ui/pin-input.ts +57 -31
  22. package/.nuxt/ui/popover.ts +1 -1
  23. package/.nuxt/ui/progress.ts +93 -54
  24. package/.nuxt/ui/radio-group.ts +25 -2
  25. package/.nuxt/ui/select-menu.ts +89 -54
  26. package/.nuxt/ui/select.ts +89 -54
  27. package/.nuxt/ui/separator.ts +52 -23
  28. package/.nuxt/ui/slideover.ts +11 -4
  29. package/.nuxt/ui/slider.ts +45 -22
  30. package/.nuxt/ui/stepper.ts +45 -23
  31. package/.nuxt/ui/switch.ts +20 -2
  32. package/.nuxt/ui/table.ts +30 -13
  33. package/.nuxt/ui/tabs.ts +67 -39
  34. package/.nuxt/ui/textarea.ts +88 -53
  35. package/.nuxt/ui/toast.ts +11 -1
  36. package/.nuxt/ui/toaster.ts +27 -11
  37. package/.nuxt/ui/tooltip.ts +1 -1
  38. package/README.md +8 -1
  39. package/cli/commands/make/component.mjs +12 -0
  40. package/cli/package.json +2 -2
  41. package/cli/templates.mjs +21 -16
  42. package/dist/chunks/component-meta.cjs +2590 -2511
  43. package/dist/chunks/component-meta.mjs +2590 -2511
  44. package/dist/client/devtools/200.html +1 -1
  45. package/dist/client/devtools/404.html +1 -1
  46. package/dist/client/devtools/_nuxt/CgoQwOTV.js +57 -0
  47. package/dist/client/devtools/_nuxt/DLmPhbaq.js +1 -0
  48. package/dist/client/devtools/_nuxt/DYuJNiSW.js +1 -0
  49. package/dist/client/devtools/_nuxt/builds/latest.json +1 -1
  50. package/dist/client/devtools/_nuxt/builds/meta/3184ffe2-7577-4441-af98-fe6e81c30e3a.json +1 -0
  51. package/dist/client/devtools/_nuxt/entry.ChJczkiN.css +1 -0
  52. package/dist/client/devtools/_nuxt/error-404.DyY3rYaL.css +1 -0
  53. package/dist/client/devtools/_nuxt/error-500.D_1PCaJh.css +1 -0
  54. package/dist/client/devtools/index.html +1 -1
  55. package/dist/module.cjs +1 -1
  56. package/dist/module.json +1 -1
  57. package/dist/module.mjs +1 -1
  58. package/dist/runtime/components/Accordion.vue +4 -3
  59. package/dist/runtime/components/Alert.vue +5 -4
  60. package/dist/runtime/components/Avatar.vue +35 -13
  61. package/dist/runtime/components/AvatarGroup.vue +4 -3
  62. package/dist/runtime/components/Badge.vue +4 -3
  63. package/dist/runtime/components/Breadcrumb.vue +3 -3
  64. package/dist/runtime/components/Button.vue +4 -3
  65. package/dist/runtime/components/ButtonGroup.vue +4 -3
  66. package/dist/runtime/components/Calendar.vue +6 -3
  67. package/dist/runtime/components/Card.vue +3 -3
  68. package/dist/runtime/components/Carousel.vue +4 -3
  69. package/dist/runtime/components/Checkbox.vue +6 -5
  70. package/dist/runtime/components/Chip.vue +5 -4
  71. package/dist/runtime/components/Collapsible.vue +6 -4
  72. package/dist/runtime/components/ColorPicker.vue +48 -16
  73. package/dist/runtime/components/CommandPalette.vue +14 -7
  74. package/dist/runtime/components/Container.vue +3 -3
  75. package/dist/runtime/components/ContextMenu.vue +4 -3
  76. package/dist/runtime/components/ContextMenuContent.vue +4 -4
  77. package/dist/runtime/components/Drawer.vue +9 -4
  78. package/dist/runtime/components/DropdownMenu.vue +4 -3
  79. package/dist/runtime/components/DropdownMenuContent.vue +4 -4
  80. package/dist/runtime/components/Form.vue +59 -29
  81. package/dist/runtime/components/FormField.vue +14 -7
  82. package/dist/runtime/components/Input.vue +7 -5
  83. package/dist/runtime/components/InputMenu.vue +19 -7
  84. package/dist/runtime/components/InputNumber.vue +9 -5
  85. package/dist/runtime/components/Kbd.vue +4 -3
  86. package/dist/runtime/components/Link.vue +3 -3
  87. package/dist/runtime/components/Modal.vue +10 -15
  88. package/dist/runtime/components/NavigationMenu.vue +97 -69
  89. package/dist/runtime/components/Pagination.vue +3 -3
  90. package/dist/runtime/components/PinInput.vue +9 -7
  91. package/dist/runtime/components/Popover.vue +9 -8
  92. package/dist/runtime/components/Progress.vue +6 -5
  93. package/dist/runtime/components/RadioGroup.vue +6 -5
  94. package/dist/runtime/components/Select.vue +12 -9
  95. package/dist/runtime/components/SelectMenu.vue +25 -8
  96. package/dist/runtime/components/Separator.vue +4 -3
  97. package/dist/runtime/components/Skeleton.vue +3 -3
  98. package/dist/runtime/components/Slideover.vue +10 -14
  99. package/dist/runtime/components/Slider.vue +6 -5
  100. package/dist/runtime/components/Stepper.vue +8 -6
  101. package/dist/runtime/components/Switch.vue +6 -5
  102. package/dist/runtime/components/Table.vue +4 -3
  103. package/dist/runtime/components/Tabs.vue +5 -3
  104. package/dist/runtime/components/Textarea.vue +7 -5
  105. package/dist/runtime/components/Toast.vue +4 -3
  106. package/dist/runtime/components/Toaster.vue +4 -3
  107. package/dist/runtime/components/Tooltip.vue +3 -3
  108. package/dist/runtime/composables/defineShortcuts.js +1 -1
  109. package/dist/runtime/composables/useFormField.d.ts +7 -3
  110. package/dist/runtime/composables/useFormField.js +18 -8
  111. package/dist/runtime/composables/useToast.d.ts +1 -1
  112. package/dist/runtime/composables/useToast.js +21 -7
  113. package/dist/runtime/index.css +1 -1
  114. package/dist/runtime/locale/ar.js +3 -1
  115. package/dist/runtime/locale/cs.js +3 -1
  116. package/dist/runtime/locale/da.js +54 -0
  117. package/dist/runtime/locale/de.js +3 -1
  118. package/dist/runtime/locale/el.js +54 -0
  119. package/dist/runtime/locale/en.js +3 -1
  120. package/dist/runtime/locale/es.js +5 -3
  121. package/dist/runtime/locale/et.d.ts +2 -0
  122. package/dist/runtime/locale/et.js +54 -0
  123. package/dist/runtime/locale/fa_ir.js +3 -1
  124. package/dist/runtime/locale/fi.d.ts +2 -0
  125. package/dist/runtime/locale/fi.js +54 -0
  126. package/dist/runtime/locale/fr.js +3 -1
  127. package/dist/runtime/locale/he.d.ts +2 -0
  128. package/dist/runtime/locale/he.js +53 -0
  129. package/dist/runtime/locale/hi.d.ts +2 -0
  130. package/dist/runtime/locale/hi.js +54 -0
  131. package/dist/runtime/locale/hu.d.ts +2 -0
  132. package/dist/runtime/locale/hu.js +54 -0
  133. package/dist/runtime/locale/id.d.ts +2 -0
  134. package/dist/runtime/locale/id.js +54 -0
  135. package/dist/runtime/locale/index.d.ts +14 -2
  136. package/dist/runtime/locale/index.js +14 -2
  137. package/dist/runtime/locale/it.js +3 -1
  138. package/dist/runtime/locale/ja.js +3 -1
  139. package/dist/runtime/locale/km.d.ts +2 -0
  140. package/dist/runtime/locale/km.js +54 -0
  141. package/dist/runtime/locale/ko.js +3 -1
  142. package/dist/runtime/locale/nb_no.d.ts +2 -0
  143. package/dist/runtime/locale/nb_no.js +54 -0
  144. package/dist/runtime/locale/nl.js +3 -1
  145. package/dist/runtime/locale/pl.js +3 -1
  146. package/dist/runtime/locale/pt.js +3 -1
  147. package/dist/runtime/locale/pt_br.js +3 -1
  148. package/dist/runtime/locale/ru.js +3 -1
  149. package/dist/runtime/locale/sk.js +3 -1
  150. package/dist/runtime/locale/sv.d.ts +2 -0
  151. package/dist/runtime/locale/sv.js +54 -0
  152. package/dist/runtime/locale/th.d.ts +2 -0
  153. package/dist/runtime/locale/th.js +54 -0
  154. package/dist/runtime/locale/tr.js +3 -1
  155. package/dist/runtime/locale/uk.d.ts +2 -0
  156. package/dist/runtime/locale/uk.js +54 -0
  157. package/dist/runtime/locale/vi.d.ts +2 -0
  158. package/dist/runtime/locale/vi.js +54 -0
  159. package/dist/runtime/locale/zh_cn.d.ts +2 -0
  160. package/dist/runtime/locale/{zh_hans.js → zh_cn.js} +4 -2
  161. package/dist/runtime/locale/zh_tw.d.ts +2 -0
  162. package/dist/runtime/locale/{zh_hant.js → zh_tw.js} +12 -10
  163. package/dist/runtime/plugins/colors.js +7 -4
  164. package/dist/runtime/types/form.d.ts +23 -14
  165. package/dist/runtime/types/form.js +2 -2
  166. package/dist/runtime/types/locale.d.ts +2 -0
  167. package/dist/runtime/utils/form.d.ts +2 -2
  168. package/dist/runtime/utils/form.js +5 -7
  169. package/dist/runtime/utils/tv.d.ts +1 -0
  170. package/dist/runtime/utils/tv.js +4 -0
  171. package/dist/runtime/vue/components/Icon.vue +2 -2
  172. package/dist/runtime/vue/components/Link.vue +2 -2
  173. package/dist/runtime/vue/plugins/head.d.ts +1 -1
  174. package/dist/runtime/vue/plugins/head.js +3 -3
  175. package/dist/shared/{ui.D_93iuwH.mjs → ui.CENZ17mM.mjs} +131 -48
  176. package/dist/shared/{ui.CwmIkD_Y.cjs → ui.ClMXjgLK.cjs} +131 -48
  177. package/dist/unplugin.cjs +5 -5
  178. package/dist/unplugin.mjs +5 -5
  179. package/dist/vite.cjs +1 -1
  180. package/dist/vite.mjs +1 -1
  181. package/package.json +55 -51
  182. package/dist/client/devtools/_nuxt/B1fTd_Rf.js +0 -1
  183. package/dist/client/devtools/_nuxt/CSUxkedg.js +0 -1
  184. package/dist/client/devtools/_nuxt/CbRTGpb7.js +0 -1
  185. package/dist/client/devtools/_nuxt/YQTlxbcT.js +0 -53
  186. package/dist/client/devtools/_nuxt/builds/meta/b3465abc-0a92-4ee7-ba6b-e5174891fcc0.json +0 -1
  187. package/dist/client/devtools/_nuxt/entry.DXXceQev.css +0 -1
  188. package/dist/client/devtools/_nuxt/error-404.BJpkZFyY.css +0 -1
  189. package/dist/client/devtools/_nuxt/error-500.D3i9VDlt.css +0 -1
  190. /package/dist/runtime/locale/{zh_hans.d.ts → da.d.ts} +0 -0
  191. /package/dist/runtime/locale/{zh_hant.d.ts → el.d.ts} +0 -0
@@ -1,28 +1,39 @@
1
1
  <script lang="ts">
2
- import { tv, type VariantProps } from 'tailwind-variants'
2
+ import type { VariantProps } from 'tailwind-variants'
3
3
  import type { NavigationMenuRootProps, NavigationMenuRootEmits, NavigationMenuContentProps, CollapsibleRootProps } from 'reka-ui'
4
4
  import type { AppConfig } from '@nuxt/schema'
5
5
  import _appConfig from '#build/app.config'
6
6
  import theme from '#build/ui/navigation-menu'
7
7
  import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
8
+ import { tv } from '../utils/tv'
8
9
  import type { AvatarProps, BadgeProps, LinkProps } from '../types'
9
10
  import type { DynamicSlots, MaybeArrayOfArray, MaybeArrayOfArrayItem, PartialString } from '../types/utils'
10
11
 
11
- const appConfig = _appConfig as AppConfig & { ui: { navigationMenu: Partial<typeof theme> } }
12
+ const appConfigNavigationMenu = _appConfig as AppConfig & { ui: { navigationMenu: Partial<typeof theme> } }
12
13
 
13
- const navigationMenu = tv({ extend: tv(theme), ...(appConfig.ui?.navigationMenu || {}) })
14
+ const navigationMenu = tv({ extend: tv(theme), ...(appConfigNavigationMenu.ui?.navigationMenu || {}) })
14
15
 
15
- export interface NavigationMenuChildItem extends Omit<NavigationMenuItem, 'children'> {
16
+ export interface NavigationMenuChildItem extends Omit<NavigationMenuItem, 'children' | 'type'> {
16
17
  /** Description is only used when `orientation` is `horizontal`. */
17
18
  description?: string
18
19
  }
19
20
 
20
- export interface NavigationMenuItem extends Omit<LinkProps, 'raw' | 'custom'>, Pick<CollapsibleRootProps, 'defaultOpen' | 'open'> {
21
+ export interface NavigationMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'>, Pick<CollapsibleRootProps, 'defaultOpen' | 'open'> {
21
22
  label?: string
22
23
  icon?: string
23
24
  avatar?: AvatarProps
25
+ /**
26
+ * Display a badge on the item.
27
+ * `{ size: 'sm', color: 'neutral', variant: 'outline' }`{lang="ts-type"}
28
+ */
24
29
  badge?: string | number | BadgeProps
25
30
  trailingIcon?: string
31
+ /**
32
+ * The type of the item.
33
+ * The `label` type only works on `vertical` orientation.
34
+ * @defaultValue 'link'
35
+ */
36
+ type?: 'label' | 'link'
26
37
  slot?: string
27
38
  value?: string
28
39
  children?: NavigationMenuChildItem[]
@@ -50,11 +61,23 @@ export interface NavigationMenuProps<T> extends Pick<NavigationMenuRootProps, 'm
50
61
  * @defaultValue 'horizontal'
51
62
  */
52
63
  orientation?: NavigationMenuRootProps['orientation']
64
+ /**
65
+ * Collapse the navigation menu to only show icons.
66
+ * Only works when `orientation` is `vertical`.
67
+ * @defaultValue false
68
+ */
69
+ collapsed?: boolean
53
70
  /** Display a line next to the active item. */
54
71
  highlight?: boolean
55
72
  highlightColor?: NavigationMenuVariants['highlightColor']
56
73
  /** The content of the menu. */
57
74
  content?: Omit<NavigationMenuContentProps, 'as' | 'asChild' | 'forceMount'>
75
+ /**
76
+ * The orientation of the content.
77
+ * Only works when `orientation` is `horizontal`.
78
+ * @defaultValue 'horizontal'
79
+ */
80
+ contentOrientation?: NavigationMenuVariants['contentOrientation']
58
81
  /**
59
82
  * Display an arrow alongside the menu.
60
83
  * @defaultValue false
@@ -125,6 +148,7 @@ extendDevtoolsMeta({
125
148
  import { computed, toRef } from 'vue'
126
149
  import { NavigationMenuRoot, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuIndicator, NavigationMenuViewport, useForwardPropsEmits } from 'reka-ui'
127
150
  import { createReusableTemplate } from '@vueuse/core'
151
+ import { useAppConfig } from '#imports'
128
152
  import { get } from '../utils'
129
153
  import { pickLinkProps } from '../utils/link'
130
154
  import ULinkBase from './LinkBase.vue'
@@ -136,9 +160,10 @@ import UCollapsible from './Collapsible.vue'
136
160
 
137
161
  const props = withDefaults(defineProps<NavigationMenuProps<I>>(), {
138
162
  orientation: 'horizontal',
163
+ contentOrientation: 'horizontal',
139
164
  delayDuration: 0,
140
- labelKey: 'label',
141
- unmountOnHide: true
165
+ unmountOnHide: true,
166
+ labelKey: 'label'
142
167
  })
143
168
  const emits = defineEmits<NavigationMenuEmits>()
144
169
  const slots = defineSlots<NavigationMenuSlots<T>>()
@@ -158,10 +183,14 @@ const rootProps = useForwardPropsEmits(computed(() => ({
158
183
 
159
184
  const contentProps = toRef(() => props.content)
160
185
 
161
- const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: NavigationMenuItem, active?: boolean, index: number }>()
186
+ const appConfig = useAppConfig()
187
+ const [DefineLinkTemplate, ReuseLinkTemplate] = createReusableTemplate<{ item: NavigationMenuItem, index: number, active?: boolean }>()
188
+ const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: NavigationMenuItem, index: number }>()
162
189
 
163
190
  const ui = computed(() => navigationMenu({
164
191
  orientation: props.orientation,
192
+ contentOrientation: props.contentOrientation,
193
+ collapsed: props.collapsed,
165
194
  color: props.color,
166
195
  variant: props.variant,
167
196
  highlight: props.highlight,
@@ -172,14 +201,17 @@ const lists = computed(() => props.items?.length ? (Array.isArray(props.items[0]
172
201
  </script>
173
202
 
174
203
  <template>
175
- <DefineItemTemplate v-slot="{ item, active, index }">
204
+ <DefineLinkTemplate v-slot="{ item, active, index }">
176
205
  <slot :name="item.slot || 'item'" :item="(item as T)" :index="index">
177
206
  <slot :name="item.slot ? `${item.slot}-leading` : 'item-leading'" :item="(item as T)" :active="active" :index="index">
178
207
  <UAvatar v-if="item.avatar" :size="((props.ui?.linkLeadingAvatarSize || ui.linkLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.linkLeadingAvatar({ class: props.ui?.linkLeadingAvatar, active, disabled: !!item.disabled })" />
179
208
  <UIcon v-else-if="item.icon" :name="item.icon" :class="ui.linkLeadingIcon({ class: props.ui?.linkLeadingIcon, active, disabled: !!item.disabled })" />
180
209
  </slot>
181
210
 
182
- <span v-if="get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label` : 'item-label']" :class="ui.linkLabel({ class: props.ui?.linkLabel })">
211
+ <span
212
+ v-if="(!collapsed || orientation !== 'vertical') && (get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label` : 'item-label'])"
213
+ :class="ui.linkLabel({ class: props.ui?.linkLabel })"
214
+ >
183
215
  <slot :name="item.slot ? `${item.slot}-label` : 'item-label'" :item="(item as T)" :active="active" :index="index">
184
216
  {{ get(item, props.labelKey as string) }}
185
217
  </slot>
@@ -187,7 +219,7 @@ const lists = computed(() => props.items?.length ? (Array.isArray(props.items[0]
187
219
  <UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.linkLabelExternalIcon({ class: props.ui?.linkLabelExternalIcon, active })" />
188
220
  </span>
189
221
 
190
- <span v-if="item.badge || (orientation === 'horizontal' && (item.children?.length || !!slots[item.slot ? `${item.slot}-content` : 'item-content'])) || (orientation === 'vertical' && item.children?.length) || item.trailingIcon || !!slots[item.slot ? `${item.slot}-trailing` : 'item-trailing']" :class="ui.linkTrailing({ class: props.ui?.linkTrailing })">
222
+ <span v-if="(!collapsed || orientation !== 'vertical') && (item.badge || (orientation === 'horizontal' && (item.children?.length || !!slots[item.slot ? `${item.slot}-content` : 'item-content'])) || (orientation === 'vertical' && item.children?.length) || item.trailingIcon || !!slots[item.slot ? `${item.slot}-trailing` : 'item-trailing'])" :class="ui.linkTrailing({ class: props.ui?.linkTrailing })">
191
223
  <slot :name="item.slot ? `${item.slot}-trailing` : 'item-trailing'" :item="(item as T)" :active="active" :index="index">
192
224
  <UBadge
193
225
  v-if="item.badge"
@@ -203,77 +235,73 @@ const lists = computed(() => props.items?.length ? (Array.isArray(props.items[0]
203
235
  </slot>
204
236
  </span>
205
237
  </slot>
206
- </DefineItemTemplate>
238
+ </DefineLinkTemplate>
207
239
 
208
- <NavigationMenuRoot v-bind="rootProps" :class="ui.root({ class: [props.class, props.ui?.root] })">
209
- <template v-for="(list, listIndex) in lists" :key="`list-${listIndex}`">
210
- <NavigationMenuList :class="ui.list({ class: props.ui?.list })">
240
+ <DefineItemTemplate v-slot="{ item, index }">
241
+ <component
242
+ :is="(orientation === 'vertical' && item.children?.length) ? UCollapsible : NavigationMenuItem"
243
+ as="li"
244
+ :value="item.value || String(index)"
245
+ :default-open="item.defaultOpen"
246
+ :unmount-on-hide="(orientation === 'vertical' && item.children?.length) ? unmountOnHide : undefined"
247
+ :open="item.open"
248
+ >
249
+ <div v-if="orientation === 'vertical' && item.type === 'label'" :class="ui.label({ class: props.ui?.label })">
250
+ <ReuseLinkTemplate :item="(item as T)" :index="index" />
251
+ </div>
252
+ <ULink v-else-if="item.type !== 'label'" v-slot="{ active, ...slotProps }" v-bind="(orientation === 'vertical' && item.children?.length) ? {} : pickLinkProps(item as Omit<NavigationMenuItem, 'type'>)" custom>
211
253
  <component
212
- :is="(item.children?.length && orientation === 'vertical') ? UCollapsible : NavigationMenuItem"
213
- v-for="(item, index) in list"
214
- :key="`list-${listIndex}-${index}`"
215
- as="li"
216
- :value="item.value || String(index)"
217
- :default-open="item.defaultOpen"
218
- :unmount-on-hide="(item.children?.length && orientation === 'vertical') ? unmountOnHide : undefined"
219
- :open="item.open"
220
- :class="ui.item({ class: props.ui?.item })"
254
+ :is="(orientation === 'horizontal' && (item.children?.length || !!slots[item.slot ? `${item.slot}-content` : 'item-content'])) ? NavigationMenuTrigger : NavigationMenuLink"
255
+ as-child
256
+ :active="active"
257
+ :disabled="item.disabled"
258
+ @select="item.onSelect"
221
259
  >
222
- <ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item)" custom>
223
- <component
224
- :is="(orientation === 'horizontal' && (item.children?.length || !!slots[item.slot ? `${item.slot}-content` : 'item-content'])) ? NavigationMenuTrigger : NavigationMenuLink"
225
- as-child
226
- :active="active"
227
- :disabled="item.disabled"
228
- @select="item.onSelect"
229
- >
230
- <ULinkBase v-bind="slotProps" :class="ui.link({ class: [props.ui?.link, item.class], active, disabled: !!item.disabled })">
231
- <ReuseItemTemplate :item="(item as T)" :active="active" :index="index" />
232
- </ULinkBase>
233
- </component>
234
-
235
- <NavigationMenuContent v-if="orientation === 'horizontal' && (item.children?.length || !!slots[item.slot ? `${item.slot}-content` : 'item-content'])" v-bind="contentProps" :class="ui.content({ class: props.ui?.content })">
236
- <slot :name="item.slot ? `${item.slot}-content` : 'item-content'" :item="(item as T)" :active="active" :index="index">
237
- <ul :class="ui.childList({ class: props.ui?.childList })">
238
- <li v-for="(childItem, childIndex) in item.children" :key="childIndex" :class="ui.childItem({ class: props.ui?.childItem })">
239
- <ULink v-slot="{ active: childActive, ...childSlotProps }" v-bind="pickLinkProps(childItem)" custom>
240
- <NavigationMenuLink as-child :active="childActive" @select="childItem.onSelect">
241
- <ULinkBase v-bind="childSlotProps" :class="ui.childLink({ class: [props.ui?.childLink, childItem.class], active: childActive })">
242
- <UIcon v-if="childItem.icon" :name="childItem.icon" :class="ui.childLinkIcon({ class: props.ui?.childLinkIcon, active: childActive })" />
243
-
244
- <div :class="ui.childLinkWrapper({ class: props.ui?.childLinkWrapper })">
245
- <p :class="ui.childLinkLabel({ class: props.ui?.childLinkLabel, active: childActive })">
246
- {{ get(childItem, props.labelKey as string) }}
247
-
248
- <UIcon v-if="childItem.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.childLinkLabelExternalIcon({ class: props.ui?.childLinkLabelExternalIcon, active: childActive })" />
249
- </p>
250
- <p v-if="childItem.description" :class="ui.childLinkDescription({ class: props.ui?.childLinkDescription, active: childActive })">
251
- {{ childItem.description }}
252
- </p>
253
- </div>
254
- </ULinkBase>
255
- </NavigationMenuLink>
256
- </ULink>
257
- </li>
258
- </ul>
259
- </slot>
260
- </NavigationMenuContent>
261
- </ULink>
260
+ <ULinkBase v-bind="slotProps" :class="ui.link({ class: [props.ui?.link, item.class], active: active || item.active, disabled: !!item.disabled, level: !(orientation === 'vertical' && item.children?.length) })">
261
+ <ReuseLinkTemplate :item="(item as T)" :active="active || item.active" :index="index" />
262
+ </ULinkBase>
263
+ </component>
262
264
 
263
- <template v-if="orientation === 'vertical' && item.children?.length" #content>
265
+ <NavigationMenuContent v-if="orientation === 'horizontal' && (item.children?.length || !!slots[item.slot ? `${item.slot}-content` : 'item-content'])" v-bind="contentProps" :class="ui.content({ class: props.ui?.content })">
266
+ <slot :name="item.slot ? `${item.slot}-content` : 'item-content'" :item="(item as T)" :active="active" :index="index">
264
267
  <ul :class="ui.childList({ class: props.ui?.childList })">
265
268
  <li v-for="(childItem, childIndex) in item.children" :key="childIndex" :class="ui.childItem({ class: props.ui?.childItem })">
266
269
  <ULink v-slot="{ active: childActive, ...childSlotProps }" v-bind="pickLinkProps(childItem)" custom>
267
270
  <NavigationMenuLink as-child :active="childActive" @select="childItem.onSelect">
268
- <ULinkBase v-bind="childSlotProps" :class="ui.link({ class: [props.ui?.link, childItem.class], active: childActive, disabled: !!childItem.disabled })">
269
- <ReuseItemTemplate :item="(childItem as T)" :active="childActive" :index="childIndex" />
271
+ <ULinkBase v-bind="childSlotProps" :class="ui.childLink({ class: [props.ui?.childLink, childItem.class], active: childActive })">
272
+ <UIcon v-if="childItem.icon" :name="childItem.icon" :class="ui.childLinkIcon({ class: props.ui?.childLinkIcon, active: childActive })" />
273
+
274
+ <div :class="ui.childLinkWrapper({ class: props.ui?.childLinkWrapper })">
275
+ <p :class="ui.childLinkLabel({ class: props.ui?.childLinkLabel, active: childActive })">
276
+ {{ get(childItem, props.labelKey as string) }}
277
+
278
+ <UIcon v-if="childItem.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.childLinkLabelExternalIcon({ class: props.ui?.childLinkLabelExternalIcon, active: childActive })" />
279
+ </p>
280
+ <p v-if="childItem.description" :class="ui.childLinkDescription({ class: props.ui?.childLinkDescription, active: childActive })">
281
+ {{ childItem.description }}
282
+ </p>
283
+ </div>
270
284
  </ULinkBase>
271
285
  </NavigationMenuLink>
272
286
  </ULink>
273
287
  </li>
274
288
  </ul>
275
- </template>
276
- </component>
289
+ </slot>
290
+ </NavigationMenuContent>
291
+ </ULink>
292
+
293
+ <template v-if="orientation === 'vertical' && item.children?.length" #content>
294
+ <ul :class="ui.childList({ class: props.ui?.childList })">
295
+ <ReuseItemTemplate v-for="(childItem, childIndex) in item.children" :key="childIndex" :item="childItem" :index="childIndex" :class="ui.childItem({ class: props.ui?.childItem })" />
296
+ </ul>
297
+ </template>
298
+ </component>
299
+ </DefineItemTemplate>
300
+
301
+ <NavigationMenuRoot v-bind="rootProps" :data-collapsed="collapsed" :class="ui.root({ class: [props.class, props.ui?.root] })">
302
+ <template v-for="(list, listIndex) in lists" :key="`list-${listIndex}`">
303
+ <NavigationMenuList :class="ui.list({ class: props.ui?.list })">
304
+ <ReuseItemTemplate v-for="(item, index) in list" :key="`list-${listIndex}-${index}`" :item="item" :index="index" :class="ui.item({ class: props.ui?.item })" />
277
305
  </NavigationMenuList>
278
306
 
279
307
  <div v-if="orientation === 'vertical' && listIndex < lists.length - 1" :class="ui.separator({ class: props.ui?.separator })" />
@@ -1,16 +1,16 @@
1
1
  <script lang="ts">
2
- import { tv } from 'tailwind-variants'
3
2
  import type { PaginationRootProps, PaginationRootEmits } from 'reka-ui'
4
3
  import type { AppConfig } from '@nuxt/schema'
5
4
  import type { RouteLocationRaw } from '#vue-router'
6
5
  import _appConfig from '#build/app.config'
7
6
  import theme from '#build/ui/pagination'
8
7
  import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
8
+ import { tv } from '../utils/tv'
9
9
  import type { ButtonProps } from '../types'
10
10
 
11
- const appConfig = _appConfig as AppConfig & { ui: { pagination: Partial<typeof theme> } }
11
+ const appConfigPagination = _appConfig as AppConfig & { ui: { pagination: Partial<typeof theme> } }
12
12
 
13
- const pagination = tv({ extend: tv(theme), ...(appConfig.ui?.pagination || {}) })
13
+ const pagination = tv({ extend: tv(theme), ...(appConfigPagination.ui?.pagination || {}) })
14
14
 
15
15
  export interface PaginationProps extends Partial<Pick<PaginationRootProps, 'defaultPage' | 'disabled' | 'itemsPerPage' | 'page' | 'showEdges' | 'siblingCount' | 'total'>> {
16
16
  /**
@@ -1,14 +1,15 @@
1
1
  <script lang="ts">
2
+ import type { VariantProps } from 'tailwind-variants'
3
+ import type { PinInputRootEmits, PinInputRootProps } from 'reka-ui'
4
+ import type { AppConfig } from '@nuxt/schema'
2
5
  import _appConfig from '#build/app.config'
3
6
  import theme from '#build/ui/pin-input'
4
- import type { AppConfig } from '@nuxt/schema'
5
- import type { PinInputRootEmits, PinInputRootProps } from 'reka-ui'
6
- import { tv, type VariantProps } from 'tailwind-variants'
7
+ import { tv } from '../utils/tv'
7
8
  import type { PartialString } from '../types/utils'
8
9
 
9
- const appConfig = _appConfig as AppConfig & { ui: { pinInput: Partial<typeof theme> } }
10
+ const appConfigPinInput = _appConfig as AppConfig & { ui: { pinInput: Partial<typeof theme> } }
10
11
 
11
- const pinInput = tv({ extend: tv(theme), ...(appConfig.ui?.pinInput || {}) })
12
+ const pinInput = tv({ extend: tv(theme), ...(appConfigPinInput.ui?.pinInput || {}) })
12
13
 
13
14
  type PinInputVariants = VariantProps<typeof pinInput>
14
15
 
@@ -49,7 +50,7 @@ const props = withDefaults(defineProps<PinInputProps>(), {
49
50
  const emits = defineEmits<PinInputEmits>()
50
51
 
51
52
  const rootProps = useForwardPropsEmits(reactivePick(props, 'defaultValue', 'disabled', 'id', 'mask', 'modelValue', 'name', 'otp', 'placeholder', 'required', 'type'), emits)
52
- const { emitFormInput, emitFormChange, emitFormBlur, size, color, id, name, highlight, disabled } = useFormField<PinInputProps>(props)
53
+ const { emitFormInput, emitFormFocus, emitFormChange, emitFormBlur, size, color, id, name, highlight, disabled, ariaAttrs } = useFormField<PinInputProps>(props)
53
54
 
54
55
  const ui = computed(() => pinInput({
55
56
  color: color.value,
@@ -76,7 +77,7 @@ function onBlur(event: FocusEvent) {
76
77
 
77
78
  <template>
78
79
  <PinInputRoot
79
- v-bind="rootProps"
80
+ v-bind="{ ...rootProps, ...ariaAttrs }"
80
81
  :id="id"
81
82
  :name="name"
82
83
  :class="ui.root({ class: [props.class, props.ui?.root] })"
@@ -91,6 +92,7 @@ function onBlur(event: FocusEvent) {
91
92
  v-bind="$attrs"
92
93
  :disabled="disabled"
93
94
  @blur="onBlur"
95
+ @focus="emitFormFocus"
94
96
  />
95
97
  </PinInputRoot>
96
98
  </template>
@@ -1,14 +1,14 @@
1
1
  <script lang="ts">
2
- import { tv } from 'tailwind-variants'
3
2
  import type { PopoverRootProps, HoverCardRootProps, PopoverRootEmits, PopoverContentProps, PopoverArrowProps } from 'reka-ui'
4
3
  import type { AppConfig } from '@nuxt/schema'
5
4
  import _appConfig from '#build/app.config'
6
5
  import theme from '#build/ui/popover'
6
+ import { tv } from '../utils/tv'
7
7
  import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
8
8
 
9
- const appConfig = _appConfig as AppConfig & { ui: { popover: Partial<typeof theme> } }
9
+ const appConfigPopover = _appConfig as AppConfig & { ui: { popover: Partial<typeof theme> } }
10
10
 
11
- const popover = tv({ extend: tv(theme), ...(appConfig.ui?.popover || {}) })
11
+ const popover = tv({ extend: tv(theme), ...(appConfigPopover.ui?.popover || {}) })
12
12
 
13
13
  export interface PopoverProps extends PopoverRootProps, Pick<HoverCardRootProps, 'openDelay' | 'closeDelay'> {
14
14
  /**
@@ -32,10 +32,10 @@ export interface PopoverProps extends PopoverRootProps, Pick<HoverCardRootProps,
32
32
  */
33
33
  portal?: boolean
34
34
  /**
35
- * When `true`, the popover will not close when clicking outside.
36
- * @defaultValue false
35
+ * When `false`, the popover will not close when clicking outside or pressing escape.
36
+ * @defaultValue true
37
37
  */
38
- preventClose?: boolean
38
+ dismissible?: boolean
39
39
  class?: any
40
40
  ui?: Partial<typeof popover.slots>
41
41
  }
@@ -61,7 +61,8 @@ const props = withDefaults(defineProps<PopoverProps>(), {
61
61
  portal: true,
62
62
  mode: 'click',
63
63
  openDelay: 0,
64
- closeDelay: 0
64
+ closeDelay: 0,
65
+ dismissible: true
65
66
  })
66
67
  const emits = defineEmits<PopoverEmits>()
67
68
  const slots = defineSlots<PopoverSlots>()
@@ -70,7 +71,7 @@ const pick = props.mode === 'hover' ? reactivePick(props, 'defaultOpen', 'open',
70
71
  const rootProps = useForwardPropsEmits(pick, emits)
71
72
  const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8 }) as PopoverContentProps)
72
73
  const contentEvents = computed(() => {
73
- if (props.preventClose) {
74
+ if (!props.dismissible) {
74
75
  return {
75
76
  pointerDownOutside: (e: Event) => e.preventDefault(),
76
77
  interactOutside: (e: Event) => e.preventDefault(),
@@ -1,14 +1,15 @@
1
1
  <!-- eslint-disable vue/block-tag-newline -->
2
2
  <script lang="ts">
3
- import { tv, type VariantProps } from 'tailwind-variants'
3
+ import type { VariantProps } from 'tailwind-variants'
4
4
  import type { ProgressRootProps, ProgressRootEmits } from 'reka-ui'
5
5
  import type { AppConfig } from '@nuxt/schema'
6
6
  import _appConfig from '#build/app.config'
7
7
  import theme from '#build/ui/progress'
8
+ import { tv } from '../utils/tv'
8
9
 
9
- const appConfig = _appConfig as AppConfig & { ui: { progress: Partial<typeof theme> } }
10
+ const appConfigProgress = _appConfig as AppConfig & { ui: { progress: Partial<typeof theme> } }
10
11
 
11
- const progress = tv({ extend: tv(theme), ...(appConfig.ui?.progress || {}) })
12
+ const progress = tv({ extend: tv(theme), ...(appConfigProgress.ui?.progress || {}) })
12
13
 
13
14
  type ProgressVariants = VariantProps<typeof progress>
14
15
 
@@ -58,7 +59,7 @@ const props = withDefaults(defineProps<ProgressProps>(), {
58
59
  orientation: 'horizontal'
59
60
  })
60
61
  const emits = defineEmits<ProgressEmits>()
61
- defineSlots<ProgressSlots>()
62
+ const slots = defineSlots<ProgressSlots>()
62
63
 
63
64
  const { dir } = useLocale()
64
65
 
@@ -160,7 +161,7 @@ const ui = computed(() => progress({
160
161
 
161
162
  <template>
162
163
  <Primitive :as="as" :class="ui.root({ class: [props.class, props.ui?.root] })">
163
- <div v-if="!isIndeterminate && (status || $slots.status)" :class="ui.status({ class: props.ui?.status })" :style="statusStyle">
164
+ <div v-if="!isIndeterminate && (status || !!slots.status)" :class="ui.status({ class: props.ui?.status })" :style="statusStyle">
164
165
  <slot name="status" :percent="percent">
165
166
  {{ percent }}%
166
167
  </slot>
@@ -1,14 +1,15 @@
1
1
  <script lang="ts">
2
- import { tv, type VariantProps } from 'tailwind-variants'
2
+ import type { VariantProps } from 'tailwind-variants'
3
3
  import type { RadioGroupRootProps, RadioGroupRootEmits, AcceptableValue } from 'reka-ui'
4
4
  import type { AppConfig } from '@nuxt/schema'
5
5
  import _appConfig from '#build/app.config'
6
6
  import theme from '#build/ui/radio-group'
7
7
  import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
8
+ import { tv } from '../utils/tv'
8
9
 
9
- const appConfig = _appConfig as AppConfig & { ui: { radioGroup: Partial<typeof theme> } }
10
+ const appConfigRadioGroup = _appConfig as AppConfig & { ui: { radioGroup: Partial<typeof theme> } }
10
11
 
11
- const radioGroup = tv({ extend: tv(theme), ...(appConfig.ui?.radioGroup || {}) })
12
+ const radioGroup = tv({ extend: tv(theme), ...(appConfigRadioGroup.ui?.radioGroup || {}) })
12
13
 
13
14
  type RadioGroupVariants = VariantProps<typeof radioGroup>
14
15
 
@@ -86,7 +87,7 @@ const slots = defineSlots<RadioGroupSlots<T>>()
86
87
 
87
88
  const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'orientation', 'loop', 'required'), emits)
88
89
 
89
- const { emitFormChange, emitFormInput, color, name, size, id: _id, disabled } = useFormField<RadioGroupProps<T>>(props, { bind: false })
90
+ const { emitFormChange, emitFormInput, color, name, size, id: _id, disabled, ariaAttrs } = useFormField<RadioGroupProps<T>>(props, { bind: false })
90
91
  const id = _id.value ?? useId()
91
92
 
92
93
  const ui = computed(() => radioGroup({
@@ -146,7 +147,7 @@ function onUpdate(value: any) {
146
147
  :class="ui.root({ class: [props.class, props.ui?.root] })"
147
148
  @update:model-value="onUpdate"
148
149
  >
149
- <fieldset :class="ui.fieldset({ class: props.ui?.fieldset })">
150
+ <fieldset :class="ui.fieldset({ class: props.ui?.fieldset })" v-bind="ariaAttrs">
150
151
  <legend v-if="legend || !!slots.legend" :class="ui.legend({ class: props.ui?.legend })">
151
152
  <slot name="legend">
152
153
  {{ legend }}
@@ -1,17 +1,18 @@
1
1
  <script lang="ts">
2
- import { tv, type VariantProps } from 'tailwind-variants'
2
+ import type { VariantProps } from 'tailwind-variants'
3
3
  import type { SelectRootProps, SelectRootEmits, SelectContentProps, SelectArrowProps, AcceptableValue } from 'reka-ui'
4
4
  import type { AppConfig } from '@nuxt/schema'
5
5
  import _appConfig from '#build/app.config'
6
6
  import theme from '#build/ui/select'
7
7
  import type { UseComponentIconsProps } from '../composables/useComponentIcons'
8
8
  import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
9
+ import { tv } from '../utils/tv'
9
10
  import type { AvatarProps, ChipProps, InputProps } from '../types'
10
11
  import type { PartialString, MaybeArrayOfArray, MaybeArrayOfArrayItem, SelectModelValue, SelectModelValueEmits, SelectItemKey } from '../types/utils'
11
12
 
12
- const appConfig = _appConfig as AppConfig & { ui: { select: Partial<typeof theme> } }
13
+ const appConfigSelect = _appConfig as AppConfig & { ui: { select: Partial<typeof theme> } }
13
14
 
14
- const select = tv({ extend: tv(theme), ...(appConfig.ui?.select || {}) })
15
+ const select = tv({ extend: tv(theme), ...(appConfigSelect.ui?.select || {}) })
15
16
 
16
17
  export interface SelectItem {
17
18
  label?: string
@@ -128,11 +129,11 @@ const emits = defineEmits<SelectEmits<T, V, M>>()
128
129
  const slots = defineSlots<SelectSlots<T, M>>()
129
130
 
130
131
  const appConfig = useAppConfig()
131
- const rootProps = useForwardPropsEmits(reactivePick(props, 'modelValue', 'defaultValue', 'open', 'defaultOpen', 'disabled', 'autocomplete', 'required', 'multiple'), emits)
132
+ const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen', 'disabled', 'autocomplete', 'required', 'multiple'), emits)
132
133
  const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }) as SelectContentProps)
133
134
  const arrowProps = toRef(() => props.arrow as SelectArrowProps)
134
135
 
135
- const { emitFormChange, emitFormInput, emitFormBlur, size: formGroupSize, color, id, name, highlight, disabled } = useFormField<InputProps>(props)
136
+ const { emitFormChange, emitFormInput, emitFormBlur, emitFormFocus, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField<InputProps>(props)
136
137
  const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
137
138
  const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: appConfig.ui.icons.chevronDown })))
138
139
 
@@ -158,7 +159,7 @@ function displayValue(value?: AcceptableValue | AcceptableValue[]): string | und
158
159
  return value.map(v => displayValue(v)).filter(Boolean).join(', ')
159
160
  }
160
161
 
161
- const item = items.value.find(item => compare(typeof item === 'object' ? get(item, props.valueKey as string) : item, value))
162
+ const item = items.value.find(item => compare(typeof item === 'object' ? get(item as Record<string, any>, props.valueKey as string) : item, value))
162
163
  return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
163
164
  }
164
165
 
@@ -178,6 +179,7 @@ function onUpdateOpen(value: boolean) {
178
179
  } else {
179
180
  const event = new FocusEvent('focus')
180
181
  emits('focus', event)
182
+ emitFormFocus()
181
183
  }
182
184
  }
183
185
  </script>
@@ -185,16 +187,17 @@ function onUpdateOpen(value: boolean) {
185
187
  <!-- eslint-disable vue/no-template-shadow -->
186
188
  <template>
187
189
  <SelectRoot
188
- :id="id"
189
190
  v-slot="{ modelValue, open }"
190
- v-bind="rootProps"
191
191
  :name="name"
192
+ v-bind="rootProps"
192
193
  :autocomplete="autocomplete"
193
194
  :disabled="disabled"
195
+ :default-value="(defaultValue as (AcceptableValue | AcceptableValue[] | undefined))"
196
+ :model-value="(modelValue as (AcceptableValue | AcceptableValue[] | undefined))"
194
197
  @update:model-value="onUpdate"
195
198
  @update:open="onUpdateOpen"
196
199
  >
197
- <SelectTrigger :class="ui.base({ class: [props.class, props.ui?.base] })">
200
+ <SelectTrigger :id="id" :class="ui.base({ class: [props.class, props.ui?.base] })" v-bind="ariaAttrs">
198
201
  <span v-if="isLeading || !!avatar || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
199
202
  <slot name="leading" :model-value="(modelValue as M extends true ? AcceptableValue[] : AcceptableValue)" :open="open" :ui="ui">
200
203
  <UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
@@ -1,17 +1,18 @@
1
1
  <script lang="ts">
2
- import { tv, type VariantProps } from 'tailwind-variants'
2
+ import type { VariantProps } from 'tailwind-variants'
3
3
  import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxArrowProps, AcceptableValue } from 'reka-ui'
4
4
  import type { AppConfig } from '@nuxt/schema'
5
5
  import _appConfig from '#build/app.config'
6
6
  import theme from '#build/ui/select-menu'
7
7
  import type { UseComponentIconsProps } from '../composables/useComponentIcons'
8
8
  import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
9
+ import { tv } from '../utils/tv'
9
10
  import type { AvatarProps, ChipProps, InputProps } from '../types'
10
11
  import type { PartialString, MaybeArrayOfArray, MaybeArrayOfArrayItem, SelectModelValue, SelectModelValueEmits, SelectItemKey } from '../types/utils'
11
12
 
12
- const appConfig = _appConfig as AppConfig & { ui: { selectMenu: Partial<typeof theme> } }
13
+ const appConfigSelectMenu = _appConfig as AppConfig & { ui: { selectMenu: Partial<typeof theme> } }
13
14
 
14
- const selectMenu = tv({ extend: tv(theme), ...(appConfig.ui?.selectMenu || {}) })
15
+ const selectMenu = tv({ extend: tv(theme), ...(appConfigSelectMenu.ui?.selectMenu || {}) })
15
16
 
16
17
  export interface SelectMenuItem {
17
18
  label?: string
@@ -150,7 +151,8 @@ import UInput from './Input.vue'
150
151
  const props = withDefaults(defineProps<SelectMenuProps<T, I, V, M>>(), {
151
152
  portal: true,
152
153
  searchInput: true,
153
- labelKey: 'label' as never
154
+ labelKey: 'label' as never,
155
+ resetSearchTermOnBlur: true
154
156
  })
155
157
  const emits = defineEmits<SelectMenuEmits<T, V, M>>()
156
158
  const slots = defineSlots<SelectMenuSlots<T, M>>()
@@ -164,9 +166,9 @@ const { contains } = useFilter({ sensitivity: 'base' })
164
166
  const rootProps = useForwardPropsEmits(reactivePick(props, 'modelValue', 'defaultValue', 'open', 'defaultOpen', 'multiple', 'resetSearchTermOnBlur', 'highlightOnHover'), emits)
165
167
  const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }) as ComboboxContentProps)
166
168
  const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)
167
- const searchInputProps = toRef(() => defu(props.searchInput, { placeholder: 'Search...', variant: 'none' }) as InputProps)
169
+ const searchInputProps = toRef(() => defu(props.searchInput, { placeholder: t('selectMenu.search'), variant: 'none' }) as InputProps)
168
170
 
169
- const { emitFormBlur, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled } = useFormField<InputProps>(props)
171
+ const { emitFormBlur, emitFormFocus, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField<InputProps>(props)
170
172
  const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
171
173
  const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: appConfig.ui.icons.chevronDown })))
172
174
 
@@ -194,7 +196,7 @@ function displayValue(value: T | T[]): string {
194
196
  return value && (typeof value === 'object' ? get(value, props.labelKey as string) : value)
195
197
  }
196
198
 
197
- const item = items.value.find(item => compare(typeof item === 'object' ? get(item, props.valueKey as string) : item, value))
199
+ const item = items.value.find(item => compare(typeof item === 'object' ? get(item as Record<string, any>, props.valueKey as string) : item, value))
198
200
  return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
199
201
  }
200
202
 
@@ -250,13 +252,28 @@ function onUpdate(value: any) {
250
252
  }
251
253
 
252
254
  function onUpdateOpen(value: boolean) {
255
+ let timeoutId
256
+
253
257
  if (!value) {
254
258
  const event = new FocusEvent('blur')
259
+
255
260
  emits('blur', event)
256
261
  emitFormBlur()
262
+
263
+ // Since we use `displayValue` prop inside ComboboxInput we should reset searchTerm manually
264
+ // https://reka-ui.com/docs/components/combobox#api-reference
265
+ if (props.resetSearchTermOnBlur) {
266
+ const STATE_ANIMATION_DELAY_MS = 100
267
+
268
+ timeoutId = setTimeout(() => {
269
+ searchTerm.value = ''
270
+ }, STATE_ANIMATION_DELAY_MS)
271
+ }
257
272
  } else {
258
273
  const event = new FocusEvent('focus')
259
274
  emits('focus', event)
275
+ emitFormFocus()
276
+ clearTimeout(timeoutId)
260
277
  }
261
278
  }
262
279
  </script>
@@ -282,7 +299,7 @@ function onUpdateOpen(value: boolean) {
282
299
  <ComboboxRoot
283
300
  :id="id"
284
301
  v-slot="{ modelValue, open }"
285
- v-bind="rootProps"
302
+ v-bind="{ ...rootProps, ...ariaAttrs }"
286
303
  ignore-filter
287
304
  as-child
288
305
  :name="name"