🛠️ Refactor search handling in search.svelte and +page.svelte; implement custom addParams and deleteParam utility

This commit is contained in:
pheralb
2025-09-01 11:34:24 +01:00
parent e6d441e9f2
commit 2a38b834c3
4 changed files with 110 additions and 55 deletions
+5 -15
View File
@@ -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
View File
@@ -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>
+51 -37
View File
@@ -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>
+35
View File
@@ -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 };