autocomplete

With groups and labels

With groups and labels

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

src/autocomplete/c-autocomplete-8.vue

<!--
  Кейс проверяет только закрытое состояние (см. docs/PORTING.md §5):
  `open` в оригинале управляемый и по умолчанию `false`, попап не
  монтируется вовсе, пока не раскрыт. Датасет пользователей сокращён
  (2 группы вместо 6) — на закрытое состояние это не влияет, а больший
  список нигде не виден. Сетевые аватары (randomuser.me) заменены
  статичным 1x1 data-URI (PIXEL), сетевые запросы недетерминированы.
-->
<script setup lang="ts">
import { computed, ref } from "vue"
import { Autocomplete, AutocompleteCollection, AutocompleteContent, AutocompleteEmpty, AutocompleteGroup, AutocompleteGroupLabel, AutocompleteInput, AutocompleteItem, AutocompleteList } from "@/components/reui/autocomplete"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"

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

interface User {
  id: string
  name: string
  group: string
  position: string
  avatar: string
}

interface UserGroup {
  group: string
  items: User[]
}

const usersData: User[] = [
  { id: "john-doe", name: "John Doe", group: "Development Team", position: "Senior Frontend Developer", avatar: PIXEL },
  { id: "jane-smith", name: "Jane Smith", group: "Development Team", position: "Full Stack Developer", avatar: PIXEL },
  { id: "alex-martinez", name: "Alex Martinez", group: "Design Team", position: "Lead UX Designer", avatar: PIXEL },
  { id: "emma-davis", name: "Emma Davis", group: "Design Team", position: "UI Designer", avatar: PIXEL },
]

function groupUsers(users: User[]): UserGroup[] {
  const groups: Record<string, User[]> = {}
  users.forEach((item) => {
    ;(groups[item.group] ??= []).push(item)
  })
  const order = ["Development Team", "Design Team"]
  return order.map((group) => ({ group, items: groups[group] ?? [] }))
}

const groupedUsers = groupUsers(usersData)

const value = ref("")
const open = ref(false)

const filteredItems = computed<UserGroup[]>(() => {
  if (!value.value) return groupedUsers
  const q = value.value.toLowerCase()
  return groupedUsers
    .map((group) => ({
      ...group,
      items: group.items.filter(
        (item) =>
          item.name.toLowerCase().includes(q) ||
          item.group.toLowerCase().includes(q) ||
          item.position.toLowerCase().includes(q)
      ),
    }))
    .filter((group) => group.items.length > 0)
})
</script>

<template>
  <div class="w-full max-w-xs">
    <Autocomplete v-model="value" v-model:open="open">
      <AutocompleteInput placeholder="e.g. John, Developer, Marketing" />
      <AutocompleteContent v-if="open" class="pt-0">
        <AutocompleteEmpty v-if="filteredItems.length === 0">No matching users found.</AutocompleteEmpty>
        <AutocompleteList v-else class="not-empty:py-0">
          <AutocompleteGroup v-for="group in filteredItems" :key="group.group" class="py-0">
            <AutocompleteGroupLabel class="bg-background text-muted-foreground sticky top-0 z-10 me-1.5 py-2.5 text-xs font-medium">
              {{ group.group }}
            </AutocompleteGroupLabel>
            <AutocompleteCollection>
              <AutocompleteItem
                v-for="item in group.items"
                :key="item.id"
                :value="item.name"
                class="flex items-center gap-2.5 rounded-lg"
              >
                <Avatar class="size-9">
                  <AvatarImage :src="item.avatar" :alt="item.name" />
                  <AvatarFallback>{{ item.name.split(" ").map((n) => n[0]).join("") }}</AvatarFallback>
                </Avatar>
                <div class="min-w-0 flex-1">
                  <div class="truncate font-medium">{{ item.name }}</div>
                  <div class="text-muted-foreground truncate text-sm">{{ item.position }}</div>
                </div>
              </AutocompleteItem>
            </AutocompleteCollection>
          </AutocompleteGroup>
        </AutocompleteList>
      </AutocompleteContent>
    </Autocomplete>
  </div>
</template>

Установка

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

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

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