refactor: Add tag filtering functionality to theme search
This commit is contained in:
@@ -7,6 +7,7 @@ import { Button } from "./ui/button";
|
|||||||
|
|
||||||
export default function MarketplacePage() {
|
export default function MarketplacePage() {
|
||||||
const [searchInput, setSearchInput] = React.useState("");
|
const [searchInput, setSearchInput] = React.useState("");
|
||||||
|
const [tags, setTags] = React.useState<string[]>(["all"]);
|
||||||
const [themes, setThemes] = React.useState<ZenTheme[]>([]);
|
const [themes, setThemes] = React.useState<ZenTheme[]>([]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@@ -18,11 +19,11 @@ export default function MarketplacePage() {
|
|||||||
<div className="mx-auto w-full text-center border-b pt-48 pb-24 mb-12 bg-gradient-to-r from-[#f6cfbe] to-[#b9dcf2] dark:from-[#0d1117] dark:to-[#0d1117]">
|
<div className="mx-auto w-full text-center border-b pt-48 pb-24 mb-12 bg-gradient-to-r from-[#f6cfbe] to-[#b9dcf2] dark:from-[#0d1117] dark:to-[#0d1117]">
|
||||||
<div className="w-full lg:w-1/2 xl:w-1/2 mx-auto px-2 lg:px-none">
|
<div className="w-full lg:w-1/2 xl:w-1/2 mx-auto px-2 lg:px-none">
|
||||||
<h1 className="text-4xl lg:text-7xl font-bold">Themes Store</h1>
|
<h1 className="text-4xl lg:text-7xl font-bold">Themes Store</h1>
|
||||||
<ThemesSearch input={searchInput} setInput={setSearchInput} />
|
<ThemesSearch input={searchInput} setInput={setSearchInput} tags={tags} setTags={setTags} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-8 mt-10 w-full lg:w-1/2 xl:w-2/3 2xl:w-3/4">
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-8 mt-10 w-full lg:w-1/2 xl:w-2/3 2xl:w-3/4">
|
||||||
{getThemesFromSearch(themes, searchInput).map((theme) => (
|
{getThemesFromSearch(themes, searchInput, tags).map((theme) => (
|
||||||
<ThemeCard key={theme.name} theme={theme} />
|
<ThemeCard key={theme.name} theme={theme} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,23 @@
|
|||||||
import { SearchIcon } from "lucide-react";
|
import { SearchIcon } from "lucide-react";
|
||||||
import { Button } from "./ui/button";
|
import { Button } from "./ui/button";
|
||||||
|
import { ny } from "@/lib/utils";
|
||||||
|
|
||||||
|
const TAGS = [
|
||||||
|
"all",
|
||||||
|
"color-scheme",
|
||||||
|
"utility",
|
||||||
|
];
|
||||||
|
|
||||||
export default function ThemesSearch({
|
export default function ThemesSearch({
|
||||||
input, setInput
|
input, setInput, tags, setTags
|
||||||
}: {
|
}: {
|
||||||
input: string;
|
input: string;
|
||||||
setInput: (input: string) => void;
|
setInput: (input: string) => void;
|
||||||
|
tags: string[];
|
||||||
|
setTags: (tags: string[]) => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div className="flex w-full p-2 bg-black/10 dark:bg-muted/50 rounded-full overflow-hidden mt-10 items-center border border-black dark:border-muted">
|
<div className="flex w-full p-2 bg-black/10 dark:bg-muted/50 rounded-full overflow-hidden mt-10 items-center border border-black dark:border-muted">
|
||||||
<SearchIcon className="w-6 h-6 mx-4 text-black dark:text-white" />
|
<SearchIcon className="w-6 h-6 mx-4 text-black dark:text-white" />
|
||||||
<input
|
<input
|
||||||
@@ -26,5 +36,16 @@ export default function ThemesSearch({
|
|||||||
className="text-muted rounded-full rounded-l-none border-l border-black dark:border-none hidden md:block"
|
className="text-muted rounded-full rounded-l-none border-l border-black dark:border-none hidden md:block"
|
||||||
>Create your theme</Button>
|
>Create your theme</Button>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-2 mt-4">
|
||||||
|
{TAGS.map((tag) => (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
key={tag}
|
||||||
|
onClick={() => setTags([tag])}
|
||||||
|
className={ny(`!rounded-full px-5 ${tags.includes(tag) ? "bg-black dark:bg-white text-white dark:text-black" : ""}`)}
|
||||||
|
>{tag}</Button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ export interface ZenTheme {
|
|||||||
id: string
|
id: string
|
||||||
homepage?: string
|
homepage?: string
|
||||||
readme: string
|
readme: string
|
||||||
preferences: {
|
preferences?: string
|
||||||
[key: string]: string
|
isColorTheme: boolean
|
||||||
},
|
|
||||||
author: string
|
author: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,8 +27,16 @@ export async function getAllThemes() {
|
|||||||
return themesArray;
|
return themesArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getThemesFromSearch(themes: ZenTheme[], query: string): ZenTheme[] {
|
export function getThemesFromSearch(themes: ZenTheme[], query: string, tags: string[]): ZenTheme[] {
|
||||||
return themes.filter((theme) => theme.name.toLowerCase().includes(query.toLowerCase()));
|
let filtered = themes.filter((theme) => theme.name.toLowerCase().includes(query.toLowerCase()));
|
||||||
|
if (tags.includes("all")) return filtered;
|
||||||
|
const isSearchingForColorScheme = tags.includes("color-scheme");
|
||||||
|
const isSearchingForUtility = !isSearchingForColorScheme && tags.includes("utility");
|
||||||
|
return filtered.filter((theme) => {
|
||||||
|
if (isSearchingForColorScheme && theme.isColorTheme) return true;
|
||||||
|
if (isSearchingForUtility && !theme.isColorTheme) return true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getThemeFromId(id: string) {
|
export async function getThemeFromId(id: string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user