Data

ProgressList

Items each with a label, a bar and its value or percentage, marked in red when one goes over its limit.

#data#progress#list#dashboard#no-js

Demo

Preview
  • Travel$1,840 of $2,500
  • SoftwareAnnual licences renewed in March$2,900 of $2,500
  • Hardware$600 of $4,000
  • TrainingNot started
  • Yes64%
  • Maybe21%
  • No15%

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

Example

The code of the preview above.

demos/progress_list.qwe
view ProgressListDemo() =    <div.demo-row style="align-items: flex-start; gap: 32px">        <div style="flex: 1 1 280px; max-width: 380px">            <ProgressList id="progress-list-demo-budget" label="Budget by category" items={[                { label: "Travel", value: 1840, limit: 2500, note: "$1,840 of $2,500" },                { label: "Software", value: 2900, limit: 2500, note: "$2,900 of $2,500", description: "Annual licences renewed in March" },                { label: "Hardware", value: 600, limit: 4000, note: "$600 of $4,000", tone: "success" },                { label: "Training", value: 0, limit: 1000, note: "Not started", tone: "neutral" },            ]}/>        </div>        <div style="flex: 1 1 240px; max-width: 320px">            <ProgressList id="progress-list-demo-poll" label="Would you recommend us?" show="percent" order="desc" size="sm" items={[                { label: "Maybe", value: 21 }, { label: "Yes", value: 64, tone: "success" }, { label: "No", value: 15, tone: "warning" },            ]}/>        </div>    </div>

Source

ui/progress_list.qwe
// ProgressList: items each with a label, a bar and its value: goals, budgets by// category, storage by kind, skills, survey answers.////   <ProgressList label="Budget" items={[//       { label: "Travel", value: 1840, limit: 2500, note: "$1,840 of $2,500" },//       { label: "Software", value: 2900, limit: 2500, note: "$2,900 of $2,500" },//   ]}/>//   <ProgressList show="percent" items={[{ label: "Yes", value: 62 }, { label: "No", value: 38 }]}/>//// items: maps of { label, value, limit?, note?, tone?, description? }: value of// limit (the list's limit, 100 by default); note: the text on the right (by// default what show says); tone: the bar's colour, "accent" (default),// "neutral", "success", "warning", "danger" or "info". description: a line under// the label. show: what the right side says without a note: "value" ("1,840 /// 2,500", default), "percent" ("74%") or "none". over: when a value passes its// limit the bar is full and in the danger tone (true, default). order: "desc" or// "asc" orders the items by their share. size: "sm" or "md" (default): the// bars' thickness. label: the list's name for screen readers. Each bar is a// role="progressbar" named by its label.import ui/theme.qwe use ui_clslet progress_list_css = css`.ui-progresslist { display: grid; gap: 14px; margin: 0; padding: 0; list-style: none; font: 14px/1.4 var(--ui-font); color: var(--ui-fg); }.ui-progresslist-sm { gap: 10px; font-size: 13px; }.ui-progresslist-item { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 5px 12px; --t: var(--ui-accent); }.ui-progresslist-neutral { --t: var(--ui-muted); }.ui-progresslist-success { --t: var(--ui-success); }.ui-progresslist-warning { --t: var(--ui-warning); }.ui-progresslist-danger { --t: var(--ui-danger); }.ui-progresslist-info { --t: var(--ui-info); }.ui-progresslist-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }.ui-progresslist-desc { display: block; color: var(--ui-muted); font-size: 12px; font-weight: 400; white-space: normal; }.ui-progresslist-note { color: var(--ui-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }.ui-progresslist-over .ui-progresslist-note { color: var(--ui-danger); font-weight: 600; }.ui-progresslist-track { grid-column: 1 / -1; height: 8px; overflow: hidden; border-radius: 999px; background: var(--ui-subtle);  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ui-border) 60%, transparent); }.ui-progresslist-sm .ui-progresslist-track { height: 5px; }.ui-progresslist-fill { height: 100%; border-radius: inherit; background: var(--t); transition: width .3s ease; }@media (prefers-reduced-motion: reduce) { .ui-progresslist-fill { transition: none; } }`fn progress_list_num(n: num) -> str = format_number(round(n * 100) / 100, "en-US")// A number for an ARIA value: plain, whole ones without ".0".fn progress_list_plain(n: num) -> str {    let r = round(n * 100) / 100    r == floor(r) ? str(floor(r)) : str(r)}view ProgressList(items: list, limit: num = 100, show: str = "value", over: bool = true, order: str? = null, size: str = "md",                  label: str? = null, id: str? = null, class: str? = null) {    style global progress_list_css    let share = (it: map) => {        let top = it.limit ?? limit        top <= 0 ? 0 : it.value / top    }    let ordered = order == "desc" ? reverse(sort_by(items, (it) => share(it))) : order == "asc" ? sort_by(items, (it) => share(it)) : items    <ul class={ui_cls(["ui-progresslist", size == "sm" && "ui-progresslist-sm", class])} id={id} aria-label={label}>        {for it in ordered => {            let top = it.limit ?? limit            let past = over && it.value > top            let pct = top <= 0 ? 0 : round(min([max([it.value, 0]), top]) * 1000 / top) / 10            let real = top <= 0 ? 0 : round(it.value * 100 / top)            let note = it.note ?? (show == "percent" ? `${real}%` : show == "value" ? `${progress_list_num(it.value)} / ${progress_list_num(top)}` : null)            <li class={ui_cls(["ui-progresslist-item", `ui-progresslist-${past ? "danger" : (it.tone ?? "accent")}`, past && "ui-progresslist-over"])}>                <span.ui-progresslist-label>{it.label}{it.description != null && <span.ui-progresslist-desc>{it.description}</span>}</span>                {note != null ? <span.ui-progresslist-note>{note}</span> : <span></span>}                <div.ui-progresslist-track role="progressbar" aria-label={it.label} aria-valuemin="0" aria-valuemax={progress_list_plain(top)}                     aria-valuenow={progress_list_plain(min([max([it.value, 0]), top]))} aria-valuetext={note ?? `${real}%`}>                    <div.ui-progresslist-fill style={`width: ${pct}%`}></div>                </div>            </li>        }}    </ul>}
Thread
64Useful? Like it, or add to its thread below.

Thread 0 posts

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

No posts yet. The first one opens the thread.