@nuxt/ui 3.0.1 → 3.0.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 (71) hide show
  1. package/.nuxt/ui/button.ts +36 -36
  2. package/.nuxt/ui/container.ts +1 -1
  3. package/.nuxt/ui/navigation-menu.ts +2 -2
  4. package/.nuxt/ui/switch.ts +1 -1
  5. package/.nuxt/ui/tabs.ts +1 -1
  6. package/dist/module.cjs +1 -1
  7. package/dist/module.d.cts +2 -1
  8. package/dist/module.d.mts +2 -1
  9. package/dist/module.d.ts +2 -1
  10. package/dist/module.json +1 -1
  11. package/dist/module.mjs +1 -1
  12. package/dist/runtime/components/Accordion.vue +9 -8
  13. package/dist/runtime/components/Alert.vue +1 -1
  14. package/dist/runtime/components/App.vue +1 -1
  15. package/dist/runtime/components/Avatar.vue +7 -5
  16. package/dist/runtime/components/Breadcrumb.vue +11 -10
  17. package/dist/runtime/components/Button.vue +1 -1
  18. package/dist/runtime/components/Calendar.vue +40 -11
  19. package/dist/runtime/components/Carousel.vue +7 -3
  20. package/dist/runtime/components/CommandPalette.vue +12 -11
  21. package/dist/runtime/components/ContextMenu.vue +24 -12
  22. package/dist/runtime/components/ContextMenuContent.vue +22 -15
  23. package/dist/runtime/components/Drawer.vue +1 -5
  24. package/dist/runtime/components/DropdownMenu.vue +23 -12
  25. package/dist/runtime/components/DropdownMenuContent.vue +22 -15
  26. package/dist/runtime/components/Form.vue +1 -0
  27. package/dist/runtime/components/FormField.vue +1 -0
  28. package/dist/runtime/components/InputMenu.vue +92 -54
  29. package/dist/runtime/components/Link.vue +3 -0
  30. package/dist/runtime/components/LinkBase.vue +1 -0
  31. package/dist/runtime/components/Modal.vue +1 -1
  32. package/dist/runtime/components/NavigationMenu.vue +49 -25
  33. package/dist/runtime/components/RadioGroup.vue +23 -12
  34. package/dist/runtime/components/Select.vue +69 -42
  35. package/dist/runtime/components/SelectMenu.vue +83 -48
  36. package/dist/runtime/components/Slideover.vue +1 -1
  37. package/dist/runtime/components/Stepper.vue +11 -10
  38. package/dist/runtime/components/Table.vue +7 -2
  39. package/dist/runtime/components/Tabs.vue +6 -5
  40. package/dist/runtime/components/Toast.vue +1 -1
  41. package/dist/runtime/components/Tree.vue +46 -32
  42. package/dist/runtime/composables/defineShortcuts.js +0 -1
  43. package/dist/runtime/composables/useFormField.js +1 -1
  44. package/dist/runtime/composables/useOverlay.d.ts +3 -2
  45. package/dist/runtime/locale/hy.d.ts +3 -0
  46. package/dist/runtime/locale/hy.js +54 -0
  47. package/dist/runtime/locale/index.d.ts +1 -0
  48. package/dist/runtime/locale/index.js +1 -0
  49. package/dist/runtime/types/form.d.ts +1 -0
  50. package/dist/runtime/types/index.d.ts +1 -0
  51. package/dist/runtime/types/index.js +1 -0
  52. package/dist/runtime/types/utils.d.ts +28 -8
  53. package/dist/runtime/utils/index.d.ts +1 -0
  54. package/dist/runtime/utils/index.js +3 -0
  55. package/dist/runtime/utils/link.d.ts +2 -25
  56. package/dist/runtime/utils/link.js +31 -1
  57. package/dist/runtime/vue/components/Link.vue +3 -0
  58. package/dist/runtime/vue/stubs.d.ts +7 -2
  59. package/dist/runtime/vue/stubs.js +11 -2
  60. package/dist/shared/{ui.C6uTSFTI.cjs → ui.BL-XB3u1.cjs} +16 -16
  61. package/dist/shared/{ui.BCaWD5Ek.mjs → ui.BkNUP964.mjs} +16 -16
  62. package/dist/types.d.mts +7 -1
  63. package/dist/types.d.ts +7 -1
  64. package/dist/unplugin.cjs +1 -1
  65. package/dist/unplugin.d.cts +2 -1
  66. package/dist/unplugin.d.mts +2 -1
  67. package/dist/unplugin.d.ts +2 -1
  68. package/dist/unplugin.mjs +1 -1
  69. package/dist/vite.cjs +1 -1
  70. package/dist/vite.mjs +1 -1
  71. package/package.json +39 -22
