You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/web/routes/uix/components/qr-code/+page.svelte

270 lines
11 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<script lang="ts">
import {
QrCode,
type QrCodeSize,
type QrCodeCellShape,
type ErrorCorrection
} from '$uix/eidos/components/qr-code';
import { compileMorfo } from '$uix/morfo';
import { qrCodeMorfo } from '@/uix/morfo/components/qr-code';
type Tab = 'live' | 'examples' | 'api' | 'morfo' | 'recipe' | 'a11y';
let tab = $state<Tab>('live');
let value = $state('https://uix.dev/qr-code');
let size = $state<QrCodeSize>('lg');
let cellShape = $state<QrCodeCellShape>('square');
let errorCorrection = $state<ErrorCorrection>('M');
let margin = $state(4);
let color = $state<string | undefined>(undefined);
let withLogo = $state(false);
const sizes: QrCodeSize[] = ['xs', 'sm', 'md', 'lg', 'xl'];
const shapes: QrCodeCellShape[] = ['square', 'rounded', 'dots'];
const eccs: ErrorCorrection[] = ['L', 'M', 'Q', 'H'];
const colors: { label: string; value: string | undefined }[] = [
{ label: 'default', value: undefined },
{ label: 'violet', value: '#7c3aed' },
{ label: 'teal', value: '#0d9488' },
{ label: 'rose', value: '#e11d48' }
];
const compiled = compileMorfo(qrCodeMorfo);
const partsList = $derived([...compiled.parts.byKebab.values()]);
const snippet = $derived(
[
"<script lang='ts'>",
" import { QrCode } from '$uix/eidos/components/qr-code';",
'</' + 'script>',
'',
'<QrCode',
` value="${value}"`,
size !== 'md' && ` size="${size}"`,
cellShape !== 'square' && ` cellShape="${cellShape}"`,
errorCorrection !== 'M' && ` errorCorrection="${errorCorrection}"`,
margin !== 4 && ` margin={${margin}}`,
color && ` color="${color}"`,
'>',
withLogo && ' {#snippet logo()}<img src={brand} alt="" />{/snippet}',
' <QrCode.DownloadTrigger format="png">Download</QrCode.DownloadTrigger>',
'</QrCode>'
]
.filter(Boolean)
.join('\n')
);
</script>
<svelte:head>
<meta name="perm-axes" content="" />
</svelte:head>
{#snippet brandLogo()}
<svg viewBox="0 0 24 24" fill={color ?? '#18181b'} aria-hidden="true">
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
</svg>
{/snippet}
<div data-uix-canvas-inner>
<header>
<div data-uix-eyebrow>Display · QR code</div>
<h1 data-uix-page-title>QR code</h1>
<p data-uix-page-lede>
Themeable QR rendered from an <strong>own zero-dependency encoder</strong> (`$libs/qr` —
ISO/IEC 18004, versions 1–40, all ECC levels). Cell shapes (square / rounded / dots) keep
the function patterns square so any scanner locks on; an optional logo clears the centre and
auto-boosts ECC to H; export to PNG / SVG. No npm encoder dependency.
</p>
<div data-uix-page-meta>
<span data-uix-meta-pill><span data-uix-meta-key>encoder</span>own · 0-dep</span>
<span data-uix-meta-pill><span data-uix-meta-key>parts</span>{compiled.parts.order.length}</span>
<span data-uix-meta-pill><span data-uix-meta-key>versions</span>1–40</span>
<span data-uix-meta-pill><span data-uix-meta-key>ecc</span>L · M · Q · H</span>
</div>
</header>
<div data-uix-stage>
<div data-uix-stage-area style="display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
<QrCode {value} {size} {cellShape} {errorCorrection} {margin} {color} logo={withLogo ? brandLogo : undefined}>
<span style="display: inline-flex; gap: 0.5rem; margin-inline-start: 0.5rem;">
<QrCode.DownloadTrigger format="png" size="sm">PNG</QrCode.DownloadTrigger>
<QrCode.DownloadTrigger format="svg" size="sm" variant="solid" color="neutral">SVG</QrCode.DownloadTrigger>
</span>
</QrCode>
</div>
<div data-uix-stage-trace>
<span data-uix-stage-trace-key>encodes</span>
<span><span data-uix-stage-trace-event>{value || '∅'}</span></span>
<span style="margin-inline-start: auto;"><span data-uix-stage-trace-key>ecc</span> {withLogo ? 'H (logo)' : errorCorrection}</span>
</div>
</div>
<div data-uix-tabs role="tablist">
<button data-uix-tab data-active={tab === 'live'} onclick={() => (tab = 'live')}>Live</button>
<button data-uix-tab data-active={tab === 'examples'} onclick={() => (tab = 'examples')}>Examples</button>
<button data-uix-tab data-active={tab === 'api'} onclick={() => (tab = 'api')}>API</button>
<button data-uix-tab data-active={tab === 'morfo'} onclick={() => (tab = 'morfo')}>
<span data-uix-layer-badge="morfo">morfo</span>
<span data-uix-tab-count>{partsList.length}p</span>
</button>
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}>Recipe</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
{#if tab === 'live'}
<section data-uix-section>
<h2 data-uix-section-title>Controls</h2>
<p data-uix-section-desc>Pure <span data-uix-layer-badge="eidos">eidos</span> display — no soma. <code>value</code> → matrix → SVG.</p>
<div data-uix-controls>
<label data-uix-control>
<span data-uix-control-label>value</span>
<input type="text" bind:value />
</label>
<label data-uix-control>
<span data-uix-control-label>cellShape</span>
<span data-uix-chips role="radiogroup">
{#each shapes as s}
<button data-uix-chip data-active={cellShape === s} onclick={() => (cellShape = s)}>{s}</button>
{/each}
</span>
</label>
<label data-uix-control>
<span data-uix-control-label>size</span>
<span data-uix-chips role="radiogroup">
{#each sizes as s}
<button data-uix-chip data-active={size === s} onclick={() => (size = s)}>{s}</button>
{/each}
</span>
</label>
<label data-uix-control>
<span data-uix-control-label>errorCorrection</span>
<span data-uix-chips role="radiogroup">
{#each eccs as e}
<button data-uix-chip data-active={errorCorrection === e} onclick={() => (errorCorrection = e)}>{e}</button>
{/each}
</span>
</label>
<label data-uix-control>
<span data-uix-control-label>color</span>
<span data-uix-chips role="radiogroup">
{#each colors as c}
<button data-uix-chip data-active={color === c.value} onclick={() => (color = c.value)}>{c.label}</button>
{/each}
</span>
</label>
<label data-uix-control>
<span data-uix-control-label>margin (quiet zone)</span>
<span data-uix-chips role="radiogroup">
{#each [2, 4, 6] as m}
<button data-uix-chip data-active={margin === m} onclick={() => (margin = m)}>{m}</button>
{/each}
</span>
</label>
<label data-uix-control>
<span data-uix-control-label>logo (→ ECC H)</span>
<span data-uix-switch><input type="checkbox" bind:checked={withLogo} /><span data-uix-switch-label>{withLogo ? 'on' : 'off'}</span></span>
</label>
</div>
<div data-uix-code>
<div data-uix-code-head>
<span data-uix-layer-badge="eidos">eidos</span>
<span>display + DownloadTrigger</span>
<span data-uix-code-lang>svelte</span>
</div>
<pre><code>{snippet}</code></pre>
</div>
</section>
{/if}
{#if tab === 'examples'}
<section data-uix-section>
<h2 data-uix-section-title>Examples</h2>
<p data-uix-section-desc>Cell shapes, themed colour, logo — all stay scannable (function patterns square, ECC auto-boost).</p>
<div data-uix-stage-area style="display: flex; gap: 1.5rem; flex-wrap: wrap;">
<QrCode value="https://uix.dev" size="md" />
<QrCode value="https://uix.dev" size="md" cellShape="rounded" color="#7c3aed" />
<QrCode value="https://uix.dev" size="md" cellShape="dots" color="#0d9488" />
<QrCode value="https://uix.dev" size="md" cellShape="rounded" color="#e11d48" logo={brandLogo} />
</div>
</section>
{/if}
{#if tab === 'api'}
<section data-uix-section>
<h2 data-uix-section-title>API reference</h2>
<div data-uix-table-wrap>
<table data-uix-table>
<thead><tr><th>Prop</th><th>Type</th><th>Default</th><th>Description</th></tr></thead>
<tbody>
<tr><td class="name">value</td><td class="type">string</td><td class="default">''</td><td>Data to encode.</td></tr>
<tr><td class="name">size</td><td class="type">ResponsiveProp&lt;number | 'xs'…'xl'&gt;</td><td class="default">'md'</td><td>Box size (px or preset).</td></tr>
<tr><td class="name">cellShape</td><td class="type">'square' | 'rounded' | 'dots'</td><td class="default">'square'</td><td>Data-module shape; finders stay square.</td></tr>
<tr><td class="name">errorCorrection</td><td class="type">'L' | 'M' | 'Q' | 'H'</td><td class="default">'M'</td><td>Redundancy. Auto-boosted to 'H' with a logo.</td></tr>
<tr><td class="name">color / background</td><td class="type">string</td><td class="default">fixed tokens</td><td>Themeable (you own the contrast).</td></tr>
<tr><td class="name">margin</td><td class="type">number</td><td class="default">4</td><td>Quiet zone in modules.</td></tr>
<tr><td class="name">logo</td><td class="type">Snippet</td><td class="default empty">—</td><td>Centred overlay; clears cells + boosts ECC.</td></tr>
<tr><td class="name">alt</td><td class="type">string</td><td class="default">value</td><td>Accessible name.</td></tr>
</tbody>
</table>
</div>
</section>
{/if}
{#if tab === 'morfo'}
<section data-uix-section>
<h2 data-uix-section-title><span data-uix-layer-badge="morfo">morfo</span> · declarative contract</h2>
<p data-uix-section-desc>Source: <code>src/uix/morfo/components/qr-code.ts</code>. <code>scope:['eidos']</code> — a display primitive, no soma.</p>
<div data-uix-table-wrap>
<table data-uix-table>
<thead><tr><th>Part</th><th>Marker</th><th>Element</th><th>Role</th><th>Optional</th></tr></thead>
<tbody>
{#each partsList as part}
<tr>
<td class="name">{part.kebab}</td>
<td><code data-uix-part-marker>[{part.marker}]</code></td>
<td class="type">&lt;{part.defaultElement}&gt;</td>
<td class="type">{part.role ?? '—'}</td>
<td class="default">{part.optional ? 'yes' : 'no'}</td>
</tr>
{/each}
</tbody>
</table>
</div>
</section>
{/if}
{#if tab === 'recipe'}
<section data-uix-section>
<h2 data-uix-section-title>Eidos recipe</h2>
<p data-uix-section-desc>Tokens at <code>lib/recipes/base.ts</code>. Default fg/bg are <strong>fixed-tone</strong> (dark-on-light) for scannability — a QR can't invert in dark mode; theming goes through <code>color</code>/<code>background</code>.</p>
<div data-uix-table-wrap>
<table data-uix-table>
<thead><tr><th>Token</th><th>Role</th></tr></thead>
<tbody>
<tr><td class="name">--qr-code-fg / --qr-code-bg</td><td>module colour / quiet-zone (fixed-tone)</td></tr>
<tr><td class="name">--qr-code-size-{'{'}xs…xl{'}'}</td><td>size presets</td></tr>
<tr><td class="name">--qr-code-radius</td><td>frame radius</td></tr>
<tr><td class="name">--qr-code-overlay-bg / -padding / -radius</td><td>logo backing</td></tr>
</tbody>
</table>
</div>
</section>
{/if}
{#if tab === 'a11y'}
<section data-uix-section>
<h2 data-uix-section-title>Accessibility</h2>
<div data-uix-table-wrap>
<table data-uix-table>
<thead><tr><th>Part</th><th>Attribute</th><th>Value</th></tr></thead>
<tbody>
<tr><td class="name">frame (svg)</td><td>role / aria-label</td><td class="type">"img" / alt ?? value</td></tr>
<tr><td class="name">pattern / overlay</td><td>aria-hidden</td><td class="type">"true" (decorative)</td></tr>
<tr><td class="name">download-trigger</td><td>type / aria-label</td><td class="type">"button" / "Download QR code"</td></tr>
</tbody>
</table>
</div>
</section>
{/if}
</div>

Powered by TurnKey Linux.