Refactor logo management components: remove LogoModal, add Actions and Grid components, enhance List and Preview components with improved theme handling and SVG support.

This commit is contained in:
sHa
2025-05-01 11:57:23 +03:00
parent 0313d091fc
commit c62d453946
6 changed files with 40 additions and 38 deletions

View File

@@ -1,7 +1,7 @@
<script> <script>
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import LogoGrid from './components/LogoGrid.svelte'; import Grid from './components/Grid.svelte';
import LogoList from './components/LogoList.svelte'; import List from './components/List.svelte';
let viewMode = 'grid'; // 'grid' or 'list' let viewMode = 'grid'; // 'grid' or 'list'
let searchQuery = ''; let searchQuery = '';
@@ -261,14 +261,14 @@
<div class="logos-container"> <div class="logos-container">
{#if viewMode === 'grid'} {#if viewMode === 'grid'}
<LogoGrid <Grid
logos={filteredLogos} logos={filteredLogos}
onCopy={copyUrl} onCopy={copyUrl}
onDownload={downloadLogo} onDownload={downloadLogo}
theme={effectiveTheme} theme={effectiveTheme}
/> />
{:else} {:else}
<LogoList <List
logos={filteredLogos} logos={filteredLogos}
onCopy={copyUrl} onCopy={copyUrl}
onDownload={downloadLogo} onDownload={downloadLogo}

View File

@@ -1,6 +1,6 @@
<script> <script>
import LogoModal from './LogoModal.svelte'; import Preview from './Preview.svelte';
import LogoActions from './LogoActions.svelte'; import Actions from './Actions.svelte';
import InlineSvg from './InlineSvg.svelte'; import InlineSvg from './InlineSvg.svelte';
import { onMount, onDestroy } from 'svelte'; import { onMount, onDestroy } from 'svelte';
import { getDefaultLogoColor, getThemeColor } from '../utils/colorTheme.js'; import { getDefaultLogoColor, getThemeColor } from '../utils/colorTheme.js';
@@ -53,7 +53,7 @@ $: getLogoThemeColor = logo => getDefaultLogoColor(logo.colors, theme);
} }
</script> </script>
<LogoModal show={showModal} logo={selectedLogo} theme={theme} on:close={closeModal} /> <Preview show={showModal} logo={selectedLogo} theme={theme} on:close={closeModal} />
<div class="logo-grid"> <div class="logo-grid">
{#each logos as logo} {#each logos as logo}
@@ -115,7 +115,7 @@ $: getLogoThemeColor = logo => getDefaultLogoColor(logo.colors, theme);
{/if} {/if}
</div> </div>
<div class="logo-actions"> <div class="logo-actions">
<LogoActions {logo} {onCopy} {onDownload} /> <Actions {logo} {onCopy} {onDownload} />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,11 +1,11 @@
<script> <script>
import { onMount } from 'svelte'; import { onMount } from "svelte";
export let path; export let path;
export let color; export let color;
export let colorConfig = { target: 'path', attribute: 'fill' }; export let colorConfig = { target: "path", attribute: "fill" };
export let alt; export const alt = "";
let svgHtml = ''; let svgHtml = "";
async function fetchAndColorSvg() { async function fetchAndColorSvg() {
const res = await fetch(path); const res = await fetch(path);
@@ -17,10 +17,10 @@
} }
// Parse and update color only if user selected // Parse and update color only if user selected
const parser = new DOMParser(); const parser = new DOMParser();
const doc = parser.parseFromString(text, 'image/svg+xml'); const doc = parser.parseFromString(text, "image/svg+xml");
// 1. Parse <style> rules and apply as inline attributes before removing <style> // 1. Parse <style> rules and apply as inline attributes before removing <style>
const styleEls = Array.from(doc.querySelectorAll('style')); const styleEls = Array.from(doc.querySelectorAll("style"));
styleEls.forEach(styleEl => { styleEls.forEach((styleEl) => {
const css = styleEl.textContent; const css = styleEl.textContent;
// Only handle simple .class { ... } rules // Only handle simple .class { ... } rules
const regex = /\.([\w-]+)\s*{([^}]*)}/g; const regex = /\.([\w-]+)\s*{([^}]*)}/g;
@@ -29,19 +29,22 @@
const className = match[1]; const className = match[1];
const rules = match[2]; const rules = match[2];
// Find all elements with this class // Find all elements with this class
doc.querySelectorAll('.' + className).forEach(el => { doc.querySelectorAll("." + className).forEach((el) => {
rules.split(';').forEach(rule => { rules.split(";").forEach((rule) => {
const [prop, value] = rule.split(':').map(s => s && s.trim()); const [prop, value] = rule.split(":").map((s) => s && s.trim());
if (prop && value) { if (prop && value) {
// Apply all style properties, not just fill/stroke // Apply all style properties, not just fill/stroke
el.setAttribute(prop.replace(/-([a-z])/g, g => g[1].toUpperCase()), value); el.setAttribute(
prop.replace(/-([a-z])/g, (g) => g[1].toUpperCase()),
value,
);
} }
}); });
}); });
} }
}); });
// Remove all <style> elements // Remove all <style> elements
styleEls.forEach(styleEl => styleEl.remove()); styleEls.forEach((styleEl) => styleEl.remove());
let targets; let targets;
if (colorConfig.selector) { if (colorConfig.selector) {
targets = doc.querySelectorAll(colorConfig.selector); targets = doc.querySelectorAll(colorConfig.selector);
@@ -50,21 +53,21 @@
} else { } else {
targets = []; targets = [];
} }
targets.forEach(el => { targets.forEach((el) => {
if (colorConfig.attribute) { if (colorConfig.attribute) {
// Legacy: force a single attribute // Legacy: force a single attribute
el.setAttribute(colorConfig.attribute, color); el.setAttribute(colorConfig.attribute, color);
} else { } else {
// Always override fill and stroke unless they are 'none' // Always override fill and stroke unless they are 'none'
if (el.hasAttribute('fill') && el.getAttribute('fill') !== 'none') { if (el.hasAttribute("fill") && el.getAttribute("fill") !== "none") {
el.setAttribute('fill', color); el.setAttribute("fill", color);
} }
if (el.hasAttribute('stroke') && el.getAttribute('stroke') !== 'none') { if (el.hasAttribute("stroke") && el.getAttribute("stroke") !== "none") {
el.setAttribute('stroke', color); el.setAttribute("stroke", color);
} }
if (!el.hasAttribute('fill') && !el.hasAttribute('stroke')) { if (!el.hasAttribute("fill") && !el.hasAttribute("stroke")) {
// If neither, prefer fill // If neither, prefer fill
el.setAttribute('fill', color); el.setAttribute("fill", color);
} }
} }
}); });

