spb/svgarden Public
SVGarden — searchable bank of 74 self-contained SVG+CSS animation snippets (svgarden.dev)
HTML 79.2%
Astro 10.3%
JavaScript 6%
CSS 3.7%
Shell 0.8%
1---2/**3 * ============================================================4 * SVGarden — https://www.svgarden.dev5 * Author : Simon-Pierre Boucher6 * Contact: contact@spboucher.ai7 * File : src/components/CodeBlock.astro8 * Desc : Shiki-highlighted code panel with Copy + Download buttons9 * ============================================================10 */11import { Code } from 'astro:components';1213interface Props {14 code: string; // displayed AND copied — includes the attribution header15 label: string; // e.g. snippets/loaders/spinner-dash.html16 downloadName?: string; // e.g. spinner-dash.html — omits the Download button if absent17}1819const { code, label, downloadName } = Astro.props;20---2122<div class="sg-panel sg-codeblock" data-sg-codeblock>23 <div class="sg-panel-bar">24 <strong>Code</strong>25 <span>{label}</span>26 <span class="sg-spacer"></span>27 {downloadName && (28 <button class="sg-btn sg-btn-sm" type="button" data-sg-download={downloadName}>29 Download .html30 </button>31 )}32 <button class="sg-btn sg-btn-sm sg-btn-primary" type="button" data-sg-copy>33 Copy code34 </button>35 </div>36 <Code code={code} lang="html" themes={{ light: 'github-light-high-contrast', dark: 'github-dark' }} />37 <script type="text/plain" data-sg-copy-source>{code}</script>38</div>3940<style is:global>41 /* Dual-theme Shiki: swap to the dark palette when the site is dark */42 .astro-code { background: #ffffff !important; }43 [data-theme='dark'] .astro-code,44 [data-theme='dark'] .astro-code span {45 color: var(--shiki-dark) !important;46 background-color: transparent !important;47 font-style: var(--shiki-dark-font-style) !important;48 font-weight: var(--shiki-dark-font-weight) !important;49 }50 [data-theme='dark'] .astro-code { background-color: #16161a !important; }51</style>5253<script>54 // Copy + download. The current text lives in the data-sg-copy-source island;55 // the Customizer script rewrites it when values change.56 for (const block of document.querySelectorAll('[data-sg-codeblock]')) {57 const source = () => block.querySelector('[data-sg-copy-source]')?.textContent ?? '';5859 const copyBtn = block.querySelector<HTMLButtonElement>('[data-sg-copy]');60 copyBtn?.addEventListener('click', async () => {61 await navigator.clipboard.writeText(source());62 copyBtn.classList.add('sg-btn-done');63 copyBtn.textContent = 'Copied ✓';64 setTimeout(() => {65 copyBtn.classList.remove('sg-btn-done');66 copyBtn.textContent = 'Copy code';67 }, 1600);68 });6970 const dlBtn = block.querySelector<HTMLButtonElement>('[data-sg-download]');71 dlBtn?.addEventListener('click', () => {72 const blob = new Blob([source()], { type: 'text/html' });73 const a = document.createElement('a');74 a.href = URL.createObjectURL(blob);75 a.download = dlBtn.dataset.sgDownload!;76 a.click();77 URL.revokeObjectURL(a.href);78 });79 }80</script>81