SegmentedControl
One choice of a few, as a pill with the picked one raised: real radio buttons that a form sends.
Demo
€96 a year: two months free.
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/segmented_control.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/segmented_control.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/segmented_control.qwe
component SegmentedControlDemoLive() { state plan = "y" <div.demo-stack> <SegmentedControl name="sc-plan" label="Billing period" value={plan} on_change={(v) => { plan = v }} options={[{ label: "Monthly", value: "m" }, { label: "Yearly", value: "y" }]}/> <p.demo-note>{plan == "y" ? "€96 a year: two months free." : "€10 a month."}</p> </div>}view SegmentedControlDemo() = <div.demo-stack> <div.demo-row> <SegmentedControl name="sc-view" label="View" options={["Day", "Week", "Month"]} value="Week"/> <SegmentedControl name="sc-align" label="Align" size="sm" options={["Left", "Center", "Right"]}/> <SegmentedControl name="sc-off" label="Disabled" options={["On", "Off"]} disabled={true}/> </div> <div.demo-narrow> <SegmentedControl name="sc-full" label="Sort" full={true} options={["Newest", "Popular", "Oldest"]}/> </div> <SegmentedControlDemoLive/> </div>Source
ui/segmented_control.qwe
// SegmentedControl: a row of choices where one is picked, shown as a pill with the// picked one raised. It is a group of real radio buttons, so it works in a plain// <form> (sends `name` = the picked value) and with the arrow keys.//// <SegmentedControl name="view" options={["Day", "Week", "Month"]} value="Week"/>// <SegmentedControl name="plan" label="Billing" value={plan} on_change={(v) => { plan = v }}// options={[{ label: "Monthly", value: "m" }, { label: "Yearly", value: "y" }]}/>//// name: the radios' name, unique on the page. options: strings, or maps of// { label, value }. value: the one picked (the first if none).// label: what screen readers call the group. size: "sm" or "md" (default).// full: as wide as its container, the segments sharing it. disabled: all of it.// on_change(value): inside one of your components, called with the value picked.import ui/theme.qwe use ui_clslet segmented_control_css = css`.ui-segmented { display: inline-flex; justify-self: start; gap: 2px; padding: 3px; border-radius: calc(var(--ui-radius-sm) + 3px); background: var(--ui-subtle); border: 1px solid var(--ui-border); font: 500 14px/1 var(--ui-font); color: var(--ui-muted);}.ui-segmented-full { display: flex; justify-self: stretch; }.ui-segmented-full .ui-segmented-opt { flex: 1 1 0; }.ui-segmented-opt { position: relative; display: flex; cursor: pointer; }.ui-segmented-opt input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }.ui-segmented-opt span { flex: 1; display: inline-flex; align-items: center; justify-content: center; height: 30px; padding: 0 14px; border-radius: var(--ui-radius-sm); white-space: nowrap; transition: background .15s, color .15s, box-shadow .15s;}.ui-segmented-opt:hover span { color: var(--ui-fg); }.ui-segmented-opt input:checked + span { background: var(--ui-surface); color: var(--ui-fg); box-shadow: var(--ui-shadow); }.ui-segmented-opt input:focus-visible + span { box-shadow: var(--ui-focus); }.ui-segmented-opt input:checked:focus-visible + span { box-shadow: var(--ui-shadow), var(--ui-focus); }.ui-segmented-sm .ui-segmented-opt span { height: 24px; padding: 0 10px; font-size: 13px; }.ui-segmented:has(input:disabled) { opacity: .55; }.ui-segmented:has(input:disabled) .ui-segmented-opt { cursor: not-allowed; }@media (prefers-reduced-motion: reduce) { .ui-segmented-opt span { transition: none; } }`view SegmentedControl(name: str, options: list = [], value: str? = null, label: str? = null, size: str = "md", full: bool = false, disabled: bool = false, on_change = null, class: str? = null) { style global segmented_control_css let opts = options | map((o) => type_of(o) == "str" ? { label: o, value: o } : o) let picked = value ?? (len(opts) > 0 ? opts[0].value : null) <div class={ui_cls(["ui-segmented", size == "sm" && "ui-segmented-sm", full && "ui-segmented-full", class])} role="radiogroup" aria-label={label}> {for o in opts => <label.ui-segmented-opt> <input type="radio" name={name} value={o.value} checked={o.value == picked} disabled={disabled} @change={(e) => { on_change && on_change(e.value) }}/> <span>{o.label}</span> </label>} </div>}Related
FilterTabsTabs with counts that filter a list, one selected, moved between with the arrow keys; a hidden input holds the key.5 views · 6 downloadsBirthdayInputDay, month and year drop-downs that give one date in a hidden input, the days following the month and the years the age allowed.5 views · 7 downloadsCheckboxCardsAny number of choices as cards with a title, a line of detail and a check, each a real checkbox a form sends.5 views · 6 downloadsChipGroupChips to pick one or several of, like filters: real radios or checkboxes that a form sends, with counts.6 views · 4 downloadsComboboxA text box that filters a list of options as you type, or asks for them (a server, a map service), with the arrow keys, Enter and Escape.5 views · 6 downloadsCountryPickerA drop-down of every country with its flag and ISO code, favourites first, sending the two-letter code with a form.5 views · 7 downloads
Thread
Thread 0 posts
On SegmentedControl: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.