mirror of
https://github.com/pheralb/svgl.git
synced 2025-12-29 08:01:36 +08:00
🛠️ Refactor search handling in search.svelte and +page.svelte; implement custom addParams and deleteParam utility
This commit is contained in:
@@ -2,12 +2,9 @@
|
||||
import { cn } from "@/utils/cn";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
import { page } from "$app/state";
|
||||
import { goto } from "$app/navigation";
|
||||
|
||||
import { addParams } from "@/utils/searchParams";
|
||||
import SearchIcon from "@lucide/svelte/icons/search";
|
||||
import CommandIcon from "@lucide/svelte/icons/command";
|
||||
import { SvelteURLSearchParams } from "svelte/reactivity";
|
||||
|
||||
interface Props {
|
||||
searchValue: string;
|
||||
@@ -19,19 +16,12 @@
|
||||
let inputElement: HTMLInputElement;
|
||||
|
||||
const onInput = (event: Event) => {
|
||||
const param = "search";
|
||||
const value = (event.target as HTMLInputElement).value;
|
||||
onSearch(value);
|
||||
const params = new SvelteURLSearchParams(page.url.searchParams);
|
||||
if (value) {
|
||||
params.set(param, value);
|
||||
} else {
|
||||
params.delete(param);
|
||||
}
|
||||
goto(`?${params.toString()}`, {
|
||||
keepFocus: true,
|
||||
noScroll: true,
|
||||
replaceState: true,
|
||||
addParams({
|
||||
params: {
|
||||
search: value,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
+19
-3
@@ -3,6 +3,7 @@
|
||||
import type { PageProps } from "./$types";
|
||||
|
||||
import { cn } from "@/utils/cn";
|
||||
import { deleteParam } from "@/utils/searchParams";
|
||||
import { svgsData } from "@/data";
|
||||
import { searchWithFuse } from "@/utils/searchWithFuse";
|
||||
|
||||
@@ -12,11 +13,12 @@
|
||||
import SvgCard from "@/components/svgs/svgCard.svelte";
|
||||
import SortSvgs from "@/components/svgs/sortSvgs.svelte";
|
||||
import Container from "@/components/container.svelte";
|
||||
import SearchXIcon from "@lucide/svelte/icons/search-x";
|
||||
|
||||
import PageCard from "@/components/pageCard.svelte";
|
||||
import PageHeader from "@/components/pageHeader.svelte";
|
||||
import FolderIcon from "@lucide/svelte/icons/folder";
|
||||
import FolderSearchIcon from "@lucide/svelte/icons/folder-search";
|
||||
import PageHeader from "@/components/pageHeader.svelte";
|
||||
import Button from "@/components/ui/button/button.svelte";
|
||||
|
||||
// SSR Data:
|
||||
let { data }: PageProps = $props();
|
||||
@@ -60,6 +62,13 @@
|
||||
searchSvgs();
|
||||
};
|
||||
|
||||
const handleClearSearch = () => {
|
||||
searchTerm = "";
|
||||
filteredSvgs = sorted ? alphabeticallySorted : latestSorted;
|
||||
deleteParam("search");
|
||||
updateDisplaySvgs();
|
||||
};
|
||||
|
||||
$effect(() => {
|
||||
updateDisplaySvgs();
|
||||
});
|
||||
@@ -87,7 +96,14 @@
|
||||
<span>logos</span>
|
||||
</p>
|
||||
{:else}
|
||||
<FolderSearchIcon size={18} strokeWidth={1.5} />
|
||||
<Button
|
||||
title="Clear Search"
|
||||
onclick={handleClearSearch}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
>
|
||||
<SearchXIcon size={18} strokeWidth={1.5} />
|
||||
</Button>
|
||||
<p>
|
||||
<span class="font-mono">{filteredSvgs.length}</span>
|
||||
<span>logos</span>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import type { PageProps } from "./$types";
|
||||
|
||||
import { page } from "$app/state";
|
||||
import { goto } from "$app/navigation";
|
||||
import { SvelteURLSearchParams } from "svelte/reactivity";
|
||||
|
||||
import { cn } from "@/utils/cn";
|
||||
@@ -14,68 +13,70 @@
|
||||
import Search from "@/components/search.svelte";
|
||||
import SvgCard from "@/components/svgs/svgCard.svelte";
|
||||
import Container from "@/components/container.svelte";
|
||||
import SearchXIcon from "@lucide/svelte/icons/search-x";
|
||||
|
||||
import PageCard from "@/components/pageCard.svelte";
|
||||
import PageHeader from "@/components/pageHeader.svelte";
|
||||
import FolderIcon from "@lucide/svelte/icons/folder-open";
|
||||
import ArrowLeftIcon from "@lucide/svelte/icons/arrow-left";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { Button, buttonVariants } from "@/components/ui/button";
|
||||
import SortSvgs from "@/components/svgs/sortSvgs.svelte";
|
||||
import { deleteParam } from "@/utils/searchParams";
|
||||
|
||||
// SSR Data:
|
||||
let { data }: PageProps = $props();
|
||||
const directoryData = $derived(data);
|
||||
|
||||
// States:
|
||||
let maxDisplay = 30;
|
||||
let searchTerm = $state<string>(data.searchTerm || "");
|
||||
let filteredSvgs = $derived<iSVG[]>(data.filteredSvgs);
|
||||
let filteredSvgs = $derived<iSVG[]>(data.initialSvgs);
|
||||
let sorted = $state<boolean>(data.sorted);
|
||||
let displaySvgs = $state<iSVG[]>([]);
|
||||
let showAll = $state<boolean>(false);
|
||||
|
||||
const updateDisplaySvgs = () => {
|
||||
displaySvgs = showAll ? filteredSvgs : filteredSvgs.slice(0, maxDisplay);
|
||||
};
|
||||
|
||||
const searchSvgs = () => {
|
||||
if (!searchTerm) {
|
||||
filteredSvgs = data.svgs;
|
||||
filteredSvgs = sorted ? data.alphabeticallySorted : data.latestSorted;
|
||||
updateDisplaySvgs();
|
||||
return;
|
||||
}
|
||||
if (searchTerm.length < 3) {
|
||||
filteredSvgs = data.svgs.filter((svg: iSVG) =>
|
||||
filteredSvgs = (
|
||||
sorted ? data.alphabeticallySorted : data.latestSorted
|
||||
).filter((svg: iSVG) =>
|
||||
svg.title.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||
);
|
||||
} else {
|
||||
filteredSvgs = searchWithFuse(data.svgs)
|
||||
filteredSvgs = searchWithFuse(filteredSvgs)
|
||||
.search(searchTerm)
|
||||
.map((result) => result.item);
|
||||
}
|
||||
updateDisplaySvgs();
|
||||
};
|
||||
|
||||
const handleSearch = (value: string) => {
|
||||
searchTerm = value;
|
||||
|
||||
const params = new SvelteURLSearchParams(page.url.searchParams);
|
||||
if (value) {
|
||||
params.set("search", value);
|
||||
} else {
|
||||
params.delete("search");
|
||||
}
|
||||
|
||||
goto(`?${params.toString()}`, {
|
||||
keepFocus: true,
|
||||
noScroll: true,
|
||||
replaceState: true,
|
||||
});
|
||||
|
||||
searchSvgs();
|
||||
};
|
||||
|
||||
const formatCategory = (category: string) =>
|
||||
category.charAt(0).toUpperCase() + category.slice(1);
|
||||
const handleClearSearch = () => {
|
||||
searchTerm = "";
|
||||
deleteParam("search");
|
||||
updateDisplaySvgs();
|
||||
};
|
||||
|
||||
$effect(() => {
|
||||
filteredSvgs = data.svgs.filter((svg: iSVG) =>
|
||||
svg.title.toLowerCase().includes(searchTerm.toLowerCase()),
|
||||
);
|
||||
updateDisplaySvgs();
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{formatCategory(directoryData.category)} SVG logos - Svgl</title>
|
||||
<title>{directoryData.category} SVG logos - Svgl</title>
|
||||
</svelte:head>
|
||||
|
||||
<Search
|
||||
@@ -91,24 +92,29 @@
|
||||
>
|
||||
<a
|
||||
href="/"
|
||||
class={cn(
|
||||
buttonVariants({ class: "group", variant: "ghost", size: "icon" }),
|
||||
)}
|
||||
class={cn(buttonVariants({ variant: "ghost", size: "icon" }))}
|
||||
>
|
||||
<ArrowLeftIcon
|
||||
size={18}
|
||||
strokeWidth={1.5}
|
||||
class="transition-transform group-hover:translate-x-[-2px]"
|
||||
/>
|
||||
<ArrowLeftIcon size={18} strokeWidth={1.5} />
|
||||
</a>
|
||||
<FolderIcon size={18} strokeWidth={1.5} />
|
||||
{#if searchTerm}
|
||||
<Button
|
||||
title="Clear Search"
|
||||
onclick={handleClearSearch}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
>
|
||||
<SearchXIcon size={18} strokeWidth={1.5} />
|
||||
</Button>
|
||||
{:else}
|
||||
<FolderIcon class="ml-1" size={18} strokeWidth={1.5} />
|
||||
{/if}
|
||||
<p>
|
||||
{formatCategory(directoryData.category)}
|
||||
{directoryData.category}
|
||||
</p>
|
||||
<span>-</span>
|
||||
{#if !searchTerm}
|
||||
<p>
|
||||
<span>{data.svgs.length} SVGs </span>
|
||||
<span>{data.initialSvgs.length} SVGs </span>
|
||||
</p>
|
||||
{:else}
|
||||
<p>
|
||||
@@ -117,6 +123,14 @@
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
<SortSvgs
|
||||
className={cn(filteredSvgs.length === 0 && "hidden")}
|
||||
isSorted={sorted}
|
||||
onSortedChange={(value) => {
|
||||
sorted = value;
|
||||
searchSvgs();
|
||||
}}
|
||||
/>
|
||||
</PageHeader>
|
||||
<Container className="my-6">
|
||||
<Grid>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { page } from "$app/state";
|
||||
import { goto } from "$app/navigation";
|
||||
import { SvelteURLSearchParams } from "svelte/reactivity";
|
||||
|
||||
interface SearchParams {
|
||||
params: Record<string, string | null>;
|
||||
}
|
||||
|
||||
const addParams = ({ params }: SearchParams) => {
|
||||
const searchParams = new SvelteURLSearchParams(page.url.searchParams);
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value) {
|
||||
searchParams.set(key, value);
|
||||
} else {
|
||||
searchParams.delete(key);
|
||||
}
|
||||
});
|
||||
goto(`?${searchParams.toString()}`, {
|
||||
keepFocus: true,
|
||||
noScroll: true,
|
||||
replaceState: true,
|
||||
});
|
||||
};
|
||||
|
||||
const deleteParam = (key: string) => {
|
||||
const params = new SvelteURLSearchParams(page.url.searchParams);
|
||||
params.delete(key);
|
||||
goto(`?${params.toString()}`, {
|
||||
keepFocus: true,
|
||||
noScroll: true,
|
||||
replaceState: true,
|
||||
});
|
||||
};
|
||||
|
||||
export { addParams, deleteParam };
|
||||
Reference in New Issue
Block a user