@bitrix24/b24ui-nuxt 0.1.7 → 0.2.1
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/b24ui/alert.ts +18 -8
- package/.nuxt/b24ui/badge.ts +1 -1
- package/.nuxt/b24ui/content/description-list.ts +17 -7
- package/.nuxt/b24ui/index.ts +1 -0
- package/.nuxt/b24ui/input-menu.ts +591 -0
- package/.nuxt/b24ui/input.ts +5 -5
- package/.nuxt/b24ui/select-menu.ts +6 -6
- package/.nuxt/b24ui/select.ts +6 -6
- package/.nuxt/b24ui/toast.ts +18 -8
- package/README.md +8 -8
- package/dist/meta.cjs +5409 -331
- package/dist/meta.d.cts +5409 -331
- package/dist/meta.d.mts +5409 -331
- package/dist/meta.d.ts +5409 -331
- package/dist/meta.mjs +5409 -331
- package/dist/module.cjs +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/components/Alert.vue +13 -10
- package/dist/runtime/components/InputMenu.vue +507 -0
- package/dist/runtime/components/Toast.vue +26 -14
- package/dist/runtime/components/Toaster.vue +2 -2
- package/dist/runtime/components/content/DescriptionList.vue +9 -7
- package/dist/runtime/composables/useComponentIcons.d.ts +2 -2
- package/dist/runtime/composables/useToast.d.ts +5 -4
- package/dist/runtime/composables/useToast.js +2 -2
- package/dist/runtime/types/index.d.ts +1 -0
- package/dist/runtime/types/index.js +1 -0
- package/dist/runtime/types/utils.d.ts +5 -0
- package/dist/shared/{b24ui-nuxt.vQRZieQw.mjs → b24ui-nuxt.CrjojW8t.mjs} +292 -31
- package/dist/shared/{b24ui-nuxt.ZUYaG6CJ.cjs → b24ui-nuxt.D5cXbZSx.cjs} +292 -31
- package/dist/unplugin.cjs +1 -1
- package/dist/unplugin.mjs +1 -1
- package/dist/vite.cjs +1 -1
- package/dist/vite.mjs +1 -1
- package/package.json +18 -1
package/dist/module.cjs
CHANGED
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defu } from 'defu';
|
|
2
2
|
import { defineNuxtModule, createResolver, addVitePlugin, addPlugin, addComponentsDir, addImportsDir, hasNuxtModule, installModule } from '@nuxt/kit';
|
|
3
|
-
import { d as defaultOptions, a as getDefaultUiConfig, b as addTemplates } from './shared/b24ui-nuxt.
|
|
3
|
+
import { d as defaultOptions, a as getDefaultUiConfig, b as addTemplates } from './shared/b24ui-nuxt.CrjojW8t.mjs';
|
|
4
4
|
import 'node:url';
|
|
5
5
|
import 'scule';
|
|
6
6
|
|
|
@@ -23,11 +23,12 @@ export interface AlertProps {
|
|
|
23
23
|
icon?: IconComponent
|
|
24
24
|
avatar?: AvatarProps
|
|
25
25
|
color?: AlertVariants['color']
|
|
26
|
+
orientation?: AlertVariants['orientation']
|
|
26
27
|
size?: AlertVariants['size']
|
|
27
28
|
/**
|
|
28
29
|
* Display a list of actions:
|
|
29
|
-
* - under the title and description
|
|
30
|
-
* - next to the close button
|
|
30
|
+
* - under the title and description when orientation is `vertical`
|
|
31
|
+
* - next to the close button when orientation is `horizontal`
|
|
31
32
|
* `{ size: 'xs' }`{lang="ts-type"}
|
|
32
33
|
*/
|
|
33
34
|
actions?: ButtonProps[]
|
|
@@ -68,22 +69,24 @@ import icons from '../dictionary/icons'
|
|
|
68
69
|
import B24Avatar from './Avatar.vue'
|
|
69
70
|
import B24Button from './Button.vue'
|
|
70
71
|
|
|
71
|
-
const props = defineProps<AlertProps>()
|
|
72
|
+
const props = withDefaults(defineProps<AlertProps>(), {
|
|
73
|
+
orientation: 'vertical'
|
|
74
|
+
})
|
|
72
75
|
const emits = defineEmits<AlertEmits>()
|
|
73
76
|
const slots = defineSlots<AlertSlots>()
|
|
74
77
|
|
|
75
78
|
const { t } = useLocale()
|
|
76
79
|
|
|
77
|
-
const multiline = computed(() => !!props.title && !!props.description)
|
|
78
|
-
|
|
79
80
|
const b24ui = computed(() => alert({
|
|
80
81
|
color: props.color,
|
|
81
|
-
size: props.size
|
|
82
|
+
size: props.size,
|
|
83
|
+
orientation: props.orientation,
|
|
84
|
+
title: !!props.title || !!slots.title
|
|
82
85
|
}))
|
|
83
86
|
</script>
|
|
84
87
|
|
|
85
88
|
<template>
|
|
86
|
-
<Primitive :as="as" :class="b24ui.root({ class: [props.class, props.b24ui?.root]
|
|
89
|
+
<Primitive :as="as" :data-orientation="orientation" :class="b24ui.root({ class: [props.class, props.b24ui?.root] })">
|
|
87
90
|
<slot name="leading">
|
|
88
91
|
<Component
|
|
89
92
|
:is="icon"
|
|
@@ -105,15 +108,15 @@ const b24ui = computed(() => alert({
|
|
|
105
108
|
</slot>
|
|
106
109
|
</div>
|
|
107
110
|
|
|
108
|
-
<div v-if="
|
|
111
|
+
<div v-if="orientation === 'vertical' && actions?.length" :class="b24ui.actions({ class: props.b24ui?.actions })">
|
|
109
112
|
<slot name="actions">
|
|
110
113
|
<B24Button v-for="(action, index) in actions" :key="index" size="xs" v-bind="action" />
|
|
111
114
|
</slot>
|
|
112
115
|
</div>
|
|
113
116
|
</div>
|
|
114
117
|
|
|
115
|
-
<div v-if="(
|
|
116
|
-
<template v-if="
|
|
118
|
+
<div v-if="(orientation === 'horizontal' && actions?.length) || close" :class="b24ui.actions({ class: props.b24ui?.actions, orientation: 'horizontal' })">
|
|
119
|
+
<template v-if="orientation === 'horizontal' && actions?.length">
|
|
117
120
|
<slot name="actions">
|
|
118
121
|
<B24Button v-for="(action, index) in actions" :key="index" size="xs" v-bind="action" />
|
|
119
122
|
</slot>
|
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { InputHTMLAttributes } from 'vue'
|
|
3
|
+
import type { VariantProps } from 'tailwind-variants'
|
|
4
|
+
import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxArrowProps, AcceptableValue } from 'reka-ui'
|
|
5
|
+
import type { AppConfig } from '@nuxt/schema'
|
|
6
|
+
import _appConfig from '#build/app.config'
|
|
7
|
+
import theme from '#build/b24ui/input-menu'
|
|
8
|
+
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
|
9
|
+
import { tv } from '../utils/tv'
|
|
10
|
+
import type { AvatarProps, ChipProps, InputProps, IconComponent } from '../types'
|
|
11
|
+
import type { PartialString, MaybeArrayOfArray, MaybeArrayOfArrayItem, SelectModelValue, SelectModelValueEmits, SelectItemKey } from '../types/utils'
|
|
12
|
+
|
|
13
|
+
const appConfigInputMenu = _appConfig as AppConfig & { b24ui: { inputMenu: Partial<typeof theme> } }
|
|
14
|
+
|
|
15
|
+
const inputMenu = tv({ extend: tv(theme), ...(appConfigInputMenu.b24ui?.inputMenu || {}) })
|
|
16
|
+
|
|
17
|
+
export interface InputMenuItem {
|
|
18
|
+
label?: string
|
|
19
|
+
icon?: IconComponent
|
|
20
|
+
avatar?: AvatarProps
|
|
21
|
+
chip?: ChipProps
|
|
22
|
+
/**
|
|
23
|
+
* The item type.
|
|
24
|
+
* @defaultValue 'item'
|
|
25
|
+
*/
|
|
26
|
+
type?: 'label' | 'separator' | 'item'
|
|
27
|
+
disabled?: boolean
|
|
28
|
+
onSelect?(e?: Event): void
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type InputMenuVariants = VariantProps<typeof inputMenu>
|
|
32
|
+
|
|
33
|
+
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 {
|
|
34
|
+
/**
|
|
35
|
+
* The element or component this component should render as.
|
|
36
|
+
* @defaultValue 'div'
|
|
37
|
+
*/
|
|
38
|
+
as?: any
|
|
39
|
+
id?: string
|
|
40
|
+
type?: InputHTMLAttributes['type']
|
|
41
|
+
/** The placeholder text when the input is empty. */
|
|
42
|
+
placeholder?: string
|
|
43
|
+
color?: InputMenuVariants['color']
|
|
44
|
+
size?: InputMenuVariants['size']
|
|
45
|
+
/** Removes padding from input. */
|
|
46
|
+
noPadding?: boolean
|
|
47
|
+
/** removes all borders (rings). */
|
|
48
|
+
noBorder?: boolean
|
|
49
|
+
/** removes all borders (rings) except the bottom one. */
|
|
50
|
+
underline?: boolean
|
|
51
|
+
/** Rounds the corners of the button. */
|
|
52
|
+
rounded?: boolean
|
|
53
|
+
required?: boolean
|
|
54
|
+
autofocus?: boolean
|
|
55
|
+
autofocusDelay?: number
|
|
56
|
+
/**
|
|
57
|
+
* The icon displayed to open the menu.
|
|
58
|
+
* @defaultValue icons.chevronDown = `ChevronDownIcon`
|
|
59
|
+
*/
|
|
60
|
+
trailingIcon?: IconComponent
|
|
61
|
+
/**
|
|
62
|
+
* The icon displayed when an item is selected.
|
|
63
|
+
* @defaultValue icons.check = `CheckIcon`
|
|
64
|
+
*/
|
|
65
|
+
selectedIcon?: IconComponent
|
|
66
|
+
/**
|
|
67
|
+
* The icon displayed to delete a tag.
|
|
68
|
+
* Works only when `multiple` is `true`.
|
|
69
|
+
* @defaultValue icons.close = `Cross30Icon`
|
|
70
|
+
*/
|
|
71
|
+
deleteIcon?: IconComponent
|
|
72
|
+
/**
|
|
73
|
+
* The content of the menu.
|
|
74
|
+
* @defaultValue { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }
|
|
75
|
+
*/
|
|
76
|
+
content?: Omit<ComboboxContentProps, 'as' | 'asChild' | 'forceMount'>
|
|
77
|
+
/**
|
|
78
|
+
* Display an arrow alongside the menu.
|
|
79
|
+
* @defaultValue false
|
|
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 InputMenu when initially rendered. Use when you do not need to control the state of the InputMenu. */
|
|
99
|
+
defaultValue?: SelectModelValue<T, V, M>
|
|
100
|
+
/** The controlled value of the InputMenu. 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
|
+
tag?: string
|
|
105
|
+
tagColor?: InputMenuVariants['tagColor']
|
|
106
|
+
/** Highlight the ring color like a focus state. */
|
|
107
|
+
highlight?: boolean
|
|
108
|
+
/**
|
|
109
|
+
* Determines if custom user input that does not exist in options can be added.
|
|
110
|
+
* @defaultValue false
|
|
111
|
+
*/
|
|
112
|
+
createItem?: boolean | 'always' | { position?: 'top' | 'bottom', when?: 'empty' | 'always' }
|
|
113
|
+
/**
|
|
114
|
+
* Fields to filter items by.
|
|
115
|
+
* @defaultValue [labelKey]
|
|
116
|
+
*/
|
|
117
|
+
filterFields?: string[]
|
|
118
|
+
/**
|
|
119
|
+
* When `true`, disable the default filters, useful for custom filtering (useAsyncData, useFetch, etc.).
|
|
120
|
+
* @defaultValue false
|
|
121
|
+
*/
|
|
122
|
+
ignoreFilter?: boolean
|
|
123
|
+
class?: any
|
|
124
|
+
b24ui?: PartialString<typeof inputMenu.slots>
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export type InputMenuEmits<T, V, M extends boolean> = Omit<ComboboxRootEmits<T>, 'update:modelValue'> & {
|
|
128
|
+
change: [payload: Event]
|
|
129
|
+
blur: [payload: FocusEvent]
|
|
130
|
+
focus: [payload: FocusEvent]
|
|
131
|
+
create: [item: string]
|
|
132
|
+
} & SelectModelValueEmits<T, V, M>
|
|
133
|
+
|
|
134
|
+
type SlotProps<T> = (props: { item: T, index: number }) => any
|
|
135
|
+
|
|
136
|
+
export interface InputMenuSlots<T, M extends boolean> {
|
|
137
|
+
'leading'(props: { modelValue?: M extends true ? T[] : T, open: boolean, b24ui: any }): any
|
|
138
|
+
'trailing'(props: { modelValue?: M extends true ? T[] : T, open: boolean, b24ui: any }): any
|
|
139
|
+
'empty'(props: { searchTerm?: string }): any
|
|
140
|
+
'item': SlotProps<T>
|
|
141
|
+
'item-leading': SlotProps<T>
|
|
142
|
+
'item-label': SlotProps<T>
|
|
143
|
+
'item-trailing': SlotProps<T>
|
|
144
|
+
'tags-item-text': SlotProps<T>
|
|
145
|
+
'tags-item-delete': SlotProps<T>
|
|
146
|
+
'create-item-label'(props: { item: string }): any
|
|
147
|
+
}
|
|
148
|
+
</script>
|
|
149
|
+
|
|
150
|
+
<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">
|
|
151
|
+
import { computed, ref, toRef, onMounted, toRaw } from 'vue'
|
|
152
|
+
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'
|
|
153
|
+
import { defu } from 'defu'
|
|
154
|
+
import { isEqual } from 'ohash'
|
|
155
|
+
import { reactivePick, createReusableTemplate } from '@vueuse/core'
|
|
156
|
+
import { useButtonGroup } from '../composables/useButtonGroup'
|
|
157
|
+
import { useComponentIcons } from '../composables/useComponentIcons'
|
|
158
|
+
import { useFormField } from '../composables/useFormField'
|
|
159
|
+
import { useLocale } from '../composables/useLocale'
|
|
160
|
+
import { get, compare } from '../utils'
|
|
161
|
+
import icons from '../dictionary/icons'
|
|
162
|
+
import B24Avatar from './Avatar.vue'
|
|
163
|
+
import B24Chip from './Chip.vue'
|
|
164
|
+
|
|
165
|
+
defineOptions({ inheritAttrs: false })
|
|
166
|
+
|
|
167
|
+
const props = withDefaults(defineProps<InputMenuProps<T, I, V, M>>(), {
|
|
168
|
+
type: 'text',
|
|
169
|
+
autofocusDelay: 0,
|
|
170
|
+
portal: true,
|
|
171
|
+
labelKey: 'label' as never
|
|
172
|
+
})
|
|
173
|
+
const emits = defineEmits<InputMenuEmits<T, V, M>>()
|
|
174
|
+
const slots = defineSlots<InputMenuSlots<T, M>>()
|
|
175
|
+
|
|
176
|
+
const searchTerm = defineModel<string>('searchTerm', { default: '' })
|
|
177
|
+
|
|
178
|
+
const { t } = useLocale()
|
|
179
|
+
const { contains } = useFilter({ sensitivity: 'base' })
|
|
180
|
+
|
|
181
|
+
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'open', 'defaultOpen', 'multiple', 'resetSearchTermOnBlur', 'highlightOnHover', 'ignoreFilter'), emits)
|
|
182
|
+
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }) as ComboboxContentProps)
|
|
183
|
+
const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)
|
|
184
|
+
|
|
185
|
+
const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, 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: icons.chevronDown })))
|
|
188
|
+
|
|
189
|
+
const inputSize = computed(() => buttonGroupSize.value || formGroupSize.value)
|
|
190
|
+
|
|
191
|
+
const isTag = computed(() => {
|
|
192
|
+
return props.tag
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
const [DefineCreateItemTemplate, ReuseCreateItemTemplate] = createReusableTemplate()
|
|
196
|
+
|
|
197
|
+
const b24ui = computed(() => inputMenu({
|
|
198
|
+
color: color.value,
|
|
199
|
+
size: inputSize?.value,
|
|
200
|
+
loading: props.loading,
|
|
201
|
+
tagColor: props.tagColor,
|
|
202
|
+
highlight: highlight.value,
|
|
203
|
+
rounded: Boolean(props.rounded),
|
|
204
|
+
noPadding: Boolean(props.noPadding),
|
|
205
|
+
noBorder: Boolean(props.noBorder),
|
|
206
|
+
underline: Boolean(props.underline),
|
|
207
|
+
leading: Boolean(isLeading.value || !!props.avatar || !!slots.leading),
|
|
208
|
+
trailing: Boolean(isTrailing.value || !!slots.trailing),
|
|
209
|
+
multiple: props.multiple,
|
|
210
|
+
buttonGroup: orientation.value
|
|
211
|
+
}))
|
|
212
|
+
|
|
213
|
+
function displayValue(value: T): string {
|
|
214
|
+
if (!props.valueKey) {
|
|
215
|
+
return value && (typeof value === 'object' ? get(value, props.labelKey as string) : value)
|
|
216
|
+
}
|
|
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)
|
|
220
|
+
}
|
|
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
|
+
|
|
226
|
+
const filteredGroups = computed(() => {
|
|
227
|
+
if (props.ignoreFilter || !searchTerm.value) {
|
|
228
|
+
return groups.value
|
|
229
|
+
}
|
|
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)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (item.type && ['label', 'separator'].includes(item.type)) {
|
|
239
|
+
return true
|
|
240
|
+
}
|
|
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
|
+
|
|
247
|
+
const createItem = computed(() => {
|
|
248
|
+
if (!props.createItem || !searchTerm.value) {
|
|
249
|
+
return false
|
|
250
|
+
}
|
|
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))
|
|
256
|
+
}
|
|
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
|
+
|
|
264
|
+
function autoFocus() {
|
|
265
|
+
if (props.autofocus) {
|
|
266
|
+
inputRef.value?.$el?.focus()
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
onMounted(() => {
|
|
271
|
+
setTimeout(() => {
|
|
272
|
+
autoFocus()
|
|
273
|
+
}, props.autofocusDelay)
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
function onUpdate(value: any) {
|
|
277
|
+
if (toRaw(props.modelValue) === value) {
|
|
278
|
+
return
|
|
279
|
+
}
|
|
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
|
+
}
|
|
286
|
+
|
|
287
|
+
function onBlur(event: FocusEvent) {
|
|
288
|
+
emits('blur', event)
|
|
289
|
+
emitFormBlur()
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function onFocus(event: FocusEvent) {
|
|
293
|
+
emits('focus', event)
|
|
294
|
+
emitFormFocus()
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function onUpdateOpen(value: boolean) {
|
|
298
|
+
if (!value) {
|
|
299
|
+
const event = new FocusEvent('blur')
|
|
300
|
+
emits('blur', event)
|
|
301
|
+
emitFormBlur()
|
|
302
|
+
} else {
|
|
303
|
+
const event = new FocusEvent('focus')
|
|
304
|
+
emits('focus', event)
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function onRemoveTag(event: any) {
|
|
309
|
+
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>)
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
defineExpose({
|
|
317
|
+
inputRef
|
|
318
|
+
})
|
|
319
|
+
</script>
|
|
320
|
+
|
|
321
|
+
<!-- eslint-disable vue/no-template-shadow -->
|
|
322
|
+
<template>
|
|
323
|
+
<DefineCreateItemTemplate>
|
|
324
|
+
<ComboboxGroup :class="b24ui.group({ addNew: true, class: props.b24ui?.group })">
|
|
325
|
+
<ComboboxItem
|
|
326
|
+
:class="b24ui.item({ addNew: true, class: props.b24ui?.item })"
|
|
327
|
+
:value="searchTerm"
|
|
328
|
+
@select.prevent="emits('create', searchTerm)"
|
|
329
|
+
>
|
|
330
|
+
<span :class="b24ui.itemLabel({ addNew: true, class: props.b24ui?.itemLabel })">
|
|
331
|
+
<slot name="create-item-label" :item="searchTerm">
|
|
332
|
+
<Component
|
|
333
|
+
:is="icons.plus"
|
|
334
|
+
:class="b24ui.itemLeadingIcon({ addNew: true, class: props.b24ui?.itemLeadingIcon })"
|
|
335
|
+
/>
|
|
336
|
+
{{ t('inputMenu.create', { label: searchTerm }) }}
|
|
337
|
+
</slot>
|
|
338
|
+
</span>
|
|
339
|
+
</ComboboxItem>
|
|
340
|
+
</ComboboxGroup>
|
|
341
|
+
</DefineCreateItemTemplate>
|
|
342
|
+
|
|
343
|
+
<ComboboxRoot
|
|
344
|
+
:id="id"
|
|
345
|
+
v-slot="{ modelValue, open }"
|
|
346
|
+
v-bind="rootProps"
|
|
347
|
+
:name="name"
|
|
348
|
+
:disabled="disabled"
|
|
349
|
+
:class="b24ui.root({ class: [props.class, props.b24ui?.root] })"
|
|
350
|
+
:as-child="!!multiple"
|
|
351
|
+
ignore-filter
|
|
352
|
+
@update:model-value="onUpdate"
|
|
353
|
+
@update:open="onUpdateOpen"
|
|
354
|
+
@keydown.enter="$event.preventDefault()"
|
|
355
|
+
>
|
|
356
|
+
<div v-if="isTag" :class="b24ui.tag({ class: props.b24ui?.tag })">
|
|
357
|
+
{{ props.tag }}
|
|
358
|
+
</div>
|
|
359
|
+
<ComboboxAnchor :as-child="!multiple" :class="b24ui.base({ class: props.b24ui?.base })">
|
|
360
|
+
<TagsInputRoot
|
|
361
|
+
v-if="multiple"
|
|
362
|
+
v-slot="{ modelValue: tags }"
|
|
363
|
+
:model-value="(modelValue as string[])"
|
|
364
|
+
:disabled="disabled"
|
|
365
|
+
delimiter=""
|
|
366
|
+
as-child
|
|
367
|
+
@blur="onBlur"
|
|
368
|
+
@focus="onFocus"
|
|
369
|
+
@remove-tag="onRemoveTag"
|
|
370
|
+
>
|
|
371
|
+
<TagsInputItem v-for="(item, index) in tags" :key="index" :value="(item as string)" :class="b24ui.tagsItem({ class: props.b24ui?.tagsItem })">
|
|
372
|
+
<TagsInputItemText :class="b24ui.tagsItemText({ class: props.b24ui?.tagsItemText })">
|
|
373
|
+
<slot name="tags-item-text" :item="(item as T)" :index="index">
|
|
374
|
+
{{ displayValue(item as T) }}
|
|
375
|
+
</slot>
|
|
376
|
+
</TagsInputItemText>
|
|
377
|
+
|
|
378
|
+
<TagsInputItemDelete :class="b24ui.tagsItemDelete({ class: props.b24ui?.tagsItemDelete })" :disabled="disabled">
|
|
379
|
+
<slot name="tags-item-delete" :item="(item as T)" :index="index">
|
|
380
|
+
<Component
|
|
381
|
+
:is="deleteIcon || icons.close"
|
|
382
|
+
:class="b24ui.tagsItemDeleteIcon({ class: props.b24ui?.tagsItemDeleteIcon })"
|
|
383
|
+
/>
|
|
384
|
+
</slot>
|
|
385
|
+
</TagsInputItemDelete>
|
|
386
|
+
</TagsInputItem>
|
|
387
|
+
|
|
388
|
+
<ComboboxInput v-model="searchTerm" :display-value="displayValue" as-child>
|
|
389
|
+
<TagsInputInput
|
|
390
|
+
ref="inputRef"
|
|
391
|
+
v-bind="{ ...$attrs, ...ariaAttrs }"
|
|
392
|
+
:placeholder="placeholder"
|
|
393
|
+
:required="required"
|
|
394
|
+
:class="b24ui.tagsInput({ class: props.b24ui?.tagsInput })"
|
|
395
|
+
@keydown.enter.prevent
|
|
396
|
+
/>
|
|
397
|
+
</ComboboxInput>
|
|
398
|
+
</TagsInputRoot>
|
|
399
|
+
|
|
400
|
+
<ComboboxInput
|
|
401
|
+
v-else
|
|
402
|
+
ref="inputRef"
|
|
403
|
+
v-model="searchTerm"
|
|
404
|
+
:display-value="displayValue"
|
|
405
|
+
v-bind="{ ...$attrs, ...ariaAttrs }"
|
|
406
|
+
:type="type"
|
|
407
|
+
:placeholder="placeholder"
|
|
408
|
+
:required="required"
|
|
409
|
+
@blur="onBlur"
|
|
410
|
+
@focus="onFocus"
|
|
411
|
+
/>
|
|
412
|
+
|
|
413
|
+
<span v-if="isLeading || !!avatar || !!slots.leading" :class="b24ui.leading({ class: props.b24ui?.leading })">
|
|
414
|
+
<slot name="leading" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :b24ui="b24ui">
|
|
415
|
+
<Component
|
|
416
|
+
:is="leadingIconName"
|
|
417
|
+
v-if="isLeading && leadingIconName"
|
|
418
|
+
:class="b24ui.leadingIcon({ class: props.b24ui?.leadingIcon })"
|
|
419
|
+
/>
|
|
420
|
+
<B24Avatar v-else-if="!!avatar" :size="((props.b24ui?.itemLeadingAvatarSize || b24ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="b24ui.itemLeadingAvatar({ class: props.b24ui?.itemLeadingAvatar })" />
|
|
421
|
+
</slot>
|
|
422
|
+
</span>
|
|
423
|
+
|
|
424
|
+
<ComboboxTrigger v-if="isTrailing || !!slots.trailing" :class="b24ui.trailing({ class: props.b24ui?.trailing })">
|
|
425
|
+
<slot name="trailing" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :b24ui="b24ui">
|
|
426
|
+
<Component
|
|
427
|
+
:is="trailingIconName"
|
|
428
|
+
v-if="trailingIconName"
|
|
429
|
+
:class="b24ui.trailingIcon({ class: props.b24ui?.trailingIcon })"
|
|
430
|
+
/>
|
|
431
|
+
</slot>
|
|
432
|
+
</ComboboxTrigger>
|
|
433
|
+
</ComboboxAnchor>
|
|
434
|
+
|
|
435
|
+
<ComboboxPortal :disabled="!portal">
|
|
436
|
+
<ComboboxContent :class="b24ui.content({ class: props.b24ui?.content })" v-bind="contentProps">
|
|
437
|
+
<ComboboxEmpty :class="b24ui.empty({ class: props.b24ui?.empty })">
|
|
438
|
+
<slot name="empty" :search-term="searchTerm">
|
|
439
|
+
{{ searchTerm ? t('inputMenu.noMatch', { searchTerm }) : t('inputMenu.noData') }}
|
|
440
|
+
</slot>
|
|
441
|
+
</ComboboxEmpty>
|
|
442
|
+
|
|
443
|
+
<ComboboxViewport :class="b24ui.viewport({ class: props.b24ui?.viewport })">
|
|
444
|
+
<ReuseCreateItemTemplate v-if="createItem && createItemPosition === 'top'" />
|
|
445
|
+
|
|
446
|
+
<ComboboxGroup v-for="(group, groupIndex) in filteredGroups" :key="`group-${groupIndex}`" :class="b24ui.group({ class: props.b24ui?.group })">
|
|
447
|
+
<template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
|
|
448
|
+
<ComboboxLabel v-if="item?.type === 'label'" :class="b24ui.label({ class: props.b24ui?.label })">
|
|
449
|
+
{{ get(item, props.labelKey as string) }}
|
|
450
|
+
</ComboboxLabel>
|
|
451
|
+
|
|
452
|
+
<ComboboxSeparator v-else-if="item?.type === 'separator'" :class="b24ui.separator({ class: props.b24ui?.separator })" />
|
|
453
|
+
|
|
454
|
+
<ComboboxItem
|
|
455
|
+
v-else
|
|
456
|
+
:class="b24ui.item({ class: props.b24ui?.item })"
|
|
457
|
+
:disabled="item.disabled"
|
|
458
|
+
:value="valueKey && typeof item === 'object' ? get(item, props.valueKey as string) : item"
|
|
459
|
+
@select="item.onSelect"
|
|
460
|
+
>
|
|
461
|
+
<slot name="item" :item="(item as T)" :index="index">
|
|
462
|
+
<slot name="item-leading" :item="(item as T)" :index="index">
|
|
463
|
+
<Component
|
|
464
|
+
:is="item.icon"
|
|
465
|
+
v-if="item.icon"
|
|
466
|
+
:class="b24ui.itemLeadingIcon({ class: props.b24ui?.itemLeadingIcon })"
|
|
467
|
+
/>
|
|
468
|
+
<B24Avatar v-else-if="item.avatar" :size="((props.b24ui?.itemLeadingAvatarSize || b24ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="b24ui.itemLeadingAvatar({ class: props.b24ui?.itemLeadingAvatar })" />
|
|
469
|
+
<B24Chip
|
|
470
|
+
v-else-if="item.chip"
|
|
471
|
+
:size="((props.b24ui?.itemLeadingChipSize || b24ui.itemLeadingChipSize()) as ChipProps['size'])"
|
|
472
|
+
inset
|
|
473
|
+
standalone
|
|
474
|
+
v-bind="item.chip"
|
|
475
|
+
:class="b24ui.itemLeadingChip({ class: props.b24ui?.itemLeadingChip })"
|
|
476
|
+
/>
|
|
477
|
+
</slot>
|
|
478
|
+
|
|
479
|
+
<span :class="b24ui.itemLabel({ class: props.b24ui?.itemLabel })">
|
|
480
|
+
<slot name="item-label" :item="(item as T)" :index="index">
|
|
481
|
+
{{ typeof item === 'object' ? get(item, props.labelKey as string) : item }}
|
|
482
|
+
</slot>
|
|
483
|
+
</span>
|
|
484
|
+
|
|
485
|
+
<span :class="b24ui.itemTrailing({ class: props.b24ui?.itemTrailing })">
|
|
486
|
+
<slot name="item-trailing" :item="(item as T)" :index="index" />
|
|
487
|
+
|
|
488
|
+
<ComboboxItemIndicator as-child>
|
|
489
|
+
<Component
|
|
490
|
+
:is="selectedIcon || icons.check"
|
|
491
|
+
:class="b24ui.itemTrailingIcon({ class: props.b24ui?.itemTrailingIcon })"
|
|
492
|
+
/>
|
|
493
|
+
</ComboboxItemIndicator>
|
|
494
|
+
</span>
|
|
495
|
+
</slot>
|
|
496
|
+
</ComboboxItem>
|
|
497
|
+
</template>
|
|
498
|
+
</ComboboxGroup>
|
|
499
|
+
|
|
500
|
+
<ReuseCreateItemTemplate v-if="createItem && createItemPosition === 'bottom'" />
|
|
501
|
+
</ComboboxViewport>
|
|
502
|
+
|
|
503
|
+
<ComboboxArrow v-if="!!arrow" v-bind="arrowProps" :class="b24ui.arrow({ class: props.b24ui?.arrow })" />
|
|
504
|
+
</ComboboxContent>
|
|
505
|
+
</ComboboxPortal>
|
|
506
|
+
</ComboboxRoot>
|
|
507
|
+
</template>
|