{"version":3,"file":"Sidebar.cjs","names":[],"sources":["../../../src/components/Sidebar/Sidebar.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — header, items, footer are the three regions,\n * value/onChange the selection, and collapsed/width/collapsedWidth the two layout\n * modes it switches between.\n */\nimport { useId, type HTMLAttributes, type ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Sidebar.module.css\";\nimport { SidebarEntry } from \"./SidebarEntry\";\n\nexport interface SidebarItem {\n    key: string;\n    label: ReactNode;\n    icon?: ReactNode;\n    badge?: ReactNode;\n    disabled?: boolean;\n    /**\n     * Renders the entry as a link to this URL instead of a button.\n     *\n     * `onChange` still fires on click, so an app that tracks the active key keeps\n     * working — the difference is that the entry now behaves like navigation:\n     * middle-click, ctrl-click and \"copy link address\" do what the user expects,\n     * and a screen reader announces a link rather than a button.\n     *\n     * A `disabled` entry ignores this and stays a `<button disabled>`: there is no\n     * disabled state for an anchor, and dropping the `href` to fake one leaves a\n     * link that announces itself as actionable and is not.\n     */\n    href?: string;\n}\n\n/**\n * A section heading. Every item after it belongs to the section, until the next\n * section or separator.\n */\nexport interface SidebarSection {\n    type: \"section\";\n    key: string;\n    label: ReactNode;\n}\n\n/** A plain divider, for splitting the list without naming the parts. */\nexport interface SidebarSeparator {\n    type: \"separator\";\n    key: string;\n}\n\n/**\n * One entry of the navigation list.\n *\n * An entry with no `type` is an item, which is what keeps a plain\n * `SidebarItem[]` a valid `SidebarEntry[]`: adding sections to this component\n * needed no change at any existing call site.\n */\nexport type SidebarEntry = ({ type?: \"item\" } & SidebarItem) | SidebarSection | SidebarSeparator;\n\nexport interface SidebarProps extends Omit<HTMLAttributes<HTMLElement>, \"onChange\"> {\n    /** Top slot — typically the logo + brand. */\n    header?: ReactNode;\n    /** Navigation entries. An entry with no `type` is an item. */\n    items: SidebarEntry[];\n    /** Active item key. */\n    value?: string;\n    /** Fires when an item is clicked. Receives the item's `key`. */\n    onChange?: (key: string) => void;\n    /** Bottom slot — typically settings/profile/logout. */\n    footer?: ReactNode;\n    /** Collapsed mode — only icons visible. Default `false`. */\n    collapsed?: boolean;\n    /** Width when expanded, in pixels or any CSS length. Default `240px`. */\n    width?: number | string;\n    /** Width when collapsed, in pixels or any CSS length. Default `64px`. */\n    collapsedWidth?: number | string;\n}\n\n/** A run of items, optionally under a section heading. */\ninterface ItemBlock {\n    kind: \"items\";\n    key: string;\n    section?: SidebarSection;\n    items: SidebarItem[];\n}\n\n/** A standalone divider between blocks. */\ninterface SeparatorBlock {\n    kind: \"separator\";\n    key: string;\n}\n\ntype Block = ItemBlock | SeparatorBlock;\n\n/**\n * Fold the flat entry list into the blocks that get rendered.\n *\n * A section opens a block that swallows every following item; a separator closes\n * whatever is open, so items after it are loose again. Items before the first\n * section are loose too — which is the whole existing behaviour, and why a list\n * with no sections comes out of here as one unnamed block.\n *\n * @param entries - The `items` prop, as given.\n * @returns Blocks in render order.\n */\nfunction toBlocks(entries: readonly SidebarEntry[]): Block[] {\n    const blocks: Block[] = [];\n    let open: ItemBlock | undefined;\n\n    for (const entry of entries) {\n        if (entry.type === \"separator\") {\n            blocks.push({ kind: \"separator\", key: entry.key });\n            open = undefined;\n            continue;\n        }\n        if (entry.type === \"section\") {\n            open = { kind: \"items\", key: entry.key, section: entry, items: [] };\n            blocks.push(open);\n            continue;\n        }\n        if (!open) {\n            open = { kind: \"items\", key: `loose-${entry.key}`, items: [] };\n            blocks.push(open);\n        }\n        open.items.push(entry);\n    }\n    return blocks;\n}\n\n/**\n * Desktop sidebar navigation. Pair with `<Show above=\"md\">` and a `Drawer`\n * for mobile.\n *\n * Sixteen screens in one flat list is a wall, so `items` also takes section\n * headings and separators. A section renders as a labelled `role=\"group\"`, which\n * is what tells a screen reader \"Monitoring, group, 3 items\" — the reason it is\n * not a styled `disabled` item, which would announce an unavailable button and\n * stay in the navigation order.\n *\n * @example\n * const [tab, setTab] = useState(\"home\");\n * <Show above=\"md\">\n *     <Sidebar\n *         header={<Brand />}\n *         items={[{ key: \"home\", label: \"Home\", icon: <Home /> }]}\n *         value={tab}\n *         onChange={setTab}\n *     />\n * </Show>\n *\n * @example\n * <Sidebar\n *     items={[\n *         { type: \"section\", key: \"monitoring\", label: \"Monitoring\" },\n *         { key: \"overview\", label: \"Overview\", href: \"/overview\" },\n *         { key: \"activity\", label: \"Activity\", href: \"/activity\" },\n *         { type: \"section\", key: \"admin\", label: \"Administration\" },\n *         { key: \"settings\", label: \"Settings\", href: \"/settings\" },\n *     ]}\n *     value={tab}\n *     onChange={setTab}\n * />\n */\nexport function Sidebar({\n    header,\n    items,\n    value,\n    onChange,\n    footer,\n    collapsed = false,\n    width = 240,\n    collapsedWidth = 64,\n    className,\n    style,\n    ...props\n}: SidebarProps) {\n    const baseId = useId();\n    const blocks = toBlocks(items);\n    const finalWidth =\n        typeof (collapsed ? collapsedWidth : width) === \"number\"\n            ? `${collapsed ? collapsedWidth : width}px`\n            : collapsed\n              ? collapsedWidth\n              : width;\n\n    return (\n        <aside\n            className={cn(styles.sidebar, collapsed && styles.collapsed, className)}\n            style={{ width: finalWidth, ...style }}\n            {...props}\n        >\n            {header && <div className={styles.header}>{header}</div>}\n            <nav className={styles.nav} aria-label=\"Navegação lateral\">\n                {blocks.map((block) => {\n                    if (block.kind === \"separator\") {\n                        return <hr key={block.key} className={styles.separator} />;\n                    }\n                    if (!block.section) {\n                        return (\n                            <div key={block.key} className={styles.group}>\n                                {block.items.map((item) => (\n                                    <SidebarEntry\n                                        key={item.key}\n                                        item={item}\n                                        active={item.key === value}\n                                        collapsed={collapsed}\n                                        onSelect={onChange}\n                                    />\n                                ))}\n                            </div>\n                        );\n                    }\n                    const labelId = `${baseId}-${block.key}`;\n                    return (\n                        <div\n                            key={block.key}\n                            role=\"group\"\n                            aria-labelledby={labelId}\n                            className={styles.group}\n                        >\n                            <div\n                                id={labelId}\n                                role=\"presentation\"\n                                className={cn(styles.section, collapsed && styles.sectionCollapsed)}\n                            >\n                                {block.section.label}\n                            </div>\n                            {block.items.map((item) => (\n                                <SidebarEntry\n                                    key={item.key}\n                                    item={item}\n                                    active={item.key === value}\n                                    collapsed={collapsed}\n                                    onSelect={onChange}\n                                />\n                            ))}\n                        </div>\n                    );\n                })}\n            </nav>\n            {footer && <div className={styles.footer}>{footer}</div>}\n        </aside>\n    );\n}\n"],"mappings":"8JAsGA,SAAS,EAAS,EAA2C,CACzD,IAAM,EAAkB,CAAC,EACrB,EAEJ,IAAK,IAAM,KAAS,EAAS,CACzB,GAAI,EAAM,OAAS,YAAa,CAC5B,EAAO,KAAK,CAAE,KAAM,YAAa,IAAK,EAAM,GAAI,CAAC,EACjD,EAAO,IAAA,GACP,QACJ,CACA,GAAI,EAAM,OAAS,UAAW,CAC1B,EAAO,CAAE,KAAM,QAAS,IAAK,EAAM,IAAK,QAAS,EAAO,MAAO,CAAC,CAAE,EAClE,EAAO,KAAK,CAAI,EAChB,QACJ,CACK,IACD,EAAO,CAAE,KAAM,QAAS,IAAK,SAAS,EAAM,MAAO,MAAO,CAAC,CAAE,EAC7D,EAAO,KAAK,CAAI,GAEpB,EAAK,MAAM,KAAK,CAAK,CACzB,CACA,OAAO,CACX,CAoCA,SAAgB,EAAQ,CACpB,SACA,QACA,QACA,WACA,SACA,YAAY,GACZ,QAAQ,IACR,iBAAiB,GACjB,YACA,QACA,GAAG,GACU,CACb,IAAM,GAAA,EAAS,EAAA,MAAA,CAAM,EACf,EAAS,EAAS,CAAK,EACvB,EACF,OAAQ,EAAY,EAAiB,IAAW,SAC1C,GAAG,EAAY,EAAiB,EAAM,IACtC,EACE,EACA,EAEZ,OACI,EAAA,EAAA,KAAA,CAAC,QAAD,CACI,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,GAAa,EAAA,QAAO,UAAW,CAAS,EACtE,MAAO,CAAE,MAAO,EAAY,GAAG,CAAM,EACrC,GAAI,EAHR,SAAA,CAKK,IAAU,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,OAAS,SAAA,CAAY,CAAA,GACvD,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,IAAK,aAAW,oBAClC,SAAA,EAAO,IAAK,GAAU,CACnB,GAAI,EAAM,OAAS,YACf,OAAO,EAAA,EAAA,IAAA,CAAC,KAAD,CAAoB,UAAW,EAAA,QAAO,SAAY,EAAzC,EAAM,GAAmC,EAE7D,GAAI,CAAC,EAAM,QACP,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAqB,UAAW,EAAA,QAAO,MAClC,SAAA,EAAM,MAAM,IAAK,IACd,EAAA,EAAA,IAAA,CAAC,EAAA,aAAD,CAEU,OACN,OAAQ,EAAK,MAAQ,EACV,YACX,SAAU,CACb,EALQ,EAAK,GAKb,CACJ,CACA,EAVK,EAAM,GAUX,EAGb,IAAM,EAAU,GAAG,EAAO,GAAG,EAAM,MACnC,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAEI,KAAK,QACL,kBAAiB,EACjB,UAAW,EAAA,QAAO,MAJtB,SAAA,EAMI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,GAAI,EACJ,KAAK,eACL,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,GAAa,EAAA,QAAO,gBAAgB,EAEjE,SAAA,EAAM,QAAQ,KACd,CAAA,EACJ,EAAM,MAAM,IAAK,IACd,EAAA,EAAA,IAAA,CAAC,EAAA,aAAD,CAEU,OACN,OAAQ,EAAK,MAAQ,EACV,YACX,SAAU,CACb,EALQ,EAAK,GAKb,CACJ,CACA,CArBI,EAAA,EAAM,GAqBV,CAEb,CAAC,CACA,CAAA,EACJ,IAAU,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,OAAS,SAAA,CAAY,CAAA,CACpD,GAEf"}