@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.
- package/.nuxt/ui/button.ts +36 -36
- package/.nuxt/ui/container.ts +1 -1
- package/.nuxt/ui/navigation-menu.ts +2 -2
- package/.nuxt/ui/switch.ts +1 -1
- package/.nuxt/ui/tabs.ts +1 -1
- package/dist/module.cjs +1 -1
- package/dist/module.d.cts +2 -1
- package/dist/module.d.mts +2 -1
- package/dist/module.d.ts +2 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/components/Accordion.vue +9 -8
- package/dist/runtime/components/Alert.vue +1 -1
- package/dist/runtime/components/App.vue +1 -1
- package/dist/runtime/components/Avatar.vue +7 -5
- package/dist/runtime/components/Breadcrumb.vue +11 -10
- package/dist/runtime/components/Button.vue +1 -1
- package/dist/runtime/components/Calendar.vue +40 -11
- package/dist/runtime/components/Carousel.vue +7 -3
- package/dist/runtime/components/CommandPalette.vue +12 -11
- package/dist/runtime/components/ContextMenu.vue +24 -12
- package/dist/runtime/components/ContextMenuContent.vue +22 -15
- package/dist/runtime/components/Drawer.vue +1 -5
- package/dist/runtime/components/DropdownMenu.vue +23 -12
- package/dist/runtime/components/DropdownMenuContent.vue +22 -15
- package/dist/runtime/components/Form.vue +1 -0
- package/dist/runtime/components/FormField.vue +1 -0
- package/dist/runtime/components/InputMenu.vue +92 -54
- package/dist/runtime/components/Link.vue +3 -0
- package/dist/runtime/components/LinkBase.vue +1 -0
- package/dist/runtime/components/Modal.vue +1 -1
- package/dist/runtime/components/NavigationMenu.vue +49 -25
- package/dist/runtime/components/RadioGroup.vue +23 -12
- package/dist/runtime/components/Select.vue +69 -42
- package/dist/runtime/components/SelectMenu.vue +83 -48
- package/dist/runtime/components/Slideover.vue +1 -1
- package/dist/runtime/components/Stepper.vue +11 -10
- package/dist/runtime/components/Table.vue +7 -2
- package/dist/runtime/components/Tabs.vue +6 -5
- package/dist/runtime/components/Toast.vue +1 -1
- package/dist/runtime/components/Tree.vue +46 -32
- package/dist/runtime/composables/defineShortcuts.js +0 -1
- package/dist/runtime/composables/useFormField.js +1 -1
- package/dist/runtime/composables/useOverlay.d.ts +3 -2
- package/dist/runtime/locale/hy.d.ts +3 -0
- package/dist/runtime/locale/hy.js +54 -0
- package/dist/runtime/locale/index.d.ts +1 -0
- package/dist/runtime/locale/index.js +1 -0
- package/dist/runtime/types/form.d.ts +1 -0
- package/dist/runtime/types/index.d.ts +1 -0
- package/dist/runtime/types/index.js +1 -0
- package/dist/runtime/types/utils.d.ts +28 -8
- package/dist/runtime/utils/index.d.ts +1 -0
- package/dist/runtime/utils/index.js +3 -0
- package/dist/runtime/utils/link.d.ts +2 -25
- package/dist/runtime/utils/link.js +31 -1
- package/dist/runtime/vue/components/Link.vue +3 -0
- package/dist/runtime/vue/stubs.d.ts +7 -2
- package/dist/runtime/vue/stubs.js +11 -2
- package/dist/shared/{ui.C6uTSFTI.cjs → ui.BL-XB3u1.cjs} +16 -16
- package/dist/shared/{ui.BCaWD5Ek.mjs → ui.BkNUP964.mjs} +16 -16
- package/dist/types.d.mts +7 -1
- package/dist/types.d.ts +7 -1
- package/dist/unplugin.cjs +1 -1
- package/dist/unplugin.d.cts +2 -1
- package/dist/unplugin.d.mts +2 -1
- package/dist/unplugin.d.ts +2 -1
- package/dist/unplugin.mjs +1 -1
- package/dist/vite.cjs +1 -1
- package/dist/vite.mjs +1 -1
- package/package.json +39 -22
|
@@ -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
|
|
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
|
-
|
|
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?:
|
|
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
|
|
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="
|
|
134
|
-
:default-value="
|
|
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
|
|
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
|
|
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'
|
|
@@ -9,7 +9,7 @@ import theme from '#build/ui/command-palette'
|
|
|
9
9
|
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
|
10
10
|
import { tv } from '../utils/tv'
|
|
11
11
|
import type { AvatarProps, ButtonProps, ChipProps, KbdProps, InputProps, LinkProps } from '../types'
|
|
12
|
-
import type {
|
|
12
|
+
import type { PartialString } from '../types/utils'
|
|
13
13
|
|
|
14
14
|
const appConfigCommandPalette = _appConfig as AppConfig & { ui: { commandPalette: Partial<typeof theme> } }
|
|
15
15
|
|
|
@@ -31,6 +31,7 @@ export interface CommandPaletteItem extends Omit<LinkProps, 'type' | 'raw' | 'cu
|
|
|
31
31
|
disabled?: boolean
|
|
32
32
|
slot?: string
|
|
33
33
|
onSelect?(e?: Event): void
|
|
34
|
+
[key: string]: any
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
export interface CommandPaletteGroup<T> {
|
|
@@ -73,7 +74,7 @@ export interface CommandPaletteProps<G, T> extends Pick<ListboxRootProps, 'multi
|
|
|
73
74
|
selectedIcon?: string
|
|
74
75
|
/**
|
|
75
76
|
* The placeholder text for the input.
|
|
76
|
-
* @defaultValue '
|
|
77
|
+
* @defaultValue t('commandPalette.placeholder')
|
|
77
78
|
*/
|
|
78
79
|
placeholder?: InputProps['placeholder']
|
|
79
80
|
/**
|
|
@@ -125,12 +126,12 @@ type SlotProps<T> = (props: { item: T, index: number }) => any
|
|
|
125
126
|
|
|
126
127
|
export type CommandPaletteSlots<G extends { slot?: string }, T extends { slot?: string }> = {
|
|
127
128
|
'empty'(props: { searchTerm?: string }): any
|
|
128
|
-
'close'(props: { ui:
|
|
129
|
+
'close'(props: { ui: ReturnType<typeof commandPalette> }): any
|
|
129
130
|
'item': SlotProps<T>
|
|
130
131
|
'item-leading': SlotProps<T>
|
|
131
132
|
'item-label': SlotProps<T>
|
|
132
133
|
'item-trailing': SlotProps<T>
|
|
133
|
-
} &
|
|
134
|
+
} & Record<string, SlotProps<G>> & Record<string, SlotProps<T>>
|
|
134
135
|
|
|
135
136
|
</script>
|
|
136
137
|
|
|
@@ -282,14 +283,14 @@ const groups = computed(() => {
|
|
|
282
283
|
|
|
283
284
|
<ListboxContent :class="ui.content({ class: props.ui?.content })">
|
|
284
285
|
<div v-if="groups?.length" :class="ui.viewport({ class: props.ui?.viewport })">
|
|
285
|
-
<ListboxGroup v-for="
|
|
286
|
+
<ListboxGroup v-for="group in groups" :key="`group-${group.id}`" :class="ui.group({ class: props.ui?.group })">
|
|
286
287
|
<ListboxGroupLabel v-if="get(group, props.labelKey as string)" :class="ui.label({ class: props.ui?.label })">
|
|
287
288
|
{{ get(group, props.labelKey as string) }}
|
|
288
289
|
</ListboxGroupLabel>
|
|
289
290
|
|
|
290
291
|
<ListboxItem
|
|
291
292
|
v-for="(item, index) in group.items"
|
|
292
|
-
:key="`group-${
|
|
293
|
+
:key="`group-${group.id}-${index}`"
|
|
293
294
|
:value="omit(item, ['matches' as any, 'group' as any, 'onSelect', 'labelHtml', 'suffixHtml'])"
|
|
294
295
|
:disabled="item.disabled"
|
|
295
296
|
as-child
|
|
@@ -297,8 +298,8 @@ const groups = computed(() => {
|
|
|
297
298
|
>
|
|
298
299
|
<ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item)" custom>
|
|
299
300
|
<ULinkBase v-bind="slotProps" :class="ui.item({ class: props.ui?.item, active: active || item.active })">
|
|
300
|
-
<slot :name="item.slot || group.slot || 'item'" :item="item" :index="index">
|
|
301
|
-
<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">
|
|
302
303
|
<UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon, loading: true })" />
|
|
303
304
|
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon, active: active || item.active })" />
|
|
304
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 })" />
|
|
@@ -312,8 +313,8 @@ const groups = computed(() => {
|
|
|
312
313
|
/>
|
|
313
314
|
</slot>
|
|
314
315
|
|
|
315
|
-
<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 })">
|
|
316
|
-
<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">
|
|
317
318
|
<span v-if="item.prefix" :class="ui.itemLabelPrefix({ class: props.ui?.itemLabelPrefix })">{{ item.prefix }}</span>
|
|
318
319
|
|
|
319
320
|
<span :class="ui.itemLabelBase({ class: props.ui?.itemLabelBase, active: active || item.active })" v-html="item.labelHtml || get(item, props.labelKey as string)" />
|
|
@@ -323,7 +324,7 @@ const groups = computed(() => {
|
|
|
323
324
|
</span>
|
|
324
325
|
|
|
325
326
|
<span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
|
|
326
|
-
<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">
|
|
327
328
|
<span v-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: props.ui?.itemTrailingKbds })">
|
|
328
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" />
|
|
329
330
|
</span>
|
|
@@ -7,7 +7,14 @@ import _appConfig from '#build/app.config'
|
|
|
7
7
|
import theme from '#build/ui/context-menu'
|
|
8
8
|
import { tv } from '../utils/tv'
|
|
9
9
|
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
|
10
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
ArrayOrNested,
|
|
12
|
+
DynamicSlots,
|
|
13
|
+
MergeTypes,
|
|
14
|
+
NestedItem,
|
|
15
|
+
PartialString,
|
|
16
|
+
EmitsToProps
|
|
17
|
+
} from '../types/utils'
|
|
11
18
|
|
|
12
19
|
const appConfigContextMenu = _appConfig as AppConfig & { ui: { contextMenu: Partial<typeof theme> } }
|
|
13
20
|
|
|
@@ -36,17 +43,18 @@ export interface ContextMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custo
|
|
|
36
43
|
checked?: boolean
|
|
37
44
|
open?: boolean
|
|
38
45
|
defaultOpen?: boolean
|
|
39
|
-
children?: ContextMenuItem
|
|
46
|
+
children?: ArrayOrNested<ContextMenuItem>
|
|
40
47
|
onSelect?(e: Event): void
|
|
41
48
|
onUpdateChecked?(checked: boolean): void
|
|
49
|
+
[key: string]: any
|
|
42
50
|
}
|
|
43
51
|
|
|
44
|
-
export interface ContextMenuProps<T> extends Omit<ContextMenuRootProps, 'dir'> {
|
|
52
|
+
export interface ContextMenuProps<T extends ArrayOrNested<ContextMenuItem> = ArrayOrNested<ContextMenuItem>> extends Omit<ContextMenuRootProps, 'dir'> {
|
|
45
53
|
/**
|
|
46
54
|
* @defaultValue 'md'
|
|
47
55
|
*/
|
|
48
56
|
size?: ContextMenuVariants['size']
|
|
49
|
-
items?: T
|
|
57
|
+
items?: T
|
|
50
58
|
/**
|
|
51
59
|
* The icon displayed when an item is checked.
|
|
52
60
|
* @defaultValue appConfig.ui.icons.check
|
|
@@ -77,7 +85,7 @@ export interface ContextMenuProps<T> extends Omit<ContextMenuRootProps, 'dir'> {
|
|
|
77
85
|
* The key used to get the label from the item.
|
|
78
86
|
* @defaultValue 'label'
|
|
79
87
|
*/
|
|
80
|
-
labelKey?:
|
|
88
|
+
labelKey?: keyof NestedItem<T>
|
|
81
89
|
disabled?: boolean
|
|
82
90
|
class?: any
|
|
83
91
|
ui?: PartialString<typeof contextMenu.slots>
|
|
@@ -85,19 +93,22 @@ export interface ContextMenuProps<T> extends Omit<ContextMenuRootProps, 'dir'> {
|
|
|
85
93
|
|
|
86
94
|
export interface ContextMenuEmits extends ContextMenuRootEmits {}
|
|
87
95
|
|
|
88
|
-
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
|
|
89
97
|
|
|
90
|
-
export type ContextMenuSlots<
|
|
98
|
+
export type ContextMenuSlots<
|
|
99
|
+
A extends ArrayOrNested<ContextMenuItem> = ArrayOrNested<ContextMenuItem>,
|
|
100
|
+
T extends NestedItem<A> = NestedItem<A>
|
|
101
|
+
> = {
|
|
91
102
|
'default'(props?: {}): any
|
|
92
103
|
'item': SlotProps<T>
|
|
93
104
|
'item-leading': SlotProps<T>
|
|
94
105
|
'item-label': SlotProps<T>
|
|
95
106
|
'item-trailing': SlotProps<T>
|
|
96
|
-
} & DynamicSlots<T,
|
|
107
|
+
} & DynamicSlots<MergeTypes<T>, 'leading' | 'label' | 'trailing', { active?: boolean, index: number }>
|
|
97
108
|
|
|
98
109
|
</script>
|
|
99
110
|
|
|
100
|
-
<script setup lang="ts" generic="T extends ContextMenuItem">
|
|
111
|
+
<script setup lang="ts" generic="T extends ArrayOrNested<ContextMenuItem>">
|
|
101
112
|
import { computed, toRef } from 'vue'
|
|
102
113
|
import { ContextMenuRoot, ContextMenuTrigger, useForwardPropsEmits } from 'reka-ui'
|
|
103
114
|
import { reactivePick } from '@vueuse/core'
|
|
@@ -114,8 +125,9 @@ const emits = defineEmits<ContextMenuEmits>()
|
|
|
114
125
|
const slots = defineSlots<ContextMenuSlots<T>>()
|
|
115
126
|
|
|
116
127
|
const rootProps = useForwardPropsEmits(reactivePick(props, 'modal'), emits)
|
|
128
|
+
|
|
117
129
|
const contentProps = toRef(() => props.content)
|
|
118
|
-
const proxySlots = omit(slots, ['default'])
|
|
130
|
+
const proxySlots = omit(slots, ['default'])
|
|
119
131
|
|
|
120
132
|
const ui = computed(() => contextMenu({
|
|
121
133
|
size: props.size
|
|
@@ -135,13 +147,13 @@ const ui = computed(() => contextMenu({
|
|
|
135
147
|
v-bind="contentProps"
|
|
136
148
|
:items="items"
|
|
137
149
|
:portal="portal"
|
|
138
|
-
:label-key="labelKey"
|
|
150
|
+
:label-key="(labelKey as keyof NestedItem<T>)"
|
|
139
151
|
:checked-icon="checkedIcon"
|
|
140
152
|
:loading-icon="loadingIcon"
|
|
141
153
|
:external-icon="externalIcon"
|
|
142
154
|
>
|
|
143
155
|
<template v-for="(_, name) in proxySlots" #[name]="slotData">
|
|
144
|
-
<slot :name="name" v-bind="slotData" />
|
|
156
|
+
<slot :name="(name as keyof ContextMenuSlots<T>)" v-bind="slotData" />
|
|
145
157
|
</template>
|
|
146
158
|
</UContextMenuContent>
|
|
147
159
|
</ContextMenuRoot>
|
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
import type { ContextMenuContentProps as RekaContextMenuContentProps, ContextMenuContentEmits as RekaContextMenuContentEmits } from 'reka-ui'
|
|
3
3
|
import theme from '#build/ui/context-menu'
|
|
4
4
|
import { tv } from '../utils/tv'
|
|
5
|
-
import type {
|
|
5
|
+
import type { AvatarProps, ContextMenuItem, ContextMenuSlots, KbdProps } from '../types'
|
|
6
|
+
import type { ArrayOrNested, NestedItem } from '../types/utils'
|
|
6
7
|
|
|
7
8
|
const _contextMenu = tv(theme)()
|
|
8
9
|
|
|
9
|
-
interface ContextMenuContentProps<T
|
|
10
|
-
items?: T
|
|
10
|
+
interface ContextMenuContentProps<T extends ArrayOrNested<ContextMenuItem>> extends Omit<RekaContextMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
|
|
11
|
+
items?: T
|
|
11
12
|
portal?: boolean
|
|
12
13
|
sub?: boolean
|
|
13
|
-
labelKey:
|
|
14
|
+
labelKey: keyof NestedItem<T>
|
|
14
15
|
/**
|
|
15
16
|
* @IconifyIcon
|
|
16
17
|
*/
|
|
@@ -31,13 +32,13 @@ interface ContextMenuContentProps<T> extends Omit<RekaContextMenuContentProps, '
|
|
|
31
32
|
interface ContextMenuContentEmits extends RekaContextMenuContentEmits {}
|
|
32
33
|
</script>
|
|
33
34
|
|
|
34
|
-
<script setup lang="ts" generic="T extends ContextMenuItem">
|
|
35
|
+
<script setup lang="ts" generic="T extends ArrayOrNested<ContextMenuItem>">
|
|
35
36
|
import { computed } from 'vue'
|
|
36
37
|
import { ContextMenu } from 'reka-ui/namespaced'
|
|
37
38
|
import { useForwardPropsEmits } from 'reka-ui'
|
|
38
39
|
import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
|
|
39
40
|
import { useAppConfig } from '#imports'
|
|
40
|
-
import { omit, get } from '../utils'
|
|
41
|
+
import { omit, get, isArrayOfArray } from '../utils'
|
|
41
42
|
import { pickLinkProps } from '../utils/link'
|
|
42
43
|
import ULinkBase from './LinkBase.vue'
|
|
43
44
|
import ULink from './Link.vue'
|
|
@@ -53,24 +54,30 @@ const slots = defineSlots<ContextMenuSlots<T>>()
|
|
|
53
54
|
|
|
54
55
|
const appConfig = useAppConfig()
|
|
55
56
|
const contentProps = useForwardPropsEmits(reactiveOmit(props, 'sub', 'items', 'portal', 'labelKey', 'checkedIcon', 'loadingIcon', 'externalIcon', 'class', 'ui', 'uiOverride'), emits)
|
|
56
|
-
const proxySlots = omit(slots, ['default'])
|
|
57
|
+
const proxySlots = omit(slots, ['default'])
|
|
57
58
|
|
|
58
59
|
const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: ContextMenuItem, active?: boolean, index: number }>()
|
|
59
60
|
|
|
60
|
-
const groups = computed
|
|
61
|
+
const groups = computed<ContextMenuItem[][]>(() =>
|
|
62
|
+
props.items?.length
|
|
63
|
+
? isArrayOfArray(props.items)
|
|
64
|
+
? props.items
|
|
65
|
+
: [props.items]
|
|
66
|
+
: []
|
|
67
|
+
)
|
|
61
68
|
</script>
|
|
62
69
|
|
|
63
70
|
<template>
|
|
64
71
|
<DefineItemTemplate v-slot="{ item, active, index }">
|
|
65
|
-
<slot :name="item.slot || 'item'" :item="
|
|
66
|
-
<slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="
|
|
72
|
+
<slot :name="((item.slot || 'item') as keyof ContextMenuSlots<T>)" :item="item" :index="index">
|
|
73
|
+
<slot :name="((item.slot ? `${item.slot}-leading`: 'item-leading') as keyof ContextMenuSlots<T>)" :item="item" :active="active" :index="index">
|
|
67
74
|
<UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, color: item?.color, loading: true })" />
|
|
68
75
|
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, color: item?.color, active })" />
|
|
69
76
|
<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 })" />
|
|
70
77
|
</slot>
|
|
71
78
|
|
|
72
|
-
<span v-if="get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
|
|
73
|
-
<slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="
|
|
79
|
+
<span v-if="get(item, props.labelKey as string) || !!slots[(item.slot ? `${item.slot}-label`: 'item-label') as keyof ContextMenuSlots<T>]" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
|
|
80
|
+
<slot :name="((item.slot ? `${item.slot}-label`: 'item-label') as keyof ContextMenuSlots<T>)" :item="item" :active="active" :index="index">
|
|
74
81
|
{{ get(item, props.labelKey as string) }}
|
|
75
82
|
</slot>
|
|
76
83
|
|
|
@@ -78,7 +85,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
78
85
|
</span>
|
|
79
86
|
|
|
80
87
|
<span :class="ui.itemTrailing({ class: uiOverride?.itemTrailing })">
|
|
81
|
-
<slot :name="item.slot ? `${item.slot}-trailing`: 'item-trailing'" :item="
|
|
88
|
+
<slot :name="((item.slot ? `${item.slot}-trailing`: 'item-trailing') as keyof ContextMenuSlots<T>)" :item="item" :active="active" :index="index">
|
|
82
89
|
<UIcon v-if="item.children?.length" :name="appConfig.ui.icons.chevronRight" :class="ui.itemTrailingIcon({ class: uiOverride?.itemTrailingIcon, color: item?.color, active })" />
|
|
83
90
|
<span v-else-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: uiOverride?.itemTrailingKbds })">
|
|
84
91
|
<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" />
|
|
@@ -117,7 +124,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
117
124
|
:ui="ui"
|
|
118
125
|
:ui-override="uiOverride"
|
|
119
126
|
:portal="portal"
|
|
120
|
-
:items="item.children"
|
|
127
|
+
:items="(item.children as T)"
|
|
121
128
|
:align-offset="-4"
|
|
122
129
|
:label-key="labelKey"
|
|
123
130
|
:checked-icon="checkedIcon"
|
|
@@ -126,7 +133,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
126
133
|
v-bind="item.content"
|
|
127
134
|
>
|
|
128
135
|
<template v-for="(_, name) in proxySlots" #[name]="slotData: any">
|
|
129
|
-
<slot :name="name" v-bind="slotData" />
|
|
136
|
+
<slot :name="(name as keyof ContextMenuSlots<T>)" v-bind="slotData" />
|
|
130
137
|
</template>
|
|
131
138
|
</UContextMenuContent>
|
|
132
139
|
</ContextMenu.Sub>
|
|
@@ -41,10 +41,6 @@ export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | '
|
|
|
41
41
|
* @defaultValue true
|
|
42
42
|
*/
|
|
43
43
|
portal?: boolean
|
|
44
|
-
/**
|
|
45
|
-
* Index of a `snapPoint` from which the overlay fade should be applied. Defaults to the last snap point.
|
|
46
|
-
*/
|
|
47
|
-
fadeFromIndex?: any
|
|
48
44
|
class?: any
|
|
49
45
|
ui?: Partial<typeof drawer.slots>
|
|
50
46
|
}
|
|
@@ -79,7 +75,7 @@ const props = withDefaults(defineProps<DrawerProps>(), {
|
|
|
79
75
|
const emits = defineEmits<DrawerEmits>()
|
|
80
76
|
const slots = defineSlots<DrawerSlots>()
|
|
81
77
|
|
|
82
|
-
const rootProps = useForwardPropsEmits(reactivePick(props, 'activeSnapPoint', 'closeThreshold', 'shouldScaleBackground', 'setBackgroundColorOnScale', 'scrollLockTimeout', 'fixed', 'dismissible', 'modal', 'open', 'defaultOpen', 'nested', 'direction', 'noBodyStyles', 'handleOnly', 'preventScrollRestoration', 'snapPoints'
|
|
78
|
+
const rootProps = useForwardPropsEmits(reactivePick(props, 'activeSnapPoint', 'closeThreshold', 'shouldScaleBackground', 'setBackgroundColorOnScale', 'scrollLockTimeout', 'fixed', 'dismissible', 'modal', 'open', 'defaultOpen', 'nested', 'direction', 'noBodyStyles', 'handleOnly', 'preventScrollRestoration', 'snapPoints'), emits)
|
|
83
79
|
const contentProps = toRef(() => props.content)
|
|
84
80
|
const contentEvents = {
|
|
85
81
|
closeAutoFocus: (e: Event) => e.preventDefault()
|
|
@@ -7,7 +7,14 @@ import _appConfig from '#build/app.config'
|
|
|
7
7
|
import theme from '#build/ui/dropdown-menu'
|
|
8
8
|
import { tv } from '../utils/tv'
|
|
9
9
|
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
|
10
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
ArrayOrNested,
|
|
12
|
+
DynamicSlots,
|
|
13
|
+
MergeTypes,
|
|
14
|
+
NestedItem,
|
|
15
|
+
PartialString,
|
|
16
|
+
EmitsToProps
|
|
17
|
+
} from '../types/utils'
|
|
11
18
|
|
|
12
19
|
const appConfigDropdownMenu = _appConfig as AppConfig & { ui: { dropdownMenu: Partial<typeof theme> } }
|
|
13
20
|
|
|
@@ -36,17 +43,18 @@ export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cust
|
|
|
36
43
|
checked?: boolean
|
|
37
44
|
open?: boolean
|
|
38
45
|
defaultOpen?: boolean
|
|
39
|
-
children?: DropdownMenuItem
|
|
46
|
+
children?: ArrayOrNested<DropdownMenuItem>
|
|
40
47
|
onSelect?(e: Event): void
|
|
41
48
|
onUpdateChecked?(checked: boolean): void
|
|
49
|
+
[key: string]: any
|
|
42
50
|
}
|
|
43
51
|
|
|
44
|
-
export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'> {
|
|
52
|
+
export interface DropdownMenuProps<T extends ArrayOrNested<DropdownMenuItem> = ArrayOrNested<DropdownMenuItem>> extends Omit<DropdownMenuRootProps, 'dir'> {
|
|
45
53
|
/**
|
|
46
54
|
* @defaultValue 'md'
|
|
47
55
|
*/
|
|
48
56
|
size?: DropdownMenuVariants['size']
|
|
49
|
-
items?: T
|
|
57
|
+
items?: T
|
|
50
58
|
/**
|
|
51
59
|
* The icon displayed when an item is checked.
|
|
52
60
|
* @defaultValue appConfig.ui.icons.check
|
|
@@ -85,7 +93,7 @@ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'>
|
|
|
85
93
|
* The key used to get the label from the item.
|
|
86
94
|
* @defaultValue 'label'
|
|
87
95
|
*/
|
|
88
|
-
labelKey?:
|
|
96
|
+
labelKey?: keyof NestedItem<T>
|
|
89
97
|
disabled?: boolean
|
|
90
98
|
class?: any
|
|
91
99
|
ui?: PartialString<typeof dropdownMenu.slots>
|
|
@@ -93,19 +101,22 @@ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'>
|
|
|
93
101
|
|
|
94
102
|
export interface DropdownMenuEmits extends DropdownMenuRootEmits {}
|
|
95
103
|
|
|
96
|
-
type SlotProps<T> = (props: { item: T, active?: boolean, index: number }) => any
|
|
104
|
+
type SlotProps<T extends DropdownMenuItem> = (props: { item: T, active?: boolean, index: number }) => any
|
|
97
105
|
|
|
98
|
-
export type DropdownMenuSlots<
|
|
106
|
+
export type DropdownMenuSlots<
|
|
107
|
+
A extends ArrayOrNested<DropdownMenuItem> = ArrayOrNested<DropdownMenuItem>,
|
|
108
|
+
T extends NestedItem<A> = NestedItem<A>
|
|
109
|
+
> = {
|
|
99
110
|
'default'(props: { open: boolean }): any
|
|
100
111
|
'item': SlotProps<T>
|
|
101
112
|
'item-leading': SlotProps<T>
|
|
102
113
|
'item-label': SlotProps<T>
|
|
103
114
|
'item-trailing': SlotProps<T>
|
|
104
|
-
} & DynamicSlots<T,
|
|
115
|
+
} & DynamicSlots<MergeTypes<T>, 'leading' | 'label' | 'trailing', { active?: boolean, index: number }>
|
|
105
116
|
|
|
106
117
|
</script>
|
|
107
118
|
|
|
108
|
-
<script setup lang="ts" generic="T extends DropdownMenuItem">
|
|
119
|
+
<script setup lang="ts" generic="T extends ArrayOrNested<DropdownMenuItem>">
|
|
109
120
|
import { computed, toRef } from 'vue'
|
|
110
121
|
import { defu } from 'defu'
|
|
111
122
|
import { DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuArrow, useForwardPropsEmits } from 'reka-ui'
|
|
@@ -125,7 +136,7 @@ const slots = defineSlots<DropdownMenuSlots<T>>()
|
|
|
125
136
|
const rootProps = useForwardPropsEmits(reactivePick(props, 'defaultOpen', 'open', 'modal'), emits)
|
|
126
137
|
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8 }) as DropdownMenuContentProps)
|
|
127
138
|
const arrowProps = toRef(() => props.arrow as DropdownMenuArrowProps)
|
|
128
|
-
const proxySlots = omit(slots, ['default'])
|
|
139
|
+
const proxySlots = omit(slots, ['default'])
|
|
129
140
|
|
|
130
141
|
const ui = computed(() => dropdownMenu({
|
|
131
142
|
size: props.size
|
|
@@ -145,13 +156,13 @@ const ui = computed(() => dropdownMenu({
|
|
|
145
156
|
v-bind="contentProps"
|
|
146
157
|
:items="items"
|
|
147
158
|
:portal="portal"
|
|
148
|
-
:label-key="labelKey"
|
|
159
|
+
:label-key="(labelKey as keyof NestedItem<T>)"
|
|
149
160
|
:checked-icon="checkedIcon"
|
|
150
161
|
:loading-icon="loadingIcon"
|
|
151
162
|
:external-icon="externalIcon"
|
|
152
163
|
>
|
|
153
164
|
<template v-for="(_, name) in proxySlots" #[name]="slotData">
|
|
154
|
-
<slot :name="name" v-bind="slotData" />
|
|
165
|
+
<slot :name="(name as keyof DropdownMenuSlots<T>)" v-bind="slotData" />
|
|
155
166
|
</template>
|
|
156
167
|
|
|
157
168
|
<DropdownMenuArrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow({ class: props.ui?.arrow })" />
|