Select
A drop-down on the browser's own select: options, groups, a placeholder, and a list box for several.
Demo
Where you pay tax.
Team needs a card on file.
Cmd- or Ctrl-click to choose several.
Chosen: Rust
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/select.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/select.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/select.qwe
component SelectDemoLive() { state picked = ["Rust"] <div.demo-stack> <Select name="langs" label="Languages you know" multiple={true} rows={4} value={picked} options={["Quillo", "Rust", "Python", "JavaScript", "Go"]} hint="Cmd- or Ctrl-click to choose several." on_change={(vs) => { picked = vs }}/> <p.demo-note>Chosen: {len(picked) > 0 ? join(picked, ", ") : "none"}</p> </div>}view SelectDemo() = <div.demo-stack.demo-narrow> <Select name="country" label="Country" placeholder="Choose a country" required={true} options={["France", "Japan", "Peru", "Kenya"]} hint="Where you pay tax."/> <Select name="plan" label="Plan" value="team" error="Team needs a card on file." options={[{ label: "Free", value: "free" }, { label: "Team, $8 a seat", value: "team" }, { label: "Enterprise (talk to us)", value: "ent", disabled: true }]}/> <div.demo-row> <Select name="tz" label="Time zone" size="sm" value="Tokyo" groups={[{ label: "Europe", options: ["London", "Paris", "Rome"] }, { label: "Asia", options: ["Seoul", "Tokyo"] }]}/> <Select name="locked" label="Region" size="sm" options={["EU"]} disabled={true}/> </div> <SelectDemoLive/> </div>Source
ui/select.qwe
// Select: a labelled drop-down, on the browser's own <select>, so it works in a// plain <form>, with the keyboard and with each phone's own picker.//// <Select name="country" label="Country" placeholder="Choose one" options={["France", "Japan", "Peru"]}/>// <Select name="plan" label="Plan" value="team"// options={[{ label: "Free", value: "free" }, { label: "Team", value: "team" },// { label: "Enterprise", value: "ent", disabled: true }]}/>// <Select name="tz" label="Time zone" groups={[{ label: "Europe", options: ["Paris", "Rome"] },// { label: "Asia", options: ["Tokyo"] }]}/>//// options: a list of strings, or of { label, value, disabled? }. groups: a list of// { label, options }, shown under their headings (after any plain options).// value: the chosen value; with multiple={true}, a list of them, and it shows as a// list box (rows: how many lines). placeholder: a first line that cannot be chosen,// shown until something is. hint, error, required, disabled, field (the id,// default "select-" + name) and size ("sm" or "md") are as in TextField.// on_change(value): inside one of your components, given the chosen value (a list// of them when multiple).import ui/theme.qwe use ui_clslet select_css = css`.ui-select { display: grid; gap: 6px; min-width: 0; font: 14px/1.4 var(--ui-font); color: var(--ui-fg); }.ui-select-label { font-size: 13px; font-weight: 500; color: var(--ui-fg); }.ui-select-req { margin-left: 2px; color: var(--ui-danger); }.ui-select-box { position: relative; display: block; }.ui-select-box::after { content: ""; position: absolute; right: 14px; top: 50%; width: 7px; height: 7px; margin-top: -6px; border: solid var(--ui-muted); border-width: 0 1.5px 1.5px 0; transform: rotate(45deg); pointer-events: none;}.ui-select select { appearance: none; display: block; width: 100%; height: 38px; padding: 0 36px 0 12px; background: var(--ui-surface); color: var(--ui-fg); font: 14px var(--ui-font); border: 1px solid var(--ui-border); border-radius: var(--ui-radius-sm); cursor: pointer; text-overflow: ellipsis; transition: border-color .15s, box-shadow .15s;}.ui-select select:hover { border-color: var(--ui-strong); }.ui-select select:focus-visible { outline: none; border-color: var(--ui-accent); box-shadow: var(--ui-focus); }.ui-select select:has(option[value=""]:checked) { color: var(--ui-muted); }.ui-select option { color: var(--ui-fg); background: var(--ui-surface); }.ui-select option:disabled { color: var(--ui-muted); }.ui-select-multi .ui-select-box::after { display: none; }.ui-select-multi select { height: auto; padding: 4px; }.ui-select-multi option { padding: 5px 8px; border-radius: 4px; }.ui-select-multi option:checked { background: var(--ui-accent-soft); color: var(--ui-fg); }.ui-select-sm select { height: 30px; padding: 0 30px 0 10px; font-size: 13px; }.ui-select-sm .ui-select-box::after { right: 11px; width: 6px; height: 6px; margin-top: -5px; }.ui-select-invalid select, .ui-select-invalid select:hover { border-color: var(--ui-danger); }.ui-select-invalid select:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--ui-danger) 30%, transparent); }.ui-select-hint, .ui-select-error { margin: 0; font-size: 13px; color: var(--ui-muted); }.ui-select-error { color: var(--ui-danger); }.ui-select-disabled { opacity: .55; }.ui-select-disabled select { cursor: not-allowed; }.ui-select-disabled select:hover { border-color: var(--ui-border); }@media (prefers-reduced-motion: reduce) { .ui-select select { transition: none; } }`// An option as { label, value, disabled }, from a string or a map.fn select_option(o) = type_of(o) == "map" ? { label: o.label ?? str(o.value), value: str(o.value ?? o.label), disabled: o.disabled ?? false } : { label: str(o), value: str(o), disabled: false }view Select(name: str? = null, label: str? = null, value: str | num | list | null = null, placeholder: str? = null, hint: str? = null, error: str? = null, required: bool = false, disabled: bool = false, field: str? = null, size: str = "md", options: list = [], groups: list = [], multiple: bool = false, rows: int? = null, on_change = null, class: str? = null) { style global select_css let id = field ?? `select-${name ?? re.replace(lower(label ?? "field"), "[^a-z0-9]+", "-")}` let described = ui_cls([hint != null && `${id}-hint`, error != null && `${id}-error`]) let chosen = value == null ? [] : type_of(value) == "list" ? value | map((v) => str(v)) : [str(value)] let opt = (o) => { let it = select_option(o) <option value={it.value} disabled={it.disabled} selected={contains(chosen, it.value)}>{it.label}</option> } <div class={ui_cls(["ui-select", size == "sm" && "ui-select-sm", multiple && "ui-select-multi", error != null && "ui-select-invalid", disabled && "ui-select-disabled", class])}> {label != null && <label.ui-select-label for={id}> {label}{required && <span.ui-select-req aria-hidden="true">*</span>} </label>} <span.ui-select-box> <select id={id} name={name} required={required} disabled={disabled} multiple={multiple} size={rows} aria-invalid={error != null ? "true" : null} aria-describedby={described != "" ? described : null} @change={(e) => { on_change && on_change(multiple ? js("Array.from", e.target.selectedOptions) | map((o) => o.value) : e.value) }}> {placeholder != null && !multiple && <option value="" disabled selected={len(chosen) == 0}>{placeholder}</option>} {for o in options => opt(o)} {for g in groups => <optgroup label={g.label}>{for o in g.options => opt(o)}</optgroup>} </select> </span> {hint != null && <p.ui-select-hint id={`${id}-hint`}>{hint}</p>} {error != null && <p.ui-select-error id={`${id}-error`} role="alert">{error}</p>} </div>}Related
BirthdayInputDay, 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 downloadsDependentSelectTwo drop-downs where the second's options depend on the first, both real selects a form sends.5 views · 7 downloads
Thread
Thread 0 posts
On Select: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.