@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.
Files changed (49) hide show
  1. package/README.md +4 -2
  2. package/dist/module.d.mts +19 -5
  3. package/dist/module.d.ts +19 -5
  4. package/dist/module.json +2 -1
  5. package/dist/module.mjs +514 -338
  6. package/dist/runtime/components/Accordion.vue +13 -5
  7. package/dist/runtime/components/Alert.vue +2 -2
  8. package/dist/runtime/components/Breadcrumb.vue +16 -8
  9. package/dist/runtime/components/Button.vue +4 -2
  10. package/dist/runtime/components/Carousel.vue +306 -0
  11. package/dist/runtime/components/Checkbox.vue +3 -6
  12. package/dist/runtime/components/CommandPalette.vue +27 -18
  13. package/dist/runtime/components/ContextMenu.vue +33 -9
  14. package/dist/runtime/components/ContextMenuContent.vue +36 -8
  15. package/dist/runtime/components/DropdownMenu.vue +33 -9
  16. package/dist/runtime/components/DropdownMenuContent.vue +38 -8
  17. package/dist/runtime/components/Form.vue +6 -2
  18. package/dist/runtime/components/Input.vue +6 -4
  19. package/dist/runtime/components/InputMenu.vue +40 -24
  20. package/dist/runtime/components/LinkBase.vue +4 -2
  21. package/dist/runtime/components/Modal.vue +2 -2
  22. package/dist/runtime/components/NavigationMenu.vue +80 -43
  23. package/dist/runtime/components/Pagination.vue +2 -2
  24. package/dist/runtime/components/RadioGroup.vue +22 -3
  25. package/dist/runtime/components/Select.vue +22 -10
  26. package/dist/runtime/components/SelectMenu.vue +43 -31
  27. package/dist/runtime/components/Slideover.vue +2 -2
  28. package/dist/runtime/components/Table.vue +232 -0
  29. package/dist/runtime/components/Tabs.vue +15 -7
  30. package/dist/runtime/components/Toast.vue +2 -2
  31. package/dist/runtime/components/Toaster.vue +1 -1
  32. package/dist/runtime/composables/defineShortcuts.js +1 -1
  33. package/dist/runtime/composables/useComponentIcons.d.ts +3 -0
  34. package/dist/runtime/composables/useModal.d.ts +1 -1
  35. package/dist/runtime/composables/useSlideover.d.ts +1 -1
  36. package/dist/runtime/index.css +1 -1
  37. package/dist/runtime/keyframes.css +1 -0
  38. package/dist/runtime/plugins/colors.js +11 -7
  39. package/dist/runtime/types/form.d.ts +3 -1
  40. package/dist/runtime/types/index.d.ts +2 -0
  41. package/dist/runtime/types/index.js +2 -0
  42. package/dist/runtime/utils/form.d.ts +7 -1
  43. package/dist/runtime/utils/form.js +25 -1
  44. package/dist/runtime/utils/index.d.ts +1 -0
  45. package/dist/runtime/utils/index.js +3 -0
  46. package/dist/runtime/utils/link.d.ts +1 -1
  47. package/package.json +30 -14
  48. package/dist/runtime/types/component.d.ts +0 -17
  49. 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, ContextMenuTriggerProps, ContextMenuItemProps } from 'radix-vue'
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'>, Pick<ContextMenuItemProps, 'disabled'> {
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
- select?(e: Event): void
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'>, Pick<ContextMenuTriggerProps, 'disabled'> {
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?: Partial<typeof contextMenu.slots>
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
- <UAvatar v-if="item.avatar" :size="((props.uiOverride?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: uiOverride?.itemLeadingAvatar, active })" />
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.label || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
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.label }}
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 v-if="item.children?.length || item.kbds?.length || !!slots[item.slot ? `${item.slot}-trailing`: 'item-trailing']" :class="ui.itemTrailing({ class: uiOverride?.itemTrailing })">
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.label"
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.Item v-else as-child :disabled="item.disabled" :text-value="item.label" @select="item.select">
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, DropdownMenuTriggerProps, DropdownMenuItemProps } from 'radix-vue'
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'>, Pick<DropdownMenuItemProps, 'disabled'> {
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
- select?(e: Event): void
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'>, Pick<DropdownMenuTriggerProps, 'disabled'> {
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?: Partial<typeof dropdownMenu.slots>
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
- <UAvatar v-if="item.avatar" :size="((props.uiOverride?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: uiOverride?.itemLeadingAvatar, active })" />
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.label || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="ui.itemLabel({ class: uiOverride?.itemLabel, active })">
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.label }}
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 v-if="item.children?.length || item.kbds?.length || !!slots[item.slot ? `${item.slot}-trailing`: 'item-trailing']" :class="ui.itemTrailing({ class: uiOverride?.itemTrailing })">
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.label"
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.Item v-else as-child :disabled="item.disabled" :text-value="item.label" @select="item.select">
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, getValibotError, isValibotSchema, getZodErrors, isZodSchema, getJoiErrors, isJoiSchema } from '../utils/form'
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 getValibotError(props.state, props.schema))
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