@@ -4,14 +4,15 @@ import type { DropdownMenuContentProps as RekaDropdownMenuContentProps, Dropdown
4
4
  import theme from '#build/ui/dropdown-menu'
5
5
  import { tv } from '../utils/tv'
6
6
  import type { KbdProps, AvatarProps, DropdownMenuItem, DropdownMenuSlots } from '../types'
7
+ import type { ArrayOrNested, NestedItem } from '../types/utils'
7
8
 
8
9
  const _dropdownMenu = tv(theme)()
9
10
 
10
- interface DropdownMenuContentProps<T> extends Omit<RekaDropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
11
- items?: T[] | T[][]
11
+ interface DropdownMenuContentProps<T extends ArrayOrNested<DropdownMenuItem>> extends Omit<RekaDropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
12
+ items?: T
12
13
  portal?: boolean
13
14
  sub?: boolean
14
- labelKey: string
15
+ labelKey: keyof NestedItem<T>
15
16
  /**
16
17
  * @IconifyIcon
17
18
  */
@@ -31,19 +32,19 @@ interface DropdownMenuContentProps<T> extends Omit<RekaDropdownMenuContentProps,
31
32
 
32
33
  interface DropdownMenuContentEmits extends RekaDropdownMenuContentEmits {}
33
34
 
34
- type DropdownMenuContentSlots<T extends { slot?: string }> = Omit<DropdownMenuSlots<T>, 'default'> & {
35
+ type DropdownMenuContentSlots<T extends ArrayOrNested<DropdownMenuItem>> = Omit<DropdownMenuSlots<T>, 'default'> & {
35
36
  default(props?: {}): any
36
37
  }
37
38
 
38
39
  </script>
39
40
 
40
- <script setup lang="ts" generic="T extends DropdownMenuItem">
41
+ <script setup lang="ts" generic="T extends ArrayOrNested<DropdownMenuItem>">
41
42
  import { computed } from 'vue'
42
43
  import { DropdownMenu } from 'reka-ui/namespaced'
43
44
  import { useForwardPropsEmits } from 'reka-ui'
44
45
  import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
45
46
  import { useAppConfig } from '#imports'
46
- import { omit, get } from '../utils'
47
+ import { omit, get, isArrayOfArray } from '../utils'
47
48
  import { pickLinkProps } from '../utils/link'
48
49
  import ULinkBase from './LinkBase.vue'
49
50
  import ULink from './Link.vue'
@@ -59,24 +60,30 @@ const slots = defineSlots<DropdownMenuContentSlots<T>>()
59
60
 
60
61
  const appConfig = useAppConfig()
61
62
  const contentProps = useForwardPropsEmits(reactiveOmit(props, 'sub', 'items', 'portal', 'labelKey', 'checkedIcon', 'loadingIcon', 'externalIcon', 'class', 'ui', 'uiOverride'), emits)
62
- const proxySlots = omit(slots, ['default']) as Record<string, DropdownMenuContentSlots<T>[string]>
63
+ const proxySlots = omit(slots, ['default'])
63
64
 
64
65
  const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: DropdownMenuItem, active?: boolean, index: number }>()
65
66
 
66
- const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0]) ? props.items : [props.items]) as T[][] : [])
67
+ const groups = computed<DropdownMenuItem[][]>(() =>
68
+ props.items?.length
69
+ ? isArrayOfArray(props.items)
70
+ ? props.items
71
+ : [props.items]
72
+ : []
73
+ )
67
74
  </script>
68
75
 
69
76
  <template>
70
77
  <DefineItemTemplate v-slot="{ item, active, index }">
