@nuxt/ui 3.0.1 → 3.1.0

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 (228) hide show
  1. package/.nuxt/ui/accordion.ts +25 -4
  2. package/.nuxt/ui/alert.ts +52 -31
  3. package/.nuxt/ui/avatar-group.ts +24 -3
  4. package/.nuxt/ui/avatar.ts +26 -5
  5. package/.nuxt/ui/badge.ts +56 -35
  6. package/.nuxt/ui/breadcrumb.ts +28 -7
  7. package/.nuxt/ui/button-group.ts +23 -2
  8. package/.nuxt/ui/button.ts +66 -45
  9. package/.nuxt/ui/calendar.ts +39 -18
  10. package/.nuxt/ui/card.ts +28 -7
  11. package/.nuxt/ui/carousel.ts +29 -8
  12. package/.nuxt/ui/checkbox-group.ts +82 -0
  13. package/.nuxt/ui/checkbox.ts +163 -29
  14. package/.nuxt/ui/chip.ts +31 -10
  15. package/.nuxt/ui/collapsible.ts +23 -2
  16. package/.nuxt/ui/color-picker.ts +26 -5
  17. package/.nuxt/ui/command-palette.ts +37 -16
  18. package/.nuxt/ui/container.ts +24 -3
  19. package/.nuxt/ui/context-menu.ts +56 -35
  20. package/.nuxt/ui/drawer.ts +33 -12
  21. package/.nuxt/ui/dropdown-menu.ts +57 -36
  22. package/.nuxt/ui/form-field.ts +29 -8
  23. package/.nuxt/ui/form.ts +23 -2
  24. package/.nuxt/ui/index.ts +1 -0
  25. package/.nuxt/ui/input-menu.ts +65 -44
  26. package/.nuxt/ui/input-number.ts +43 -22
  27. package/.nuxt/ui/input.ts +46 -25
  28. package/.nuxt/ui/kbd.ts +27 -6
  29. package/.nuxt/ui/link.ts +26 -5
  30. package/.nuxt/ui/modal.ts +28 -7
  31. package/.nuxt/ui/navigation-menu.ts +103 -84
  32. package/.nuxt/ui/pagination.ts +23 -2
  33. package/.nuxt/ui/pin-input.ts +43 -22
  34. package/.nuxt/ui/popover.ts +25 -4
  35. package/.nuxt/ui/progress.ts +40 -19
  36. package/.nuxt/ui/radio-group.ts +273 -37
  37. package/.nuxt/ui/select-menu.ts +59 -35
  38. package/.nuxt/ui/select.ts +55 -34
  39. package/.nuxt/ui/separator.ts +31 -10
  40. package/.nuxt/ui/skeleton.ts +24 -3
  41. package/.nuxt/ui/slideover.ts +27 -6
  42. package/.nuxt/ui/slider.ts +39 -18
  43. package/.nuxt/ui/stepper.ts +41 -20
  44. package/.nuxt/ui/switch.ts +44 -23
  45. package/.nuxt/ui/table.ts +38 -17
  46. package/.nuxt/ui/tabs.ts +55 -34
  47. package/.nuxt/ui/textarea.ts +63 -37
  48. package/.nuxt/ui/toast.ts +47 -26
  49. package/.nuxt/ui/toaster.ts +23 -2
  50. package/.nuxt/ui/tooltip.ts +25 -4
  51. package/.nuxt/ui/tree.ts +41 -20
  52. package/.nuxt/ui.css +56 -12
  53. package/README.md +11 -0
  54. package/cli/templates.mjs +18 -17
  55. package/dist/module.d.mts +2 -1
  56. package/dist/module.json +5 -5
  57. package/dist/module.mjs +11 -7
  58. package/dist/runtime/components/Accordion.vue +36 -92
  59. package/dist/runtime/components/Accordion.vue.d.ts +91 -0
  60. package/dist/runtime/components/Alert.vue +37 -100
  61. package/dist/runtime/components/Alert.vue.d.ts +96 -0
  62. package/dist/runtime/components/App.vue +28 -34
  63. package/dist/runtime/components/App.vue.d.ts +24 -0
  64. package/dist/runtime/components/Avatar.vue +47 -78
  65. package/dist/runtime/components/Avatar.vue.d.ts +60 -0
  66. package/dist/runtime/components/AvatarGroup.vue +40 -77
  67. package/dist/runtime/components/AvatarGroup.vue.d.ts +40 -0
  68. package/dist/runtime/components/Badge.vue +33 -62
  69. package/dist/runtime/components/Badge.vue.d.ts +62 -0
  70. package/dist/runtime/components/Breadcrumb.vue +36 -88
  71. package/dist/runtime/components/Breadcrumb.vue.d.ts +82 -0
  72. package/dist/runtime/components/Button.vue +96 -105
  73. package/dist/runtime/components/Button.vue.d.ts +73 -0
  74. package/dist/runtime/components/ButtonGroup.vue +20 -46
  75. package/dist/runtime/components/ButtonGroup.vue.d.ts +55 -0
  76. package/dist/runtime/components/Calendar.vue +73 -127
  77. package/dist/runtime/components/Calendar.vue.d.ts +107 -0
  78. package/dist/runtime/components/Card.vue +18 -40
  79. package/dist/runtime/components/Card.vue.d.ts +38 -0
  80. package/dist/runtime/components/Carousel.vue +112 -230
  81. package/dist/runtime/components/Carousel.vue.d.ts +120 -0
  82. package/dist/runtime/components/Checkbox.vue +52 -93
  83. package/dist/runtime/components/Checkbox.vue.d.ts +76 -0
  84. package/dist/runtime/components/CheckboxGroup.vue +121 -0
  85. package/dist/runtime/components/CheckboxGroup.vue.d.ts +75 -0
  86. package/dist/runtime/components/Chip.vue +26 -69
  87. package/dist/runtime/components/Chip.vue.d.ts +71 -0
  88. package/dist/runtime/components/Collapsible.vue +22 -41
  89. package/dist/runtime/components/Collapsible.vue.d.ts +50 -0
  90. package/dist/runtime/components/ColorPicker.vue +111 -197
  91. package/dist/runtime/components/ColorPicker.vue.d.ts +63 -0
  92. package/dist/runtime/components/CommandPalette.vue +94 -234
  93. package/dist/runtime/components/CommandPalette.vue.d.ts +173 -0
  94. package/dist/runtime/components/Container.vue +15 -28
  95. package/dist/runtime/components/Container.vue.d.ts +27 -0
  96. package/dist/runtime/components/ContextMenu.vue +32 -119
  97. package/dist/runtime/components/ContextMenu.vue.d.ts +127 -0
  98. package/dist/runtime/components/ContextMenuContent.vue +71 -72
  99. package/dist/runtime/components/ContextMenuContent.vue.d.ts +105 -0
  100. package/dist/runtime/components/Drawer.vue +49 -87
  101. package/dist/runtime/components/Drawer.vue.d.ts +88 -0
  102. package/dist/runtime/components/DropdownMenu.vue +37 -129
  103. package/dist/runtime/components/DropdownMenu.vue.d.ts +141 -0
  104. package/dist/runtime/components/DropdownMenuContent.vue +76 -79
  105. package/dist/runtime/components/DropdownMenuContent.vue.d.ts +55 -0
  106. package/dist/runtime/components/Form.vue +135 -219
  107. package/dist/runtime/components/Form.vue.d.ts +66 -0
  108. package/dist/runtime/components/FormField.vue +36 -77
  109. package/dist/runtime/components/FormField.vue.d.ts +70 -0
  110. package/dist/runtime/components/Icon.vue +12 -19
  111. package/dist/runtime/components/Icon.vue.d.ts +17 -0
  112. package/dist/runtime/components/Input.vue +82 -140
  113. package/dist/runtime/components/Input.vue.d.ts +91 -0
  114. package/dist/runtime/components/InputMenu.vue +193 -302
  115. package/dist/runtime/components/InputMenu.vue.d.ts +312 -0
  116. package/dist/runtime/components/InputNumber.vue +71 -133
  117. package/dist/runtime/components/InputNumber.vue.d.ts +193 -0
  118. package/dist/runtime/components/Kbd.vue +20 -46
  119. package/dist/runtime/components/Kbd.vue.d.ts +55 -0
  120. package/dist/runtime/components/Link.vue +98 -173
  121. package/dist/runtime/components/Link.vue.d.ts +124 -0
  122. package/dist/runtime/components/LinkBase.vue +33 -41
  123. package/dist/runtime/components/LinkBase.vue.d.ts +48 -0
  124. package/dist/runtime/components/Modal.vue +53 -116
  125. package/dist/runtime/components/Modal.vue.d.ts +109 -0
  126. package/dist/runtime/components/NavigationMenu.vue +78 -186
  127. package/dist/runtime/components/NavigationMenu.vue.d.ts +159 -0
  128. package/dist/runtime/components/OverlayProvider.vue +14 -18
  129. package/dist/runtime/components/OverlayProvider.vue.d.ts +2 -0
  130. package/dist/runtime/components/Pagination.vue +46 -140
  131. package/dist/runtime/components/Pagination.vue.d.ts +143 -0
  132. package/dist/runtime/components/PinInput.vue +61 -78
  133. package/dist/runtime/components/PinInput.vue.d.ts +68 -0
  134. package/dist/runtime/components/Popover.vue +47 -84
  135. package/dist/runtime/components/Popover.vue.d.ts +80 -0
  136. package/dist/runtime/components/Progress.vue +70 -131
  137. package/dist/runtime/components/Progress.vue.d.ts +97 -0
  138. package/dist/runtime/components/RadioGroup.vue +75 -126
  139. package/dist/runtime/components/RadioGroup.vue.d.ts +88 -0
  140. package/dist/runtime/components/Select.vue +115 -202
  141. package/dist/runtime/components/Select.vue.d.ts +139 -0
  142. package/dist/runtime/components/SelectMenu.vue +167 -281
  143. package/dist/runtime/components/SelectMenu.vue.d.ts +184 -0
  144. package/dist/runtime/components/Separator.vue +29 -71
  145. package/dist/runtime/components/Separator.vue.d.ts +75 -0
  146. package/dist/runtime/components/Skeleton.vue +21 -23
  147. package/dist/runtime/components/Skeleton.vue.d.ts +26 -0
  148. package/dist/runtime/components/Slideover.vue +53 -120
  149. package/dist/runtime/components/Slideover.vue.d.ts +111 -0
  150. package/dist/runtime/components/Slider.vue +46 -84
  151. package/dist/runtime/components/Slider.vue.d.ts +71 -0
  152. package/dist/runtime/components/Stepper.vue +44 -113
  153. package/dist/runtime/components/Stepper.vue.d.ts +89 -0
  154. package/dist/runtime/components/Switch.vue +45 -93
  155. package/dist/runtime/components/Switch.vue.d.ts +73 -0
  156. package/dist/runtime/components/Table.vue +165 -264
  157. package/dist/runtime/components/Table.vue.d.ts +183 -0
  158. package/dist/runtime/components/Tabs.vue +40 -105
  159. package/dist/runtime/components/Tabs.vue.d.ts +104 -0
  160. package/dist/runtime/components/Textarea.vue +113 -158
  161. package/dist/runtime/components/Textarea.vue.d.ts +95 -0
  162. package/dist/runtime/components/Toast.vue +49 -110
  163. package/dist/runtime/components/Toast.vue.d.ts +95 -0
  164. package/dist/runtime/components/Toaster.vue +70 -107
  165. package/dist/runtime/components/Toaster.vue.d.ts +63 -0
  166. package/dist/runtime/components/Tooltip.vue +38 -65
  167. package/dist/runtime/components/Tooltip.vue.d.ts +65 -0
  168. package/dist/runtime/components/Tree.vue +57 -150
  169. package/dist/runtime/components/Tree.vue.d.ts +128 -0
  170. package/dist/runtime/composables/defineShortcuts.d.ts +1 -1
  171. package/dist/runtime/composables/defineShortcuts.js +0 -1
  172. package/dist/runtime/composables/useAvatarGroup.d.ts +1 -1
  173. package/dist/runtime/composables/useButtonGroup.d.ts +2 -2
  174. package/dist/runtime/composables/useFormField.d.ts +2 -2
  175. package/dist/runtime/composables/useFormField.js +1 -1
  176. package/dist/runtime/composables/useOverlay.d.ts +16 -8
  177. package/dist/runtime/composables/useOverlay.js +14 -6
  178. package/dist/runtime/composables/usePortal.d.ts +6 -0
  179. package/dist/runtime/composables/usePortal.js +17 -0
  180. package/dist/runtime/index.css +1 -1
  181. package/dist/runtime/inertia/components/Link.vue +146 -0
  182. package/dist/runtime/inertia/components/Link.vue.d.ts +92 -0
  183. package/dist/runtime/inertia/stubs.d.ts +43 -0
  184. package/dist/runtime/inertia/stubs.js +82 -0
  185. package/dist/runtime/locale/bg.d.ts +3 -0
  186. package/dist/runtime/locale/bg.js +54 -0
  187. package/dist/runtime/locale/hy.d.ts +3 -0
  188. package/dist/runtime/locale/hy.js +54 -0
  189. package/dist/runtime/locale/index.d.ts +6 -1
  190. package/dist/runtime/locale/index.js +6 -1
  191. package/dist/runtime/locale/kk.d.ts +3 -0
  192. package/dist/runtime/locale/kk.js +54 -0
  193. package/dist/runtime/locale/tj.d.ts +3 -0
  194. package/dist/runtime/locale/tj.js +54 -0
  195. package/dist/runtime/locale/ug_cn.d.ts +3 -0
  196. package/dist/runtime/locale/ug_cn.js +55 -0
  197. package/dist/runtime/types/form.d.ts +5 -1
  198. package/dist/runtime/types/index.d.ts +2 -0
  199. package/dist/runtime/types/index.js +2 -0
  200. package/dist/runtime/types/utils.d.ts +70 -10
  201. package/dist/runtime/utils/index.d.ts +1 -0
  202. package/dist/runtime/utils/index.js +3 -0
  203. package/dist/runtime/utils/link.d.ts +4 -27
  204. package/dist/runtime/utils/link.js +32 -1
  205. package/dist/runtime/utils/tv.js +2 -2
  206. package/dist/runtime/vue/components/Icon.vue +7 -8
  207. package/dist/runtime/vue/components/Icon.vue.d.ts +14 -0
  208. package/dist/runtime/vue/components/Link.vue +118 -199
  209. package/dist/runtime/vue/components/Link.vue.d.ts +124 -0
  210. package/dist/runtime/vue/stubs.d.ts +5 -0
  211. package/dist/runtime/vue/stubs.js +11 -2
  212. package/dist/shared/{ui.BCaWD5Ek.mjs → ui.D7lOOabO.mjs} +623 -364
  213. package/dist/types.d.mts +5 -1
  214. package/dist/unplugin.d.mts +6 -1
  215. package/dist/unplugin.mjs +25 -4
  216. package/dist/vite.mjs +1 -1
  217. package/package.json +122 -64
  218. package/dist/module.cjs +0 -62
  219. package/dist/module.d.cts +0 -44
  220. package/dist/module.d.ts +0 -44
  221. package/dist/shared/ui.C6uTSFTI.cjs +0 -3555
  222. package/dist/types.d.ts +0 -1
  223. package/dist/unplugin.cjs +0 -232
  224. package/dist/unplugin.d.cts +0 -61
  225. package/dist/unplugin.d.ts +0 -61
  226. package/dist/vite.cjs +0 -23
  227. package/dist/vite.d.cts +0 -15
  228. package/dist/vite.d.ts +0 -15
