search dialog: quitar lupa, cambiar texto a Buscar, rounded-xl y w-40 en trigger

This commit is contained in:
Ricardo Gonzalez
2026-05-23 16:43:14 -05:00
parent 42d22c9111
commit 002d8f06d3
+217 -1
View File
@@ -1,11 +1,79 @@
<script setup lang="ts">
import { ref, watch, computed } from "vue"
import { useI18n } from "vue-i18n"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
import { Separator } from "@/components/ui/separator"
import { SidebarTrigger } from "@/components/ui/sidebar"
import { Input } from "@/components/ui/input"
import { isDark, toggleTheme } from "@/composables/useTheme"
import { useSearch, typeColors } from "@/composables/useSearch"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
const { t } = useI18n()
const { t, locale } = useI18n()
const searchOpen = ref(false)
const searchQuery = ref("")
const { results, loading, search, clearResults } = useSearch()
watch(searchQuery, (query) => {
search(query)
})
const languages = [
{ code: "en", label: "English" },
{ code: "es", label: "Español" },
]
function setLanguage(code: string) {
locale.value = code
localStorage.setItem("alpha-language", code)
}
function setTheme(theme: "light" | "dark" | "system") {
if (theme === "system") {
document.documentElement.removeAttribute("class")
} else if (theme === "dark") {
document.documentElement.classList.add("dark")
} else {
document.documentElement.classList.remove("dark")
}
}
function handleSearchOpenChange(open: boolean) {
searchOpen.value = open
if (!open) {
searchQuery.value = ""
clearResults()
}
}
function selectResult(result: { type: string; id: number }) {
console.log("Selected:", result.type, result.id)
handleSearchOpenChange(false)
}
const groupedResults = computed(() => {
const groups: Record<string, typeof results.value> = {}
results.value.forEach((result) => {
const label = typeColors[result.type].label
if (!groups[label]) {
groups[label] = []
}
groups[label].push(result)
})
return groups
})
</script>
<template>
@@ -20,6 +88,58 @@ const { t } = useI18n()
{{ t('siteHeader.title') }}
</h1>
<div class="ml-auto flex items-center gap-2">
<Dialog v-model:open="searchOpen">
<DialogTrigger as-child>
<Button variant="outline" class="rounded-xl sm:pr-12 w-full justify-start text-muted-foreground w-40">
{{ t('siteHeader.search') }}
</Button>
</DialogTrigger>
<DialogContent class="sm:max-w-[525px] max-h-[80vh] overflow-hidden flex flex-col">
<DialogHeader>
<DialogTitle>{{ t('siteHeader.search') }}</DialogTitle>
</DialogHeader>
<div class="py-4">
<Input
v-model="searchQuery"
type="search"
:placeholder="t('siteHeader.search')"
class="w-full"
autofocus
/>
</div>
<div v-if="loading" class="py-8 text-center text-muted-foreground text-sm">
{{ t('siteHeader.searching') || 'Searching...' }}
</div>
<div v-else-if="results.length === 0 && searchQuery.trim()" class="py-8 text-center text-muted-foreground text-sm">
{{ t('siteHeader.noResults') || 'No results found' }}
</div>
<div v-else-if="results.length > 0" class="overflow-y-auto max-h-[50vh] pb-4">
<div v-for="(group, type) in groupedResults" :key="type" class="mb-4">
<h3 class="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-2 px-1">
{{ type }}
</h3>
<div class="space-y-1">
<button
v-for="result in group"
:key="`${result.type}-${result.id}`"
@click="selectResult(result)"
class="w-full flex items-center gap-3 px-3 py-2 rounded-lg text-left hover:bg-accent transition-colors"
>
<Badge :class="`${typeColors[result.type].bg} ${typeColors[result.type].text}`" variant="secondary">
{{ typeColors[result.type].label }}
</Badge>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium truncate">{{ result.title }}</p>
<p v-if="result.description" class="text-xs text-muted-foreground truncate">
{{ result.description }}
</p>
</div>
</button>
</div>
</div>
</div>
</DialogContent>
</Dialog>
<Button variant="ghost" size="icon" class="size-8" @click="toggleTheme()">
<svg v-if="isDark" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-[18px]">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
@@ -35,6 +155,102 @@ const { t } = useI18n()
</svg>
<span class="sr-only">Toggle theme</span>
</Button>
<DropdownMenu>
<DropdownMenuTrigger as-child>
<Button variant="ghost" size="icon" class="size-8">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-[18px]">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" />
<circle cx="12" cy="12" r="3" />
</svg>
<span class="sr-only">{{ t('settings.title') }}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>{{ t('settings.title') }}</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<circle cx="12" cy="12" r="10" />
<path d="M12 2c2.667 4.333 4 7.5 4 10c0 2.5-1.333 5.667-4 10" />
<path d="M2 12h20" />
<path d="M12 2c-2.667 4.333-4 7.5-4 10c0 2.5 1.333 5.667 4 10" />
</svg>
{{ t('settings.language') }}
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem
v-for="lang in languages"
:key="lang.code"
@click="setLanguage(lang.code)"
>
{{ lang.label }}
<svg v-if="locale === lang.code" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 ml-auto">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M5 12l5 5l10 -10" />
</svg>
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>
<DropdownMenuSeparator />
<DropdownMenuSub>
<DropdownMenuSubTrigger>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<circle cx="12" cy="12" r="4" />
<path d="M12 2v2" />
<path d="M12 20v2" />
<path d="M4.93 4.93l1.41 1.41" />
<path d="M17.66 17.66l1.41 1.41" />
<path d="M2 12h2" />
<path d="M20 12h2" />
<path d="M6.34 17.66l-1.41 1.41" />
<path d="M19.07 4.93l-1.41 1.41" />
</svg>
{{ t('settings.theme') }}
</DropdownMenuSubTrigger>
<DropdownMenuSubContent>
<DropdownMenuItem @click="setTheme('light')">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
{{ t('settings.light') }}
<svg v-if="!isDark" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 ml-auto">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M5 12l5 5l10 -10" />
</svg>
</DropdownMenuItem>
<DropdownMenuItem @click="setTheme('dark')">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" />
<path d="M12 3l0 18" />
<path d="M12 9l4.65 -4.65" />
<path d="M12 14.3l7.37 -7.37" />
<path d="M12 19.6l8.85 -8.85" />
</svg>
{{ t('settings.dark') }}
<svg v-if="isDark" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4 ml-auto">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M5 12l5 5l10 -10" />
</svg>
</DropdownMenuItem>
<DropdownMenuItem @click="setTheme('system')">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<rect width="20" height="14" x="2" y="3" rx="2" />
<path d="M8 21h8" />
<path d="M12 17v4" />
</svg>
{{ t('settings.system') }}
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuSub>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
</header>