71
- <slot :name="item.slot || 'item'" :item="(item as T)" :index="index">
72
- <slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="(item as T)" :active="active" :index="index">
78
+ <slot :name="((item.slot || 'item') as keyof DropdownMenuContentSlots<T>)" :item="(item as Extract<NestedItem<T>, { slot: string; }>)" :index="index">
79
+ <slot :name="((item.slot ? `${item.slot}-leading`: 'item-leading') as keyof DropdownMenuContentSlots<T>)" :item="(item as Extract<NestedItem<T>, { slot: string; }>)" :active="active" :index="index">
73
80
  <UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, color: item?.color, loading: true })" />
74
81
  <UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, color: item?.color, active })" />
75
82
  <UAvatar v-else-if="item.avatar" :size="((props.uiOverride?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: uiOverride?.itemLeadingAvatar, active })" />
76
83
  </slot>
77
84
 
78
- <span v-if="get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
79
- <slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="(item as T)" :active="active" :index="index">
85
+ <span v-if="get(item, props.labelKey as string) || !!slots[(item.slot ? `${item.slot}-label`: 'item-label') as keyof DropdownMenuContentSlots<T>]" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
86
+ <slot :name="((item.slot ? `${item.slot}-label`: 'item-label') as keyof DropdownMenuContentSlots<T>)" :item="(item as Extract<NestedItem<T>, { slot: string; }>)" :active="active" :index="index">
80
87
  {{ get(item, props.labelKey as string) }}
81
88
  </slot>
82
89
 
@@ -84,7 +91,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
84
91
  </span>
85
92
 
86
93
  <span :class="ui.itemTrailing({ class: uiOverride?.itemTrailing })">
87
- <slot :name="item.slot ? `${item.slot}-trailing`: 'item-trailing'" :item="(item as T)" :active="active" :index="index">
94
+ <slot :name="((item.slot ? `${item.slot}-trailing`: 'item-trailing') as keyof DropdownMenuContentSlots<T>)" :item="(item as Extract<NestedItem<T>, { slot: string; }>)" :active="active" :index="index">
88
95
  <UIcon v-if="item.children?.length" :name="appConfig.ui.icons.chevronRight" :class="ui.itemTrailingIcon({ class: uiOverride?.itemTrailingIcon, color: item?.color, active })" />
89
96
  <span v-else-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: uiOverride?.itemTrailingKbds })">
90
97
  <UKbd v-for="(kbd, kbdIndex) in item.kbds" :key="kbdIndex" :size="((props.uiOverride?.itemTrailingKbdsSize || ui.itemTrailingKbdsSize()) as KbdProps['size'])" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" />
@@ -123,7 +130,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
123
130
  :ui="ui"
124
131
  :ui-override="uiOverride"
125
132
  :portal="portal"
126
- :items="item.children"
133
+ :items="(item.children as T)"
127
134
  side="right"
128
135
  align="start"
129
136
  :align-offset="-4"
@@ -135,7 +142,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
135
142
  v-bind="item.content"
136
143
  >
137
144
  <template v-for="(_, name) in proxySlots" #[name]="slotData: any">
138
- <slot :name="name" v-bind="slotData" />
145
+ <slot :name="(name as keyof DropdownMenuContentSlots<T>)" v-bind="slotData" />
139
146
  </template>
140
147
  </UDropdownMenuContent>
141
148
  </DropdownMenu.Sub>
