combobox

A multi-select combobox with user tags

A multi-select combobox with user tags

Загрузка превью…

src/combobox/c-combobox-19.vue

<!--
  Unsplash-URL заменены локальным 1x1 data-URI (см. `c-combobox-10.vue`).
  `by` сравнивает объекты по `id` — аналог `isItemEqualToValue` оригинала.
-->
<script setup lang="ts">
import { ref } from "vue"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxContent, ComboboxEmpty, ComboboxItem, ComboboxList, ComboboxValue, useComboboxAnchor } from "@/components/ui/combobox"
import { Field } from "@/components/ui/field"
import { Item, ItemContent, ItemDescription, ItemTitle } from "@/components/ui/item"

const PIXEL =
  "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAUwAOw=="

const users = [
  { id: "1", name: "Alex Johnson", email: "alex@example.com", initials: "AJ" },
  { id: "2", name: "Sarah Chen", email: "sarah@example.com", initials: "SC" },
  { id: "3", name: "Michael Rodriguez", email: "michael@example.com", initials: "MR" },
  { id: "4", name: "Emma Wilson", email: "emma@example.com", initials: "EW" },
  { id: "5", name: "David Kim", email: "david@example.com", initials: "DK" },
  { id: "6", name: "Aron Thompson", email: "lisa@example.com", initials: "LT" },
  { id: "7", name: "James Brown", email: "james@example.com", initials: "JB" },
  { id: "8", name: "Maria Garcia", email: "maria@example.com", initials: "MG" },
  { id: "9", name: "Nick Johnson", email: "nick@example.com", initials: "NJ" },
  { id: "10", name: "Liam Thompson", email: "liam@example.com", initials: "LT" },
]

const members = users.map((user, index) => ({
  ...user,
  avatar: PIXEL,
  position: ["Software Engineer", "Product Manager", "UX Designer", "Technical Lead", "CTO"][
    index % 5
  ],
}))

type Member = (typeof members)[number]

const anchor = useComboboxAnchor()
const selected = ref<Member[]>([members[0]!, members[1]!])

function handleRemove(id: string) {
  selected.value = selected.value.filter((m) => m.id !== id)
}
</script>

<template>
  <Field class="max-w-xs">
    <Combobox
      multiple
      :by="(a, b) => (a as Member).id === (b as Member).id"
      :model-value="selected"
      @update:model-value="(v) => (selected = v as unknown as Member[])"
    >
      <ComboboxChips class="has-data-[slot=combobox-chip]:pl-1">
        <ComboboxValue v-slot="{ value }">
          <ComboboxChip
            v-for="member in (value as Member[]) ?? []"
            :key="member.id"
            :show-remove="true"
            class="rounded-full gap-1.5"
            @remove="handleRemove(member.id)"
          >
            <Avatar class="size-4">
              <AvatarImage :src="member.avatar" :alt="member.name" />
              <AvatarFallback class="text-[8px]">{{ member.initials }}</AvatarFallback>
            </Avatar>
            {{ member.name }}
          </ComboboxChip>
          <ComboboxChipsInput placeholder="Add members..." />
        </ComboboxValue>
      </ComboboxChips>
      <ComboboxContent :anchor="anchor" class="max-w-(--anchor-width) min-w-(--anchor-width)">
        <ComboboxEmpty>No members found.</ComboboxEmpty>
        <ComboboxList>
          <ComboboxItem v-for="member in members" :key="member.id" :value="member">
            <Item size="xs" class="p-0">
              <Avatar class="size-6">
                <AvatarImage :src="member.avatar" :alt="member.name" />
                <AvatarFallback>{{ member.initials }}</AvatarFallback>
              </Avatar>
              <ItemContent>
                <ItemTitle class="whitespace-nowrap">{{ member.name }}</ItemTitle>
                <ItemDescription>{{ member.position }}</ItemDescription>
              </ItemContent>
            </Item>
          </ComboboxItem>
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  </Field>
</template>

Установка

npx shadcn-vue@latest add https://revueui.rootapi.dev/r/c-combobox-19.json

Зависимости реестра

Источник: порт из ReUI (Keenthemes, MIT)