Full walk of combobox (popover + editable input + listbox):
- Morfo: validated. 10 parts (Provider/Input/Trigger/Content/Viewport/
Item/Group/GroupHeading/Separator/Arrow). Single + multiple via
`data-type` on Provider, `aria-multiselectable` conditional on
`prop:multiple`. Item declares `data-state` checked/unchecked,
`data-value`, `data-highlighted`, `data-disabled`. Keyboard: 6 keys
but only 2 mutate state (Enter→select, Escape→close-delegated).
- Decision documented: combobox does NOT emit emerge events for
open/close/typing/filtering/highlighting — these are navigation
mechanics, not commits. Escape close is delegated to the popover
layer's `close-dismiss` event. The 2 `commit-*` events cover the
actual user intent (choosing an item).
- `texts.label` added with catalog entry. The toggle key already
existed.
Audit script refinements:
- Extended focus-move action list to cover listbox/menu/tablist
navigation (`next-item`, `prev-item`, `first-item`, `last-item`,
`next-tab`, `prev-tab`, `first-tab`, `last-tab`) plus the popover/
dialog actions that are delegated to the containing overlay layer
(`open`, `close`, `dismiss`, `toggle`). A combobox / select / menu
that hosts Escape→close is not under-declared if the overlay layer
owns the sema event.
README rewritten with canonical sections:
- `## Baseline` summarizing air + soma + morfo coverage.
- `## Comparativa` (was `## Comparison`) — table preserved.
- `## Decisiones` documenting the no-emerge-events policy, focus-only
keys, Control composition rationale, multi-select aria propagation.
- `## Gaps` (was `## Out of scope`) rewritten with disposition tokens
(implementar / diferir / descartar) per row. ClearTrigger and
Validation state get `implementar`; Modal+footer gets `descartar`
(combobox is inline by design, not a confirm-style picker).
Audit: PASS 6 → 7. No regressions; the relaxed focus-move list
benefits other listbox/menu components too (will surface on their
audits).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Opening, closing, typing, filtering and highlighting are navigation mechanics,
not committed choices. They do not emit Sema events.
## Out of scope (v2 roadmap)
- `ClearTrigger`: Ark and React Aria expose clear behavior. Deferred to Soma
because it mutates `value` and `inputValue`. Estimated cost: 80-140 lines
across Morfo/Soma/Eidos.
- `ScrollUpButton` / `ScrollDownButton`: Air and Bits expose them. Deferred
to Soma because the part needs scroll behavior and viewport awareness.
Estimated cost: 120-180 lines.
- `ContentStatic`: Air exposes a non-floating content variant. Deferred to
Soma because it changes topology and dismissal behavior. Estimated cost:
80-140 lines.
- `menuTrigger`, `autoHighlight`, `inlineAutocomplete`: Ark and React Aria
cover these input behaviors. Deferred to Soma because they affect open,
highlighted and input value state. Estimated cost: 180-260 lines.
- Custom/freeform value: React Aria supports custom values. Deferred to
Soma/Form because it changes value semantics and submitted form value.
Estimated cost: 140-220 lines.
- Validation state: Ark and React Aria expose validation. Deferred to
Field/Soma so Combobox consumes real field state instead of visual-only
invalid selectors. Estimated cost: 120-200 lines.
- Async/virtualized collections: Ark and React Aria cover this class of
behavior. Deferred to the collection/virtualization layer, not Eidos.
## Decisiones
- **No se emiten eventos sema en open/close/typing/filter/highlight**. Son
mecánicas de navegación, no compromisos del usuario. El sema sólo viaja
con `commit-select` y `commit-unselect` (la elección concreta).
- **Las teclas de navegación interna de la listbox son focus moves**:
ArrowDown/ArrowUp/Home/End mueven el highlight, no mutan el valor. El
audit las cuenta como focus-only — el único keystroke que mutea es
Enter (mapped a `select` → covered por `commit-select`).
- **Escape delega al popover**: la tecla cierra cerrando la layer
popover/dismissal; combobox no duplica el evento `close-dismiss` que ya
emite el popover.
- **`Combobox.Control` es composición Eidos-only**: agrupa Input + Trigger
+ Indicator dentro de un único chrome. No tiene contraparte en
morfo/soma porque es puramente layout visual.
- **`ItemIndicator` es Eidos-only**: el estado del item llega vía
`data-state` que el provider sopla; ItemIndicator sólo decide qué
glyph pintar en función de ese atributo.
- **Single vs multiple lo decide el consumer via prop `type`**: el morfo
expone `data-type` en Provider para que la receta pueda diferenciar.
- **Multi-select aria-multiselectable** se aplica condicionalmente
cuando `prop:multiple` es truthy.
## Gaps
| Gap | Disposición | Detalle |
| --- | --- | --- |
| `ClearTrigger` (Ark, React Aria) | **implementar** | Mutates `value` + `inputValue`. Estimación 80-140 LOC across Morfo/Soma/Eidos. Útil para todos los pickers con field input. |
| Custom / freeform value (react-aria) | **diferir** | Cambia value semantics + form value submitted. 140-220 LOC. |
| Validation state | **implementar** | Combobox debe consumir field state real cuando esté dentro de un `<Field>`. 120-200 LOC. Bloqueante para integración Form. |
| Async / virtualized collections | **descartar** | Belongs in collection/virtualization layer, no Eidos. Out of scope. |
| Modal mode + footer pattern (como pickers) | **descartar** | Combobox es inline por diseño — no es un picker con confirmación explícita. |