@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,204 +1,85 @@
1
- <script lang="ts">
2
- import type { InputHTMLAttributes } from 'vue'
3
- import type { VariantProps } from 'tailwind-variants'
4
- import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxContentEmits, ComboboxArrowProps, AcceptableValue } from 'reka-ui'
5
- import type { AppConfig } from '@nuxt/schema'
6
- import _appConfig from '#build/app.config'
7
- import theme from '#build/ui/input-menu'
8
- import type { UseComponentIconsProps } from '../composables/useComponentIcons'
9
- import { tv } from '../utils/tv'
10
- import type { AvatarProps, ChipProps, InputProps } from '../types'
11
- import type { PartialString, MaybeArrayOfArray, MaybeArrayOfArrayItem, SelectModelValue, SelectModelValueEmits, SelectItemKey, EmitsToProps } from '../types/utils'
12
-
13
- const appConfigInputMenu = _appConfig as AppConfig & { ui: { inputMenu: Partial<typeof theme> } }
14
-
15
- const inputMenu = tv({ extend: tv(theme), ...(appConfigInputMenu.ui?.inputMenu || {}) })
16
-
17
- export interface InputMenuItem {
18
- label?: string
19
- /**
20
- * @IconifyIcon
21
- */
22
- icon?: string
23
- avatar?: AvatarProps
24
- chip?: ChipProps
25
- /**
26
- * The item type.
27
- * @defaultValue 'item'
28
- */
29
- type?: 'label' | 'separator' | 'item'
30
- disabled?: boolean
31
- onSelect?(e?: Event): void
32
- }
33
-
34
- type InputMenuVariants = VariantProps<typeof inputMenu>
35
-
36
- export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<InputMenuItem | AcceptableValue | boolean> = MaybeArrayOfArray<InputMenuItem | AcceptableValue | boolean>, V extends SelectItemKey<T> | undefined = undefined, M extends boolean = false> extends Pick<ComboboxRootProps<T>, 'open' | 'defaultOpen' | 'disabled' | 'name' | 'resetSearchTermOnBlur' | 'highlightOnHover'>, UseComponentIconsProps {
37
- /**
38
- * The element or component this component should render as.
39
- * @defaultValue 'div'
40
- */
41
- as?: any
42
- id?: string
43
- type?: InputHTMLAttributes['type']
44
- /** The placeholder text when the input is empty. */
45
- placeholder?: string
46
- /**
47
- * @defaultValue 'primary'
48
- */
49
- color?: InputMenuVariants['color']
50
- /**
51
- * @defaultValue 'outline'
52
- */
53
- variant?: InputMenuVariants['variant']
54
- /**
55
- * @defaultValue 'md'
56
- */
57
- size?: InputMenuVariants['size']
58
- required?: boolean
59
- autofocus?: boolean
60
- autofocusDelay?: number
61
- /**
62
- * The icon displayed to open the menu.
63
- * @defaultValue appConfig.ui.icons.chevronDown
64
- * @IconifyIcon
65
- */
66
- trailingIcon?: string
67
- /**
68
- * The icon displayed when an item is selected.
69
- * @defaultValue appConfig.ui.icons.check
70
- * @IconifyIcon
71
- */
72
- selectedIcon?: string
73
- /**
74
- * The icon displayed to delete a tag.
75
- * Works only when `multiple` is `true`.
76
- * @defaultValue appConfig.ui.icons.close
77
- * @IconifyIcon
78
- */
79
- deleteIcon?: string
80
- /**
81
- * The content of the menu.
82
- * @defaultValue { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }
83
- */
84
- content?: Omit<ComboboxContentProps, 'as' | 'asChild' | 'forceMount'> & Partial<EmitsToProps<ComboboxContentEmits>>
85
- /**
86
- * Display an arrow alongside the menu.
87
- * @defaultValue false
88
- */
89
- arrow?: boolean | Omit<ComboboxArrowProps, 'as' | 'asChild'>
90
- /**
91
- * Render the menu in a portal.
92
- * @defaultValue true
93
- */
94
- portal?: boolean
95
- /**
96
- * When `items` is an array of objects, select the field to use as the value instead of the object itself.
97
- * @defaultValue undefined
98
- */
99
- valueKey?: V
100
- /**
101
- * When `items` is an array of objects, select the field to use as the label.
102
- * @defaultValue 'label'
103
- */
104
- labelKey?: V
105
- items?: I
106
- /** The value of the InputMenu when initially rendered. Use when you do not need to control the state of the InputMenu. */
107
- defaultValue?: SelectModelValue<T, V, M>
108
- /** The controlled value of the InputMenu. Can be binded-with with `v-model`. */
109
- modelValue?: SelectModelValue<T, V, M>
110
- /** Whether multiple options can be selected or not. */
111
- multiple?: M & boolean
112
- /** Highlight the ring color like a focus state. */
113
- highlight?: boolean
114
- /**
115
- * Determines if custom user input that does not exist in options can be added.
116
- * @defaultValue false
117
- */
118
- createItem?: boolean | 'always' | { position?: 'top' | 'bottom', when?: 'empty' | 'always' }
119
- /**
120
- * Fields to filter items by.
121
- * @defaultValue [labelKey]
122
- */
123
- filterFields?: string[]
124
- /**
125
- * When `true`, disable the default filters, useful for custom filtering (useAsyncData, useFetch, etc.).
126
- * @defaultValue false
127
- */
128
- ignoreFilter?: boolean
129
- class?: any
130
- ui?: PartialString<typeof inputMenu.slots>
131
- }
132
-
133
- export type InputMenuEmits<T, V, M extends boolean> = Omit<ComboboxRootEmits<T>, 'update:modelValue'> & {
134
- change: [payload: Event]
135
- blur: [payload: FocusEvent]
136
- focus: [payload: FocusEvent]
137
- create: [item: string]
138
- } & SelectModelValueEmits<T, V, M>
139
-
140
- type SlotProps<T> = (props: { item: T, index: number }) => any
141
-
142
- export interface InputMenuSlots<T, M extends boolean> {
143
- 'leading'(props: { modelValue?: M extends true ? T[] : T, open: boolean, ui: any }): any
144
- 'trailing'(props: { modelValue?: M extends true ? T[] : T, open: boolean, ui: any }): any
145
- 'empty'(props: { searchTerm?: string }): any
146
- 'item': SlotProps<T>
147
- 'item-leading': SlotProps<T>
148
- 'item-label': SlotProps<T>
149
- 'item-trailing': SlotProps<T>
150
- 'tags-item-text': SlotProps<T>
151
- 'tags-item-delete': SlotProps<T>
152
- 'create-item-label'(props: { item: string }): any
153
- }
1
+ <script>
2
+ import theme from "#build/ui/input-menu";
154
3
  </script>