View File

@@ -1,6 +1,6 @@
<script> <script>
import LogoModal from './LogoModal.svelte'; import Preview from './Preview.svelte';
import LogoActions from './LogoActions.svelte'; import Actions from './Actions.svelte';
import InlineSvg from './InlineSvg.svelte'; import InlineSvg from './InlineSvg.svelte';
import { getThemeColor, getDefaultLogoColor } from '../utils/colorTheme.js'; import { getThemeColor, getDefaultLogoColor } from '../utils/colorTheme.js';
import { onMount, onDestroy } from 'svelte'; import { onMount, onDestroy } from 'svelte';
@@ -64,7 +64,7 @@
} }
</script> </script>
<LogoModal show={showModal} logo={selectedLogo} on:close={closeModal} /> <Preview show={showModal} logo={selectedLogo} on:close={closeModal} />
<div class="logo-list"> <div class="logo-list">
{#each logos as logo} {#each logos as logo}
@@ -77,13 +77,12 @@
on:keydown={(e) => (e.key === 'Enter' || e.key === ' ') && openPreview(logo)} on:keydown={(e) => (e.key === 'Enter' || e.key === ' ') && openPreview(logo)}
style="cursor:pointer;" style="cursor:pointer;"
> >
{#if isSvgLogo(logo)} {#if isSvgLogo(logo)} <InlineSvg
<InlineSvg path={logo.path}
path={logo.path} color={logo.colors ? (logo._activeColor || getLogoThemeColor(logo)) : undefined}
color={logo.colors ? (logo._activeColor || getLogoThemeColor(logo)) : undefined} colorConfig={logo.colors ? logo.colorConfig : undefined}
colorConfig={logo.colors ? logo.colorConfig : undefined} alt={logo.name}
alt={logo.name} />
/>
{:else} {:else}
<img src={logo.path} alt={logo.name} /> <img src={logo.path} alt={logo.name} />
{/if} {/if}
@@ -125,7 +124,7 @@
{/if} {/if}
</div> </div>
<div class="logo-actions"> <div class="logo-actions">
<LogoActions {logo} {onCopy} {onDownload} /> <Actions {logo} {onCopy} {onDownload} />
</div> </div>
</div> </div>
{:else} {:else}