@nuxt/ui 3.0.0-alpha.5 → 3.0.0-alpha.7
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/README.md +4 -2
- package/dist/module.d.mts +19 -5
- package/dist/module.d.ts +19 -5
- package/dist/module.json +2 -1
- package/dist/module.mjs +514 -338
- package/dist/runtime/components/Accordion.vue +13 -5
- package/dist/runtime/components/Alert.vue +2 -2
- package/dist/runtime/components/Breadcrumb.vue +16 -8
- package/dist/runtime/components/Button.vue +4 -2
- package/dist/runtime/components/Carousel.vue +306 -0
- package/dist/runtime/components/Checkbox.vue +3 -6
- package/dist/runtime/components/CommandPalette.vue +27 -18
- package/dist/runtime/components/ContextMenu.vue +33 -9
- package/dist/runtime/components/ContextMenuContent.vue +36 -8
- package/dist/runtime/components/DropdownMenu.vue +33 -9
- package/dist/runtime/components/DropdownMenuContent.vue +38 -8
- package/dist/runtime/components/Form.vue +6 -2
- package/dist/runtime/components/Input.vue +6 -4
- package/dist/runtime/components/InputMenu.vue +40 -24
- package/dist/runtime/components/LinkBase.vue +4 -2
- package/dist/runtime/components/Modal.vue +2 -2
- package/dist/runtime/components/NavigationMenu.vue +80 -43
- package/dist/runtime/components/Pagination.vue +2 -2
- package/dist/runtime/components/RadioGroup.vue +22 -3
- package/dist/runtime/components/Select.vue +22 -10
- package/dist/runtime/components/SelectMenu.vue +43 -31
- package/dist/runtime/components/Slideover.vue +2 -2
- package/dist/runtime/components/Table.vue +232 -0
- package/dist/runtime/components/Tabs.vue +15 -7
- package/dist/runtime/components/Toast.vue +2 -2
- package/dist/runtime/components/Toaster.vue +1 -1
- package/dist/runtime/composables/defineShortcuts.js +1 -1
- package/dist/runtime/composables/useComponentIcons.d.ts +3 -0
- package/dist/runtime/composables/useModal.d.ts +1 -1
- package/dist/runtime/composables/useSlideover.d.ts +1 -1
- package/dist/runtime/index.css +1 -1
- package/dist/runtime/keyframes.css +1 -0
- package/dist/runtime/plugins/colors.js +11 -7
- package/dist/runtime/types/form.d.ts +3 -1
- package/dist/runtime/types/index.d.ts +2 -0
- package/dist/runtime/types/index.js +2 -0
- package/dist/runtime/utils/form.d.ts +7 -1
- package/dist/runtime/utils/form.js +25 -1
- package/dist/runtime/utils/index.d.ts +1 -0
- package/dist/runtime/utils/index.js +3 -0
- package/dist/runtime/utils/link.d.ts +1 -1
- package/package.json +30 -14
- package/dist/runtime/types/component.d.ts +0 -17
- package/dist/runtime/types/component.js +0 -0
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
<!-- eslint-disable vue/block-tag-newline -->
|
|
2
2
|
<script lang="ts">
|
|
3
3
|
import { tv, type VariantProps } from 'tailwind-variants'
|
|
4
|
-
import type { ContextMenuRootProps, ContextMenuRootEmits, ContextMenuContentProps
|
|
4
|
+
import type { ContextMenuRootProps, ContextMenuRootEmits, ContextMenuContentProps } from 'radix-vue'
|
|
5
5
|
import type { AppConfig } from '@nuxt/schema'
|
|
6
6
|
import _appConfig from '#build/app.config'
|
|
7
7
|
import theme from '#build/ui/context-menu'
|
|
8
8
|
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
|
9
|
-
import type { DynamicSlots } from '../types/utils'
|
|
9
|
+
import type { DynamicSlots, PartialString } from '../types/utils'
|
|
10
10
|
|
|
11
11
|
const appConfig = _appConfig as AppConfig & { ui: { contextMenu: Partial<typeof theme> } }
|
|
12
12
|
|
|
13
13
|
const contextMenu = tv({ extend: tv(theme), ...(appConfig.ui?.contextMenu || {}) })
|
|
14
14
|
|
|
15
|
-
export interface ContextMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'
|
|
15
|
+
export interface ContextMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'> {
|
|
16
16
|
label?: string
|
|
17
17
|
icon?: string
|
|
18
18
|
avatar?: AvatarProps
|
|
@@ -22,19 +22,33 @@ export interface ContextMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custo
|
|
|
22
22
|
* The item type.
|
|
23
23
|
* @defaultValue 'link'
|
|
24
24
|
*/
|
|
25
|
-
type?: 'label' | 'separator' | 'link'
|
|
25
|
+
type?: 'label' | 'separator' | 'link' | 'checkbox'
|
|
26
26
|
slot?: string
|
|
27
|
+
loading?: boolean
|
|
28
|
+
disabled?: boolean
|
|
29
|
+
checked?: boolean
|
|
27
30
|
open?: boolean
|
|
28
31
|
defaultOpen?: boolean
|
|
29
32
|
children?: ContextMenuItem[] | ContextMenuItem[][]
|
|
30
|
-
|
|
33
|
+
onSelect?(e: Event): void
|
|
34
|
+
onUpdateChecked?(checked: boolean): void
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
type ContextMenuVariants = VariantProps<typeof contextMenu>
|
|
34
38
|
|
|
35
|
-
export interface ContextMenuProps<T> extends Omit<ContextMenuRootProps, 'dir'
|
|
39
|
+
export interface ContextMenuProps<T> extends Omit<ContextMenuRootProps, 'dir'> {
|
|
36
40
|
size?: ContextMenuVariants['size']
|
|
37
41
|
items?: T[] | T[][]
|
|
42
|
+
/**
|
|
43
|
+
* The icon displayed when an item is checked.
|
|
44
|
+
* @defaultValue appConfig.ui.icons.check
|
|
45
|
+
*/
|
|
46
|
+
checkedIcon?: string
|
|
47
|
+
/**
|
|
48
|
+
* The icon displayed when an item is loading.
|
|
49
|
+
* @defaultValue appConfig.ui.icons.loading
|
|
50
|
+
*/
|
|
51
|
+
loadingIcon?: string
|
|
38
52
|
/** The content of the menu. */
|
|
39
53
|
content?: Omit<ContextMenuContentProps, 'as' | 'asChild' | 'forceMount'>
|
|
40
54
|
/**
|
|
@@ -42,8 +56,14 @@ export interface ContextMenuProps<T> extends Omit<ContextMenuRootProps, 'dir'>,
|
|
|
42
56
|
* @defaultValue true
|
|
43
57
|
*/
|
|
44
58
|
portal?: boolean
|
|
59
|
+
/**
|
|
60
|
+
* The key used to get the label from the item.
|
|
61
|
+
* @defaultValue 'label'
|
|
62
|
+
*/
|
|
63
|
+
labelKey?: string
|
|
64
|
+
disabled?: boolean
|
|
45
65
|
class?: any
|
|
46
|
-
ui?:
|
|
66
|
+
ui?: PartialString<typeof contextMenu.slots>
|
|
47
67
|
}
|
|
48
68
|
|
|
49
69
|
export interface ContextMenuEmits extends ContextMenuRootEmits {}
|
|
@@ -64,12 +84,13 @@ export type ContextMenuSlots<T extends { slot?: string }> = {
|
|
|
64
84
|
import { computed, toRef } from 'vue'
|
|
65
85
|
import { ContextMenuRoot, ContextMenuTrigger, useForwardPropsEmits } from 'radix-vue'
|
|
66
86
|
import { reactivePick } from '@vueuse/core'
|
|
67
|
-
import UContextMenuContent from './ContextMenuContent.vue'
|
|
68
87
|
import { omit } from '../utils'
|
|
88
|
+
import UContextMenuContent from './ContextMenuContent.vue'
|
|
69
89
|
|
|
70
90
|
const props = withDefaults(defineProps<ContextMenuProps<T>>(), {
|
|
71
91
|
portal: true,
|
|
72
|
-
modal: true
|
|
92
|
+
modal: true,
|
|
93
|
+
labelKey: 'label'
|
|
73
94
|
})
|
|
74
95
|
const emits = defineEmits<ContextMenuEmits>()
|
|
75
96
|
const slots = defineSlots<ContextMenuSlots<T>>()
|
|
@@ -96,6 +117,9 @@ const ui = computed(() => contextMenu({
|
|
|
96
117
|
v-bind="contentProps"
|
|
97
118
|
:items="items"
|
|
98
119
|
:portal="portal"
|
|
120
|
+
:label-key="labelKey"
|
|
121
|
+
:checked-icon="checkedIcon"
|
|
122
|
+
:loading-icon="loadingIcon"
|
|
99
123
|
>
|
|
100
124
|
<template v-for="(_, name) in proxySlots" #[name]="slotData: any">
|
|
101
125
|
<slot :name="name" v-bind="slotData" />
|
|
@@ -10,6 +10,9 @@ interface ContextMenuContentProps<T> extends Omit<RadixContextMenuContentProps,
|
|
|
10
10
|
items?: T[] | T[][]
|
|
11
11
|
portal?: boolean
|
|
12
12
|
sub?: boolean
|
|
13
|
+
labelKey: string
|
|
14
|
+
checkedIcon?: string
|
|
15
|
+
loadingIcon?: string
|
|
13
16
|
class?: any
|
|
14
17
|
ui: typeof _contextMenu
|
|
15
18
|
uiOverride?: any
|
|
@@ -24,13 +27,13 @@ import { ContextMenu } from 'radix-vue/namespaced'
|
|
|
24
27
|
import { useForwardPropsEmits } from 'radix-vue'
|
|
25
28
|
import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
|
|
26
29
|
import { useAppConfig } from '#imports'
|
|
30
|
+
import { omit, get } from '../utils'
|
|
31
|
+
import { pickLinkProps } from '../utils/link'
|
|
27
32
|
import ULinkBase from './LinkBase.vue'
|
|
28
33
|
import ULink from './Link.vue'
|
|
29
34
|
import UAvatar from './Avatar.vue'
|
|
30
35
|
import UIcon from './Icon.vue'
|
|
31
36
|
import UKbd from './Kbd.vue'
|
|
32
|
-
import { omit } from '../utils'
|
|
33
|
-
import { pickLinkProps } from '../utils/link'
|
|
34
37
|
|
|
35
38
|
const props = defineProps<ContextMenuContentProps<T>>()
|
|
36
39
|
const emits = defineEmits<ContextMenuContentEmits>()
|
|
@@ -49,25 +52,30 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
49
52
|
<DefineItemTemplate v-slot="{ item, active, index }">
|
|
50
53
|
<slot :name="item.slot || 'item'" :item="(item as T)" :index="index">
|
|
51
54
|
<slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="(item as T)" :active="active" :index="index">
|
|
52
|
-
<
|
|
55
|
+
<UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, loading: true })" />
|
|
53
56
|
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, active })" />
|
|
57
|
+
<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 })" />
|
|
54
58
|
</slot>
|
|
55
59
|
|
|
56
|
-
<span v-if="item.
|
|
60
|
+
<span v-if="get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
|
|
57
61
|
<slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="(item as T)" :active="active" :index="index">
|
|
58
|
-
{{ item.
|
|
62
|
+
{{ get(item, props.labelKey as string) }}
|
|
59
63
|
</slot>
|
|
60
64
|
|
|
61
65
|
<UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.itemLabelExternalIcon({ class: uiOverride?.itemLabelExternalIcon, active })" />
|
|
62
66
|
</span>
|
|
63
67
|
|
|
64
|
-
<span
|
|
68
|
+
<span :class="ui.itemTrailing({ class: uiOverride?.itemTrailing })">
|
|
65
69
|
<slot :name="item.slot ? `${item.slot}-trailing`: 'item-trailing'" :item="(item as T)" :active="active" :index="index">
|
|
66
70
|
<UIcon v-if="item.children?.length" :name="appConfig.ui.icons.chevronRight" :class="ui.itemTrailingIcon({ class: uiOverride?.itemTrailingIcon, active })" />
|
|
67
71
|
<span v-else-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: uiOverride?.itemTrailingKbds })">
|
|
68
72
|
<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" />
|
|
69
73
|
</span>
|
|
70
74
|
</slot>
|
|
75
|
+
|
|
76
|
+
<ContextMenu.ItemIndicator as-child>
|
|
77
|
+
<UIcon :name="checkedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: uiOverride?.itemTrailingIcon })" />
|
|
78
|
+
</ContextMenu.ItemIndicator>
|
|
71
79
|
</span>
|
|
72
80
|
</slot>
|
|
73
81
|
</DefineItemTemplate>
|
|
@@ -85,7 +93,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
85
93
|
as="button"
|
|
86
94
|
type="button"
|
|
87
95
|
:disabled="item.disabled"
|
|
88
|
-
:text-value="item.
|
|
96
|
+
:text-value="get(item, props.labelKey as string)"
|
|
89
97
|
:class="ui.item({ class: uiOverride?.item })"
|
|
90
98
|
>
|
|
91
99
|
<ReuseItemTemplate :item="item" :index="index" />
|
|
@@ -99,6 +107,9 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
99
107
|
:portal="portal"
|
|
100
108
|
:items="item.children"
|
|
101
109
|
:align-offset="-4"
|
|
110
|
+
:label-key="labelKey"
|
|
111
|
+
:checked-icon="checkedIcon"
|
|
112
|
+
:loading-icon="loadingIcon"
|
|
102
113
|
v-bind="item.content"
|
|
103
114
|
>
|
|
104
115
|
<template v-for="(_, name) in proxySlots" #[name]="slotData: any">
|
|
@@ -106,7 +117,24 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
106
117
|
</template>
|
|
107
118
|
</UContextMenuContent>
|
|
108
119
|
</ContextMenu.Sub>
|
|
109
|
-
<ContextMenu.
|
|
120
|
+
<ContextMenu.CheckboxItem
|
|
121
|
+
v-else-if="item.type === 'checkbox'"
|
|
122
|
+
:checked="item.checked"
|
|
123
|
+
:disabled="item.disabled"
|
|
124
|
+
:text-value="get(item, props.labelKey as string)"
|
|
125
|
+
:class="ui.item({ class: [uiOverride?.item, item.class] })"
|
|
126
|
+
@update:checked="item.onUpdateChecked"
|
|
127
|
+
@select="item.onSelect"
|
|
128
|
+
>
|
|
129
|
+
<ReuseItemTemplate :item="item" :index="index" />
|
|
130
|
+
</ContextMenu.CheckboxItem>
|
|
131
|
+
<ContextMenu.Item
|
|
132
|
+
v-else
|
|
133
|
+
as-child
|
|
134
|
+
:disabled="item.disabled"
|
|
135
|
+
:text-value="get(item, props.labelKey as string)"
|
|
136
|
+
@select="item.onSelect"
|
|
137
|
+
>
|
|
110
138
|
<ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item as Omit<ContextMenuItem, 'type'>)" custom>
|
|
111
139
|
<ULinkBase v-bind="slotProps" :class="ui.item({ class: [uiOverride?.item, item.class], active })">
|
|
112
140
|
<ReuseItemTemplate :item="item" :active="active" :index="index" />
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
<!-- eslint-disable vue/block-tag-newline -->
|
|
2
2
|
<script lang="ts">
|
|
3
3
|
import { tv, type VariantProps } from 'tailwind-variants'
|
|
4
|
-
import type { DropdownMenuRootProps, DropdownMenuRootEmits, DropdownMenuContentProps, DropdownMenuArrowProps
|
|
4
|
+
import type { DropdownMenuRootProps, DropdownMenuRootEmits, DropdownMenuContentProps, DropdownMenuArrowProps } from 'radix-vue'
|
|
5
5
|
import type { AppConfig } from '@nuxt/schema'
|
|
6
6
|
import _appConfig from '#build/app.config'
|
|
7
7
|
import theme from '#build/ui/dropdown-menu'
|
|
8
8
|
import type { AvatarProps, KbdProps, LinkProps } from '../types'
|
|
9
|
-
import type { DynamicSlots } from '../types/utils'
|
|
9
|
+
import type { DynamicSlots, PartialString } from '../types/utils'
|
|
10
10
|
|
|
11
11
|
const appConfig = _appConfig as AppConfig & { ui: { dropdownMenu: Partial<typeof theme> } }
|
|
12
12
|
|
|
13
13
|
const dropdownMenu = tv({ extend: tv(theme), ...(appConfig.ui?.dropdownMenu || {}) })
|
|
14
14
|
|
|
15
|
-
export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'
|
|
15
|
+
export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'> {
|
|
16
16
|
label?: string
|
|
17
17
|
icon?: string
|
|
18
18
|
avatar?: AvatarProps
|
|
@@ -22,19 +22,33 @@ export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cust
|
|
|
22
22
|
* The item type.
|
|
23
23
|
* @defaultValue 'link'
|
|
24
24
|
*/
|
|
25
|
-
type?: 'label' | 'separator' | 'link'
|
|
25
|
+
type?: 'label' | 'separator' | 'link' | 'checkbox'
|
|
26
26
|
slot?: string
|
|
27
|
+
loading?: boolean
|
|
28
|
+
disabled?: boolean
|
|
29
|
+
checked?: boolean
|
|
27
30
|
open?: boolean
|
|
28
31
|
defaultOpen?: boolean
|
|
29
32
|
children?: DropdownMenuItem[] | DropdownMenuItem[][]
|
|
30
|
-
|
|
33
|
+
onSelect?(e: Event): void
|
|
34
|
+
onUpdateChecked?(checked: boolean): void
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
type DropdownMenuVariants = VariantProps<typeof dropdownMenu>
|
|
34
38
|
|
|
35
|
-
export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'
|
|
39
|
+
export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'> {
|
|
36
40
|
size?: DropdownMenuVariants['size']
|
|
37
41
|
items?: T[] | T[][]
|
|
42
|
+
/**
|
|
43
|
+
* The icon displayed when an item is checked.
|
|
44
|
+
* @defaultValue appConfig.ui.icons.check
|
|
45
|
+
*/
|
|
46
|
+
checkedIcon?: string
|
|
47
|
+
/**
|
|
48
|
+
* The icon displayed when an item is loading.
|
|
49
|
+
* @defaultValue appConfig.ui.icons.loading
|
|
50
|
+
*/
|
|
51
|
+
loadingIcon?: string
|
|
38
52
|
/**
|
|
39
53
|
* The content of the menu.
|
|
40
54
|
* @defaultValue { side: 'bottom', sideOffset: 8 }
|
|
@@ -50,8 +64,14 @@ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'>
|
|
|
50
64
|
* @defaultValue true
|
|
51
65
|
*/
|
|
52
66
|
portal?: boolean
|
|
67
|
+
/**
|
|
68
|
+
* The key used to get the label from the item.
|
|
69
|
+
* @defaultValue 'label'
|
|
70
|
+
*/
|
|
71
|
+
labelKey?: string
|
|
72
|
+
disabled?: boolean
|
|
53
73
|
class?: any
|
|
54
|
-
ui?:
|
|
74
|
+
ui?: PartialString<typeof dropdownMenu.slots>
|
|
55
75
|
}
|
|
56
76
|
|
|
57
77
|
export interface DropdownMenuEmits extends DropdownMenuRootEmits {}
|
|
@@ -73,12 +93,13 @@ import { computed, toRef } from 'vue'
|
|
|
73
93
|
import { defu } from 'defu'
|
|
74
94
|
import { DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuArrow, useForwardPropsEmits } from 'radix-vue'
|
|
75
95
|
import { reactivePick } from '@vueuse/core'
|
|
76
|
-
import UDropdownMenuContent from './DropdownMenuContent.vue'
|
|
77
96
|
import { omit } from '../utils'
|
|
97
|
+
import UDropdownMenuContent from './DropdownMenuContent.vue'
|
|
78
98
|
|
|
79
99
|
const props = withDefaults(defineProps<DropdownMenuProps<T>>(), {
|
|
80
100
|
portal: true,
|
|
81
|
-
modal: true
|
|
101
|
+
modal: true,
|
|
102
|
+
labelKey: 'label'
|
|
82
103
|
})
|
|
83
104
|
const emits = defineEmits<DropdownMenuEmits>()
|
|
84
105
|
const slots = defineSlots<DropdownMenuSlots<T>>()
|
|
@@ -106,6 +127,9 @@ const ui = computed(() => dropdownMenu({
|
|
|
106
127
|
v-bind="contentProps"
|
|
107
128
|
:items="items"
|
|
108
129
|
:portal="portal"
|
|
130
|
+
:label-key="labelKey"
|
|
131
|
+
:checked-icon="checkedIcon"
|
|
132
|
+
:loading-icon="loadingIcon"
|
|
109
133
|
>
|
|
110
134
|
<template v-for="(_, name) in proxySlots" #[name]="slotData: any">
|
|
111
135
|
<slot :name="name" v-bind="slotData" />
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<!-- eslint-disable vue/block-tag-newline -->
|
|
1
2
|
<script lang="ts">
|
|
2
3
|
import { tv } from 'tailwind-variants'
|
|
3
4
|
import type { DropdownMenuContentProps as RadixDropdownMenuContentProps, DropdownMenuContentEmits as RadixDropdownMenuContentEmits } from 'radix-vue'
|
|
@@ -10,6 +11,9 @@ interface DropdownMenuContentProps<T> extends Omit<RadixDropdownMenuContentProps
|
|
|
10
11
|
items?: T[] | T[][]
|
|
11
12
|
portal?: boolean
|
|
12
13
|
sub?: boolean
|
|
14
|
+
labelKey: string
|
|
15
|
+
checkedIcon?: string
|
|
16
|
+
loadingIcon?: string
|
|
13
17
|
class?: any
|
|
14
18
|
ui: typeof _dropdownMenu
|
|
15
19
|
uiOverride?: any
|
|
@@ -20,6 +24,7 @@ interface DropdownMenuContentEmits extends RadixDropdownMenuContentEmits {}
|
|
|
20
24
|
type DropdownMenuContentSlots<T extends { slot?: string }> = Omit<DropdownMenuSlots<T>, 'default'> & {
|
|
21
25
|
default(props?: {}): any
|
|
22
26
|
}
|
|
27
|
+
|
|
23
28
|
</script>
|
|
24
29
|
|
|
25
30
|
<script setup lang="ts" generic="T extends DropdownMenuItem">
|
|
@@ -28,13 +33,13 @@ import { DropdownMenu } from 'radix-vue/namespaced'
|
|
|
28
33
|
import { useForwardPropsEmits } from 'radix-vue'
|
|
29
34
|
import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
|
|
30
35
|
import { useAppConfig } from '#imports'
|
|
36
|
+
import { omit, get } from '../utils'
|
|
37
|
+
import { pickLinkProps } from '../utils/link'
|
|
31
38
|
import ULinkBase from './LinkBase.vue'
|
|
32
39
|
import ULink from './Link.vue'
|
|
33
40
|
import UAvatar from './Avatar.vue'
|
|
34
41
|
import UIcon from './Icon.vue'
|
|
35
42
|
import UKbd from './Kbd.vue'
|
|
36
|
-
import { omit } from '../utils'
|
|
37
|
-
import { pickLinkProps } from '../utils/link'
|
|
38
43
|
|
|
39
44
|
const props = defineProps<DropdownMenuContentProps<T>>()
|
|
40
45
|
const emits = defineEmits<DropdownMenuContentEmits>()
|
|
@@ -53,25 +58,30 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
53
58
|
<DefineItemTemplate v-slot="{ item, active, index }">
|
|
54
59
|
<slot :name="item.slot || 'item'" :item="(item as T)" :index="index">
|
|
55
60
|
<slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="(item as T)" :active="active" :index="index">
|
|
56
|
-
<
|
|
61
|
+
<UIcon v-if="item.loading" :name="loadingIcon || appConfig.ui.icons.loading" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, loading: true })" />
|
|
57
62
|
<UIcon v-else-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: uiOverride?.itemLeadingIcon, active })" />
|
|
63
|
+
<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 })" />
|
|
58
64
|
</slot>
|
|
59
65
|
|
|
60
|
-
<span v-if="item.
|
|
66
|
+
<span v-if="get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
|
|
61
67
|
<slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="(item as T)" :active="active" :index="index">
|
|
62
|
-
{{ item.
|
|
68
|
+
{{ get(item, props.labelKey as string) }}
|
|
63
69
|
</slot>
|
|
64
70
|
|
|
65
71
|
<UIcon v-if="item.target === '_blank'" :name="appConfig.ui.icons.external" :class="ui.itemLabelExternalIcon({ class: uiOverride?.itemLabelExternalIcon, active })" />
|
|
66
72
|
</span>
|
|
67
73
|
|
|
68
|
-
<span
|
|
74
|
+
<span :class="ui.itemTrailing({ class: uiOverride?.itemTrailing })">
|
|
69
75
|
<slot :name="item.slot ? `${item.slot}-trailing`: 'item-trailing'" :item="(item as T)" :active="active" :index="index">
|
|
70
76
|
<UIcon v-if="item.children?.length" :name="appConfig.ui.icons.chevronRight" :class="ui.itemTrailingIcon({ class: uiOverride?.itemTrailingIcon, active })" />
|
|
71
77
|
<span v-else-if="item.kbds?.length" :class="ui.itemTrailingKbds({ class: uiOverride?.itemTrailingKbds })">
|
|
72
78
|
<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" />
|
|
73
79
|
</span>
|
|
74
80
|
</slot>
|
|
81
|
+
|
|
82
|
+
<DropdownMenu.ItemIndicator as-child>
|
|
83
|
+
<UIcon :name="checkedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: uiOverride?.itemTrailingIcon })" />
|
|
84
|
+
</DropdownMenu.ItemIndicator>
|
|
75
85
|
</span>
|
|
76
86
|
</slot>
|
|
77
87
|
</DefineItemTemplate>
|
|
@@ -89,7 +99,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
89
99
|
as="button"
|
|
90
100
|
type="button"
|
|
91
101
|
:disabled="item.disabled"
|
|
92
|
-
:text-value="item.
|
|
102
|
+
:text-value="get(item, props.labelKey as string)"
|
|
93
103
|
:class="ui.item({ class: uiOverride?.item })"
|
|
94
104
|
>
|
|
95
105
|
<ReuseItemTemplate :item="item" :index="index" />
|
|
@@ -106,6 +116,9 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
106
116
|
align="start"
|
|
107
117
|
:align-offset="-4"
|
|
108
118
|
:side-offset="3"
|
|
119
|
+
:label-key="labelKey"
|
|
120
|
+
:checked-icon="checkedIcon"
|
|
121
|
+
:loading-icon="loadingIcon"
|
|
109
122
|
v-bind="item.content"
|
|
110
123
|
>
|
|
111
124
|
<template v-for="(_, name) in proxySlots" #[name]="slotData: any">
|
|
@@ -113,7 +126,24 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
|
|
113
126
|
</template>
|
|
114
127
|
</UDropdownMenuContent>
|
|
115
128
|
</DropdownMenu.Sub>
|
|
116
|
-
<DropdownMenu.
|
|
129
|
+
<DropdownMenu.CheckboxItem
|
|
130
|
+
v-else-if="item.type === 'checkbox'"
|
|
131
|
+
:checked="item.checked"
|
|
132
|
+
:disabled="item.disabled"
|
|
133
|
+
:text-value="get(item, props.labelKey as string)"
|
|
134
|
+
:class="ui.item({ class: [uiOverride?.item, item.class] })"
|
|
135
|
+
@update:checked="item.onUpdateChecked"
|
|
136
|
+
@select="item.onSelect"
|
|
137
|
+
>
|
|
138
|
+
<ReuseItemTemplate :item="item" :index="index" />
|
|
139
|
+
</DropdownMenu.CheckboxItem>
|
|
140
|
+
<DropdownMenu.Item
|
|
141
|
+
v-else
|
|
142
|
+
as-child
|
|
143
|
+
:disabled="item.disabled"
|
|
144
|
+
:text-value="get(item, props.labelKey as string)"
|
|
145
|
+
@select="item.onSelect"
|
|
146
|
+
>
|
|
117
147
|
<ULink v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item as Omit<DropdownMenuItem, 'type'>)" custom>
|
|
118
148
|
<ULinkBase v-bind="slotProps" :class="ui.item({ class: [uiOverride?.item, item.class], active })">
|
|
119
149
|
<ReuseItemTemplate :item="item" :active="active" :index="index" />
|
|
@@ -35,7 +35,7 @@ export interface FormSlots {
|
|
|
35
35
|
import { provide, inject, nextTick, ref, onUnmounted, onMounted, computed, useId, readonly } from 'vue'
|
|
36
36
|
import { useEventBus } from '@vueuse/core'
|
|
37
37
|
import { formOptionsInjectionKey, formInputsInjectionKey, formBusInjectionKey, formLoadingInjectionKey } from '../composables/useFormField'
|
|
38
|
-
import { getYupErrors, isYupSchema,
|
|
38
|
+
import { getYupErrors, isYupSchema, getValibotErrors, isValibotSchema, getZodErrors, isZodSchema, getJoiErrors, isJoiSchema, getStandardErrors, isStandardSchema, getSuperStructErrors, isSuperStructSchema } from '../utils/form'
|
|
39
39
|
import { FormValidationException } from '../types/form'
|
|
40
40
|
|
|
41
41
|
const props = withDefaults(defineProps<FormProps<T>>(), {
|
|
@@ -112,7 +112,11 @@ async function getErrors(): Promise<FormErrorWithId[]> {
|
|
|
112
112
|
} else if (isJoiSchema(props.schema)) {
|
|
113
113
|
errs = errs.concat(await getJoiErrors(props.state, props.schema))
|
|
114
114
|
} else if (isValibotSchema(props.schema)) {
|
|
115
|
-
errs = errs.concat(await
|
|
115
|
+
errs = errs.concat(await getValibotErrors(props.state, props.schema))
|
|
116
|
+
} else if (isSuperStructSchema(props.schema)) {
|
|
117
|
+
errs = errs.concat(await getSuperStructErrors(props.state, props.schema))
|
|
118
|
+
} else if (isStandardSchema(props.schema)) {
|
|
119
|
+
errs = errs.concat(await getStandardErrors(props.state, props.schema))
|
|
116
120
|
} else {
|
|
117
121
|
throw new Error('Form validation failed: Unsupported form schema')
|
|
118
122
|
}
|
|
@@ -5,6 +5,7 @@ import type { AppConfig } from '@nuxt/schema'
|
|
|
5
5
|
import _appConfig from '#build/app.config'
|
|
6
6
|
import theme from '#build/ui/input'
|
|
7
7
|
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
|
8
|
+
import type { AvatarProps } from '../types'
|
|
8
9
|
import type { PartialString } from '../types/utils'
|
|
9
10
|
|
|
10
11
|
const appConfig = _appConfig as AppConfig & { ui: { input: Partial<typeof theme> } }
|
|
@@ -51,8 +52,8 @@ import { ref, computed, onMounted } from 'vue'
|
|
|
51
52
|
import { useButtonGroup } from '../composables/useButtonGroup'
|
|
52
53
|
import { useComponentIcons } from '../composables/useComponentIcons'
|
|
53
54
|
import { useFormField } from '../composables/useFormField'
|
|
54
|
-
import UIcon from './Icon.vue'
|
|
55
55
|
import { looseToNumber } from '../utils'
|
|
56
|
+
import UIcon from './Icon.vue'
|
|
56
57
|
|
|
57
58
|
defineOptions({ inheritAttrs: false })
|
|
58
59
|
|
|
@@ -79,7 +80,7 @@ const ui = computed(() => input({
|
|
|
79
80
|
size: inputSize?.value,
|
|
80
81
|
loading: props.loading,
|
|
81
82
|
highlight: highlight.value,
|
|
82
|
-
leading: isLeading.value || !!slots.leading,
|
|
83
|
+
leading: isLeading.value || !!props.avatar || !!slots.leading,
|
|
83
84
|
trailing: isTrailing.value || !!slots.trailing,
|
|
84
85
|
buttonGroup: orientation.value
|
|
85
86
|
}))
|
|
@@ -165,9 +166,10 @@ onMounted(() => {
|
|
|
165
166
|
|
|
166
167
|
<slot />
|
|
167
168
|
|
|
168
|
-
<span v-if="isLeading || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
|
|
169
|
+
<span v-if="isLeading || !!avatar || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
|
|
169
170
|
<slot name="leading">
|
|
170
|
-
<UIcon v-if="leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
|
|
171
|
+
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
|
|
172
|
+
<UAvatar v-else-if="!!avatar" :size="((props.ui?.leadingAvatarSize || ui.leadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.leadingAvatar({ class: props.ui?.leadingAvatar })" />
|
|
171
173
|
</slot>
|
|
172
174
|
</span>
|
|
173
175
|
|