QuotaBar
Several quotas in one list, each a bar with its numbers, amber near its limit and red at it.
Demo
Below, compact: one line each.
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/quota_bar.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/quota_bar.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/quota_bar.qwe
let quota_bar_demo_items = [ { label: "Storage", used: 38.2, allowed: 50, unit: " GB", note: "Files, backups and exports", href: "#quota_bar" }, { label: "API calls", used: 92000, allowed: 100000, warn_at: 85, note: "Resets on the 1st" }, { label: "Team members", used: 5, allowed: 5 }, { label: "Email sends", used: 1200, allowed: 10000 }, { label: "Projects", used: 3, allowed: null },]view QuotaBarDemo() = <div.demo-stack> <div.demo-narrow><QuotaBar id="quota-bar-demo" quotas={quota_bar_demo_items}/></div> <p.demo-note>Below, compact: one line each.</p> <QuotaBar compact={true} label="Plan limits" quotas={take(quota_bar_demo_items, 4)}/> </div>Source
ui/quota_bar.qwe
// QuotaBar: several allowances in one list, each a bar with its numbers, amber// near its limit and red at it. For one on its own, UsageMeter.//// <QuotaBar quotas={[// { label: "Storage", used: 38.2, allowed: 50, unit: " GB" },// { label: "API calls", used: 92000, allowed: 100000, warn_at: 85, note: "Resets on the 1st" },// { label: "Projects", used: 3, allowed: null },// ]}/>//// quotas: a list of { label, used, allowed, unit?, warn_at?, note?, href? }: the// numbers (allowed null for no limit, thousands get commas), unit after each// ("%", " GB"), warn_at the percentage from which it is amber (80; red at 100 and// over), note a quieter line under the label, href a link from the label.// compact: one line a quota, the bar beside the label. label: the list's name// for screen readers ("Quotas"). Each bar is a role="meter" with its numbers as// its text.import ui/theme.qwe use ui_clslet quota_bar_css = css`.ui-quota { display: grid; gap: 14px; margin: 0; padding: 0; list-style: none; font: 13px/1.4 var(--ui-font); color: var(--ui-fg); }.ui-quota-row { display: grid; gap: 5px; min-width: 0; --t: var(--ui-accent); }.ui-quota-warn { --t: var(--ui-warning); }.ui-quota-full { --t: var(--ui-danger); }.ui-quota-head { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 2px 12px; }.ui-quota-label { font-weight: 500; font-size: 14px; }.ui-quota-label a { color: inherit; text-decoration: none; border-radius: 3px; }.ui-quota-label a:hover { color: var(--ui-accent); text-decoration: underline; }.ui-quota-label a:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-quota-note { display: block; color: var(--ui-muted); font-size: 12px; font-weight: 400; }.ui-quota-nums { color: var(--ui-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }.ui-quota-warn .ui-quota-nums, .ui-quota-full .ui-quota-nums { color: var(--t); font-weight: 600; }.ui-quota-track { position: relative; height: 6px; 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-quota-fill { height: 100%; border-radius: inherit; background: var(--t); transition: width .3s ease; }.ui-quota-compact { gap: 8px; }.ui-quota-compact .ui-quota-row { grid-template-columns: minmax(90px, 1fr) minmax(0, 2fr) auto; align-items: center; column-gap: 12px; }.ui-quota-compact .ui-quota-head { display: contents; }.ui-quota-compact .ui-quota-track { order: 1; }.ui-quota-compact .ui-quota-nums { order: 2; }.ui-quota-compact .ui-quota-note { display: none; }@media (prefers-reduced-motion: reduce) { .ui-quota-fill { transition: none; } }`// A number with thousands separators, to two decimals at most: 92,000 or 38.2.fn quota_bar_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 QuotaBar(quotas: list, compact: bool = false, label: str = "Quotas", id: str? = null, class: str? = null) { style global quota_bar_css <ul id={id} class={ui_cls(["ui-quota", compact && "ui-quota-compact", class])} aria-label={label}> {for q in quotas => { let unit = q.unit ?? "" let limit = q.allowed let pct = limit != null && limit > 0 ? q.used * 100 / limit : 0 let mood = limit == null ? null : pct >= 100 ? "full" : pct >= (q.warn_at ?? 80) ? "warn" : null let text = limit == null ? `${quota_bar_num(q.used)}${unit} used, no limit` : `${quota_bar_num(q.used)}${unit} of ${quota_bar_num(limit)}${unit} (${round(pct)}%)` <li class={ui_cls(["ui-quota-row", mood != null && `ui-quota-${mood}`])}> <div.ui-quota-head aria-hidden="true"> <span.ui-quota-label> {q.href != null ? <a href={q.href}>{q.label}</a> : q.label} {q.note != null && <span.ui-quota-note>{q.note}</span>} </span> <span.ui-quota-nums>{limit == null ? `${quota_bar_num(q.used)}${unit} · no limit` : `${quota_bar_num(q.used)}${unit} / ${quota_bar_num(limit)}${unit}`}</span> </div> <div.ui-quota-track role="meter" aria-label={q.label} aria-valuemin="0" aria-valuemax={limit != null ? quota_bar_num(limit) : null} aria-valuenow={quota_bar_num(q.used)} aria-valuetext={text}> <div.ui-quota-fill style={`width: ${round((pct > 100 ? 100 : pct) * 10) / 10}%`}></div> </div> </li> }} </ul>}Related
OnboardingChecklistSteps 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 downloadsUsageMeterUsed of allowed as a bar with the numbers, amber near the limit, red at it, and plain when there is no limit.6 views · 7 downloadsProgressListItems each with a label, a bar and its value or percentage, marked in red when one goes over its limit.6 views · 4 downloadsSessionListSigned-in devices with browser, system, place and time, this one marked, and Sign out for each or all the others.6 views · 5 downloadsSettingsSectionA titled block of settings rows, each a label and hint with its control at the right (SettingsRow).5 views · 5 downloads
Thread
Thread 0 posts
On QuotaBar: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.