@nuxt/ui 3.0.0 → 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 (98) hide show
  1. package/.nuxt/ui/button.ts +36 -36
  2. package/.nuxt/ui/calendar.ts +1 -1
  3. package/.nuxt/ui/container.ts +1 -1
  4. package/.nuxt/ui/drawer.ts +8 -5
  5. package/.nuxt/ui/navigation-menu.ts +2 -2
  6. package/.nuxt/ui/switch.ts +1 -1
  7. package/.nuxt/ui/tabs.ts +1 -1
  8. package/.nuxt/ui.css +1 -1
  9. package/README.md +11 -11
  10. package/cli/package.json +1 -1
  11. package/dist/module.cjs +2 -2
  12. package/dist/module.d.cts +8 -7
  13. package/dist/module.d.mts +8 -7
  14. package/dist/module.d.ts +8 -7
  15. package/dist/module.json +2 -2
  16. package/dist/module.mjs +2 -2
  17. package/dist/runtime/components/Accordion.vue +11 -8
  18. package/dist/runtime/components/Alert.vue +1 -1
  19. package/dist/runtime/components/App.vue +1 -1
  20. package/dist/runtime/components/Avatar.vue +7 -5
  21. package/dist/runtime/components/Breadcrumb.vue +13 -10
  22. package/dist/runtime/components/Button.vue +1 -1
  23. package/dist/runtime/components/Calendar.vue +40 -11
  24. package/dist/runtime/components/Carousel.vue +7 -3
  25. package/dist/runtime/components/ColorPicker.vue +2 -0
  26. package/dist/runtime/components/CommandPalette.vue +14 -11
  27. package/dist/runtime/components/ContextMenu.vue +31 -17
  28. package/dist/runtime/components/ContextMenuContent.vue +22 -15
  29. package/dist/runtime/components/Drawer.vue +10 -15
  30. package/dist/runtime/components/DropdownMenu.vue +29 -16
  31. package/dist/runtime/components/DropdownMenuContent.vue +22 -15
  32. package/dist/runtime/components/Form.vue +22 -1
  33. package/dist/runtime/components/FormField.vue +6 -0
  34. package/dist/runtime/components/InputMenu.vue +93 -55
  35. package/dist/runtime/components/Link.vue +3 -0
  36. package/dist/runtime/components/LinkBase.vue +1 -0
  37. package/dist/runtime/components/Modal.vue +11 -8
  38. package/dist/runtime/components/NavigationMenu.vue +53 -27
  39. package/dist/runtime/components/PinInput.vue +2 -0
  40. package/dist/runtime/components/Popover.vue +3 -2
  41. package/dist/runtime/components/RadioGroup.vue +25 -14
  42. package/dist/runtime/components/Select.vue +70 -43
  43. package/dist/runtime/components/SelectMenu.vue +84 -49
  44. package/dist/runtime/components/Slideover.vue +11 -8
  45. package/dist/runtime/components/Stepper.vue +13 -10
  46. package/dist/runtime/components/Table.vue +8 -3
  47. package/dist/runtime/components/Tabs.vue +8 -5
  48. package/dist/runtime/components/Toast.vue +2 -2
  49. package/dist/runtime/components/Tooltip.vue +3 -2
  50. package/dist/runtime/components/Tree.vue +48 -32
  51. package/dist/runtime/composables/defineLocale.js +1 -0
  52. package/dist/runtime/composables/useFormField.js +1 -1
  53. package/dist/runtime/composables/useKbd.js +1 -1
  54. package/dist/runtime/composables/useLocale.js +2 -2
  55. package/dist/runtime/composables/useOverlay.d.ts +3 -2
  56. package/dist/runtime/composables/useOverlay.js +1 -1
  57. package/dist/runtime/locale/ca.d.ts +3 -0
  58. package/dist/runtime/locale/ca.js +54 -0
  59. package/dist/runtime/locale/ckb.d.ts +3 -0
  60. package/dist/runtime/locale/ckb.js +55 -0
  61. package/dist/runtime/locale/hy.d.ts +3 -0
  62. package/dist/runtime/locale/hy.js +54 -0
  63. package/dist/runtime/locale/index.d.ts +6 -0
  64. package/dist/runtime/locale/index.js +6 -0
  65. package/dist/runtime/locale/ro.d.ts +3 -0
  66. package/dist/runtime/locale/ro.js +54 -0
  67. package/dist/runtime/locale/ur.d.ts +3 -0
  68. package/dist/runtime/locale/ur.js +55 -0
  69. package/dist/runtime/locale/uz.d.ts +3 -0
  70. package/dist/runtime/locale/uz.js +54 -0
  71. package/dist/runtime/plugins/colors.js +19 -15
  72. package/dist/runtime/types/form.d.ts +2 -3
  73. package/dist/runtime/types/index.d.ts +2 -0
  74. package/dist/runtime/types/index.js +2 -0
  75. package/dist/runtime/types/utils.d.ts +28 -8
  76. package/dist/runtime/utils/form.d.ts +0 -4
  77. package/dist/runtime/utils/form.js +2 -47
  78. package/dist/runtime/utils/index.d.ts +1 -0
  79. package/dist/runtime/utils/index.js +3 -0
  80. package/dist/runtime/utils/link.d.ts +2 -25
  81. package/dist/runtime/utils/link.js +31 -1
  82. package/dist/runtime/utils/tv.js +1 -1
  83. package/dist/runtime/vue/components/Link.vue +3 -0
  84. package/dist/runtime/vue/plugins/head.js +3 -0
  85. package/dist/runtime/vue/stubs.d.ts +7 -3
  86. package/dist/runtime/vue/stubs.js +12 -4
  87. package/dist/shared/{ui.COBs1u5H.cjs → ui.BL-XB3u1.cjs} +26 -26
  88. package/dist/shared/{ui.yjbGE7Dx.mjs → ui.BkNUP964.mjs} +26 -26
  89. package/dist/types.d.mts +7 -1
  90. package/dist/types.d.ts +7 -1
  91. package/dist/unplugin.cjs +6 -2
  92. package/dist/unplugin.d.cts +2 -1
  93. package/dist/unplugin.d.mts +2 -1
  94. package/dist/unplugin.d.ts +2 -1
  95. package/dist/unplugin.mjs +6 -2
  96. package/dist/vite.cjs +1 -1
  97. package/dist/vite.mjs +1 -1
  98. package/package.json +46 -29
