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.
svelte-kit-vice/src/uix/eidos/components/menubar
dev e09170f9da
feat(uix): Menubar.Panel — una entrada puede abrir un panel (role=dialog)
2 months ago
..
README.md feat(uix): Menubar.Panel — una entrada puede abrir un panel (role=dialog) 2 months ago
index.ts feat(uix): Menubar.Panel — una entrada puede abrir un panel (role=dialog) 2 months ago
menubar-content.svelte fix(uix): menubar menu gap = --space-1 (4px) from the bar, not the trigger 3 months ago
menubar-context.ts feat(eidos): port Menubar (desktop File/Edit/View bar) 5 months ago
menubar-menu.svelte feat(eidos): port Menubar (desktop File/Edit/View bar) 5 months ago
menubar-panel.svelte feat(uix): Menubar.Panel — una entrada puede abrir un panel (role=dialog) 2 months ago
menubar-trigger.svelte refactor(menubar): concentric trigger radius via [data-shape-nest] (A2) 4 months ago
menubar.css feat(uix): Menubar.Panel — una entrada puede abrir un panel (role=dialog) 2 months ago
menubar.svelte perf(eidos): code-split 7 menu recipes + order-independent menu-indicator (Phase 3, batch 2) 4 months ago
types.ts feat(uix): Menubar.Panel — una entrada puede abrir un panel (role=dialog) 2 months ago

README.md

Menubar (eidos)

Fecha de revisión: 2026-07-10 (dossier S4, re-auditoría 2026-07-07).

Barra de menús de escritorio (File / Edit / View / …): triggers de nivel superior con roving focus y, por menú, el árbol interno completo de DropdownMenu reutilizado literalmente. Soma posee valor (qué menú está abierto), teclado y ARIA; eidos añade size y la receta de la barra.

Contrato headless: soma README · Morfo: morfo/components/menubar.ts · APG: menubar.

Baseline

Portado del baseline air (la escala de tallas sm|md|lg espeja la de air — documentado en types: un menubar es chrome de escritorio; xs amontonaría los triggers y xl no tiene caso). Los cambios estructurales sobre air: las partes internas por-menú ya no son propias — son re-exports de DropdownMenu (ver Decisiones).

Superficie

<Menubar bind:value size="md">
	<Menubar.Menu value="file">
		<Menubar.Trigger>File</Menubar.Trigger>
		<Menubar.Content>
			<Menubar.Item onSelect={newFile}>New…</Menubar.Item>
			<Menubar.Separator />
			<Menubar.Sub>
				<Menubar.SubTrigger>Export</Menubar.SubTrigger>
				<Menubar.SubContent>…</Menubar.SubContent>
			</Menubar.Sub>
		</Menubar.Content>
	</Menubar.Menu>
	<Menubar.Menu value="edit">…</Menubar.Menu>
</Menubar>
  • Partes propias: root (role="menubar", horizontal), Menu (scope por menú), Trigger (role="menuitem", data-state/data-highlighted/ data-menubar-value), Content (panel de menú) y Panel (superficie role="dialog" para controles de formulario — alternativa a Content).
  • Partes re-exportadas de DropdownMenu: Item, Group, GroupHeading, Separator, CheckboxItem, CheckboxGroup, RadioGroup, RadioItem, Sub, SubTrigger, SubContent, Arrow.
  • Props headless: value/onValueChange (menú abierto), dir, loop; per-Menu: onOpenChange/onOpenChangeComplete.
  • Props eidos: size (sm|md|lg, responsive; cascada a los paneles vía contexto).
  • Teclado del trigger: Enter/Space/ArrowDown abren; ArrowLeft/Right mueven entre triggers; Home/End extremos; dentro del menú rige el teclado de DropdownMenu.

Comparativa

Capacidad UIX Radix Bits UI shadcn React Aria
Menubar dedicado ✓ ✓ ✓ ✓ (Radix) No (Menu suelto)
Roving + hover-open tras engage ✓ ✓ ✓ ✓ —
Partes por-menú = las del dropdown ✓ (re-export literal) ✓ (paridad API) ✓ ✓ —
Checkbox/Radio items + submenús ✓ ✓ ✓ ✓ —
loop + RTL ✓ ✓ ✓ ✓ —
Evento perceptivo en el select ✓ (commit-select) No No No —
Superficie panel (role=dialog) por entrada ✓ (Menubar.Panel) No No No No

Menubar.Panel es una EXTENSIÓN sobre las referencias headless (ninguna lo modela): los precedentes son las barras reales que mezclan menús y paneles — los menu extras de macOS (Wi-Fi / Control Center: trigger en barra, panel no-menu, Esc cierra, las flechas operan el panel), las galerías del ribbon de Office y los paneles de barra de Canva.

Referencias: Radix Menubar · Bits UI Menubar · APG Menubar.

