DefaultMultiSelect.vue 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. <template>
  2. <div class="column" :class="attrs.class" :style="attrs.style">
  3. <div v-if="label || $slots.label" class="q-pl-xs">
  4. <slot name="label">
  5. <span>{{ label }}</span>
  6. </slot>
  7. <span v-if="required" class="text-negative q-ml-xs">*</span>
  8. </div>
  9. <div class="col">
  10. <q-select
  11. ref="selectRef"
  12. v-model="model"
  13. v-bind="selectAttrs"
  14. behavior="menu"
  15. hide-bottom-space
  16. hide-dropdown-icon
  17. multiple
  18. use-chips
  19. :bg-color="bgColor"
  20. :class="inputClass"
  21. :error="!!error"
  22. :error-message="errorMessage"
  23. :popup-content-class="popupContentClass"
  24. :rules="rules"
  25. @update:model-value="error = null"
  26. >
  27. <template #append>
  28. <q-icon name="mdi-chevron-down" class="text-text" />
  29. </template>
  30. <template #selected-item="scope">
  31. <q-chip
  32. class="q-ma-xs"
  33. dense
  34. removable
  35. @remove="scope.removeAtIndex(scope.index)"
  36. >
  37. {{ scope.opt?.label ?? scope.opt }}
  38. </q-chip>
  39. </template>
  40. <template v-for="(_, slotName) in $slots" #[slotName]="scope">
  41. <slot :name="slotName" v-bind="scope" />
  42. </template>
  43. </q-select>
  44. </div>
  45. </div>
  46. </template>
  47. <script setup>
  48. import { computed, onBeforeMount, ref, useAttrs } from "vue";
  49. defineOptions({
  50. inheritAttrs: false,
  51. });
  52. const props = defineProps({
  53. label: { type: String, default: "" },
  54. rules: { type: Array, default: () => [] },
  55. inputClass: { type: String, default: null },
  56. popupContentClass: { type: String, default: null },
  57. bgColor: { type: String, default: "surface" },
  58. });
  59. const { label, inputClass, popupContentClass, bgColor, rules } = props;
  60. const attrs = useAttrs();
  61. const model = defineModel({
  62. type: Array, default: () => [],
  63. });
  64. const error = defineModel("error", {
  65. type: [String, Object, Array, Boolean, null],
  66. });
  67. const selectRef = ref(null);
  68. const required = ref(false);
  69. const errorMessage = computed(() => {
  70. if (error.value == null) return void 0;
  71. if (typeof error.value === "boolean") return void 0;
  72. return String(error.value);
  73. });
  74. const selectAttrs = computed(() => {
  75. // eslint-disable-next-line no-unused-vars
  76. const { class: _, style: __, ...rest } = attrs;
  77. return rest;
  78. });
  79. onBeforeMount(() => {
  80. rules.forEach((r) => {
  81. if (r?.$id === "required") {
  82. required.value = true;
  83. }
  84. });
  85. });
  86. defineExpose({
  87. focus: () => {
  88. if (selectRef.value && typeof selectRef.value.focus === "function") {
  89. selectRef.value.focus();
  90. }
  91. },
  92. });
  93. </script>