Price
An amount written in its currency, with the old price struck through and the discount worked out.
Demo
$19.99$29.99was $39.99, −25% off1.234,50 €was 1.499,00 €, −18% off¥1,250Free
£12.50was £20.00$49.00was $79.00, −38% off$249.00$9.50was $12.00
Numbers are written for their currency and locale; text is shown as it is. The old price is struck through and the discount worked out.
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/price.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/price.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/price.qwe
view PriceDemo() = <div.demo-stack> <div.demo-row style="gap: 28px"> <Price amount={19.99}/> <Price amount={29.99} old={39.99}/> <Price amount={1234.5} old={1499} currency="EUR" locale="de-DE"/> <Price amount={1250} currency="JPY" locale="ja-JP"/> <Price amount={0}/> </div> <div.demo-row style="gap: 28px"> <Price amount="£12.50" old="£20.00" size="sm"/> <Price amount={49} old={79} size="lg" tone="danger"/> <Price amount={249} size="xl" tone="accent"/> <Price amount={9.5} old={12} discount={false}/> </div> <p.demo-note>Numbers are written for their currency and locale; text is shown as it is. The old price is struck through and the discount worked out.</p> </div>Source
ui/price.qwe
// Price: an amount written in its currency, with the old price struck through and// the discount beside it when there was one. A number is formatted for the// currency and locale (money.format does it: "$19.99", "1.234,50 €", "¥1,250");// text is shown as it is, for amounts formatted elsewhere.//// <Price amount={19.99}/>// <Price amount={29.99} old={39.99} currency="EUR" locale="de-DE"/>// <Price amount="£12.50" old="£20.00" size="lg" discount={false}/>//// amount: the price, a number or text. old: the price before, struck through// (screen readers hear "was ..."). discount: a "−25%" beside it, worked out from// the two when both are numbers (true). currency: ISO code ("USD"). locale: how// to write it ("en-US"; the program's locale when it has one). size: "sm", "md"// (default), "lg" or "xl". tone: "fg" (default), "accent" or "danger" (red when// discounted, as shops do). free: what to show for 0 ("Free"; null shows "$0.00").// cents: false drops the ".00" of a whole amount ("$12"; true, so "$12.00").//// price_text(amount, currency, locale, cents) and price_percent(amount, old) are what it// uses, for other components: product_card, cart_item, cart_summary and others in// this kit need this file.import ui/theme.qwe use ui_clslet price_css = css`.ui-price { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: .4em; font: 600 15px/1.2 var(--ui-font); color: var(--ui-fg); font-variant-numeric: tabular-nums; white-space: nowrap; }.ui-price-now { font-size: 1em; }.ui-price-old { font-weight: 400; font-size: .85em; color: var(--ui-muted); text-decoration: line-through; }.ui-price-off { display: inline-flex; align-items: center; height: 1.4em; padding: 0 .45em; border-radius: 999px; font-size: .72em; font-weight: 600; color: var(--ui-danger); background: var(--ui-danger-soft); }.ui-price-sm { font-size: 13px; }.ui-price-lg { font-size: 20px; }.ui-price-xl { font-size: 28px; letter-spacing: -.01em; }.ui-price-accent .ui-price-now { color: var(--ui-accent); }.ui-price-danger .ui-price-now { color: var(--ui-danger); }`// An amount as text in its currency: a number formatted for the locale, text as it// is. cents: false drops the ".00" of a whole amount ("$12", "1.000 €").fn price_text(amount, currency: str = "USD", locale: str? = null, cents: bool = true) -> str { if type_of(amount) == "str" { return amount } if amount == null { return "" } let m = money.of(dec(amount), currency, "half_up") if is_err(m) { return str(amount) } let text = locale != null ? money.format(m, locale) : money.format(m) let units = money.minor_units(currency) if cents || units == 0 || m.amount != floor(m.amount) { return text } re.replace(text, `[.,]0{${units}}(\\D*)$`, "$1")}// The discount as a whole percentage ("25" for 29.99 from 39.99), or null when// either is text or the old price is not above the new one.fn price_percent(amount, old) -> int? { if type_of(amount) == "str" || type_of(old) == "str" || amount == null || old == null { return null } let a = dec(amount) let o = dec(old) if o <= 0 || a >= o { return null } int(round((o - a) * 100 / o))}view Price(amount, old = null, currency: str = "USD", locale: str? = null, discount: bool = true, size: str = "md", tone: str = "fg", free: str? = "Free", cents: bool = true, label: str? = null, class: str? = null) { style global price_css let zero = type_of(amount) != "str" && amount != null && dec(amount) == 0 && free != null let now_text = zero ? free : price_text(amount, currency, locale, cents) let old_text = old != null ? price_text(old, currency, locale, cents) : null let pct = discount ? price_percent(amount, old) : null <span class={ui_cls(["ui-price", size != "md" && `ui-price-${size}`, tone != "fg" && `ui-price-${tone}`, class])} aria-label={label}> <span.ui-price-now>{now_text}</span> {old_text != null && <s.ui-price-old><span.ui-sr>was </span>{old_text}</s>} {pct != null && <span.ui-price-off><span.ui-sr>, </span>−{pct}%<span.ui-sr> off</span></span>} </span>}Related
CartItemOne line of a cart: picture, name and options, a quantity control, the line's total and Remove, as inputs a form sends.6 views · 6 downloadsCartSummarySubtotal, shipping, tax, a discount and the total added up, with a Checkout button as a link, a form or a callback.6 views · 7 downloadsDiscountBadge"−20%" or "Save $10" as a small label, in tones, solid, soft or outline, or pinned to an image's corner.7 views · 8 downloadsPlanToggleMonthly or yearly billing as a pill of two radios, with the saving shown on the yearly side.6 views · 4 downloadsPriceTagA bold price with its period (/month) and a note under it, for plans, rooms and seats.6 views · 4 downloadsPricingCardOne plan: name, price and period, what it includes, and a button; highlighted with a badge when it is the one to pick.7 views · 6 downloads
Thread
Thread 0 posts
On Price: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.