Feedback

ProgressRing

Circular progress in four sizes, with its percentage or a value of your own in the middle, or turning while not known.

#feedback#progress#no-js

Demo

Preview

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/progress_ring.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/progress_ring.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/progress_ring.qwe
view ProgressRingDemo() =    <div.demo-stack>        <div.demo-row style="gap: 24px">            <ProgressRing value={8} size="sm" label="Uploading"/>            <ProgressRing value={42} label="Storage used"/>            <ProgressRing value={3} max={5} value_text="3/5" size="lg" tone="success" label="Lessons done"/>            <ProgressRing value={91} size="xl" tone="warning" thickness={8} label="Monthly quota" show_label={true}/>        </div>        <div.demo-row style="gap: 24px">            <ProgressRing value={100} tone="success" label="Backup complete" show_label={true}/>            <ProgressRing value={0} label="Not started" show_label={true}/>            <ProgressRing size="sm" label="Syncing" show_label={true}/>            <ProgressRing size="lg" tone="neutral" label="Preparing export" show_label={true}/>        </div>    </div>

Source

ui/progress_ring.qwe
// ProgressRing: how far along something is, as a ring with the percentage in it,// or a turning arc while that is not known yet (role="progressbar").////   <ProgressRing value={72} label="Storage used"/>//   <ProgressRing value={3} max={5} value_text="3/5" size="lg" label="Lessons done" show_label={true}/>//   <ProgressRing label="Uploading" size="sm"/>              no value: indeterminate//// value: from 0 to max (100 by default); null means not known yet, shown as an arc// that turns (and stands still with reduced motion). label: what it measures, read// by screen readers; show_label also writes it beside the ring (below it with// size="xl"). show_value: the percentage in the middle (true; sizes from md up);// value_text: other text there ("3/5", "12 GB"). size: "sm" (32px), "md" (48px,// default), "lg" (72px) or "xl" (112px). thickness: the ring's width, out of the// ring's 100 (10). tone: "accent" (default), "neutral", "success", "warning",// "danger" or "info".import ui/theme.qwe use ui_clslet progress_ring_css = css`.ui-pr { display: inline-flex; align-items: center; gap: 10px; vertical-align: middle; font: 13px/1.4 var(--ui-font);  color: var(--ui-fg); --t: var(--ui-accent); --s: 48px; }.ui-pr-neutral { --t: var(--ui-muted); }.ui-pr-success { --t: var(--ui-success); }.ui-pr-warning { --t: var(--ui-warning); }.ui-pr-danger { --t: var(--ui-danger); }.ui-pr-info { --t: var(--ui-info); }.ui-pr-sm { --s: 32px; }.ui-pr-lg { --s: 72px; }.ui-pr-xl { --s: 112px; flex-direction: column; gap: 8px; }.ui-pr-ring { position: relative; display: grid; place-items: center; flex: none; width: var(--s); height: var(--s); }.ui-pr-ring svg { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }.ui-pr-ring circle { fill: none; }.ui-pr-track { stroke: var(--ui-subtle); }.ui-pr-arc { stroke: var(--t); stroke-linecap: round; transition: stroke-dasharray .35s ease; }.ui-pr-busy svg { animation: ui-pr-turn 1s linear infinite; }@keyframes ui-pr-turn { from { transform: rotate(-90deg); } to { transform: rotate(270deg); } }.ui-pr-value { position: relative; font-size: calc(var(--s) * .22); font-weight: 650; font-variant-numeric: tabular-nums; }.ui-pr-sm .ui-pr-value { display: none; }.ui-pr-xl .ui-pr-value { font-size: 26px; }.ui-pr-label { color: var(--ui-muted); }@media (prefers-reduced-motion: reduce) {  .ui-pr-arc { transition: none; }  .ui-pr-busy svg { animation: ui-pr-pulse 2s ease-in-out infinite; }}@keyframes ui-pr-pulse { 50% { opacity: .45; } }`// A number as text, whole ones without ".0", to one decimal at most.fn progress_ring_num(n: num) -> str {    let r = round(n * 10) / 10    r == floor(r) ? str(floor(r)) : str(r)}view ProgressRing(value: num? = null, max: num = 100, label: str = "Progress", show_label: bool = false,                  show_value: bool = true, value_text: str? = null, size: str = "md", thickness: num = 10,                  tone: str = "accent", class: str? = null) {    style global progress_ring_css    let known = value != null && max > 0    let part = known ? (value < 0 ? 0 : (value > max ? max : value)) : 0    let pct = known ? round(part * 1000 / max) / 10 : 0    let shown = value_text ?? `${round(pct)}%`    let w = thickness < 2 ? 2 : (thickness > 30 ? 30 : thickness)    let r = progress_ring_num(50 - w / 2)    <div class={ui_cls(["ui-pr", `ui-pr-${tone}`, size != "md" && `ui-pr-${size}`, !known && "ui-pr-busy", class])}         role="progressbar" aria-label={label} aria-valuemin={known ? "0" : null}         aria-valuemax={known ? progress_ring_num(max) : null} aria-valuenow={known ? progress_ring_num(part) : null}         aria-valuetext={known ? shown : null}>        <span.ui-pr-ring>            <svg viewBox="0 0 100 100" aria-hidden="true">                <circle.ui-pr-track cx="50" cy="50" r={r} stroke-width={progress_ring_num(w)}/>                {(!known || pct > 0) && <circle.ui-pr-arc cx="50" cy="50" r={r} stroke-width={progress_ring_num(w)}                    pathLength="100" stroke-dasharray={known ? `${progress_ring_num(pct)} 100` : "28 100"}/>}            </svg>            {known && show_value && <span.ui-pr-value aria-hidden="true">{shown}</span>}        </span>        {show_label && <span.ui-pr-label aria-hidden="true">{label}</span>}    </div>}
Thread
67Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.