@@ -1,196 +1,82 @@
1
- <script lang="ts">
2
- import type { VariantProps } from 'tailwind-variants'
3
- import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxContentEmits, ComboboxArrowProps, AcceptableValue } from 'reka-ui'
4
- import type { AppConfig } from '@nuxt/schema'
5
- import _appConfig from '#build/app.config'
6
- import theme from '#build/ui/select-menu'
7
- import type { UseComponentIconsProps } from '../composables/useComponentIcons'
8
- import { tv } from '../utils/tv'
9
- import type { AvatarProps, ChipProps, InputProps } from '../types'
10
- import type { PartialString, MaybeArrayOfArray, MaybeArrayOfArrayItem, SelectModelValue, SelectModelValueEmits, SelectItemKey, EmitsToProps } from '../types/utils'
11
-
12
- const appConfigSelectMenu = _appConfig as AppConfig & { ui: { selectMenu: Partial<typeof theme> } }
13
-
14
- const selectMenu = tv({ extend: tv(theme), ...(appConfigSelectMenu.ui?.selectMenu || {}) })
15
-
16
- export interface SelectMenuItem {
17
- label?: string
18
- /**
19
- * @IconifyIcon
20
- */
21
- icon?: string
22
- avatar?: AvatarProps
23
- chip?: ChipProps
24
- /**
25
- * The item type.
26
- * @defaultValue 'item'
27
- */
28
- type?: 'label' | 'separator' | 'item'
29
- disabled?: boolean
30
- onSelect?(e?: Event): void
31
- }
32
-
33
- type SelectMenuVariants = VariantProps<typeof selectMenu>
34
-
35
- export interface SelectMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<SelectMenuItem | AcceptableValue | boolean> = MaybeArrayOfArray<SelectMenuItem | AcceptableValue | boolean>, V extends SelectItemKey<T> | undefined = undefined, M extends boolean = false> extends Pick<ComboboxRootProps<T>, 'open' | 'defaultOpen' | 'disabled' | 'name' | 'resetSearchTermOnBlur' | 'highlightOnHover'>, UseComponentIconsProps {
36
- id?: string
37
- /** The placeholder text when the select is empty. */
38
- placeholder?: string
39
- /**
40
- * Whether to display the search input or not.
41
- * Can be an object to pass additional props to the input.
42
- * `{ placeholder: 'Search...', variant: 'none' }`{lang="ts-type"}
43
- * @defaultValue true
44
- */
45
- searchInput?: boolean | InputProps
46
- /**
47
- * @defaultValue 'primary'
48
- */
49
- color?: SelectMenuVariants['color']
50
- /**
51
- * @defaultValue 'outline'
52
- */
53
- variant?: SelectMenuVariants['variant']
54
- /**
55
- * @defaultValue 'md'
56
- */
57
- size?: SelectMenuVariants['size']
58
- required?: boolean
59
- /**
60
- * The icon displayed to open the menu.
61
- * @defaultValue appConfig.ui.icons.chevronDown
62
- * @IconifyIcon
63
- */
64
- trailingIcon?: string
65
- /**
66
- * The icon displayed when an item is selected.
67
- * @defaultValue appConfig.ui.icons.check
68
- * @IconifyIcon
69
- */
70
- selectedIcon?: string
71
- /**
72
- * The content of the menu.
73
- * @defaultValue { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }
74
- */
75
- content?: Omit<ComboboxContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<ComboboxContentEmits>>
76
- /**
77
- * Display an arrow alongside the menu.
78
- * @defaultValue false
79
- * @IconifyIcon
80
- */
81
- arrow?: boolean | Omit<ComboboxArrowProps, 'as' | 'asChild'>
82
- /**
83
- * Render the menu in a portal.
84
- * @defaultValue true
85
- */
86
- portal?: boolean
87
- /**
88
- * When `items` is an array of objects, select the field to use as the value instead of the object itself.
89
- * @defaultValue undefined
90
- */
91
- valueKey?: V
92
- /**
93
- * When `items` is an array of objects, select the field to use as the label.
94
- * @defaultValue 'label'
95
- */
96
- labelKey?: V
97
- items?: I
98
- /** The value of the SelectMenu when initially rendered. Use when you do not need to control the state of the SelectMenu. */
99
- defaultValue?: SelectModelValue<T, V, M>
100
- /** The controlled value of the SelectMenu. Can be binded-with with `v-model`. */
101
- modelValue?: SelectModelValue<T, V, M>
102
- /** Whether multiple options can be selected or not. */
103
- multiple?: M & boolean
104
- /** Highlight the ring color like a focus state. */
105
- highlight?: boolean
106
- /**
107
- * Determines if custom user input that does not exist in options can be added.
108
- * @defaultValue false
109
- */
110
- createItem?: boolean | 'always' | { position?: 'top' | 'bottom', when?: 'empty' | 'always' }
111
- /**
112
- * Fields to filter items by.
113
- * @defaultValue [labelKey]
114
- */
115
- filterFields?: string[]
116
- /**
117
- * When `true`, disable the default filters, useful for custom filtering (useAsyncData, useFetch, etc.).
118
- * @defaultValue false
119
- */
120
- ignoreFilter?: boolean
121
- class?: any
122
- ui?: PartialString<typeof selectMenu.slots>
123
- }
124
-
125
- export type SelectMenuEmits<T, V, M extends boolean> = Omit<ComboboxRootEmits<T>, 'update:modelValue'> & {
126
- change: [payload: Event]
127
- blur: [payload: FocusEvent]
128
- focus: [payload: FocusEvent]
129
- create: [item: string]
130
- } & SelectModelValueEmits<T, V, M>
131
-
132
- type SlotProps<T> = (props: { item: T, index: number }) => any
133
-
134
- export interface SelectMenuSlots<T, M extends boolean> {
135
- 'leading'(props: { modelValue?: M extends true ? T[] : T, open: boolean, ui: any }): any
136
- 'default'(props: { modelValue?: M extends true ? T[] : T, open: boolean }): any
137
- 'trailing'(props: { modelValue?: M extends true ? T[] : T, open: boolean, ui: any }): any
138
- 'empty'(props: { searchTerm?: string }): any
139
- 'item': SlotProps<T>
140
- 'item-leading': SlotProps<T>
141
- 'item-label': SlotProps<T>
142
- 'item-trailing': SlotProps<T>
143
- 'create-item-label'(props: { item: string }): any
144
- }
1
+ <script>
2
+ import theme from "#build/ui/select-menu";
145
3
  </script>
