1# llimphi-widget-select23Modern *select* / dropdown control.
45Modelled as a **gradient of complexity**: the same piece serves everything from
6the dumbest select to one with async loading and badges, without changing widget
7— you just switch layers on.
89```text10 ┌──────────────────────────────┐
11 │ ◷ Pending ● ▾ │ ← trigger (closed)
12 └──────────────────────────────┘
13 │ click → view_overlay
14 ▼
15 ┃ ⌕ Search… ← search box (layer 2)
16 ┃ ✓ ◷ Pending 12 ← rich item: check · icon · label · badge (layer 3)
17 ┃ ◔ In progress 3
18 ┃ ◉ Done · ← dot badge
19 ┃ ─────────────────────
20 ┃ ⟳ Loading… ← async state (layer 4): Loading / Error+retry / empty
21```2223## The four layers24251. **Simple select.** `SelectItem::new` + `select_trigger_view` (the closed
26 button showing the choice) + `select_menu_view` with `SelectPhase::Ready`
27 mounted in `App::view_overlay`. `on_pick(idx)` returns the **original** index
28 of the chosen item; the app stores it and closes the menu. That is all.
292. **Searchable (combobox).** Set `searchable: true`. The box on top paints
30 `query`; the app routes typing through `on_key` into a `TextInputState` and
31 re-filters with `filter`. `step_active` moves the highlight with ↑/↓ skipping
32 disabled entries; `resolve` translates the highlighted position back to the
33 original index for Enter.
343. **Rich items + badges.** Every `SelectItem` carries `icon`, `sublabel` and a
35 `SelectBadge` (a `12` count, a `beta` text chip, or a status dot) — reusing
36 `llimphi-widget-badge`. Multiple selection falls out for free: pass several
37 indices in `selected` and the menu paints a ✓ on each (the toggle/close
38 semantics are the app's call).
394. **Async loading.** The menu does not require having the data: `SelectPhase`
40 distinguishes `Loading` (a "Loading…" row), `Error` (message + an `on_retry`
41 action) and `Ready`. The pattern: the app opens the menu, fires
42 `Handle::spawn`, paints `Loading`, and switches to `Ready` when the worker
43 returns. A *generation* (`u64`) discards stale answers if the user reopened or
44 retyped — see the `select_demo` example.
4546The look is that of `context-menu`: a rounded panel with a hairline border, rows
47as pills with a soft hover, an accent indicator on the active row, and a
48full-screen scrim that dismisses on click-outside.
4950---
5152Part of **llimphi** — see [llimphi](../../README.md).
README.md
llimphi-widget-select
Modern select / dropdown control.
Modelled as a gradient of complexity: the same piece serves everything from
the dumbest select to one with async loading and badges, without changing widget
— you just switch layers on.
Simple select.SelectItem::new + select_trigger_view (the closed
button showing the choice) + select_menu_view with SelectPhase::Ready
mounted in App::view_overlay. on_pick(idx) returns the original index
of the chosen item; the app stores it and closes the menu. That is all.
Searchable (combobox). Set searchable: true. The box on top paints
query; the app routes typing through on_key into a TextInputState and
re-filters with filter. step_active moves the highlight with ↑/↓ skipping
disabled entries; resolve translates the highlighted position back to the
original index for Enter.
Rich items + badges. Every SelectItem carries icon, sublabel and a
SelectBadge (a 12 count, a beta text chip, or a status dot) — reusing
llimphi-widget-badge. Multiple selection falls out for free: pass several
indices in selected and the menu paints a ✓ on each (the toggle/close
semantics are the app's call).
Async loading. The menu does not require having the data: SelectPhase
distinguishes Loading (a "Loading…" row), Error (message + an on_retry
action) and Ready. The pattern: the app opens the menu, fires
Handle::spawn, paints Loading, and switches to Ready when the worker
returns. A generation (u64) discards stale answers if the user reopened or
retyped — see the select_demo example.
The look is that of context-menu: a rounded panel with a hairline border, rows
as pills with a soft hover, an accent indicator on the active row, and a
full-screen scrim that dismisses on click-outside.