mirror of
https://github.com/pheralb/svgl.git
synced 2025-12-29 08:01:36 +08:00
Compare commits
7 Commits
55199765be
...
803e13001a
| Author | SHA1 | Date | |
|---|---|---|---|
| 803e13001a | |||
| 77356d3215 | |||
| 1591ea3146 | |||
| 2a38b834c3 | |||
| e6d441e9f2 | |||
| bc34bdc904 | |||
| 2692c7d34d |
@@ -18,11 +18,6 @@
|
||||
}
|
||||
|
||||
let { githubStars }: HeaderProps = $props();
|
||||
|
||||
const headerItemsClasses = cn(
|
||||
buttonVariants({ variant: "ghost" }),
|
||||
"hover:bg-neutral-200 dark:hover:bg-neutral-800",
|
||||
);
|
||||
</script>
|
||||
|
||||
<header
|
||||
@@ -39,24 +34,35 @@
|
||||
</a>
|
||||
<SvglVersion />
|
||||
</div>
|
||||
<div class="flex h-8 items-center">
|
||||
<div class="flex items-center space-x-0.5">
|
||||
<div class="flex h-5 items-center space-x-2.5">
|
||||
<div class="flex items-center space-x-1.5">
|
||||
<a
|
||||
target="_blank"
|
||||
title="X/Twitter"
|
||||
href={globals.twitterUrl}
|
||||
class={cn(headerItemsClasses, "h-9 w-9")}
|
||||
class={cn(
|
||||
buttonVariants({ variant: "ghost", size: "icon" }),
|
||||
"hover:bg-neutral-200 dark:hover:bg-neutral-800",
|
||||
)}
|
||||
>
|
||||
<Twitter size={18} />
|
||||
</a>
|
||||
<ModeToggle className={cn(headerItemsClasses, "h-9 w-9")} />
|
||||
<ModeToggle
|
||||
className={cn(
|
||||
buttonVariants({ variant: "ghost", size: "icon" }),
|
||||
"hover:bg-neutral-200 dark:hover:bg-neutral-800",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Separator orientation="vertical" class="mx-2 h-8" />
|
||||
<Separator orientation="vertical" />
|
||||
<a
|
||||
target="_blank"
|
||||
title="GitHub Repository"
|
||||
href={globals.githubUrl}
|
||||
class={cn(headerItemsClasses, "h-9 w-fit")}
|
||||
class={cn(
|
||||
buttonVariants({ variant: "ghost" }),
|
||||
"w-fit hover:bg-neutral-200 dark:hover:bg-neutral-800",
|
||||
)}
|
||||
>
|
||||
<Github size={20} />
|
||||
<span class="text-neutral-600 dark:text-neutral-400">
|
||||
@@ -65,7 +71,7 @@
|
||||
: githubStars.toLocaleString()}
|
||||
</span>
|
||||
</a>
|
||||
<Separator orientation="vertical" class="mr-3 ml-2" />
|
||||
<Separator orientation="vertical" />
|
||||
<a
|
||||
target="_blank"
|
||||
href={globals.submitUrl}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type { iSVG } from "@/types/svg";
|
||||
|
||||
import { cn } from "@/utils/cn";
|
||||
import { mode } from "mode-watcher";
|
||||
import { getSvgImgUrl } from "@/data";
|
||||
|
||||
// Icons:
|
||||
import XIcon from "@lucide/svelte/icons/x";
|
||||
@@ -32,7 +34,6 @@
|
||||
// States:
|
||||
let wordmarkSvg = $state<boolean>(false);
|
||||
let moreTagsOptions = $state<boolean>(false);
|
||||
let changeThemeMode = $state<boolean>(false);
|
||||
|
||||
// Icon Stroke & Size:
|
||||
let iconStroke = 1.8;
|
||||
@@ -69,47 +70,17 @@
|
||||
<AddToFavorite svg={svgInfo} />
|
||||
</div>
|
||||
<!-- Image -->
|
||||
{#if wordmarkSvg == true && svgInfo.wordmark !== undefined}
|
||||
{#if changeThemeMode}
|
||||
<img
|
||||
class={cn("block", globalImageStyles)}
|
||||
src={typeof svgInfo.wordmark !== "string"
|
||||
? mode.current === "dark"
|
||||
? svgInfo.wordmark?.light || ""
|
||||
: svgInfo.wordmark?.dark || ""
|
||||
: svgInfo.wordmark || ""}
|
||||
alt={svgInfo.title}
|
||||
title={svgInfo.title}
|
||||
loading="lazy"
|
||||
/>
|
||||
{:else}
|
||||
<img
|
||||
class={cn("hidden dark:block", globalImageStyles)}
|
||||
src={typeof svgInfo.wordmark !== "string"
|
||||
? svgInfo.wordmark?.dark || ""
|
||||
: svgInfo.wordmark || ""}
|
||||
alt={svgInfo.title}
|
||||
title={svgInfo.title}
|
||||
loading="lazy"
|
||||
/>
|
||||
<img
|
||||
class={cn("block dark:hidden", globalImageStyles)}
|
||||
src={typeof svgInfo.wordmark !== "string"
|
||||
? svgInfo.wordmark?.light || ""
|
||||
: svgInfo.wordmark || ""}
|
||||
alt={svgInfo.title}
|
||||
title={svgInfo.title}
|
||||
loading="lazy"
|
||||
/>
|
||||
{/if}
|
||||
{:else if changeThemeMode}
|
||||
{#if wordmarkSvg && svgInfo.wordmark !== undefined}
|
||||
<img
|
||||
class={cn("block", globalImageStyles)}
|
||||
src={typeof svgInfo.route !== "string"
|
||||
? mode.current === "dark"
|
||||
? svgInfo.route.light
|
||||
: svgInfo.route.dark
|
||||
: svgInfo.route}
|
||||
class={cn("hidden dark:block", globalImageStyles)}
|
||||
src={getSvgImgUrl({ url: svgInfo.wordmark, isDark: true })}
|
||||
alt={svgInfo.title}
|
||||
title={svgInfo.title}
|
||||
loading="lazy"
|
||||
/>
|
||||
<img
|
||||
class={cn("block dark:hidden", globalImageStyles)}
|
||||
src={getSvgImgUrl({ url: svgInfo.wordmark, isDark: false })}
|
||||
alt={svgInfo.title}
|
||||
title={svgInfo.title}
|
||||
loading="lazy"
|
||||
@@ -117,18 +88,14 @@
|
||||
{:else}
|
||||
<img
|
||||
class={cn("hidden dark:block", globalImageStyles)}
|
||||
src={typeof svgInfo.route !== "string"
|
||||
? svgInfo.route.dark
|
||||
: svgInfo.route}
|
||||
src={getSvgImgUrl({ url: svgInfo.route, isDark: true })}
|
||||
alt={svgInfo.title}
|
||||
title={svgInfo.title}
|
||||
loading="lazy"
|
||||
/>
|
||||
<img
|
||||
class={cn("block dark:hidden", globalImageStyles)}
|
||||
src={typeof svgInfo.route !== "string"
|
||||
? svgInfo.route.light
|
||||
: svgInfo.route}
|
||||
src={getSvgImgUrl({ url: svgInfo.route, isDark: false })}
|
||||
alt={svgInfo.title}
|
||||
title={svgInfo.title}
|
||||
loading="lazy"
|
||||
@@ -148,7 +115,8 @@
|
||||
href={`/directory/${c.toLowerCase()}`}
|
||||
class={badgeVariants({
|
||||
variant: "outline",
|
||||
class: "cursor-pointer font-mono",
|
||||
class:
|
||||
"cursor-pointer font-mono hover:border-neutral-400 dark:hover:border-neutral-600",
|
||||
})}
|
||||
title={`This icon is part of the ${svgInfo.category} category`}
|
||||
>
|
||||
@@ -164,7 +132,8 @@
|
||||
<Popover.Trigger
|
||||
class={badgeVariants({
|
||||
variant: "outline",
|
||||
class: "cursor-pointer font-mono",
|
||||
class:
|
||||
"cursor-pointer font-mono hover:border-neutral-400 dark:hover:border-neutral-600",
|
||||
})}
|
||||
title="More Tags"
|
||||
>
|
||||
@@ -193,7 +162,8 @@
|
||||
href={`/directory/${svgInfo.category.toLowerCase()}`}
|
||||
class={badgeVariants({
|
||||
variant: "outline",
|
||||
class: "cursor-pointer font-mono",
|
||||
class:
|
||||
"cursor-pointer font-mono hover:border-neutral-400 dark:hover:border-neutral-600",
|
||||
})}
|
||||
>
|
||||
{svgInfo.category}
|
||||
@@ -221,13 +191,7 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<DownloadSvg
|
||||
{svgInfo}
|
||||
isDarkTheme={() => {
|
||||
const dark = document.documentElement.classList.contains("dark");
|
||||
return dark;
|
||||
}}
|
||||
/>
|
||||
<DownloadSvg {svgInfo} isDarkTheme={() => mode.current === "dark"} />
|
||||
|
||||
<a
|
||||
href={svgInfo.url}
|
||||
|
||||
+23
-13
@@ -1,11 +1,12 @@
|
||||
import type { iSVG } from "@/types/svg";
|
||||
import { svgs } from "./svgs";
|
||||
import type { iSVG, ThemeOptions } from "@/types/svg";
|
||||
import type { Category } from "@/types/categories";
|
||||
import { svgs } from "@/data/svgs";
|
||||
|
||||
export const svgsData = svgs.map((svg: iSVG, index: number) => {
|
||||
return { id: index, ...svg };
|
||||
}) as iSVG[];
|
||||
|
||||
export const getCategories = () => {
|
||||
export const getCategories = (): Category[] => {
|
||||
const categories = svgs
|
||||
.flatMap((svg) =>
|
||||
Array.isArray(svg.category) ? svg.category : [svg.category],
|
||||
@@ -14,14 +15,23 @@ export const getCategories = () => {
|
||||
return categories;
|
||||
};
|
||||
|
||||
export const getCategoriesForDirectory = () => {
|
||||
const categories = svgs
|
||||
.flatMap((svg) =>
|
||||
Array.isArray(svg.category) ? svg.category : [svg.category],
|
||||
)
|
||||
.filter((category, index, array) => array.indexOf(category) === index)
|
||||
.map((category) => ({
|
||||
slug: category.toLowerCase(),
|
||||
}));
|
||||
return categories;
|
||||
export const getSvgsByCategory = (category: string): iSVG[] =>
|
||||
svgsData.filter((svg: iSVG) => {
|
||||
if (Array.isArray(svg.category)) {
|
||||
return svg.category.some(
|
||||
(categoryItem) => categoryItem.toLowerCase() === category.toLowerCase(),
|
||||
);
|
||||
} else {
|
||||
return svg.category.toLowerCase() === category.toLowerCase();
|
||||
}
|
||||
});
|
||||
|
||||
interface GetSvgImgUrl {
|
||||
url: string | ThemeOptions;
|
||||
isDark: boolean;
|
||||
}
|
||||
|
||||
export const getSvgImgUrl = ({ url, isDark }: GetSvgImgUrl) => {
|
||||
if (typeof url === "string") return url;
|
||||
return isDark ? url.dark : url.light;
|
||||
};
|
||||
|
||||
+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>
|
||||
|
||||
+2
-1
@@ -23,7 +23,8 @@ export const load: Load = ({ url }) => {
|
||||
svg.title.toLowerCase().includes(searchParam.toLowerCase()),
|
||||
);
|
||||
} else {
|
||||
filteredSvgs = searchWithFuse(filteredSvgs)
|
||||
const baseData = sortParam ? alphabeticallySorted : latestSorted;
|
||||
filteredSvgs = searchWithFuse(baseData)
|
||||
.search(searchParam)
|
||||
.map((result) => result.item);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,48 +1,50 @@
|
||||
import type { PageLoad } from "./$types";
|
||||
import type { iSVG } from "@/types/svg";
|
||||
|
||||
import { svgs } from "@/data/svgs";
|
||||
import { error } from "@sveltejs/kit";
|
||||
import { getSvgsByCategory } from "@/data";
|
||||
import { searchWithFuse } from "@/utils/searchWithFuse";
|
||||
|
||||
export const load: PageLoad = (async ({ params, url }) => {
|
||||
const { category } = params;
|
||||
const searchParam = url.searchParams.get("search") || "";
|
||||
const sortParam = url.searchParams.get("sort") === "alphabetical";
|
||||
|
||||
const svgsByCategory = svgs.filter((svg: iSVG) => {
|
||||
if (Array.isArray(svg.category)) {
|
||||
return svg.category.some(
|
||||
(categoryItem) => categoryItem.toLowerCase() === category.toLowerCase(),
|
||||
);
|
||||
} else {
|
||||
return svg.category.toLowerCase() === category.toLowerCase();
|
||||
}
|
||||
});
|
||||
const svgsByCategory = getSvgsByCategory(category);
|
||||
|
||||
if (svgsByCategory.length === 0) {
|
||||
if (!svgsByCategory.length) {
|
||||
throw error(404, "Category not found");
|
||||
}
|
||||
|
||||
let filteredSvgs: iSVG[] = [];
|
||||
const latestSorted = [...svgsByCategory].sort((a, b) => b.id! - a.id!);
|
||||
const alphabeticallySorted = [...svgsByCategory].sort((a, b) =>
|
||||
a.title.localeCompare(b.title),
|
||||
);
|
||||
const formatCategory = category.charAt(0).toUpperCase() + category.slice(1);
|
||||
|
||||
if (!searchParam) {
|
||||
filteredSvgs = svgsByCategory;
|
||||
filteredSvgs = sortParam ? alphabeticallySorted : latestSorted;
|
||||
} else {
|
||||
if (searchParam.length < 3) {
|
||||
filteredSvgs = svgsByCategory.filter((svg: iSVG) =>
|
||||
const baseData = sortParam ? alphabeticallySorted : latestSorted;
|
||||
filteredSvgs = baseData.filter((svg: iSVG) =>
|
||||
svg.title.toLowerCase().includes(searchParam.toLowerCase()),
|
||||
);
|
||||
} else {
|
||||
filteredSvgs = searchWithFuse(svgsByCategory)
|
||||
const baseData = sortParam ? alphabeticallySorted : latestSorted;
|
||||
filteredSvgs = searchWithFuse(baseData)
|
||||
.search(searchParam)
|
||||
.map((result) => result.item);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
category: category,
|
||||
category: formatCategory,
|
||||
searchTerm: searchParam,
|
||||
svgs: svgsByCategory,
|
||||
filteredSvgs: filteredSvgs,
|
||||
sorted: sortParam,
|
||||
initialSvgs: filteredSvgs,
|
||||
latestSorted,
|
||||
alphabeticallySorted,
|
||||
};
|
||||
}) satisfies PageLoad;
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
<meta name="description" content={data.document.description} />
|
||||
</svelte:head>
|
||||
|
||||
<PageCard>
|
||||
<PageCard
|
||||
containerClass="mt-0"
|
||||
contentCardClass="max-h-[calc(100vh-5.2rem)] min-h-[calc(100vh-5.2rem)]"
|
||||
>
|
||||
<PageHeader>
|
||||
<div
|
||||
class="flex items-center space-x-2 font-medium text-neutral-950 dark:text-neutral-50"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export type tCategory =
|
||||
export type Category =
|
||||
| "All"
|
||||
| "AI"
|
||||
| "Software"
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import type { tCategory } from "./categories";
|
||||
import type { Category } from "./categories";
|
||||
|
||||
export type ThemeOptions = {
|
||||
dark: string;
|
||||
@@ -8,7 +8,7 @@ export type ThemeOptions = {
|
||||
export interface iSVG {
|
||||
id?: number;
|
||||
title: string;
|
||||
category: tCategory | tCategory[];
|
||||
category: Category | Category[];
|
||||
route: string | ThemeOptions;
|
||||
wordmark?: string | ThemeOptions;
|
||||
brandUrl?: string;
|
||||
|
||||
@@ -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