@nuxt/ui 2.18.7 → 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.
Files changed (94) hide show
  1. package/dist/module.d.mts +1 -1
  2. package/dist/module.d.ts +1 -1
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +3 -3
  5. package/dist/runtime/components/data/Table.vue +82 -27
  6. package/dist/runtime/components/data/Table.vue.d.ts +45 -37
  7. package/dist/runtime/components/elements/AvatarGroup.js +2 -2
  8. package/dist/runtime/components/elements/Carousel.vue +1 -1
  9. package/dist/runtime/components/elements/Dropdown.vue +6 -2
  10. package/dist/runtime/components/elements/MeterGroup.d.ts +1 -1
  11. package/dist/runtime/components/elements/Progress.vue +1 -1
  12. package/dist/runtime/components/forms/Checkbox.vue.d.ts +1 -1
  13. package/dist/runtime/components/forms/Form.vue +104 -43
  14. package/dist/runtime/components/forms/Form.vue.d.ts +5 -3
  15. package/dist/runtime/components/forms/Input.vue +5 -2
  16. package/dist/runtime/components/forms/Input.vue.d.ts +3 -0
  17. package/dist/runtime/components/forms/InputMenu.vue +419 -8
  18. package/dist/runtime/components/forms/InputMenu.vue.d.ts +19 -0
  19. package/dist/runtime/components/forms/Radio.vue.d.ts +1 -1
  20. package/dist/runtime/components/forms/RadioGroup.vue +3 -4
  21. package/dist/runtime/components/forms/RadioGroup.vue.d.ts +1 -1
  22. package/dist/runtime/components/forms/SelectMenu.vue +501 -11
  23. package/dist/runtime/components/forms/SelectMenu.vue.d.ts +30 -2
  24. package/dist/runtime/components/forms/Textarea.vue +7 -4
  25. package/dist/runtime/components/forms/Textarea.vue.d.ts +3 -0
  26. package/dist/runtime/components/layout/Divider.vue +1 -1
  27. package/dist/runtime/components/layout/Divider.vue.d.ts +5 -2
  28. package/dist/runtime/components/navigation/CommandPalette.vue +1 -1
  29. package/dist/runtime/components/navigation/CommandPalette.vue.d.ts +1 -1
  30. package/dist/runtime/components/navigation/CommandPaletteGroup.vue.d.ts +1 -1
  31. package/dist/runtime/components/navigation/Pagination.vue +4 -4
  32. package/dist/runtime/components/navigation/Pagination.vue.d.ts +1 -1
  33. package/dist/runtime/components/navigation/Tabs.vue +1 -1
  34. package/dist/runtime/components/navigation/VerticalNavigation.vue.d.ts +6 -2
  35. package/dist/runtime/components/overlays/Modal.vue +1 -1
  36. package/dist/runtime/components/overlays/Notifications.vue +1 -1
  37. package/dist/runtime/components/overlays/Popover.vue +6 -2
  38. package/dist/runtime/composables/defineShortcuts.js +1 -1
  39. package/dist/runtime/composables/useButtonGroup.d.ts +1 -1
  40. package/dist/runtime/composables/useUI.js +1 -1
  41. package/dist/runtime/plugins/modals.js +1 -1
  42. package/dist/runtime/plugins/slideovers.js +1 -1
  43. package/dist/runtime/types/accordion.d.ts +1 -1
  44. package/dist/runtime/types/alert.d.ts +4 -4
  45. package/dist/runtime/types/avatar.d.ts +3 -3
  46. package/dist/runtime/types/badge.d.ts +3 -3
  47. package/dist/runtime/types/button.d.ts +3 -5
  48. package/dist/runtime/types/chip.d.ts +2 -2
  49. package/dist/runtime/types/command-palette.d.ts +2 -2
  50. package/dist/runtime/types/component.d.ts +3 -3
  51. package/dist/runtime/types/divider.d.ts +3 -3
  52. package/dist/runtime/types/dropdown.d.ts +1 -1
  53. package/dist/runtime/types/form-group.d.ts +2 -2
  54. package/dist/runtime/types/form.d.ts +7 -2
  55. package/dist/runtime/types/index.d.ts +1 -0
  56. package/dist/runtime/types/index.js +1 -0
  57. package/dist/runtime/types/input.d.ts +3 -3
  58. package/dist/runtime/types/kbd.d.ts +2 -2
  59. package/dist/runtime/types/meter.d.ts +2 -2
  60. package/dist/runtime/types/modal.d.ts +2 -2
  61. package/dist/runtime/types/notification.d.ts +1 -1
  62. package/dist/runtime/types/progress.d.ts +2 -2
  63. package/dist/runtime/types/range.d.ts +3 -3
  64. package/dist/runtime/types/select.d.ts +3 -3
  65. package/dist/runtime/types/table.d.ts +18 -0
  66. package/dist/runtime/types/textarea.d.ts +3 -3
  67. package/dist/runtime/types/toggle.d.ts +3 -3
  68. package/dist/runtime/types/utils.d.ts +2 -2
  69. package/dist/runtime/ui.config/data/table.d.ts +1 -0
  70. package/dist/runtime/ui.config/data/table.js +1 -0
  71. package/dist/runtime/ui.config/elements/avatar.js +15 -15
  72. package/dist/runtime/ui.config/elements/button.js +25 -25
  73. package/dist/runtime/ui.config/elements/buttonGroup.js +1 -1
  74. package/dist/runtime/ui.config/elements/chip.js +5 -5
  75. package/dist/runtime/ui.config/elements/meter.js +20 -20
  76. package/dist/runtime/ui.config/elements/meterGroup.js +1 -1
  77. package/dist/runtime/ui.config/elements/progress.js +18 -18
  78. package/dist/runtime/ui.config/forms/formGroup.js +5 -6
  79. package/dist/runtime/ui.config/forms/input.js +40 -40
  80. package/dist/runtime/ui.config/forms/inputMenu.d.ts +6 -0
  81. package/dist/runtime/ui.config/forms/inputMenu.js +7 -1
  82. package/dist/runtime/ui.config/forms/range.js +20 -20
  83. package/dist/runtime/ui.config/forms/selectMenu.d.ts +9 -0
  84. package/dist/runtime/ui.config/forms/selectMenu.js +10 -1
  85. package/dist/runtime/ui.config/forms/toggle.js +20 -20
  86. package/dist/runtime/ui.config/layout/divider.d.ts +1 -0
  87. package/dist/runtime/ui.config/layout/divider.js +12 -11
  88. package/dist/runtime/ui.config/navigation/horizontalNavigation.js +1 -1
  89. package/dist/runtime/ui.config/navigation/verticalNavigation.js +1 -1
  90. package/dist/runtime/ui.config/popper.js +0 -1
  91. package/dist/runtime/utils/colors.js +7 -7
  92. package/dist/runtime/utils/index.js +4 -4
  93. package/dist/runtime/utils/lodash.js +1 -1
  94. package/package.json +21 -19
