@nuxt/ui 2.19.0 → 2.19.2
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.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/components/elements/ButtonGroup.d.ts +1 -1
- package/dist/runtime/components/elements/MeterGroup.d.ts +1 -1
- package/dist/runtime/components/forms/InputMenu.vue +1 -401
- package/dist/runtime/components/forms/SelectMenu.vue +1 -480
- package/dist/runtime/types/button.d.ts +1 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -14,7 +14,7 @@ const __filename = __cjs_url__.fileURLToPath(import.meta.url);
|
|
|
14
14
|
const __dirname = __cjs_path__.dirname(__filename);
|
|
15
15
|
const require = __cjs_mod__.createRequire(import.meta.url);
|
|
16
16
|
const name = "@nuxt/ui";
|
|
17
|
-
const version = "2.19.
|
|
17
|
+
const version = "2.19.2";
|
|
18
18
|
|
|
19
19
|
function createTemplates(nuxt = useNuxt()) {
|
|
20
20
|
const template = addTemplate({
|
|
@@ -145,8 +145,8 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
145
145
|
default: () => {};
|
|
146
146
|
};
|
|
147
147
|
}>> & Readonly<{}>, {
|
|
148
|
-
orientation: "horizontal" | "vertical";
|
|
149
148
|
size: ButtonSize;
|
|
149
|
+
orientation: "horizontal" | "vertical";
|
|
150
150
|
ui: Partial<{
|
|
151
151
|
wrapper: {
|
|
152
152
|
horizontal: string;
|
|
@@ -373,407 +373,7 @@ export default defineComponent({
|
|
|
373
373
|
lazy: props.searchLazy
|
|
374
374
|
});
|
|
375
375
|
function escapeRegExp(string) {
|
|
376
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g,
|
|
377
|
-
import { ref, computed, toRef, watch, defineComponent, toRaw } from 'vue'
|
|
378
|
-
import type { PropType } from 'vue'
|
|
379
|
-
import {
|
|
380
|
-
Combobox as HCombobox,
|
|
381
|
-
ComboboxButton as HComboboxButton,
|
|
382
|
-
ComboboxOptions as HComboboxOptions,
|
|
383
|
-
ComboboxOption as HComboboxOption,
|
|
384
|
-
ComboboxInput as HComboboxInput,
|
|
385
|
-
provideUseId
|
|
386
|
-
} from '@headlessui/vue'
|
|
387
|
-
import { computedAsync, useDebounceFn } from '@vueuse/core'
|
|
388
|
-
import { defu } from 'defu'
|
|
389
|
-
import { twMerge, twJoin } from 'tailwind-merge'
|
|
390
|
-
import UIcon from '../elements/Icon.vue'
|
|
391
|
-
import UAvatar from '../elements/Avatar.vue'
|
|
392
|
-
import { useUI } from '../../composables/useUI'
|
|
393
|
-
import { usePopper } from '../../composables/usePopper'
|
|
394
|
-
import { useFormGroup } from '../../composables/useFormGroup'
|
|
395
|
-
import { get, mergeConfig } from '../../utils'
|
|
396
|
-
import { useInjectButtonGroup } from '../../composables/useButtonGroup'
|
|
397
|
-
import type { InputSize, InputColor, InputVariant, PopperOptions, Strategy, DeepPartial } from '../../types/index'
|
|
398
|
-
// @ts-expect-error
|
|
399
|
-
import appConfig from '#build/app.config'
|
|
400
|
-
import { input, inputMenu } from '#ui/ui.config'
|
|
401
|
-
import { useId } from '#imports'
|
|
402
|
-
|
|
403
|
-
const config = mergeConfig<typeof input>(appConfig.ui.strategy, appConfig.ui.input, input)
|
|
404
|
-
|
|
405
|
-
const configMenu = mergeConfig<typeof inputMenu>(appConfig.ui.strategy, appConfig.ui.inputMenu, inputMenu)
|
|
406
|
-
|
|
407
|
-
export default defineComponent({
|
|
408
|
-
components: {
|
|
409
|
-
HCombobox,
|
|
410
|
-
HComboboxButton,
|
|
411
|
-
HComboboxOptions,
|
|
412
|
-
HComboboxOption,
|
|
413
|
-
HComboboxInput,
|
|
414
|
-
UIcon,
|
|
415
|
-
UAvatar
|
|
416
|
-
},
|
|
417
|
-
inheritAttrs: false,
|
|
418
|
-
props: {
|
|
419
|
-
modelValue: {
|
|
420
|
-
type: [String, Number, Object, Array] as PropType<string | number | object | Array<any> | null>,
|
|
421
|
-
default: ''
|
|
422
|
-
},
|
|
423
|
-
query: {
|
|
424
|
-
type: String,
|
|
425
|
-
default: null
|
|
426
|
-
},
|
|
427
|
-
by: {
|
|
428
|
-
type: String,
|
|
429
|
-
default: undefined
|
|
430
|
-
},
|
|
431
|
-
options: {
|
|
432
|
-
type: Array as PropType<{ [key: string]: any, disabled?: boolean }[] | string[]>,
|
|
433
|
-
default: () => []
|
|
434
|
-
},
|
|
435
|
-
id: {
|
|
436
|
-
type: String,
|
|
437
|
-
default: null
|
|
438
|
-
},
|
|
439
|
-
name: {
|
|
440
|
-
type: String,
|
|
441
|
-
default: null
|
|
442
|
-
},
|
|
443
|
-
required: {
|
|
444
|
-
type: Boolean,
|
|
445
|
-
default: false
|
|
446
|
-
},
|
|
447
|
-
icon: {
|
|
448
|
-
type: String,
|
|
449
|
-
default: null
|
|
450
|
-
},
|
|
451
|
-
loadingIcon: {
|
|
452
|
-
type: String,
|
|
453
|
-
default: () => config.default.loadingIcon
|
|
454
|
-
},
|
|
455
|
-
leadingIcon: {
|
|
456
|
-
type: String,
|
|
457
|
-
default: null
|
|
458
|
-
},
|
|
459
|
-
trailingIcon: {
|
|
460
|
-
type: String,
|
|
461
|
-
default: () => configMenu.default.trailingIcon
|
|
462
|
-
},
|
|
463
|
-
trailing: {
|
|
464
|
-
type: Boolean,
|
|
465
|
-
default: false
|
|
466
|
-
},
|
|
467
|
-
leading: {
|
|
468
|
-
type: Boolean,
|
|
469
|
-
default: false
|
|
470
|
-
},
|
|
471
|
-
loading: {
|
|
472
|
-
type: Boolean,
|
|
473
|
-
default: false
|
|
474
|
-
},
|
|
475
|
-
selectedIcon: {
|
|
476
|
-
type: String,
|
|
477
|
-
default: () => configMenu.default.selectedIcon
|
|
478
|
-
},
|
|
479
|
-
disabled: {
|
|
480
|
-
type: Boolean,
|
|
481
|
-
default: false
|
|
482
|
-
},
|
|
483
|
-
nullable: {
|
|
484
|
-
type: Boolean,
|
|
485
|
-
default: false
|
|
486
|
-
},
|
|
487
|
-
placeholder: {
|
|
488
|
-
type: String,
|
|
489
|
-
default: null
|
|
490
|
-
},
|
|
491
|
-
padded: {
|
|
492
|
-
type: Boolean,
|
|
493
|
-
default: true
|
|
494
|
-
},
|
|
495
|
-
size: {
|
|
496
|
-
type: String as PropType<InputSize>,
|
|
497
|
-
default: null,
|
|
498
|
-
validator(value: string) {
|
|
499
|
-
return Object.keys(config.size).includes(value)
|
|
500
|
-
}
|
|
501
|
-
},
|
|
502
|
-
color: {
|
|
503
|
-
type: String as PropType<InputColor>,
|
|
504
|
-
default: () => config.default.color,
|
|
505
|
-
validator(value: string) {
|
|
506
|
-
return [...appConfig.ui.colors, ...Object.keys(config.color)].includes(value)
|
|
507
|
-
}
|
|
508
|
-
},
|
|
509
|
-
variant: {
|
|
510
|
-
type: String as PropType<InputVariant>,
|
|
511
|
-
default: () => config.default.variant,
|
|
512
|
-
validator(value: string) {
|
|
513
|
-
return [
|
|
514
|
-
...Object.keys(config.variant),
|
|
515
|
-
...Object.values(config.color).flatMap(value => Object.keys(value))
|
|
516
|
-
].includes(value)
|
|
517
|
-
}
|
|
518
|
-
},
|
|
519
|
-
optionAttribute: {
|
|
520
|
-
type: String,
|
|
521
|
-
default: 'label'
|
|
522
|
-
},
|
|
523
|
-
valueAttribute: {
|
|
524
|
-
type: String,
|
|
525
|
-
default: null
|
|
526
|
-
},
|
|
527
|
-
search: {
|
|
528
|
-
type: Function as PropType<((query: string) => Promise<any[]> | any[])>,
|
|
529
|
-
default: undefined
|
|
530
|
-
},
|
|
531
|
-
searchAttributes: {
|
|
532
|
-
type: Array,
|
|
533
|
-
default: null
|
|
534
|
-
},
|
|
535
|
-
searchLazy: {
|
|
536
|
-
type: Boolean,
|
|
537
|
-
default: false
|
|
538
|
-
},
|
|
539
|
-
debounce: {
|
|
540
|
-
type: Number,
|
|
541
|
-
default: 200
|
|
542
|
-
},
|
|
543
|
-
popper: {
|
|
544
|
-
type: Object as PropType<PopperOptions>,
|
|
545
|
-
default: () => ({})
|
|
546
|
-
},
|
|
547
|
-
inputClass: {
|
|
548
|
-
type: String,
|
|
549
|
-
default: null
|
|
550
|
-
},
|
|
551
|
-
class: {
|
|
552
|
-
type: [String, Object, Array] as PropType<any>,
|
|
553
|
-
default: () => ''
|
|
554
|
-
},
|
|
555
|
-
ui: {
|
|
556
|
-
type: Object as PropType<DeepPartial<typeof config> & { strategy?: Strategy }>,
|
|
557
|
-
default: () => ({})
|
|
558
|
-
},
|
|
559
|
-
uiMenu: {
|
|
560
|
-
type: Object as PropType<DeepPartial<typeof configMenu> & { strategy?: Strategy }>,
|
|
561
|
-
default: () => ({})
|
|
562
|
-
}
|
|
563
|
-
},
|
|
564
|
-
emits: ['update:modelValue', 'update:query', 'open', 'close', 'change'],
|
|
565
|
-
setup(props, { emit, slots }) {
|
|
566
|
-
const { ui, attrs } = useUI('input', toRef(props, 'ui'), config, toRef(props, 'class'))
|
|
567
|
-
const { ui: uiMenu } = useUI('inputMenu', toRef(props, 'uiMenu'), configMenu)
|
|
568
|
-
|
|
569
|
-
const popper = computed<PopperOptions>(() => defu({}, props.popper, uiMenu.value.popper as PopperOptions))
|
|
570
|
-
|
|
571
|
-
const [trigger, container] = usePopper(popper.value)
|
|
572
|
-
|
|
573
|
-
const { size: sizeButtonGroup, rounded } = useInjectButtonGroup({ ui, props })
|
|
574
|
-
const { emitFormBlur, emitFormChange, inputId, color, size: sizeFormGroup, name } = useFormGroup(props, config)
|
|
575
|
-
|
|
576
|
-
const size = computed(() => sizeButtonGroup.value ?? sizeFormGroup.value)
|
|
577
|
-
|
|
578
|
-
const internalQuery = ref('')
|
|
579
|
-
const query = computed({
|
|
580
|
-
get() {
|
|
581
|
-
return props.query ?? internalQuery.value
|
|
582
|
-
},
|
|
583
|
-
set(value) {
|
|
584
|
-
internalQuery.value = value
|
|
585
|
-
emit('update:query', value)
|
|
586
|
-
}
|
|
587
|
-
})
|
|
588
|
-
|
|
589
|
-
const label = computed(() => {
|
|
590
|
-
if (!props.modelValue) {
|
|
591
|
-
return
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
if (props.valueAttribute) {
|
|
595
|
-
const option = options.value.find(option => option[props.valueAttribute] === props.modelValue)
|
|
596
|
-
return option ? accessor(option, props.optionAttribute) : null
|
|
597
|
-
} else {
|
|
598
|
-
return ['string', 'number'].includes(typeof props.modelValue) ? props.modelValue : accessor(props.modelValue as Record<string, any>, props.optionAttribute)
|
|
599
|
-
}
|
|
600
|
-
})
|
|
601
|
-
|
|
602
|
-
const inputClass = computed(() => {
|
|
603
|
-
const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant]
|
|
604
|
-
|
|
605
|
-
return twMerge(twJoin(
|
|
606
|
-
ui.value.base,
|
|
607
|
-
ui.value.form,
|
|
608
|
-
rounded.value,
|
|
609
|
-
ui.value.placeholder,
|
|
610
|
-
ui.value.size[size.value],
|
|
611
|
-
props.padded ? ui.value.padding[size.value] : 'p-0',
|
|
612
|
-
variant?.replaceAll('{color}', color.value),
|
|
613
|
-
(isLeading.value || slots.leading) && ui.value.leading.padding[size.value],
|
|
614
|
-
(isTrailing.value || slots.trailing) && ui.value.trailing.padding[size.value]
|
|
615
|
-
), props.inputClass)
|
|
616
|
-
})
|
|
617
|
-
|
|
618
|
-
const isLeading = computed(() => {
|
|
619
|
-
return (props.icon && props.leading) || (props.icon && !props.trailing) || (props.loading && !props.trailing) || props.leadingIcon
|
|
620
|
-
})
|
|
621
|
-
|
|
622
|
-
const isTrailing = computed(() => {
|
|
623
|
-
return (props.icon && props.trailing) || (props.loading && props.trailing) || props.trailingIcon
|
|
624
|
-
})
|
|
625
|
-
|
|
626
|
-
const leadingIconName = computed(() => {
|
|
627
|
-
if (props.loading) {
|
|
628
|
-
return props.loadingIcon
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
return props.leadingIcon || props.icon
|
|
632
|
-
})
|
|
633
|
-
|
|
634
|
-
const trailingIconName = computed(() => {
|
|
635
|
-
if (props.loading && !isLeading.value) {
|
|
636
|
-
return props.loadingIcon
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
return props.trailingIcon || props.icon
|
|
640
|
-
})
|
|
641
|
-
|
|
642
|
-
const leadingWrapperIconClass = computed(() => {
|
|
643
|
-
return twJoin(
|
|
644
|
-
ui.value.icon.leading.wrapper,
|
|
645
|
-
ui.value.icon.leading.pointer,
|
|
646
|
-
ui.value.icon.leading.padding[size.value]
|
|
647
|
-
)
|
|
648
|
-
})
|
|
649
|
-
|
|
650
|
-
const leadingIconClass = computed(() => {
|
|
651
|
-
return twJoin(
|
|
652
|
-
ui.value.icon.base,
|
|
653
|
-
color.value && appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
|
654
|
-
ui.value.icon.size[size.value],
|
|
655
|
-
props.loading && ui.value.icon.loading
|
|
656
|
-
)
|
|
657
|
-
})
|
|
658
|
-
|
|
659
|
-
const trailingWrapperIconClass = computed(() => {
|
|
660
|
-
return twJoin(
|
|
661
|
-
ui.value.icon.trailing.wrapper,
|
|
662
|
-
ui.value.icon.trailing.padding[size.value]
|
|
663
|
-
)
|
|
664
|
-
})
|
|
665
|
-
|
|
666
|
-
const trailingIconClass = computed(() => {
|
|
667
|
-
return twJoin(
|
|
668
|
-
ui.value.icon.base,
|
|
669
|
-
color.value && appConfig.ui.colors.includes(color.value) && ui.value.icon.color.replaceAll('{color}', color.value),
|
|
670
|
-
ui.value.icon.size[size.value],
|
|
671
|
-
props.loading && !isLeading.value && ui.value.icon.loading
|
|
672
|
-
)
|
|
673
|
-
})
|
|
674
|
-
|
|
675
|
-
const debouncedSearch = props.search && typeof props.search === 'function' ? useDebounceFn(props.search, props.debounce) : undefined
|
|
676
|
-
|
|
677
|
-
const options = computedAsync(async () => {
|
|
678
|
-
if (debouncedSearch) {
|
|
679
|
-
return await debouncedSearch(query.value)
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
return props.options || []
|
|
683
|
-
}, [], {
|
|
684
|
-
lazy: props.searchLazy
|
|
685
|
-
})
|
|
686
|
-
|
|
687
|
-
function escapeRegExp(string: string) {
|
|
688
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
const filteredOptions = computed(() => {
|
|
692
|
-
if (!query.value || debouncedSearch) {
|
|
693
|
-
return options.value
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
const escapedQuery = escapeRegExp(query.value)
|
|
697
|
-
|
|
698
|
-
return options.value.filter((option: any) => {
|
|
699
|
-
return (props.searchAttributes?.length ? props.searchAttributes : [props.optionAttribute]).some((searchAttribute: any) => {
|
|
700
|
-
if (['string', 'number'].includes(typeof option)) {
|
|
701
|
-
return String(option).search(new RegExp(escapedQuery, 'i')) !== -1
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
const child = get(option, searchAttribute)
|
|
705
|
-
|
|
706
|
-
return child !== null && child !== undefined && String(child).search(new RegExp(escapedQuery, 'i')) !== -1
|
|
707
|
-
})
|
|
708
|
-
})
|
|
709
|
-
})
|
|
710
|
-
|
|
711
|
-
watch(container, (value) => {
|
|
712
|
-
if (value) {
|
|
713
|
-
emit('open')
|
|
714
|
-
} else {
|
|
715
|
-
emit('close')
|
|
716
|
-
emitFormBlur()
|
|
717
|
-
}
|
|
718
|
-
})
|
|
719
|
-
|
|
720
|
-
function onUpdate(value: any) {
|
|
721
|
-
query.value = ''
|
|
722
|
-
|
|
723
|
-
if (toRaw(props.modelValue) === toRaw(value)) {
|
|
724
|
-
return
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
emit('update:modelValue', value)
|
|
728
|
-
emit('change', value)
|
|
729
|
-
|
|
730
|
-
emitFormChange()
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
function accessor<T extends Record<string, any>>(obj: T, key: string) {
|
|
734
|
-
return get(obj, key)
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
function onQueryChange(event: any) {
|
|
738
|
-
query.value = event.target.value
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
provideUseId(() => useId())
|
|
742
|
-
|
|
743
|
-
return {
|
|
744
|
-
// eslint-disable-next-line vue/no-dupe-keys
|
|
745
|
-
ui,
|
|
746
|
-
// eslint-disable-next-line vue/no-dupe-keys
|
|
747
|
-
uiMenu,
|
|
748
|
-
attrs,
|
|
749
|
-
// eslint-disable-next-line vue/no-dupe-keys
|
|
750
|
-
name,
|
|
751
|
-
inputId,
|
|
752
|
-
// eslint-disable-next-line vue/no-dupe-keys
|
|
753
|
-
popper,
|
|
754
|
-
trigger,
|
|
755
|
-
container,
|
|
756
|
-
label,
|
|
757
|
-
isLeading,
|
|
758
|
-
isTrailing,
|
|
759
|
-
// eslint-disable-next-line vue/no-dupe-keys
|
|
760
|
-
inputClass,
|
|
761
|
-
leadingIconName,
|
|
762
|
-
leadingIconClass,
|
|
763
|
-
leadingWrapperIconClass,
|
|
764
|
-
trailingIconName,
|
|
765
|
-
trailingIconClass,
|
|
766
|
-
trailingWrapperIconClass,
|
|
767
|
-
filteredOptions,
|
|
768
|
-
// eslint-disable-next-line vue/no-dupe-keys
|
|
769
|
-
query,
|
|
770
|
-
accessor,
|
|
771
|
-
onUpdate,
|
|
772
|
-
onQueryChange
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
})
|
|
776
|
-
</script>");
|
|
376
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, (match) => `\\${match}`);
|
|
777
377
|
}
|
|
778
378
|
const filteredOptions = computed(() => {
|
|
779
379
|
if (!query.value || debouncedSearch) {
|
|
@@ -453,486 +453,7 @@ export default defineComponent({
|
|
|
453
453
|
lazy: props.searchableLazy
|
|
454
454
|
});
|
|
455
455
|
function escapeRegExp(string) {
|
|
456
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g,
|
|
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>");
|
|
456
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, (match) => `\\${match}`);
|
|
936
457
|
}
|
|
937
458
|
function accessor(obj, key) {
|
|
938
459
|
return get(obj, key);
|
package/package.json
CHANGED