componente · @solu30/ui-kit/navigation
ContextualSidebarContent
El cuerpo del ContextualSidebar sin su marco, para hosts que ya tienen su propio aside.
5 sitios de import en 3 de 15 verticales — un archivo que la importa cuenta una vez, monte una instancia o veinte.
- dietaly
- gestorsalon
- raquetea
La pieza, con lugar de pantalla
Cargando el espécimen…
export function ContextualSidebarContentSpecimen() {
const [activo, setActivo] = useState('calendar')
const [openState, setOpenState] = useState<ContextualSidebarOpenState>({
sections: { sales: true, clients: false, finance: false },
groups: {},
})
const item = (id: string, label: string, icono: React.ReactNode, badge?: number) => ({
type: 'item' as const,
item: { id, label, icon: icono, ...(badge ? { badge } : {}) },
})
const sections: ContextualSidebarSection[] = [
{
id: 'dashboard',
label: 'Inicio',
collapsible: false,
content: [item('dashboard', 'Inicio', <IconHome className="h-4 w-4" />)],
},
{
id: 'calendar',
label: 'Eventos',
collapsible: false,
decorativeAccent: 'accent-1',
content: [item('calendar', 'Eventos', <IconGrid className="h-4 w-4" />)],
},
{
id: 'sales',
label: 'Ventas',
icon: <IconBriefcase className="h-4 w-4" />,
decorativeAccent: 'accent-3',
content: [
item('pos', 'Punto de Venta', <IconBriefcase className="h-4 w-4" />),
item('comandas', 'Comandas', <IconClipboard className="h-4 w-4" />, 4),
item('productos', 'Productos', <IconGrid className="h-4 w-4" />),
],
},
{
id: 'clients',
label: 'Clientes',
icon: <IconTeam className="h-4 w-4" />,
decorativeAccent: 'accent-4',
content: [
item('crm', 'Contactos', <IconTeam className="h-4 w-4" />),
item('consultas', 'Consultas', <IconInbox className="h-4 w-4" />, 2),
],
},
{
id: 'finance',
label: 'Finanzas',
icon: <IconChart className="h-4 w-4" />,
decorativeAccent: 'accent-6',
content: [
item('cajas', 'Cajas', <IconChart className="h-4 w-4" />),
item('financeCollections', 'Cobros', <IconChart className="h-4 w-4" />),
],
},
]
return (
<Stage label="ContextualSidebarContent · el cajón de navegación móvil de GestorSalon" tall wide>
<div className="flex flex-col items-center gap-2 px-1 py-1">
<PhoneFrame className="w-[240px]">
<div className="flex h-[360px] flex-col overflow-hidden bg-bkg">
<div className="flex items-center justify-between border-b border-content/10 px-3 py-2.5">
<span className="text-xs font-medium text-content">GestorSalón</span>
<span className="text-[11px] text-content/50">cerrar</span>
</div>
<div className="flex min-h-0 flex-1 flex-col overflow-y-auto">
<ContextualSidebarContent
sections={sections}
activeItemId={activo}
onNavigate={(it) => setActivo(it.id)}
collapsed={false}
openState={openState}
onOpenStateChange={setOpenState}
decorativeGlow={false}
/>
</div>
</div>
</PhoneFrame>
<p className="max-w-[380px] text-center text-[11px] text-content/60">
Mismo árbol que el sidebar de escritorio y mismo `openState` en localStorage: abrir
«Ventas» acá lo deja abierto allá. Lo único que cambia es el contenedor.
</p>
</div>
</Stage>
)
}De specimens/navigation.tsx · ContextualSidebarContentSpecimen: el espécimen del Muestrario, no un ejemplo oficial del paquete. Muestra cómo se usa la pieza de verdad, montada contra los tipos instalados —eso es lo que vale— pero no es documentación del kit.
Cómo se usa
import { ContextualSidebarContent } from '@solu30/ui-kit/navigation'Props
| Prop | Requerida | Tipo | Qué hace |
|---|---|---|---|
| sections | sí | ContextualSidebarSection[] | — |
| activeItemId | opcional | string | — |
| activeSubItems | opcional | ContextualSidebarItem[] | — |
| subPanelLabel | opcional | string | — |
| onSubPanelBack | opcional | () => void | — |
| onNavigate | sí | (item: ContextualSidebarItem) => void | — |
| resolveIcon | opcional | (name: string) => ReactNode | — |
| collapsed | opcional | boolean | Defaults to NavigationProvider state when omitted. |
| subPanelAlwaysExpanded | opcional | boolean | ADR-077 — readable sub-panel labels even when rail collapsed. |
| collapsedMode | opcional | ContextualSidebarCollapsedMode | ADR-077 — collapsed rail mode. Default 'items'. |
| onSectionActivate | opcional | (section: ContextualSidebarSection) => void | ADR-077 — section drill-in callback for collapsedMode="sections". |
| openState | opcional | ContextualSidebarOpenState | ADR-027 addendum v2 — controlled open/closed state for `content`. |
| defaultOpenState | opcional | ContextualSidebarOpenState | ADR-027 addendum v2 — initial state when uncontrolled. |
| onOpenStateChange | opcional | (next: ContextualSidebarOpenState) => void | ADR-027 addendum v2 — emitted on every open/closed change. |
| decorativeGlow | opcional | boolean | Default `true` = current behavior. ADR-027 addendum v5. |
| sectionBadgeRollup | opcional | boolean | Default `false` = current behavior. ADR-027 addendum v6. |
Ejemplo del paquete
// El contenido del sidebar sin su chrome: sirve para montarlo adentro de un Sheet en móvil.
<ContextualSidebarContent
sections={secciones}
activeItemId={rutaActiva}
onNavigate={(item) => router.push(item.href)}
resolveIcon={(n) => <DynamicIcon name={n} />}
collapsed={plegado}
/>Del @example del JSDoc de @solu30/ui-kit, sin editar: son 65 de los 541 símbolos. La sangría y los renglones en blanco son los del paquete.