m/cup
1
0
mirror of https://github.com/sergi0g/cup.git synced 2025-11-08 05:03:49 -05:00

Added search

This commit is contained in:
Sergio
2024-09-09 11:18:28 +03:00
parent 572ca8858a
commit 2c120ffaff
5 changed files with 64 additions and 3 deletions

View File

@@ -1,7 +1,13 @@
#!/bin/sh
# Exit on error
set -e
# This is kind of like a shim that makes sure the frontend is rebuilt when running a build. For example you can run `./build.sh cargo build --release`
# Remove old files
rm -rf src/static
# Frontend
cd web/

Binary file not shown.

View File

@@ -7,9 +7,11 @@ import Loading from "./components/Loading";
import { Data } from "./types";
import { theme } from "./theme";
import RefreshButton from "./components/RefreshButton";
import Search from "./components/Search";
function App() {
const [data, setData] = useState<Data | null>(null);
const [searchQuery, setSearchQuery] = useState("");
if (!data) return <Loading onLoad={setData} />;
return (
<div
@@ -41,10 +43,11 @@ function App() {
<LastChecked datetime={data.last_updated} />
<RefreshButton />
</div>
<Search onChange={setSearchQuery}/>
<ul
className={`*:py-4 *:px-6 *:flex *:items-center *:gap-3 dark:divide-${theme}-800 divide-y dark:text-white`}
>
{Object.entries(data.images).map(([name, status]) => (
{Object.entries(data.images).filter(([name]) => name.includes(searchQuery)).map(([name, status]) => (
<Image name={name} status={status} key={name} />
))}
</ul>

View File

@@ -0,0 +1,51 @@
import { ChangeEvent, useState } from "react";
import { theme } from "../theme";
import { IconSearch, IconX } from "@tabler/icons-react";
export default function Search({
onChange,
}: {
onChange: (value: string) => void;
}) {
const [searchQuery, setSearchQuery] = useState("");
const [showClear, setShowClear] = useState(false);
const handleChange = (event: ChangeEvent) => {
const value = (event.target as HTMLInputElement).value;
setSearchQuery(value);
onChange(value);
if (value !== "") {
setShowClear(true);
} else setShowClear(false);
};
const handleClear = () => {
setShowClear(false);
setSearchQuery("");
onChange("");
};
return (
<div className={`w-full px-6 text-${theme}-500`}>
<div
className={`flex items-center w-full rounded-md border border-${theme}-200 dark:border-${theme}-700 px-2 gap-1 bg-${theme}-800 flex-nowrap peer`}
>
<IconSearch className="size-5" />
<div className="w-full">
<input
className={`w-full h-10 text-sm text-${theme}-400 focus:outline-none peer bg-transparent placeholder:text-${theme}-500`}
placeholder="Search"
onChange={handleChange}
value={searchQuery}
></input>
</div>
{showClear && (
<button onClick={handleClear} className={`hover:text-${theme}-400`}>
<IconX className="size-5" />
</button>
)}
</div>
<div
className="relative -translate-y-[8px] h-[8px] border-b-blue-600 border-b-2 w-0 peer-has-[:focus]:w-full transition-all duration-200 rounded-md left-1/2 -translate-x-1/2"
style={{ clipPath: "inset(calc(100% - 2px) 0 0 0)" }}
></div>
</div>
);
}

View File

@@ -24,10 +24,11 @@ export default {
},
{
pattern: /text-(gray|neutral)-400/,
variants: ["hover"]
},
{
pattern: /text-(gray|neutral)-500/,
variants: ["dark"],
variants: ["dark", "placeholder"],
},
{
pattern: /divide-(gray|neutral)-800/,
@@ -37,7 +38,7 @@ export default {
pattern: /border-(gray|neutral)-200/,
},
{
pattern: /border-(gray|neutral)-800/,
pattern: /border-(gray|neutral)-700/,
variants: ["dark"],
},
],