@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.
- package/.nuxt/ui/button.ts +36 -36
- package/.nuxt/ui/calendar.ts +1 -1
- package/.nuxt/ui/container.ts +1 -1
- package/.nuxt/ui/drawer.ts +8 -5
- package/.nuxt/ui/navigation-menu.ts +2 -2
- package/.nuxt/ui/switch.ts +1 -1
- package/.nuxt/ui/tabs.ts +1 -1
- package/.nuxt/ui.css +1 -1
- package/README.md +11 -11
- package/cli/package.json +1 -1
- package/dist/module.cjs +2 -2
- package/dist/module.d.cts +8 -7
- package/dist/module.d.mts +8 -7
- package/dist/module.d.ts +8 -7
- package/dist/module.json +2 -2
- package/dist/module.mjs +2 -2
- package/dist/runtime/components/Accordion.vue +11 -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 +13 -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/ColorPicker.vue +2 -0
- package/dist/runtime/components/CommandPalette.vue +14 -11
- package/dist/runtime/components/ContextMenu.vue +31 -17
- package/dist/runtime/components/ContextMenuContent.vue +22 -15
- package/dist/runtime/components/Drawer.vue +10 -15
- package/dist/runtime/components/DropdownMenu.vue +29 -16
- package/dist/runtime/components/DropdownMenuContent.vue +22 -15
- package/dist/runtime/components/Form.vue +22 -1
- package/dist/runtime/components/FormField.vue +6 -0
- package/dist/runtime/components/InputMenu.vue +93 -55
- package/dist/runtime/components/Link.vue +3 -0
- package/dist/runtime/components/LinkBase.vue +1 -0
- package/dist/runtime/components/Modal.vue +11 -8
- package/dist/runtime/components/NavigationMenu.vue +53 -27
- package/dist/runtime/components/PinInput.vue +2 -0
- package/dist/runtime/components/Popover.vue +3 -2
- package/dist/runtime/components/RadioGroup.vue +25 -14
- package/dist/runtime/components/Select.vue +70 -43
- package/dist/runtime/components/SelectMenu.vue +84 -49
- package/dist/runtime/components/Slideover.vue +11 -8
- package/dist/runtime/components/Stepper.vue +13 -10
- package/dist/runtime/components/Table.vue +8 -3
- package/dist/runtime/components/Tabs.vue +8 -5
- package/dist/runtime/components/Toast.vue +2 -2
- package/dist/runtime/components/Tooltip.vue +3 -2
- package/dist/runtime/components/Tree.vue +48 -32
- package/dist/runtime/composables/defineLocale.js +1 -0
- package/dist/runtime/composables/useFormField.js +1 -1
- package/dist/runtime/composables/useKbd.js +1 -1
- package/dist/runtime/composables/useLocale.js +2 -2
- package/dist/runtime/composables/useOverlay.d.ts +3 -2
- package/dist/runtime/composables/useOverlay.js +1 -1
- package/dist/runtime/locale/ca.d.ts +3 -0
- package/dist/runtime/locale/ca.js +54 -0
- package/dist/runtime/locale/ckb.d.ts +3 -0
- package/dist/runtime/locale/ckb.js +55 -0
- package/dist/runtime/locale/hy.d.ts +3 -0
- package/dist/runtime/locale/hy.js +54 -0
- package/dist/runtime/locale/index.d.ts +6 -0
- package/dist/runtime/locale/index.js +6 -0
- package/dist/runtime/locale/ro.d.ts +3 -0
- package/dist/runtime/locale/ro.js +54 -0
- package/dist/runtime/locale/ur.d.ts +3 -0
- package/dist/runtime/locale/ur.js +55 -0
- package/dist/runtime/locale/uz.d.ts +3 -0
- package/dist/runtime/locale/uz.js +54 -0
- package/dist/runtime/plugins/colors.js +19 -15
- package/dist/runtime/types/form.d.ts +2 -3
- package/dist/runtime/types/index.d.ts +2 -0
- package/dist/runtime/types/index.js +2 -0
- package/dist/runtime/types/utils.d.ts +28 -8
- package/dist/runtime/utils/form.d.ts +0 -4
- package/dist/runtime/utils/form.js +2 -47
- 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/utils/tv.js +1 -1
- package/dist/runtime/vue/components/Link.vue +3 -0
- package/dist/runtime/vue/plugins/head.js +3 -0
- package/dist/runtime/vue/stubs.d.ts +7 -3
- package/dist/runtime/vue/stubs.js +12 -4
- package/dist/shared/{ui.COBs1u5H.cjs → ui.BL-XB3u1.cjs} +26 -26
- package/dist/shared/{ui.yjbGE7Dx.mjs → ui.BkNUP964.mjs} +26 -26
- package/dist/types.d.mts +7 -1
- package/dist/types.d.ts +7 -1
- package/dist/unplugin.cjs +6 -2
- 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 +6 -2
- package/dist/vite.cjs +1 -1
- package/dist/vite.mjs +1 -1
- package/package.json +46 -29
|
@@ -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>
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { DrawerRootProps, DrawerRootEmits } from 'vaul-vue'
|
|
3
|
-
import type { DialogContentProps } from 'reka-ui'
|
|
3
|
+
import type { 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/drawer'
|
|
7
7
|
import { tv } from '../utils/tv'
|
|
8
|
+
import type { EmitsToProps } from '../types/utils'
|
|
8
9
|
|
|
9
10
|
const appConfigDrawer = _appConfig as AppConfig & { ui: { drawer: Partial<typeof theme> } }
|
|
10
11
|
|
|
11
12
|
const drawer = tv({ extend: tv(theme), ...(appConfigDrawer.ui?.drawer || {}) })
|
|
12
13
|
|
|
13
|
-
export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | 'closeThreshold' | '
|
|
14
|
+
export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | 'closeThreshold' | 'shouldScaleBackground' | 'setBackgroundColorOnScale' | 'scrollLockTimeout' | 'fixed' | 'dismissible' | 'modal' | 'open' | 'defaultOpen' | 'nested' | 'direction' | 'noBodyStyles' | 'handleOnly' | 'preventScrollRestoration' | 'snapPoints'> {
|
|
14
15
|
/**
|
|
15
16
|
* The element or component this component should render as.
|
|
16
17
|
* @defaultValue 'div'
|
|
@@ -24,7 +25,7 @@ export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | '
|
|
|
24
25
|
*/
|
|
25
26
|
inset?: boolean
|
|
26
27
|
/** The content of the drawer. */
|
|
27
|
-
content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'>
|
|
28
|
+
content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<DialogContentEmits>>
|
|
28
29
|
/**
|
|
29
30
|
* Render an overlay behind the drawer.
|
|
30
31
|
* @defaultValue true
|
|
@@ -40,11 +41,6 @@ export interface DrawerProps extends Pick<DrawerRootProps, 'activeSnapPoint' | '
|
|
|
40
41
|
* @defaultValue true
|
|
41
42
|
*/
|
|
42
43
|
portal?: boolean
|
|
43
|
-
/**
|
|
44
|
-
* When `false`, the drawer will not close when clicking outside or pressing escape.
|
|
45
|
-
* @defaultValue true
|
|
46
|
-
*/
|
|
47
|
-
dismissible?: boolean
|
|
48
44
|
class?: any
|
|
49
45
|
ui?: Partial<typeof drawer.slots>
|
|
50
46
|
}
|
|
@@ -53,7 +49,6 @@ export interface DrawerEmits extends DrawerRootEmits {}
|
|
|
53
49
|
|
|
54
50
|
export interface DrawerSlots {
|
|
55
51
|
default(props?: {}): any
|
|
56
|
-
handle(props?: {}): any
|
|
57
52
|
content(props?: {}): any
|
|
58
53
|
header(props?: {}): any
|
|
59
54
|
title(props?: {}): any
|
|
@@ -66,19 +61,21 @@ export interface DrawerSlots {
|
|
|
66
61
|
<script setup lang="ts">
|
|
67
62
|
import { computed, toRef } from 'vue'
|
|
68
63
|
import { useForwardPropsEmits } from 'reka-ui'
|
|
69
|
-
import { DrawerRoot, DrawerTrigger, DrawerPortal, DrawerOverlay, DrawerContent, DrawerTitle, DrawerDescription } from 'vaul-vue'
|
|
64
|
+
import { DrawerRoot, DrawerTrigger, DrawerPortal, DrawerOverlay, DrawerContent, DrawerTitle, DrawerDescription, DrawerHandle } from 'vaul-vue'
|
|
70
65
|
import { reactivePick } from '@vueuse/core'
|
|
71
66
|
|
|
72
67
|
const props = withDefaults(defineProps<DrawerProps>(), {
|
|
73
68
|
direction: 'bottom',
|
|
74
69
|
portal: true,
|
|
75
70
|
overlay: true,
|
|
76
|
-
handle: true
|
|
71
|
+
handle: true,
|
|
72
|
+
modal: true,
|
|
73
|
+
dismissible: true
|
|
77
74
|
})
|
|
78
75
|
const emits = defineEmits<DrawerEmits>()
|
|
79
76
|
const slots = defineSlots<DrawerSlots>()
|
|
80
77
|
|
|
81
|
-
const rootProps = useForwardPropsEmits(reactivePick(props, 'activeSnapPoint', 'closeThreshold', '
|
|
78
|
+
const rootProps = useForwardPropsEmits(reactivePick(props, 'activeSnapPoint', 'closeThreshold', 'shouldScaleBackground', 'setBackgroundColorOnScale', 'scrollLockTimeout', 'fixed', 'dismissible', 'modal', 'open', 'defaultOpen', 'nested', 'direction', 'noBodyStyles', 'handleOnly', 'preventScrollRestoration', 'snapPoints'), emits)
|
|
82
79
|
const contentProps = toRef(() => props.content)
|
|
83
80
|
const contentEvents = {
|
|
84
81
|
closeAutoFocus: (e: Event) => e.preventDefault()
|
|
@@ -100,9 +97,7 @@ const ui = computed(() => drawer({
|
|
|
100
97
|
<DrawerOverlay v-if="overlay" :class="ui.overlay({ class: props.ui?.overlay })" />
|
|
101
98
|
|
|
102
99
|
<DrawerContent :class="ui.content({ class: [!slots.default && props.class, props.ui?.content] })" v-bind="contentProps" v-on="contentEvents">
|
|
103
|
-
<
|
|
104
|
-
<div v-if="handle" :class="ui.handle({ class: props.ui?.handle })" />
|
|
105
|
-
</slot>
|
|
100
|
+
<DrawerHandle v-if="handle" :class="ui.handle({ class: props.ui?.handle })" />
|
|
106
101
|
|
|
107
102
|
<slot name="content">
|
|
108
103
|
<div :class="ui.container({ class: props.ui?.container })">
|
|
@@ -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 { DropdownMenuRootProps, DropdownMenuRootEmits, DropdownMenuContentProps, DropdownMenuArrowProps } from 'reka-ui'
|
|
4
|
+
import type { DropdownMenuRootProps, DropdownMenuRootEmits, DropdownMenuContentProps, DropdownMenuContentEmits, DropdownMenuArrowProps } 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/dropdown-menu'
|
|
7
8
|
import { tv } from '../utils/tv'
|
|
8
9
|
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
|
9
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
ArrayOrNested,
|
|
12
|
+
DynamicSlots,
|
|
13
|
+
MergeTypes,
|
|
14
|
+
NestedItem,
|
|
15
|
+
PartialString,
|
|
16
|
+
EmitsToProps
|
|
17
|
+
} from '../types/utils'
|
|
10
18
|
|
|
11
19
|
const appConfigDropdownMenu = _appConfig as AppConfig & { ui: { dropdownMenu: Partial<typeof theme> } }
|
|
12
20
|
|
|
@@ -22,7 +30,7 @@ export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cust
|
|
|
22
30
|
icon?: string
|
|
23
31
|
color?: DropdownMenuVariants['color']
|
|
24
32
|
avatar?: AvatarProps
|
|
25
|
-
content?: Omit<DropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'>
|
|
33
|
+
content?: Omit<DropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<DropdownMenuContentEmits>>
|
|
26
34
|
kbds?: KbdProps['value'][] | KbdProps[]
|
|
27
35
|
/**
|
|
28
36
|
* The item type.
|
|
@@ -35,17 +43,18 @@ export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cust
|
|
|
35
43
|
checked?: boolean
|
|
36
44
|
open?: boolean
|
|
37
45
|
defaultOpen?: boolean
|
|
38
|
-
children?: DropdownMenuItem
|
|
46
|
+
children?: ArrayOrNested<DropdownMenuItem>
|
|
39
47
|
onSelect?(e: Event): void
|
|
40
48
|
onUpdateChecked?(checked: boolean): void
|
|
49
|
+
[key: string]: any
|
|
41
50
|
}
|
|
42
51
|
|
|
43
|
-
export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'> {
|
|
52
|
+
export interface DropdownMenuProps<T extends ArrayOrNested<DropdownMenuItem> = ArrayOrNested<DropdownMenuItem>> extends Omit<DropdownMenuRootProps, 'dir'> {
|
|
44
53
|
/**
|
|
45
54
|
* @defaultValue 'md'
|
|
46
55
|
*/
|
|
47
56
|
size?: DropdownMenuVariants['size']
|
|
48
|
-
items?: T
|
|
57
|
+
items?: T
|
|
49
58
|
/**
|
|
50
59
|
* The icon displayed when an item is checked.
|
|
51
60
|
* @defaultValue appConfig.ui.icons.check
|
|
@@ -69,7 +78,7 @@ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'>
|
|
|
69
78
|
* The content of the menu.
|
|
70
79
|
* @defaultValue { side: 'bottom', sideOffset: 8, collisionPadding: 8 }
|
|
71
80
|
*/
|
|
72
|
-
content?: Omit<DropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'>
|
|
81
|
+
content?: Omit<DropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<DropdownMenuContentEmits>>
|
|
73
82
|
/**
|
|
74
83
|
* Display an arrow alongside the menu.
|
|
75
84
|
* @defaultValue false
|
|
@@ -84,7 +93,7 @@ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'>
|
|
|
84
93
|
* The key used to get the label from the item.
|
|
85
94
|
* @defaultValue 'label'
|
|
86
95
|
*/
|
|
87
|
-
labelKey?:
|
|
96
|
+
labelKey?: keyof NestedItem<T>
|
|
88
97
|
disabled?: boolean
|
|
89
98
|
class?: any
|
|
90
99
|
ui?: PartialString<typeof dropdownMenu.slots>
|
|
@@ -92,18 +101,22 @@ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'>
|
|
|
92
101
|
|
|
93
102
|
export interface DropdownMenuEmits extends DropdownMenuRootEmits {}
|
|
94
103
|
|
|
95
|
-
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
|
|
96
105
|
|
|
97
|
-
export type DropdownMenuSlots<
|
|
106
|
+
export type DropdownMenuSlots<
|
|
107
|
+
A extends ArrayOrNested<DropdownMenuItem> = ArrayOrNested<DropdownMenuItem>,
|
|
108
|
+
T extends NestedItem<A> = NestedItem<A>
|
|
109
|
+
> = {
|
|
98
110
|
'default'(props: { open: boolean }): any
|
|
99
111
|
'item': SlotProps<T>
|
|
100
112
|
'item-leading': SlotProps<T>
|
|
101
113
|
'item-label': SlotProps<T>
|
|
102
114
|
'item-trailing': SlotProps<T>
|
|
103
|
-
} & DynamicSlots<T,
|
|
115
|
+
} & DynamicSlots<MergeTypes<T>, 'leading' | 'label' | 'trailing', { active?: boolean, index: number }>
|
|
116
|
+
|
|
104
117
|
</script>
|
|
105
118
|
|
|
106
|
-
<script setup lang="ts" generic="T extends DropdownMenuItem">
|
|
119
|
+
<script setup lang="ts" generic="T extends ArrayOrNested<DropdownMenuItem>">
|
|
107
120
|
import { computed, toRef } from 'vue'
|
|
108
121
|
import { defu } from 'defu'
|
|
109
122
|
import { DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuArrow, useForwardPropsEmits } from 'reka-ui'
|
|
@@ -123,7 +136,7 @@ const slots = defineSlots<DropdownMenuSlots<T>>()
|
|
|
123
136
|
const rootProps = useForwardPropsEmits(reactivePick(props, 'defaultOpen', 'open', 'modal'), emits)
|
|
124
137
|
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8 }) as DropdownMenuContentProps)
|
|
125
138
|
const arrowProps = toRef(() => props.arrow as DropdownMenuArrowProps)
|
|
126
|
-
const proxySlots = omit(slots, ['default'])
|
|
139
|
+
const proxySlots = omit(slots, ['default'])
|
|
127
140
|
|
|
128
141
|
const ui = computed(() => dropdownMenu({
|
|
129
142
|
size: props.size
|
|
@@ -143,13 +156,13 @@ const ui = computed(() => dropdownMenu({
|
|
|
143
156
|
v-bind="contentProps"
|
|
144
157
|
:items="items"
|
|
145
158
|
:portal="portal"
|
|
146
|
-
:label-key="labelKey"
|
|
159
|
+
:label-key="(labelKey as keyof NestedItem<T>)"
|
|
147
160
|
:checked-icon="checkedIcon"
|
|
148
161
|
:loading-icon="loadingIcon"
|
|
149
162
|
:external-icon="externalIcon"
|
|
150
163
|
>
|
|
151
|
-
<template v-for="(_, name) in proxySlots" #[name]="slotData
|
|
152
|
-
<slot :name="name" v-bind="slotData" />
|
|
164
|
+
<template v-for="(_, name) in proxySlots" #[name]="slotData">
|
|
165
|
+
<slot :name="(name as keyof DropdownMenuSlots<T>)" v-bind="slotData" />
|
|
153
166
|
</template>
|
|
154
167
|
|
|
155
168
|
<DropdownMenuArrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow({ class: props.ui?.arrow })" />
|
|
@@ -4,14 +4,15 @@ import type { DropdownMenuContentProps as RekaDropdownMenuContentProps, Dropdown
|
|
|
4
4
|
import theme from '#build/ui/dropdown-menu'
|
|
5
5
|
import { tv } from '../utils/tv'
|
|
6
6
|
import type { KbdProps, AvatarProps, DropdownMenuItem, DropdownMenuSlots } from '../types'
|
|
7
|
+
import type { ArrayOrNested, NestedItem } from '../types/utils'
|
|
7
8
|
|
|
8
9
|
const _dropdownMenu = tv(theme)()
|
|
9
10
|
|
|
10
|
-
interface DropdownMenuContentProps<T
|
|
11
|
-
items?: T
|
|
11
|
+
interface DropdownMenuContentProps<T extends ArrayOrNested<DropdownMenuItem>> extends Omit<RekaDropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
|
|
12
|
+
items?: T
|
|
12
13
|
portal?: boolean
|
|
13
14
|
sub?: boolean
|
|
14
|
-
labelKey:
|
|
15
|
+
labelKey: keyof NestedItem<T>
|
|
15
16
|
/**
|
|
16
17
|
* @IconifyIcon
|
|
17
18
|
*/
|
|
@@ -31,19 +32,19 @@ interface DropdownMenuContentProps<T> extends Omit<RekaDropdownMenuContentProps,
|
|
|
31
32
|
|
|
32
33
|
interface DropdownMenuContentEmits extends RekaDropdownMenuContentEmits {}
|
|
33
34
|
|
|
34
|
-
type DropdownMenuContentSlots<T extends
|
|
35
|
+
type DropdownMenuContentSlots<T extends ArrayOrNested<DropdownMenuItem>> = Omit<DropdownMenuSlots<T>, 'default'> & {
|
|
35
36
|
default(props?: {}): any
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
</script>
|
|
39
40
|
|
|
40
|
-
<script setup lang="ts" generic="T extends DropdownMenuItem">
|
|
41
|
+
<script setup lang="ts" generic="T extends ArrayOrNested<DropdownMenuItem>">
|
|
41
42
|
import { computed } from 'vue'
|
|
42
43
|
import { DropdownMenu } from 'reka-ui/namespaced'
|
|
43
44
|
import { useForwardPropsEmits } from 'reka-ui'
|
|
44
45
|
import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
|
|
45
46
|
import { useAppConfig } from '#imports'
|
|
46
|
-
import { omit, get } from '../utils'
|
|
47
|
+
import { omit, get, isArrayOfArray } from '../utils'
|
|
47
48
|
import { pickLinkProps } from '../utils/link'
|
|
48
49
|
import ULinkBase from './LinkBase.vue'
|
|
49
50
|
import ULink from './Link.vue'
|
|
@@ -59,24 +60,30 @@ const slots = defineSlots<DropdownMenuContentSlots<T>>()
|
|
|
59
60
|
|
|
60
61
|
const appConfig = useAppConfig()
|
|
61
62
|
const contentProps = useForwardPropsEmits(reactiveOmit(props, 'sub', 'items', 'portal', 'labelKey', 'checkedIcon', 'loadingIcon', 'externalIcon', 'class', 'ui', 'uiOverride'), emits)
|
|
62
|
-
const proxySlots = omit(slots, ['default'])
|
|
63
|
+
const proxySlots = omit(slots, ['default'])
|
|
63
64
|
|
|
64
65
|
const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: DropdownMenuItem, active?: boolean, index: number }>()
|
|
65
66
|
|
|
66
|
-
const groups = computed
|
|
67
|
+
const groups = computed<DropdownMenuItem[][]>(() =>
|
|
68
|
+
props.items?.length
|
|
69
|
+
? isArrayOfArray(props.items)
|
|
70
|
+
? props.items
|
|
71
|
+
: [props.items]
|
|
72
|
+
: []
|
|
73
|
+
)
|
|
67
74
|
</script>
|
|
68
75
|
|
|
69
76
|
<template>
|
|
70
77
|
<DefineItemTemplate v-slot="{ item, active, index }">
|
|
71
|
-
<slot :name="item.slot || 'item'" :item="(item as T)" :index="index">
|
|
72
|
-
<slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="(item as T)" :active="active" :index="index">
|
|
78
|
+
<slot :name="((item.slot || 'item') as keyof DropdownMenuContentSlots<T>)" :item="(item as Extract<NestedItem<T>, { slot: string; }>)" :index="index">
|
|
79
|
+
<slot :name="((item.slot ? `${item.slot}-leading`: 'item-leading') as keyof DropdownMenuContentSlots<T>)" :item="(item as Extract<NestedItem<T>, { slot: string; }>)" :active="active" :index="index">
|
|
73
80
|
<UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, color: item?.color, loading: true })" />
|
|
74
81
|
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, color: item?.color, active })" />
|
|
75
82
|
<UAvatar v-else-if="item.avatar" :size="((props.uiOverride?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: uiOverride?.itemLeadingAvatar, active })" />
|
|
76
83
|
</slot>
|
|
77
84
|
|
|
78
|
-
<span v-if="get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
|
|
79
|
-
<slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="(item as T)" :active="active" :index="index">
|
|
85
|
+
<span v-if="get(item, props.labelKey as string) || !!slots[(item.slot ? `${item.slot}-label`: 'item-label') as keyof DropdownMenuContentSlots<T>]" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
|
|
86
|
+
<slot :name="((item.slot ? `${item.slot}-label`: 'item-label') as keyof DropdownMenuContentSlots<T>)" :item="(item as Extract<NestedItem<T>, { slot: string; }>)" :active="active" :index="index">
|
|
80
87
|
{{ get(item, props.labelKey as string) }}
|
|
81
88
|
</slot>
|
|
82
89
|
|
|
@@ -84,7 +91,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
84
91
|
</span>
|
|
85
92
|
|
|
86
93
|
<span :class="ui.itemTrailing({ class: uiOverride?.itemTrailing })">
|
|
87
|
-
<slot :name="item.slot ? `${item.slot}-trailing`: 'item-trailing'" :item="(item as T)" :active="active" :index="index">
|
|
94
|
+
<slot :name="((item.slot ? `${item.slot}-trailing`: 'item-trailing') as keyof DropdownMenuContentSlots<T>)" :item="(item as Extract<NestedItem<T>, { slot: string; }>)" :active="active" :index="index">
|
|
88
95
|
<UIcon v-if="item.children?.length" :name="appConfig.ui.icons.chevronRight" :class="ui.itemTrailingIcon({ class: uiOverride?.itemTrailingIcon, color: item?.color, active })" />
|
|
89
96
|
<span v-else-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: uiOverride?.itemTrailingKbds })">
|
|
90
97
|
<UKbd v-for="(kbd, kbdIndex) in item.kbds" :key="kbdIndex" :size="((props.uiOverride?.itemTrailingKbdsSize || ui.itemTrailingKbdsSize()) as KbdProps['size'])" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" />
|
|
@@ -123,7 +130,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
123
130
|
:ui="ui"
|
|
124
131
|
:ui-override="uiOverride"
|
|
125
132
|
:portal="portal"
|
|
126
|
-
:items="item.children"
|
|
133
|
+
:items="(item.children as T)"
|
|
127
134
|
side="right"
|
|
128
135
|
align="start"
|
|
129
136
|
:align-offset="-4"
|
|
@@ -135,7 +142,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
135
142
|
v-bind="item.content"
|
|
136
143
|
>
|
|
137
144
|
<template v-for="(_, name) in proxySlots" #[name]="slotData: any">
|
|
138
|
-
<slot :name="name" v-bind="slotData" />
|
|
145
|
+
<slot :name="(name as keyof DropdownMenuContentSlots<T>)" v-bind="slotData" />
|
|
139
146
|
</template>
|
|
140
147
|
</UDropdownMenuContent>
|
|
141
148
|
</DropdownMenu.Sub>
|
|
@@ -12,14 +12,34 @@ const form = tv({ extend: tv(theme), ...(appConfigForm.ui?.form || {}) })
|
|
|
12
12
|
|
|
13
13
|
export interface FormProps<T extends object> {
|
|
14
14
|
id?: string | number
|
|
15
|
+
/** Schema to validate the form state. Supports Standard Schema objects, Yup, Joi, and Superstructs. */
|
|
15
16
|
schema?: FormSchema<T>
|
|
17
|
+
/** An object representing the current state of the form. */
|
|
16
18
|
state: Partial<T>
|
|
19
|
+
/**
|
|
20
|
+
* Custom validation function to validate the form state.
|
|
21
|
+
* @param state - The current state of the form.
|
|
22
|
+
* @returns A promise that resolves to an array of FormError objects, or an array of FormError objects directly.
|
|
23
|
+
*/
|
|
17
24
|
validate?: (state: Partial<T>) => Promise<FormError[]> | FormError[]
|
|
25
|
+
/**
|
|
26
|
+
* The list of input events that trigger the form validation.
|
|
27
|
+
* @defaultValue `['blur', 'change', 'input']`
|
|
28
|
+
*/
|
|
18
29
|
validateOn?: FormInputEvents[]
|
|
30
|
+
/** Disable all inputs inside the form. */
|
|
19
31
|
disabled?: boolean
|
|
32
|
+
/**
|
|
33
|
+
* Delay in milliseconds before validating the form on input events.
|
|
34
|
+
* @defaultValue `300`
|
|
35
|
+
*/
|
|
20
36
|
validateOnInputDelay?: number
|
|
21
|
-
|
|
37
|
+
/**
|
|
38
|
+
* If true, schema transformations will be applied to the state on submit.
|
|
39
|
+
* @defaultValue `true`
|
|
40
|
+
*/
|
|
22
41
|
transform?: boolean
|
|
42
|
+
class?: any
|
|
23
43
|
onSubmit?: ((event: FormSubmitEvent<T>) => void | Promise<void>) | (() => void | Promise<void>)
|
|
24
44
|
}
|
|
25
45
|
|
|
@@ -197,6 +217,7 @@ async function onSubmitWrapper(payload: Event) {
|
|
|
197
217
|
try {
|
|
198
218
|
event.data = await _validate({ nested: true, transform: props.transform })
|
|
199
219
|
await props.onSubmit?.(event)
|
|
220
|
+
dirtyFields.clear()
|
|
200
221
|
} catch (error) {
|
|
201
222
|
if (!(error instanceof FormValidationException)) {
|
|
202
223
|
throw error
|
|
@@ -31,7 +31,12 @@ export interface FormFieldProps {
|
|
|
31
31
|
*/
|
|
32
32
|
size?: FormFieldVariants['size']
|
|
33
33
|
required?: boolean
|
|
34
|
+
/** If true, validation on input will be active immediately instead of waiting for a blur event. */
|
|
34
35
|
eagerValidation?: boolean
|
|
36
|
+
/**
|
|
37
|
+
* Delay in milliseconds before validating the form on input events.
|
|
38
|
+
* @defaultValue `300`
|
|
39
|
+
*/
|
|
35
40
|
validateOnInputDelay?: number
|
|
36
41
|
class?: any
|
|
37
42
|
ui?: Partial<typeof formField.slots>
|
|
@@ -81,6 +86,7 @@ provide(formFieldInjectionKey, computed(() => ({
|
|
|
81
86
|
errorPattern: props.errorPattern,
|
|
82
87
|
hint: props.hint,
|
|
83
88
|
description: props.description,
|
|
89
|
+
help: props.help,
|
|
84
90
|
ariaId
|
|
85
91
|
}) as FormFieldInjectedOptions<FormFieldProps>))
|
|
86
92
|
</script>
|