Decisiones

  • Pack sema propio con la razón in-place (header del morfo): "subtle commit + tap haptic for high-frequency top-level menu triggers" — un menubar se pulsa decenas de veces por sesión; el carácter baja la intensidad en vez de subirla.
  • UN evento propio (commit-select, affirm, sobre el trigger): los ítems, checkbox/radio y submenús DELEGAN en el DropdownMenu compuesto (quinta cita del patrón de delegación del catálogo) — sus eventos sema son los del dropdown.
  • Re-export literal de las partes internas en eidos: Menubar.Item ES DropdownMenu.Item, etc. — el partial compartido menu-indicator aplica automáticamente y un fix visual del dropdown alcanza al menubar sin drift.
  • data-menubar-value en el trigger enlaza trigger↔menú para el estado controlado (value/onValueChange).
  • Menubar.Panel — superficie de formulario por entrada (2026-07-16, encargo del editor palabras panelMode='menubar' v2, componente genérico). Motivo: el Content de menú intercepta ↑↓/Home/End + typeahead (navegación de items) y eso rompe cualquier control de formulario compuesto dentro. El Panel es un part propio del morfo (data-menubar-panel, role="dialog", aria-labelledby→trigger; el trigger pasa a aria-haspopup="dialog") que NO añade teclado propio: las teclas dentro pertenecen a sus controles (las flechas ←→ NO cruzan menús desde dentro — mueven carets/sliders; Esc devuelve a la barra). La coordinación de barra (roving, hover-slide, keyboard-slide, commit-select, un-abierto-a-la-vez) queda intacta porque vive en el Trigger/Provider. Presencia = tipo (N-7): componer <Panel> en vez de <Content> declara la superficie. Cableado: reusa el shell flotante del DropdownMenu anidado (anchor del trigger, presence, canal contentId/setContentRef), con FocusScope/Dismissal propios y SIN TextSelection (los inputs necesitan selección) ni ScrollLock. Foco: apertura por teclado → primer control; por puntero → la superficie (hover jamás roba el foco a un input). Elevación vía data-depth="overlay" + tokens públicos --menubar-panel-* (z · min-width · max-height · padding · radius).
  • Mega-panel = customAnchor={barra} a secas (2026-07-17). El ancho es automático: soma pre-mide el anchor (--menubar-panel-anchor-width en el elemento) y la recipe dimensiona la superficie data-anchored con contain: inline-size — el positioner flotante mide el WRAPPER (min-width: max-content) y la intrínseca de un grid (suma de tracks) lo inflaba, volteando el placement; el containment fija la contribución al ancho usado (en modo anclado el ancho viene del anchor, nunca del contenido). Regla de consumo: el style del Panel viste al POSITIONER (mismo plumbing que todo Content del catálogo) — el layout del contenido (grid/flex) va en un elemento INTERNO.
  • La apertura sigue al roving por teclado (2026-07-16): con un menú abierto, ArrowLeft/Right/Home/End sobre los triggers deslizan la apertura al destino — el espejo de teclado del hover-follow (la doctrina del commit-select ya lo prometía). El slide vive en la navegación explícita del provider, no en onfocus: un listener de focus no distingue el roving del focus-return del FocusScope al cerrarse el menú que se desliza. Ese focus-return se suprime en <Menubar.Content> (patrón Radix: preventDefault en onCloseAutoFocus cuando otro menú del menubar queda abierto); el cierre simple (Escape / outside / select) conserva el retorno de foco normal.
  • Toda apertura por teclado aterriza en el PRIMER ITEM activo (2026-07-16, decisión de usuario tras confusión real: un panel abierto sin opción activa no dice cómo navegar). Cubre ArrowDown/Enter/Space en el trigger, el slide ←→/Home/End entre triggers y el cross-menu desde dentro de un panel — la señal viaja como openReason: 'keyboard' del menubar al DropdownMenu interno, cuyo autofocus enfoca getItems()[0] en vez del panel (APG "focus first item"; Word hace lo mismo en el slide). Las aperturas por PUNTERO (click, hover-follow) mantienen el foco en el panel: ahí resalta el hover y un item pre-activado sería una fila fantasma bajo el cursor.

Gaps

Gap Disposición Detalle
Tests del wrapper eidos (el soma tiene suite ✓) diferir Pasada SYS-2 conjunta.
API extendida sobre las referencias (p. ej. trigger-on-hover configurable) diferir Paridad Radix/Bits ya alcanzada; sin caso real.
Intent del commit-select fijo en affirm descartar Seleccionar una entrada de menú no carga evaluación variable; el intent por-evento es intrínseco (doctrina per-event intent).
Typeahead a nivel de barra (menubar cerrado salta al trigger por inicial) diferir APG lo lista; ni Radix ni Bits lo implementan (el typeahead vive dentro del menú abierto, ✓ aquí). Paridad mantenida; sin caso real.
ArrowUp en el trigger abre enfocando el último item diferir Radix lo soporta; exige que el DropdownMenu compuesto acepte "open with last item highlighted". Se aborda en el dropdown, no con un hack local.

Referencias

Powered by TurnKey Linux.