Initial shadcn/ui integration + improve copySvg component + move addToFavorite

This commit is contained in:
pheralb
2025-08-29 12:43:17 +01:00
parent acabd32b2d
commit 3e282c2057
5 changed files with 135 additions and 13 deletions
+40
View File
@@ -0,0 +1,40 @@
<script lang="ts">
import * as Select from "@/components/ui/select";
import { pkgManager, type PackageManager } from "@/stores/pkgManager.store";
import Npm from "@/components/logos/npm.svelte";
import Pnpm from "@/components/logos/pnpm.svelte";
import Yarn from "@/components/logos/yarn.svelte";
import Bun from "@/components/logos/bun.svelte";
import { buttonVariants } from "./ui/button";
let pkg = $derived($pkgManager);
const managers = {
npm: { label: "npm", Icon: Npm },
pnpm: { label: "pnpm", Icon: Pnpm },
yarn: { label: "yarn", Icon: Yarn },
bun: { label: "bun", Icon: Bun },
};
</script>
<Select.Root type="single" bind:value={pkg}>
<Select.Trigger class={buttonVariants({ variant: "outline", size: "sm" })}>
{#if managers[pkg]}
{@const { Icon, label } = managers[pkg]}
<Icon size={14} />
<span>{label}</span>
{/if}
</Select.Trigger>
<Select.Content sideOffset={1.5}>
{#each Object.entries(managers) as [value, { Icon, label }]}
<Select.Item
{value}
onclick={() => pkgManager.set(value as PackageManager)}
>
<Icon size={16} />
<span>{label}</span>
</Select.Item>
{/each}
</Select.Content>
</Select.Root>
@@ -1,8 +1,9 @@
<script lang="ts"> <script lang="ts">
import type { iSVG } from "@/types/svg"; import type { iSVG } from "@/types/svg";
import { cn } from "@/utils/cn";
import favoritesStore from "@/stores/favorites.store"; import favoritesStore from "@/stores/favorites.store";
import HeartIcon from "@lucide/svelte/icons/heart"; import HeartIcon from "@lucide/svelte/icons/heart";
import { cn } from "@/utils/cn";
interface Props { interface Props {
svg: iSVG; svg: iSVG;
@@ -0,0 +1,49 @@
<script lang="ts">
import { clipboard } from "@/utils/clipboard";
import CopyIcon from "@lucide/svelte/icons/copy";
import { Button } from "@/components/ui/button";
import SelectPkgManager from "@/components/selectPkgManager.svelte";
import { pkgManager, type PackageManager } from "@/stores/pkgManager.store";
import Shadcn from "../logos/shadcn.svelte";
interface Props {
svgTitle: string;
}
let { svgTitle }: Props = $props();
const shadcnCommands: Record<PackageManager, string> = {
npm: "npx shadcn@latest add",
pnpm: "pnpm dlx shadcn@latest add",
yarn: "yarn dlx shadcn@latest add",
bun: "bunx shadcn@latest add",
};
let pkg = $derived($pkgManager);
let shadcnCommand = $derived(shadcnCommands[pkg]);
const svgFormatTitle = svgTitle
.toLowerCase()
.replace(/\s+/g, "-")
.replace(/[^a-z0-9-]/g, "");
const handleCopy = () => {
clipboard(`${shadcnCommand} @svgl/${svgFormatTitle}`);
};
</script>
<div class="flex items-center justify-between space-x-2">
<SelectPkgManager />
<Button variant="outline" onclick={handleCopy} size="sm">
<CopyIcon size={14} />
<span>Copy</span>
</Button>
</div>
<div
class="flex items-center space-x-2 rounded-md border border-neutral-200 p-2.5 dark:border-neutral-800"
>
<Shadcn size={14} />
<code class="font-mono text-sm select-all">
{shadcnCommand} @svgl/{svgFormatTitle}
</code>
</div>
+43 -11
View File
@@ -2,6 +2,7 @@
import type { iSVG } from "@/types/svg"; import type { iSVG } from "@/types/svg";
// Utils: // Utils:
import { cn } from "@/utils/cn";
import { clipboard } from "@/utils/clipboard"; import { clipboard } from "@/utils/clipboard";
import { getPrefixFromSvgUrl, prefixSvgIds } from "@/utils/prefixSvgIds"; import { getPrefixFromSvgUrl, prefixSvgIds } from "@/utils/prefixSvgIds";
import { copyToClipboard as figmaCopyToClipboard } from "@/figma/copy-to-clipboard"; import { copyToClipboard as figmaCopyToClipboard } from "@/figma/copy-to-clipboard";
@@ -15,9 +16,12 @@
// UI Components: // UI Components:
import { toast } from "svelte-sonner"; import { toast } from "svelte-sonner";
import * as Tabs from "@/components/ui/tabs"; import * as Tabs from "@/components/ui/tabs";
import { Button } from "@/components/ui/button"; import { Button, buttonVariants } from "@/components/ui/button";
import * as Popover from "@/components/ui/popover"; import * as Popover from "@/components/ui/popover";
// CLIs:
import CopyShadcnCommand from "@/components/svgs/copyShadcnCommand.svelte";
// Templates: // Templates:
import { getSource } from "@/templates/getSource"; import { getSource } from "@/templates/getSource";
import { getVueCode } from "@/templates/getVueCode"; import { getVueCode } from "@/templates/getVueCode";
@@ -360,7 +364,13 @@
<Popover.Root bind:open={optionsOpen}> <Popover.Root bind:open={optionsOpen}>
<Popover.Trigger <Popover.Trigger
title="Copy SVG element as svg file, React TSX code, or React JSX code" title="Copy SVG element as svg file, React TSX code, or React JSX code"
class="flex items-center space-x-2 rounded-md p-2 duration-100 hover:bg-neutral-200 dark:hover:bg-neutral-700/40" class={cn(
buttonVariants({
variant: "ghost",
size: "icon",
class: "hover:bg-neutral-200",
}),
)}
> >
{#if optionsOpen} {#if optionsOpen}
<XIcon {size} strokeWidth={iconStroke} /> <XIcon {size} strokeWidth={iconStroke} />
@@ -370,32 +380,37 @@
<CopyIcon {size} strokeWidth={iconStroke} /> <CopyIcon {size} strokeWidth={iconStroke} />
{/if} {/if}
</Popover.Trigger> </Popover.Trigger>
<Popover.Content class="flex flex-col space-y-2 p-4" sideOffset={2}> <Popover.Content class="flex w-fit flex-col space-y-2 p-4" sideOffset={2}>
<Tabs.Root value="source" class="flex w-full flex-col space-y-1"> <Tabs.Root value="source" class="flex w-full flex-col space-y-1">
<Tabs.List> <Tabs.List class="w-fit border-none bg-transparent">
<Tabs.Trigger value="source">Source</Tabs.Trigger> <Tabs.Trigger value="source">Source</Tabs.Trigger>
<Tabs.Trigger value="shadcn">shadcn/ui</Tabs.Trigger>
<div <div
class="ml-3 flex flex-row space-x-0.5 border-l border-dashed border-neutral-200 pl-3 dark:border-neutral-800" class="ml-3 flex flex-row space-x-1 border-l border-neutral-200 pl-3 dark:border-neutral-800"
>
<Tabs.Trigger
class="px-2.5"
value="web-component"
title="Web Component"
> >
<Tabs.Trigger value="web-component" title="Web Component">
<WebComponents size={21} /> <WebComponents size={21} />
</Tabs.Trigger> </Tabs.Trigger>
<Tabs.Trigger value="react" title="React"> <Tabs.Trigger class="px-2.5" value="react" title="React">
<React size={20} /> <React size={20} />
</Tabs.Trigger> </Tabs.Trigger>
<Tabs.Trigger value="vue" title="Vue"> <Tabs.Trigger class="px-2.5" value="vue" title="Vue">
<Vue size={20} /> <Vue size={20} />
</Tabs.Trigger> </Tabs.Trigger>
<Tabs.Trigger value="svelte" title="Svelte"> <Tabs.Trigger class="px-2.5" value="svelte" title="Svelte">
<Svelte size={20} /> <Svelte size={20} />
</Tabs.Trigger> </Tabs.Trigger>
<Tabs.Trigger value="angular" title="Angular"> <Tabs.Trigger class="px-2.5" value="angular" title="Angular">
<Angular size={20} /> <Angular size={20} />
</Tabs.Trigger> </Tabs.Trigger>
<Tabs.Trigger <Tabs.Trigger
value="astro" value="astro"
title="Astro" title="Astro"
class="text-black dark:text-white" class="px-2.5 text-black dark:text-white"
> >
<Astro size={21} /> <Astro size={21} />
</Tabs.Trigger> </Tabs.Trigger>
@@ -406,6 +421,7 @@
<section class="flex flex-col space-y-2"> <section class="flex flex-col space-y-2">
<Button <Button
variant="outline" variant="outline"
class="justify-start"
title={isWordmarkSvg title={isWordmarkSvg
? "Copy wordmark SVG to clipboard" ? "Copy wordmark SVG to clipboard"
: "Copy SVG to clipboard"} : "Copy SVG to clipboard"}
@@ -416,11 +432,18 @@
</Button> </Button>
</section> </section>
</Tabs.Content> </Tabs.Content>
<!-- CLI -->
<Tabs.Content value="shadcn">
<section class="flex flex-col space-y-2">
<CopyShadcnCommand svgTitle={svgInfo.title} />
</section>
</Tabs.Content>
<!-- React --> <!-- React -->
<Tabs.Content value="react"> <Tabs.Content value="react">
<section class="flex flex-col space-y-2"> <section class="flex flex-col space-y-2">
<Button <Button
variant="outline" variant="outline"
class="justify-start"
title="Copy as React component" title="Copy as React component"
disabled={isLoading} disabled={isLoading}
onclick={() => convertSvgReactComponent(true)} onclick={() => convertSvgReactComponent(true)}
@@ -430,6 +453,7 @@
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
class="justify-start"
title="Copy as React component" title="Copy as React component"
disabled={isLoading} disabled={isLoading}
onclick={() => convertSvgReactComponent(false)} onclick={() => convertSvgReactComponent(false)}
@@ -444,6 +468,7 @@
<section class="flex flex-col space-y-2"> <section class="flex flex-col space-y-2">
<Button <Button
variant="outline" variant="outline"
class="justify-start"
title="Copy as Svelte component" title="Copy as Svelte component"
disabled={isLoading} disabled={isLoading}
onclick={() => convertSvgSvelteComponent(false)} onclick={() => convertSvgSvelteComponent(false)}
@@ -454,6 +479,7 @@
<Button <Button
variant="outline" variant="outline"
class="justify-start"
title="Copy as Svelte component" title="Copy as Svelte component"
disabled={isLoading} disabled={isLoading}
onclick={() => convertSvgSvelteComponent(false)} onclick={() => convertSvgSvelteComponent(false)}
@@ -464,6 +490,7 @@
<Button <Button
variant="outline" variant="outline"
class="justify-start"
title="Copy as Svelte component" title="Copy as Svelte component"
disabled={isLoading} disabled={isLoading}
onclick={() => convertSvgSvelteComponent(true)} onclick={() => convertSvgSvelteComponent(true)}
@@ -478,6 +505,7 @@
<section class="flex flex-col space-y-2"> <section class="flex flex-col space-y-2">
<Button <Button
variant="outline" variant="outline"
class="justify-start"
title="Copy as Vue component" title="Copy as Vue component"
disabled={isLoading} disabled={isLoading}
onclick={() => convertSvgVueComponent(false)} onclick={() => convertSvgVueComponent(false)}
@@ -487,6 +515,7 @@
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
class="justify-start"
title="Copy as Vue component" title="Copy as Vue component"
disabled={isLoading} disabled={isLoading}
onclick={() => convertSvgVueComponent(true)} onclick={() => convertSvgVueComponent(true)}
@@ -501,6 +530,7 @@
<section class="flex flex-col space-y-2"> <section class="flex flex-col space-y-2">
<Button <Button
variant="outline" variant="outline"
class="justify-start"
title="Copy as Standalone Component" title="Copy as Standalone Component"
disabled={isLoading} disabled={isLoading}
onclick={() => convertSvgAngularComponent()} onclick={() => convertSvgAngularComponent()}
@@ -515,6 +545,7 @@
<section class="flex flex-col space-y-2"> <section class="flex flex-col space-y-2">
<Button <Button
variant="outline" variant="outline"
class="justify-start"
title="Copy as Web Component" title="Copy as Web Component"
disabled={isLoading} disabled={isLoading}
onclick={() => convertSvgWebComponent()} onclick={() => convertSvgWebComponent()}
@@ -529,6 +560,7 @@
<section class="flex flex-col space-y-2"> <section class="flex flex-col space-y-2">
<Button <Button
variant="outline" variant="outline"
class="justify-start"
title="Copy as Astro Component" title="Copy as Astro Component"
disabled={isLoading} disabled={isLoading}
onclick={() => convertSvgAstroComponent()} onclick={() => convertSvgAstroComponent()}
+1 -1
View File
@@ -19,7 +19,7 @@
// Components: // Components:
import CopySvg from "@/components/svgs/copySvg.svelte"; import CopySvg from "@/components/svgs/copySvg.svelte";
import DownloadSvg from "@/components/svgs/downloadSvg.svelte"; import DownloadSvg from "@/components/svgs/downloadSvg.svelte";
import AddToFavorite from "@/components/addToFavorite.svelte"; import AddToFavorite from "@/components/svgs/addToFavorite.svelte";
// Props: // Props:
interface Props { interface Props {