Charts

Sparkline

A small inline chart of a list of numbers: a line, an area or bars, safe with no data or flat data.

#chart#data#no-js

Demo

Preview

Revenue this week up 50%.

Get it

It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.

$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/sparkline.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/sparkline.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/sparkline.qwe
view SparklineDemo() =    <div.demo-stack>        <div.demo-row>            <Sparkline values={[12, 18, 14, 22, 19, 26, 30]} label="Signups"/>            <Sparkline values={[40, 38, 41, 35, 30, 32, 28, 24]} variant="area" tone="danger" label="Stock"/>            <Sparkline values={[3, 7, 4, 9, 6, 11, 8, 12, 10, 14]} variant="area" tone="success" width={160}/>            <Sparkline values={[4, 0, 7, 3, 5, 8, 2]} variant="bar" width={80} tone="info" label="Orders"/>        </div>        <p.demo-note>Revenue this week <Sparkline values={[5, 9, 7, 12, 10, 15]} width={64} height={18} dot={false}/> up 50%.</p>        <div.demo-row>            <Sparkline values={[]} label="Empty"/>            <Sparkline values={[7]} label="One value"/>            <Sparkline values={[5, 5, 5, 5]} tone="neutral" label="Flat"/>            <Sparkline values={[-4, 2, -1, 5, -3]} variant="bar" tone="warning" width={80} label="Net"/>        </div>    </div>

Source

ui/sparkline.qwe
// Sparkline: a small chart in the line of text, of a list of numbers.////   <Sparkline values={[12, 18, 14, 22, 30]}/>//   <Sparkline values={daily_signups} variant="area" tone="success" label="Signups"/>//   <Sparkline values={[4, 0, 7, 3]} variant="bar" width={80}/>//// values: the numbers, oldest first; it spans their lowest to their highest (a bar// chart starts at 0). No values draws a dashed line, one or all equal a flat one.// variant: "line" (default), "area" (the line, filled under) or "bar".// width and height: its size in pixels (120 by 32). tone: "accent" (default),// "neutral", "success", "warning", "danger" or "info". dot: the last point marked// (true by default). label: what it shows; screen readers hear "Signups: 12 to 30"// ("Trend" by default).import ui/theme.qwe use ui_clslet sparkline_css = css`.ui-spark { display: inline-block; vertical-align: middle; overflow: visible; color: var(--ui-accent); }.ui-spark-neutral { color: var(--ui-muted); }.ui-spark-success { color: var(--ui-success); }.ui-spark-warning { color: var(--ui-warning); }.ui-spark-danger { color: var(--ui-danger); }.ui-spark-info { color: var(--ui-info); }.ui-spark-line { fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }.ui-spark-fill { fill: currentColor; opacity: .14; stroke: none; }.ui-spark-none { fill: none; stroke: var(--ui-border); stroke-width: 1.5; stroke-dasharray: 3 3; }.ui-spark-dot { fill: currentColor; stroke: var(--ui-bg); stroke-width: 1.5; }.ui-spark-bar { fill: currentColor; opacity: .85; }.ui-spark-bar:last-child { opacity: 1; }`// A coordinate to one decimal, written the same on the server and in the browser.fn sparkline_num(n: num) -> str {    let r = round(n * 10) / 10    r == floor(r) ? str(floor(r)) : str(r)}view Sparkline(values: list = [], width: int = 120, height: int = 32, variant: str = "line",               tone: str = "accent", dot: bool = true, label: str = "Trend", class: str? = null) {    style global sparkline_css    let n = len(values)    let pad = 3    let bars = variant == "bar"    let lo = n == 0 ? 0 : (bars ? min([0, min(values)]) : min(values))    let hi = n == 0 ? 0 : (bars ? max([0, max(values)]) : max(values))    let span = hi - lo    let y = (v) => span == 0 ? (bars ? height : height / 2) : pad + (hi - v) * (height - 2 * pad) / span    let pts = n == 1 ? [values[0], values[0]] : values    let m = len(pts)    let xs = for i in range(m) => pad + i * (width - 2 * pad) / (m - 1)    let line = "M" + join(for i in range(m) => `${sparkline_num(xs[i])},${sparkline_num(y(pts[i]))}`, " L")    let bottom = sparkline_num(height - pad)    let slot = n == 0 ? 0 : width / n    let base = y(0)    let said = n == 0 ? `${label}: no data`        : `${label}: ${sparkline_num(values[0])}${n > 1 ? ` to ${sparkline_num(values[-1])}` : ""}`    <svg class={ui_cls(["ui-spark", tone != "accent" && `ui-spark-${tone}`, class])} width={width} height={height}         viewBox={`0 0 ${width} ${height}`} role="img" aria-label={said}>        {n == 0 ? <path.ui-spark-none d={`M${pad},${sparkline_num(height / 2)} H${width - pad}`}/>            : bars ? (for [i, v] in enumerate(values) => {                  let top = y(v)                  let tall = abs(base - top)                  <rect.ui-spark-bar x={sparkline_num(i * slot + slot * 0.15)} width={sparkline_num(slot * 0.7)}                        y={sparkline_num(tall < 1 ? base - 1 : (top < base ? top : base))}                        height={sparkline_num(tall < 1 ? 1 : tall)} rx="1"/>              })            : [variant == "area" && <path.ui-spark-fill d={`${line} L${sparkline_num(xs[m - 1])},${bottom} L${sparkline_num(xs[0])},${bottom} Z`}/>,               <path.ui-spark-line d={line}/>,               dot && <circle.ui-spark-dot cx={sparkline_num(xs[m - 1])} cy={sparkline_num(y(pts[m - 1]))} r="2.5"/>]}    </svg>}
Thread
67Useful? Like it, or add to its thread below.

Thread 0 posts

On Sparkline: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.