155
4
 
156
- <script setup lang="ts" generic="T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<InputMenuItem | AcceptableValue | boolean> = MaybeArrayOfArray<InputMenuItem | AcceptableValue | boolean>, V extends SelectItemKey<T> | undefined = undefined, M extends boolean = false">
157
- import { computed, ref, toRef, onMounted, toRaw } from 'vue'
158
- import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxPortal, ComboboxContent, ComboboxViewport, ComboboxEmpty, ComboboxGroup, ComboboxLabel, ComboboxSeparator, ComboboxItem, ComboboxItemIndicator, TagsInputRoot, TagsInputItem, TagsInputItemText, TagsInputItemDelete, TagsInputInput, useForwardPropsEmits, useFilter } from 'reka-ui'
159
- import { defu } from 'defu'
160
- import { isEqual } from 'ohash/utils'
161
- import { reactivePick, createReusableTemplate } from '@vueuse/core'
162
- import { useAppConfig } from '#imports'
163
- import { useButtonGroup } from '../composables/useButtonGroup'
164
- import { useComponentIcons } from '../composables/useComponentIcons'
165
- import { useFormField } from '../composables/useFormField'
166
- import { useLocale } from '../composables/useLocale'
167
- import { get, compare } from '../utils'
168
- import UIcon from './Icon.vue'
169
- import UAvatar from './Avatar.vue'
170
- import UChip from './Chip.vue'
171
-
172
- defineOptions({ inheritAttrs: false })
173
-
174
- const props = withDefaults(defineProps<InputMenuProps<T, I, V, M>>(), {
175
- type: 'text',
176
- autofocusDelay: 0,
177
- portal: true,
178
- labelKey: 'label' as never
179
- })
180
- const emits = defineEmits<InputMenuEmits<T, V, M>>()
181
- const slots = defineSlots<InputMenuSlots<T, M>>()
182
-
183
- const searchTerm = defineModel<string>('searchTerm', { default: '' })
184
-
185
- const { t } = useLocale()
186
- const appConfig = useAppConfig()
187
- const { contains } = useFilter({ sensitivity: 'base' })
188
-
189
- const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'open', 'defaultOpen', 'required', 'multiple', 'resetSearchTermOnBlur', 'highlightOnHover', 'ignoreFilter'), emits)
190
- const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }) as ComboboxContentProps)
191
- const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)
192
-
193
- const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField<InputProps>(props)
194
- const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
195
- const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: appConfig.ui.icons.chevronDown })))
196
-
197
- const inputSize = computed(() => buttonGroupSize.value || formGroupSize.value)
198
-
199
- const [DefineCreateItemTemplate, ReuseCreateItemTemplate] = createReusableTemplate()
200
-
201
- const ui = computed(() => inputMenu({
5
+ <script setup>
6
+ import { computed, ref, toRef, onMounted, toRaw } from "vue";
7
+ import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxPortal, ComboboxContent, ComboboxViewport, ComboboxEmpty, ComboboxGroup, ComboboxLabel, ComboboxSeparator, ComboboxItem, ComboboxItemIndicator, TagsInputRoot, TagsInputItem, TagsInputItemText, TagsInputItemDelete, TagsInputInput, useForwardPropsEmits, useFilter } from "reka-ui";
8
+ import { defu } from "defu";
9
+ import { isEqual } from "ohash/utils";
10
+ import { reactivePick, createReusableTemplate } from "@vueuse/core";
11
+ import { useAppConfig } from "#imports";
12
+ import { useButtonGroup } from "../composables/useButtonGroup";
13
+ import { useComponentIcons } from "../composables/useComponentIcons";
14
+ import { useFormField } from "../composables/useFormField";
15
+ import { useLocale } from "../composables/useLocale";
16
+ import { usePortal } from "../composables/usePortal";
17
+ import { compare, get, isArrayOfArray } from "../utils";
18
+ import { tv } from "../utils/tv";
19
+ import UIcon from "./Icon.vue";
20
+ import UAvatar from "./Avatar.vue";
21
+ import UChip from "./Chip.vue";
22
+ defineOptions({ inheritAttrs: false });
23
+ const props = defineProps({
24
+ as: { type: null, required: false },
25
+ id: { type: String, required: false },
26
+ type: { type: null, required: false, default: "text" },
27
+ placeholder: { type: String, required: false },
28
+ color: { type: null, required: false },
29
+ variant: { type: null, required: false },
30
+ size: { type: null, required: false },
31
+ required: { type: Boolean, required: false },
32
+ autofocus: { type: Boolean, required: false },
33
+ autofocusDelay: { type: Number, required: false, default: 0 },
34
+ trailingIcon: { type: String, required: false },
35
+ selectedIcon: { type: String, required: false },
36
+ deleteIcon: { type: String, required: false },
37
+ content: { type: Object, required: false },
38
+ arrow: { type: [Boolean, Object], required: false },
39
+ portal: { type: [Boolean, String], required: false, skipCheck: true, default: true },
40
+ valueKey: { type: null, required: false },
41
+ labelKey: { type: null, required: false, default: "label" },
42
+ items: { type: null, required: false },
43
+ defaultValue: { type: null, required: false },
44
+ modelValue: { type: null, required: false },
45
+ multiple: { type: Boolean, required: false },
46
+ highlight: { type: Boolean, required: false },
47
+ createItem: { type: [Boolean, String, Object], required: false },
48
+ filterFields: { type: Array, required: false },
49
+ ignoreFilter: { type: Boolean, required: false },
50
+ class: { type: null, required: false },
51
+ ui: { type: null, required: false },
52
+ open: { type: Boolean, required: false },
53
+ defaultOpen: { type: Boolean, required: false },
54
+ disabled: { type: Boolean, required: false },
55
+ name: { type: String, required: false },
56
+ resetSearchTermOnBlur: { type: Boolean, required: false, default: true },
57
+ resetSearchTermOnSelect: { type: Boolean, required: false, default: true },
58
+ highlightOnHover: { type: Boolean, required: false },
59
+ icon: { type: String, required: false },
60
+ avatar: { type: Object, required: false },
61
+ leading: { type: Boolean, required: false },
62
+ leadingIcon: { type: String, required: false },
63
+ trailing: { type: Boolean, required: false },
64
+ loading: { type: Boolean, required: false },
65
+ loadingIcon: { type: String, required: false }
66
+ });
67
+ const emits = defineEmits(["update:open", "change", "blur", "focus", "create", "highlight", "update:modelValue"]);
68
+ const slots = defineSlots();
69
+ const searchTerm = defineModel("searchTerm", { type: String, ...{ default: "" } });
70
+ const { t } = useLocale();
71
+ const appConfig = useAppConfig();
72
+ const { contains } = useFilter({ sensitivity: "base" });
73
+ const rootProps = useForwardPropsEmits(reactivePick(props, "as", "modelValue", "defaultValue", "open", "defaultOpen", "required", "multiple", "resetSearchTermOnBlur", "resetSearchTermOnSelect", "highlightOnHover", "ignoreFilter"), emits);
74
+ const portalProps = usePortal(toRef(() => props.portal));
75
+ const contentProps = toRef(() => defu(props.content, { side: "bottom", sideOffset: 8, collisionPadding: 8, position: "popper" }));
76
+ const arrowProps = toRef(() => props.arrow);
77
+ const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField(props);
78
+ const { orientation, size: buttonGroupSize } = useButtonGroup(props);
79
+ const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: appConfig.ui.icons.chevronDown })));
80
+ const inputSize = computed(() => buttonGroupSize.value || formGroupSize.value);
81
+ const [DefineCreateItemTemplate, ReuseCreateItemTemplate] = createReusableTemplate();
82
+ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.ui?.inputMenu || {} })({
202
83
  color: color.value,
203
84
  variant: props.variant,
204
85
  size: inputSize?.value,
@@ -208,117 +89,123 @@ const ui = computed(() => inputMenu({
208
89
  trailing: isTrailing.value || !!slots.trailing,
209
90
  multiple: props.multiple,
210
91
  buttonGroup: orientation.value
211
- }))
212
-
213
- function displayValue(value: T): string {
92
+ }));
93
+ function displayValue(value) {
214
94
  if (!props.valueKey) {
215
- return value && (typeof value === 'object' ? get(value, props.labelKey as string) : value)
95
+ return value && (typeof value === "object" ? get(value, props.labelKey) : value);
216
96
  }
217
-
218
- const item = items.value.find(item => compare(typeof item === 'object' ? get(item as Record<string, any>, props.valueKey as string) : item, value))
219
- return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
97
+ const item = items.value.find((item2) => compare(typeof item2 === "object" ? get(item2, props.valueKey) : item2, value));
98
+ return item && (typeof item === "object" ? get(item, props.labelKey) : item);
220
99
  }
