feat: enhance SVG handling by adding source fetching and copying functionality

This commit is contained in:
sHa
2025-05-14 21:34:53 +03:00
parent 554861c940
commit d841f27454
4 changed files with 158 additions and 20 deletions

View File

@@ -700,3 +700,8 @@ footer {
padding: 0.3em 0.8em; padding: 0.3em 0.8em;
text-align: center; text-align: center;
} }
textarea {
background-color: var(--background-color);
color: var(--color-text);
}

View File

@@ -44,7 +44,14 @@ $: getLogoThemeColor = logo => getDefaultLogoColor(logo.colors, theme);
} }
</script> </script>
<Preview show={showModal} logo={selectedLogo} theme={theme} on:close={closeModal} /> <Preview
show={showModal}
logo={selectedLogo}
theme={theme}
{onCopy}
{onDownload}
on:close={closeModal}
/>
<div class="logo-grid"> <div class="logo-grid">
{#each logos as logo} {#each logos as logo}

View File

@@ -1,15 +1,30 @@
<script> <script>
import { onMount, onDestroy, createEventDispatcher } from 'svelte'; import { onMount, onDestroy, createEventDispatcher } from 'svelte';
import InlineSvg from './InlineSvg.svelte'; import InlineSvg from './InlineSvg.svelte';
import Actions from './Actions.svelte';
import { getDefaultLogoColor, getThemeColor } from '../utils/colorTheme.js'; import { getDefaultLogoColor, getThemeColor } from '../utils/colorTheme.js';
import { generateColorSetCircle } from "../utils/colorCircles.js"; import { generateColorSetCircle } from "../utils/colorCircles.js";
import { fetchSvgSource } from "../utils/svgSource.js";
export let show = false; export let show = false;
export let logo = null; export let logo = null;
export let theme; export let theme;
export let openLogoByAnchor = () => {}; export let openLogoByAnchor = () => {};
export let onCopy = () => {};
export let onDownload = (path, name) => {
const a = document.createElement('a');
a.href = path;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
const dispatch = createEventDispatcher(); // For SVG source code display
let svgSource = '';
let isFetchingSvgSource = false;
const dispatch = createEventDispatcher(); // We no longer need the notification state and handlers since Actions component handles this
function close() { function close() {
show = false; show = false;
@@ -65,6 +80,20 @@
// Watch for show/close to update scroll lock // Watch for show/close to update scroll lock
$: if (show && logo) { $: if (show && logo) {
document.body.style.overflow = 'hidden'; document.body.style.overflow = 'hidden';
// Fetch SVG source when logo is displayed and is an SVG
if (logo.format === 'SVG' && !svgSource) {
isFetchingSvgSource = true;
fetchSvgSource(logo.path)
.then(source => {
svgSource = source;
isFetchingSvgSource = false;
})
.catch(err => {
console.error('Error fetching SVG source:', err);
isFetchingSvgSource = false;
});
}
} else { } else {
document.body.style.overflow = ''; document.body.style.overflow = '';
} }
@@ -153,6 +182,7 @@
{/if} {/if}
</div> </div>
</div> </div>
<div class="right-column">
<div class="logo-details fullscreen-details"> <div class="logo-details fullscreen-details">
{#if isSvgLogo(logo) && logo.colors} {#if isSvgLogo(logo) && logo.colors}
<div class="color-switcher-preview"> <div class="color-switcher-preview">
@@ -218,12 +248,51 @@
</div> </div>
{/if} {/if}
</div> </div>
<div class="preview-actions-container">
<div class="actions-wrapper">
<Actions
logo={logo}
onDownload={onDownload}
/>
</div>
</div>
{#if isSvgLogo(logo) && svgSource}
<div class="source-code-container">
<h3>SVG Source Code</h3>
<div class="source-code-wrapper">
<textarea class="source-code" readonly>{svgSource}</textarea>
</div>
</div>
{/if}
</div>
</div> </div>
</div> </div>
{/if} {/if}
</div> </div>
<style> <style>
.preview-actions-container {
width: 100%;
background: var(--color-card);
color: var(--color-text);
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 2px 16px 4px rgba(0,0,0,0.18);
z-index: 1;
position: relative;
}
.actions-wrapper {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
width: 100%;
}
.set-circle { .set-circle {
background: var(--color-border); background: var(--color-border);
color: var(--color-text); color: var(--color-text);
@@ -297,12 +366,12 @@
flex: 1 1 auto; flex: 1 1 auto;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: stretch; align-items: flex-start;
justify-content: center; justify-content: center;
width: 100vw; width: 100vw;
height: 100%; height: 100%;
background: var(--color-card); background: var(--color-card);
padding: 0; padding: 0 2rem 2rem 2rem;
gap: 2.5rem; gap: 2.5rem;
overflow: hidden; overflow: hidden;
} }
@@ -338,25 +407,21 @@
max-height: 100%; max-height: 100%;
} }
.logo-details.fullscreen-details { .logo-details.fullscreen-details {
flex: 1 1 300px; width: 100%;
min-width: 220px;
max-width: 300px;
background: var(--color-card); background: var(--color-card);
color: var(--color-text); color: var(--color-text);
border-radius: 12px; border-radius: 12px;
padding: 2rem 2rem 1.5rem 2rem; padding: 1.5rem;
margin: 0 2rem 0 0;
box-shadow: 0 2px 16px 4px rgba(0,0,0,0.18); box-shadow: 0 2px 16px 4px rgba(0,0,0,0.18);
overflow-y: auto; overflow-y: auto;
align-self: center;
z-index: 1; z-index: 1;
max-height: 100%;
} }
.logo-tags { .logo-tags {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.5rem; gap: 0.5rem;
} }
.set-circle { .set-circle {
background: var(--color-border); background: var(--color-border);
color: var(--color-text); color: var(--color-text);
@@ -373,6 +438,14 @@
color: #eee; color: #eee;
} }
.preview-actions-container {
margin-top: 2rem;
border-top: 1px solid var(--color-border);
padding-top: 1rem;
}
/* These styles are no longer needed as we're using the Actions component */
@media (max-width: 900px) { @media (max-width: 900px) {
.modal-body.fullscreen-body { .modal-body.fullscreen-body {
flex-direction: column; flex-direction: column;
@@ -390,5 +463,60 @@
.modal-header { .modal-header {
padding: 1.2rem 0.7rem 0.7rem 0.7rem; padding: 1.2rem 0.7rem 0.7rem 0.7rem;
} }
.right-column {
max-width: 100%;
width: 100%;
}
.preview-actions-container {
margin: 0 auto;
width: 100%;
}
}
.right-column {
display: flex;
flex-direction: column;
gap: 2rem;
width: 300px;
}
.source-code-container {
width: 100%;
background: var(--color-card);
color: var(--color-text);
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 2px 16px 4px rgba(0,0,0,0.18);
z-index: 1;
position: relative;
margin-top: 2rem;
}
.source-code-container h3 {
font-size: 1rem;
margin: 0 0 1rem 0;
color: var(--color-text);
}
.source-code-wrapper {
position: relative;
width: 100%;
border-radius: 6px;
overflow: hidden;
border: 1px solid var(--color-border, #ddd);
}
.source-code {
width: 100%;
min-height: 180px;
max-height: 300px;
padding: 1rem;
font-family: monospace;
font-size: 0.6rem;
line-height: 1.4;
border: none;
border-radius: 6px;
resize: none;
overflow-y: auto;
} }
</style> </style>

View File

@@ -21,15 +21,13 @@ export async function fetchSvgSource(path) {
* Copy SVG source to clipboard * Copy SVG source to clipboard
* *
* @param {string} path - Path to the SVG file * @param {string} path - Path to the SVG file
* @returns {Promise<boolean>} - True if successful, false otherwise * @returns {Promise<string>} - The SVG source code
*/ */
export async function copySvgSource(path) { export async function copySvgSource(path) {
try { try {
const svgSource = await fetchSvgSource(path); return await fetchSvgSource(path);
await navigator.clipboard.writeText(svgSource);
return true;
} catch (error) { } catch (error) {
console.error('Error copying SVG source:', error); console.error('Error fetching SVG source:', error);
return false; throw error;
} }
} }