@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.
- package/.nuxt/ui/accordion.ts +25 -4
- package/.nuxt/ui/alert.ts +52 -31
- package/.nuxt/ui/avatar-group.ts +24 -3
- package/.nuxt/ui/avatar.ts +26 -5
- package/.nuxt/ui/badge.ts +56 -35
- package/.nuxt/ui/breadcrumb.ts +28 -7
- package/.nuxt/ui/button-group.ts +23 -2
- package/.nuxt/ui/button.ts +66 -45
- package/.nuxt/ui/calendar.ts +39 -18
- package/.nuxt/ui/card.ts +28 -7
- package/.nuxt/ui/carousel.ts +29 -8
- package/.nuxt/ui/checkbox-group.ts +82 -0
- package/.nuxt/ui/checkbox.ts +163 -29
- package/.nuxt/ui/chip.ts +31 -10
- package/.nuxt/ui/collapsible.ts +23 -2
- package/.nuxt/ui/color-picker.ts +26 -5
- package/.nuxt/ui/command-palette.ts +37 -16
- package/.nuxt/ui/container.ts +24 -3
- package/.nuxt/ui/context-menu.ts +56 -35
- package/.nuxt/ui/drawer.ts +33 -12
- package/.nuxt/ui/dropdown-menu.ts +57 -36
- package/.nuxt/ui/form-field.ts +29 -8
- package/.nuxt/ui/form.ts +23 -2
- package/.nuxt/ui/index.ts +1 -0
- package/.nuxt/ui/input-menu.ts +65 -44
- package/.nuxt/ui/input-number.ts +43 -22
- package/.nuxt/ui/input.ts +46 -25
- package/.nuxt/ui/kbd.ts +27 -6
- package/.nuxt/ui/link.ts +26 -5
- package/.nuxt/ui/modal.ts +28 -7
- package/.nuxt/ui/navigation-menu.ts +103 -84
- package/.nuxt/ui/pagination.ts +23 -2
- package/.nuxt/ui/pin-input.ts +43 -22
- package/.nuxt/ui/popover.ts +25 -4
- package/.nuxt/ui/progress.ts +40 -19
- package/.nuxt/ui/radio-group.ts +273 -37
- package/.nuxt/ui/select-menu.ts +59 -35
- package/.nuxt/ui/select.ts +55 -34
- package/.nuxt/ui/separator.ts +31 -10
- package/.nuxt/ui/skeleton.ts +24 -3
- package/.nuxt/ui/slideover.ts +27 -6
- package/.nuxt/ui/slider.ts +39 -18
- package/.nuxt/ui/stepper.ts +41 -20
- package/.nuxt/ui/switch.ts +44 -23
- package/.nuxt/ui/table.ts +38 -17
- package/.nuxt/ui/tabs.ts +55 -34
- package/.nuxt/ui/textarea.ts +63 -37
- package/.nuxt/ui/toast.ts +47 -26
- package/.nuxt/ui/toaster.ts +23 -2
- package/.nuxt/ui/tooltip.ts +25 -4
- package/.nuxt/ui/tree.ts +41 -20
- package/.nuxt/ui.css +56 -12
- package/README.md +11 -0
- package/cli/templates.mjs +18 -17
- package/dist/module.d.mts +2 -1
- package/dist/module.json +5 -5
- package/dist/module.mjs +11 -7
- package/dist/runtime/components/Accordion.vue +36 -92
- package/dist/runtime/components/Accordion.vue.d.ts +91 -0
- package/dist/runtime/components/Alert.vue +37 -100
- package/dist/runtime/components/Alert.vue.d.ts +96 -0
- package/dist/runtime/components/App.vue +28 -34
- package/dist/runtime/components/App.vue.d.ts +24 -0
- package/dist/runtime/components/Avatar.vue +47 -78
- package/dist/runtime/components/Avatar.vue.d.ts +60 -0
- package/dist/runtime/components/AvatarGroup.vue +40 -77
- package/dist/runtime/components/AvatarGroup.vue.d.ts +40 -0
- package/dist/runtime/components/Badge.vue +33 -62
- package/dist/runtime/components/Badge.vue.d.ts +62 -0
- package/dist/runtime/components/Breadcrumb.vue +36 -88
- package/dist/runtime/components/Breadcrumb.vue.d.ts +82 -0
- package/dist/runtime/components/Button.vue +96 -105
- package/dist/runtime/components/Button.vue.d.ts +73 -0
- package/dist/runtime/components/ButtonGroup.vue +20 -46
- package/dist/runtime/components/ButtonGroup.vue.d.ts +55 -0
- package/dist/runtime/components/Calendar.vue +73 -127
- package/dist/runtime/components/Calendar.vue.d.ts +107 -0
- package/dist/runtime/components/Card.vue +18 -40
- package/dist/runtime/components/Card.vue.d.ts +38 -0
- package/dist/runtime/components/Carousel.vue +112 -230
- package/dist/runtime/components/Carousel.vue.d.ts +120 -0
- package/dist/runtime/components/Checkbox.vue +52 -93
- package/dist/runtime/components/Checkbox.vue.d.ts +76 -0
- package/dist/runtime/components/CheckboxGroup.vue +121 -0
- package/dist/runtime/components/CheckboxGroup.vue.d.ts +75 -0
- package/dist/runtime/components/Chip.vue +26 -69
- package/dist/runtime/components/Chip.vue.d.ts +71 -0
- package/dist/runtime/components/Collapsible.vue +22 -41
- package/dist/runtime/components/Collapsible.vue.d.ts +50 -0
- package/dist/runtime/components/ColorPicker.vue +111 -197
- package/dist/runtime/components/ColorPicker.vue.d.ts +63 -0
- package/dist/runtime/components/CommandPalette.vue +94 -234
- package/dist/runtime/components/CommandPalette.vue.d.ts +173 -0
- package/dist/runtime/components/Container.vue +15 -28
- package/dist/runtime/components/Container.vue.d.ts +27 -0
- package/dist/runtime/components/ContextMenu.vue +32 -119
- package/dist/runtime/components/ContextMenu.vue.d.ts +127 -0
- package/dist/runtime/components/ContextMenuContent.vue +71 -72
- package/dist/runtime/components/ContextMenuContent.vue.d.ts +105 -0
- package/dist/runtime/components/Drawer.vue +49 -87
- package/dist/runtime/components/Drawer.vue.d.ts +88 -0
- package/dist/runtime/components/DropdownMenu.vue +37 -129
- package/dist/runtime/components/DropdownMenu.vue.d.ts +141 -0
- package/dist/runtime/components/DropdownMenuContent.vue +76 -79
- package/dist/runtime/components/DropdownMenuContent.vue.d.ts +55 -0
- package/dist/runtime/components/Form.vue +135 -219
- package/dist/runtime/components/Form.vue.d.ts +66 -0
- package/dist/runtime/components/FormField.vue +36 -77
- package/dist/runtime/components/FormField.vue.d.ts +70 -0
- package/dist/runtime/components/Icon.vue +12 -19
- package/dist/runtime/components/Icon.vue.d.ts +17 -0
- package/dist/runtime/components/Input.vue +82 -140
- package/dist/runtime/components/Input.vue.d.ts +91 -0
- package/dist/runtime/components/InputMenu.vue +193 -302
- package/dist/runtime/components/InputMenu.vue.d.ts +312 -0
- package/dist/runtime/components/InputNumber.vue +71 -133
- package/dist/runtime/components/InputNumber.vue.d.ts +193 -0
- package/dist/runtime/components/Kbd.vue +20 -46
- package/dist/runtime/components/Kbd.vue.d.ts +55 -0
- package/dist/runtime/components/Link.vue +98 -173
- package/dist/runtime/components/Link.vue.d.ts +124 -0
- package/dist/runtime/components/LinkBase.vue +33 -41
- package/dist/runtime/components/LinkBase.vue.d.ts +48 -0
- package/dist/runtime/components/Modal.vue +53 -116
- package/dist/runtime/components/Modal.vue.d.ts +109 -0
- package/dist/runtime/components/NavigationMenu.vue +78 -186
- package/dist/runtime/components/NavigationMenu.vue.d.ts +159 -0
- package/dist/runtime/components/OverlayProvider.vue +14 -18
- package/dist/runtime/components/OverlayProvider.vue.d.ts +2 -0
- package/dist/runtime/components/Pagination.vue +46 -140
- package/dist/runtime/components/Pagination.vue.d.ts +143 -0
- package/dist/runtime/components/PinInput.vue +61 -78
- package/dist/runtime/components/PinInput.vue.d.ts +68 -0
- package/dist/runtime/components/Popover.vue +47 -84
- package/dist/runtime/components/Popover.vue.d.ts +80 -0
- package/dist/runtime/components/Progress.vue +70 -131
- package/dist/runtime/components/Progress.vue.d.ts +97 -0
- package/dist/runtime/components/RadioGroup.vue +75 -126
- package/dist/runtime/components/RadioGroup.vue.d.ts +88 -0
- package/dist/runtime/components/Select.vue +115 -202
- package/dist/runtime/components/Select.vue.d.ts +139 -0
- package/dist/runtime/components/SelectMenu.vue +167 -281
- package/dist/runtime/components/SelectMenu.vue.d.ts +184 -0
- package/dist/runtime/components/Separator.vue +29 -71
- package/dist/runtime/components/Separator.vue.d.ts +75 -0
- package/dist/runtime/components/Skeleton.vue +21 -23
- package/dist/runtime/components/Skeleton.vue.d.ts +26 -0
- package/dist/runtime/components/Slideover.vue +53 -120
- package/dist/runtime/components/Slideover.vue.d.ts +111 -0
- package/dist/runtime/components/Slider.vue +46 -84
- package/dist/runtime/components/Slider.vue.d.ts +71 -0
- package/dist/runtime/components/Stepper.vue +44 -113
- package/dist/runtime/components/Stepper.vue.d.ts +89 -0
- package/dist/runtime/components/Switch.vue +45 -93
- package/dist/runtime/components/Switch.vue.d.ts +73 -0
- package/dist/runtime/components/Table.vue +165 -264
- package/dist/runtime/components/Table.vue.d.ts +183 -0
- package/dist/runtime/components/Tabs.vue +40 -105
- package/dist/runtime/components/Tabs.vue.d.ts +104 -0
- package/dist/runtime/components/Textarea.vue +113 -158
- package/dist/runtime/components/Textarea.vue.d.ts +95 -0
- package/dist/runtime/components/Toast.vue +49 -110
- package/dist/runtime/components/Toast.vue.d.ts +95 -0
- package/dist/runtime/components/Toaster.vue +70 -107
- package/dist/runtime/components/Toaster.vue.d.ts +63 -0
- package/dist/runtime/components/Tooltip.vue +38 -65
- package/dist/runtime/components/Tooltip.vue.d.ts +65 -0
- package/dist/runtime/components/Tree.vue +57 -150
- package/dist/runtime/components/Tree.vue.d.ts +128 -0
- package/dist/runtime/composables/defineShortcuts.d.ts +1 -1
- package/dist/runtime/composables/defineShortcuts.js +0 -1
- package/dist/runtime/composables/useAvatarGroup.d.ts +1 -1
- package/dist/runtime/composables/useButtonGroup.d.ts +2 -2
- package/dist/runtime/composables/useFormField.d.ts +2 -2
- package/dist/runtime/composables/useFormField.js +1 -1
- package/dist/runtime/composables/useOverlay.d.ts +16 -8
- package/dist/runtime/composables/useOverlay.js +14 -6
- package/dist/runtime/composables/usePortal.d.ts +6 -0
- package/dist/runtime/composables/usePortal.js +17 -0
- package/dist/runtime/index.css +1 -1
- package/dist/runtime/inertia/components/Link.vue +146 -0
- package/dist/runtime/inertia/components/Link.vue.d.ts +92 -0
- package/dist/runtime/inertia/stubs.d.ts +43 -0
- package/dist/runtime/inertia/stubs.js +82 -0
- package/dist/runtime/locale/bg.d.ts +3 -0
- package/dist/runtime/locale/bg.js +54 -0
- package/dist/runtime/locale/hy.d.ts +3 -0
- package/dist/runtime/locale/hy.js +54 -0
- package/dist/runtime/locale/index.d.ts +6 -1
- package/dist/runtime/locale/index.js +6 -1
- package/dist/runtime/locale/kk.d.ts +3 -0
- package/dist/runtime/locale/kk.js +54 -0
- package/dist/runtime/locale/tj.d.ts +3 -0
- package/dist/runtime/locale/tj.js +54 -0
- package/dist/runtime/locale/ug_cn.d.ts +3 -0
- package/dist/runtime/locale/ug_cn.js +55 -0
- package/dist/runtime/types/form.d.ts +5 -1
- package/dist/runtime/types/index.d.ts +2 -0
- package/dist/runtime/types/index.js +2 -0
- package/dist/runtime/types/utils.d.ts +70 -10
- package/dist/runtime/utils/index.d.ts +1 -0
- package/dist/runtime/utils/index.js +3 -0
- package/dist/runtime/utils/link.d.ts +4 -27
- package/dist/runtime/utils/link.js +32 -1
- package/dist/runtime/utils/tv.js +2 -2
- package/dist/runtime/vue/components/Icon.vue +7 -8
- package/dist/runtime/vue/components/Icon.vue.d.ts +14 -0
- package/dist/runtime/vue/components/Link.vue +118 -199
- package/dist/runtime/vue/components/Link.vue.d.ts +124 -0
- package/dist/runtime/vue/stubs.d.ts +5 -0
- package/dist/runtime/vue/stubs.js +11 -2
- package/dist/shared/{ui.BCaWD5Ek.mjs → ui.D7lOOabO.mjs} +623 -364
- package/dist/types.d.mts +5 -1
- package/dist/unplugin.d.mts +6 -1
- package/dist/unplugin.mjs +25 -4
- package/dist/vite.mjs +1 -1
- package/package.json +122 -64
- package/dist/module.cjs +0 -62
- package/dist/module.d.cts +0 -44
- package/dist/module.d.ts +0 -44
- package/dist/shared/ui.C6uTSFTI.cjs +0 -3555
- package/dist/types.d.ts +0 -1
- package/dist/unplugin.cjs +0 -232
- package/dist/unplugin.d.cts +0 -61
- package/dist/unplugin.d.ts +0 -61
- package/dist/vite.cjs +0 -23
- package/dist/vite.d.cts +0 -15
- package/dist/vite.d.ts +0 -15
|
@@ -1,196 +1,82 @@
|
|
|
1
|
-
<script
|
|
2
|
-
import
|
|
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
|
|
148
|
-
import { computed, toRef, toRaw } from
|
|
149
|
-
import { ComboboxRoot, ComboboxArrow, ComboboxAnchor, ComboboxInput, ComboboxTrigger, ComboboxPortal, ComboboxContent, ComboboxViewport, ComboboxEmpty, ComboboxGroup, ComboboxLabel, ComboboxSeparator, ComboboxItem, ComboboxItemIndicator, FocusScope, useForwardPropsEmits, useFilter } from
|
|
150
|
-
import { defu } from
|
|
151
|
-
import { reactivePick, createReusableTemplate } from
|
|
152
|
-
import { useAppConfig } from
|
|
153
|
-
import { useButtonGroup } from
|
|
154
|
-
import { useComponentIcons } from
|
|
155
|
-
import { useFormField } from
|
|
156
|
-
import { useLocale } from
|
|
157
|
-
import {
|
|
158
|
-
import
|
|
159
|
-
import
|
|
160
|
-
import
|
|
161
|
-
import
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const props =
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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 ===
|
|
94
|
+
return value && (typeof value === "object" ? get(value, props.labelKey) : value);
|
|
211
95
|
}
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
const items = computed(() => groups.value.flatMap(group => group)
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
234
|
-
return true
|
|
112
|
+
if (item.type && ["label", "separator"].includes(item.type)) {
|
|
113
|
+
return true;
|
|
235
114
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
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
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
269
|
-
let timeoutId
|
|
270
|
-
|
|
144
|
+
function onUpdateOpen(value) {
|
|
145
|
+
let timeoutId;
|
|
271
146
|
if (!value) {
|
|
272
|
-
const event = new FocusEvent(
|
|
273
|
-
|
|
274
|
-
|
|
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(
|
|
288
|
-
emits(
|
|
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="
|
|
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="
|
|
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="
|
|
334
|
-
<template v-for="displayedModelValue in [displayValue(modelValue
|
|
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 ?? '
|
|
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="
|
|
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
|
|
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
|
|
371
|
-
{{ get(item, props.labelKey
|
|
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
|
|
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 &&
|
|
381
|
-
@select="item
|
|
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="
|
|
384
|
-
<slot name="item-leading" :item="
|
|
385
|
-
<UIcon v-if="item.icon" :name="item.icon" :class="ui.itemLeadingIcon({ class: props.ui?.itemLeadingIcon })" />
|
|
386
|
-
<UAvatar v-else-if="item.avatar" :size="
|
|
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="
|
|
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="
|
|
399
|
-
{{
|
|
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="
|
|
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 })" />
|