@bitrix24/b24ui-nuxt 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const defu = require('defu');
4
4
  const kit = require('@nuxt/kit');
5
- const templates = require('./shared/b24ui-nuxt.D8eTlsIC.cjs');
5
+ const templates = require('./shared/b24ui-nuxt.ZUYaG6CJ.cjs');
6
6
  require('node:url');
7
7
  require('scule');
8
8
 
package/dist/module.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "nuxt": ">=3.13.1"
6
6
  },
7
7
  "docs": "https://bitrix24.github.io/b24ui/guide/installation-nuxt-app.html",
8
- "version": "0.1.5",
8
+ "version": "0.1.7",
9
9
  "builder": {
10
10
  "@nuxt/module-builder": "0.8.4",
11
11
  "unbuild": "2.0.0"
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.CGBDJv97.mjs';
3
+ import { d as defaultOptions, a as getDefaultUiConfig, b as addTemplates } from './shared/b24ui-nuxt.vQRZieQw.mjs';
4
4
  import 'node:url';
5
5
  import 'scule';
6
6
 
@@ -44,9 +44,8 @@ provide(localeContextInjectionKey, locale)
44
44
  <slot />
45
45
  </B24Toaster>
46
46
  <slot v-else />
47
+ <!-- B24ModalProvider / -->
48
+ <!-- B24SlideoverProvider / -->
47
49
  </TooltipProvider>
48
-
49
- <!-- B24ModalProvider / -->
50
- <!-- B24SlideoverProvider / -->
51
50
  </ConfigProvider>
52
51
  </template>
@@ -44,6 +44,7 @@ export interface BadgeSlots {
44
44
  <script setup lang="ts">
45
45
  import { computed } from 'vue'
46
46
  import { Primitive } from 'reka-ui'
47
+ import { useButtonGroup } from '../composables/useButtonGroup'
47
48
  import { useComponentIcons } from '../composables/useComponentIcons'
48
49
  import Cross20Icon from '@bitrix24/b24icons-vue/actions/Cross20Icon'
49
50
  import B24Avatar from './Avatar.vue'
@@ -61,12 +62,14 @@ async function onCloseClickWrapper(event: MouseEvent) {
61
62
  } finally { /* empty */ }
62
63
  }
63
64
 
65
+ const { orientation, size: buttonGroupSize } = useButtonGroup<BadgeProps>(props)
64
66
  const { isLeading, leadingIconName } = useComponentIcons(props)
65
67
 
66
68
  const b24ui = computed(() => badge({
67
69
  color: props.color,
68
70
  depth: props.depth,
69
- size: props.size,
71
+ size: buttonGroupSize.value || props.size,
72
+ buttonGroup: orientation.value,
70
73
  useLink: Boolean(props.useLink),
71
74
  useClose: Boolean(props.useClose),
72
75
  useFill: Boolean(props.useFill),
@@ -130,10 +130,8 @@ const b24ui = computed(() => button({
130
130
  @click="onClickWrapper"
131
131
  >
132
132
  <div
133
+ v-if="isLoading"
133
134
  class="h-full w-full absolute inset-0 flex flex-row flex-nowrap items-center justify-center"
134
- :class="[
135
- isLoading ? 'visible' : 'invisible'
136
- ]"
137
135
  >
138
136
  <LoaderWaitIcon v-if="useWait" class="size-2xl" aria-hidden="true" />
139
137
  <LoaderClockIcon v-else-if="useClock" class="size-2xl" aria-hidden="true" />
@@ -52,7 +52,7 @@ provide(buttonGroupInjectionKey, computed(() => ({
52
52
  </script>
53
53
 
54
54
  <template>
55
- <Primitive :as="as" :class="buttonGroup({ orientation, class: props.class })">
55
+ <Primitive :as="as" class="group/items is-button-group" :class="buttonGroup({ orientation, class: props.class })">
56
56
  <slot />
57
57
  </Primitive>
58
58
  </template>
@@ -0,0 +1,465 @@
1
+ <script lang="ts">
2
+ import type { VariantProps } from 'tailwind-variants'
3
+ import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxArrowProps, AcceptableValue } from 'reka-ui'
4
+ import type { AppConfig } from '@nuxt/schema'
5
+ import _appConfig from '#build/app.config'
6
+ import theme from '#build/b24ui/select-menu'
7
+ import type { UseComponentIconsProps } from '../composables/useComponentIcons'
8
+ import { tv } from '../utils/tv'
9
+ import type { AvatarProps, ChipProps, InputProps, IconComponent } from '../types'
10
+ import type { PartialString, MaybeArrayOfArray, MaybeArrayOfArrayItem, SelectModelValue, SelectModelValueEmits, SelectItemKey } from '../types/utils'
11
+
12
+ const appConfigSelectMenu = _appConfig as AppConfig & { b24ui: { selectMenu: Partial<typeof theme> } }
13
+
14
+ const selectMenu = tv({ extend: tv(theme), ...(appConfigSelectMenu.b24ui?.selectMenu || {}) })
15
+
16
+ export interface SelectMenuItem {
17
+ label?: string
18
+ icon?: IconComponent
19
+ avatar?: AvatarProps
20
+ chip?: ChipProps
21
+ /**
22
+ * The item type.
23
+ * @defaultValue 'item'
24
+ */
25
+ type?: 'label' | 'separator' | 'item'
26
+ disabled?: boolean
27
+ onSelect?(e?: Event): void
28
+ }
29
+
30
+ type SelectMenuVariants = VariantProps<typeof selectMenu>
31
+
32
+ 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 {
33
+ id?: string
34
+ /** The placeholder text when the select is empty. */
35
+ placeholder?: string
36
+ /**
37
+ * Whether to display the search input or not.
38
+ * Can be an object to pass additional props to the input.
39
+ * `{ placeholder: 'Search...', type: 'search' }`{lang="ts-type"}
40
+ * @defaultValue true
41
+ */
42
+ searchInput?: boolean | InputProps
43
+ color?: SelectMenuVariants['color']
44
+ size?: SelectMenuVariants['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
+ tag?: string
54
+ tagColor?: SelectMenuVariants['tagColor']
55
+ required?: boolean
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 content of the menu.
68
+ * @defaultValue { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }
69
+ */
70
+ content?: Omit<ComboboxContentProps, 'as' | 'asChild' | 'forceMount'>
71
+ /**
72
+ * Display an arrow alongside the menu.
73
+ * @defaultValue false
74
+ */
75
+ arrow?: boolean | Omit<ComboboxArrowProps, 'as' | 'asChild'>
76
+ /**
77
+ * Render the menu in a portal.
78
+ * @defaultValue true
79
+ */
80
+ portal?: boolean
81
+ /**
82
+ * When `items` is an array of objects, select the field to use as the value instead of the object itself.
83
+ * @defaultValue undefined
84
+ */
85
+ valueKey?: V
86
+ /**
87
+ * When `items` is an array of objects, select the field to use as the label.
88
+ * @defaultValue 'label'
89
+ */
90
+ labelKey?: V
91
+ items?: I
92
+ /** The value of the SelectMenu when initially rendered. Use when you do not need to control the state of the SelectMenu. */
93
+ defaultValue?: SelectModelValue<T, V, M>
94
+ /** The controlled value of the SelectMenu. Can be binded-with with `v-model`. */
95
+ modelValue?: SelectModelValue<T, V, M>
96
+ /** Whether multiple options can be selected or not. */
97
+ multiple?: M & boolean
98
+ /** Highlight the ring color like a focus state. */
99
+ highlight?: boolean
100
+ /**
101
+ * Determines if custom user input that does not exist in options can be added.
102
+ * @defaultValue false
103
+ */
104
+ createItem?: boolean | 'always' | { position?: 'top' | 'bottom', when?: 'empty' | 'always' }
105
+ /**
106
+ * Fields to filter items by.
107
+ * @defaultValue [labelKey]
108
+ */
109
+ filterFields?: string[]
110
+ /**
111
+ * When `true`, disable the default filters, useful for custom filtering (useAsyncData, useFetch, etc.).
112
+ * @defaultValue false
113
+ */
114
+ ignoreFilter?: boolean
115
+ class?: any
116
+ b24ui?: PartialString<typeof selectMenu.slots>
117
+ }
118
+
119
+ export type SelectMenuEmits<T, V, M extends boolean> = Omit<ComboboxRootEmits<T>, 'update:modelValue'> & {
120
+ change: [payload: Event]
121
+ blur: [payload: FocusEvent]
122
+ focus: [payload: FocusEvent]
123
+ create: [item: string]
124
+ } & SelectModelValueEmits<T, V, M>
125
+
126
+ type SlotProps<T> = (props: { item: T, index: number }) => any
127
+
128
+ export interface SelectMenuSlots<T, M extends boolean> {
129
+ 'leading'(props: { modelValue?: M extends true ? T[] : T, open: boolean, b24ui: any }): any
130
+ 'default'(props: { modelValue?: M extends true ? T[] : T, open: boolean }): any
131
+ 'trailing'(props: { modelValue?: M extends true ? T[] : T, open: boolean, b24ui: any }): any
132
+ 'empty'(props: { searchTerm?: string }): any
133
+ 'item': SlotProps<T>
134
+ 'item-leading': SlotProps<T>
135
+ 'item-label': SlotProps<T>
136
+ 'item-trailing': SlotProps<T>
137
+ 'create-item-label'(props: { item: string }): any
138
+ }
139
+ </script>
140
+
141
+ <script setup lang="ts" generic="T extends MaybeArrayOfArrayItem<I>, I extends MaybeArrayOfArray<SelectMenuItem | AcceptableValue | boolean> = MaybeArrayOfArray<SelectMenuItem | AcceptableValue | boolean>, V extends SelectItemKey<T> | undefined = undefined, M extends boolean = false">
142
+ import { computed, toRef, toRaw } from 'vue'
143
+ import {
144
+ ComboboxRoot,
145
+ ComboboxArrow,
146
+ ComboboxAnchor,
147
+ ComboboxInput,
148
+ ComboboxTrigger,
149
+ ComboboxPortal,
150
+ ComboboxContent,
151
+ ComboboxViewport,
152
+ ComboboxEmpty,
153
+ ComboboxGroup,
154
+ ComboboxLabel,
155
+ ComboboxSeparator,
156
+ ComboboxItem,
157
+ ComboboxItemIndicator,
158
+ useForwardPropsEmits,
159
+ useFilter,
160
+ Primitive
161
+ } from 'reka-ui'
162
+ import { defu } from 'defu'
163
+ import { reactivePick, createReusableTemplate } from '@vueuse/core'
164
+ import { useButtonGroup } from '../composables/useButtonGroup'
165
+ import { useComponentIcons } from '../composables/useComponentIcons'
166
+ import { useFormField } from '../composables/useFormField'
167
+ import { useLocale } from '../composables/useLocale'
168
+ import { get, compare } from '../utils'
169
+ import icons from '../dictionary/icons'
170
+ import B24Avatar from './Avatar.vue'
171
+ import B24Chip from './Chip.vue'
172
+ import B24Input from './Input.vue'
173
+
174
+ const props = withDefaults(defineProps<SelectMenuProps<T, I, V, M>>(), {
175
+ portal: true,
176
+ searchInput: true,
177
+ labelKey: 'label' as never,
178
+ resetSearchTermOnBlur: true
179
+ })
180
+ const emits = defineEmits<SelectMenuEmits<T, V, M>>()
181
+ const slots = defineSlots<SelectMenuSlots<T, M>>()
182
+
183
+ const searchTerm = defineModel<string>('searchTerm', { default: '' })
184
+
185
+ const { t } = useLocale()
186
+ const { contains } = useFilter({ sensitivity: 'base' })
187
+
188
+ const rootProps = useForwardPropsEmits(reactivePick(props, 'modelValue', 'defaultValue', 'open', 'defaultOpen', 'multiple', 'resetSearchTermOnBlur', 'highlightOnHover'), emits)
189
+ const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8, position: 'popper' }) as ComboboxContentProps)
190
+ const arrowProps = toRef(() => props.arrow as ComboboxArrowProps)
191
+ const searchInputProps = toRef(() => defu(props.searchInput, { placeholder: t('selectMenu.search'), type: 'search' }) as InputProps)
192
+
193
+ const { emitFormBlur, emitFormFocus, emitFormInput, emitFormChange, 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(
196
+ props,
197
+ { trailingIcon: icons.chevronDown }
198
+ )))
199
+
200
+ const selectSize = computed(() => buttonGroupSize.value || formGroupSize.value)
201
+
202
+ const [DefineCreateItemTemplate, ReuseCreateItemTemplate] = createReusableTemplate()
203
+
204
+ const isTag = computed(() => {
205
+ return props.tag
206
+ })
207
+
208
+ const b24ui = computed(() => selectMenu({
209
+ color: color.value,
210
+ size: selectSize?.value,
211
+ loading: props.loading,
212
+ rounded: Boolean(props.rounded),
213
+ noPadding: Boolean(props.noPadding),
214
+ noBorder: Boolean(props.noBorder),
215
+ underline: Boolean(props.underline),
216
+ highlight: highlight.value,
217
+ tagColor: props.tagColor,
218
+ leading: Boolean(isLeading.value || !!props.avatar || !!slots.leading),
219
+ trailing: Boolean(isTrailing.value || !!slots.trailing),
220
+ buttonGroup: orientation.value
221
+ }))
222
+
223
+ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0]) ? props.items : [props.items]) as SelectMenuItem[][] : [])
224
+ // eslint-disable-next-line vue/no-dupe-keys
225
+ const items = computed(() => groups.value.flatMap(group => group) as T[])
226
+
227
+ function displayValue(value: T | T[]): string {
228
+ if (props.multiple && Array.isArray(value)) {
229
+ return value.map(v => displayValue(v)).filter(Boolean).join(', ')
230
+ }
231
+
232
+ if (!props.valueKey) {
233
+ return value && (typeof value === 'object' ? get(value, props.labelKey as string) : value)
234
+ }
235
+
236
+ const item = items.value.find(item => compare(typeof item === 'object' ? get(item as Record<string, any>, props.valueKey as string) : item, value))
237
+ return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
238
+ }
239
+
240
+ const filteredGroups = computed(() => {
241
+ if (props.ignoreFilter || !searchTerm.value) {
242
+ return groups.value
243
+ }
244
+
245
+ const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey] as string[]
246
+
247
+ return groups.value.map(items => items.filter((item) => {
248
+ if (typeof item !== 'object') {
249
+ return contains(item, searchTerm.value)
250
+ }
251
+
252
+ if (item.type && ['label', 'separator'].includes(item.type)) {
253
+ return true
254
+ }
255
+
256
+ return fields.some(field => contains(get(item, field), searchTerm.value))
257
+ })).filter(group => group.filter(item => !item.type || !['label', 'separator'].includes(item.type)).length > 0)
258
+ })
259
+ const filteredItems = computed(() => filteredGroups.value.flatMap(group => group) as T[])
260
+
261
+ const createItem = computed(() => {
262
+ if (!props.createItem || !searchTerm.value) {
263
+ return false
264
+ }
265
+
266
+ const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } as T : searchTerm.value
267
+
268
+ if ((typeof props.createItem === 'object' && props.createItem.when === 'always') || props.createItem === 'always') {
269
+ return !filteredItems.value.find(item => compare(item, newItem, props.valueKey))
270
+ }
271
+
272
+ return !filteredItems.value.length
273
+ })
274
+ const createItemPosition = computed(() => typeof props.createItem === 'object' ? props.createItem.position : 'bottom')
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
+
284
+ emitFormChange()
285
+ emitFormInput()
286
+ }
287
+
288
+ function onUpdateOpen(value: boolean) {
289
+ let timeoutId
290
+
291
+ if (!value) {
292
+ const event = new FocusEvent('blur')
293
+
294
+ emits('blur', event)
295
+ emitFormBlur()
296
+
297
+ // Since we use `displayValue` prop inside ComboboxInput we should reset searchTerm manually
298
+ // https://reka-ui.com/docs/components/combobox#api-reference
299
+ if (props.resetSearchTermOnBlur) {
300
+ const STATE_ANIMATION_DELAY_MS = 100
301
+
302
+ timeoutId = setTimeout(() => {
303
+ searchTerm.value = ''
304
+ }, STATE_ANIMATION_DELAY_MS)
305
+ }
306
+ } else {
307
+ const event = new FocusEvent('focus')
308
+ emits('focus', event)
309
+ emitFormFocus()
310
+ clearTimeout(timeoutId)
311
+ }
312
+ }
313
+ </script>
314
+
315
+ <!-- eslint-disable vue/no-template-shadow -->
316
+ <template>
317
+ <Primitive as="div" :class="b24ui.root({ addNew: true, class: [props.b24ui?.root] })">
318
+ <DefineCreateItemTemplate>
319
+ <ComboboxGroup :class="b24ui.group({ addNew: true, class: props.b24ui?.group })">
320
+ <ComboboxItem
321
+ :class="b24ui.item({ addNew: true, class: props.b24ui?.item })"
322
+ :value="searchTerm"
323
+ @select.prevent="emits('create', searchTerm)"
324
+ >
325
+ <span :class="b24ui.itemLabel({ addNew: true, class: props.b24ui?.itemLabel })">
326
+ <slot name="create-item-label" :item="searchTerm">
327
+ <Component
328
+ :is="icons.plus"
329
+ :class="b24ui.itemLeadingIcon({ addNew: true, class: props.b24ui?.itemLeadingIcon })"
330
+ />
331
+ {{ t('selectMenu.create', { label: searchTerm }) }}
332
+ </slot>
333
+ </span>
334
+ </ComboboxItem>
335
+ </ComboboxGroup>
336
+ </DefineCreateItemTemplate>
337
+
338
+ <ComboboxRoot
339
+ :id="id"
340
+ v-slot="{ modelValue, open }"
341
+ v-bind="{ ...rootProps, ...ariaAttrs }"
342
+ ignore-filter
343
+ as-child
344
+ :name="name"
345
+ :disabled="disabled"
346
+ @update:model-value="onUpdate"
347
+ @update:open="onUpdateOpen"
348
+ >
349
+ <ComboboxAnchor as-child>
350
+ <ComboboxTrigger :class="b24ui.base({ class: [props.class, props.b24ui?.base] })" tabindex="0">
351
+ <div v-if="isTag" :class="b24ui.tag({ class: props.b24ui?.tag })">
352
+ {{ props.tag }}
353
+ </div>
354
+ <span v-if="isLeading || !!avatar || !!slots.leading" :class="b24ui.leading({ class: props.b24ui?.leading })">
355
+ <slot name="leading" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :b24ui="b24ui">
356
+ <Component
357
+ :is="leadingIconName"
358
+ v-if="isLeading && leadingIconName"
359
+ :class="b24ui.leadingIcon({ class: props.b24ui?.leadingIcon })"
360
+ />
361
+ <B24Avatar v-else-if="!!avatar" :size="((props.b24ui?.itemLeadingAvatarSize || b24ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="b24ui.itemLeadingAvatar({ class: props.b24ui?.itemLeadingAvatar })" />
362
+ </slot>
363
+ </span>
364
+
365
+ <slot :model-value="(modelValue as M extends true ? T[] : T)" :open="open">
366
+ <template v-for="displayedModelValue in [displayValue(modelValue as M extends true ? T[] : T)]" :key="displayedModelValue">
367
+ <span v-if="displayedModelValue" :class="b24ui.value({ class: props.b24ui?.value })">
368
+ {{ displayedModelValue }}
369
+ </span>
370
+ <span v-else :class="b24ui.placeholder({ class: props.b24ui?.placeholder })">
371
+ {{ placeholder ?? '&nbsp;' }}
372
+ </span>
373
+ </template>
374
+ </slot>
375
+
376
+ <span v-if="isTrailing || !!slots.trailing" :class="b24ui.trailing({ class: props.b24ui?.trailing })">
377
+ <slot name="trailing" :model-value="(modelValue as M extends true ? T[] : T)" :open="open" :b24ui="b24ui">
378
+ <Component
379
+ :is="trailingIconName"
380
+ v-if="trailingIconName"
381
+ :class="b24ui.trailingIcon({ class: props.b24ui?.trailingIcon })"
382
+ />
383
+ </slot>
384
+ </span>
385
+ </ComboboxTrigger>
386
+ </ComboboxAnchor>
387
+
388
+ <ComboboxPortal :disabled="!portal">
389
+ <ComboboxContent :class="b24ui.content({ class: props.b24ui?.content })" v-bind="contentProps">
390
+ <ComboboxInput v-if="!!searchInput" v-model="searchTerm" :display-value="() => searchTerm" as-child>
391
+ <B24Input no-border autofocus autocomplete="off" v-bind="searchInputProps" :class="b24ui.input({ class: props.b24ui?.input })" />
392
+ </ComboboxInput>
393
+
394
+ <ComboboxEmpty :class="b24ui.empty({ class: props.b24ui?.empty })">
395
+ <slot name="empty" :search-term="searchTerm">
396
+ {{ searchTerm ? t('selectMenu.noMatch', { searchTerm }) : t('selectMenu.noData') }}
397
+ </slot>
398
+ </ComboboxEmpty>
399
+
400
+ <ComboboxViewport :class="b24ui.viewport({ class: props.b24ui?.viewport })">
401
+ <ReuseCreateItemTemplate v-if="createItem && createItemPosition === 'top'" />
402
+
403
+ <ComboboxGroup v-for="(group, groupIndex) in filteredGroups" :key="`group-${groupIndex}`" :class="b24ui.group({ class: props.b24ui?.group })">
404
+ <template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
405
+ <ComboboxLabel v-if="item?.type === 'label'" :class="b24ui.label({ class: props.b24ui?.label })">
406
+ {{ get(item, props.labelKey as string) }}
407
+ </ComboboxLabel>
408
+
409
+ <ComboboxSeparator v-else-if="item?.type === 'separator'" :class="b24ui.separator({ class: props.b24ui?.separator })" />
410
+
411
+ <ComboboxItem
412
+ v-else
413
+ :class="b24ui.item({ class: props.b24ui?.item })"
414
+ :disabled="item.disabled"
415
+ :value="valueKey && typeof item === 'object' ? get(item, props.valueKey as string) : item"
416
+ @select="item.onSelect"
417
+ >
418
+ <slot name="item" :item="(item as T)" :index="index">
419
+ <slot name="item-leading" :item="(item as T)" :index="index">
420
+ <Component
421
+ :is="item.icon"
422
+ v-if="item.icon"
423
+ :class="b24ui.itemLeadingIcon({ class: props.b24ui?.itemLeadingIcon })"
424
+ />
425
+ <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 })" />
426
+ <B24Chip
427
+ v-else-if="item.chip"
428
+ :size="((props.b24ui?.itemLeadingChipSize || b24ui.itemLeadingChipSize()) as ChipProps['size'])"
429
+ inset
430
+ standalone
431
+ v-bind="item.chip"
432
+ :class="b24ui.itemLeadingChip({ class: props.b24ui?.itemLeadingChip })"
433
+ />
434
+ </slot>
435
+
436
+ <span :class="b24ui.itemLabel({ class: props.b24ui?.itemLabel })">
437
+ <slot name="item-label" :item="(item as T)" :index="index">
438
+ {{ typeof item === 'object' ? get(item, props.labelKey as string) : item }}
439
+ </slot>
440
+ </span>
441
+
442
+ <span :class="b24ui.itemTrailing({ class: props.b24ui?.itemTrailing })">
443
+ <slot name="item-trailing" :item="(item as T)" :index="index" />
444
+
445
+ <ComboboxItemIndicator as-child>
446
+ <Component
447
+ :is="selectedIcon || icons.check"
448
+ :class="b24ui.itemTrailingIcon({ class: props.b24ui?.itemTrailingIcon })"
449
+ />
450
+ </ComboboxItemIndicator>
451
+ </span>
452
+ </slot>
453
+ </ComboboxItem>
454
+ </template>
455
+ </ComboboxGroup>
456
+
457
+ <ReuseCreateItemTemplate v-if="createItem && createItemPosition === 'bottom'" />
458
+ </ComboboxViewport>
459
+
460
+ <ComboboxArrow v-if="!!arrow" v-bind="arrowProps" :class="b24ui.arrow({ class: props.b24ui?.arrow })" />
461
+ </ComboboxContent>
462
+ </ComboboxPortal>
463
+ </ComboboxRoot>
464
+ </Primitive>
465
+ </template>
@@ -15,6 +15,6 @@ export interface UseComponentIconsProps {
15
15
  export declare function useComponentIcons(componentProps: MaybeRefOrGetter<UseComponentIconsProps>): {
16
16
  isLeading: import("vue").ComputedRef<any>;
17
17
  isTrailing: import("vue").ComputedRef<boolean>;
18
- leadingIconName: import("vue").ComputedRef<IconComponent | undefined>;
19
- trailingIconName: import("vue").ComputedRef<IconComponent | undefined>;
18
+ leadingIconName: import("vue").ComputedRef<import("vue").FunctionalComponent<import("vue").HTMLAttributes & import("vue").VNodeProps, {}, any, {}> | undefined>;
19
+ trailingIconName: import("vue").ComputedRef<import("vue").FunctionalComponent<import("vue").HTMLAttributes & import("vue").VNodeProps, {}, any, {}> | undefined>;
20
20
  };
@@ -19,6 +19,7 @@ export * from '../components/Progress.vue';
19
19
  export * from '../components/RadioGroup.vue';
20
20
  export * from '../components/Range.vue';
21
21
  export * from '../components/Select.vue';
22
+ export * from '../components/SelectMenu.vue';
22
23
  export * from '../components/Separator.vue';
23
24
  export * from '../components/Skeleton.vue';
24
25
  export * from '../components/Switch.vue';
@@ -31,3 +32,4 @@ export * from '../components/content/DescriptionList.vue';
31
32
  export * from './form';
32
33
  export * from './locale';
33
34
  export * from './icons';
35
+ export * from './utils';
@@ -19,6 +19,7 @@ export * from "../components/Progress.vue";
19
19
  export * from "../components/RadioGroup.vue";
20
20
  export * from "../components/Range.vue";
21
21
  export * from "../components/Select.vue";
22
+ export * from "../components/SelectMenu.vue";
22
23
  export * from "../components/Separator.vue";
23
24
  export * from "../components/Skeleton.vue";
24
25
  export * from "../components/Switch.vue";
@@ -31,3 +32,4 @@ export * from "../components/content/DescriptionList.vue";
31
32
  export * from "./form.js";
32
33
  export * from "./locale.js";
33
34
  export * from "./icons.js";
35
+ export * from "./utils.js";
@@ -99,6 +99,7 @@ import { reactiveOmit } from '@vueuse/core'
99
99
  import { hasProtocol } from 'ufo'
100
100
  import { useRoute } from '#imports'
101
101
  import { RouterLink } from 'vue-router'
102
+ import B24LinkBase from './../../components/LinkBase.vue'
102
103
 
103
104
  defineOptions({ inheritAttrs: false })
104
105