146
4
 
147
- <script setup lang="ts" generic="T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<SelectMenuItem | AcceptableValue | boolean> = MaybeArrayOfArray<SelectMenuItem | AcceptableValue | boolean>, V extends SelectItemKey<T> | undefined = undefined, M extends boolean = false">
148
- import { computed, toRef, toRaw } from 'vue'
149
- import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxPortal, ComboboxContent, ComboboxViewport, ComboboxEmpty, ComboboxGroup, ComboboxLabel, ComboboxSeparator, ComboboxItem, ComboboxItemIndicator, FocusScope, useForwardPropsEmits, useFilter } from 'reka-ui'
150
- import { defu } from 'defu'
151
- import { reactivePick, createReusableTemplate } from '@vueuse/core'
152
- import { useAppConfig } from '#imports'
153
- import { useButtonGroup } from '../composables/useButtonGroup'
154
- import { useComponentIcons } from '../composables/useComponentIcons'
155
- import { useFormField } from '../composables/useFormField'
156
- import { useLocale } from '../composables/useLocale'
157
- import { get, compare } from '../utils'
158
- import UIcon from './Icon.vue'
159
- import UAvatar from './Avatar.vue'
160
- import UChip from './Chip.vue'
161
- import UInput from './Input.vue'
162
-
163
- defineOptions({ inheritAttrs: false })
164
-
165
- const props = withDefaults(defineProps<SelectMenuProps<T, I, V, M>>(), {
166
- portal: true,
167
- searchInput: true,
168
- labelKey: 'label' as never,
169
- resetSearchTermOnBlur: true
170
- })
171
- const emits = defineEmits<SelectMenuEmits<T, V, M>>()
172
- const slots = defineSlots<SelectMenuSlots<T, M>>()
173
-
174
- const searchTerm = defineModel<string>('searchTerm', { default: '' })
175
-
176
- const { t } = useLocale()
177
- const appConfig = useAppConfig()
178
- const { contains } = useFilter({ sensitivity: 'base' })
179
-
180
- const rootProps = useForwardPropsEmits(reactivePick(props, 'modelValue', 'defaultValue', 'open', 'defaultOpen', 'required', 'multiple', 'resetSearchTermOnBlur', 'highlightOnHover'), emits)
181
- const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }) as ComboboxContentProps)
182
- const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)
183
- const searchInputProps = toRef(() => defu(props.searchInput, { placeholder: t('selectMenu.search'), variant: 'none' }) as InputProps)
184
-
185
- const { emitFormBlur, emitFormFocus, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField<InputProps>(props)
186
- const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
187
- const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: appConfig.ui.icons.chevronDown })))
188
-
189
- const selectSize = computed(() => buttonGroupSize.value || formGroupSize.value)
190
-
191
- const [DefineCreateItemTemplate, ReuseCreateItemTemplate] = createReusableTemplate()
192
-
193
- const ui = computed(() => selectMenu({
5
+ <script setup>
6
+ import { computed, toRef, toRaw } from "vue";
7
+ import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxPortal, ComboboxContent, ComboboxViewport, ComboboxEmpty, ComboboxGroup, ComboboxLabel, ComboboxSeparator, ComboboxItem, ComboboxItemIndicator, FocusScope, useForwardPropsEmits, useFilter } from "reka-ui";
8
+ import { defu } from "defu";
9
+ import { reactivePick, createReusableTemplate } from "@vueuse/core";
10
+ import { useAppConfig } from "#imports";
11
+ import { useButtonGroup } from "../composables/useButtonGroup";
12
+ import { useComponentIcons } from "../composables/useComponentIcons";
13
+ import { useFormField } from "../composables/useFormField";
14
+ import { useLocale } from "../composables/useLocale";
15
+ import { usePortal } from "../composables/usePortal";
16
+ import { compare, get, isArrayOfArray } from "../utils";
17
+ import { tv } from "../utils/tv";
18
+ import UIcon from "./Icon.vue";
19
+ import UAvatar from "./Avatar.vue";
20
+ import UChip from "./Chip.vue";
21
+ import UInput from "./Input.vue";
22
+ defineOptions({ inheritAttrs: false });
23
+ const props = defineProps({
24
+ id: { type: String, required: false },
25
+ placeholder: { type: String, required: false },
26
+ searchInput: { type: [Boolean, Object], required: false, default: true },
27
+ color: { type: null, required: false },
28
+ variant: { type: null, required: false },
29
+ size: { type: null, required: false },
30
+ required: { type: Boolean, required: false },
31
+ trailingIcon: { type: String, required: false },
32
+ selectedIcon: { type: String, required: false },
33
+ content: { type: Object, required: false },
34
+ arrow: { type: [Boolean, Object], required: false },
35
+ portal: { type: [Boolean, String], required: false, skipCheck: true, default: true },
36
+ valueKey: { type: null, required: false },
37
+ labelKey: { type: null, required: false, default: "label" },
38
+ items: { type: null, required: false },
39
+ defaultValue: { type: null, required: false },
40
+ modelValue: { type: null, required: false },
41
+ multiple: { type: Boolean, required: false },
42
+ highlight: { type: Boolean, required: false },
43
+ createItem: { type: [Boolean, String, Object], required: false },
44
+ filterFields: { type: Array, required: false },
45
+ ignoreFilter: { type: Boolean, required: false },
46
+ class: { type: null, required: false },
47
+ ui: { type: null, required: false },
48
+ open: { type: Boolean, required: false },
49
+ defaultOpen: { type: Boolean, required: false },
50
+ disabled: { type: Boolean, required: false },
51
+ name: { type: String, required: false },
52
+ resetSearchTermOnBlur: { type: Boolean, required: false, default: true },
53
+ resetSearchTermOnSelect: { type: Boolean, required: false, default: true },
54
+ highlightOnHover: { type: Boolean, required: false },
55
+ icon: { type: String, required: false },
56
+ avatar: { type: Object, required: false },
57
+ leading: { type: Boolean, required: false },
58
+ leadingIcon: { type: String, required: false },
59
+ trailing: { type: Boolean, required: false },
60
+ loading: { type: Boolean, required: false },
61
+ loadingIcon: { type: String, required: false }
62
+ });
63
+ const emits = defineEmits(["update:open", "change", "blur", "focus", "create", "highlight", "update:modelValue"]);
64
+ const slots = defineSlots();
65
+ const searchTerm = defineModel("searchTerm", { type: String, ...{ default: "" } });
66
+ const { t } = useLocale();
67
+ const appConfig = useAppConfig();
68
+ const { contains } = useFilter({ sensitivity: "base" });
69
+ const rootProps = useForwardPropsEmits(reactivePick(props, "modelValue", "defaultValue", "open", "defaultOpen", "required", "multiple", "resetSearchTermOnBlur", "resetSearchTermOnSelect", "highlightOnHover"), emits);
70
+ const portalProps = usePortal(toRef(() => props.portal));
71
+ const contentProps = toRef(() => defu(props.content, { side: "bottom", sideOffset: 8, collisionPadding: 8, position: "popper" }));
72
+ const arrowProps = toRef(() => props.arrow);
73
+ const searchInputProps = toRef(() => defu(props.searchInput, { placeholder: t("selectMenu.search"), variant: "none" }));
74
+ const { emitFormBlur, emitFormFocus, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField(props);
75
+ const { orientation, size: buttonGroupSize } = useButtonGroup(props);
76
+ const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: appConfig.ui.icons.chevronDown })));
77
+ const selectSize = computed(() => buttonGroupSize.value || formGroupSize.value);
78
+ const [DefineCreateItemTemplate, ReuseCreateItemTemplate] = createReusableTemplate();
79
+ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.ui?.selectMenu || {} })({
194
80
  color: color.value,
195
81
  variant: props.variant,
196
82
  size: selectSize?.value,
@@ -199,100 +85,96 @@ const ui = computed(() => selectMenu({
199
85
  leading: isLeading.value || !!props.avatar || !!slots.leading,
200
86
  trailing: isTrailing.value || !!slots.trailing,
201
87
  buttonGroup: orientation.value
202
- }))
203
-
204
- function displayValue(value: T | T[]): string {
88
+ }));
89
+ function displayValue(value) {
205
90
  if (props.multiple && Array.isArray(value)) {
206
- return value.map(v => displayValue(v)).filter(Boolean).join(', ')
91
+ return value.map((v) => displayValue(v)).filter(Boolean).join(", ");
207
92
  }
208
-
209
93
  if (!props.valueKey) {
210
- return value && (typeof value === 'object' ? get(value, props.labelKey as string) : value)
94
+ return value && (typeof value === "object" ? get(value, props.labelKey) : value);
211
95
  }
212
-
213
- const item = items.value.find(item => compare(typeof item === 'object' ? get(item as Record<string, any>, props.valueKey as string) : item, value))
214
- return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
96
+ const item = items.value.find((item2) => compare(typeof item2 === "object" ? get(item2, props.valueKey) : item2, value));
97
+ return item && (typeof item === "object" ? get(item, props.labelKey) : item);
215
98
  }
