@bitrix24/b24ui-nuxt 0.5.3 → 0.5.5
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/button.ts +10 -10
- package/.nuxt/b24ui/container.ts +1 -1
- package/.nuxt/b24ui/navigation-menu.ts +2 -2
- package/.nuxt/b24ui/switch.ts +1 -1
- package/.nuxt/b24ui/tabs.ts +1 -1
- package/dist/meta.cjs +47928 -45201
- package/dist/meta.d.cts +47928 -45201
- package/dist/meta.d.mts +47928 -45201
- package/dist/meta.d.ts +47928 -45201
- package/dist/meta.mjs +47928 -45201
- package/dist/module.cjs +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/components/Alert.vue +1 -1
- package/dist/runtime/components/App.vue +1 -1
- package/dist/runtime/components/Button.vue +1 -1
- package/dist/runtime/components/Calendar.vue +40 -12
- package/dist/runtime/components/DescriptionList.vue +99 -85
- package/dist/runtime/components/DropdownMenu.vue +23 -12
- package/dist/runtime/components/DropdownMenuContent.vue +22 -15
- package/dist/runtime/components/Form.vue +1 -0
- package/dist/runtime/components/FormField.vue +1 -0
- package/dist/runtime/components/InputMenu.vue +91 -53
- package/dist/runtime/components/Link.vue +3 -0
- package/dist/runtime/components/LinkBase.vue +1 -0
- package/dist/runtime/components/Modal.vue +1 -1
- package/dist/runtime/components/NavigationMenu.vue +49 -25
- package/dist/runtime/components/RadioGroup.vue +23 -12
- package/dist/runtime/components/Select.vue +74 -47
- package/dist/runtime/components/SelectMenu.vue +95 -56
- package/dist/runtime/components/Slideover.vue +1 -1
- package/dist/runtime/components/Tabs.vue +6 -5
- package/dist/runtime/components/Toast.vue +1 -1
- package/dist/runtime/composables/defineLocale.js +1 -0
- package/dist/runtime/composables/useFormField.js +1 -1
- package/dist/runtime/types/form.d.ts +1 -0
- package/dist/runtime/types/utils.d.ts +28 -7
- package/dist/runtime/utils/index.d.ts +1 -0
- package/dist/runtime/utils/index.js +3 -0
- package/dist/runtime/utils/link.d.ts +2 -25
- package/dist/runtime/utils/link.js +31 -1
- package/dist/runtime/vue/components/Link.vue +3 -0
- package/dist/runtime/vue/stubs.d.ts +2 -2
- package/dist/shared/{b24ui-nuxt.Bh_5o1_9.cjs → b24ui-nuxt.BGKKwlPY.cjs} +17 -15
- package/dist/shared/{b24ui-nuxt.BIuy4yic.mjs → b24ui-nuxt.DWwKgFlo.mjs} +17 -15
- 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 +32 -17
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { InputHTMLAttributes } from 'vue'
|
|
3
3
|
import type { VariantProps } from 'tailwind-variants'
|
|
4
|
-
import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxContentEmits, ComboboxArrowProps
|
|
4
|
+
import type { ComboboxRootProps, ComboboxRootEmits, ComboboxContentProps, ComboboxContentEmits, ComboboxArrowProps } from 'reka-ui'
|
|
5
5
|
import type { AppConfig } from '@nuxt/schema'
|
|
6
6
|
import _appConfig from '#build/app.config'
|
|
7
7
|
import theme from '#build/b24ui/input-menu'
|
|
8
8
|
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
|
|
9
9
|
import { tv } from '../utils/tv'
|
|
10
10
|
import type { AvatarProps, ChipProps, InputProps, IconComponent } from '../types'
|
|
11
|
-
import type {
|
|
11
|
+
import type {
|
|
12
|
+
AcceptableValue,
|
|
13
|
+
ArrayOrNested,
|
|
14
|
+
GetItemKeys,
|
|
15
|
+
GetModelValue,
|
|
16
|
+
GetModelValueEmits,
|
|
17
|
+
NestedItem,
|
|
18
|
+
PartialString,
|
|
19
|
+
EmitsToProps
|
|
20
|
+
} from '../types/utils'
|
|
12
21
|
|
|
13
22
|
const appConfigInputMenu = _appConfig as AppConfig & { b24ui: { inputMenu: Partial<typeof theme> } }
|
|
14
23
|
|
|
@@ -16,7 +25,7 @@ const inputMenu = tv({ extend: tv(theme), ...(appConfigInputMenu.b24ui?.inputMen
|
|
|
16
25
|
|
|
17
26
|
type InputMenuVariants = VariantProps<typeof inputMenu>
|
|
18
27
|
|
|
19
|
-
|
|
28
|
+
interface _InputMenuItem {
|
|
20
29
|
label?: string
|
|
21
30
|
/**
|
|
22
31
|
* Display an icon on the left side.
|
|
@@ -31,11 +40,14 @@ export interface InputMenuItem {
|
|
|
31
40
|
* @defaultValue 'item'
|
|
32
41
|
*/
|
|
33
42
|
type?: 'label' | 'separator' | 'item'
|
|
43
|
+
value?: string | number
|
|
34
44
|
disabled?: boolean
|
|
35
45
|
onSelect?(e?: Event): void
|
|
46
|
+
[key: string]: any
|
|
36
47
|
}
|
|
48
|
+
export type InputMenuItem = _InputMenuItem | AcceptableValue | boolean
|
|
37
49
|
|
|
38
|
-
export interface InputMenuProps<T extends
|
|
50
|
+
export interface InputMenuProps<T extends ArrayOrNested<InputMenuItem> = ArrayOrNested<InputMenuItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false> extends Pick<ComboboxRootProps<T>, 'open' | 'defaultOpen' | 'disabled' | 'name' | 'resetSearchTermOnBlur' | 'highlightOnHover'>, UseComponentIconsProps {
|
|
39
51
|
/**
|
|
40
52
|
* The element or component this component should render as.
|
|
41
53
|
* @defaultValue 'div'
|
|
@@ -128,21 +140,21 @@ export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends Ma
|
|
|
128
140
|
* When `items` is an array of objects, select the field to use as the value instead of the object itself.
|
|
129
141
|
* @defaultValue undefined
|
|
130
142
|
*/
|
|
131
|
-
valueKey?:
|
|
143
|
+
valueKey?: VK
|
|
132
144
|
/**
|
|
133
145
|
* When `items` is an array of objects, select the field to use as the label.
|
|
134
146
|
* @defaultValue 'label'
|
|
135
147
|
*/
|
|
136
|
-
labelKey?:
|
|
137
|
-
items?:
|
|
148
|
+
labelKey?: keyof NestedItem<T>
|
|
149
|
+
items?: T
|
|
138
150
|
/**
|
|
139
151
|
* The value of the InputMenu when initially rendered. Use when you do not need to control the state of the InputMenu
|
|
140
152
|
*/
|
|
141
|
-
defaultValue?:
|
|
153
|
+
defaultValue?: GetModelValue<T, VK, M>
|
|
142
154
|
/**
|
|
143
155
|
* The controlled value of the InputMenu. Can be binded-with with `v-model`
|
|
144
156
|
*/
|
|
145
|
-
modelValue?:
|
|
157
|
+
modelValue?: GetModelValue<T, VK, M>
|
|
146
158
|
/**
|
|
147
159
|
* Whether multiple options can be selected or not
|
|
148
160
|
* @defaultValue false
|
|
@@ -177,18 +189,28 @@ export interface InputMenuProps<T extends MaybeArrayOfArrayItem<I>, I extends Ma
|
|
|
177
189
|
b24ui?: PartialString<typeof inputMenu.slots>
|
|
178
190
|
}
|
|
179
191
|
|
|
180
|
-
export type InputMenuEmits<
|
|
192
|
+
export type InputMenuEmits<A extends ArrayOrNested<InputMenuItem>, VK extends GetItemKeys<A> | undefined, M extends boolean> = Pick<ComboboxRootEmits, 'update:open'> & {
|
|
181
193
|
change: [payload: Event]
|
|
182
194
|
blur: [payload: FocusEvent]
|
|
183
195
|
focus: [payload: FocusEvent]
|
|
184
196
|
create: [item: string]
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
197
|
+
/** Event handler when highlighted element changes. */
|
|
198
|
+
highlight: [payload: {
|
|
199
|
+
ref: HTMLElement
|
|
200
|
+
value: GetModelValue<A, VK, M>
|
|
201
|
+
} | undefined]
|
|
202
|
+
} & GetModelValueEmits<A, VK, M>
|
|
203
|
+
|
|
204
|
+
type SlotProps<T extends InputMenuItem> = (props: { item: T, index: number }) => any
|
|
205
|
+
|
|
206
|
+
export interface InputMenuSlots<
|
|
207
|
+
A extends ArrayOrNested<InputMenuItem> = ArrayOrNested<InputMenuItem>,
|
|
208
|
+
VK extends GetItemKeys<A> | undefined = undefined,
|
|
209
|
+
M extends boolean = false,
|
|
210
|
+
T extends NestedItem<A> = NestedItem<A>
|
|
211
|
+
> {
|
|
212
|
+
'leading'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean, b24ui: ReturnType<typeof inputMenu> }): any
|
|
213
|
+
'trailing'(props: { modelValue?: GetModelValue<A, VK, M>, open: boolean, b24ui: ReturnType<typeof inputMenu> }): any
|
|
192
214
|
'empty'(props: { searchTerm?: string }): any
|
|
193
215
|
'item': SlotProps<T>
|
|
194
216
|
'item-leading': SlotProps<T>
|
|
@@ -200,7 +222,7 @@ export interface InputMenuSlots<T, M extends boolean> {
|
|
|
200
222
|
}
|
|
201
223
|
</script>
|
|
202
224
|
|
|
203
|
-
<script setup lang="ts" generic="T extends
|
|
225
|
+
<script setup lang="ts" generic="T extends ArrayOrNested<InputMenuItem>, VK extends GetItemKeys<T> | undefined = undefined, M extends boolean = false">
|
|
204
226
|
import { computed, ref, toRef, onMounted, toRaw } from 'vue'
|
|
205
227
|
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'
|
|
206
228
|
import { defu } from 'defu'
|
|
@@ -210,21 +232,21 @@ import { useButtonGroup } from '../composables/useButtonGroup'
|
|
|
210
232
|
import { useComponentIcons } from '../composables/useComponentIcons'
|
|
211
233
|
import { useFormField } from '../composables/useFormField'
|
|
212
234
|
import { useLocale } from '../composables/useLocale'
|
|
213
|
-
import { get,
|
|
235
|
+
import { compare, get, isArrayOfArray } from '../utils'
|
|
214
236
|
import icons from '../dictionary/icons'
|
|
215
237
|
import B24Avatar from './Avatar.vue'
|
|
216
238
|
import B24Chip from './Chip.vue'
|
|
217
239
|
|
|
218
240
|
defineOptions({ inheritAttrs: false })
|
|
219
241
|
|
|
220
|
-
const props = withDefaults(defineProps<InputMenuProps<T,
|
|
242
|
+
const props = withDefaults(defineProps<InputMenuProps<T, VK, M>>(), {
|
|
221
243
|
type: 'text',
|
|
222
244
|
autofocusDelay: 0,
|
|
223
245
|
portal: true,
|
|
224
246
|
labelKey: 'label' as never
|
|
225
247
|
})
|
|
226
|
-
const emits = defineEmits<InputMenuEmits<T,
|
|
227
|
-
const slots = defineSlots<InputMenuSlots<T, M>>()
|
|
248
|
+
const emits = defineEmits<InputMenuEmits<T, VK, M>>()
|
|
249
|
+
const slots = defineSlots<InputMenuSlots<T, VK, M>>()
|
|
228
250
|
|
|
229
251
|
const searchTerm = defineModel<string>('searchTerm', { default: '' })
|
|
230
252
|
|
|
@@ -272,9 +294,15 @@ function displayValue(value: T): string {
|
|
|
272
294
|
return item && (typeof item === 'object' ? get(item, props.labelKey as string) : item)
|
|
273
295
|
}
|
|
274
296
|
|
|
275
|
-
const groups = computed
|
|
297
|
+
const groups = computed<InputMenuItem[][]>(() =>
|
|
298
|
+
props.items?.length
|
|
299
|
+
? isArrayOfArray(props.items)
|
|
300
|
+
? props.items
|
|
301
|
+
: [props.items]
|
|
302
|
+
: []
|
|
303
|
+
)
|
|
276
304
|
// eslint-disable-next-line vue/no-dupe-keys
|
|
277
|
-
const items = computed(() => groups.value.flatMap(group => group)
|
|
305
|
+
const items = computed(() => groups.value.flatMap(group => group))
|
|
278
306
|
|
|
279
307
|
const filteredGroups = computed(() => {
|
|
280
308
|
if (props.ignoreFilter || !searchTerm.value) {
|
|
@@ -283,9 +311,9 @@ const filteredGroups = computed(() => {
|
|
|
283
311
|
|
|
284
312
|
const fields = Array.isArray(props.filterFields) ? props.filterFields : [props.labelKey] as string[]
|
|
285
313
|
|
|
286
|
-
return groups.value.map(
|
|
287
|
-
if (typeof item !== 'object') {
|
|
288
|
-
return contains(item, searchTerm.value)
|
|
314
|
+
return groups.value.map(group => group.filter((item) => {
|
|
315
|
+
if (typeof item !== 'object' || item === null) {
|
|
316
|
+
return contains(String(item), searchTerm.value)
|
|
289
317
|
}
|
|
290
318
|
|
|
291
319
|
if (item.type && ['label', 'separator'].includes(item.type)) {
|
|
@@ -293,19 +321,25 @@ const filteredGroups = computed(() => {
|
|
|
293
321
|
}
|
|
294
322
|
|
|
295
323
|
return fields.some(field => contains(get(item, field), searchTerm.value))
|
|
296
|
-
})).filter(group => group.filter(item =>
|
|
324
|
+
})).filter(group => group.filter(item =>
|
|
325
|
+
/**
|
|
326
|
+
* @memo fix not obj
|
|
327
|
+
* @see selectMenu
|
|
328
|
+
*/
|
|
329
|
+
typeof item !== 'object' || (isInputItem(item) && (!item.type || !['label', 'separator'].includes(item.type)))
|
|
330
|
+
).length > 0)
|
|
297
331
|
})
|
|
298
|
-
const filteredItems = computed(() => filteredGroups.value.flatMap(group => group)
|
|
332
|
+
const filteredItems = computed(() => filteredGroups.value.flatMap(group => group))
|
|
299
333
|
|
|
300
334
|
const createItem = computed(() => {
|
|
301
335
|
if (!props.createItem || !searchTerm.value) {
|
|
302
336
|
return false
|
|
303
337
|
}
|
|
304
338
|
|
|
305
|
-
const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } as T : searchTerm.value
|
|
339
|
+
const newItem = props.valueKey ? { [props.valueKey]: searchTerm.value } as NestedItem<T> : searchTerm.value
|
|
306
340
|
|
|
307
341
|
if ((typeof props.createItem === 'object' && props.createItem.when === 'always') || props.createItem === 'always') {
|
|
308
|
-
return !filteredItems.value.find(item => compare(item, newItem, props.valueKey))
|
|
342
|
+
return !filteredItems.value.find(item => compare(item, newItem, String(props.valueKey)))
|
|
309
343
|
}
|
|
310
344
|
|
|
311
345
|
return !filteredItems.value.length
|
|
@@ -360,12 +394,16 @@ function onUpdateOpen(value: boolean) {
|
|
|
360
394
|
|
|
361
395
|
function onRemoveTag(event: any) {
|
|
362
396
|
if (props.multiple) {
|
|
363
|
-
const modelValue = props.modelValue as
|
|
397
|
+
const modelValue = props.modelValue as GetModelValue<T, VK, true>
|
|
364
398
|
const filteredValue = modelValue.filter(value => !isEqual(value, event))
|
|
365
|
-
emits('update:modelValue', filteredValue as
|
|
399
|
+
emits('update:modelValue', filteredValue as GetModelValue<T, VK, M>)
|
|
366
400
|
}
|
|
367
401
|
}
|
|
368
402
|
|
|
403
|
+
function isInputItem(item: InputMenuItem): item is _InputMenuItem {
|
|
404
|
+
return typeof item === 'object' && item !== null
|
|
405
|
+
}
|
|
406
|
+
|
|
369
407
|
defineExpose({
|
|
370
408
|
inputRef
|
|
371
409
|
})
|
|
@@ -424,13 +462,13 @@ defineExpose({
|
|
|
424
462
|
>
|
|
425
463
|
<TagsInputItem v-for="(item, index) in tags" :key="index" :value="(item as string)" :class="b24ui.tagsItem({ class: props.b24ui?.tagsItem })">
|
|
426
464
|
<TagsInputItemText :class="b24ui.tagsItemText({ class: props.b24ui?.tagsItemText })">
|
|
427
|
-
<slot name="tags-item-text" :item="(item as T)" :index="index">
|
|
465
|
+
<slot name="tags-item-text" :item="(item as NestedItem<T>)" :index="index">
|
|
428
466
|
{{ displayValue(item as T) }}
|
|
429
467
|
</slot>
|
|
430
468
|
</TagsInputItemText>
|
|
431
469
|
|
|
432
470
|
<TagsInputItemDelete :class="b24ui.tagsItemDelete({ class: props.b24ui?.tagsItemDelete })" :disabled="disabled">
|
|
433
|
-
<slot name="tags-item-delete" :item="(item as T)" :index="index">
|
|
471
|
+
<slot name="tags-item-delete" :item="(item as NestedItem<T>)" :index="index">
|
|
434
472
|
<Component
|
|
435
473
|
:is="deleteIcon || icons.close"
|
|
436
474
|
:class="b24ui.tagsItemDeleteIcon({ class: props.b24ui?.tagsItemDeleteIcon })"
|
|
@@ -464,7 +502,7 @@ defineExpose({
|
|
|
464
502
|
/>
|
|
465
503
|
|
|
466
504
|
<span v-if="isLeading || !!avatar || !!slots.leading" :class="b24ui.leading({ class: props.b24ui?.leading })">
|
|
467
|
-
<slot name="leading" :model-value="(modelValue as
|
|
505
|
+
<slot name="leading" :model-value="(modelValue as GetModelValue<T, VK, M>)" :open="open" :b24ui="b24ui">
|
|
468
506
|
<Component
|
|
469
507
|
:is="leadingIconName"
|
|
470
508
|
v-if="isLeading && leadingIconName"
|
|
@@ -475,7 +513,7 @@ defineExpose({
|
|
|
475
513
|
</span>
|
|
476
514
|
|
|
477
515
|
<ComboboxTrigger v-if="isTrailing || !!slots.trailing" :class="b24ui.trailing({ class: props.b24ui?.trailing })">
|
|
478
|
-
<slot name="trailing" :model-value="(modelValue as
|
|
516
|
+
<slot name="trailing" :model-value="(modelValue as GetModelValue<T, VK, M>)" :open="open" :b24ui="b24ui">
|
|
479
517
|
<Component
|
|
480
518
|
:is="trailingIconName"
|
|
481
519
|
v-if="trailingIconName"
|
|
@@ -498,29 +536,29 @@ defineExpose({
|
|
|
498
536
|
|
|
499
537
|
<ComboboxGroup v-for="(group, groupIndex) in filteredGroups" :key="`group-${groupIndex}`" :class="b24ui.group({ class: props.b24ui?.group })">
|
|
500
538
|
<template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
|
|
501
|
-
<ComboboxLabel v-if="item
|
|
539
|
+
<ComboboxLabel v-if="isInputItem(item) && item.type === 'label'" :class="b24ui.label({ class: props.b24ui?.label })">
|
|
502
540
|
{{ get(item, props.labelKey as string) }}
|
|
503
541
|
</ComboboxLabel>
|
|
504
542
|
|
|
505
|
-
<ComboboxSeparator v-else-if="item
|
|
543
|
+
<ComboboxSeparator v-else-if="isInputItem(item) && item.type === 'separator'" :class="b24ui.separator({ class: props.b24ui?.separator })" />
|
|
506
544
|
|
|
507
545
|
<ComboboxItem
|
|
508
546
|
v-else
|
|
509
|
-
:class="b24ui.item({ class: props.b24ui?.item, colorItem: item?.color })"
|
|
510
|
-
:disabled="item.disabled"
|
|
511
|
-
:value="valueKey &&
|
|
512
|
-
@select="item.onSelect"
|
|
547
|
+
:class="b24ui.item({ class: props.b24ui?.item, colorItem: isInputItem(item) ? item?.color : undefined })"
|
|
548
|
+
:disabled="isInputItem(item) && item.disabled"
|
|
549
|
+
:value="props.valueKey && isInputItem(item) ? get(item, String(props.valueKey)) : item"
|
|
550
|
+
@select="isInputItem(item) && item.onSelect"
|
|
513
551
|
>
|
|
514
|
-
<slot name="item" :item="(item as T)" :index="index">
|
|
515
|
-
<slot name="item-leading" :item="(item as T)" :index="index">
|
|
552
|
+
<slot name="item" :item="(item as NestedItem<T>)" :index="index">
|
|
553
|
+
<slot name="item-leading" :item="(item as NestedItem<T>)" :index="index">
|
|
516
554
|
<Component
|
|
517
555
|
:is="item.icon"
|
|
518
|
-
v-if="item.icon"
|
|
556
|
+
v-if="isInputItem(item) && item.icon"
|
|
519
557
|
:class="b24ui.itemLeadingIcon({ class: props.b24ui?.itemLeadingIcon, colorItem: item?.color })"
|
|
520
558
|
/>
|
|
521
|
-
<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, colorItem: item?.color })" />
|
|
559
|
+
<B24Avatar v-else-if="isInputItem(item) && item.avatar" :size="((props.b24ui?.itemLeadingAvatarSize || b24ui.itemLeadingAvatarSize()) as AvatarProps['size'])" v-bind="item.avatar" :class="b24ui.itemLeadingAvatar({ class: props.b24ui?.itemLeadingAvatar, colorItem: item?.color })" />
|
|
522
560
|
<B24Chip
|
|
523
|
-
v-else-if="item.chip"
|
|
561
|
+
v-else-if="isInputItem(item) && item.chip"
|
|
524
562
|
:size="((props.b24ui?.itemLeadingChipSize || b24ui.itemLeadingChipSize()) as ChipProps['size'])"
|
|
525
563
|
inset
|
|
526
564
|
standalone
|
|
@@ -530,18 +568,18 @@ defineExpose({
|
|
|
530
568
|
</slot>
|
|
531
569
|
|
|
532
570
|
<span :class="b24ui.itemLabel({ class: props.b24ui?.itemLabel })">
|
|
533
|
-
<slot name="item-label" :item="(item as T)" :index="index">
|
|
534
|
-
{{
|
|
571
|
+
<slot name="item-label" :item="(item as NestedItem<T>)" :index="index">
|
|
572
|
+
{{ isInputItem(item) ? get(item, props.labelKey as string) : item }}
|
|
535
573
|
</slot>
|
|
536
574
|
</span>
|
|
537
575
|
|
|
538
|
-
<span :class="b24ui.itemTrailing({ class: props.b24ui?.itemTrailing, colorItem: item?.color })">
|
|
539
|
-
<slot name="item-trailing" :item="(item as T)" :index="index" />
|
|
576
|
+
<span :class="b24ui.itemTrailing({ class: props.b24ui?.itemTrailing, colorItem: isInputItem(item) ? item?.color : undefined })">
|
|
577
|
+
<slot name="item-trailing" :item="(item as NestedItem<T>)" :index="index" />
|
|
540
578
|
|
|
541
579
|
<ComboboxItemIndicator as-child>
|
|
542
580
|
<Component
|
|
543
581
|
:is="selectedIcon || icons.check"
|
|
544
|
-
:class="b24ui.itemTrailingIcon({ class: props.b24ui?.itemTrailingIcon, colorItem: item?.color })"
|
|
582
|
+
:class="b24ui.itemTrailingIcon({ class: props.b24ui?.itemTrailingIcon, colorItem: isInputItem(item) ? item?.color : undefined })"
|
|
545
583
|
/>
|
|
546
584
|
</ComboboxItemIndicator>
|
|
547
585
|
</span>
|
|
@@ -104,6 +104,7 @@ defineOptions({ inheritAttrs: false })
|
|
|
104
104
|
const props = withDefaults(defineProps<LinkProps>(), {
|
|
105
105
|
as: 'button',
|
|
106
106
|
type: 'button',
|
|
107
|
+
ariaCurrentValue: 'page',
|
|
107
108
|
active: undefined,
|
|
108
109
|
isAction: false,
|
|
109
110
|
activeClass: '',
|
|
@@ -190,6 +191,7 @@ function resolveLinkClass({ route, isActive, isExactActive }: any) {
|
|
|
190
191
|
<slot
|
|
191
192
|
v-bind="{
|
|
192
193
|
...$attrs,
|
|
194
|
+
...(exact && isExactActive ? { 'aria-current': props.ariaCurrentValue } : {}),
|
|
193
195
|
as,
|
|
194
196
|
type,
|
|
195
197
|
disabled,
|
|
@@ -206,6 +208,7 @@ function resolveLinkClass({ route, isActive, isExactActive }: any) {
|
|
|
206
208
|
v-else
|
|
207
209
|
v-bind="{
|
|
208
210
|
...$attrs,
|
|
211
|
+
...(exact && isExactActive ? { 'aria-current': props.ariaCurrentValue } : {}),
|
|
209
212
|
as,
|
|
210
213
|
type,
|
|
211
214
|
disabled,
|
|
@@ -82,7 +82,7 @@ export interface ModalSlots {
|
|
|
82
82
|
header(props?: {}): any
|
|
83
83
|
title(props?: {}): any
|
|
84
84
|
description(props?: {}): any
|
|
85
|
-
close(props: { b24ui:
|
|
85
|
+
close(props: { b24ui: ReturnType<typeof modal> }): any
|
|
86
86
|
body(props?: {}): any
|
|
87
87
|
footer(props?: {}): any
|
|
88
88
|
}
|
|
@@ -7,15 +7,23 @@ import _appConfig from '#build/app.config'
|
|
|
7
7
|
import theme from '#build/b24ui/navigation-menu'
|
|
8
8
|
import { tv } from '../utils/tv'
|
|
9
9
|
import type { AvatarProps, BadgeProps, LinkProps, IconComponent } from '../types'
|
|
10
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
ArrayOrNested,
|
|
12
|
+
DynamicSlots,
|
|
13
|
+
MergeTypes,
|
|
14
|
+
NestedItem,
|
|
15
|
+
PartialString,
|
|
16
|
+
EmitsToProps
|
|
17
|
+
} from '../types/utils'
|
|
11
18
|
|
|
12
19
|
const appConfigNavigationMenu = _appConfig as AppConfig & { b24ui: { navigationMenu: Partial<typeof theme> } }
|
|
13
20
|
|
|
14
21
|
const navigationMenu = tv({ extend: tv(theme), ...(appConfigNavigationMenu.b24ui?.navigationMenu || {}) })
|
|
15
22
|
|
|
16
|
-
export interface NavigationMenuChildItem extends Omit<NavigationMenuItem, '
|
|
23
|
+
export interface NavigationMenuChildItem extends Omit<NavigationMenuItem, 'type'> {
|
|
17
24
|
/** Description is only used when `orientation` is `horizontal`. */
|
|
18
25
|
description?: string
|
|
26
|
+
[key: string]: any
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
export interface NavigationMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'>, Pick<CollapsibleRootProps, 'defaultOpen' | 'open'> {
|
|
@@ -48,11 +56,12 @@ export interface NavigationMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cu
|
|
|
48
56
|
*/
|
|
49
57
|
viewportRtl?: boolean
|
|
50
58
|
onSelect?(e: Event): void
|
|
59
|
+
[key: string]: any
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
type NavigationMenuVariants = VariantProps<typeof navigationMenu>
|
|
54
63
|
|
|
55
|
-
export interface NavigationMenuProps<T> extends Pick<NavigationMenuRootProps, 'modelValue' | 'defaultValue' | 'delayDuration' | 'disableClickTrigger' | 'disableHoverTrigger' | 'skipDelayDuration' | 'disablePointerLeaveClose' | 'unmountOnHide'> {
|
|
64
|
+
export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem> = ArrayOrNested<NavigationMenuItem>> extends Pick<NavigationMenuRootProps, 'modelValue' | 'defaultValue' | 'delayDuration' | 'disableClickTrigger' | 'disableHoverTrigger' | 'skipDelayDuration' | 'disablePointerLeaveClose' | 'unmountOnHide'> {
|
|
56
65
|
/**
|
|
57
66
|
* The element or component this component should render as.
|
|
58
67
|
* @defaultValue 'div'
|
|
@@ -116,30 +125,33 @@ export interface NavigationMenuProps<T> extends Pick<NavigationMenuRootProps, 'm
|
|
|
116
125
|
* The key used to get the label from the item.
|
|
117
126
|
* @defaultValue 'label'
|
|
118
127
|
*/
|
|
119
|
-
labelKey?:
|
|
128
|
+
labelKey?: keyof NestedItem<T>
|
|
120
129
|
class?: any
|
|
121
130
|
b24ui?: PartialString<typeof navigationMenu.slots>
|
|
122
131
|
}
|
|
123
132
|
|
|
124
133
|
export interface NavigationMenuEmits extends NavigationMenuRootEmits {}
|
|
125
134
|
|
|
126
|
-
type SlotProps<T> = (props: { item: T, index: number, active?: boolean }) => any
|
|
135
|
+
type SlotProps<T extends NavigationMenuItem> = (props: { item: T, index: number, active?: boolean }) => any
|
|
127
136
|
|
|
128
|
-
export type NavigationMenuSlots<
|
|
137
|
+
export type NavigationMenuSlots<
|
|
138
|
+
A extends ArrayOrNested<NavigationMenuItem> = ArrayOrNested<NavigationMenuItem>,
|
|
139
|
+
T extends NestedItem<A> = NestedItem<A>
|
|
140
|
+
> = {
|
|
129
141
|
'item': SlotProps<T>
|
|
130
142
|
'item-leading': SlotProps<T>
|
|
131
143
|
'item-label': SlotProps<T>
|
|
132
144
|
'item-trailing': SlotProps<T>
|
|
133
145
|
'item-content': SlotProps<T>
|
|
134
|
-
} & DynamicSlots<T,
|
|
146
|
+
} & DynamicSlots<MergeTypes<T>, 'leading' | 'label' | 'trailing' | 'content', { index: number, active?: boolean }>
|
|
135
147
|
|
|
136
148
|
</script>
|
|
137
149
|
|
|
138
|
-
<script setup lang="ts" generic="T extends
|
|
150
|
+
<script setup lang="ts" generic="T extends ArrayOrNested<NavigationMenuItem>">
|
|
139
151
|
import { computed, toRef } from 'vue'
|
|
140
152
|
import { NavigationMenuRoot, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuIndicator, NavigationMenuViewport, useForwardPropsEmits } from 'reka-ui'
|
|
141
153
|
import { createReusableTemplate } from '@vueuse/core'
|
|
142
|
-
import { get } from '../utils'
|
|
154
|
+
import { get, isArrayOfArray } from '../utils'
|
|
143
155
|
import { pickLinkProps } from '../utils/link'
|
|
144
156
|
import icons from '../dictionary/icons'
|
|
145
157
|
import B24LinkBase from './LinkBase.vue'
|
|
@@ -148,7 +160,7 @@ import B24Avatar from './Avatar.vue'
|
|
|
148
160
|
import B24Badge from './Badge.vue'
|
|
149
161
|
import B24Collapsible from './Collapsible.vue'
|
|
150
162
|
|
|
151
|
-
const props = withDefaults(defineProps<NavigationMenuProps<
|
|
163
|
+
const props = withDefaults(defineProps<NavigationMenuProps<T>>(), {
|
|
152
164
|
orientation: 'horizontal',
|
|
153
165
|
contentOrientation: 'vertical',
|
|
154
166
|
externalIcon: true,
|
|
@@ -174,8 +186,14 @@ const rootProps = useForwardPropsEmits(computed(() => ({
|
|
|
174
186
|
|
|
175
187
|
const contentProps = toRef(() => props.content)
|
|
176
188
|
|
|
177
|
-
const [DefineLinkTemplate, ReuseLinkTemplate] = createReusableTemplate<
|
|
178
|
-
|
|
189
|
+
const [DefineLinkTemplate, ReuseLinkTemplate] = createReusableTemplate<
|
|
190
|
+
{ item: NavigationMenuItem, index: number, active?: boolean },
|
|
191
|
+
NavigationMenuSlots<T>
|
|
192
|
+
>()
|
|
193
|
+
const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<
|
|
194
|
+
{ item: NavigationMenuItem, index: number, level?: number },
|
|
195
|
+
NavigationMenuSlots<T>
|
|
196
|
+
>({
|
|
179
197
|
props: {
|
|
180
198
|
item: Object,
|
|
181
199
|
index: Number,
|
|
@@ -193,14 +211,20 @@ const b24ui = computed(() => navigationMenu({
|
|
|
193
211
|
highlightColor: props.highlightColor || props.color
|
|
194
212
|
}))
|
|
195
213
|
|
|
196
|
-
const lists = computed
|
|
214
|
+
const lists = computed<NavigationMenuItem[][]>(() =>
|
|
215
|
+
props.items?.length
|
|
216
|
+
? isArrayOfArray(props.items)
|
|
217
|
+
? props.items
|
|
218
|
+
: [props.items]
|
|
219
|
+
: []
|
|
220
|
+
)
|
|
197
221
|
</script>
|
|
198
222
|
|
|
199
223
|
<template>
|
|
200
224
|
<DefineLinkTemplate v-slot="{ item, active, index }">
|
|
201
|
-
<slot :name="item.slot || 'item'" :item="
|
|
225
|
+
<slot :name="((item.slot || 'item') as keyof NavigationMenuSlots<T>)" :item="item" :index="index">
|
|
202
226
|
<span :class="b24ui.linkLabelWrapper({ class: props.b24ui?.linkLabelWrapper, active })">
|
|
203
|
-
<slot :name="item.slot ? `${item.slot}-leading` : 'item-leading'" :item="
|
|
227
|
+
<slot :name="((item.slot ? `${item.slot}-leading` : 'item-leading') as keyof NavigationMenuSlots<T>)" :item="item" :active="active" :index="index">
|
|
204
228
|
<Component
|
|
205
229
|
:is="item.icon"
|
|
206
230
|
v-if="item.icon"
|
|
@@ -210,10 +234,10 @@ const lists = computed(() => props.items?.length ? (Array.isArray(props.items[0]
|
|
|
210
234
|
</slot>
|
|
211
235
|
|
|
212
236
|
<span
|
|
213
|
-
v-if="(!collapsed || orientation !== 'vertical') && (get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label` : 'item-label'])"
|
|
237
|
+
v-if="(!collapsed || orientation !== 'vertical') && (get(item, props.labelKey as string) || !!slots[(item.slot ? `${item.slot}-label` : 'item-label') as keyof NavigationMenuSlots<T>])"
|
|
214
238
|
:class="b24ui.linkLabel({ class: props.b24ui?.linkLabel, active })"
|
|
215
239
|
>
|
|
216
|
-
<slot :name="item.slot ? `${item.slot}-label` : 'item-label'" :item="
|
|
240
|
+
<slot :name="((item.slot ? `${item.slot}-label` : 'item-label') as keyof NavigationMenuSlots<T>)" :item="item" :active="active" :index="index">
|
|
217
241
|
{{ get(item, props.labelKey as string) }}
|
|
218
242
|
</slot>
|
|
219
243
|
|
|
@@ -224,11 +248,11 @@ const lists = computed(() => props.items?.length ? (Array.isArray(props.items[0]
|
|
|
224
248
|
/>
|
|
225
249
|
</span>
|
|
226
250
|
</span>
|
|
227
|
-
<span v-if="(!collapsed || orientation !== 'vertical') && (item.badge || (orientation === 'horizontal' && (item.children?.length || !!slots[item.slot ? `${item.slot}-content` : 'item-content'])) || (orientation === 'vertical' && item.children?.length) || item.trailingIcon || !!slots[item.slot ? `${item.slot}-trailing` : 'item-trailing'])" :class="b24ui.linkTrailing({ class: props.b24ui?.linkTrailing })">
|
|
228
|
-
<slot :name="item.slot ? `${item.slot}-trailing` : 'item-trailing'" :item="
|
|
251
|
+
<span v-if="(!collapsed || orientation !== 'vertical') && (item.badge || (orientation === 'horizontal' && (item.children?.length || !!slots[(item.slot ? `${item.slot}-content` : 'item-content') as keyof NavigationMenuSlots<T>])) || (orientation === 'vertical' && item.children?.length) || item.trailingIcon || !!slots[(item.slot ? `${item.slot}-trailing` : 'item-trailing') as keyof NavigationMenuSlots<T>])" :class="b24ui.linkTrailing({ class: props.b24ui?.linkTrailing })">
|
|
252
|
+
<slot :name="((item.slot ? `${item.slot}-trailing` : 'item-trailing') as keyof NavigationMenuSlots<T>)" :item="item" :active="active" :index="index">
|
|
229
253
|
<Component
|
|
230
254
|
:is="item.trailingIcon || trailingIcon || icons.chevronDown"
|
|
231
|
-
v-if="(orientation === 'horizontal' && (item.children?.length || !!slots[item.slot ? `${item.slot}-content` : 'item-content'])) || (orientation === 'vertical' && item.children?.length)"
|
|
255
|
+
v-if="(orientation === 'horizontal' && (item.children?.length || !!slots[(item.slot ? `${item.slot}-content` : 'item-content') as keyof NavigationMenuSlots<T>])) || (orientation === 'vertical' && item.children?.length)"
|
|
232
256
|
:class="b24ui.linkTrailingIcon({ class: props.b24ui?.linkTrailingIcon, active })"
|
|
233
257
|
/>
|
|
234
258
|
<Component
|
|
@@ -261,28 +285,28 @@ const lists = computed(() => props.items?.length ? (Array.isArray(props.items[0]
|
|
|
261
285
|
:open="item.open"
|
|
262
286
|
>
|
|
263
287
|
<div v-if="orientation === 'vertical' && item.type === 'label'" :class="b24ui.label({ class: props.b24ui?.label })">
|
|
264
|
-
<ReuseLinkTemplate :item="
|
|
288
|
+
<ReuseLinkTemplate :item="item" :index="index" />
|
|
265
289
|
</div>
|
|
266
290
|
<B24Link v-else-if="item.type !== 'label'" v-slot="{ active, ...slotProps }" v-bind="(orientation === 'vertical' && item.children?.length && !collapsed) ? {} : pickLinkProps(item as Omit<NavigationMenuItem, 'type'>)" custom>
|
|
267
291
|
<component
|
|
268
|
-
:is="(orientation === 'horizontal' && (item.children?.length || !!slots[item.slot ? `${item.slot}-content` : 'item-content'])) ? NavigationMenuTrigger : NavigationMenuLink"
|
|
292
|
+
:is="(orientation === 'horizontal' && (item.children?.length || !!slots[(item.slot ? `${item.slot}-content` : 'item-content') as keyof NavigationMenuSlots<T>])) ? NavigationMenuTrigger : NavigationMenuLink"
|
|
269
293
|
as-child
|
|
270
294
|
:active="active || item.active"
|
|
271
295
|
:disabled="item.disabled"
|
|
272
296
|
@select="item.onSelect"
|
|
273
297
|
>
|
|
274
298
|
<B24LinkBase v-bind="slotProps" :class="b24ui.link({ class: [props.b24ui?.link, item.class], active: active || item.active, disabled: !!item.disabled, level: orientation === 'horizontal' || level > 0 })">
|
|
275
|
-
<ReuseLinkTemplate :item="
|
|
299
|
+
<ReuseLinkTemplate :item="item" :active="active || item.active" :index="index" />
|
|
276
300
|
</B24LinkBase>
|
|
277
301
|
</component>
|
|
278
302
|
|
|
279
303
|
<NavigationMenuContent
|
|
280
|
-
v-if="orientation === 'horizontal' && (item.children?.length || !!slots[item.slot ? `${item.slot}-content` : 'item-content'])"
|
|
304
|
+
v-if="orientation === 'horizontal' && (item.children?.length || !!slots[(item.slot ? `${item.slot}-content` : 'item-content') as keyof NavigationMenuSlots<T>])"
|
|
281
305
|
v-bind="contentProps"
|
|
282
306
|
:data-viewport="item.viewportRtl ? 'rtl' : 'ltr'"
|
|
283
307
|
:class="b24ui.content({ class: props.b24ui?.content })"
|
|
284
308
|
>
|
|
285
|
-
<slot :name="item.slot ? `${item.slot}-content` : 'item-content'" :item="
|
|
309
|
+
<slot :name="((item.slot ? `${item.slot}-content` : 'item-content') as keyof NavigationMenuSlots<T>)" :item="item" :active="active" :index="index">
|
|
286
310
|
<ul :class="b24ui.childList({ class: props.b24ui?.childList })">
|
|
287
311
|
<li v-for="(childItem, childIndex) in item.children" :key="childIndex" :class="b24ui.childItem({ class: props.b24ui?.childItem })">
|
|
288
312
|
<B24Link v-slot="{ active: childActive, ...childSlotProps }" v-bind="pickLinkProps(childItem)" custom>
|