@@ -1,19 +1,29 @@
1
1
  <script lang="ts">
2
2
  import type { VariantProps } from 'tailwind-variants'
3
- import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxArrowProps, AcceptableValue } from 'reka-ui'
3
+ import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxContentEmits, ComboboxArrowProps } 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 { tv } from '../utils/tv'
9
9
  import type { AvatarProps, ChipProps, InputProps } from '../types'
10
- import type { PartialString, MaybeArrayOfArray, MaybeArrayOfArrayItem, SelectModelValue, SelectModelValueEmits, SelectItemKey } from '../types/utils'
10
+ import type {
11
+ AcceptableValue,
12
+ ArrayOrNested,
13
+ GetItemKeys,
14
+ GetItemValue,
15
+ GetModelValue,
16
+ GetModelValueEmits,
17
+ NestedItem,
18
+ PartialString,
19
+ EmitsToProps
20
+ } from '../types/utils'
11
21
 
12
22
  const appConfigSelectMenu = _appConfig as AppConfig & { ui: { selectMenu: Partial<typeof theme> } }
13
23
 
14
24
  const selectMenu = tv({ extend: tv(theme), ...(appConfigSelectMenu.ui?.selectMenu || {}) })
15
25
 
16
- export interface SelectMenuItem {
26
+ interface _SelectMenuItem {
17
27
  label?: string
18
28
  /**
19
29
  * @IconifyIcon
@@ -26,13 +36,16 @@ export interface SelectMenuItem {
26
36
  * @defaultValue 'item'
27
37
  */
28
38
  type?: 'label' | 'separator' | 'item'
39
+ value?: string | number
29
40
  disabled?: boolean
30
41
  onSelect?(e?: Event): void
42
+ [key: string]: any
31
43
  }
44
+ export type SelectMenuItem = _SelectMenuItem | AcceptableValue | boolean
32
45
 
33
46
  type SelectMenuVariants = VariantProps<typeof selectMenu>
34
47
 
35
- export interface SelectMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<SelectMenuItem | AcceptableValue | boolean> = MaybeArrayOfArray<SelectMenuItem | 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 SelectMenuProps<T extends ArrayOrNested<SelectMenuItem> = ArrayOrNested<SelectMenuItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false> extends Pick<ComboboxRootProps<T>, 'open' | 'defaultOpen' | 'disabled' | 'name' | 'resetSearchTermOnBlur' | 'highlightOnHover'>, UseComponentIconsProps {
36
49
  id?: string
37
50
  /** The placeholder text when the select is empty. */
38
51
  placeholder?: string
@@ -72,7 +85,7 @@ export interface SelectMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends M
72
85
  * The content of the menu.
73
86
  * @defaultValue { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }
74
87
  */
75
- content?: Omit<ComboboxContentProps, 'as' | 'asChild' | 'forceMount'>
88
+ content?: Omit<ComboboxContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<ComboboxContentEmits>>
76
89
  /**
77
90
  * Display an arrow alongside the menu.
78
91
  * @defaultValue false
@@ -88,17 +101,17 @@ export interface SelectMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends M
88
101
  * When `items` is an array of objects, select the field to use as the value instead of the object itself.
89
102
  * @defaultValue undefined
90
103
  */
91
- valueKey?: V
104
+ valueKey?: VK
92
105
  /**
93
106
  * When `items` is an array of objects, select the field to use as the label.
94
107
  * @defaultValue 'label'
95
108
  */
96
- labelKey?: V
97
- items?: I
109
+ labelKey?: keyof NestedItem<T>
110
+ items?: T
98
111
  /** The value of the SelectMenu when initially rendered. Use when you do not need to control the state of the SelectMenu. */
99
- defaultValue?: SelectModelValue<T, V, M>
112
+ defaultValue?: GetModelValue<T, VK, M>
100
113
  /** The controlled value of the SelectMenu. Can be binded-with with `v-model`. */
101
- modelValue?: SelectModelValue<T, V, M>
114
+ modelValue?: GetModelValue<T, VK, M>
102
115
  /** Whether multiple options can be selected or not. */
103
116
  multiple?: M & boolean
104
117
  /** Highlight the ring color like a focus state. */
@@ -122,19 +135,29 @@ export interface SelectMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends M
122
135
  ui?: PartialString<typeof selectMenu.slots>
123
136
  }
124
137
 
125
- export type SelectMenuEmits<T, V, M extends boolean> = Omit<ComboboxRootEmits<T>, 'update:modelValue'> & {
138
+ export type SelectMenuEmits<A extends ArrayOrNested<SelectMenuItem>, VK extends GetItemKeys<A> | undefined, M extends boolean> = Pick<ComboboxRootEmits, 'update:open'> & {
126
139
  change: [payload: Event]
127
140
  blur: [payload: FocusEvent]
128
141
  focus: [payload: FocusEvent]
129
142
  create: [item: string]
130
- } & SelectModelValueEmits<T, V, M>
131
-
132
- type SlotProps<T> = (props: { item: T, index: number }) => any
133
-
134
- export interface SelectMenuSlots<T, M extends boolean> {
135
- 'leading'(props: { modelValue?: M extends true ? T[] : T, open: boolean, ui: any }): any
136
- 'default'(props: { modelValue?: M extends true ? T[] : T, open: boolean }): any
137
- 'trailing'(props: { modelValue?: M extends true ? T[] : T, open: boolean, ui: any }): any
143
+ /** Event handler when highlighted element changes. */
144
+ highlight: [payload: {
145
+ ref: HTMLElement
146
+ value: GetModelValue<A, VK, M>
147
+ } | undefined]
148
+ } & GetModelValueEmits<A, VK, M>
149
+
150
+ type SlotProps<T extends SelectMenuItem> = (props: { item: T, index: number }) => any
151
+
152
+ export interface SelectMenuSlots<
153
+ A extends ArrayOrNested<SelectMenuItem> = ArrayOrNested<SelectMenuItem>,
154
+ VK extends GetItemKeys<A> | undefined = undefined,
155
+ M extends boolean = false,
156
+ T extends NestedItem<A> = NestedItem<A>
157
+ > {
158
+ 'leading'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean, ui: ReturnType<typeof selectMenu> }): any
159
+ 'default'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean }): any
160
+ 'trailing'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean, ui: ReturnType<typeof selectMenu> }): any
138
161
  'empty'(props: { searchTerm?: string }): any
