Separate download component + create new dialog

This commit is contained in:
pheralb 2024-01-25 13:41:26 +00:00
parent 5c711e4838
commit d1c140f614
2 changed files with 245 additions and 140 deletions

View 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}

View File

@ -1,35 +1,23 @@
<script lang="ts">
import type { iSVG } from '../types/svg';
import download from 'downloadjs';
import { toast } from 'svelte-sonner';
import jszip from 'jszip';
// Utils:
import { cn } from '@/utils/cn';
import { MIMETYPE, getSvgContent } from '@/utils/getSvgContent';
import { flyAndScale } from '@/utils/flyAndScale';
// Icons:
import {
CopyIcon,
DownloadIcon,
LinkIcon,
PackageIcon,
PaintBucket,
ChevronsRight,
Baseline
} from 'lucide-svelte';
import { CopyIcon, LinkIcon, ChevronsRight, Baseline } from 'lucide-svelte';
// Main Card:
// Components & styles:
import CardSpotlight from './cardSpotlight.svelte';
import { DropdownMenu } from 'bits-ui';
import DownloadSvg from './downloadSvg.svelte';
import { badgeStyles } from '@/ui/styles';
// Figma
import { onMount } from 'svelte';
import { copyToClipboard as figmaCopyToClipboard } from '@/figma/copy-to-clipboard';
import { insertSVG as figmaInsertSVG } from '@/figma/insert-svg';
import { badgeStyles } from '@/ui/styles';
// Props:
export let svgInfo: iSVG;
@ -43,60 +31,6 @@
// Wordmark SVG:
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:
const copyToClipboard = async (url?: string) => {
const data = {
@ -154,7 +88,7 @@
<!-- Image -->
{#if wordmarkSvg == true}
<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'
? svgInfo.wordmark?.dark || ''
: svgInfo.wordmark || ''}
@ -163,7 +97,7 @@
loading="lazy"
/>
<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'
? svgInfo.wordmark?.light || ''
: svgInfo.wordmark || ''}
@ -173,14 +107,14 @@
/>
{:else}
<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}
alt={svgInfo.title}
title={svgInfo.title}
loading="lazy"
/>
<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}
alt={svgInfo.title}
title={svgInfo.title}
@ -297,73 +231,14 @@
</button>
{/if}
{#if typeof svgInfo.route !== 'string'}
<DropdownMenu.Root>
<DropdownMenu.Trigger
title="Download SVG"
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} />
</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';
<DownloadSvg
{svgInfo}
isDarkTheme={() => {
const dark = document.documentElement.classList.contains('dark');
return dark;
}}
/>
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
href={svgInfo.url}
title="Website"