You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
|
|
5 months ago | |
|---|---|---|
| .. | ||
| README.md | 5 months ago | |
| combobox-arrow.svelte | 5 months ago | |
| combobox-clear-button.svelte | 5 months ago | |
| combobox-content.svelte | 5 months ago | |
| combobox-control.svelte | 5 months ago | |
| combobox-group-heading.svelte | 5 months ago | |
| combobox-group.svelte | 5 months ago | |
| combobox-indicator.svelte | 5 months ago | |
| combobox-input.svelte | 5 months ago | |
| combobox-item-indicator.svelte | 5 months ago | |
| combobox-item.svelte | 5 months ago | |
| combobox-selected-tag-remove.svelte | 5 months ago | |
| combobox-selected-tag.svelte | 5 months ago | |
| combobox-selected-tags.svelte | 5 months ago | |
| combobox-separator.svelte | 5 months ago | |
| combobox-trigger.svelte | 5 months ago | |
| combobox-viewport.svelte | 5 months ago | |
| combobox.css | 5 months ago | |
| combobox.svelte | 5 months ago | |
| context.svelte.ts | 5 months ago | |
| index.ts | 5 months ago | |
| types.ts | 5 months ago | |
README.md
Combobox
Eidos Combobox wraps Soma Combobox and keeps the component compound:
<Combobox> + Control, Input, Trigger, Indicator, Portal,
Content, Viewport, Item, ItemIndicator, Group, GroupHeading,
Separator and Arrow.
Baseline
- air: tenía un Combobox completo con input editable, popover listbox, groups/separators, hidden form value, scroll affordances y validation state. Es la referencia ergonomic.
- soma actual: cubre input value + open state + single/multiple values + hidden inputs + groups + floating + dismissal + keyboard nav.
- morfo: declara las parts y el contrato data-/aria-; eventos sema sólo
para
commit-selectycommit-unselect.
Comparativa
| Feature | Air | Radix | Bits | Ark | shadcn-svelte | React Aria | Soma | Eidos | Decision |
|---|---|---|---|---|---|---|---|---|---|
| Editable input + popup listbox | ✅ | ❌ | ✅ | ✅ | ⚠️ | ✅ | ✅ | ✅ | Covered with Input + Content; Radix has no dedicated Combobox primitive. |
| Compound anatomy | ✅ | ❌ | ✅ | ✅ | ⚠️ | ✅ | ✅ | ✅ | Eidos exposes one namespace and attached parts; the root is <Combobox> itself. |
| Input + trigger grouped visually | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Combobox.Control is Eidos-only layout. |
| Portal + floating content + arrow | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Soma owns Floating; Eidos consumes data-side and floating CSS vars. |
| Groups, headings and separators | ✅ | ❌ | ✅ | ✅ | ⚠️ | ✅ | ✅ | ✅ | Covered. |
| Selected item indicator | ⚠️ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ItemIndicator is Eidos-only; item state comes from Soma/Morfo. |
| Single select | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Covered. |
| Multiple select | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | Covered by Soma type="multiple"; Eidos only styles selected items. |
| Typeahead / filtering input | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Soma owns input value and filtering surface. |
| Hidden form value | ✅ | ❌ | ✅ | ✅ | ⚠️ | ✅ | ✅ | ✅ | Soma renders hidden inputs when name is passed. |
| Clear trigger | ❌ | ❌ | ⚠️ | ✅ | ❌ | ✅ | ❌ | ❌ | Defer to Soma; clearing changes value/input state. |
| Scroll up/down affordance parts | ✅ | ❌ | ✅ | ⚠️ | ❌ | ❌ | ❌ | ❌ | Defer to Soma; requires viewport scroll behavior. |
| Static/non-floating content | ✅ | ❌ | ⚠️ | ⚠️ | ❌ | ❌ | ❌ | ❌ | Defer to Soma if inline listbox topology is needed. |
| Menu trigger behavior | ❌ | ❌ | ⚠️ | ✅ | ❌ | ✅ | ❌ | ❌ | Defer to Soma; affects when open state changes. |
| Inline autocomplete / auto highlight | ❌ | ❌ | ⚠️ | ✅ | ❌ | ✅ | ❌ | ❌ | Defer to Soma; input behavior, not visual styling. |
| Custom value / freeform commit | ❌ | ❌ | ⚠️ | ⚠️ | ❌ | ✅ | ❌ | ❌ | Defer to Soma/Form; needs value semantics. |
| Validation state | ✅ | ❌ | ⚠️ | ✅ | ⚠️ | ✅ | ❌ | ❌ | Defer to Field/Soma; Eidos must not invent invalid state. |
| Async/virtualized collections | ❌ | ❌ | ⚠️ | ✅ | ❌ | ✅ | ❌ | ❌ | Belongs in collection/virtualization, not visual wrapper. |
References:
- Bits UI Combobox: https://bits-ui.com/docs/components/combobox
- Ark UI Combobox: https://ark-ui.com/docs/components/combobox
- shadcn-svelte Combobox: https://www.shadcn-svelte.com/docs/components/combobox
- React Aria ComboBox: https://react-spectrum.adobe.com/react-aria/ComboBox.html
- Radix Primitives overview: https://www.radix-ui.com/primitives/docs/overview/introduction
Layer split
| Layer | Owns |
|---|---|
| Morfo | Parts, ARIA/data contract, commit-select and commit-unselect events. |
| Soma | Input text, open state, single/multiple values, hidden form inputs, groups, floating, dismissal and keyboard navigation. |
| Sema | Subtle form commit sounds + tap haptic for select/unselect. Typing, filtering, open/close and highlight are intentionally silent. |
| Eidos | Control layout, size, variant, intent color, trigger icon, selected item indicator, panel recipe and animation. |
Morfo/Sema
| Event | Family | Verb | Intent | Target | Sequence | Reason |
|---|---|---|---|---|---|---|
commit-select |
commit |
select |
affirm |
item |
post |
User confirms an option. The concrete item is the perceived target. |
commit-unselect |
commit |
remove |
neutral |
item |
post |
Multiple deselect or single clear via allowDeselect. |
Opening, closing, typing, filtering and highlighting are navigation mechanics, not committed choices. They do not emit Sema events.
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-selectycommit-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 porcommit-select). - Escape delega al popover: la tecla cierra cerrando la layer
popover/dismissal; combobox no duplica el evento
close-dismissque ya emite el popover. Combobox.Controles composición Eidos-only: agrupa Input + Trigger- Indicator dentro de un único chrome. No tiene contraparte en morfo/soma porque es puramente layout visual.
ItemIndicatores Eidos-only: el estado del item llega víadata-stateque 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 exponedata-typeen Provider para que la receta pueda diferenciar. - Multi-select aria-multiselectable se aplica condicionalmente
cuando
prop:multiplees 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. |
ScrollUpButton / ScrollDownButton (air, bits) |
diferir | Requiere scroll behavior + viewport awareness en soma. Sin demanda actual. 120-180 LOC. |
ContentStatic (variante inline, no floating) |
diferir | Cambia topología y dismissal. Sin caso de uso. 80-140 LOC. |
menuTrigger / autoHighlight / inlineAutocomplete (ark, react-aria) |
diferir | Afectan open/highlighted/input value state. 180-260 LOC. Esperar caso real. |
| 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. |
Usage
<script lang="ts">
import { Combobox } from '$uix/eidos/components/combobox'
let value = $state<string[]>([])
let inputValue = $state('')
</script>
<Combobox bind:value bind:inputValue name="framework" size="md" variant="surface" color="primary">
<Combobox.Control>
<Combobox.Input placeholder="Search framework..." />
<Combobox.Trigger>
<Combobox.Indicator />
</Combobox.Trigger>
</Combobox.Control>
<Combobox.Portal>
<Combobox.Content sideOffset={6}>
<Combobox.Viewport>
<Combobox.Group>
<Combobox.GroupHeading>Frontend</Combobox.GroupHeading>
<Combobox.Item value="svelte" label="Svelte">
<span>Svelte</span>
<Combobox.ItemIndicator />
</Combobox.Item>
<Combobox.Item value="react" label="React">
<span>React</span>
<Combobox.ItemIndicator />
</Combobox.Item>
<Combobox.Item value="vue" label="Vue">
<span>Vue</span>
<Combobox.ItemIndicator />
</Combobox.Item>
<Combobox.Item value="solid" label="Solid">
<span>Solid</span>
<Combobox.ItemIndicator />
</Combobox.Item>
</Combobox.Group>
<Combobox.Separator />
<Combobox.Group>
<Combobox.GroupHeading>Runtime</Combobox.GroupHeading>
<Combobox.Item value="node" label="Node">
<span>Node</span>
<Combobox.ItemIndicator />
</Combobox.Item>
<Combobox.Item value="deno" label="Deno">
<span>Deno</span>
<Combobox.ItemIndicator />
</Combobox.Item>
</Combobox.Group>
</Combobox.Viewport>
<Combobox.Arrow />
</Combobox.Content>
</Combobox.Portal>
</Combobox>