139
162
  'item': SlotProps<T>
140
163
  'item-leading': SlotProps<T>
@@ -144,7 +167,7 @@ export interface SelectMenuSlots<T, M extends boolean> {
144
167
  }
145
168
  </script>
146
169
 
147
- <script setup lang="ts" generic="T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<SelectMenuItem | AcceptableValue | boolean> = MaybeArrayOfArray<SelectMenuItem | AcceptableValue | boolean>, V extends SelectItemKey<T> | undefined = undefined, M extends boolean = false">
170
+ <script setup lang="ts" generic="T extends ArrayOrNested<SelectMenuItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false">
148
171
  import { computed, toRef, toRaw } from 'vue'
149
172
  import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxPortal, ComboboxContent, ComboboxViewport, ComboboxEmpty, ComboboxGroup, ComboboxLabel, ComboboxSeparator, ComboboxItem, ComboboxItemIndicator, FocusScope, useForwardPropsEmits, useFilter } from 'reka-ui'
150
173
  import { defu } from 'defu'
@@ -154,7 +177,7 @@ import { useButtonGroup } from '../composables/useButtonGroup'
154
177
  import { useComponentIcons } from '../composables/useComponentIcons'
155
178
  import { useFormField } from '../composables/useFormField'
156
179
  import { useLocale } from '../composables/useLocale'