216
-
217
- const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0]) ? props.items : [props.items]) as SelectMenuItem[][] : [])
218
- // eslint-disable-next-line vue/no-dupe-keys
219
- const items = computed(() => groups.value.flatMap(group => group) as T[])
220
-
99
+ const groups = computed(
100
+ () => props.items?.length ? isArrayOfArray(props.items) ? props.items : [props.items] : []
101
+ );
102
+ const items = computed(() => groups.value.flatMap((group) => group));
221
103
  const filteredGroups = computed(() => {
222
104
  if (props.ignoreFilter || !searchTerm.value) {
223
- return groups.value
105
+ return groups.value;
224
106
  }
225
-
226
- const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey] as string[]
227
-
228
- return groups.value.map(items => items.filter((item) => {
229
- if (typeof item !== 'object') {
230
- return contains(item, searchTerm.value)
107
+ const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey];
108
+ return groups.value.map((items2) => items2.filter((item) => {
109
+ if (typeof item !== "object" || item === null) {
110
+ return contains(String(item), searchTerm.value);
231
111
  }
232
-
233
- if (item.type && ['label', 'separator'].includes(item.type)) {
234
- return true
112
+ if (item.type && ["label", "separator"].includes(item.type)) {
113
+ return true;
235
114
  }
236
-
237
- return fields.some(field => contains(get(item, field), searchTerm.value))
238
- })).filter(group => group.filter(item => !item.type || !['label', 'separator'].includes(item.type)).length > 0)
239
- })
240
- const filteredItems = computed(() => filteredGroups.value.flatMap(group => group) as T[])
241
-
115
+ return fields.some((field) => contains(get(item, field), searchTerm.value));
116
+ })).filter((group) => group.filter(
117
+ (item) => !isSelectItem(item) || (!item.type || !["label", "separator"].includes(item.type))
118
+ ).length > 0);
119
+ });
120
+ const filteredItems = computed(() => filteredGroups.value.flatMap((group) => group));
242
121
  const createItem = computed(() => {
243
122
  if (!props.createItem || !searchTerm.value) {
244
- return false
123
+ return false;
245
124
  }
246
-
247
- const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } as T : searchTerm.value
248
-
249
- if ((typeof props.createItem === 'object' && props.createItem.when === 'always') || props.createItem === 'always') {
250
- return !filteredItems.value.find(item => compare(item, newItem, props.valueKey))
125
+ const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } : searchTerm.value;
126
+ if (typeof props.createItem === "object" && props.createItem.when === "always" || props.createItem === "always") {
127
+ return !filteredItems.value.find((item) => compare(item, newItem, props.valueKey));
251
128
  }
252
-
253
- return !filteredItems.value.length
254
- })
255
- const createItemPosition = computed(() => typeof props.createItem === 'object' ? props.createItem.position : 'bottom')
256
-
257
- function onUpdate(value: any) {
129
+ return !filteredItems.value.length;
130
+ });
131
+ const createItemPosition = computed(() => typeof props.createItem === "object" ? props.createItem.position : "bottom");
132
+ function onUpdate(value) {
258
133
  if (toRaw(props.modelValue) === value) {
259
- return
134
+ return;
135
+ }
136
+ const event = new Event("change", { target: { value } });
137
+ emits("change", event);
138
+ emitFormChange();
139
+ emitFormInput();
140
+ if (props.resetSearchTermOnSelect) {
141
+ searchTerm.value = "";
260
142
  }
261
- // @ts-expect-error - 'target' does not exist in type 'EventInit'
262
- const event = new Event('change', { target: { value } })
263
- emits('change', event)
264
- emitFormChange()
265
- emitFormInput()
266
143
  }