@@ -217,6 +217,7 @@ async function onSubmitWrapper(payload: Event) {
217
217
  try {
218
218
  event.data = await _validate({ nested: true, transform: props.transform })
219
219
  await props.onSubmit?.(event)
220
+ dirtyFields.clear()
220
221
  } catch (error) {
221
222
  if (!(error instanceof FormValidationException)) {
222
223
  throw error
@@ -86,6 +86,7 @@ provide(formFieldInjectionKey, computed(() => ({
86
86
  errorPattern: props.errorPattern,
87
87
  hint: props.hint,
88
88
  description: props.description,
89
+ help: props.help,
89
90
  ariaId
90
91
  }) as FormFieldInjectedOptions<FormFieldProps>))
91
92
  </script>
@@ -1,20 +1,29 @@
1
1
  <script lang="ts">
2
2
  import type { InputHTMLAttributes } from 'vue'
3
3
  import type { VariantProps } from 'tailwind-variants'
4
- import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxContentEmits, ComboboxArrowProps, AcceptableValue } from 'reka-ui'
4
+ import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxContentEmits, ComboboxArrowProps } 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/input-menu'
8
8
  import type { UseComponentIconsProps } from '../composables/useComponentIcons'
9
9
  import { tv } from '../utils/tv'
10
10
  import type { AvatarProps, ChipProps, InputProps } from '../types'
11
- import type { PartialString, MaybeArrayOfArray, MaybeArrayOfArrayItem, SelectModelValue, SelectModelValueEmits, SelectItemKey, EmitsToProps } from '../types/utils'
11
+ import type {
12
+ AcceptableValue,
13
+ ArrayOrNested,
14
+ GetItemKeys,
15
+ GetModelValue,
16
+ GetModelValueEmits,
17
+ NestedItem,
18
+ PartialString,
19
+ EmitsToProps
20
+ } from '../types/utils'
12
21
 
13
22
  const appConfigInputMenu = _appConfig as AppConfig & { ui: { inputMenu: Partial<typeof theme> } }
14
23
 
15
24
  const inputMenu = tv({ extend: tv(theme), ...(appConfigInputMenu.ui?.inputMenu || {}) })
16
25
 
17
- export interface InputMenuItem {
26
+ interface _InputMenuItem {
18
27
  label?: string
19
28
  /**
20
29
  * @IconifyIcon
@@ -27,13 +36,16 @@ export interface InputMenuItem {
27
36
  * @defaultValue 'item'
28
37
  */
29
38
  type?: 'label' | 'separator' | 'item'
39
+ value?: string | number
30
40
  disabled?: boolean
31
41
  onSelect?(e?: Event): void
42
+ [key: string]: any
32
43
  }
44
+ export type InputMenuItem = _InputMenuItem | AcceptableValue | boolean
33
45
 
34
46
  type InputMenuVariants = VariantProps<typeof inputMenu>
35
47
 
36
- export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<InputMenuItem | AcceptableValue | boolean> = MaybeArrayOfArray<InputMenuItem | AcceptableValue | boolean>, V extends SelectItemKey<T> | undefined = undefined, M extends boolean = false> extends Pick<ComboboxRootProps<T>, 'open' | 'defaultOpen' | 'disabled' | 'name' | 'resetSearchTermOnBlur' | 'highlightOnHover'>, UseComponentIconsProps {
48
+ export interface InputMenuProps<T extends ArrayOrNested<InputMenuItem> = ArrayOrNested<InputMenuItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false> extends Pick<ComboboxRootProps<T>, 'open' | 'defaultOpen' | 'disabled' | 'name' | 'resetSearchTermOnBlur' | 'highlightOnHover'>, UseComponentIconsProps {
37
49
  /**
38
50
  * The element or component this component should render as.
39
51
  * @defaultValue 'div'
@@ -96,17 +108,17 @@ export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends Ma
96
108
  * When `items` is an array of objects, select the field to use as the value instead of the object itself.
97
109
  * @defaultValue undefined
98
110
  */
99
- valueKey?: V
111
+ valueKey?: VK
100
112
  /**
101
113
  * When `items` is an array of objects, select the field to use as the label.
102
114
  * @defaultValue 'label'
103
115
  */
104
- labelKey?: V
105
- items?: I
116
+ labelKey?: keyof NestedItem<T>
117
+ items?: T
106
118
  /** The value of the InputMenu when initially rendered. Use when you do not need to control the state of the InputMenu. */
107
- defaultValue?: SelectModelValue<T, V, M>
119
+ defaultValue?: GetModelValue<T, VK, M>
108
120
  /** The controlled value of the InputMenu. Can be binded-with with `v-model`. */
109
- modelValue?: SelectModelValue<T, V, M>
121
+ modelValue?: GetModelValue<T, VK, M>
110
122
  /** Whether multiple options can be selected or not. */
111
123
  multiple?: M & boolean
112
124
  /** Highlight the ring color like a focus state. */
@@ -130,18 +142,28 @@ export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends Ma
130
142
  ui?: PartialString<typeof inputMenu.slots>
131
143
  }
132
144
 
133
- export type InputMenuEmits<T, V, M extends boolean> = Omit<ComboboxRootEmits<T>, 'update:modelValue'> & {
145
+ export type InputMenuEmits<A extends ArrayOrNested<InputMenuItem>, VK extends GetItemKeys<A> | undefined, M extends boolean> = Pick<ComboboxRootEmits, 'update:open'> & {
134
146
  change: [payload: Event]
135
147
  blur: [payload: FocusEvent]
136
148
  focus: [payload: FocusEvent]
137
149
  create: [item: string]
138
- } & SelectModelValueEmits<T, V, M>
139
-
140
- type SlotProps<T> = (props: { item: T, index: number }) => any
141
-
142
- export interface InputMenuSlots<T, M extends boolean> {
143
- 'leading'(props: { modelValue?: M extends true ? T[] : T, open: boolean, ui: any }): any
144
- 'trailing'(props: { modelValue?: M extends true ? T[] : T, open: boolean, ui: any }): any
150
+ /** Event handler when highlighted element changes. */
151
+ highlight: [payload: {
152
+ ref: HTMLElement
153
+ value: GetModelValue<A, VK, M>
154
+ } | undefined]
155
+ } & GetModelValueEmits<A, VK, M>
156
+
157
+ type SlotProps<T extends InputMenuItem> = (props: { item: T, index: number }) => any
158
+
159
+ export interface InputMenuSlots<
160
+ A extends ArrayOrNested<InputMenuItem> = ArrayOrNested<InputMenuItem>,
161
+ VK extends GetItemKeys<A> | undefined = undefined,
162
+ M extends boolean = false,
163
+ T extends NestedItem<A> = NestedItem<A>
164
+ > {
165
+ 'leading'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean, ui: ReturnType<typeof inputMenu> }): any
166
+ 'trailing'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean, ui: ReturnType<typeof inputMenu> }): any
145
167
  'empty'(props: { searchTerm?: string }): any
146
168
  'item': SlotProps<T>
147
169
  'item-leading': SlotProps<T>
@@ -153,8 +175,8 @@ export interface InputMenuSlots<T, M extends boolean> {
153
175
  }
154
176
  </script>
155
177
 
156
- <script setup lang="ts" generic="T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<InputMenuItem | AcceptableValue | boolean> = MaybeArrayOfArray<InputMenuItem | AcceptableValue | boolean>, V extends SelectItemKey<T> | undefined = undefined, M extends boolean = false">
157
- import { computed, ref, toRef, onMounted, toRaw } from 'vue'
178
+ <script setup lang="ts" generic="T extends ArrayOrNested<InputMenuItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false">
179
+ import { computed, ref, toRef, onMounted, toRaw, nextTick } from 'vue'
158
180
  import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxPortal, ComboboxContent, ComboboxViewport, ComboboxEmpty, ComboboxGroup, ComboboxLabel, ComboboxSeparator, ComboboxItem, ComboboxItemIndicator, TagsInputRoot, TagsInputItem, TagsInputItemText, TagsInputItemDelete, TagsInputInput, useForwardPropsEmits, useFilter } from 'reka-ui'
