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.
217 lines
9.6 KiB
217 lines
9.6 KiB
<script lang="ts">
|
|
import { Sparkline } from '$uix/eidos/components/chart';
|
|
import { compileMorfo } from '$uix/morfo';
|
|
import { chartMorfo } from '@/uix/morfo/components/chart';
|
|
import type { CurveName } from '$libs/plots';
|
|
import type { ColorRole } from '$uix/eidos/lib/types';
|
|
|
|
type Tab = 'live' | 'examples' | 'api' | 'morfo' | 'recipe' | 'a11y';
|
|
let tab = $state<Tab>('live');
|
|
|
|
const data = [4, 6, 5, 8, 7, 9, 8, 11, 10, 13, 12, 15, 14, 17].map((v, i) => ({ i, v }));
|
|
|
|
let mode = $state<'line' | 'bars'>('line');
|
|
let curve = $state<CurveName>('monotone');
|
|
let area = $state(true);
|
|
let color = $state<ColorRole>('primary');
|
|
let height = $state(40);
|
|
|
|
const modes = ['line', 'bars'] as const;
|
|
const curves: CurveName[] = ['monotone', 'linear', 'step'];
|
|
const colorRoles: ColorRole[] = ['primary', 'affirm', 'risk', 'secondary', 'threat'];
|
|
const heights = [32, 40, 64];
|
|
|
|
const compiled = compileMorfo(chartMorfo);
|
|
const partsList = $derived([...compiled.parts.byKebab.values()]);
|
|
|
|
const snippet = $derived(
|
|
[
|
|
"<script lang='ts'>",
|
|
" import { Sparkline } from '$uix/eidos/components/chart';",
|
|
'',
|
|
' // just { x, y } points',
|
|
' const data = [{ i: 0, v: 4 }, { i: 1, v: 6 }, { i: 2, v: 5 }];',
|
|
'</' + 'script>',
|
|
'',
|
|
'<Sparkline',
|
|
' {data}',
|
|
' x="i"',
|
|
' y="v"',
|
|
mode === 'bars' && ' as="bars"',
|
|
curve !== 'monotone' && ` curve="${curve}"`,
|
|
!area && ' area={false}',
|
|
color !== 'primary' && ` color="${color}"`,
|
|
height !== 40 && ` height={${height}}`,
|
|
'/>'
|
|
]
|
|
.filter(Boolean)
|
|
.join('\n')
|
|
);
|
|
</script>
|
|
|
|
<div data-uix-canvas-inner>
|
|
<header>
|
|
<div data-uix-eyebrow>Charts · Sparkline</div>
|
|
<h1 data-uix-page-title>Sparkline</h1>
|
|
<p data-uix-page-lede>
|
|
A compact, axis-less trend line for inline use in tables, cards and feeds — built on the same
|
|
zero-dependency <code>$libs/plots</code> engine. Optional area fill; themed by role.
|
|
</p>
|
|
<div data-uix-page-meta>
|
|
<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>render</span>svg · 0-dep</span>
|
|
<span data-uix-meta-pill><span data-uix-meta-key>chrome</span>none</span>
|
|
<span data-uix-meta-pill><span data-uix-meta-key>curve</span>{curve}</span>
|
|
</div>
|
|
</header>
|
|
|
|
<div data-uix-stage>
|
|
<div data-uix-stage-area style="max-inline-size: 240px;">
|
|
<Sparkline {data} x="i" y="v" as={mode} {curve} {area} {color} {height} />
|
|
</div>
|
|
<div data-uix-stage-trace>
|
|
<span data-uix-stage-trace-key>renders</span>
|
|
<span><span data-uix-stage-trace-event>{curve}</span>{area ? ' · area' : ' · line'} · {color}</span>
|
|
<span style="margin-inline-start: auto;"><span data-uix-stage-trace-key>height</span> {height}px</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 — every control changes the live chart above.</p>
|
|
<div data-uix-controls>
|
|
<label data-uix-control>
|
|
<span data-uix-control-label>as</span>
|
|
<span data-uix-chips role="radiogroup">
|
|
{#each modes as m}<button data-uix-chip data-active={mode === m} onclick={() => (mode = m)}>{m}</button>{/each}
|
|
</span>
|
|
</label>
|
|
<label data-uix-control>
|
|
<span data-uix-control-label>curve</span>
|
|
<span data-uix-chips role="radiogroup">
|
|
{#each curves as c}<button data-uix-chip data-active={curve === c} onclick={() => (curve = c)}>{c}</button>{/each}
|
|
</span>
|
|
</label>
|
|
<label data-uix-control>
|
|
<span data-uix-control-label>color</span>
|
|
<span data-uix-chips role="radiogroup">
|
|
{#each colorRoles as r}<button data-uix-chip data-active={color === r} onclick={() => (color = r)}>{r}</button>{/each}
|
|
</span>
|
|
</label>
|
|
<label data-uix-control>
|
|
<span data-uix-control-label>height</span>
|
|
<span data-uix-chips role="radiogroup">
|
|
{#each heights as h}<button data-uix-chip data-active={height === h} onclick={() => (height = h)}>{h}</button>{/each}
|
|
</span>
|
|
</label>
|
|
<label data-uix-control>
|
|
<span data-uix-control-label>area fill</span>
|
|
<span data-uix-switch><input type="checkbox" bind:checked={area} /><span data-uix-switch-label>{area ? 'on' : 'off'}</span></span>
|
|
</label>
|
|
</div>
|
|
<div data-uix-code>
|
|
<div data-uix-code-head>
|
|
<span data-uix-layer-badge="eidos">eidos</span>
|
|
<span>Sparkline preset</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>Inline trends, one per metric — the canonical sparkline use.</p>
|
|
<div data-uix-stage-area style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; max-inline-size: 640px;">
|
|
<div><div style="font-size: 0.75rem; color: var(--color-content-secondary); margin-bottom: 0.25rem;">Sessions</div><Sparkline {data} x="i" y="v" /></div>
|
|
<div><div style="font-size: 0.75rem; color: var(--color-content-secondary); margin-bottom: 0.25rem;">Revenue</div><Sparkline {data} x="i" y="v" color="affirm" /></div>
|
|
<div><div style="font-size: 0.75rem; color: var(--color-content-secondary); margin-bottom: 0.25rem;">Errors · bars</div><Sparkline {data} x="i" y="v" as="bars" color="threat" /></div>
|
|
</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">data</td><td class="type">{`readonly Record<string, unknown>[]`}</td><td class="default empty">—</td><td>Rows of data.</td></tr>
|
|
<tr><td class="name">x</td><td class="type">string</td><td class="default empty">—</td><td>x position key.</td></tr>
|
|
<tr><td class="name">y</td><td class="type">string</td><td class="default empty">—</td><td>Single series key.</td></tr>
|
|
<tr><td class="name">as</td><td class="type">'line' | 'bars'</td><td class="default">'line'</td><td>Render as a trend line or vertical bars.</td></tr>
|
|
<tr><td class="name">curve</td><td class="type">'monotone' | 'linear' | 'step'</td><td class="default">'monotone'</td><td>Point connection.</td></tr>
|
|
<tr><td class="name">area</td><td class="type">boolean</td><td class="default">true</td><td>Fill under the line.</td></tr>
|
|
<tr><td class="name">color</td><td class="type">ColorRole</td><td class="default empty">primary</td><td>Series colour by role.</td></tr>
|
|
<tr><td class="name">height</td><td class="type">number</td><td class="default">40</td><td>Compact height (width fills container).</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<p data-uix-section-desc>No axes, grid or legend — a sparkline is intentionally chrome-less.</p>
|
|
</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/chart.ts</code>. Sparkline uses only the provider + line (+ area) parts of the shared contract.</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"><{part.defaultElement}></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>No <code>--chart-*</code> tokens — the line/area colour comes from the role token.</p>
|
|
<div data-uix-table-wrap>
|
|
<table data-uix-table>
|
|
<thead><tr><th>Surface</th><th>Token</th></tr></thead>
|
|
<tbody>
|
|
<tr><td class="name">line / area</td><td class="type">var(--color-{'{'}role{'}'}-solid) + fill-opacity</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">provider (svg)</td><td>role / aria-label</td><td class="type">"img" / ariaLabel</td></tr>
|
|
</tbody>
|
|
</table>
|
|
<p data-uix-section-desc style="margin-top: 0.75rem;">Pass <code>ariaLabel</code> with the trend summary (e.g. "Sessions, up 12% this week"); decorative sparklines may omit it.</p>
|
|
</div>
|
|
</section>
|
|
{/if}
|
|
</div>
|