267
-
268
- function onUpdateOpen(value: boolean) {
269
- let timeoutId
270
-
144
+ function onUpdateOpen(value) {
145
+ let timeoutId;
271
146
  if (!value) {
272
- const event = new FocusEvent('blur')
273
-
274
- emits('blur', event)
275
- emitFormBlur()
276
-
277
- // Since we use `displayValue` prop inside ComboboxInput we should reset searchTerm manually
278
- // https://reka-ui.com/docs/components/combobox#api-reference
147
+ const event = new FocusEvent("blur");
148
+ emits("blur", event);
149
+ emitFormBlur();
279
150
  if (props.resetSearchTermOnBlur) {
280
- const STATE_ANIMATION_DELAY_MS = 100
281
-
151
+ const STATE_ANIMATION_DELAY_MS = 100;
282
152
  timeoutId = setTimeout(() => {
283
- searchTerm.value = ''
284
- }, STATE_ANIMATION_DELAY_MS)
153
+ searchTerm.value = "";
154
+ }, STATE_ANIMATION_DELAY_MS);
285
155
  }
286
156
  } else {
287
- const event = new FocusEvent('focus')
288
- emits('focus', event)
289
- emitFormFocus()
290
- clearTimeout(timeoutId)
157
+ const event = new FocusEvent("focus");
158
+ emits("focus", event);
159
+ emitFormFocus();
160
+ clearTimeout(timeoutId);
291
161
  }