221
-
222
- const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0]) ? props.items : [props.items]) as InputMenuItem[][] : [])
223
- // eslint-disable-next-line vue/no-dupe-keys
224
- const items = computed(() => groups.value.flatMap(group => group) as T[])
225
-
100
+ const groups = computed(
101
+ () => props.items?.length ? isArrayOfArray(props.items) ? props.items : [props.items] : []
102
+ );
103
+ const items = computed(() => groups.value.flatMap((group) => group));
226
104
  const filteredGroups = computed(() => {
227
105
  if (props.ignoreFilter || !searchTerm.value) {
228
- return groups.value
106
+ return groups.value;
229
107
  }
230
-
231
- const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey] as string[]
232
-
233
- return groups.value.map(items => items.filter((item) => {
234
- if (typeof item !== 'object') {
235
- return contains(item, searchTerm.value)
108
+ const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey];
109
+ return groups.value.map((group) => group.filter((item) => {
110
+ if (typeof item !== "object" || item === null) {
111
+ return contains(String(item), searchTerm.value);
236
112
  }
237
-
238
- if (item.type && ['label', 'separator'].includes(item.type)) {
239
- return true
113
+ if (item.type && ["label", "separator"].includes(item.type)) {
114
+ return true;
240
115
  }
241
-
242
- return fields.some(field => contains(get(item, field), searchTerm.value))
243
- })).filter(group => group.filter(item => !item.type || !['label', 'separator'].includes(item.type)).length > 0)
244
- })
245
- const filteredItems = computed(() => filteredGroups.value.flatMap(group => group) as T[])
246
-
116
+ return fields.some((field) => contains(get(item, field), searchTerm.value));
117
+ })).filter((group) => group.filter(
118
+ (item) => !isInputItem(item) || (!item.type || !["label", "separator"].includes(item.type))
119
+ ).length > 0);
120
+ });
121
+ const filteredItems = computed(() => filteredGroups.value.flatMap((group) => group));
247
122
  const createItem = computed(() => {
248
123
  if (!props.createItem || !searchTerm.value) {
249
- return false
124
+ return false;
250
125
  }
251
-
252
- const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } as T : searchTerm.value
253
-
254
- if ((typeof props.createItem === 'object' && props.createItem.when === 'always') || props.createItem === 'always') {
255
- return !filteredItems.value.find(item => compare(item, newItem, props.valueKey))
126
+ const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } : searchTerm.value;
127
+ if (typeof props.createItem === "object" && props.createItem.when === "always" || props.createItem === "always") {
128
+ return !filteredItems.value.find((item) => compare(item, newItem, props.valueKey));
256
129
  }
257
-
258
- return !filteredItems.value.length
259
- })
260
- const createItemPosition = computed(() => typeof props.createItem === 'object' ? props.createItem.position : 'bottom')
261
-
262
- const inputRef = ref<InstanceType<typeof ComboboxInput> | null>(null)
263
-
130
+ return !filteredItems.value.length;
131
+ });
132
+ const createItemPosition = computed(() => typeof props.createItem === "object" ? props.createItem.position : "bottom");
133
+ const inputRef = ref(null);
264
134
  function autoFocus() {
265
135
  if (props.autofocus) {
266
- inputRef.value?.$el?.focus()
136
+ inputRef.value?.$el?.focus();
267
137
  }
268
138
  }
