search dialog: quitar lupa, cambiar texto a Buscar, rounded-xl y w-40 en trigger
This commit is contained in:
@@ -1,11 +1,79 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref, watch, computed } from "vue"
|
||||||
import { useI18n } from "vue-i18n"
|
import { useI18n } from "vue-i18n"
|
||||||
import { Button } from "@/components/ui/button"
|
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 { Separator } from "@/components/ui/separator"
|
||||||
import { SidebarTrigger } from "@/components/ui/sidebar"
|
import { SidebarTrigger } from "@/components/ui/sidebar"
|
||||||
|
import { Input } from "@/components/ui/input"
|
||||||
import { isDark, toggleTheme } from "@/composables/useTheme"
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -20,6 +88,58 @@ const { t } = useI18n()
|
|||||||
{{ t('siteHeader.title') }}
|
{{ t('siteHeader.title') }}
|
||||||
</h1>
|
</h1>
|
||||||
<div class="ml-auto flex items-center gap-2">
|
<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()">
|
<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]">
|
<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" />
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
@@ -35,6 +155,102 @@ const { t } = useI18n()
|
|||||||
</svg>
|
</svg>
|
||||||
<span class="sr-only">Toggle theme</span>
|
<span class="sr-only">Toggle theme</span>
|
||||||
</Button>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
Reference in New Issue
Block a user