@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,3 +1,4 @@
1
+ <!-- eslint-disable vue/block-tag-newline -->
1
2
  <script lang="ts">
2
3
  import type { AccordionRootProps, AccordionRootEmits } from 'reka-ui'
3
4
  import type { AppConfig } from '@nuxt/schema'
@@ -25,9 +26,10 @@ export interface AccordionItem {
25
26
  /** A unique value for the accordion item. Defaults to the index. */
26
27
  value?: string
27
28
  disabled?: boolean
29
+ [key: string]: any
28
30
  }
29
31
 
30
- export interface AccordionProps<T> extends Pick<AccordionRootProps, 'collapsible' | 'defaultValue' | 'modelValue' | 'type' | 'disabled' | 'unmountOnHide'> {
32
+ export interface AccordionProps<T extends AccordionItem = AccordionItem> extends Pick<AccordionRootProps, 'collapsible' | 'defaultValue' | 'modelValue' | 'type' | 'disabled' | 'unmountOnHide'> {
31
33
  /**
32
34
  * The element or component this component should render as.
33
35
  * @defaultValue 'div'
@@ -51,15 +53,16 @@ export interface AccordionProps<T> extends Pick<AccordionRootProps, 'collapsible
51
53
 
52
54
  export interface AccordionEmits extends AccordionRootEmits {}
53
55
 
54
- type SlotProps<T> = (props: { item: T, index: number, open: boolean }) => any
56
+ type SlotProps<T extends AccordionItem> = (props: { item: T, index: number, open: boolean }) => any
55
57
 
56
- export type AccordionSlots<T extends { slot?: string }> = {
58
+ export type AccordionSlots<T extends AccordionItem = AccordionItem> = {
57
59
  leading: SlotProps<T>
58
60
  default: SlotProps<T>
59
61
  trailing: SlotProps<T>
60
62
  content: SlotProps<T>
61
63
  body: SlotProps<T>
62
- } & DynamicSlots<T, SlotProps<T>>
64
+ } & DynamicSlots<T, 'body', { index: number, open: boolean }>
65
+
63
66
  </script>
64
67
 
65
68
  <script setup lang="ts" generic="T extends AccordionItem">
@@ -90,7 +93,7 @@ const ui = computed(() => accordion({
90
93
  <template>
91
94
  <AccordionRoot v-bind="rootProps" :class="ui.root({ class: [props.class, props.ui?.root] })">
92
95
  <AccordionItem
93
- v-for="(item, index) in items"
96
+ v-for="(item, index) in props.items"
94
97
  v-slot="{ open }"
95
98
  :key="index"
96
99
  :value="item.value || String(index)"
@@ -113,10 +116,10 @@ const ui = computed(() => accordion({
113
116
  </AccordionTrigger>
114
117
  </AccordionHeader>
115
118
 
116
- <AccordionContent v-if="item.content || !!slots.content || (item.slot && !!slots[item.slot]) || !!slots.body || (item.slot && !!slots[`${item.slot}-body`])" :class="ui.content({ class: props.ui?.content })">
117
- <slot :name="item.slot || 'content'" :item="item" :index="index" :open="open">
119
+ <AccordionContent v-if="item.content || !!slots.content || (item.slot && !!slots[item.slot as keyof AccordionSlots<T>]) || !!slots.body || (item.slot && !!slots[`${item.slot}-body` as keyof AccordionSlots<T>])" :class="ui.content({ class: props.ui?.content })">
120
+ <slot :name="((item.slot || 'content') as keyof AccordionSlots<T>)" :item="item" :index="index" :open="open">
118
121
  <div :class="ui.body({ class: props.ui?.body })">
119
- <slot :name="item.slot ? `${item.slot}-body`: 'body'" :item="item" :index="index" :open="open">
122
+ <slot :name="((item.slot ? `${item.slot}-body`: 'body') as keyof AccordionSlots<T>)" :item="item" :index="index" :open="open">
120
123
  {{ item.content }}
121
124
  </slot>
122
125
  </div>
@@ -71,7 +71,7 @@ export interface AlertSlots {
71
71
  title(props?: {}): any
72
72
  description(props?: {}): any
73
73
  actions(props?: {}): any
74
- close(props: { ui: any }): any
74
+ close(props: { ui: ReturnType<typeof alert> }): any
75
75
  }
76
76
  </script>
77
77
 
@@ -17,7 +17,7 @@ export default {
17
17
  }
18
18
  </script>
19
19
 
20
- <script setup lang="ts" generic="T extends Messages = Messages">
20
+ <script setup lang="ts" generic="T extends Messages">
21
21
  import { toRef, useId, provide } from 'vue'
22
22
  import { ConfigProvider, TooltipProvider, useForwardProps } from 'reka-ui'
23
23
  import { reactivePick } from '@vueuse/core'
@@ -40,7 +40,7 @@ export interface AvatarSlots {
40
40
 
41
41
  <script setup lang="ts">
42
42
  import { ref, computed, watch } from 'vue'
43
- import { Primitive } from 'reka-ui'
43
+ import { Primitive, Slot } from 'reka-ui'
44
44
  import ImageComponent from '#build/ui-image-component'
45
45
  import { useAvatarGroup } from '../composables/useAvatarGroup'
46
46
  import UIcon from './Icon.vue'
@@ -98,9 +98,11 @@ function onError() {
98
98
  @error="onError"
99
99
  />
100
100
 
101
- <slot v-else>
102
- <UIcon v-if="icon" :name="icon" :class="ui.icon({ class: props.ui?.icon })" />
103
- <span v-else :class="ui.fallback({ class: props.ui?.fallback })">{{ fallback || '&nbsp;' }}</span>
104
- </slot>
101
+ <Slot v-else v-bind="$attrs">
102
+ <slot>
103
+ <UIcon v-if="icon" :name="icon" :class="ui.icon({ class: props.ui?.icon })" />
104
+ <span v-else :class="ui.fallback({ class: props.ui?.fallback })">{{ fallback || '&nbsp;' }}</span>
105
+ </slot>
106
+ </Slot>
105
107
  </Primitive>
106
108
  </template>
@@ -1,3 +1,4 @@
1
+ <!-- eslint-disable vue/block-tag-newline -->
1
2
  <script lang="ts">
2
3
  import type { AppConfig } from '@nuxt/schema'
3
4
  import _appConfig from '#build/app.config'
@@ -18,9 +19,10 @@ export interface BreadcrumbItem extends Omit<LinkProps, 'raw' | 'custom'> {
18
19
  icon?: string
19
20
  avatar?: AvatarProps
20
21
  slot?: string
22
+ [key: string]: any
21
23
  }
22
24
 
23
- export interface BreadcrumbProps<T> {
25
+ export interface BreadcrumbProps<T extends BreadcrumbItem = BreadcrumbItem> {
24
26
  /**
25
27
  * The element or component this component should render as.
26
28
  * @defaultValue 'nav'
@@ -42,15 +44,16 @@ export interface BreadcrumbProps<T> {
42
44
  ui?: PartialString<typeof breadcrumb.slots>
43
45
  }
44
46
 
45
- type SlotProps<T> = (props: { item: T, index: number, active?: boolean }) => any
47
+ type SlotProps<T extends BreadcrumbItem> = (props: { item: T, index: number, active?: boolean }) => any
46
48
 
47
- export type BreadcrumbSlots<T extends { slot?: string }> = {
49
+ export type BreadcrumbSlots<T extends BreadcrumbItem = BreadcrumbItem> = {
48
50
  'item': SlotProps<T>
49
51
  'item-leading': SlotProps<T>
50
52
  'item-label': SlotProps<T>
51
53
  'item-trailing': SlotProps<T>
52
- 'separator'(props?: {}): any
53
- } & DynamicSlots<T, SlotProps<T>>
54
+ 'separator': any
55
+ } & DynamicSlots<T, 'leading' | 'label' | 'trailing', { index: number, active?: boolean }>
56
+
54
57
  </script>
55
58
 
56
59
  <script setup lang="ts" generic="T extends BreadcrumbItem">
@@ -86,19 +89,19 @@ const ui = breadcrumb()
86
89
  <li :class="ui.item({ class: props.ui?.item })">
87
90
  <ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item)" custom>
88
91
  <ULinkBase v-bind="slotProps" as="span" :aria-current="active && (index === items!.length - 1) ? 'page' : undefined" :class="ui.link({ class: [props.ui?.link, item.class], active: index === items!.length - 1, disabled: !!item.disabled, to: !!item.to })">
89
- <slot :name="item.slot || 'item'" :item="item" :index="index">
90
- <slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="item" :active="index === items!.length - 1" :index="index">
92
+ <slot :name="((item.slot || 'item') as keyof BreadcrumbSlots<T>)" :item="item" :index="index">
93
+ <slot :name="((item.slot ? `${item.slot}-leading`: 'item-leading') as keyof BreadcrumbSlots<T>)" :item="item" :active="index === items!.length - 1" :index="index">
91
94
  <UIcon v-if="item.icon" :name="item.icon" :class="ui.linkLeadingIcon({ class: props.ui?.linkLeadingIcon, active: index === items!.length - 1 })" />
92
95
  <UAvatar v-else-if="item.avatar" :size="((props.ui?.linkLeadingAvatarSize || ui.linkLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.linkLeadingAvatar({ class: props.ui?.linkLeadingAvatar, active: index === items!.length - 1 })" />
93
96
  </slot>
94
97
 
95
- <span v-if="get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.linkLabel({ class: props.ui?.linkLabel })">
96
- <slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="item" :active="index === items!.length - 1" :index="index">
98
+ <span v-if="get(item, props.labelKey as string) || !!slots[(item.slot ? `${item.slot}-label`: 'item-label') as keyof BreadcrumbSlots<T>]" :class="ui.linkLabel({ class: props.ui?.linkLabel })">
99
+ <slot :name="((item.slot ? `${item.slot}-label`: 'item-label') as keyof BreadcrumbSlots<T>)" :item="item" :active="index === items!.length - 1" :index="index">
97
100
  {{ get(item, props.labelKey as string) }}
98
101
  </slot>
99
102
  </span>
100
103
 
101
- <slot :name="item.slot ? `${item.slot}-trailing`: 'item-trailing'" :item="item" :active="index === items!.length - 1" :index="index" />
104
+ <slot :name="((item.slot ? `${item.slot}-trailing`: 'item-trailing') as keyof BreadcrumbSlots<T>)" :item="item" :active="index === items!.length - 1" :index="index" />
102
105
  </slot>
103
106
  </ULinkBase>
104
107
  </ULink>
@@ -125,7 +125,7 @@ const ui = computed(() => tv({
125
125
  :type="type"
126
126
  :disabled="disabled || isLoading"
127
127
  :class="ui.base({ class: [props.class, props.ui?.base] })"
128
- v-bind="omit(linkProps, ['type', 'disabled'])"
128
+ v-bind="omit(linkProps, ['type', 'disabled', 'onClick'])"
129
129
  custom
130
130
  >
131
131
  <ULinkBase
@@ -1,8 +1,9 @@
1
1
  <script lang="ts">
2
2
  import type { VariantProps } from 'tailwind-variants'
3
- import type { CalendarRootProps, CalendarRootEmits, RangeCalendarRootEmits, DateRange, CalendarCellTriggerProps } from 'reka-ui'
3
+ import type { CalendarRootProps, CalendarRootEmits, RangeCalendarRootProps, RangeCalendarRootEmits, DateRange, CalendarCellTriggerProps } from 'reka-ui'
4
4
  import type { DateValue } from '@internationalized/date'
5
5
  import type { AppConfig } from '@nuxt/schema'
6
+ import type { ButtonProps } from '../types'
6
7
  import _appConfig from '#build/app.config'
7
8
  import theme from '#build/ui/calendar'
8
9
  import { tv } from '../utils/tv'
@@ -14,13 +15,21 @@ const calendar = tv({ extend: tv(theme), ...(appConfigCalendar.ui?.calendar || {
14
15
 
15
16
  type CalendarVariants = VariantProps<typeof calendar>
16
17
 
17
- type CalendarModelValue<R extends boolean = false, M extends boolean = false> = R extends true
18
+ type CalendarDefaultValue<R extends boolean = false, M extends boolean = false> = R extends true
18
19
  ? DateRange
19
20
  : M extends true
20
21
  ? DateValue[]
21
22
  : DateValue
23
+ type CalendarModelValue<R extends boolean = false, M extends boolean = false> = R extends true
24
+ ? (DateRange | null)
25
+ : M extends true
26
+ ? (DateValue[] | undefined)
27
+ : (DateValue | undefined)
22
28
 
23
- export interface CalendarProps<R extends boolean, M extends boolean> extends Omit<CalendarRootProps, 'as' | 'asChild' | 'modelValue' | 'defaultValue' | 'dir' | 'locale' | 'calendarLabel' | 'multiple'> {
29
+ type _CalendarRootProps = Omit<CalendarRootProps, 'as' | 'asChild' | 'modelValue' | 'defaultValue' | 'dir' | 'locale' | 'calendarLabel' | 'multiple'>
30
+ type _RangeCalendarRootProps = Omit<RangeCalendarRootProps, 'as' | 'asChild' | 'modelValue' | 'defaultValue' | 'dir' | 'locale' | 'calendarLabel' | 'multiple'>
31
+
32
+ export interface CalendarProps<R extends boolean = false, M extends boolean = false> extends _RangeCalendarRootProps, _CalendarRootProps {
24
33
  /**
25
34
  * The element or component this component should render as.
26
35
  * @defaultValue 'div'
@@ -32,24 +41,44 @@ export interface CalendarProps<R extends boolean, M extends boolean> extends Omi
32
41
  * @IconifyIcon
33
42
  */
34
43
  nextYearIcon?: string
44
+ /**
45
+ * Configure the next year button.
46
+ * `{ color: 'neutral', variant: 'ghost' }`{lang="ts-type"}
47
+ */
48
+ nextYear?: ButtonProps
35
49
  /**
36
50
  * The icon to use for the next month control.
37
51
  * @defaultValue appConfig.ui.icons.chevronRight
38
52
  * @IconifyIcon
39
53
  */
40
54
  nextMonthIcon?: string
55
+ /**
56
+ * Configure the next month button.
57
+ * `{ color: 'neutral', variant: 'ghost' }`{lang="ts-type"}
58
+ */
59
+ nextMonth?: ButtonProps
41
60
  /**
42
61
  * The icon to use for the previous year control.
43
62
  * @defaultValue appConfig.ui.icons.chevronDoubleLeft
44
63
  * @IconifyIcon
45
64
  */
46
65
  prevYearIcon?: string
66
+ /**
67
+ * Configure the prev year button.
68
+ * `{ color: 'neutral', variant: 'ghost' }`{lang="ts-type"}
69
+ */
70
+ prevYear?: ButtonProps
47
71
  /**
48
72
  * The icon to use for the previous month control.
49
73
  * @defaultValue appConfig.ui.icons.chevronLeft
50
74
  * @IconifyIcon
51
75
  */
52
76
  prevMonthIcon?: string
77
+ /**
78
+ * Configure the prev month button.
79
+ * `{ color: 'neutral', variant: 'ghost' }`{lang="ts-type"}
80
+ */
81
+ prevMonth?: ButtonProps
53
82
  /**
54
83
  * @defaultValue 'primary'
55
84
  */
@@ -66,7 +95,7 @@ export interface CalendarProps<R extends boolean, M extends boolean> extends Omi
66
95
  monthControls?: boolean
67
96
  /** Show year controls */
68
97
  yearControls?: boolean
69
- defaultValue?: CalendarModelValue<R, M>
98
+ defaultValue?: CalendarDefaultValue<R, M>
70
99
  modelValue?: CalendarModelValue<R, M>
71
100
  class?: any
72
101
  ui?: PartialString<typeof calendar.slots>
@@ -83,7 +112,7 @@ export interface CalendarSlots {
83
112
  }
84
113
  </script>
85
114
 
86
- <script setup lang="ts" generic="R extends boolean = false, M extends boolean = false">
115
+ <script setup lang="ts" generic="R extends boolean, M extends boolean">
87
116
  import { computed } from 'vue'
88
117
  import { useForwardPropsEmits } from 'reka-ui'
89
118
  import { Calendar as SingleCalendar, RangeCalendar } from 'reka-ui/namespaced'
@@ -130,18 +159,18 @@ const Calendar = computed(() => props.range ? RangeCalendar : SingleCalendar)
130
159
  <Calendar.Root
131
160
  v-slot="{ weekDays, grid }"
132
161
  v-bind="rootProps"
133
- :model-value="(modelValue as CalendarModelValue<true & false>)"
134
- :default-value="(defaultValue as CalendarModelValue<true & false>)"
162
+ :model-value="modelValue"
163
+ :default-value="defaultValue"
135
164
  :locale="locale"
136
165
  :dir="dir"
137
166
  :class="ui.root({ class: [props.class, props.ui?.root] })"
138
167
  >
139
168
  <Calendar.Header :class="ui.header({ class: props.ui?.header })">
140
169
  <Calendar.Prev v-if="props.yearControls" :prev-page="(date: DateValue) => paginateYear(date, -1)" :aria-label="t('calendar.prevYear')" as-child>
141
- <UButton :icon="prevYearIcon" :size="props.size" color="neutral" variant="ghost" />
170
+ <UButton :icon="prevYearIcon" :size="props.size" color="neutral" variant="ghost" v-bind="props.prevYear" />
142
171
  </Calendar.Prev>
143
172
  <Calendar.Prev v-if="props.monthControls" :aria-label="t('calendar.prevMonth')" as-child>
144
- <UButton :icon="prevMonthIcon" :size="props.size" color="neutral" variant="ghost" />
173
+ <UButton :icon="prevMonthIcon" :size="props.size" color="neutral" variant="ghost" v-bind="props.prevMonth" />
145
174
  </Calendar.Prev>
146
175
  <Calendar.Heading v-slot="{ headingValue }" :class="ui.heading({ class: props.ui?.heading })">
147
176
  <slot name="heading" :value="headingValue">
@@ -149,10 +178,10 @@ const Calendar = computed(() => props.range ? RangeCalendar : SingleCalendar)
149
178
  </slot>
150
179
  </Calendar.Heading>
151
180
  <Calendar.Next v-if="props.monthControls" :aria-label="t('calendar.nextMonth')" as-child>
152
- <UButton :icon="nextMonthIcon" :size="props.size" color="neutral" variant="ghost" />
181
+ <UButton :icon="nextMonthIcon" :size="props.size" color="neutral" variant="ghost" v-bind="props.nextMonth" />
153
182
  </Calendar.Next>
154
183
  <Calendar.Next v-if="props.yearControls" :next-page="(date: DateValue) => paginateYear(date, 1)" :aria-label="t('calendar.nextYear')" as-child>
155
- <UButton :icon="nextYearIcon" :size="props.size" color="neutral" variant="ghost" />
184
+ <UButton :icon="nextYearIcon" :size="props.size" color="neutral" variant="ghost" v-bind="props.nextYear" />
156
185
  </Calendar.Next>
157
186
  </Calendar.Header>
158
187
  <div :class="ui.body({ class: props.ui?.body })">
@@ -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 { AppConfig } from '@nuxt/schema'
@@ -21,7 +22,9 @@ const carousel = tv({ extend: tv(theme), ...(appConfigCarousel.ui?.carousel || {
21
22
 
22
23
  type CarouselVariants = VariantProps<typeof carousel>
23
24
 
24
- export interface CarouselProps<T> extends Omit<EmblaOptionsType, 'axis' | 'container' | 'slides' | 'direction'> {
25
+ export type CarouselItem = AcceptableValue
26
+
27
+ export interface CarouselProps<T extends CarouselItem = CarouselItem> extends Omit<EmblaOptionsType, 'axis' | 'container' | 'slides' | 'direction'> {
25
28
  /**
26
29
  * The element or component this component should render as.
27
30
  * @defaultValue 'div'
@@ -99,12 +102,13 @@ export interface CarouselProps<T> extends Omit<EmblaOptionsType, 'axis' | 'conta
99
102
  ui?: PartialString<typeof carousel.slots>
100
103
  }
101
104
 
102
- export type CarouselSlots<T> = {
105
+ export type CarouselSlots<T extends CarouselItem = CarouselItem> = {
103
106
  default(props: { item: T, index: number }): any
104
107
  }
108
+
105
109
  </script>
106
110
 
107
- <script setup lang="ts" generic="T extends AcceptableValue">
111
+ <script setup lang="ts" generic="T extends CarouselItem">
108
112
  import { computed, ref, watch, onMounted } from 'vue'
109
113
  import useEmblaCarousel from 'embla-carousel-vue'
110
114
  import { Primitive, useForwardProps } from 'reka-ui'
@@ -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 { MaybeRefOrGetter } from '@vueuse/shared'
@@ -70,6 +71,7 @@ export type ColorPickerProps = {
70
71
  class?: any
71
72
  ui?: Partial<typeof colorPicker.slots>
72
73
  }
74
+
73
75
  </script>
74
76
 
75
77
  <script setup lang="ts">
@@ -1,3 +1,4 @@
1
+ <!-- eslint-disable vue/block-tag-newline -->
1
2
  <script lang="ts">
2
3
  import type { ListboxRootProps, ListboxRootEmits } from 'reka-ui'
3
4
  import type { FuseResult } from 'fuse.js'
@@ -8,7 +9,7 @@ import theme from '#build/ui/command-palette'
8
9
  import type { UseComponentIconsProps } from '../composables/useComponentIcons'
9
10
  import { tv } from '../utils/tv'
10
11
  import type { AvatarProps, ButtonProps, ChipProps, KbdProps, InputProps, LinkProps } from '../types'
11
- import type { DynamicSlots, PartialString } from '../types/utils'
12
+ import type { PartialString } from '../types/utils'
12
13
 
13
14
  const appConfigCommandPalette = _appConfig as AppConfig & { ui: { commandPalette: Partial<typeof theme> } }
14
15
 
@@ -30,6 +31,7 @@ export interface CommandPaletteItem extends Omit<LinkProps, 'type' | 'raw' | 'cu
30
31
  disabled?: boolean
31
32
  slot?: string
32
33
  onSelect?(e?: Event): void
34
+ [key: string]: any
33
35
  }
34
36
 
35
37
  export interface CommandPaletteGroup<T> {
@@ -72,7 +74,7 @@ export interface CommandPaletteProps<G, T> extends Pick<ListboxRootProps, 'multi
72
74
  selectedIcon?: string
73
75
  /**
74
76
  * The placeholder text for the input.
75
- * @defaultValue 'Type a command or search...'
77
+ * @defaultValue t('commandPalette.placeholder')
76
78
  */
77
79
  placeholder?: InputProps['placeholder']
78
80
  /**
@@ -124,12 +126,13 @@ type SlotProps<T> = (props: { item: T, index: number }) => any
124
126
 
125
127
  export type CommandPaletteSlots<G extends { slot?: string }, T extends { slot?: string }> = {
126
128
  'empty'(props: { searchTerm?: string }): any
127
- 'close'(props: { ui: any }): any
129
+ 'close'(props: { ui: ReturnType<typeof commandPalette> }): any
128
130
  'item': SlotProps<T>
129
131
  'item-leading': SlotProps<T>
130
132
  'item-label': SlotProps<T>
131
133
  'item-trailing': SlotProps<T>
132
- } & DynamicSlots<G, SlotProps<T>> & DynamicSlots<T, SlotProps<T>>
134
+ } & Record<string, SlotProps<G>> & Record<string, SlotProps<T>>
135
+
133
136
  </script>
134
137
 
135
138
  <script setup lang="ts" generic="G extends CommandPaletteGroup<T>, T extends CommandPaletteItem">
@@ -280,14 +283,14 @@ const groups = computed(() => {
280
283
 
281
284
  <ListboxContent :class="ui.content({ class: props.ui?.content })">
282
285
  <div v-if="groups?.length" :class="ui.viewport({ class: props.ui?.viewport })">
283
- <ListboxGroup v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
286
+ <ListboxGroup v-for="group in groups" :key="`group-${group.id}`" :class="ui.group({ class: props.ui?.group })">
284
287
  <ListboxGroupLabel v-if="get(group, props.labelKey as string)" :class="ui.label({ class: props.ui?.label })">
285
288
  {{ get(group, props.labelKey as string) }}
286
289
  </ListboxGroupLabel>
287
290
 
288
291
  <ListboxItem
289
292
  v-for="(item, index) in group.items"
290
- :key="`group-${groupIndex}-${index}`"
293
+ :key="`group-${group.id}-${index}`"
291
294
  :value="omit(item, ['matches' as any, 'group' as any, 'onSelect', 'labelHtml', 'suffixHtml'])"
292
295
  :disabled="item.disabled"
293
296
  as-child
@@ -295,8 +298,8 @@ const groups = computed(() => {
295
298
  >
296
299
  <ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item)" custom>
297
300
  <ULinkBase v-bind="slotProps" :class="ui.item({ class: props.ui?.item, active: active || item.active })">
298
- <slot :name="item.slot || group.slot || 'item'" :item="item" :index="index">
299
- <slot :name="item.slot ? `${item.slot}-leading` : group.slot ? `${group.slot}-leading` : `item-leading`" :item="item" :index="index">
301
+ <slot :name="((item.slot || group.slot || 'item') as keyof CommandPaletteSlots<G, T>)" :item="(item as any)" :index="index">
302
+ <slot :name="((item.slot ? `${item.slot}-leading` : group.slot ? `${group.slot}-leading` : `item-leading`) as keyof CommandPaletteSlots<G, T>)" :item="(item as any)" :index="index">
300
303
  <UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon, loading: true })" />
301
304
  <UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon, active: active || item.active })" />
302
305
  <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, active: active || item.active })" />
@@ -310,8 +313,8 @@ const groups = computed(() => {
310
313
  />
311
314
  </slot>
312
315
 
313
- <span v-if="item.labelHtml || get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`]" :class="ui.itemLabel({ class: props.ui?.itemLabel, active: active || item.active })">
314
- <slot :name="item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`" :item="item" :index="index">
316
+ <span v-if="item.labelHtml || get(item, props.labelKey as string) || !!slots[(item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`) as keyof CommandPaletteSlots<G, T>]" :class="ui.itemLabel({ class: props.ui?.itemLabel, active: active || item.active })">
317
+ <slot :name="((item.slot ? `${item.slot}-label` : group.slot ? `${group.slot}-label` : `item-label`) as keyof CommandPaletteSlots<G, T>)" :item="(item as any)" :index="index">
315
318
  <span v-if="item.prefix" :class="ui.itemLabelPrefix({ class: props.ui?.itemLabelPrefix })">{{ item.prefix }}</span>
316
319
 
317
320
  <span :class="ui.itemLabelBase({ class: props.ui?.itemLabelBase, active: active || item.active })" v-html="item.labelHtml || get(item, props.labelKey as string)" />
@@ -321,7 +324,7 @@ const groups = computed(() => {
321
324
  </span>
322
325
 
323
326
  <span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
324
- <slot :name="item.slot ? `${item.slot}-trailing` : group.slot ? `${group.slot}-trailing` : `item-trailing`" :item="item" :index="index">
327
+ <slot :name="((item.slot ? `${item.slot}-trailing` : group.slot ? `${group.slot}-trailing` : `item-trailing`) as keyof CommandPaletteSlots<G, T>)" :item="(item as any)" :index="index">
325
328
  <span v-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: props.ui?.itemTrailingKbds })">
326
329
  <UKbd v-for="(kbd, kbdIndex) in item.kbds" :key="kbdIndex" :size="((props.ui?.itemTrailingKbdsSize || ui.itemTrailingKbdsSize()) as KbdProps['size'])" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" />
327
330
  </span>
@@ -1,12 +1,20 @@
1
+ <!-- eslint-disable vue/block-tag-newline -->
1
2
  <script lang="ts">
2
3
  import type { VariantProps } from 'tailwind-variants'
3
- import type { ContextMenuRootProps, ContextMenuRootEmits, ContextMenuContentProps } from 'reka-ui'
4
+ import type { ContextMenuRootProps, ContextMenuRootEmits, ContextMenuContentProps, ContextMenuContentEmits } from 'reka-ui'
4
5
  import type { AppConfig } from '@nuxt/schema'
5
6
  import _appConfig from '#build/app.config'
6
7
  import theme from '#build/ui/context-menu'
7
8
  import { tv } from '../utils/tv'
8
9
  import type { AvatarProps, KbdProps, LinkProps } from '../types'
9
- import type { DynamicSlots, PartialString } from '../types/utils'
10
+ import type {
11
+ ArrayOrNested,
12
+ DynamicSlots,
13
+ MergeTypes,
14
+ NestedItem,
15
+ PartialString,
16
+ EmitsToProps
17
+ } from '../types/utils'
10
18
 
11
19
  const appConfigContextMenu = _appConfig as AppConfig & { ui: { contextMenu: Partial<typeof theme> } }
12
20
 
@@ -22,7 +30,7 @@ export interface ContextMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custo
22
30
  icon?: string
23
31
  color?: ContextMenuVariants['color']
24
32
  avatar?: AvatarProps
25
- content?: Omit<ContextMenuContentProps, 'as' | 'asChild' | 'forceMount'>
33
+ content?: Omit<ContextMenuContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<ContextMenuContentEmits>>
26
34
  kbds?: KbdProps['value'][] | KbdProps[]
27
35
  /**
28
36
  * The item type.
@@ -35,17 +43,18 @@ export interface ContextMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custo
35
43
  checked?: boolean
36
44
  open?: boolean
37
45
  defaultOpen?: boolean
38
- children?: ContextMenuItem[] | ContextMenuItem[][]
46
+ children?: ArrayOrNested<ContextMenuItem>
39
47
  onSelect?(e: Event): void
40
48
  onUpdateChecked?(checked: boolean): void
49
+ [key: string]: any
41
50
  }
42
51
 
43
- export interface ContextMenuProps<T> extends Omit<ContextMenuRootProps, 'dir'> {
52
+ export interface ContextMenuProps<T extends ArrayOrNested<ContextMenuItem> = ArrayOrNested<ContextMenuItem>> extends Omit<ContextMenuRootProps, 'dir'> {
44
53
  /**
45
54
  * @defaultValue 'md'
46
55
  */
47
56
  size?: ContextMenuVariants['size']
48
- items?: T[] | T[][]
57
+ items?: T
49
58
  /**
50
59
  * The icon displayed when an item is checked.
51
60
  * @defaultValue appConfig.ui.icons.check
@@ -66,7 +75,7 @@ export interface ContextMenuProps<T> extends Omit<ContextMenuRootProps, 'dir'> {
66
75
  */
67
76
  externalIcon?: boolean | string
68
77
  /** The content of the menu. */
69
- content?: Omit<ContextMenuContentProps, 'as' | 'asChild' | 'forceMount'>
78
+ content?: Omit<ContextMenuContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<ContextMenuContentEmits>>
70
79
  /**
71
80
  * Render the menu in a portal.
72
81
  * @defaultValue true
@@ -76,7 +85,7 @@ export interface ContextMenuProps<T> extends Omit<ContextMenuRootProps, 'dir'> {
76
85
  * The key used to get the label from the item.
77
86
  * @defaultValue 'label'
78
87
  */
79
- labelKey?: string
88
+ labelKey?: keyof NestedItem<T>
80
89
  disabled?: boolean
81
90
  class?: any
82
91
  ui?: PartialString<typeof contextMenu.slots>
@@ -84,18 +93,22 @@ export interface ContextMenuProps<T> extends Omit<ContextMenuRootProps, 'dir'> {
84
93
 
85
94
  export interface ContextMenuEmits extends ContextMenuRootEmits {}
86
95
 
87
- type SlotProps<T> = (props: { item: T, active?: boolean, index: number }) => any
96
+ type SlotProps<T extends ContextMenuItem> = (props: { item: T, active?: boolean, index: number }) => any
88
97
 
89
- export type ContextMenuSlots<T extends { slot?: string }> = {
98
+ export type ContextMenuSlots<
99
+ A extends ArrayOrNested<ContextMenuItem> = ArrayOrNested<ContextMenuItem>,
100
+ T extends NestedItem<A> = NestedItem<A>
101
+ > = {
90
102
  'default'(props?: {}): any
91
103
  'item': SlotProps<T>
92
104
  'item-leading': SlotProps<T>
93
105
  'item-label': SlotProps<T>
94
106
  'item-trailing': SlotProps<T>
95
- } & DynamicSlots<T, SlotProps<T>>
107
+ } & DynamicSlots<MergeTypes<T>, 'leading' | 'label' | 'trailing', { active?: boolean, index: number }>
108
+
96
109
  </script>
97
110
 
98
- <script setup lang="ts" generic="T extends ContextMenuItem">
111
+ <script setup lang="ts" generic="T extends ArrayOrNested<ContextMenuItem>">
99
112
  import { computed, toRef } from 'vue'
100
113
  import { ContextMenuRoot, ContextMenuTrigger, useForwardPropsEmits } from 'reka-ui'
101
114
  import { reactivePick } from '@vueuse/core'
@@ -112,8 +125,9 @@ const emits = defineEmits<ContextMenuEmits>()
112
125
  const slots = defineSlots<ContextMenuSlots<T>>()
113
126
 
114
127
  const rootProps = useForwardPropsEmits(reactivePick(props, 'modal'), emits)
115
- const contentProps = toRef(() => props.content as ContextMenuContentProps)
116
- const proxySlots = omit(slots, ['default']) as Record<string, ContextMenuSlots<T>[string]>
128
+
129
+ const contentProps = toRef(() => props.content)
130
+ const proxySlots = omit(slots, ['default'])
117
131
 
118
132
  const ui = computed(() => contextMenu({
119
133
  size: props.size
@@ -133,13 +147,13 @@ const ui = computed(() => contextMenu({
133
147
  v-bind="contentProps"
134
148
  :items="items"
135
149
  :portal="portal"
136
- :label-key="labelKey"
150
+ :label-key="(labelKey as keyof NestedItem<T>)"
137
151
  :checked-icon="checkedIcon"
138
152
  :loading-icon="loadingIcon"
139
153
  :external-icon="externalIcon"
140
154
  >
141
- <template v-for="(_, name) in proxySlots" #[name]="slotData: any">
142
- <slot :name="name" v-bind="slotData" />
155
+ <template v-for="(_, name) in proxySlots" #[name]="slotData">
156
+ <slot :name="(name as keyof ContextMenuSlots<T>)" v-bind="slotData" />
143
157
  </template>
144
158
  </UContextMenuContent>
145
159
  </ContextMenuRoot>