Updated search bar
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { Settings } from "lucide-react"
|
import { Settings } from "lucide-react"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { SearchBar } from "./search-bar"
|
import { TopSearchBar } from "./search-bar"
|
||||||
import { ThemeSwitcher } from "./theme-switcher"
|
import { ThemeSwitcher } from "./theme-switcher"
|
||||||
|
|
||||||
export default function Header({ searchBar = false }: { searchBar?: boolean }) {
|
export default function Header({ searchBar = false }: { searchBar?: boolean }) {
|
||||||
@@ -10,7 +10,7 @@ export default function Header({ searchBar = false }: { searchBar?: boolean }) {
|
|||||||
<Link href="/">
|
<Link href="/">
|
||||||
<h1 className="text-lg font-semibold">HypStats</h1>
|
<h1 className="text-lg font-semibold">HypStats</h1>
|
||||||
</Link>
|
</Link>
|
||||||
{searchBar && <SearchBar navbar />}
|
{searchBar && <TopSearchBar />}
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<ThemeSwitcher />
|
<ThemeSwitcher />
|
||||||
<Link href="/settings">
|
<Link href="/settings">
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import { cn } from "@/lib/utils"
|
import { capitalizeFirstLetter, cn } from "@/lib/utils"
|
||||||
import { Search } from "lucide-react"
|
import { Search } from "lucide-react"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
import { useEffect, useRef, useState } from "react"
|
import { useEffect, useRef, useState } from "react"
|
||||||
import { Button } from "./ui/button"
|
import { Button } from "./ui/button"
|
||||||
|
|
||||||
export function SearchBar({ navbar }: { navbar?: boolean }) {
|
export function TopSearchBar() {
|
||||||
const [input, setInput] = useState("")
|
const [input, setInput] = useState("")
|
||||||
const ref = useRef<HTMLInputElement>(null)
|
const ref = useRef<HTMLInputElement>(null)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -31,23 +31,19 @@ export function SearchBar({ navbar }: { navbar?: boolean }) {
|
|||||||
|
|
||||||
async function handleSearch(e: React.FormEvent) {
|
async function handleSearch(e: React.FormEvent) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
if (input.trim()) {
|
if (input.trim()) {
|
||||||
router.push(`/player/${encodeURIComponent(input.trim())}`)
|
router.push(`/player/${encodeURIComponent(input.trim())}`)
|
||||||
}
|
}
|
||||||
if (navbar) {
|
|
||||||
setInput("")
|
setInput("")
|
||||||
ref.current?.blur()
|
ref.current?.blur()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("w-full max-w-4xl px-4", !navbar && "mt-8")}>
|
<div className={cn("w-full max-w-4xl px-4")}>
|
||||||
<form onSubmit={handleSearch}>
|
<form onSubmit={handleSearch}>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={!navbar ? "Search for a player..." : ""}
|
|
||||||
className="px-5"
|
className="px-5"
|
||||||
ref={ref}
|
ref={ref}
|
||||||
value={input}
|
value={input}
|
||||||
@@ -66,3 +62,102 @@ export function SearchBar({ navbar }: { navbar?: boolean }) {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pages = ["player", "guild"] as const
|
||||||
|
|
||||||
|
const guildTypes = ["player", "name", "id"] as const
|
||||||
|
|
||||||
|
export function SearchBar() {
|
||||||
|
const [input, setInput] = useState("")
|
||||||
|
const [selected, setSelected] = useState<typeof pages[number]>("player")
|
||||||
|
const [guildType, setGuildType] = useState<typeof guildTypes[number]>("player")
|
||||||
|
const ref = useRef<HTMLInputElement>(null)
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const controller = new AbortController()
|
||||||
|
|
||||||
|
window.addEventListener("keydown", (ev) => {
|
||||||
|
if (!ref.current) return
|
||||||
|
|
||||||
|
if (ev.ctrlKey && ev.key === "k") {
|
||||||
|
ev.preventDefault()
|
||||||
|
ref.current.focus()
|
||||||
|
}
|
||||||
|
}, { signal: controller.signal })
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
controller.abort()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function handleSearch(e: React.FormEvent) {
|
||||||
|
e.preventDefault()
|
||||||
|
if (!input.trim()) return
|
||||||
|
|
||||||
|
switch (selected) {
|
||||||
|
case "player":
|
||||||
|
router.push(`/player/${encodeURIComponent(input.trim())}`)
|
||||||
|
break
|
||||||
|
case "guild":
|
||||||
|
router.push(`/guild/${encodeURIComponent(input.trim())}?type=${guildType}`)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
router.push(`/player/${encodeURIComponent(input.trim())}`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="px-4 mt-8 w-full max-w-4xl">
|
||||||
|
<form onSubmit={handleSearch}>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder={`Search for a ${selected}${selected === "player" ? "..." : " using " + guildType + "..."} `}
|
||||||
|
className="px-5"
|
||||||
|
ref={ref}
|
||||||
|
value={input}
|
||||||
|
onChange={(e) => setInput(e.target.value)}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
handleSearch(e)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button type="submit">
|
||||||
|
<Search />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-4 items-center mt-5">
|
||||||
|
{pages.map(p => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
key={p}
|
||||||
|
variant={selected === p ? "default" : "outline"}
|
||||||
|
onClick={() => setSelected(p)}
|
||||||
|
>
|
||||||
|
{capitalizeFirstLetter(p)}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
{selected === "guild" && (
|
||||||
|
<div className="flex gap-4 items-center mt-5">
|
||||||
|
{guildTypes.map(gt => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
key={gt}
|
||||||
|
variant={guildType === gt ? "default" : "outline"}
|
||||||
|
onClick={() => setGuildType(gt)}
|
||||||
|
>
|
||||||
|
{capitalizeFirstLetter(gt)}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user