@nuxt/ui 2.18.6 → 2.19.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/dist/module.d.mts +1 -1
- package/dist/module.d.ts +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +3 -3
- package/dist/runtime/components/data/Table.vue +82 -27
- package/dist/runtime/components/data/Table.vue.d.ts +49 -41
- package/dist/runtime/components/elements/Alert.vue.d.ts +1 -1
- package/dist/runtime/components/elements/AvatarGroup.js +2 -2
- package/dist/runtime/components/elements/Carousel.vue +16 -7
- package/dist/runtime/components/elements/Carousel.vue.d.ts +9 -0
- package/dist/runtime/components/elements/Dropdown.vue +7 -3
- package/dist/runtime/components/elements/MeterGroup.d.ts +1 -1
- package/dist/runtime/components/elements/Progress.vue +1 -1
- package/dist/runtime/components/forms/Checkbox.vue.d.ts +5 -5
- package/dist/runtime/components/forms/Form.vue +104 -43
- package/dist/runtime/components/forms/Form.vue.d.ts +5 -3
- package/dist/runtime/components/forms/Input.vue +5 -2
- package/dist/runtime/components/forms/Input.vue.d.ts +5 -2
- package/dist/runtime/components/forms/InputMenu.vue +419 -8
- package/dist/runtime/components/forms/InputMenu.vue.d.ts +22 -3
- package/dist/runtime/components/forms/Radio.vue.d.ts +5 -5
- package/dist/runtime/components/forms/RadioGroup.vue +3 -4
- package/dist/runtime/components/forms/RadioGroup.vue.d.ts +8 -8
- package/dist/runtime/components/forms/Range.vue.d.ts +2 -2
- package/dist/runtime/components/forms/Select.vue.d.ts +3 -3
- package/dist/runtime/components/forms/SelectMenu.vue +501 -11
- package/dist/runtime/components/forms/SelectMenu.vue.d.ts +33 -5
- package/dist/runtime/components/forms/Textarea.vue +7 -4
- package/dist/runtime/components/forms/Textarea.vue.d.ts +5 -2
- package/dist/runtime/components/forms/Toggle.vue.d.ts +2 -2
- package/dist/runtime/components/layout/Divider.vue +1 -1
- package/dist/runtime/components/layout/Divider.vue.d.ts +5 -2
- package/dist/runtime/components/navigation/CommandPalette.vue +1 -1
- package/dist/runtime/components/navigation/CommandPalette.vue.d.ts +1 -1
- package/dist/runtime/components/navigation/CommandPaletteGroup.vue.d.ts +1 -1
- package/dist/runtime/components/navigation/Pagination.vue +4 -4
- package/dist/runtime/components/navigation/Pagination.vue.d.ts +1 -1
- package/dist/runtime/components/navigation/Tabs.vue +1 -1
- package/dist/runtime/components/navigation/VerticalNavigation.vue.d.ts +6 -2
- package/dist/runtime/components/overlays/Modal.vue +1 -1
- package/dist/runtime/components/overlays/Notification.vue.d.ts +1 -1
- package/dist/runtime/components/overlays/Notifications.vue +1 -1
- package/dist/runtime/components/overlays/Notifications.vue.d.ts +1 -1
- package/dist/runtime/components/overlays/Popover.vue +7 -3
- package/dist/runtime/components/overlays/Tooltip.vue +5 -3
- package/dist/runtime/components/overlays/Tooltip.vue.d.ts +1 -0
- package/dist/runtime/composables/defineShortcuts.js +1 -1
- package/dist/runtime/composables/useButtonGroup.d.ts +1 -1
- package/dist/runtime/composables/useUI.js +1 -1
- package/dist/runtime/plugins/modals.js +1 -1
- package/dist/runtime/plugins/slideovers.js +1 -1
- package/dist/runtime/types/accordion.d.ts +1 -1
- package/dist/runtime/types/alert.d.ts +4 -4
- package/dist/runtime/types/avatar.d.ts +3 -3
- package/dist/runtime/types/badge.d.ts +3 -3
- package/dist/runtime/types/button.d.ts +3 -5
- package/dist/runtime/types/chip.d.ts +2 -2
- package/dist/runtime/types/command-palette.d.ts +2 -2
- package/dist/runtime/types/component.d.ts +3 -3
- package/dist/runtime/types/divider.d.ts +3 -3
- package/dist/runtime/types/dropdown.d.ts +1 -1
- package/dist/runtime/types/form-group.d.ts +2 -2
- package/dist/runtime/types/form.d.ts +7 -2
- package/dist/runtime/types/index.d.ts +1 -0
- package/dist/runtime/types/index.js +1 -0
- package/dist/runtime/types/input.d.ts +3 -3
- package/dist/runtime/types/kbd.d.ts +2 -2
- package/dist/runtime/types/meter.d.ts +2 -2
- package/dist/runtime/types/modal.d.ts +2 -2
- package/dist/runtime/types/notification.d.ts +1 -1
- package/dist/runtime/types/progress.d.ts +2 -2
- package/dist/runtime/types/range.d.ts +3 -3
- package/dist/runtime/types/select.d.ts +3 -3
- package/dist/runtime/types/table.d.ts +18 -0
- package/dist/runtime/types/textarea.d.ts +3 -3
- package/dist/runtime/types/toggle.d.ts +3 -3
- package/dist/runtime/types/utils.d.ts +2 -2
- package/dist/runtime/ui.config/data/table.d.ts +1 -0
- package/dist/runtime/ui.config/data/table.js +1 -0
- package/dist/runtime/ui.config/elements/avatar.js +15 -15
- package/dist/runtime/ui.config/elements/button.js +25 -25
- package/dist/runtime/ui.config/elements/buttonGroup.js +1 -1
- package/dist/runtime/ui.config/elements/carousel.js +2 -2
- package/dist/runtime/ui.config/elements/chip.js +5 -5
- package/dist/runtime/ui.config/elements/meter.js +20 -20
- package/dist/runtime/ui.config/elements/meterGroup.js +1 -1
- package/dist/runtime/ui.config/elements/progress.js +18 -18
- package/dist/runtime/ui.config/forms/formGroup.js +5 -6
- package/dist/runtime/ui.config/forms/input.js +40 -40
- package/dist/runtime/ui.config/forms/inputMenu.d.ts +6 -0
- package/dist/runtime/ui.config/forms/inputMenu.js +7 -1
- package/dist/runtime/ui.config/forms/range.js +20 -20
- package/dist/runtime/ui.config/forms/selectMenu.d.ts +9 -0
- package/dist/runtime/ui.config/forms/selectMenu.js +10 -1
- package/dist/runtime/ui.config/forms/toggle.js +20 -20
- package/dist/runtime/ui.config/layout/divider.d.ts +1 -0
- package/dist/runtime/ui.config/layout/divider.js +12 -11
- package/dist/runtime/ui.config/navigation/horizontalNavigation.js +1 -1
- package/dist/runtime/ui.config/navigation/verticalNavigation.js +1 -1
- package/dist/runtime/ui.config/popper.js +0 -1
- package/dist/runtime/utils/colors.js +7 -7
- package/dist/runtime/utils/index.js +4 -4
- package/dist/runtime/utils/link.js +1 -1
- package/dist/runtime/utils/lodash.js +1 -1
- package/package.json +23 -21
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
/>
|
|
87
87
|
<span v-else-if="option.chip" :class="uiMenu.option.chip.base" :style="{ background: `#${option.chip}` }" />
|
|
88
88
|
|
|
89
|
-
<span class="truncate">{{ ['string', 'number'].includes(typeof option) ? option : option
|
|
89
|
+
<span class="truncate">{{ ['string', 'number'].includes(typeof option) ? option : accessor(option, optionAttribute) }}</span>
|
|
90
90
|
</slot>
|
|
91
91
|
</div>
|
|
92
92
|
|
|
@@ -100,19 +100,19 @@
|
|
|
100
100
|
<li :class="[uiMenu.option.base, uiMenu.option.rounded, uiMenu.option.padding, uiMenu.option.size, uiMenu.option.color, active ? uiMenu.option.active : uiMenu.option.inactive]">
|
|
101
101
|
<div :class="uiMenu.option.container">
|
|
102
102
|
<slot name="option-create" :option="createOption" :active="active" :selected="optionSelected">
|
|
103
|
-
<span :class="uiMenu.option.create">Create "{{ createOption
|
|
103
|
+
<span :class="uiMenu.option.create">Create "{{ typeof createOption === 'string' ? createOption : accessor(createOption, optionAttribute) }}"</span>
|
|
104
104
|
</slot>
|
|
105
105
|
</div>
|
|
106
106
|
</li>
|
|
107
107
|
</component>
|
|
108
108
|
<p v-else-if="searchable && query && !filteredOptions?.length" :class="uiMenu.option.empty">
|
|
109
109
|
<slot name="option-empty" :query="query">
|
|
110
|
-
|
|
110
|
+
{{ uiMenu.default.optionEmpty.label.replace('{query}', query) }}
|
|
111
111
|
</slot>
|
|
112
112
|
</p>
|
|
113
113
|
<p v-else-if="!filteredOptions?.length" :class="uiMenu.empty">
|
|
114
114
|
<slot name="empty" :query="query">
|
|
115
|
-
|
|
115
|
+
{{ uiMenu.default.empty.label }}
|
|
116
116
|
</slot>
|
|
117
117
|
</p>
|
|
118
118
|
</component>
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
</template>
|
|
124
124
|
|
|
125
125
|
<script>
|
|
126
|
-
import { ref, computed, toRef, watch, defineComponent } from "vue";
|
|
126
|
+
import { ref, computed, toRef, watch, defineComponent, toRaw } from "vue";
|
|
127
127
|
import {
|
|
128
128
|
Combobox as HCombobox,
|
|
129
129
|
ComboboxButton as HComboboxButton,
|
|
@@ -241,7 +241,7 @@ export default defineComponent({
|
|
|
241
241
|
},
|
|
242
242
|
searchablePlaceholder: {
|
|
243
243
|
type: String,
|
|
244
|
-
default:
|
|
244
|
+
default: () => configMenu.default.searchablePlaceholder.label
|
|
245
245
|
},
|
|
246
246
|
searchableLazy: {
|
|
247
247
|
type: Boolean,
|
|
@@ -374,9 +374,9 @@ export default defineComponent({
|
|
|
374
374
|
}
|
|
375
375
|
} else if (props.modelValue !== void 0 && props.modelValue !== null) {
|
|
376
376
|
if (props.valueAttribute) {
|
|
377
|
-
return selected.value
|
|
377
|
+
return accessor(selected.value, props.optionAttribute) ?? null;
|
|
378
378
|
} else {
|
|
379
|
-
return ["string", "number"].includes(typeof props.modelValue) ? props.modelValue : props.modelValue
|
|
379
|
+
return ["string", "number"].includes(typeof props.modelValue) ? props.modelValue : accessor(props.modelValue, props.optionAttribute);
|
|
380
380
|
}
|
|
381
381
|
}
|
|
382
382
|
return null;
|
|
@@ -452,17 +452,503 @@ export default defineComponent({
|
|
|
452
452
|
}, [], {
|
|
453
453
|
lazy: props.searchableLazy
|
|
454
454
|
});
|
|
455
|
+
function escapeRegExp(string) {
|
|
456
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\<script lang="ts">
|
|
457
|
+
import { ref, computed, toRef, watch, defineComponent, toRaw } from 'vue'
|
|
458
|
+
import type { PropType } from 'vue'
|
|
459
|
+
import {
|
|
460
|
+
Combobox as HCombobox,
|
|
461
|
+
ComboboxButton as HComboboxButton,
|
|
462
|
+
ComboboxOptions as HComboboxOptions,
|
|
463
|
+
ComboboxOption as HComboboxOption,
|
|
464
|
+
ComboboxInput as HComboboxInput,
|
|
465
|
+
Listbox as HListbox,
|
|
466
|
+
ListboxButton as HListboxButton,
|
|
467
|
+
ListboxOptions as HListboxOptions,
|
|
468
|
+
ListboxOption as HListboxOption,
|
|
469
|
+
provideUseId
|
|
470
|
+
} from '@headlessui/vue'
|
|
471
|
+
import { computedAsync, useDebounceFn } from '@vueuse/core'
|
|
472
|
+
import { defu } from 'defu'
|
|
473
|
+
import { twMerge, twJoin } from 'tailwind-merge'
|
|
474
|
+
import UIcon from '../elements/Icon.vue'
|
|
475
|
+
import UAvatar from '../elements/Avatar.vue'
|
|
476
|
+
import { useUI } from '../../composables/useUI'
|
|
477
|
+
import { usePopper } from '../../composables/usePopper'
|
|
478
|
+
import { useFormGroup } from '../../composables/useFormGroup'
|
|
479
|
+
import { get, mergeConfig } from '../../utils'
|
|
480
|
+
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
|
481
|
+
import type { SelectSize, SelectColor, SelectVariant, PopperOptions, Strategy, DeepPartial } from '../../types/index'
|
|
482
|
+
// @ts-expect-error
|
|
483
|
+
import appConfig from '#build/app.config'
|
|
484
|
+
import { select, selectMenu } from '#ui/ui.config'
|
|
485
|
+
import { useId } from '#imports'
|
|
486
|
+
|
|
487
|
+
const config = mergeConfig<typeof select>(appConfig.ui.strategy, appConfig.ui.select, select)
|
|
488
|
+
|
|
489
|
+
const configMenu = mergeConfig<typeof selectMenu>(appConfig.ui.strategy, appConfig.ui.selectMenu, selectMenu)
|
|
490
|
+
|
|
491
|
+
export default defineComponent({
|
|
492
|
+
components: {
|
|
493
|
+
HCombobox,
|
|
494
|
+
HComboboxButton,
|
|
495
|
+
HComboboxOptions,
|
|
496
|
+
HComboboxOption,
|
|
497
|
+
HComboboxInput,
|
|
498
|
+
HListbox,
|
|
499
|
+
HListboxButton,
|
|
500
|
+
HListboxOptions,
|
|
501
|
+
HListboxOption,
|
|
502
|
+
UIcon,
|
|
503
|
+
UAvatar
|
|
504
|
+
},
|
|
505
|
+
inheritAttrs: false,
|
|
506
|
+
props: {
|
|
507
|
+
modelValue: {
|
|
508
|
+
type: [String, Number, Object, Array, Boolean] as PropType<string | number | object | Array<any> | boolean | null>,
|
|
509
|
+
default: ''
|
|
510
|
+
},
|
|
511
|
+
query: {
|
|
512
|
+
type: String,
|
|
513
|
+
default: null
|
|
514
|
+
},
|
|
515
|
+
by: {
|
|
516
|
+
type: String,
|
|
517
|
+
default: undefined
|
|
518
|
+
},
|
|
519
|
+
options: {
|
|
520
|
+
type: Array as PropType<{ [key: string]: any, disabled?: boolean }[] | string[]>,
|
|
521
|
+
default: () => []
|
|
522
|
+
},
|
|
523
|
+
id: {
|
|
524
|
+
type: String,
|
|
525
|
+
default: null
|
|
526
|
+
},
|
|
527
|
+
name: {
|
|
528
|
+
type: String,
|
|
529
|
+
default: null
|
|
530
|
+
},
|
|
531
|
+
required: {
|
|
532
|
+
type: Boolean,
|
|
533
|
+
default: false
|
|
534
|
+
},
|
|
535
|
+
icon: {
|
|
536
|
+
type: String,
|
|
537
|
+
default: null
|
|
538
|
+
},
|
|
539
|
+
loadingIcon: {
|
|
540
|
+
type: String,
|
|
541
|
+
default: () => config.default.loadingIcon
|
|
542
|
+
},
|
|
543
|
+
leadingIcon: {
|
|
544
|
+
type: String,
|
|
545
|
+
default: null
|
|
546
|
+
},
|
|
547
|
+
trailingIcon: {
|
|
548
|
+
type: String,
|
|
549
|
+
default: () => config.default.trailingIcon
|
|
550
|
+
},
|
|
551
|
+
trailing: {
|
|
552
|
+
type: Boolean,
|
|
553
|
+
default: false
|
|
554
|
+
},
|
|
555
|
+
leading: {
|
|
556
|
+
type: Boolean,
|
|
557
|
+
default: false
|
|
558
|
+
},
|
|
559
|
+
loading: {
|
|
560
|
+
type: Boolean,
|
|
561
|
+
default: false
|
|
562
|
+
},
|
|
563
|
+
selectedIcon: {
|
|
564
|
+
type: String,
|
|
565
|
+
default: () => configMenu.default.selectedIcon
|
|
566
|
+
},
|
|
567
|
+
disabled: {
|
|
568
|
+
type: Boolean,
|
|
569
|
+
default: false
|
|
570
|
+
},
|
|
571
|
+
multiple: {
|
|
572
|
+
type: Boolean,
|
|
573
|
+
default: false
|
|
574
|
+
},
|
|
575
|
+
searchable: {
|
|
576
|
+
type: [Boolean, Function] as PropType<boolean | ((query: string) => Promise<any[]> | any[])>,
|
|
577
|
+
default: false
|
|
578
|
+
},
|
|
579
|
+
searchablePlaceholder: {
|
|
580
|
+
type: String,
|
|
581
|
+
default: () => configMenu.default.searchablePlaceholder.label
|
|
582
|
+
},
|
|
583
|
+
searchableLazy: {
|
|
584
|
+
type: Boolean,
|
|
585
|
+
default: false
|
|
586
|
+
},
|
|
587
|
+
clearSearchOnClose: {
|
|
588
|
+
type: Boolean,
|
|
589
|
+
default: () => configMenu.default.clearSearchOnClose
|
|
590
|
+
},
|
|
591
|
+
debounce: {
|
|
592
|
+
type: Number,
|
|
593
|
+
default: 200
|
|
594
|
+
},
|
|
595
|
+
creatable: {
|
|
596
|
+
type: Boolean,
|
|
597
|
+
default: false
|
|
598
|
+
},
|
|
599
|
+
showCreateOptionWhen: {
|
|
600
|
+
type: [String, Function] as PropType<'always' | 'empty' | ((query: string, results: any[]) => boolean)>,
|
|
601
|
+
default: () => configMenu.default.showCreateOptionWhen
|
|
602
|
+
},
|
|
603
|
+
placeholder: {
|
|
604
|
+
type: String,
|
|
605
|
+
default: null
|
|
606
|
+
},
|
|
607
|
+
padded: {
|
|
608
|
+
type: Boolean,
|
|
609
|
+
default: true
|
|
610
|
+
},
|
|
611
|
+
size: {
|
|
612
|
+
type: String as PropType<SelectSize>,
|
|
613
|
+
default: null,
|
|
614
|
+
validator(value: string) {
|
|
615
|
+
return Object.keys(config.size).includes(value)
|
|
616
|
+
}
|
|
617
|
+
},
|
|
618
|
+
color: {
|
|
619
|
+
type: String as PropType<SelectColor>,
|
|
620
|
+
default: () => config.default.color,
|
|
621
|
+
validator(value: string) {
|
|
622
|
+
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
|
623
|
+
}
|
|
624
|
+
},
|
|
625
|
+
variant: {
|
|
626
|
+
type: String as PropType<SelectVariant>,
|
|
627
|
+
default: () => config.default.variant,
|
|
628
|
+
validator(value: string) {
|
|
629
|
+
return [
|
|
630
|
+
...Object.keys(config.variant),
|
|
631
|
+
...Object.values(config.color).flatMap(value => Object.keys(value))
|
|
632
|
+
].includes(value)
|
|
633
|
+
}
|
|
634
|
+
},
|
|
635
|
+
optionAttribute: {
|
|
636
|
+
type: String,
|
|
637
|
+
default: 'label'
|
|
638
|
+
},
|
|
639
|
+
valueAttribute: {
|
|
640
|
+
type: String,
|
|
641
|
+
default: null
|
|
642
|
+
},
|
|
643
|
+
searchAttributes: {
|
|
644
|
+
type: Array,
|
|
645
|
+
default: null
|
|
646
|
+
},
|
|
647
|
+
popper: {
|
|
648
|
+
type: Object as PropType<PopperOptions>,
|
|
649
|
+
default: () => ({})
|
|
650
|
+
},
|
|
651
|
+
selectClass: {
|
|
652
|
+
type: String,
|
|
653
|
+
default: null
|
|
654
|
+
},
|
|
655
|
+
class: {
|
|
656
|
+
type: [String, Object, Array] as PropType<any>,
|
|
657
|
+
default: () => ''
|
|
658
|
+
},
|
|
659
|
+
ui: {
|
|
660
|
+
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
|
|
661
|
+
default: () => ({})
|
|
662
|
+
},
|
|
663
|
+
uiMenu: {
|
|
664
|
+
type: Object as PropType<DeepPartial<typeof configMenu> & { strategy?: Strategy }>,
|
|
665
|
+
default: () => ({})
|
|
666
|
+
}
|
|
667
|
+
},
|
|
668
|
+
emits: ['update:modelValue', 'update:query', 'open', 'close', 'change'],
|
|
669
|
+
setup(props, { emit, slots }) {
|
|
670
|
+
if (import.meta.dev && props.multiple && !Array.isArray(props.modelValue)) {
|
|
671
|
+
console.warn(`[@nuxt/ui] The USelectMenu components needs to have a modelValue of type Array when using the multiple prop. Got '${typeof props.modelValue}' instead.`, props.modelValue)
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const { ui, attrs } = useUI('select', toRef(props, 'ui'), config, toRef(props, 'class'))
|
|
675
|
+
const { ui: uiMenu } = useUI('selectMenu', toRef(props, 'uiMenu'), configMenu)
|
|
676
|
+
|
|
677
|
+
const popper = computed<PopperOptions>(() => defu({}, props.popper, uiMenu.value.popper as PopperOptions))
|
|
678
|
+
|
|
679
|
+
const [trigger, container] = usePopper(popper.value)
|
|
680
|
+
|
|
681
|
+
const { size: sizeButtonGroup, rounded } = useInjectButtonGroup({ ui, props })
|
|
682
|
+
const { emitFormBlur, emitFormChange, inputId, color, size: sizeFormGroup, name } = useFormGroup(props, config)
|
|
683
|
+
|
|
684
|
+
const size = computed(() => sizeButtonGroup.value ?? sizeFormGroup.value)
|
|
685
|
+
|
|
686
|
+
const internalQuery = ref('')
|
|
687
|
+
const query = computed({
|
|
688
|
+
get() {
|
|
689
|
+
return props.query ?? internalQuery.value
|
|
690
|
+
},
|
|
691
|
+
set(value) {
|
|
692
|
+
internalQuery.value = value
|
|
693
|
+
emit('update:query', value)
|
|
694
|
+
}
|
|
695
|
+
})
|
|
696
|
+
|
|
697
|
+
const selected = computed(() => {
|
|
698
|
+
if (props.multiple) {
|
|
699
|
+
if (!Array.isArray(props.modelValue) || !props.modelValue.length) {
|
|
700
|
+
return []
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (props.valueAttribute) {
|
|
704
|
+
return options.value.filter(option => (props.modelValue as any[]).includes(option[props.valueAttribute]))
|
|
705
|
+
}
|
|
706
|
+
return options.value.filter(option => (props.modelValue as any[]).includes(option))
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
if (props.valueAttribute) {
|
|
710
|
+
return options.value.find(option => option[props.valueAttribute] === props.modelValue)
|
|
711
|
+
}
|
|
712
|
+
return options.value.find(option => option === props.modelValue)
|
|
713
|
+
})
|
|
714
|
+
|
|
715
|
+
const label = computed(() => {
|
|
716
|
+
if (props.multiple) {
|
|
717
|
+
if (Array.isArray(props.modelValue) && props.modelValue.length) {
|
|
718
|
+
return `${selected.value.length} selected`
|
|
719
|
+
} else {
|
|
720
|
+
return null
|
|
721
|
+
}
|
|
722
|
+
} else if (props.modelValue !== undefined && props.modelValue !== null) {
|
|
723
|
+
if (props.valueAttribute) {
|
|
724
|
+
return accessor(selected.value, props.optionAttribute) ?? null
|
|
725
|
+
} else {
|
|
726
|
+
return ['string', 'number'].includes(typeof props.modelValue) ? props.modelValue : accessor(props.modelValue as Record<string, any>, props.optionAttribute)
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
return null
|
|
731
|
+
})
|
|
732
|
+
|
|
733
|
+
const selectClass = computed(() => {
|
|
734
|
+
const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
|
735
|
+
|
|
736
|
+
return twMerge(twJoin(
|
|
737
|
+
ui.value.base,
|
|
738
|
+
uiMenu.value.select,
|
|
739
|
+
rounded.value,
|
|
740
|
+
ui.value.size[size.value],
|
|
741
|
+
ui.value.gap[size.value],
|
|
742
|
+
props.padded ? ui.value.padding[size.value] : 'p-0',
|
|
743
|
+
variant?.replaceAll('{color}', color.value),
|
|
744
|
+
(isLeading.value || slots.leading) && ui.value.leading.padding[size.value],
|
|
745
|
+
(isTrailing.value || slots.trailing) && ui.value.trailing.padding[size.value]
|
|
746
|
+
), props.placeholder && (!props.modelValue || (Array.isArray(props.modelValue) && !props.modelValue.length)) && ui.value.placeholder, props.selectClass)
|
|
747
|
+
})
|
|
748
|
+
|
|
749
|
+
const isLeading = computed(() => {
|
|
750
|
+
return (props.icon && props.leading) || (props.icon && !props.trailing) || (props.loading && !props.trailing) || props.leadingIcon
|
|
751
|
+
})
|
|
752
|
+
|
|
753
|
+
const isTrailing = computed(() => {
|
|
754
|
+
return (props.icon && props.trailing) || (props.loading && props.trailing) || props.trailingIcon
|
|
755
|
+
})
|
|
756
|
+
|
|
757
|
+
const leadingIconName = computed(() => {
|
|
758
|
+
if (props.loading) {
|
|
759
|
+
return props.loadingIcon
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
return props.leadingIcon || props.icon
|
|
763
|
+
})
|
|
764
|
+
|
|
765
|
+
const trailingIconName = computed(() => {
|
|
766
|
+
if (props.loading && !isLeading.value) {
|
|
767
|
+
return props.loadingIcon
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
return props.trailingIcon || props.icon
|
|
771
|
+
})
|
|
772
|
+
|
|
773
|
+
const leadingWrapperIconClass = computed(() => {
|
|
774
|
+
return twJoin(
|
|
775
|
+
ui.value.icon.leading.wrapper,
|
|
776
|
+
ui.value.icon.leading.pointer,
|
|
777
|
+
ui.value.icon.leading.padding[size.value]
|
|
778
|
+
)
|
|
779
|
+
})
|
|
780
|
+
|
|
781
|
+
const leadingIconClass = computed(() => {
|
|
782
|
+
return twJoin(
|
|
783
|
+
ui.value.icon.base,
|
|
784
|
+
color.value && appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
|
785
|
+
ui.value.icon.size[size.value],
|
|
786
|
+
props.loading && ui.value.icon.loading
|
|
787
|
+
)
|
|
788
|
+
})
|
|
789
|
+
|
|
790
|
+
const trailingWrapperIconClass = computed(() => {
|
|
791
|
+
return twJoin(
|
|
792
|
+
ui.value.icon.trailing.wrapper,
|
|
793
|
+
ui.value.icon.trailing.pointer,
|
|
794
|
+
ui.value.icon.trailing.padding[size.value]
|
|
795
|
+
)
|
|
796
|
+
})
|
|
797
|
+
|
|
798
|
+
const trailingIconClass = computed(() => {
|
|
799
|
+
return twJoin(
|
|
800
|
+
ui.value.icon.base,
|
|
801
|
+
color.value && appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
|
802
|
+
ui.value.icon.size[size.value],
|
|
803
|
+
props.loading && !isLeading.value && ui.value.icon.loading
|
|
804
|
+
)
|
|
805
|
+
})
|
|
806
|
+
|
|
807
|
+
const debouncedSearch = props.searchable && typeof props.searchable === 'function' ? useDebounceFn(props.searchable, props.debounce) : undefined
|
|
808
|
+
|
|
809
|
+
const options = computedAsync(async () => {
|
|
810
|
+
if (debouncedSearch) {
|
|
811
|
+
return await debouncedSearch(query.value)
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
return props.options || []
|
|
815
|
+
}, [], {
|
|
816
|
+
lazy: props.searchableLazy
|
|
817
|
+
})
|
|
818
|
+
|
|
819
|
+
function escapeRegExp(string: string) {
|
|
820
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function accessor<T extends Record<string, any>>(obj: T, key: string) {
|
|
824
|
+
return get(obj, key)
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
const filteredOptions = computed(() => {
|
|
828
|
+
if (!query.value || debouncedSearch) {
|
|
829
|
+
return options.value
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
const escapedQuery = escapeRegExp(query.value)
|
|
833
|
+
|
|
834
|
+
return options.value.filter((option: any) => {
|
|
835
|
+
return (props.searchAttributes?.length ? props.searchAttributes : [props.optionAttribute]).some((searchAttribute: any) => {
|
|
836
|
+
if (['string', 'number'].includes(typeof option)) {
|
|
837
|
+
return String(option).search(new RegExp(escapedQuery, 'i')) !== -1
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
const child = get(option, searchAttribute)
|
|
841
|
+
|
|
842
|
+
return child !== null && child !== undefined && String(child).search(new RegExp(escapedQuery, 'i')) !== -1
|
|
843
|
+
})
|
|
844
|
+
})
|
|
845
|
+
})
|
|
846
|
+
|
|
847
|
+
const createOption = computed(() => {
|
|
848
|
+
if (query.value === '') {
|
|
849
|
+
return null
|
|
850
|
+
}
|
|
851
|
+
if (props.showCreateOptionWhen === 'empty' && filteredOptions.value.length) {
|
|
852
|
+
return null
|
|
853
|
+
}
|
|
854
|
+
if (props.showCreateOptionWhen === 'always') {
|
|
855
|
+
const existingOption = filteredOptions.value.find(option => ['string', 'number'].includes(typeof option) ? option === query.value : accessor(option, props.optionAttribute) === query.value)
|
|
856
|
+
if (existingOption) {
|
|
857
|
+
return null
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
if (typeof props.showCreateOptionWhen === 'function') {
|
|
861
|
+
if (!props.showCreateOptionWhen(query.value, filteredOptions.value)) {
|
|
862
|
+
return null
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
return ['string', 'number'].includes(typeof props.modelValue) ? query.value : { [props.optionAttribute]: query.value }
|
|
866
|
+
})
|
|
867
|
+
|
|
868
|
+
function clearOnClose() {
|
|
869
|
+
if (props.clearSearchOnClose) {
|
|
870
|
+
query.value = ''
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
watch(container, (value) => {
|
|
875
|
+
if (value) {
|
|
876
|
+
emit('open')
|
|
877
|
+
} else {
|
|
878
|
+
clearOnClose()
|
|
879
|
+
emit('close')
|
|
880
|
+
emitFormBlur()
|
|
881
|
+
}
|
|
882
|
+
})
|
|
883
|
+
|
|
884
|
+
function onUpdate(value: any) {
|
|
885
|
+
if (toRaw(props.modelValue) === value) {
|
|
886
|
+
return
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
emit('update:modelValue', value)
|
|
890
|
+
emit('change', value)
|
|
891
|
+
emitFormChange()
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
function onQueryChange(event: any) {
|
|
895
|
+
query.value = event.target.value
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
provideUseId(() => useId())
|
|
899
|
+
|
|
900
|
+
return {
|
|
901
|
+
// eslint-disable-next-line vue/no-dupe-keys
|
|
902
|
+
ui,
|
|
903
|
+
// eslint-disable-next-line vue/no-dupe-keys
|
|
904
|
+
uiMenu,
|
|
905
|
+
attrs,
|
|
906
|
+
// eslint-disable-next-line vue/no-dupe-keys
|
|
907
|
+
name,
|
|
908
|
+
inputId,
|
|
909
|
+
// eslint-disable-next-line vue/no-dupe-keys
|
|
910
|
+
popper,
|
|
911
|
+
trigger,
|
|
912
|
+
container,
|
|
913
|
+
selected,
|
|
914
|
+
label,
|
|
915
|
+
accessor,
|
|
916
|
+
isLeading,
|
|
917
|
+
isTrailing,
|
|
918
|
+
// eslint-disable-next-line vue/no-dupe-keys
|
|
919
|
+
selectClass,
|
|
920
|
+
leadingIconName,
|
|
921
|
+
leadingIconClass,
|
|
922
|
+
leadingWrapperIconClass,
|
|
923
|
+
trailingIconName,
|
|
924
|
+
trailingIconClass,
|
|
925
|
+
trailingWrapperIconClass,
|
|
926
|
+
filteredOptions,
|
|
927
|
+
createOption,
|
|
928
|
+
// eslint-disable-next-line vue/no-dupe-keys
|
|
929
|
+
query,
|
|
930
|
+
onUpdate,
|
|
931
|
+
onQueryChange
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
})
|
|
935
|
+
</script>");
|
|
936
|
+
}
|
|
937
|
+
function accessor(obj, key) {
|
|
938
|
+
return get(obj, key);
|
|
939
|
+
}
|
|
455
940
|
const filteredOptions = computed(() => {
|
|
456
941
|
if (!query.value || debouncedSearch) {
|
|
457
942
|
return options.value;
|
|
458
943
|
}
|
|
944
|
+
const escapedQuery = escapeRegExp(query.value);
|
|
459
945
|
return options.value.filter((option) => {
|
|
460
946
|
return (props.searchAttributes?.length ? props.searchAttributes : [props.optionAttribute]).some((searchAttribute) => {
|
|
461
947
|
if (["string", "number"].includes(typeof option)) {
|
|
462
|
-
return String(option).search(new RegExp(
|
|
948
|
+
return String(option).search(new RegExp(escapedQuery, "i")) !== -1;
|
|
463
949
|
}
|
|
464
950
|
const child = get(option, searchAttribute);
|
|
465
|
-
return child !== null && child !== void 0 && String(child).search(new RegExp(
|
|
951
|
+
return child !== null && child !== void 0 && String(child).search(new RegExp(escapedQuery, "i")) !== -1;
|
|
466
952
|
});
|
|
467
953
|
});
|
|
468
954
|
});
|
|
@@ -474,7 +960,7 @@ export default defineComponent({
|
|
|
474
960
|
return null;
|
|
475
961
|
}
|
|
476
962
|
if (props.showCreateOptionWhen === "always") {
|
|
477
|
-
const existingOption = filteredOptions.value.find((option) => ["string", "number"].includes(typeof option) ? option === query.value : option
|
|
963
|
+
const existingOption = filteredOptions.value.find((option) => ["string", "number"].includes(typeof option) ? option === query.value : accessor(option, props.optionAttribute) === query.value);
|
|
478
964
|
if (existingOption) {
|
|
479
965
|
return null;
|
|
480
966
|
}
|
|
@@ -501,6 +987,9 @@ export default defineComponent({
|
|
|
501
987
|
}
|
|
502
988
|
});
|
|
503
989
|
function onUpdate(value) {
|
|
990
|
+
if (toRaw(props.modelValue) === value) {
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
504
993
|
emit("update:modelValue", value);
|
|
505
994
|
emit("change", value);
|
|
506
995
|
emitFormChange();
|
|
@@ -524,6 +1013,7 @@ export default defineComponent({
|
|
|
524
1013
|
container,
|
|
525
1014
|
selected,
|
|
526
1015
|
label,
|
|
1016
|
+
accessor,
|
|
527
1017
|
isLeading,
|
|
528
1018
|
isTrailing,
|
|
529
1019
|
// eslint-disable-next-line vue/no-dupe-keys
|
|
@@ -158,6 +158,15 @@ declare const configMenu: {
|
|
|
158
158
|
selectedIcon: string;
|
|
159
159
|
clearSearchOnClose: boolean;
|
|
160
160
|
showCreateOptionWhen: string;
|
|
161
|
+
searchablePlaceholder: {
|
|
162
|
+
label: string;
|
|
163
|
+
};
|
|
164
|
+
empty: {
|
|
165
|
+
label: string;
|
|
166
|
+
};
|
|
167
|
+
optionEmpty: {
|
|
168
|
+
label: string;
|
|
169
|
+
};
|
|
161
170
|
};
|
|
162
171
|
arrow: {
|
|
163
172
|
ring: string;
|
|
@@ -181,7 +190,7 @@ declare const configMenu: {
|
|
|
181
190
|
};
|
|
182
191
|
declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
183
192
|
modelValue: {
|
|
184
|
-
type:
|
|
193
|
+
type: PropType<string | number | object | Array<any> | boolean | null>;
|
|
185
194
|
default: string;
|
|
186
195
|
};
|
|
187
196
|
query: {
|
|
@@ -257,7 +266,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
257
266
|
};
|
|
258
267
|
searchablePlaceholder: {
|
|
259
268
|
type: StringConstructor;
|
|
260
|
-
default: string;
|
|
269
|
+
default: () => string;
|
|
261
270
|
};
|
|
262
271
|
searchableLazy: {
|
|
263
272
|
type: BooleanConstructor;
|
|
@@ -497,6 +506,15 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
497
506
|
selectedIcon: string;
|
|
498
507
|
clearSearchOnClose: boolean;
|
|
499
508
|
showCreateOptionWhen: string;
|
|
509
|
+
searchablePlaceholder: {
|
|
510
|
+
label: string;
|
|
511
|
+
};
|
|
512
|
+
empty: {
|
|
513
|
+
label: string;
|
|
514
|
+
};
|
|
515
|
+
optionEmpty: {
|
|
516
|
+
label: string;
|
|
517
|
+
};
|
|
500
518
|
};
|
|
501
519
|
arrow: {
|
|
502
520
|
ring: string;
|
|
@@ -528,6 +546,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
528
546
|
container: import("vue").Ref<import("@vueuse/core").MaybeElement, import("@vueuse/core").MaybeElement>;
|
|
529
547
|
selected: import("vue").ComputedRef<any>;
|
|
530
548
|
label: import("vue").ComputedRef<any>;
|
|
549
|
+
accessor: <T extends Record<string, any>>(obj: T, key: string) => any;
|
|
531
550
|
isLeading: import("vue").ComputedRef<string | true>;
|
|
532
551
|
isTrailing: import("vue").ComputedRef<string | true>;
|
|
533
552
|
selectClass: import("vue").ComputedRef<string>;
|
|
@@ -546,7 +565,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
546
565
|
onQueryChange: (event: any) => void;
|
|
547
566
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("change" | "close" | "update:modelValue" | "open" | "update:query")[], "change" | "close" | "update:modelValue" | "open" | "update:query", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
548
567
|
modelValue: {
|
|
549
|
-
type:
|
|
568
|
+
type: PropType<string | number | object | Array<any> | boolean | null>;
|
|
550
569
|
default: string;
|
|
551
570
|
};
|
|
552
571
|
query: {
|
|
@@ -622,7 +641,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
622
641
|
};
|
|
623
642
|
searchablePlaceholder: {
|
|
624
643
|
type: StringConstructor;
|
|
625
|
-
default: string;
|
|
644
|
+
default: () => string;
|
|
626
645
|
};
|
|
627
646
|
searchableLazy: {
|
|
628
647
|
type: BooleanConstructor;
|
|
@@ -840,7 +859,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
840
859
|
[key: string]: any;
|
|
841
860
|
disabled?: boolean;
|
|
842
861
|
}[];
|
|
843
|
-
modelValue: string | number | boolean |
|
|
862
|
+
modelValue: string | number | boolean | object | any[];
|
|
844
863
|
loading: boolean;
|
|
845
864
|
padded: boolean;
|
|
846
865
|
loadingIcon: string;
|
|
@@ -903,6 +922,15 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
903
922
|
selectedIcon: string;
|
|
904
923
|
clearSearchOnClose: boolean;
|
|
905
924
|
showCreateOptionWhen: string;
|
|
925
|
+
searchablePlaceholder: {
|
|
926
|
+
label: string;
|
|
927
|
+
};
|
|
928
|
+
empty: {
|
|
929
|
+
label: string;
|
|
930
|
+
};
|
|
931
|
+
optionEmpty: {
|
|
932
|
+
label: string;
|
|
933
|
+
};
|
|
906
934
|
}, any>;
|
|
907
935
|
arrow?: DeepPartial<{
|
|
908
936
|
ring: string;
|