292
162
  }
163
+ function onSelect(e, item) {
164
+ if (!isSelectItem(item)) {
165
+ return;
166
+ }
167
+ if (item.disabled) {
168
+ e.preventDefault();
169
+ return;
170
+ }
171
+ item.onSelect?.(e);
172
+ }
173
+ function isSelectItem(item) {
174
+ return typeof item === "object" && item !== null;
175
+ }
293
176
  </script>
294
177
 
295
- <!-- eslint-disable vue/no-template-shadow -->
296
178
  <template>
297
179
  <DefineCreateItemTemplate>
298
180
  <ComboboxGroup :class="ui.group({ class: props.ui?.group })">
@@ -324,34 +206,36 @@ function onUpdateOpen(value: boolean) {
324
206
  <ComboboxAnchor as-child>
325
207
  <ComboboxTrigger :class="ui.base({ class: [props.class, props.ui?.base] })" tabindex="0">
326
208
  <span v-if="isLeading || !!avatar || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
327
- <slot name="leading" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :ui="ui">
209
+ <slot name="leading" :model-value="modelValue" :open="open" :ui="ui">
328
210
  <UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
329
- <UAvatar v-else-if="!!avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
211
+ <UAvatar v-else-if="!!avatar" :size="props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
330
212
  </slot>
331
213
  </span>
332
214
 
333
- <slot :model-value="(modelValue as M extends true ? T[] : T)" :open="open">
334
- <template v-for="displayedModelValue in [displayValue(modelValue as M extends true ? T[] : T)]" :key="displayedModelValue">
215
+ <slot :model-value="modelValue" :open="open">
216
+ <template v-for="displayedModelValue in [displayValue(modelValue)]" :key="displayedModelValue">
335
217
  <span v-if="displayedModelValue" :class="ui.value({ class: props.ui?.value })">
336
218
  {{ displayedModelValue }}
337
219
  </span>
338
220
  <span v-else :class="ui.placeholder({ class: props.ui?.placeholder })">
339
- {{ placeholder ?? '&nbsp;' }}
221
+ {{ placeholder ?? '\xA0' }}
340
222
  </span>
341
223
  </template>
342
224
  </slot>
343
225
 
344
226
  <span v-if="isTrailing || !!slots.trailing" :class="ui.trailing({ class: props.ui?.trailing })">
345
- <slot name="trailing" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :ui="ui">
227
+ <slot name="trailing" :model-value="modelValue" :open="open" :ui="ui">
346
228
  <UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
347
229
  </slot>
348
230
  </span>
349
231
  </ComboboxTrigger>
350
232
  </ComboboxAnchor>
351
233
 
352
- <ComboboxPortal :disabled="!portal">
234
+ <ComboboxPortal v-bind="portalProps">
353
235
  <ComboboxContent :class="ui.content({ class: props.ui?.content })" v-bind="contentProps">
354
236
  <FocusScope trapped :class="ui.focusScope({ class: props.ui?.focusScope })">
237
+ <slot name="content-top" />
238
+
355
239
  <ComboboxInput v-if="!!searchInput" v-model="searchTerm" :display-value="() => searchTerm" as-child>
356
240
  <UInput autofocus autocomplete="off" v-bind="searchInputProps" :class="ui.input({ class: props.ui?.input })" />
357
241
  </ComboboxInput>
@@ -367,26 +251,26 @@ function onUpdateOpen(value: boolean) {
367
251
 
368
252
  <ComboboxGroup v-for="(group, groupIndex) in filteredGroups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
369
253
  <template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
370
- <ComboboxLabel v-if="item?.type === 'label'" :class="ui.label({ class: props.ui?.label })">
371
- {{ get(item, props.labelKey as string) }}
254
+ <ComboboxLabel v-if="isSelectItem(item) && item.type === 'label'" :class="ui.label({ class: props.ui?.label })">
255
+ {{ get(item, props.labelKey) }}
372
256
  </ComboboxLabel>
373
257
 
374
- <ComboboxSeparator v-else-if="item?.type === 'separator'" :class="ui.separator({ class: props.ui?.separator })" />
258
+ <ComboboxSeparator v-else-if="isSelectItem(item) && item.type === 'separator'" :class="ui.separator({ class: props.ui?.separator })" />
375
259
 
376
260
  <ComboboxItem
377
261
  v-else
378
262
  :class="ui.item({ class: props.ui?.item })"
379
- :disabled="item.disabled"
380
- :value="valueKey && typeof item === 'object' ? get(item, props.valueKey as string) : item"
381
- @select="item.onSelect"
263
+ :disabled="isSelectItem(item) && item.disabled"
264
+ :value="props.valueKey && isSelectItem(item) ? get(item, props.valueKey) : item"
265
+ @select="onSelect($event, item)"
382
266
  >
383
- <slot name="item" :item="(item as T)" :index="index">
384
- <slot name="item-leading" :item="(item as T)" :index="index">
385
- <UIcon v-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
386
- <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 })" />
267
+ <slot name="item" :item="item" :index="index">
268
+ <slot name="item-leading" :item="item" :index="index">
269
+ <UIcon v-if="isSelectItem(item) && item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
270
+ <UAvatar v-else-if="isSelectItem(item) && item.avatar" :size="props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
387
271
  <UChip