269
-
270
139
  onMounted(() => {
271
140
  setTimeout(() => {
272
- autoFocus()
273
- }, props.autofocusDelay)
274
- })
275
-
276
- function onUpdate(value: any) {
141
+ autoFocus();
142
+ }, props.autofocusDelay);
143
+ });
144
+ function onUpdate(value) {
277
145
  if (toRaw(props.modelValue) === value) {
278
- return
146
+ return;
147
+ }
148
+ const event = new Event("change", { target: { value } });
149
+ emits("change", event);
150
+ emitFormChange();
151
+ emitFormInput();
152
+ if (props.resetSearchTermOnSelect) {
153
+ searchTerm.value = "";
279
154
  }
280
- // @ts-expect-error - 'target' does not exist in type 'EventInit'
281
- const event = new Event('change', { target: { value } })
282
- emits('change', event)
283
- emitFormChange()
284
- emitFormInput()
285
155
  }
286
-
287
- function onBlur(event: FocusEvent) {
288
- emits('blur', event)
289
- emitFormBlur()
156
+ function onBlur(event) {
157
+ emits("blur", event);
158
+ emitFormBlur();
290
159
  }
291
-
292
- function onFocus(event: FocusEvent) {
293
- emits('focus', event)
294
- emitFormFocus()
160
+ function onFocus(event) {
161
+ emits("focus", event);
162
+ emitFormFocus();
295
163
  }
296
-
297
- function onUpdateOpen(value: boolean) {
164
+ function onUpdateOpen(value) {
165
+ let timeoutId;
298
166
  if (!value) {
299
- const event = new FocusEvent('blur')
300
- emits('blur', event)
301
- emitFormBlur()
167
+ const event = new FocusEvent("blur");
168
+ emits("blur", event);
169
+ emitFormBlur();
170
+ if (props.resetSearchTermOnBlur) {
171
+ const STATE_ANIMATION_DELAY_MS = 100;
172
+ timeoutId = setTimeout(() => {
173
+ searchTerm.value = "";
174
+ }, STATE_ANIMATION_DELAY_MS);
175
+ }
302
176
  } else {
303
- const event = new FocusEvent('focus')
304
- emits('focus', event)
177
+ const event = new FocusEvent("focus");
178
+ emits("focus", event);
179
+ emitFormFocus();
180
+ clearTimeout(timeoutId);
305
181
  }
306
182
  }
307
-
308
- function onRemoveTag(event: any) {
183
+ function onRemoveTag(event) {
309
184
  if (props.multiple) {
310
- const modelValue = props.modelValue as SelectModelValue<T, V, true>
311
- const filteredValue = modelValue.filter(value => !isEqual(value, event))
312
- emits('update:modelValue', filteredValue as SelectModelValue<T, V, M>)
185
+ const modelValue = props.modelValue;
186
+ const filteredValue = modelValue.filter((value) => !isEqual(value, event));
187
+ emits("update:modelValue", filteredValue);
188
+ onUpdate(filteredValue);
313
189
  }
314
190
  }
315
-
191
+ function onSelect(e, item) {
192
+ if (!isInputItem(item)) {
193
+ return;
194
+ }
195
+ if (item.disabled) {
196
+ e.preventDefault();
197
+ return;
198
+ }
199
+ item.onSelect?.(e);
200
+ }
201
+ function isInputItem(item) {
202
+ return typeof item === "object" && item !== null;
203
+ }
316
204
  defineExpose({
317
205
  inputRef
318
- })
206
+ });
319
207
  </script>
320
208
 
321
- <!-- eslint-disable vue/no-template-shadow -->
322
209
  <template>
323
210
  <DefineCreateItemTemplate>
324
211
  <ComboboxGroup :class="ui.group({ class: props.ui?.group })">
@@ -353,7 +240,7 @@ defineExpose({
353
240
  <TagsInputRoot
354
241
  v-if="multiple"
355
242
  v-slot="{ modelValue: tags }"
356
- :model-value="(modelValue as string[])"
243
+ :model-value="modelValue"
357
244
  :disabled="disabled"
358
245
  :required="required"
359
246
  delimiter=""
@@ -362,21 +249,21 @@ defineExpose({
362
249
  @focus="onFocus"
363
250
  @remove-tag="onRemoveTag"
364
251
  >
365
- <TagsInputItem v-for="(item, index) in tags" :key="index" :value="(item as string)" :class="ui.tagsItem({ class: props.ui?.tagsItem })">
252
+ <TagsInputItem v-for="(item, index) in tags" :key="index" :value="item" :class="ui.tagsItem({ class: props.ui?.tagsItem })">
366
253
  <TagsInputItemText :class="ui.tagsItemText({ class: props.ui?.tagsItemText })">
367
- <slot name="tags-item-text" :item="(item as T)" :index="index">
368
- {{ displayValue(item as T) }}
254
+ <slot name="tags-item-text" :item="item" :index="index">
255
+ {{ displayValue(item) }}
369
256
  </slot>
370
257
  </TagsInputItemText>
371
258
 
372
259
  <TagsInputItemDelete :class="ui.tagsItemDelete({ class: props.ui?.tagsItemDelete })" :disabled="disabled">
373
- <slot name="tags-item-delete" :item="(item as T)" :index="index">
260
+ <slot name="tags-item-delete" :item="item" :index="index">
374
261
  <UIcon :name="deleteIcon || appConfig.ui.icons.close" :class="ui.tagsItemDeleteIcon({ class: props.ui?.tagsItemDeleteIcon })" />
375
262
  </slot>
376
263
  </TagsInputItemDelete>
377
264
  </TagsInputItem>
378
265
 
379
- <ComboboxInput v-model="searchTerm" :display-value="displayValue" as-child>
266
+ <ComboboxInput v-model="searchTerm" as-child>
380
267
  <TagsInputInput
381
268
  ref="inputRef"
382
269
  v-bind="{ ...$attrs, ...ariaAttrs }"
@@ -390,7 +277,6 @@ defineExpose({
390
277
  <ComboboxInput
391
278
  v-else
392
279
  ref="inputRef"
393
- v-model="searchTerm"
394
280
  :display-value="displayValue"
395
281
  v-bind="{ ...$attrs, ...ariaAttrs }"
396
282
  :type="type"
@@ -398,24 +284,27 @@ defineExpose({
398
284
  :required="required"
399
285
  @blur="onBlur"
400
286
  @focus="onFocus"
287
+ @update:model-value="searchTerm = $event"
401
288
  />
402
289
 
403
290
  <span v-if="isLeading || !!avatar || !!slots.leading" :class="ui.leading({ class: props.ui?.leading })">
404
- <slot name="leading" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :ui="ui">
291
+ <slot name="leading" :model-value="modelValue" :open="open" :ui="ui">
405
292
  <UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
406
- <UAvatar v-else-if="!!avatar" :size="((props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
293
+ <UAvatar v-else-if="!!avatar" :size="props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()" v-bind="avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
407
294
  </slot>
408
295
  </span>
409
296
 
410
297
  <ComboboxTrigger v-if="isTrailing || !!slots.trailing" :class="ui.trailing({ class: props.ui?.trailing })">
411
- <slot name="trailing" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :ui="ui">
298
+ <slot name="trailing" :model-value="modelValue" :open="open" :ui="ui">
412
299
  <UIcon v-if="trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
413
300
  </slot>
414
301
  </ComboboxTrigger>
415
302
  </ComboboxAnchor>
416
303
 
417
- <ComboboxPortal :disabled="!portal">
304
+ <ComboboxPortal v-bind="portalProps">
418
305
  <ComboboxContent :class="ui.content({ class: props.ui?.content })" v-bind="contentProps">
306
+ <slot name="content-top" />
307
+
419
308
  <ComboboxEmpty :class="ui.empty({ class: props.ui?.empty })">
420
309
  <slot name="empty" :search-term="searchTerm">
421
310
  {{ searchTerm ? t('inputMenu.noMatch', { searchTerm }) : t('inputMenu.noData') }}
@@ -427,26 +316,26 @@ defineExpose({
427
316
 
428
317
  <ComboboxGroup v-for="(group, groupIndex) in filteredGroups" :key="`group-${groupIndex}`" :class="ui.group({ class: props.ui?.group })">
429
318
  <template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
430
- <ComboboxLabel v-if="item?.type === 'label'" :class="ui.label({ class: props.ui?.label })">
431
- {{ get(item, props.labelKey as string) }}
319
+ <ComboboxLabel v-if="isInputItem(item) && item.type === 'label'" :class="ui.label({ class: props.ui?.label })">
320
+ {{ get(item, props.labelKey) }}
432
321
  </ComboboxLabel>
433
322
 
434
- <ComboboxSeparator v-else-if="item?.type === 'separator'" :class="ui.separator({ class: props.ui?.separator })" />
323
+ <ComboboxSeparator v-else-if="isInputItem(item) && item.type === 'separator'" :class="ui.separator({ class: props.ui?.separator })" />
435
324
 
436
325
  <ComboboxItem
437
326
  v-else
438
327
  :class="ui.item({ class: props.ui?.item })"
439
- :disabled="item.disabled"
440
- :value="valueKey && typeof item === 'object' ? get(item, props.valueKey as string) : item"
441
- @select="item.onSelect"
328
+ :disabled="isInputItem(item) && item.disabled"
329
+ :value="props.valueKey && isInputItem(item) ? get(item, props.valueKey) : item"
330
+ @select="onSelect($event, item)"
442
331
  >
443
- <slot name="item" :item="(item as T)" :index="index">
444
- <slot name="item-leading" :item="(item as T)" :index="index">
445
- <UIcon v-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
446
- <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 })" />
332
+ <slot name="item" :item="item" :index="index">
333
+ <slot name="item-leading" :item="item" :index="index">
334
+ <UIcon v-if="isInputItem(item) && item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
335
+ <UAvatar v-else-if="isInputItem(item) && item.avatar" :size="props.ui?.itemLeadingAvatarSize || ui.itemLeadingAvatarSize()" v-bind="item.avatar" :class="ui.itemLeadingAvatar({ class: props.ui?.itemLeadingAvatar })" />
447
336
  <UChip
448
- v-else-if="item.chip"
449
- :size="((props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()) as ChipProps['size'])"
337
+ v-else-if="isInputItem(item) && item.chip"
338
+ :size="props.ui?.itemLeadingChipSize || ui.itemLeadingChipSize()"
450
339
  inset
451
340
  standalone
452
341
  v-bind="item.chip"
@@ -455,13 +344,13 @@ defineExpose({
455
344
  </slot>
456
345
 
457
346
  <span :class="ui.itemLabel({ class: props.ui?.itemLabel })">
458
- <slot name="item-label" :item="(item as T)" :index="index">
459
- {{ typeof item === 'object' ? get(item, props.labelKey as string) : item }}
347
+ <slot name="item-label" :item="item" :index="index">
348
+ {{ isInputItem(item) ? get(item, props.labelKey) : item }}
460
349
  </slot>
461
350
  </span>
462
351
 
463
352
  <span :class="ui.itemTrailing({ class: props.ui?.itemTrailing })">
464
- <slot name="item-trailing" :item="(item as T)" :index="index" />
353
+ <slot name="item-trailing" :item="item" :index="index" />
465
354
 
466
355
  <ComboboxItemIndicator as-child>
467
356
  <UIcon :name="selectedIcon || appConfig.ui.icons.check" :class="ui.itemTrailingIcon({ class: props.ui?.itemTrailingIcon })" />
@@ -475,6 +364,8 @@ defineExpose({
475
364
  <ReuseCreateItemTemplate v-if="createItem && createItemPosition === 'bottom'" />
476
365
  </ComboboxViewport>
477
366
 
367
+ <slot name="content-bottom" />
368
+
478
369
  <ComboboxArrow v-if="!!arrow" v-bind="arrowProps" :class="ui.arrow({ class: props.ui?.arrow })" />
479
370
  </ComboboxContent>
480
371
  </ComboboxPortal>