159
181
  import { defu } from 'defu'
160
182
  import { isEqual } from 'ohash/utils'
@@ -164,21 +186,21 @@ import { useButtonGroup } from '../composables/useButtonGroup'
164
186
  import { useComponentIcons } from '../composables/useComponentIcons'
165
187
  import { useFormField } from '../composables/useFormField'
166
188
  import { useLocale } from '../composables/useLocale'
167
- import { get, compare } from '../utils'
189
+ import { compare, get, isArrayOfArray } from '../utils'
168
190
  import UIcon from './Icon.vue'
169
191
  import UAvatar from './Avatar.vue'
170
192
  import UChip from './Chip.vue'
171
193
 
172
194
  defineOptions({ inheritAttrs: false })
173
195
 
174
- const props = withDefaults(defineProps<InputMenuProps<T, I, V, M>>(), {
196
+ const props = withDefaults(defineProps<InputMenuProps<T, VK, M>>(), {
175
197
  type: 'text',
176
198
  autofocusDelay: 0,
177
199
  portal: true,
178
200
  labelKey: 'label' as never
179
201
  })
180
- const emits = defineEmits<InputMenuEmits<T, V, M>>()
181
- const slots = defineSlots<InputMenuSlots<T, M>>()
202
+ const emits = defineEmits<InputMenuEmits<T, VK, M>>()
203
+ const slots = defineSlots<InputMenuSlots<T, VK, M>>()
182
204
 
183
205
  const searchTerm = defineModel<string>('searchTerm', { default: '' })
184
206
 
@@ -219,9 +241,15 @@ function displayValue(value: T): string {
219
241
  return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
220
242
  }
221
243
 
222
- const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0]) ? props.items : [props.items]) as InputMenuItem[][] : [])
244
+ const groups = computed<InputMenuItem[][]>(() =>
245
+ props.items?.length
246
+ ? isArrayOfArray(props.items)
247
+ ? props.items
248
+ : [props.items]
249
+ : []
250
+ )
223
251
  // eslint-disable-next-line vue/no-dupe-keys