388
- v-else-if="item.chip"
389
- :size="((props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()) as ChipProps['size'])"
272
+ v-else-if="isSelectItem(item) && item.chip"
273
+ :size="props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()"
390
274
  inset
391
275
  standalone
392
276
  v-bind="item.chip"
@@ -395,13 +279,13 @@ function onUpdateOpen(value: boolean) {
395
279
  </slot>
396
280
 
397
281
  <span :class="ui.itemLabel({ class: props.ui?.itemLabel })">
398
- <slot name="item-label" :item="(item as T)" :index="index">
399
- {{ typeof item === 'object' ? get(item, props.labelKey as string) : item }}
282
+ <slot name="item-label" :item="item" :index="index">
283
+ {{ isSelectItem(item) ? get(item, props.labelKey) : item }}
400
284
  </slot>
401
285
  </span>
402
286
 
403
287
  <span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
404
- <slot name="item-trailing" :item="(item as T)" :index="index" />
288
+ <slot name="item-trailing" :item="item" :index="index" />
405
289
 
406
290
  <ComboboxItemIndicator as-child>
407
291
  <UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: props.ui?.itemTrailingIcon })" />
@@ -414,6 +298,8 @@ function onUpdateOpen(value: boolean) {
414
298
 
415
299
  <ReuseCreateItemTemplate v-if="createItem && createItemPosition === 'bottom'" />
416
300
  </ComboboxViewport>
301
+
302
+ <slot name="content-bottom" />
417
303
  </FocusScope>
418
304
 
419
305
  <ComboboxArrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow({ class: props.ui?.arrow })" />