feat(ui): add searchable selects to compare and transfer dialogs

This commit is contained in:
大峰 authored and GitHub committed 2026-06-30 22:53:27 +08:00
1 parent 2e8d399dab
commit f1f99f6f10
11 files changed
+361 -209

No files matched your search

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, nextTick, ref, watch } from "vue";
import type { HTMLAttributes } from "vue";
import { Check, ChevronDown, Search } from "@lucide/vue";
import { Check, ChevronDown, Search, X } from "@lucide/vue";
import { Button } from "@/components/ui/button";
import type { ButtonVariants } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -16,8 +16,9 @@ const props = withDefaults(
placeholder: string;
searchPlaceholder: string;
emptyText: string;
loadingText: string;
loadingText?: string;
loading?: boolean;
disabled?: boolean;
allowCustom?: boolean;
triggerVariant?: ButtonVariants["variant"];
triggerClass?: HTMLAttributes["class"];
@@ -25,10 +26,14 @@ const props = withDefaults(
itemClass?: HTMLAttributes["class"];
displayName?: (option: string) => string;
normalizeCustom?: (value: string) => string;
clearable?: boolean;
}>(),
{
loading: false,
disabled: false,
allowCustom: false,
clearable: false,
loadingText: "Loading...",
triggerVariant: "ghost",
displayName: (option: string) => option,
normalizeCustom: (value: string) => value,
@@ -149,11 +154,12 @@ function handleKeydown(event: KeyboardEvent) {
<template>
<Popover v-model:open="open">
<PopoverTrigger as-child>
<Button type="button" :variant="triggerVariant" :title="selectedLabel" :class="cn('h-6 w-auto max-w-56 min-w-0 justify-between gap-1 border-0 bg-transparent px-1 text-xs font-normal shadow-none hover:bg-muted/50 focus-visible:ring-0', triggerClass)">
<Button type="button" :variant="triggerVariant" :disabled="disabled" :title="selectedLabel" :class="cn('h-6 w-auto max-w-56 min-w-0 justify-between gap-1 border-0 bg-transparent px-1 text-xs font-normal shadow-none hover:bg-muted/50 focus-visible:ring-0', triggerClass)">
<slot name="trigger-label" :value="modelValue" :label="selectedLabel" :loading="loading">
<span class="truncate">{{ loading ? loadingText : selectedLabel }}</span>
</slot>
<ChevronDown class="h-3 w-3 shrink-0 opacity-60" />
<X v-if="clearable && !disabled && modelValue" class="h-3 w-3 shrink-0 opacity-60 hover:opacity-100" @pointerdown.stop.prevent="emit('update:modelValue', '')" />
<ChevronDown v-else class="h-3 w-3 shrink-0 opacity-60" />
</Button>
</PopoverTrigger>
<PopoverContent align="end" :class="cn('w-52 gap-1 p-1.5', contentClass)">
@@ -182,8 +188,8 @@ function handleKeydown(event: KeyboardEvent) {
@click="selectOption(option)"
>
<Check :class="cn('h-3.5 w-3.5 shrink-0', option === modelValue ? 'opacity-100' : 'opacity-0')" />
<slot name="option-label" :option="option" :label="displayName(option)">
<span class="truncate">{{ displayName(option) }}</span>
<slot name="option-label" :option="option" :label="displayName?.(option)">
<span class="truncate">{{ displayName?.(option) }}</span>
</slot>
</button>
<button