224
- const items = computed(() => groups.value.flatMap(group => group) as T[])
252
+ const items = computed(() => groups.value.flatMap(group => group))
225
253
 
226
254
  const filteredGroups = computed(() => {
227
255
  if (props.ignoreFilter || !searchTerm.value) {
@@ -230,9 +258,9 @@ const filteredGroups = computed(() => {
230
258
 
231
259
  const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey] as string[]
232
260
 
233
- return groups.value.map(items => items.filter((item) => {
234
- if (typeof item !== 'object') {
235
- return contains(item, searchTerm.value)
261
+ return groups.value.map(group => group.filter((item) => {
262
+ if (typeof item !== 'object' || item === null) {
263
+ return contains(String(item), searchTerm.value)
236
264
  }
237
265
 
238
266
  if (item.type && ['label', 'separator'].includes(item.type)) {
@@ -240,19 +268,21 @@ const filteredGroups = computed(() => {
240
268
  }
241
269
 
242
270
  return fields.some(field => contains(get(item, field), searchTerm.value))
243
- })).filter(group => group.filter(item => !item.type || !['label', 'separator'].includes(item.type)).length > 0)
271
+ })).filter(group => group.filter(item =>
272
+ !isInputItem(item) || (!item.type || !['label', 'separator'].includes(item.type))
273
+ ).length > 0)
244
274
  })
245
- const filteredItems = computed(() => filteredGroups.value.flatMap(group => group) as T[])
275
+ const filteredItems = computed(() => filteredGroups.value.flatMap(group => group))
246
276
 
247
277
  const createItem = computed(() => {
248
278
  if (!props.createItem || !searchTerm.value) {
249
279
  return false
250
280
  }
251
281
 
252
- const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } as T : searchTerm.value
282
+ const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } as NestedItem<T> : searchTerm.value
253
283
 
254
284
  if ((typeof props.createItem === 'object' && props.createItem.when === 'always') || props.createItem === 'always') {
255
- return !filteredItems.value.find(item => compare(item, newItem, props.valueKey))
285
+ return !filteredItems.value.find(item => compare(item, newItem, String(props.valueKey)))
256
286
  }
257
287
 
258
288
  return !filteredItems.value.length
@@ -303,16 +333,24 @@ function onUpdateOpen(value: boolean) {
303
333
  const event = new FocusEvent('focus')
304
334
  emits('focus', event)
305
335
  }
336
+
337
+ nextTick(() => {
338
+ searchTerm.value = ''
339
+ })
306
340
  }
307
341
 
308
342
  function onRemoveTag(event: any) {
309
343
  if (props.multiple) {
310
- const modelValue = props.modelValue as SelectModelValue<T, V, true>
344
+ const modelValue = props.modelValue as GetModelValue<T, VK, true>
311
345
  const filteredValue = modelValue.filter(value => !isEqual(value, event))
312
- emits('update:modelValue', filteredValue as SelectModelValue<T, V, M>)
346
+ emits('update:modelValue', filteredValue as GetModelValue<T, VK, M>)
313
347
  }
314
348
  }
315
349
 
350
+ function isInputItem(item: InputMenuItem): item is _InputMenuItem {
351
+ return typeof item === 'object' && item !== null
352
+ }
353
+
316
354
  defineExpose({
317
355
  inputRef
318
356
  })
@@ -362,21 +400,21 @@ defineExpose({
362
400
  @focus="onFocus"
363
401
  @remove-tag="onRemoveTag"
364
402
  >
365
- <TagsInputItem v-for="(item, index) in tags" :key="index" :value="(item as string)" :class="ui.tagsItem({ class: props.ui?.tagsItem })">
403
+ <TagsInputItem v-for="(item, index) in tags" :key="index" :value="item" :class="ui.tagsItem({ class: props.ui?.tagsItem })">
366
404
  <TagsInputItemText :class="ui.tagsItemText({ class: props.ui?.tagsItemText })">
367
- <slot name="tags-item-text" :item="(item as T)" :index="index">
405
+ <slot name="tags-item-text" :item="(item as NestedItem<T>)" :index="index">
368
406
  {{ displayValue(item as T) }}
369
407
  </slot>
370
408
  </TagsInputItemText>
371
409
 
372
410
  <TagsInputItemDelete :class="ui.tagsItemDelete({ class: props.ui?.tagsItemDelete })" :disabled="disabled">
373
- <slot name="tags-item-delete" :item="(item as T)" :index="index">
411
+ <slot name="tags-item-delete" :item="(item as NestedItem<T>)" :index="index">
374
412
  <UIcon :name="deleteIcon || appConfig.ui.icons.close" :class="ui.tagsItemDeleteIcon({ class: props.ui?.tagsItemDeleteIcon })" />
375
413
  </slot>
376
414
  </TagsInputItemDelete>
377
415
  </TagsInputItem>
378
416
 
379
- <ComboboxInput v-model="searchTerm" :display-value="displayValue" as-child>
417
+ <ComboboxInput as-child @update:model-value="searchTerm = $event">
380
418
  <TagsInputInput
381
419
  ref="inputRef"
382
420
  v-bind="{ ...$attrs, ...ariaAttrs }"
@@ -390,7 +428,6 @@ defineExpose({
390
428
  <ComboboxInput
391
429
  v-else
392
430
  ref="inputRef"
393
- v-model="searchTerm"
394
431
  :display-value="displayValue"
395
432
  v-bind="{ ...$attrs, ...ariaAttrs }"
396
433
  :type="type"
@@ -398,17 +435,18 @@ defineExpose({
398
435
  :required="required"
399
436
  @blur="onBlur"
400
437
  @focus="onFocus"
438
+ @update:model-value="searchTerm = $event"
401
439
  />
402
440
 
403
441
  <span v-if="isLeading || !!avatar || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
404
- <slot name="leading" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :ui="ui">
442
+ <slot name="leading" :model-value="(modelValue as GetModelValue<T, VK, M>)" :open="open" :ui="ui">
405
443
  <UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
406
444
  <UAvatar v-else-if="!!avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
407
445
  </slot>
408
446
  </span>
409
447
 
410
448
  <ComboboxTrigger v-if="isTrailing || !!slots.trailing" :class="ui.trailing({ class: props.ui?.trailing })">
411
- <slot name="trailing" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :ui="ui">
449
+ <slot name="trailing" :model-value="(modelValue as GetModelValue<T, VK, M>)" :open="open" :ui="ui">
412
450
  <UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
413
451
  </slot>
414
452
  </ComboboxTrigger>
@@ -427,25 +465,25 @@ defineExpose({
427
465
 
428
466
  <ComboboxGroup v-for="(group, groupIndex) in filteredGroups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
429
467
  <template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
430
- <ComboboxLabel v-if="item?.type === 'label'" :class="ui.label({ class: props.ui?.label })">
468
+ <ComboboxLabel v-if="isInputItem(item) && item.type === 'label'" :class="ui.label({ class: props.ui?.label })">
431
469
  {{ get(item, props.labelKey as string) }}
432
470
  </ComboboxLabel>
433
471
 
434
- <ComboboxSeparator v-else-if="item?.type === 'separator'" :class="ui.separator({ class: props.ui?.separator })" />
472
+ <ComboboxSeparator v-else-if="isInputItem(item) && item.type === 'separator'" :class="ui.separator({ class: props.ui?.separator })" />
435
473
 
436
474
  <ComboboxItem
437
475
  v-else
438
476
  :class="ui.item({ class: props.ui?.item })"
439
- :disabled="item.disabled"
440
- :value="valueKey && typeof item === 'object' ? get(item, props.valueKey as string) : item"
441
- @select="item.onSelect"
477
+ :disabled="isInputItem(item) && item.disabled"
478
+ :value="props.valueKey && isInputItem(item) ? get(item, String(props.valueKey)) : item"
479
+ @select="isInputItem(item) && item.onSelect"
442
480
  >
443
- <slot name="item" :item="(item as T)" :index="index">
444
- <slot name="item-leading" :item="(item as T)" :index="index">
445
- <UIcon v-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
446
- <UAvatar v-else-if="item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
481
+ <slot name="item" :item="(item as NestedItem<T>)" :index="index">
482
+ <slot name="item-leading" :item="(item as NestedItem<T>)" :index="index">
483
+ <UIcon v-if="isInputItem(item) && item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
484
+ <UAvatar v-else-if="isInputItem(item) && item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
447
485
  <UChip
448
- v-else-if="item.chip"
486
+ v-else-if="isInputItem(item) && item.chip"
449
487
  :size="((props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()) as ChipProps['size'])"
450
488
  inset
451
489
  standalone
@@ -455,13 +493,13 @@ defineExpose({
455
493
  </slot>
456
494
 
457
495
  <span :class="ui.itemLabel({ class: props.ui?.itemLabel })">
458
- <slot name="item-label" :item="(item as T)" :index="index">
459
- {{ typeof item === 'object' ? get(item, props.labelKey as string) : item }}
496
+ <slot name="item-label" :item="(item as NestedItem<T>)" :index="index">
497
+ {{ isInputItem(item) ? get(item, props.labelKey as string) : item }}
460
498
  </slot>
461
499
  </span>
462
500
 
463
501
  <span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
464
- <slot name="item-trailing" :item="(item as T)" :index="index" />
502
+ <slot name="item-trailing" :item="(item as NestedItem<T>)" :index="index" />
465
503
 
466
504
  <ComboboxItemIndicator as-child>
467
505
  <UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: props.ui?.itemTrailingIcon })" />
@@ -102,6 +102,7 @@ defineOptions({ inheritAttrs: false })
102
102
  const props = withDefaults(defineProps<LinkProps>(), {
103
103
  as: 'button',
104
104
  type: 'button',
105
+ ariaCurrentValue: 'page',
105
106
  active: undefined,
106
107
  activeClass: '',
107
108
  inactiveClass: ''
@@ -178,6 +179,7 @@ function resolveLinkClass({ route, isActive, isExactActive }: any) {
178
179
  <slot
179
180
  v-bind="{
180
181
  ...$attrs,
182
+ ...(exact && isExactActive ? { 'aria-current': props.ariaCurrentValue } : {}),
181
183
  as,
182
184
  type,
183
185
  disabled,
@@ -194,6 +196,7 @@ function resolveLinkClass({ route, isActive, isExactActive }: any) {
194
196
  v-else
195
197
  v-bind="{
196
198
  ...$attrs,
199
+ ...(exact && isExactActive ? { 'aria-current': props.ariaCurrentValue } : {}),
197
200
  as,
198
201
  type,
199
202
  disabled,
@@ -10,6 +10,7 @@ export interface LinkBaseProps {
10
10
  navigate?: (e: MouseEvent) => void
11
11
  target?: LinkProps['target']
12
12
  rel?: LinkProps['rel']
13
+ active?: boolean
13
14
  isExternal?: boolean
14
15
  }
15
16
  </script>
@@ -67,7 +67,7 @@ export interface ModalSlots {
67
67
  header(props?: {}): any
68
68
  title(props?: {}): any
69
69
  description(props?: {}): any
70
- close(props: { ui: any }): any
70
+ close(props: { ui: ReturnType<typeof modal> }): any
71
71
  body(props?: {}): any
72
72
  footer(props?: {}): any
73
73
  }