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)