Skip to content
Snippets Groups Projects
ComboBoxSimple.vue 2.35 KiB
Newer Older
  • Learn to ignore specific revisions
  • <template>
      <ComboBox
        v-model="modelValue"
        v-bind="props"
        :choices="filteredChoices"
        :close-on-select="props.closeOnSelect ?? !Array.isArray(modelValue)"
        input-container-class="tw-flex tw-flex-wrap tw-p-2 tw-gap-2 tw-items-baseline tw-w-full form-control tw-h-auto tw-min-h-[46px]"
        input-class="tw-border-none tw-px-1 tw-w-[100px] focus:tw-shadow-none focus:tw-outline-none focus:tw-ring-0"
        @search="searchQuery = $event"
      >
        <template #default="{ choice, ...attributes }">
          <slot :choice="choice" v-bind="attributes">
            <li v-bind="attributes">
              {{ choice.name }}
            </li>
          </slot>
        </template>
    
        <template #selected="{ value, deselect, isOpen }">
          <slot name="selected" :value="value" :deselect="deselect" :is-open="isOpen">
            <template v-if="Array.isArray(value)">
              <Tag
                v-for="(item, index) in value"
                :key="index"
                :label="item.name"
                removable
                @remove="deselect(item)"
              />
            </template>
            <template v-else>
              <p v-if="value">{{ value.name }}</p>
            </template>
          </slot>
        </template>
      </ComboBox>
    </template>
    
    <script setup lang="ts" generic="T extends { id: ID, name: string }">
    import { ID } from '@/api'
    import Tag from './generic/Tag.vue'
    import ComboBox from './ComboBox.vue'
    import type { ComboBoxProps } from '@/components/ComboBox.vue'
    import { computed, ref } from 'vue'
    import { matchesSearch } from '@/util'
    
    defineOptions({
      compatConfig: { MODE: 3 },
    })
    defineSlots<{
      default(
        props: Record<string, unknown> & {
          id: string
          choice: T
          index: number
          activeIndex: number
        },
      ): unknown
      selected(props: {
        value: null | T | T[]
        deselect: (choice: null | T) => void
        isOpen: boolean
      }): unknown
    }>()
    
    const modelValue = defineModel<null | T | T[]>({ required: true })
    const props = defineProps<ComboBoxProps<T>>()
    
    const searchQuery = ref('')
    const selectedIds = computed(() => {
      if (Array.isArray(modelValue.value)) {
        return modelValue.value.map((item) => item.id)
      } else if (modelValue.value !== null) {
        return [modelValue.value.id]
      } else {
        return []
      }
    })
    
    const filteredChoices = computed(() =>
      props.choices.filter(
        ({ id, name }) => !selectedIds.value.includes(id) && matchesSearch(name, searchQuery.value),
      ),
    )
    </script>