157
- import { get, compare } from '../utils'
180
+ import { compare, get, isArrayOfArray } from '../utils'
158
181
  import UIcon from './Icon.vue'
159
182
  import UAvatar from './Avatar.vue'
160
183
  import UChip from './Chip.vue'
@@ -162,14 +185,14 @@ import UInput from './Input.vue'
162
185
 
163
186
  defineOptions({ inheritAttrs: false })
164
187
 
165
- const props = withDefaults(defineProps<SelectMenuProps<T, I, V, M>>(), {
188
+ const props = withDefaults(defineProps<SelectMenuProps<T, VK, M>>(), {
166
189
  portal: true,
167
190
  searchInput: true,
168
191
  labelKey: 'label' as never,
169
192
  resetSearchTermOnBlur: true
170
193
  })
171
- const emits = defineEmits<SelectMenuEmits<T, V, M>>()
172
- const slots = defineSlots<SelectMenuSlots<T, M>>()
194
+ const emits = defineEmits<SelectMenuEmits<T, VK, M>>()
195
+ const slots = defineSlots<SelectMenuSlots<T, VK, M>>()
173
196
 
174
197
  const searchTerm = defineModel<string>('searchTerm', { default: '' })
175
198
 
@@ -201,7 +224,7 @@ const ui = computed(() => selectMenu({
201
224
  buttonGroup: orientation.value
202
225
  }))
203
226
 
204
- function displayValue(value: T | T[]): string {
227
+ function displayValue(value: GetItemValue<T, VK> | GetItemValue<T, VK>[]): string {
205
228
  if (props.multiple && Array.isArray(value)) {
206
229
  return value.map(v => displayValue(v)).filter(Boolean).join(', ')
207
230
  }
@@ -214,7 +237,13 @@ function displayValue(value: T | T[]): string {
214
237
  return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
215
238
  }
216
239
 
217
- const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0]) ? props.items : [props.items]) as SelectMenuItem[][] : [])
240
+ const groups = computed<SelectMenuItem[][]>(() =>
241
+ props.items?.length
242
+ ? isArrayOfArray(props.items)
243
+ ? props.items
244
+ : [props.items]
245
+ : []
246
+ )
218
247
  // eslint-disable-next-line vue/no-dupe-keys
219
248
  const items = computed(() => groups.value.flatMap(group => group) as T[])
220
249
 
@@ -226,8 +255,8 @@ const filteredGroups = computed(() => {
226
255
  const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey] as string[]
227
256
 
228
257
  return groups.value.map(items => items.filter((item) => {
229
- if (typeof item !== 'object') {
230
- return contains(item, searchTerm.value)
258
+ if (typeof item !== 'object' || item === null) {
259
+ return contains(String(item), searchTerm.value)
231
260
  }
232
261
 
233
262
  if (item.type && ['label', 'separator'].includes(item.type)) {
@@ -235,19 +264,21 @@ const filteredGroups = computed(() => {
235
264
  }
236
265
 
237
266
  return fields.some(field => contains(get(item, field), searchTerm.value))
238
- })).filter(group => group.filter(item => !item.type || !['label', 'separator'].includes(item.type)).length > 0)
267
+ })).filter(group => group.filter(item =>
268
+ !isSelectItem(item) || (!item.type || !['label', 'separator'].includes(item.type))
269
+ ).length > 0)
239
270
  })
240
- const filteredItems = computed(() => filteredGroups.value.flatMap(group => group) as T[])
271
+ const filteredItems = computed(() => filteredGroups.value.flatMap(group => group))
241
272
 
242
273
  const createItem = computed(() => {
243
274
  if (!props.createItem || !searchTerm.value) {
244
275
  return false
245
276
  }
246
277
 
247
- const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } as T : searchTerm.value
278
+ const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } as NestedItem<T> : searchTerm.value
248
279
 
249
280
  if ((typeof props.createItem === 'object' && props.createItem.when === 'always') || props.createItem === 'always') {
250
- return !filteredItems.value.find(item => compare(item, newItem, props.valueKey))
281
+ return !filteredItems.value.find(item => compare(item, newItem, String(props.valueKey)))
251
282
  }
252
283
 
253
284
  return !filteredItems.value.length
@@ -290,6 +321,10 @@ function onUpdateOpen(value: boolean) {
290
321
  clearTimeout(timeoutId)
291
322
  }
292
323
  }
