picker
picker3d — choosing one of many. A select is right for five options and a
scroll for five hundred; this is the other end. It composes controls that
already exist — a table for the virtualized, filterable list, an
inputField for the query, a select3d for the group —
so there is no new machinery here, only the arrangement.
Demo
Five hundred and sixty-one names, in the shape a real model library has them:
family_thing-variant. Type to narrow, or pick a family first.
import { panel3d, label3d, picker3d } from 'tosijs-3d'
import { elements } from 'tosijs'
const { div } = elements
const readout = div({ class: 'readout' }, 'nothing picked')
// Stand-in for a library catalogue — same naming shape, generated so the demo
// carries no 561-entry literal.
const FAMILIES = ['commercial', 'residential', 'roads', 'car', 'nature', 'furniture', 'industry', 'signage', 'debris']
const KINDS = ['building', 'block', 'tower', 'bend', 'straight', 'corner', 'crossing', 'barrier', 'lamp', 'bench', 'crate']
const options = []
for (const f of FAMILIES) {
for (const k of KINDS) {
for (const v of ['a', 'b', 'c', 'd', 'e']) {
options.push({ value: `${f}_${k}-${v}`, label: `${k}-${v}`, group: f })
}
}
}
preview.append(
panel3d({ width: 320 },
label3d({ text: `library — ${options.length} models` }),
picker3d({
label: 'model',
value: options[0].value,
options,
handleChange: (v) => { readout.textContent = `picked: ${v}` },
})
),
readout
)
.preview { padding: 12px; }
.readout {
padding: 10px 2px;
font-family: ui-monospace, monospace;
color: #9aa4b2;
}
Why not a bigger popup
tosijs-3d-ensemble put it exactly, having landed 561 Kenney models into a
palette built on select3d:
The original item asked for "a real popup select" because
select3dis a cycler. With 561 entries that understates it: even a flat popup is a scroll, not a choice.
A list you scroll is a list you read. The fix is not more pixels, it is fewer candidates — so the popup's job is to get from five hundred to five before you look at anything.
Two levels, without a tree
Their content's own names give a natural taxonomy —
commercial_building-a, roads_road-bend-barrier, car_debris-bolt — and the
obvious response is a tree widget. This does it with a group chooser and a
filter field instead, which reaches the same place with two controls you
already know how to drive.
That is not only a simplicity argument. A tree needs a pointer to expand and collapse; two flat controls need a D-pad and a text field, both of which work from a controller in a headset. The tree would have been the version that only works flat.
Filtering is AND across terms, over the whole name
Typing commercial bend matches commercial_road-bend-a, because every
space-separated term must appear somewhere in the group, the label or the value.
One term over a concatenated string would make word ORDER matter, which is the
thing nobody can guess: an author who thinks "the bend, in commercial" would
type it that way and find nothing.
Selection survives filtering — that is table's rule, and it matters most here: a search box that discards what you had chosen is destructive rather than helpful.
It asks for a LAYER, and copes with being refused
A five-hundred-row list is bigger than most panels, and showPopup caps to the
panel's own bounds — which on a short panel gives a list squeezed to nothing,
placed over the control it belongs to. So this asks for showLayer (unbounded,
above the panel) and falls back to a bounded popup sized to what is actually
available. Same reasoning as the on-screen keyboard, and the same fallback.
Options
| option | ||
|---|---|---|
options |
string[], or {value, label?, group?}[] |
|
value |
'' |
the current selection |
label |
— | drawn to the left of the closed control |
filter |
'auto' |
'auto' shows the query field above filterAbove options |
filterAbove |
12 |
where 'auto' starts showing it |
groups |
'auto' |
'auto' shows the group chooser when there is more than one group |
placeholder |
'choose…' |
shown when nothing is selected |
handleChange |
the chosen value; fires on pick, and the popup closes |