Slider
A range input with its value shown beside the label and the track filled up to the thumb.
Demo
Showing hotels under $120 a night.
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/slider.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/slider.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/slider.qwe
component SliderDemoLive() { state price = 120 <div.demo-stack> <Slider name="price" label="Max price" min={10} max={500} step={10} value={price} prefix="$" on_change={(v) => { price = v }}/> <p.demo-note>{price >= 300 ? "Showing everything." : `Showing hotels under $${price} a night.`}</p> </div>}view SliderDemo() = <div.demo-stack.demo-narrow> <Slider name="volume" label="Volume" value={60} format="%"/> <Slider name="radius" label="Search radius" min={1} max={50} value={5} format=" km" hint="How far from the address to look."/> <SliderDemoLive/> <Slider name="locked" label="Locked" value={30} disabled={true}/> </div>Source
ui/slider.qwe
// Slider: a labelled range, on the browser's own <input type="range">, showing// its value as it moves. It works in a plain <form> and with the arrow keys, Home// and End; the track is filled up to the thumb.//// <Slider name="volume" label="Volume" value={60} format="%"/>// <Slider name="price" label="Max price" min={10} max={500} step={10} value={120} prefix="$"// on_change={(v) => { max_price = v }}/>//// min, max, step: as on the input (0, 100, 1). value: where it starts.// prefix / format: text before / after the number shown ("$", "%", " kg").// show_value: false hides the number. hint, disabled and field (the id, default// "slider-" + name) as in TextField. on_change(value): inside one of your// components, given the number, as it moves.import ui/theme.qwe use ui_clslet slider_css = css`.ui-slider { display: grid; gap: 6px; min-width: 0; font: 14px/1.4 var(--ui-font); color: var(--ui-fg); }.ui-slider-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }.ui-slider-label { font-size: 13px; font-weight: 500; }.ui-slider-value { font-size: 13px; color: var(--ui-muted); font-variant-numeric: tabular-nums; }.ui-slider input { --ui-slider-track: var(--ui-subtle); appearance: none; display: block; width: 100%; height: 22px; margin: 0; background: transparent; cursor: pointer;}.ui-slider input:focus { outline: none; }.ui-slider input::-webkit-slider-runnable-track { height: 6px; border-radius: 999px; box-shadow: inset 0 0 0 1px var(--ui-border); background: linear-gradient(to right, var(--ui-accent) var(--ui-slider-fill, 0%), var(--ui-slider-track) var(--ui-slider-fill, 0%));}.ui-slider input::-moz-range-track { height: 6px; border-radius: 999px; background: var(--ui-slider-track); box-shadow: inset 0 0 0 1px var(--ui-border); }.ui-slider input::-moz-range-progress { height: 6px; border-radius: 999px; background: var(--ui-accent); }.ui-slider input::-webkit-slider-thumb { appearance: none; width: 18px; height: 18px; margin-top: -6px; border-radius: 50%; background: var(--ui-surface); border: 2px solid var(--ui-accent); box-shadow: var(--ui-shadow); transition: transform .12s, box-shadow .15s;}.ui-slider input::-moz-range-thumb { width: 14px; height: 14px; border-radius: 50%; background: var(--ui-surface); border: 2px solid var(--ui-accent); box-shadow: var(--ui-shadow);}.ui-slider input:hover::-webkit-slider-thumb { transform: scale(1.1); }.ui-slider input:focus-visible::-webkit-slider-thumb { box-shadow: var(--ui-focus); }.ui-slider input:focus-visible::-moz-range-thumb { box-shadow: var(--ui-focus); }.ui-slider-hint { margin: 0; font-size: 13px; color: var(--ui-muted); }.ui-slider-disabled { opacity: .55; }.ui-slider-disabled input { cursor: not-allowed; }.ui-slider-disabled input:hover::-webkit-slider-thumb { transform: none; }@media (prefers-reduced-motion: reduce) { .ui-slider input::-webkit-slider-thumb { transition: none; } }`component Slider(name: str? = null, label: str? = null, value: num = 50, min: num = 0, max: num = 100, step: num = 1, prefix: str = "", format: str = "", show_value: bool = true, hint: str? = null, disabled: bool = false, field: str? = null, on_change = null, class: str? = null) { style global slider_css state v = value let id = field ?? `slider-${name ?? re.replace(lower(label ?? "range"), "[^a-z0-9]+", "-")}` let fill = max > min ? round((v - min) * 100 / (max - min), 2) : 0 let shown = `${prefix}${v}${format}` <div class={ui_cls(["ui-slider", disabled && "ui-slider-disabled", class])}> {(label != null || show_value) && <div.ui-slider-head> {label != null && <label.ui-slider-label for={id}>{label}</label>} {show_value && <output.ui-slider-value for={id} id={`${id}-out`}>{shown}</output>} </div>} <input type="range" id={id} name={name} min={min} max={max} step={step} value={v} disabled={disabled} style={`--ui-slider-fill: ${fill}%`} aria-valuetext={prefix != "" || format != "" ? shown : null} aria-describedby={hint != null ? `${id}-hint` : null} @input={(e) => { v = float(e.value) on_change && on_change(v) }}/> {hint != null && <p.ui-slider-hint id={`${id}-hint`}>{hint}</p>} </div>}Related
AddressFormStreet, city, region, postcode and country in the usual layout, with the autocomplete names browsers fill from.5 views · 7 downloadsAutocompleteA free-text box that offers matches as one types, with the arrow keys and Enter; whatever is typed may stay.5 views · 7 downloadsCardInputA payment card as it is typed: the number grouped, the brand, MM/YY and the code, with typos said.6 views · 6 downloadsCharacterCounterA text box or textarea with a live 120 / 280 count that warns near the limit, as a hard stop or a soft one that blocks sending.5 views · 6 downloadsCheckboxA checkbox drawn by CSS on a real input, with an indeterminate state, and a group of them in a fieldset.5 views · 6 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 downloads
Thread
Thread 0 posts
On Slider: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.