@@ -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[optionAttribute] }}</span>
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[optionAttribute] }}"</span>
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
- No results for "{{ query }}".
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
- No options.
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: "Search..."
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?.[props.optionAttribute] ?? null;
377
+ return accessor(selected.value, props.optionAttribute) ?? null;
378
378
  } else {
379
- return ["string", "number"].includes(typeof props.modelValue) ? props.modelValue : props.modelValue[props.optionAttribute];
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(query.value, "i")) !== -1;
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(query.value, "i")) !== -1;
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[props.optionAttribute] === query.value);
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;
@@ -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>;
@@ -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;
@@ -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;
@@ -130,7 +130,7 @@ export default defineComponent({
130
130
  setup(props, { emit }) {
131
131
  const { ui, attrs } = useUI("textarea", toRef(props, "ui"), config, toRef(props, "class"));
132
132
  const { emitFormBlur, emitFormInput, inputId, color, size, name } = useFormGroup(props, config);
133
- const modelModifiers = ref(defu({}, props.modelModifiers, { trim: false, lazy: false, number: false }));
133
+ const modelModifiers = ref(defu({}, props.modelModifiers, { trim: false, lazy: false, number: false, nullify: false }));
134
134
  const textarea2 = ref(null);
135
135
  const autoFocus = () => {
136
136
  if (props.autofocus) {
@@ -146,10 +146,10 @@ export default defineComponent({
146
146
  const overflow = textarea2.value.style.overflow;
147
147
  textarea2.value.style.overflow = "hidden";
148
148
  const styles = window.getComputedStyle(textarea2.value);
149
- const paddingTop = parseInt(styles.paddingTop);
150
- const paddingBottom = parseInt(styles.paddingBottom);
149
+ const paddingTop = Number.parseInt(styles.paddingTop);
150
+ const paddingBottom = Number.parseInt(styles.paddingBottom);
151
151
  const padding = paddingTop + paddingBottom;
152
- const lineHeight = parseInt(styles.lineHeight);
152
+ const lineHeight = Number.parseInt(styles.lineHeight);
153
153
  const { scrollHeight } = textarea2.value;
154
154
  const newRows = (scrollHeight - padding) / lineHeight;
155
155
  if (newRows > props.rows) {
@@ -165,6 +165,9 @@ export default defineComponent({
165
165
  if (modelModifiers.value.number) {
166
166
  value = looseToNumber(value);
167
167
  }
168
+ if (modelModifiers.value.nullify) {
169
+ value ||= null;
170
+ }
168
171
  emit("update:modelValue", value);
169
172
  emitFormInput();
170
173
  };