SPB Git

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%
2.9 KB · 81 lines astro
Raw Blame History
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