324
+
325
+ function isSelectItem(item: SelectMenuItem): item is _SelectMenuItem {
326
+ return typeof item === 'object' && item !== null
327
+ }
293
328
  </script>
294
329
 
295
330
  <!-- eslint-disable vue/no-template-shadow -->
@@ -324,14 +359,14 @@ function onUpdateOpen(value: boolean) {
324
359
  <ComboboxAnchor as-child>
325
360
  <ComboboxTrigger :class="ui.base({ class: [props.class, props.ui?.base] })" tabindex="0">
326
361
  <span v-if="isLeading || !!avatar || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
327
- <slot name="leading" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :ui="ui">
362
+ <slot name="leading" :model-value="(modelValue as GetModelValue<T, VK, M>)" :open="open" :ui="ui">
328
363
  <UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
329
364
  <UAvatar v-else-if="!!avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
330
365
  </slot>
331
366
  </span>
332
367
 
333
- <slot :model-value="(modelValue as M extends true ? T[] : T)" :open="open">
334
- <template v-for="displayedModelValue in [displayValue(modelValue as M extends true ? T[] : T)]" :key="displayedModelValue">
368
+ <slot :model-value="(modelValue as GetModelValue<T, VK, M>)" :open="open">
369
+ <template v-for="displayedModelValue in [displayValue(modelValue as GetModelValue<T, VK, M>)]" :key="displayedModelValue">
335
370
  <span v-if="displayedModelValue" :class="ui.value({ class: props.ui?.value })">
336
371
  {{ displayedModelValue }}
337
372
  </span>
@@ -342,7 +377,7 @@ function onUpdateOpen(value: boolean) {
342
377
  </slot>
343
378
 
344
379
  <span v-if="isTrailing || !!slots.trailing" :class="ui.trailing({ class: props.ui?.trailing })">
345
- <slot name="trailing" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :ui="ui">
380
+ <slot name="trailing" :model-value="(modelValue as GetModelValue<T, VK, M>)" :open="open" :ui="ui">
346
381
  <UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
347
382
  </slot>
348
383
  </span>
@@ -367,25 +402,25 @@ function onUpdateOpen(value: boolean) {
367
402
 
368
403
  <ComboboxGroup v-for="(group, groupIndex) in filteredGroups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
369
404
  <template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
370
- <ComboboxLabel v-if="item?.type === 'label'" :class="ui.label({ class: props.ui?.label })">
405
+ <ComboboxLabel v-if="isSelectItem(item) && item.type === 'label'" :class="ui.label({ class: props.ui?.label })">
371
406
  {{ get(item, props.labelKey as string) }}
372
407
  </ComboboxLabel>
373
408
 
374
- <ComboboxSeparator v-else-if="item?.type === 'separator'" :class="ui.separator({ class: props.ui?.separator })" />
409
+ <ComboboxSeparator v-else-if="isSelectItem(item) && item.type === 'separator'" :class="ui.separator({ class: props.ui?.separator })" />
375
410
 
376
411
  <ComboboxItem
377
412
  v-else
378
413
  :class="ui.item({ class: props.ui?.item })"
379
- :disabled="item.disabled"
380
- :value="valueKey && typeof item === 'object' ? get(item, props.valueKey as string) : item"
381
- @select="item.onSelect"
414
+ :disabled="isSelectItem(item) && item.disabled"
415
+ :value="props.valueKey && isSelectItem(item) ? get(item, props.valueKey as string) : item"
416
+ @select="isSelectItem(item) && item.onSelect"
382
417
  >
383
- <slot name="item" :item="(item as T)" :index="index">
384
- <slot name="item-leading" :item="(item as T)" :index="index">
385
- <UIcon v-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
386
- <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 })" />
418
+ <slot name="item" :item="(item as NestedItem<T>)" :index="index">
419
+ <slot name="item-leading" :item="(item as NestedItem<T>)" :index="index">
420
+ <UIcon v-if="isSelectItem(item) && item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
421
+ <UAvatar v-else-if="isSelectItem(item) && item.avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
387
422
  <UChip
388
- v-else-if="item.chip"
423
+ v-else-if="isSelectItem(item) && item.chip"
389
424
  :size="((props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()) as ChipProps['size'])"
390
425
  inset
391
426
  standalone
@@ -395,13 +430,13 @@ function onUpdateOpen(value: boolean) {
395
430
  </slot>
396
431
 
397
432
  <span :class="ui.itemLabel({ class: props.ui?.itemLabel })">
398
- <slot name="item-label" :item="(item as T)" :index="index">
399
- {{ typeof item === 'object' ? get(item, props.labelKey as string) : item }}
433
+ <slot name="item-label" :item="(item as NestedItem<T>)" :index="index">
434
+ {{ isSelectItem(item) ? get(item, props.labelKey as string) : item }}
400
435
  </slot>
401
436
  </span>
402
437
 
403
438
  <span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
404
- <slot name="item-trailing" :item="(item as T)" :index="index" />
439
+ <slot name="item-trailing" :item="(item as NestedItem<T>)" :index="index" />
405
440
 
406
441
  <ComboboxItemIndicator as-child>
407
442
  <UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: props.ui?.itemTrailingIcon })" />
