WishlistButton
A heart that saves a product for later: a pressed-state toggle, named for screen readers, with a hidden input for forms.
Demo
Not saved.
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/wishlist_button.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/wishlist_button.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/wishlist_button.qwe
component WishlistButtonDemoLive() { state saved = false <div.demo-row> <WishlistButton id="sku-7" text={true} on_change={(on) => { saved = on }}/> <p.demo-note>{saved ? "Saved for later (on_change)." : "Not saved."}</p> </div>}view WishlistButtonDemo() = <div.demo-stack> <WishlistButtonDemoLive/> <div.demo-row style="gap: 20px"> <WishlistButton name="wish" id="sku-1"/> <WishlistButton saved={true} size="sm"/> <WishlistButton size="lg"/> <WishlistButton variant="plain" text={true} label="Save for later" saved_label="Saved for later"/> </div> <div style="position: relative; width: 160px; height: 110px; border-radius: var(--ui-radius); background: var(--ui-subtle)"> <span style="position: absolute; top: 8px; right: 8px"><WishlistButton/></span> </div> </div>Source
ui/wishlist_button.qwe
// WishlistButton: a heart that saves a product for later and shows it is saved.// It says its state (aria-pressed) and keeps a hidden input, so a plain <form>// sends `name` while it is on.//// <WishlistButton id="sku-1"/>// <WishlistButton saved={true} text={true} on_change={(on) => { saved = on }}/>//// saved: on to start with. text: the words beside the heart ("Save" / "Saved";// false, only the heart, named for screen readers and the tooltip). label,// saved_label: other words ("Add to wishlist", "Saved to wishlist"). name: the// hidden input's name, sent as value (the id, or "on") while saved. id: the// product's id. size: "sm", "md" (default) or "lg". variant: "round" (default,// a white disc for a picture's corner) or "plain" (just the heart, inline).// on_change(saved): inside one of your components, given true or false.import ui/theme.qwe use ui_clslet wishlist_button_css = css`.ui-wish { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 36px; min-width: 36px; padding: 0; border: 1px solid var(--ui-border); border-radius: 999px; background: var(--ui-surface); color: var(--ui-muted); font: 500 13px/1 var(--ui-font); cursor: pointer; box-shadow: var(--ui-shadow); transition: color .15s, background .15s, transform .15s; }.ui-wish-text { padding: 0 14px 0 10px; }.ui-wish:hover { color: var(--ui-danger); }.ui-wish:focus-visible { outline: none; box-shadow: var(--ui-focus); }.ui-wish svg { width: 18px; height: 18px; flex: none; transition: transform .2s; }.ui-wish-on { color: var(--ui-danger); }.ui-wish-on svg { animation: ui-wish-pop .3s ease-out; }@keyframes ui-wish-pop { 40% { transform: scale(1.3); } }.ui-wish-plain { border: 0; background: transparent; box-shadow: none; height: auto; min-width: 0; }.ui-wish-plain.ui-wish-text { padding: 0; }.ui-wish-sm { height: 30px; min-width: 30px; font-size: 12px; }.ui-wish-sm svg { width: 15px; height: 15px; }.ui-wish-lg { height: 44px; min-width: 44px; font-size: 14px; }.ui-wish-lg svg { width: 22px; height: 22px; }@media (prefers-reduced-motion: reduce) { .ui-wish, .ui-wish svg { transition: none; animation: none; } }`component WishlistButton(saved: bool = false, text: bool = false, label: str = "Add to wishlist", saved_label: str = "Saved to wishlist", name: str? = null, id: str? = null, size: str = "md", variant: str = "round", on_change = null, class: str? = null) { style global wishlist_button_css state on = saved let says = on ? saved_label : label <button type="button" aria-pressed={on ? "true" : "false"} aria-label={says} title={says} data-product={id} class={ui_cls(["ui-wish", on && "ui-wish-on", text && "ui-wish-text", variant == "plain" && "ui-wish-plain", size != "md" && `ui-wish-${size}`, class])} @click={() => { on = !on on_change && on_change(on) }}> <svg viewBox="0 0 24 24" fill={on ? "currentColor" : "none"} stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20.5s-7.5-4.6-9.3-9.2C1.4 8 3.3 4.5 6.8 4.5c2 0 3.6 1.1 5.2 3 1.6-1.9 3.2-3 5.2-3 3.5 0 5.4 3.5 4.1 6.8-1.8 4.6-9.3 9.2-9.3 9.2z"/></svg> {text && <span aria-hidden="true">{on ? "Saved" : "Save"}</span>} {name != null && <input type="hidden" name={name} value={id ?? "on"} disabled={!on}/>} </button>}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 downloadsCouponInputA promo code box with Apply: on_apply(code) says whether it took, and an applied code shows with Remove.6 views · 5 downloadsPaymentMethodsWays to pay as radio cards with their marks: card (with the brands taken), PayPal, Apple Pay, Google Pay, bank, cash.6 views · 5 downloadsPlanToggleMonthly or yearly billing as a pill of two radios, with the saving shown on the yearly side.6 views · 4 downloadsProductCardA product in a shop's grid: picture, name, price with the discount, stars, a badge and Add to cart, as a plain form or a callback.6 views · 6 downloadsShippingOptionsShipping methods as radio cards, each with its price and how long it takes; a plain form sends the choice.7 views · 7 downloads
Thread
Thread 0 posts
On WishlistButton: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.