UsageMeter
Used of allowed as a bar with the numbers, amber near the limit, red at it, and plain when there is no limit.
Demo
Files, backups and exports
Resets on the 1stNear the limit
Limit reached
Over the limit
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/usage_meter.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/usage_meter.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/usage_meter.qwe
view UsageMeterDemo() = <div.demo-stack.demo-narrow> <UsageMeter id="usage-meter-demo" label="Storage" used={38.2} allowed={50} unit=" GB" hint="Files, backups and exports"/> <UsageMeter label="API calls" used={92000} allowed={100000} warn_at={85} hint="Resets on the 1st"/> <UsageMeter label="Team members" used={5} allowed={5}/> <UsageMeter label="Email sends" used={12400} allowed={10000} size="lg"/> <UsageMeter label="Projects" used={3} allowed={null} size="sm"/> </div>Source
ui/usage_meter.qwe
// UsageMeter: how much of an allowance is used, as a bar with the numbers, that// turns amber near the limit and red at it. For several at once, QuotaBar.//// <UsageMeter label="Storage" used={38.2} allowed={50} unit=" GB"/>// <UsageMeter label="API calls" used={92000} allowed={100000} warn_at={85} hint="Resets on the 1st"/>// <UsageMeter label="Seats" used={3} allowed={null}/> no limit//// label: what is measured. used and allowed: the numbers; unit comes after each// ("%", " GB"; thousands get commas). allowed null means no limit: the numbers// say so and the bar stays empty. warn_at: the percentage from which it is amber// and says "Near the limit" (80); at 100 it is red and says "Limit reached"; over// it, "Over the limit" with the bar full. hint: a line under it. show_pct: the// percentage after the numbers (true). size: "sm", "md" (default) or "lg". It is// a role="meter" with the numbers as its text.import ui/theme.qwe use ui_clslet usage_meter_css = css`.ui-usage { display: grid; gap: 6px; min-width: 0; font: 13px/1.4 var(--ui-font); color: var(--ui-fg); --t: var(--ui-accent); }.ui-usage-warn { --t: var(--ui-warning); }.ui-usage-full, .ui-usage-over { --t: var(--ui-danger); }.ui-usage-head { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 2px 12px; }.ui-usage-label { font-weight: 500; font-size: 14px; }.ui-usage-nums { color: var(--ui-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }.ui-usage-nums b { color: var(--ui-fg); font-weight: 600; }.ui-usage-track { position: relative; 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-usage-sm .ui-usage-track { height: 4px; }.ui-usage-lg .ui-usage-track { height: 12px; }.ui-usage-fill { height: 100%; border-radius: inherit; background: var(--t); transition: width .3s ease; }.ui-usage-mark { position: absolute; top: 0; bottom: 0; width: 2px; margin-left: -1px; background: var(--ui-bg); }.ui-usage-foot { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 2px 12px; color: var(--ui-muted); }.ui-usage-warning { display: inline-flex; align-items: center; gap: 5px; color: var(--t); font-weight: 600; }.ui-usage-warning svg { width: 14px; height: 14px; }@media (prefers-reduced-motion: reduce) { .ui-usage-fill { transition: none; } }`// A number with thousands separators, to two decimals at most: 92,000 or 38.2.fn usage_meter_num(n: num) -> str { let r = round(n * 100) / 100 let whole = floor(abs(r)) let frac = r == floor(r) ? "" : slice(str(round((abs(r) - whole) * 100) / 100), 1) let digits = str(whole) let groups = [] let rest = digits while len(rest) > 3 { groups = [slice(rest, len(rest) - 3, len(rest))] + groups rest = slice(rest, 0, len(rest) - 3) } (r < 0 ? "-" : "") + join([rest] + groups, ",") + frac}view UsageMeter(label: str, used: num, allowed: num? = null, unit: str = "", warn_at: num = 80, hint: str? = null, show_pct: bool = true, size: str = "md", id: str? = null, class: str? = null) { style global usage_meter_css let pct = allowed != null && allowed > 0 ? used * 100 / allowed : 0 let shown = pct < 0 ? 0 : (pct > 100 ? 100 : pct) let mood = allowed == null ? null : pct > 100 ? "over" : pct >= 100 ? "full" : pct >= warn_at ? "warn" : null let words = match mood { "over" => "Over the limit", "full" => "Limit reached", "warn" => "Near the limit", _ => null } let text = allowed == null ? `${usage_meter_num(used)}${unit} used, no limit` : `${usage_meter_num(used)}${unit} of ${usage_meter_num(allowed)}${unit}${show_pct ? ` (${round(pct)}%)` : ""}` <div id={id} class={ui_cls(["ui-usage", mood != null && `ui-usage-${mood}`, size != "md" && `ui-usage-${size}`, class])}> <div.ui-usage-head aria-hidden="true"> <span.ui-usage-label>{label}</span> <span.ui-usage-nums> <b>{usage_meter_num(used)}{unit}</b> {allowed == null ? " used, no limit" : ` of ${usage_meter_num(allowed)}${unit}`} {allowed != null && show_pct && ` · ${round(pct)}%`} </span> </div> <div.ui-usage-track role="meter" aria-label={label} aria-valuemin="0" aria-valuemax={allowed != null ? usage_meter_num(allowed) : null} aria-valuenow={usage_meter_num(used)} aria-valuetext={text}> <div.ui-usage-fill style={`width: ${round(shown * 10) / 10}%`}></div> {allowed != null && warn_at > 0 && warn_at < 100 && <span.ui-usage-mark style={`left: ${warn_at}%`}></span>} </div> {(hint != null || words != null) && <div.ui-usage-foot> <span>{hint}</span> {words != null && <span.ui-usage-warning role="status"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/></svg> {words} </span>} </div>} </div>}Related
QuotaBarSeveral quotas in one list, each a bar with its numbers, amber near its limit and red at it.7 views · 6 downloadsProgressListItems each with a label, a bar and its value or percentage, marked in red when one goes over its limit.6 views · 4 downloadsScorecardA score out of a maximum as a filled ring, with a letter grade, a verdict and the parts it is made of.8 views · 5 downloadsOnboardingChecklistSteps to finish setting up, with a progress bar, a tick on each done and a link to where the rest get done.7 views · 5 downloadsServerStatusServices with up, degraded, down or maintenance, their uptime and the last days as bars, summed up in one line.6 views · 6 downloadsKpiCardA number with its label, the change since last period coloured by whether that is good, and a small trend as a line or bars.7 views · 7 downloads
Thread
Thread 0 posts
On UsageMeter: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.