@@ -1,11 +1,12 @@
1
1
  <script lang="ts">
2
2
  import type { VariantProps } from 'tailwind-variants'
3
- import type { DialogRootProps, DialogRootEmits, DialogContentProps } from 'reka-ui'
3
+ import type { DialogRootProps, DialogRootEmits, DialogContentProps, DialogContentEmits } 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/slideover'
7
7
  import { tv } from '../utils/tv'
8
8
  import type { ButtonProps } from '../types'
9
+ import type { EmitsToProps } from '../types/utils'
9
10
 
10
11
  const appConfigSlideover = _appConfig as AppConfig & { ui: { slideover: Partial<typeof theme> } }
11
12
 
@@ -17,7 +18,7 @@ export interface SlideoverProps extends DialogRootProps {
17
18
  title?: string
18
19
  description?: string
19
20
  /** The content of the slideover. */
20
- content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'>
21
+ content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<DialogContentEmits>>
21
22
  /**
22
23
  * Render an overlay behind the slideover.
23
24
  * @defaultValue true
@@ -69,7 +70,7 @@ export interface SlideoverSlots {
69
70
  header(props?: {}): any
70
71
  title(props?: {}): any
71
72
  description(props?: {}): any
72
- close(props: { ui: any }): any
73
+ close(props: { ui: ReturnType<typeof slideover> }): any
73
74
  body(props?: {}): any
74
75
  footer(props?: {}): any
75
76
  }
@@ -101,18 +102,20 @@ const appConfig = useAppConfig()
101
102
  const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen', 'modal'), emits)
102
103
  const contentProps = toRef(() => props.content)
103
104
  const contentEvents = computed(() => {
105
+ const events = {
106
+ closeAutoFocus: (e: Event) => e.preventDefault()
107
+ }
108
+
104
109
  if (!props.dismissible) {
105
110
  return {
106
111
  pointerDownOutside: (e: Event) => e.preventDefault(),
107
112
  interactOutside: (e: Event) => e.preventDefault(),
108
113
  escapeKeyDown: (e: Event) => e.preventDefault(),
109
- closeAutoFocus: (e: Event) => e.preventDefault()
114
+ ...events
110
115
  }
111
116
  }
112
117
 
113
- return {
114
- closeAutoFocus: (e: Event) => e.preventDefault()
115
- }
118
+ return events
116
119
  })
117
120
 
118
121
  const ui = computed(() => slideover({
@@ -162,7 +165,7 @@ const ui = computed(() => slideover({
162
165
  </DialogDescription>
163
166
  </div>
164
167
 
165
- <DialogClose as-child>
168
+ <DialogClose v-if="close || !!slots.close" as-child>
166
169
  <slot name="close" :ui="ui">
167
170
  <UButton
168
171
  v-if="close"
@@ -1,3 +1,4 @@
1
+ <!-- eslint-disable vue/block-tag-newline -->
1
2
  <script lang="ts">
2
3
  import type { VariantProps } from 'tailwind-variants'
3
4
  import type { StepperRootProps, StepperRootEmits } from 'reka-ui'
@@ -24,9 +25,10 @@ export interface StepperItem {
24
25
  icon?: string
25
26
  content?: string
26
27
  disabled?: boolean
28
+ [key: string]: any
27
29
  }
28
30
 
29
- export interface StepperProps<T extends StepperItem> extends Pick<StepperRootProps, 'linear'> {
31
+ export interface StepperProps<T extends StepperItem = StepperItem> extends Pick<StepperRootProps, 'linear'> {
30
32
  /**
31
33
  * The element or component this component should render as.
32
34
  * @defaultValue 'div'
@@ -55,19 +57,20 @@ export interface StepperProps<T extends StepperItem> extends Pick<StepperRootPro
55
57
  class?: any
56
58
  }
57
59
 
58
- export type StepperEmits<T> = Omit<StepperRootEmits, 'update:modelValue'> & {
60
+ export type StepperEmits<T extends StepperItem = StepperItem> = Omit<StepperRootEmits, 'update:modelValue'> & {
59
61
  next: [payload: T]
60
62
  prev: [payload: T]
61
63
  }
62
64
 
63
65
  type SlotProps<T extends StepperItem> = (props: { item: T }) => any
64
66
 
65
- export type StepperSlots<T extends StepperItem> = {
67
+ export type StepperSlots<T extends StepperItem = StepperItem> = {
66
68
  indicator: SlotProps<T>
67
69
  title: SlotProps<T>
68
70
  description: SlotProps<T>
69
71
  content: SlotProps<T>
70
- } & DynamicSlots<T, SlotProps<T>>
72
+ } & DynamicSlots<T>
73
+
71
74
  </script>
72
75
 
73
76
  <script setup lang="ts" generic="T extends StepperItem">
@@ -106,7 +109,7 @@ const currentStepIndex = computed({
106
109
  }
107
110
  })
108
111
 
109
- const currentStep = computed(() => props.items?.[currentStepIndex.value] as T)
112
+ const currentStep = computed(() => props.items?.[currentStepIndex.value])
110
113
  const hasNext = computed(() => currentStepIndex.value < props.items?.length - 1)
111
114
  const hasPrev = computed(() => currentStepIndex.value > 0)
112
115
 
@@ -114,13 +117,13 @@ defineExpose({
114
117
  next() {
115
118
  if (hasNext.value) {
116
119
  currentStepIndex.value += 1
117
- emits('next', currentStep.value)
120
+ emits('next', currentStep.value as T)
118
121
  }
119
122
  },
120
123
  prev() {
121
124
  if (hasPrev.value) {
122
125
  currentStepIndex.value -= 1
123
- emits('prev', currentStep.value)
126
+ emits('prev', currentStep.value as T)
124
127
  }
125
128
  },
126
129
  hasNext,
@@ -171,10 +174,10 @@ defineExpose({
171
174
  </StepperItem>
172
175
  </div>
173
176
 
174
- <div v-if="currentStep?.content || !!slots.content || (currentStep?.slot && !!slots[currentStep.slot]) || (currentStep?.value && !!slots[currentStep.value])" :class="ui.content({ class: props.ui?.description })">
177
+ <div v-if="currentStep?.content || !!slots.content || currentStep?.slot" :class="ui.content({ class: props.ui?.description })">
175
178
  <slot
176
- :name="!!slots[currentStep?.slot ?? currentStep.value!] ? currentStep.slot ?? currentStep.value : 'content'"
177
- :item="currentStep"
179
+ :name="((currentStep?.slot || 'content') as keyof StepperSlots<T>)"
180
+ :item="(currentStep as Extract<T, { slot: string }>)"
178
181
  >
179
182
  {{ currentStep?.content }}
180
183
  </slot>
@@ -75,6 +75,11 @@ export interface TableProps<T extends TableData> extends TableOptions<T> {
75
75
  data?: T[]
76
76
  columns?: TableColumn<T>[]
77
77
  caption?: string
78
+ /**
79
+ * The text to display when the table is empty.
80
+ * @defaultValue t('table.noData')
81
+ */
82
+ empty?: string
78
83
  /**
79
84
  * Whether the table should have a sticky header.
80
85
  * @defaultValue false
@@ -289,7 +294,7 @@ function handleRowSelect(row: TableRow<T>, e: Event) {
289
294
  return
290
295
  }
291
296
  const target = e.target as HTMLElement
292
- const isInteractive = target.closest('button')
297
+ const isInteractive = target.closest('button') || target.closest('a')
293
298
  if (isInteractive) {
294
299
  return
295
300
  }
@@ -308,7 +313,7 @@ defineExpose({
308
313
  <template>
309
314
  <Primitive :as="as" :class="ui.root({ class: [props.class, props.ui?.root] })">
310
315
  <table :class="ui.base({ class: [props.ui?.base] })">
311
- <caption v-if="caption" :class="ui.caption({ class: [props.ui?.caption] })">
316
+ <caption v-if="caption || !!slots.caption" :class="ui.caption({ class: [props.ui?.caption] })">
312
317
  <slot name="caption">
313
318
  {{ caption }}
314
319
  </slot>
@@ -369,7 +374,7 @@ defineExpose({
369
374
  <tr v-else>
370
375
  <td :colspan="columns?.length" :class="ui.empty({ class: props.ui?.empty })">
371
376
  <slot name="empty">
372
- {{ t('table.noData') }}
377
+ {{ empty || t('table.noData') }}
373
378
  </slot>
374
379
  </td>
375
380
  </tr>
@@ -1,3 +1,4 @@
1
+ <!-- eslint-disable vue/block-tag-newline -->
1
2
  <script lang="ts">
2
3
  import type { VariantProps } from 'tailwind-variants'
3
4
  import type { TabsRootProps, TabsRootEmits } from 'reka-ui'
@@ -24,11 +25,12 @@ export interface TabsItem {
24
25
  /** A unique value for the tab item. Defaults to the index. */
25
26
  value?: string | number
26
27
  disabled?: boolean
28
+ [key: string]: any
27
29
  }
