mirror of
https://github.com/pheralb/svgl.git
synced 2024-11-13 08:46:56 +08:00
✨ Separate download component + create new dialog
This commit is contained in:
parent
5c711e4838
commit
d1c140f614
230
src/components/downloadSvg.svelte
Normal file
230
src/components/downloadSvg.svelte
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { iSVG } from '@/types/svg';
|
||||||
|
import JSZip from 'jszip';
|
||||||
|
import download from 'downloadjs';
|
||||||
|
import { toast } from 'svelte-sonner';
|
||||||
|
import { DownloadIcon } from 'lucide-svelte';
|
||||||
|
import { getSvgContent } from '@/utils/getSvgContent';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTrigger,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogDescription
|
||||||
|
} from '@/ui/dialog';
|
||||||
|
import { buttonStyles } from '@/ui/styles';
|
||||||
|
|
||||||
|
// Props:
|
||||||
|
export let svgInfo: iSVG;
|
||||||
|
export let isDarkTheme: () => boolean;
|
||||||
|
|
||||||
|
// Shared:
|
||||||
|
let iconStroke = 1.8;
|
||||||
|
let iconSize = 16;
|
||||||
|
let mainDownloadStyles =
|
||||||
|
'flex items-center space-x-2 rounded-md p-2 duration-100 hover:bg-neutral-200 dark:hover:bg-neutral-700/40';
|
||||||
|
let cardDownloadStyles =
|
||||||
|
'mt-5 flex w-full flex-col space-y-2 p-4 rounded-md shadow-sm dark:bg-neutral-800/20 bg-neutral-200/10 border border-neutral-200 dark:border-neutral-800';
|
||||||
|
|
||||||
|
// Functions:
|
||||||
|
const downloadSvg = (url?: string) => {
|
||||||
|
download(url || '');
|
||||||
|
|
||||||
|
const category = Array.isArray(svgInfo.category)
|
||||||
|
? svgInfo.category.sort().join(' - ')
|
||||||
|
: svgInfo.category;
|
||||||
|
|
||||||
|
toast.success(`Downloading...`, {
|
||||||
|
description: `${svgInfo.title} - ${category}`
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Download all variants:
|
||||||
|
const downloadAllVariants = async ({ route }: iSVG) => {
|
||||||
|
const zip = new JSZip();
|
||||||
|
|
||||||
|
if (typeof route === 'string') {
|
||||||
|
downloadSvg(route);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lightSvg = await getSvgContent(route.light, false);
|
||||||
|
const darkSvg = await getSvgContent(route.dark, false);
|
||||||
|
|
||||||
|
zip.file(`${svgInfo.title}.svg`, lightSvg);
|
||||||
|
zip.file(`${svgInfo.title}.dark.svg`, darkSvg);
|
||||||
|
|
||||||
|
if (svgInfo.wordmark) {
|
||||||
|
if (typeof svgInfo.wordmark === 'string') {
|
||||||
|
downloadSvg(svgInfo.wordmark);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lightWordmarkSvg = await getSvgContent(svgInfo.wordmark.light, false);
|
||||||
|
const darkWordmarkSvg = await getSvgContent(svgInfo.wordmark.dark, false);
|
||||||
|
|
||||||
|
zip.file(`${svgInfo.title}.wordmark.svg`, lightWordmarkSvg);
|
||||||
|
zip.file(`${svgInfo.title}.wordmark.dark.svg`, darkWordmarkSvg);
|
||||||
|
}
|
||||||
|
|
||||||
|
zip.generateAsync({ type: 'blob' }).then((content) => {
|
||||||
|
download(content, `${svgInfo.title}.zip`, 'application/zip');
|
||||||
|
});
|
||||||
|
|
||||||
|
const category = Array.isArray(svgInfo.category)
|
||||||
|
? svgInfo.category.sort().join(' - ')
|
||||||
|
: svgInfo.category;
|
||||||
|
|
||||||
|
toast.success('Downloading light & dark variants...', {
|
||||||
|
description: `${svgInfo.title} - ${category}`
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if typeof svgInfo.route === 'string'}
|
||||||
|
<button
|
||||||
|
title="Download Light & Dark variants"
|
||||||
|
class={mainDownloadStyles}
|
||||||
|
on:click={() => {
|
||||||
|
if (typeof svgInfo.route === 'string') {
|
||||||
|
downloadSvg(svgInfo.route);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DownloadIcon size={iconSize} strokeWidth={iconStroke} />
|
||||||
|
</button>
|
||||||
|
{:else}
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger title="Download SVG" class={mainDownloadStyles}>
|
||||||
|
<DownloadIcon size={iconSize} strokeWidth={iconStroke} />
|
||||||
|
</DialogTrigger>
|
||||||
|
<DialogContent class="max-w-[630px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Download {svgInfo.title}</DialogTitle>
|
||||||
|
<DialogDescription>This logo has multiple options to download.</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div class="flex w-full items-center space-x-2">
|
||||||
|
<div class={cardDownloadStyles}>
|
||||||
|
<img
|
||||||
|
src={isDarkTheme() ? svgInfo.route.dark : svgInfo.route.light}
|
||||||
|
alt={svgInfo.title}
|
||||||
|
class="h-8 my-4"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
title="Logo with light & dark variants"
|
||||||
|
class={buttonStyles}
|
||||||
|
on:click={() => downloadAllVariants(svgInfo)}
|
||||||
|
>
|
||||||
|
<DownloadIcon size={iconSize} />
|
||||||
|
<p>Light & dark variants</p>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
title="Download light variant"
|
||||||
|
class={buttonStyles}
|
||||||
|
on:click={() => {
|
||||||
|
if (typeof svgInfo.route !== 'string') {
|
||||||
|
downloadSvg(svgInfo.route.light);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DownloadIcon class="mr-2" size={iconSize} />
|
||||||
|
Only light variant
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
title="Download dark variant"
|
||||||
|
class={buttonStyles}
|
||||||
|
on:click={() => {
|
||||||
|
if (typeof svgInfo.route !== 'string') {
|
||||||
|
downloadSvg(svgInfo.route.dark);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DownloadIcon class="mr-2" size={iconSize} />
|
||||||
|
Only dark variant
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if typeof svgInfo.wordmark === 'string' && svgInfo.wordmark !== undefined}
|
||||||
|
<div class={cardDownloadStyles}>
|
||||||
|
<img
|
||||||
|
src={isDarkTheme() ? svgInfo.wordmark : svgInfo.wordmark}
|
||||||
|
alt={svgInfo.title}
|
||||||
|
class="h-8 my-4"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
title="Download Wordmark logo"
|
||||||
|
class={buttonStyles}
|
||||||
|
on:click={() => {
|
||||||
|
if (typeof svgInfo.wordmark === 'string') {
|
||||||
|
downloadSvg(svgInfo.wordmark);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DownloadIcon class="mr-2" size={iconSize} />
|
||||||
|
<p>Wordmark logo</p>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if typeof svgInfo.wordmark !== 'string' && svgInfo.wordmark !== undefined}
|
||||||
|
<div class={cardDownloadStyles}>
|
||||||
|
<img
|
||||||
|
src={isDarkTheme() ? svgInfo.wordmark.dark : svgInfo.wordmark.light}
|
||||||
|
alt={svgInfo.title}
|
||||||
|
class="h-8 my-4"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
title="Download Wordmark light variant"
|
||||||
|
class={buttonStyles}
|
||||||
|
on:click={() => {
|
||||||
|
if (typeof svgInfo.wordmark !== 'string') {
|
||||||
|
downloadAllVariants(svgInfo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DownloadIcon class="mr-2" size={iconSize} />
|
||||||
|
Light & dark variants
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
title="Download Wordmark light variant"
|
||||||
|
class={buttonStyles}
|
||||||
|
on:click={() => {
|
||||||
|
if (typeof svgInfo.wordmark !== 'string') {
|
||||||
|
downloadSvg(svgInfo.wordmark?.light);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DownloadIcon class="mr-2" size={iconSize} />
|
||||||
|
Wordmark light variant
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
title="Download Wordmark dark variant"
|
||||||
|
class={buttonStyles}
|
||||||
|
on:click={() => {
|
||||||
|
if (typeof svgInfo.wordmark !== 'string') {
|
||||||
|
downloadSvg(svgInfo.wordmark?.dark);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DownloadIcon class="mr-2" size={iconSize} />
|
||||||
|
Wordmark dark variant
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
{/if}
|
@ -1,35 +1,23 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { iSVG } from '../types/svg';
|
import type { iSVG } from '../types/svg';
|
||||||
|
|
||||||
import download from 'downloadjs';
|
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import jszip from 'jszip';
|
|
||||||
|
|
||||||
// Utils:
|
// Utils:
|
||||||
import { cn } from '@/utils/cn';
|
import { cn } from '@/utils/cn';
|
||||||
import { MIMETYPE, getSvgContent } from '@/utils/getSvgContent';
|
import { MIMETYPE, getSvgContent } from '@/utils/getSvgContent';
|
||||||
import { flyAndScale } from '@/utils/flyAndScale';
|
|
||||||
|
|
||||||
// Icons:
|
// Icons:
|
||||||
import {
|
import { CopyIcon, LinkIcon, ChevronsRight, Baseline } from 'lucide-svelte';
|
||||||
CopyIcon,
|
|
||||||
DownloadIcon,
|
|
||||||
LinkIcon,
|
|
||||||
PackageIcon,
|
|
||||||
PaintBucket,
|
|
||||||
ChevronsRight,
|
|
||||||
Baseline
|
|
||||||
} from 'lucide-svelte';
|
|
||||||
|
|
||||||
// Main Card:
|
// Components & styles:
|
||||||
import CardSpotlight from './cardSpotlight.svelte';
|
import CardSpotlight from './cardSpotlight.svelte';
|
||||||
import { DropdownMenu } from 'bits-ui';
|
import DownloadSvg from './downloadSvg.svelte';
|
||||||
|
import { badgeStyles } from '@/ui/styles';
|
||||||
|
|
||||||
// Figma
|
// Figma
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { copyToClipboard as figmaCopyToClipboard } from '@/figma/copy-to-clipboard';
|
import { copyToClipboard as figmaCopyToClipboard } from '@/figma/copy-to-clipboard';
|
||||||
import { insertSVG as figmaInsertSVG } from '@/figma/insert-svg';
|
import { insertSVG as figmaInsertSVG } from '@/figma/insert-svg';
|
||||||
import { badgeStyles } from '@/ui/styles';
|
|
||||||
|
|
||||||
// Props:
|
// Props:
|
||||||
export let svgInfo: iSVG;
|
export let svgInfo: iSVG;
|
||||||
@ -43,60 +31,6 @@
|
|||||||
// Wordmark SVG:
|
// Wordmark SVG:
|
||||||
let wordmarkSvg = false;
|
let wordmarkSvg = false;
|
||||||
|
|
||||||
// Download SVG:
|
|
||||||
const downloadSvg = (url?: string) => {
|
|
||||||
download(url || '');
|
|
||||||
|
|
||||||
const category = Array.isArray(svgInfo.category)
|
|
||||||
? svgInfo.category.sort().join(' - ')
|
|
||||||
: svgInfo.category;
|
|
||||||
|
|
||||||
toast.success(`Downloading...`, {
|
|
||||||
description: `${svgInfo.title} - ${category}`
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Download all variants:
|
|
||||||
const downloadAllVariants = async ({ route }: iSVG) => {
|
|
||||||
const zip = new jszip();
|
|
||||||
|
|
||||||
if (typeof route === 'string') {
|
|
||||||
downloadSvg(route);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lightSvg = await getSvgContent(route.light, false);
|
|
||||||
const darkSvg = await getSvgContent(route.dark, false);
|
|
||||||
|
|
||||||
zip.file(`${svgInfo.title}.svg`, lightSvg);
|
|
||||||
zip.file(`${svgInfo.title}.dark.svg`, darkSvg);
|
|
||||||
|
|
||||||
if (svgInfo.wordmark) {
|
|
||||||
if (typeof svgInfo.wordmark === 'string') {
|
|
||||||
downloadSvg(svgInfo.wordmark);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lightWordmarkSvg = await getSvgContent(svgInfo.wordmark.light, false);
|
|
||||||
const darkWordmarkSvg = await getSvgContent(svgInfo.wordmark.dark, false);
|
|
||||||
|
|
||||||
zip.file(`${svgInfo.title}.wordmark.svg`, lightWordmarkSvg);
|
|
||||||
zip.file(`${svgInfo.title}.wordmark.dark.svg`, darkWordmarkSvg);
|
|
||||||
}
|
|
||||||
|
|
||||||
zip.generateAsync({ type: 'blob' }).then((content) => {
|
|
||||||
download(content, `${svgInfo.title}.zip`, 'application/zip');
|
|
||||||
});
|
|
||||||
|
|
||||||
const category = Array.isArray(svgInfo.category)
|
|
||||||
? svgInfo.category.sort().join(' - ')
|
|
||||||
: svgInfo.category;
|
|
||||||
|
|
||||||
toast.success('Downloading light & dark variants...', {
|
|
||||||
description: `${svgInfo.title} - ${category}`
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Copy SVG to clipboard:
|
// Copy SVG to clipboard:
|
||||||
const copyToClipboard = async (url?: string) => {
|
const copyToClipboard = async (url?: string) => {
|
||||||
const data = {
|
const data = {
|
||||||
@ -154,7 +88,7 @@
|
|||||||
<!-- Image -->
|
<!-- Image -->
|
||||||
{#if wordmarkSvg == true}
|
{#if wordmarkSvg == true}
|
||||||
<img
|
<img
|
||||||
class="hidden dark:block mb-4 mt-2 max-h-10 h-auto"
|
class="hidden dark:block mb-4 mt-2 h-10"
|
||||||
src={typeof svgInfo.wordmark !== 'string'
|
src={typeof svgInfo.wordmark !== 'string'
|
||||||
? svgInfo.wordmark?.dark || ''
|
? svgInfo.wordmark?.dark || ''
|
||||||
: svgInfo.wordmark || ''}
|
: svgInfo.wordmark || ''}
|
||||||
@ -163,7 +97,7 @@
|
|||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
class="block dark:hidden mb-4 mt-2 max-h-10 h-auto"
|
class="block dark:hidden mb-4 mt-2 h-10"
|
||||||
src={typeof svgInfo.wordmark !== 'string'
|
src={typeof svgInfo.wordmark !== 'string'
|
||||||
? svgInfo.wordmark?.light || ''
|
? svgInfo.wordmark?.light || ''
|
||||||
: svgInfo.wordmark || ''}
|
: svgInfo.wordmark || ''}
|
||||||
@ -173,14 +107,14 @@
|
|||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<img
|
<img
|
||||||
class={cn('hidden dark:block mb-4 mt-2 max-h-10')}
|
class={cn('hidden dark:block mb-4 mt-2 h-10')}
|
||||||
src={typeof svgInfo.route !== 'string' ? svgInfo.route.dark : svgInfo.route}
|
src={typeof svgInfo.route !== 'string' ? svgInfo.route.dark : svgInfo.route}
|
||||||
alt={svgInfo.title}
|
alt={svgInfo.title}
|
||||||
title={svgInfo.title}
|
title={svgInfo.title}
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
class={cn('block dark:hidden mb-4 mt-2 max-h-10')}
|
class={cn('block dark:hidden mb-4 mt-2 h-10')}
|
||||||
src={typeof svgInfo.route !== 'string' ? svgInfo.route.light : svgInfo.route}
|
src={typeof svgInfo.route !== 'string' ? svgInfo.route.light : svgInfo.route}
|
||||||
alt={svgInfo.title}
|
alt={svgInfo.title}
|
||||||
title={svgInfo.title}
|
title={svgInfo.title}
|
||||||
@ -297,73 +231,14 @@
|
|||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if typeof svgInfo.route !== 'string'}
|
<DownloadSvg
|
||||||
<DropdownMenu.Root>
|
{svgInfo}
|
||||||
<DropdownMenu.Trigger
|
isDarkTheme={() => {
|
||||||
title="Download SVG"
|
const dark = document.documentElement.classList.contains('dark');
|
||||||
class="flex items-center space-x-2 rounded-md p-2 duration-100 hover:bg-neutral-200 dark:hover:bg-neutral-700/40"
|
return dark;
|
||||||
>
|
}}
|
||||||
<DownloadIcon size={iconSize} strokeWidth={iconStroke} />
|
/>
|
||||||
</DropdownMenu.Trigger>
|
|
||||||
<DropdownMenu.Content
|
|
||||||
class="w-full shadow-md max-w-[229px] rounded-md border border-neutral-100 dark:border-neutral-800 bg-white dark:bg-neutral-900 px-1 py-1.5 shadow-popover"
|
|
||||||
transition={flyAndScale}
|
|
||||||
sideOffset={3}
|
|
||||||
>
|
|
||||||
<DropdownMenu.Item
|
|
||||||
title="Download Light & Dark variants"
|
|
||||||
class="flex h-10 select-none items-center rounded-md py-3 pl-3 pr-1.5 text-sm font-medium cursor-pointer hover:bg-neutral-100 dark:hover:bg-neutral-700/40"
|
|
||||||
on:click={() => {
|
|
||||||
downloadAllVariants(svgInfo);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PackageIcon class="mr-2" size={18} />
|
|
||||||
<p>Light & dark variants</p>
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
<DropdownMenu.Item
|
|
||||||
title="Download only {document.documentElement.classList.contains('dark')
|
|
||||||
? 'dark'
|
|
||||||
: 'light'} variant"
|
|
||||||
class="flex h-10 select-none items-center rounded-md py-3 pl-3 pr-1.5 text-sm font-medium cursor-pointer hover:bg-neutral-100 dark:hover:bg-neutral-700/40"
|
|
||||||
on:click={() => {
|
|
||||||
const svgHasTheme = typeof svgInfo.route !== 'string';
|
|
||||||
|
|
||||||
if (!svgHasTheme) {
|
|
||||||
downloadSvg(
|
|
||||||
typeof svgInfo.route === 'string'
|
|
||||||
? svgInfo.route
|
|
||||||
: "Something went wrong. Couldn't copy the SVG."
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dark = document.documentElement.classList.contains('dark');
|
|
||||||
|
|
||||||
downloadSvg(
|
|
||||||
typeof svgInfo.route !== 'string'
|
|
||||||
? dark
|
|
||||||
? svgInfo.route.dark
|
|
||||||
: svgInfo.route.light
|
|
||||||
: svgInfo.route
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PaintBucket class="mr-2" size={18} />
|
|
||||||
Only {document.documentElement.classList.contains('dark') ? 'dark' : 'light'} variant
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
</DropdownMenu.Content>
|
|
||||||
</DropdownMenu.Root>
|
|
||||||
{:else}
|
|
||||||
<button
|
|
||||||
title="Download SVG"
|
|
||||||
on:click={() => {
|
|
||||||
if (typeof svgInfo.route === 'string') downloadSvg(svgInfo.route);
|
|
||||||
}}
|
|
||||||
class="flex items-center space-x-2 rounded-md p-2 duration-100 hover:bg-neutral-200 dark:hover:bg-neutral-700/40"
|
|
||||||
>
|
|
||||||
<DownloadIcon size={iconSize} strokeWidth={iconStroke} />
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
<a
|
<a
|
||||||
href={svgInfo.url}
|
href={svgInfo.url}
|
||||||
title="Website"
|
title="Website"
|
||||||
|
Loading…
Reference in New Issue
Block a user