28
30
 
29
31
  type TabsVariants = VariantProps<typeof tabs>
30
32
 
31
- export interface TabsProps<T> extends Pick<TabsRootProps<string | number>, 'defaultValue' | 'modelValue' | 'activationMode' | 'unmountOnHide'> {
33
+ export interface TabsProps<T extends TabsItem = TabsItem> extends Pick<TabsRootProps<string | number>, 'defaultValue' | 'modelValue' | 'activationMode' | 'unmountOnHide'> {
32
34
  /**
33
35
  * The element or component this component should render as.
34
36
  * @defaultValue 'div'
@@ -68,14 +70,15 @@ export interface TabsProps<T> extends Pick<TabsRootProps<string | number>, 'defa
68
70
 
69
71
  export interface TabsEmits extends TabsRootEmits<string | number> {}
70
72
 
71
- type SlotProps<T> = (props: { item: T, index: number }) => any
73
+ type SlotProps<T extends TabsItem> = (props: { item: T, index: number }) => any
72
74
 
73
- export type TabsSlots<T extends { slot?: string }> = {
75
+ export type TabsSlots<T extends TabsItem = TabsItem> = {
74
76
  leading: SlotProps<T>
75
77
  default: SlotProps<T>
76
78
  trailing: SlotProps<T>
77
79
  content: SlotProps<T>
78
- } & DynamicSlots<T, SlotProps<T>>
80
+ } & DynamicSlots<T, undefined, { index: number }>
81
+
79
82
  </script>
80
83
 
81
84
  <script setup lang="ts" generic="T extends TabsItem">
@@ -127,7 +130,7 @@ const ui = computed(() => tabs({
127
130
 
128
131
  <template v-if="!!content">
129
132
  <TabsContent v-for="(item, index) of items" :key="index" :value="item.value || String(index)" :class="ui.content({ class: props.ui?.content })">
130
- <slot :name="item.slot || 'content'" :item="item" :index="index">
133
+ <slot :name="((item.slot || 'content') as keyof TabsSlots<T>)" :item="(item as Extract<T, { slot: string; }>)" :index="index">
131
134
  {{ item.content }}
132
135
  </slot>
133
136
  </TabsContent>
@@ -66,7 +66,7 @@ export interface ToastSlots {
66
66
  title(props?: {}): any
67
67
  description(props?: {}): any
68
68
  actions(props?: {}): any
69
- close(props: { ui: any }): any
69
+ close(props: { ui: ReturnType<typeof toast> }): any
70
70
  }
71
71
  </script>
72
72
 
@@ -168,7 +168,7 @@ defineExpose({
168
168
  </slot>
169
169
  </template>
170
170
 
171
- <ToastClose as-child>
171
+ <ToastClose v-if="close || !!slots.close" as-child>
172
172
  <slot name="close" :ui="ui">
173
173
  <UButton
174
174
  v-if="close"
@@ -1,10 +1,11 @@
1
1
  <script lang="ts">
2
- import type { TooltipRootProps, TooltipRootEmits, TooltipContentProps, TooltipArrowProps } from 'reka-ui'
2
+ import type { TooltipRootProps, TooltipRootEmits, TooltipContentProps, TooltipContentEmits, TooltipArrowProps } from 'reka-ui'
3
3
  import type { AppConfig } from '@nuxt/schema'
4
4
  import _appConfig from '#build/app.config'
5
5
  import theme from '#build/ui/tooltip'
6
6
  import { tv } from '../utils/tv'
7
7
  import type { KbdProps } from '../types'
8
+ import type { EmitsToProps } from '../types/utils'
8
9
 
9
10
  const appConfigTooltip = _appConfig as AppConfig & { ui: { tooltip: Partial<typeof theme> } }
10
11
 
@@ -19,7 +20,7 @@ export interface TooltipProps extends TooltipRootProps {
19
20
  * The content of the tooltip.
20
21
  * @defaultValue { side: 'bottom', sideOffset: 8, collisionPadding: 8 }
21
22
  */
22
- content?: Omit<TooltipContentProps, 'as' | 'asChild'>
23
+ content?: Omit<TooltipContentProps, 'as' | 'asChild'> & Partial<EmitsToProps<TooltipContentEmits>>
23
24
  /**
24
25
  * Display an arrow alongside the tooltip.
25
26
  * @defaultValue false