@misael703/ui 1.22.0 → 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-HSDOGR3N.mjs → chunk-HAZDZYF4.mjs} +7 -5
- package/dist/chunk-HAZDZYF4.mjs.map +1 -0
- package/dist/{chunk-GGYKVABI.js → chunk-S4YPRJQQ.js} +7 -5
- package/dist/chunk-S4YPRJQQ.js.map +1 -0
- package/dist/components/AppShell.d.mts +26 -4
- package/dist/components/AppShell.d.ts +26 -4
- package/dist/components/AppShell.js +3 -3
- package/dist/components/AppShell.mjs +1 -1
- package/dist/components/Layout.d.mts +1 -1
- package/dist/components/Layout.d.ts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -4
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
- package/dist/chunk-GGYKVABI.js.map +0 -1
- package/dist/chunk-HSDOGR3N.mjs.map +0 -1
|
@@ -76,11 +76,13 @@ function AppShell(props) {
|
|
|
76
76
|
const { header } = props;
|
|
77
77
|
const headerTheme = props.headerTheme ?? theme;
|
|
78
78
|
const collapsedRail = props.collapsedRail ?? false;
|
|
79
|
+
const headerApi = { collapsed, toggle: () => setCollapsed(!collapsed), setCollapsed };
|
|
80
|
+
const slot = (s) => typeof s === "function" ? s(headerApi) : s;
|
|
79
81
|
return /* @__PURE__ */ jsxs("div", { className: cx("appshell", `appshell--${theme}`, "appshell--header-top", `appshell--header-${headerTheme}`, collapsedRail && "appshell--rail", collapsed && "is-collapsed", className), children: [
|
|
80
82
|
/* @__PURE__ */ jsxs("header", { className: "appshell__header", role: "banner", "data-tone": headerTheme === "brand" ? "inverse" : void 0, children: [
|
|
81
|
-
/* @__PURE__ */ jsx("div", { className: "appshell__header-left", children: header?.left }),
|
|
82
|
-
/* @__PURE__ */ jsx("div", { className: "appshell__header-center", children: header?.center }),
|
|
83
|
-
/* @__PURE__ */ jsx("div", { className: "appshell__header-right", children: header?.right })
|
|
83
|
+
/* @__PURE__ */ jsx("div", { className: "appshell__header-left", children: slot(header?.left) }),
|
|
84
|
+
/* @__PURE__ */ jsx("div", { className: "appshell__header-center", children: slot(header?.center) }),
|
|
85
|
+
/* @__PURE__ */ jsx("div", { className: "appshell__header-right", children: slot(header?.right) })
|
|
84
86
|
] }),
|
|
85
87
|
/* @__PURE__ */ jsxs("div", { className: "appshell__body", children: [
|
|
86
88
|
/* @__PURE__ */ jsxs("aside", { className: "appshell__sidebar", "aria-label": t["appshell.mainNav"], children: [
|
|
@@ -158,5 +160,5 @@ function PageHeader({ title, description, breadcrumbs, actions, meta, className
|
|
|
158
160
|
}
|
|
159
161
|
|
|
160
162
|
export { AppShell, PageHeader };
|
|
161
|
-
//# sourceMappingURL=chunk-
|
|
162
|
-
//# sourceMappingURL=chunk-
|
|
163
|
+
//# sourceMappingURL=chunk-HAZDZYF4.mjs.map
|
|
164
|
+
//# sourceMappingURL=chunk-HAZDZYF4.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/AppShell.tsx"],"names":["NavItemNode"],"mappings":"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","file":"chunk-HAZDZYF4.mjs","sourcesContent":["'use client';\nimport * as React from 'react';\nimport { cx } from '../utils/cx';\nimport { ChevronLeft, ChevronRight, MenuIcon } from './Icons';\nimport { useLocale } from '../locale/LocaleProvider';\n\n// ---------- AppShell (Sidebar + Topbar + Content) -----------------------\n// Designed to drop into a Next.js app/layout.tsx as a Client Component shell.\n\nexport interface NavItem {\n id: string;\n label: React.ReactNode;\n icon?: React.ReactNode;\n href?: string;\n active?: boolean;\n badge?: React.ReactNode;\n onSelect?: () => void;\n children?: NavItem[];\n}\n\nexport interface NavSection {\n id?: string;\n label?: React.ReactNode;\n items: NavItem[];\n}\n\nexport type AppShellTheme = 'default' | 'brand';\n\nexport type AppShellHeaderLayout = 'side' | 'top';\n\n/**\n * Collapse API handed to header-slot render-props so a consumer-placed control\n * (e.g. a hamburger in `left`) can drive the sidebar — crucially in\n * **uncontrolled** mode, where the `top` layout otherwise has no toggle\n * affordance. This is what lets `persistKey` (uncontrolled) coexist with a\n * custom header trigger: the kit owns the state + persistence, the consumer\n * owns the trigger's look and placement.\n */\nexport interface AppShellHeaderApi {\n /** Current collapsed state. */\n collapsed: boolean;\n /** Flip the collapsed state (persisted if `persistKey` is set). */\n toggle: () => void;\n /** Set the collapsed state explicitly. */\n setCollapsed: (collapsed: boolean) => void;\n}\n\n/**\n * A header slot: a static node, or a render-prop receiving {@link AppShellHeaderApi}.\n * The function form is the only way to toggle an uncontrolled shell from the\n * header (no built-in toggle exists in `top`).\n */\nexport type AppShellHeaderSlot =\n | React.ReactNode\n | ((api: AppShellHeaderApi) => React.ReactNode);\n\nexport interface AppShellHeader {\n /** Left slot — typically a menu/hamburger trigger or back action. */\n left?: AppShellHeaderSlot;\n /** Center slot — typically the brand (Logo). Lands at the true viewport centre. */\n center?: AppShellHeaderSlot;\n /** Right slot — notifications, user avatar, utilities. */\n right?: AppShellHeaderSlot;\n}\n\n/**\n * Props shared by both layouts. The layout-specific props live in\n * `AppShellSideProps` / `AppShellTopProps`; `AppShellProps` is the\n * discriminated union of the two, keyed on `headerLayout`.\n */\nexport interface AppShellBaseProps {\n sections: NavSection[];\n footer?: React.ReactNode;\n defaultCollapsed?: boolean;\n collapsed?: boolean;\n onCollapsedChange?: (c: boolean) => void;\n /**\n * Persist the collapsed state in `localStorage[persistKey]` so it survives\n * reloads. Opt-in: omit it and the shell keeps the default behaviour (resets\n * to `defaultCollapsed` on each mount). SSR-safe — the initial render still\n * uses `defaultCollapsed` (no hydration mismatch); the stored value is read\n * after mount, so a differing value may flash for one frame. Ignored in\n * controlled mode (when `collapsed` is provided): the host owns persistence.\n */\n persistKey?: string;\n children: React.ReactNode;\n className?: string;\n /**\n * Color theme (applies to both layouts):\n * - `default` (light): claro, mejor para apps data-heavy de uso prolongado.\n * - `brand`: superficie azul de marca con texto blanco. Mayor brand recall.\n * En `side` tiñe el sidebar; en `top` tiñe header + sidebar (un solo knob).\n */\n theme?: AppShellTheme;\n /** Render-prop for navigation links so the host app can use Next.js Link, etc. */\n linkAs?: (item: NavItem, content: React.ReactNode, className: string) => React.ReactNode;\n}\n\n/**\n * Sidebar layout (default, `headerLayout=\"side\"` or omitted). The brand\n * block + collapse rail live in the sidebar; the topbar sits over the\n * content with a mobile hamburger. `header` is **not** valid here — that\n * slot belongs to the `top` layout.\n */\nexport interface AppShellSideProps extends AppShellBaseProps {\n headerLayout?: 'side';\n /** Brand node in the sidebar header (expanded state). */\n brand?: React.ReactNode;\n /** Brand node shown when the rail is collapsed. Falls back to `brand`. */\n brandCollapsed?: React.ReactNode;\n /** Content of the topbar over the page (search, etc.). */\n topbar?: React.ReactNode;\n /** User slot at the right of the topbar (avatar/menu). */\n user?: React.ReactNode;\n /** Not valid in the `side` layout — the header slots belong to `top`. */\n header?: never;\n}\n\n/**\n * Top-header layout (`headerLayout=\"top\"`, v1.15.0). Full-width header\n * above the body with three slots (`header.{left,center,right}`); the\n * centre slot lands at the **true viewport centre** (1fr·auto·1fr grid).\n * The sidebar has no brand block and `collapsed` hides it entirely (no\n * 72px rail); the header is **invariant** to the collapse. `theme=\"brand\"`\n * tints both bands. The `side`-only props are **not** valid here — put your\n * chrome in `header`.\n */\nexport interface AppShellTopProps extends AppShellBaseProps {\n headerLayout: 'top';\n /** Slots for the full-width header. Brand usually goes in `center`. */\n header?: AppShellHeader;\n /**\n * Theme of the **header band only**, independent of the sidebar (`theme`).\n * Defaults to `theme`, so `theme=\"brand\"` still tints both bands (no\n * change for existing consumers). Set `theme=\"default\" headerTheme=\"brand\"`\n * for a branded top bar over a neutral, legible sidebar — common in\n * data-heavy admin apps.\n */\n headerTheme?: AppShellTheme;\n /**\n * Collapse to an icon rail (72px) instead of hiding the sidebar entirely.\n * Default `false` → `collapsed` hides the sidebar (the original `top`\n * behavior). `true` → `collapsed` keeps a 72px rail showing the nav icons\n * (labels hidden, active-item bar kept) — like the `side` layout — and\n * renders a built-in expand/collapse toggle at the bottom of the rail.\n */\n collapsedRail?: boolean;\n /** Not valid in `top` — use `header.center` for the brand. */\n brand?: never;\n /** Not valid in `top` — the sidebar collapses entirely. */\n brandCollapsed?: never;\n /** Not valid in `top` — use the `header` slots. */\n topbar?: never;\n /** Not valid in `top` — use `header.right`. */\n user?: never;\n}\n\n/**\n * Discriminated union keyed on `headerLayout`. TypeScript enforces that\n * `header` is only accepted with `headerLayout=\"top\"` and that\n * `brand`/`brandCollapsed`/`topbar`/`user` are only accepted with the\n * (default) `side` layout — passing the wrong prop for the layout is a\n * compile error instead of being silently ignored at runtime.\n */\nexport type AppShellProps = AppShellSideProps | AppShellTopProps;\n\n// Recursive nav item, memoized so a single item's parent re-render doesn't\n// churn through every other item in the tree. Stability of `linkAs` and\n// `onCloseMobile` is the parent's responsibility (we stabilize\n// `onCloseMobile` via useCallback below; consumers should memoize `linkAs`\n// if they care about avoiding renders, but for typical Next.js Link usage\n// the inline arrow is rarely a hot path).\ninterface NavItemNodeProps {\n item: NavItem;\n depth: number;\n linkAs?: AppShellBaseProps['linkAs'];\n onCloseMobile: () => void;\n}\n\nconst NavItemNode = React.memo(function NavItemNode({\n item, depth, linkAs, onCloseMobile,\n}: NavItemNodeProps) {\n const klass = cx('appshell__navitem', item.active && 'is-active', `appshell__navitem--depth-${depth}`);\n const inner = (\n <>\n {item.icon && <span className=\"appshell__navicon\" aria-hidden=\"true\">{item.icon}</span>}\n <span className=\"appshell__navlabel\">{item.label}</span>\n {item.badge && <span className=\"appshell__navbadge\">{item.badge}</span>}\n </>\n );\n const node = item.href && linkAs\n ? linkAs(item, inner, klass)\n : (\n <a\n href={item.href ?? '#'}\n className={klass}\n aria-current={item.active ? 'page' : undefined}\n onClick={(e) => {\n if (!item.href) e.preventDefault();\n item.onSelect?.();\n onCloseMobile();\n }}\n >\n {inner}\n </a>\n );\n return (\n <li>\n {node}\n {item.children && item.children.length > 0 && (\n <ul className=\"appshell__navchildren\">\n {item.children.map((c) => (\n <NavItemNode key={c.id} item={c} depth={depth + 1} linkAs={linkAs} onCloseMobile={onCloseMobile} />\n ))}\n </ul>\n )}\n </li>\n );\n});\n\nexport function AppShell(props: AppShellProps) {\n const {\n sections, footer, defaultCollapsed = false,\n collapsed: ctrlCollapsed, onCollapsedChange, persistKey,\n children, className, theme = 'default', linkAs,\n } = props;\n const [internalCollapsed, setInternalCollapsed] = React.useState(defaultCollapsed);\n const [mobileOpen, setMobileOpen] = React.useState(false);\n const t = useLocale();\n const collapsed = ctrlCollapsed ?? internalCollapsed;\n\n // SSR-safe persistence: the initial render uses `defaultCollapsed` (server\n // and first client render agree → no hydration mismatch), then we sync from\n // localStorage after mount. Only in uncontrolled mode; reads can throw\n // (Safari private mode), so they're guarded. Runs once per persistKey.\n React.useEffect(() => {\n if (persistKey == null || ctrlCollapsed !== undefined) return;\n try {\n const stored = window.localStorage.getItem(persistKey);\n if (stored === '0' || stored === '1') setInternalCollapsed(stored === '1');\n } catch {\n /* localStorage unavailable — keep defaultCollapsed */\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- sync once at mount; ctrlCollapsed read intentionally not tracked\n }, [persistKey]);\n\n const setCollapsed = (v: boolean) => {\n if (ctrlCollapsed === undefined) setInternalCollapsed(v);\n if (persistKey != null && ctrlCollapsed === undefined) {\n try {\n window.localStorage.setItem(persistKey, v ? '1' : '0');\n } catch {\n /* localStorage unavailable — persistence is best-effort */\n }\n }\n onCollapsedChange?.(v);\n };\n const closeMobile = React.useCallback(() => setMobileOpen(false), []);\n\n // Top-header variant: full-width header above the body. Topbar is\n // invariant to `collapsed` (only the inner body's columns animate); brand\n // lives in `header.center` at the true viewport centre. Default\n // `headerLayout=\"side\"` falls through to the legacy JSX below\n // (byte-identical for existing consumers). The `props.headerLayout`\n // check narrows the discriminated union, so `header` (top) and\n // `brand`/`topbar`/`user` (side) are each only in scope in their branch.\n if (props.headerLayout === 'top') {\n const { header } = props;\n // Header band themes independently of the sidebar; defaults to `theme`\n // so `theme=\"brand\"` keeps tinting both bands (back-compat).\n const headerTheme = props.headerTheme ?? theme;\n const collapsedRail = props.collapsedRail ?? false;\n // Hand the collapse API to header slots so a consumer trigger (hamburger)\n // can toggle the shell — the only path in uncontrolled `top` (no built-in\n // toggle here). A function slot is called with the API; a node renders as-is.\n const headerApi: AppShellHeaderApi = { collapsed, toggle: () => setCollapsed(!collapsed), setCollapsed };\n const slot = (s: AppShellHeaderSlot): React.ReactNode =>\n typeof s === 'function' ? (s as (api: AppShellHeaderApi) => React.ReactNode)(headerApi) : s;\n return (\n <div className={cx('appshell', `appshell--${theme}`, 'appshell--header-top', `appshell--header-${headerTheme}`, collapsedRail && 'appshell--rail', collapsed && 'is-collapsed', className)}>\n {/* On a brand header the band is dark, so re-scope foreground tokens\n via data-tone=\"inverse\" — anything inside (Avatar, badges, links)\n becomes band-aware automatically without per-call-site colors. */}\n <header className=\"appshell__header\" role=\"banner\" data-tone={headerTheme === 'brand' ? 'inverse' : undefined}>\n <div className=\"appshell__header-left\">{slot(header?.left)}</div>\n <div className=\"appshell__header-center\">{slot(header?.center)}</div>\n <div className=\"appshell__header-right\">{slot(header?.right)}</div>\n </header>\n <div className=\"appshell__body\">\n <aside className=\"appshell__sidebar\" aria-label={t['appshell.mainNav']}>\n <nav className=\"appshell__nav\">\n {sections.map((s, i) => (\n <div key={s.id ?? i} className=\"appshell__navsection\">\n {s.label && <div className=\"appshell__navlabel-section\">{s.label}</div>}\n <ul>{s.items.map((it) => (\n <NavItemNode key={it.id} item={it} depth={0} linkAs={linkAs} onCloseMobile={closeMobile} />\n ))}</ul>\n </div>\n ))}\n </nav>\n {footer != null && (\n <div className=\"appshell__sidebar-foot\">{footer}</div>\n )}\n </aside>\n <main className=\"appshell__content\" role=\"main\">{children}</main>\n </div>\n </div>\n );\n }\n\n const { brand, brandCollapsed, topbar, user } = props;\n return (\n <div className={cx('appshell', `appshell--${theme}`, collapsed && 'is-collapsed', mobileOpen && 'is-mobile-open', className)}>\n <aside className=\"appshell__sidebar\" aria-label={t['appshell.mainNav']}>\n <div className=\"appshell__brand\">\n {collapsed ? (brandCollapsed ?? brand) : brand}\n </div>\n <nav className=\"appshell__nav\">\n {sections.map((s, i) => (\n <div key={s.id ?? i} className=\"appshell__navsection\">\n {s.label && <div className=\"appshell__navlabel-section\">{s.label}</div>}\n <ul>{s.items.map((it) => (\n <NavItemNode key={it.id} item={it} depth={0} linkAs={linkAs} onCloseMobile={closeMobile} />\n ))}</ul>\n </div>\n ))}\n </nav>\n <div className=\"appshell__sidebar-foot\">\n {footer}\n <button\n type=\"button\"\n className=\"appshell__collapse\"\n onClick={() => setCollapsed(!collapsed)}\n aria-expanded={!collapsed}\n aria-label={collapsed ? t['appshell.expandMenu'] : t['appshell.collapseMenu']}\n title={collapsed ? t['appshell.expand'] : t['appshell.collapse']}\n >\n {collapsed ? <ChevronRight size={14} /> : <ChevronLeft size={14} />}\n </button>\n </div>\n </aside>\n\n <div className=\"appshell__main\">\n <header className=\"appshell__topbar\">\n <button\n type=\"button\"\n className=\"appshell__hamburger\"\n aria-label={t['appshell.openMenu']}\n aria-expanded={mobileOpen}\n onClick={() => setMobileOpen((o) => !o)}\n ><MenuIcon size={20} /></button>\n <div className=\"appshell__topbar-content\">{topbar}</div>\n {user && <div className=\"appshell__topbar-user\">{user}</div>}\n </header>\n <main className=\"appshell__content\" role=\"main\">{children}</main>\n </div>\n\n {mobileOpen && (\n <div className=\"appshell__scrim\" onClick={() => setMobileOpen(false)} aria-hidden=\"true\" />\n )}\n </div>\n );\n}\n\n// ---------- PageHeader --------------------------------------------------\nexport interface PageHeaderProps {\n title: React.ReactNode;\n description?: React.ReactNode;\n breadcrumbs?: Array<{ label: React.ReactNode; href?: string }>;\n actions?: React.ReactNode;\n meta?: React.ReactNode;\n className?: string;\n}\n\nexport function PageHeader({ title, description, breadcrumbs, actions, meta, className }: PageHeaderProps) {\n const t = useLocale();\n return (\n <div className={cx('page-header', className)}>\n {breadcrumbs && breadcrumbs.length > 0 && (\n <nav className=\"page-header__crumbs\" aria-label={t['appshell.breadcrumb']}>\n <ol>\n {breadcrumbs.map((c, i) => (\n <li key={i}>\n {c.href ? <a href={c.href}>{c.label}</a> : <span aria-current=\"page\">{c.label}</span>}\n {i < breadcrumbs.length - 1 && <span className=\"page-header__crumb-sep\" aria-hidden=\"true\">/</span>}\n </li>\n ))}\n </ol>\n </nav>\n )}\n <div className=\"page-header__row\">\n <div className=\"page-header__title-wrap\">\n <h1 className=\"page-header__title\">{title}</h1>\n {description && <p className=\"page-header__desc\">{description}</p>}\n </div>\n {actions && <div className=\"page-header__actions\">{actions}</div>}\n </div>\n {meta && <div className=\"page-header__meta\">{meta}</div>}\n </div>\n );\n}\n"]}
|
|
@@ -98,11 +98,13 @@ function AppShell(props) {
|
|
|
98
98
|
const { header } = props;
|
|
99
99
|
const headerTheme = props.headerTheme ?? theme;
|
|
100
100
|
const collapsedRail = props.collapsedRail ?? false;
|
|
101
|
+
const headerApi = { collapsed, toggle: () => setCollapsed(!collapsed), setCollapsed };
|
|
102
|
+
const slot = (s) => typeof s === "function" ? s(headerApi) : s;
|
|
101
103
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: chunkPASF6T4H_js.cx("appshell", `appshell--${theme}`, "appshell--header-top", `appshell--header-${headerTheme}`, collapsedRail && "appshell--rail", collapsed && "is-collapsed", className), children: [
|
|
102
104
|
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "appshell__header", role: "banner", "data-tone": headerTheme === "brand" ? "inverse" : void 0, children: [
|
|
103
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "appshell__header-left", children: header?.left }),
|
|
104
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "appshell__header-center", children: header?.center }),
|
|
105
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "appshell__header-right", children: header?.right })
|
|
105
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "appshell__header-left", children: slot(header?.left) }),
|
|
106
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "appshell__header-center", children: slot(header?.center) }),
|
|
107
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "appshell__header-right", children: slot(header?.right) })
|
|
106
108
|
] }),
|
|
107
109
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "appshell__body", children: [
|
|
108
110
|
/* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "appshell__sidebar", "aria-label": t["appshell.mainNav"], children: [
|
|
@@ -181,5 +183,5 @@ function PageHeader({ title, description, breadcrumbs, actions, meta, className
|
|
|
181
183
|
|
|
182
184
|
exports.AppShell = AppShell;
|
|
183
185
|
exports.PageHeader = PageHeader;
|
|
184
|
-
//# sourceMappingURL=chunk-
|
|
185
|
-
//# sourceMappingURL=chunk-
|
|
186
|
+
//# sourceMappingURL=chunk-S4YPRJQQ.js.map
|
|
187
|
+
//# sourceMappingURL=chunk-S4YPRJQQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/AppShell.tsx"],"names":["React","NavItemNode","cx","jsxs","Fragment","jsx","useLocale","ChevronRight","ChevronLeft","MenuIcon"],"mappings":"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","file":"chunk-S4YPRJQQ.js","sourcesContent":["'use client';\nimport * as React from 'react';\nimport { cx } from '../utils/cx';\nimport { ChevronLeft, ChevronRight, MenuIcon } from './Icons';\nimport { useLocale } from '../locale/LocaleProvider';\n\n// ---------- AppShell (Sidebar + Topbar + Content) -----------------------\n// Designed to drop into a Next.js app/layout.tsx as a Client Component shell.\n\nexport interface NavItem {\n id: string;\n label: React.ReactNode;\n icon?: React.ReactNode;\n href?: string;\n active?: boolean;\n badge?: React.ReactNode;\n onSelect?: () => void;\n children?: NavItem[];\n}\n\nexport interface NavSection {\n id?: string;\n label?: React.ReactNode;\n items: NavItem[];\n}\n\nexport type AppShellTheme = 'default' | 'brand';\n\nexport type AppShellHeaderLayout = 'side' | 'top';\n\n/**\n * Collapse API handed to header-slot render-props so a consumer-placed control\n * (e.g. a hamburger in `left`) can drive the sidebar — crucially in\n * **uncontrolled** mode, where the `top` layout otherwise has no toggle\n * affordance. This is what lets `persistKey` (uncontrolled) coexist with a\n * custom header trigger: the kit owns the state + persistence, the consumer\n * owns the trigger's look and placement.\n */\nexport interface AppShellHeaderApi {\n /** Current collapsed state. */\n collapsed: boolean;\n /** Flip the collapsed state (persisted if `persistKey` is set). */\n toggle: () => void;\n /** Set the collapsed state explicitly. */\n setCollapsed: (collapsed: boolean) => void;\n}\n\n/**\n * A header slot: a static node, or a render-prop receiving {@link AppShellHeaderApi}.\n * The function form is the only way to toggle an uncontrolled shell from the\n * header (no built-in toggle exists in `top`).\n */\nexport type AppShellHeaderSlot =\n | React.ReactNode\n | ((api: AppShellHeaderApi) => React.ReactNode);\n\nexport interface AppShellHeader {\n /** Left slot — typically a menu/hamburger trigger or back action. */\n left?: AppShellHeaderSlot;\n /** Center slot — typically the brand (Logo). Lands at the true viewport centre. */\n center?: AppShellHeaderSlot;\n /** Right slot — notifications, user avatar, utilities. */\n right?: AppShellHeaderSlot;\n}\n\n/**\n * Props shared by both layouts. The layout-specific props live in\n * `AppShellSideProps` / `AppShellTopProps`; `AppShellProps` is the\n * discriminated union of the two, keyed on `headerLayout`.\n */\nexport interface AppShellBaseProps {\n sections: NavSection[];\n footer?: React.ReactNode;\n defaultCollapsed?: boolean;\n collapsed?: boolean;\n onCollapsedChange?: (c: boolean) => void;\n /**\n * Persist the collapsed state in `localStorage[persistKey]` so it survives\n * reloads. Opt-in: omit it and the shell keeps the default behaviour (resets\n * to `defaultCollapsed` on each mount). SSR-safe — the initial render still\n * uses `defaultCollapsed` (no hydration mismatch); the stored value is read\n * after mount, so a differing value may flash for one frame. Ignored in\n * controlled mode (when `collapsed` is provided): the host owns persistence.\n */\n persistKey?: string;\n children: React.ReactNode;\n className?: string;\n /**\n * Color theme (applies to both layouts):\n * - `default` (light): claro, mejor para apps data-heavy de uso prolongado.\n * - `brand`: superficie azul de marca con texto blanco. Mayor brand recall.\n * En `side` tiñe el sidebar; en `top` tiñe header + sidebar (un solo knob).\n */\n theme?: AppShellTheme;\n /** Render-prop for navigation links so the host app can use Next.js Link, etc. */\n linkAs?: (item: NavItem, content: React.ReactNode, className: string) => React.ReactNode;\n}\n\n/**\n * Sidebar layout (default, `headerLayout=\"side\"` or omitted). The brand\n * block + collapse rail live in the sidebar; the topbar sits over the\n * content with a mobile hamburger. `header` is **not** valid here — that\n * slot belongs to the `top` layout.\n */\nexport interface AppShellSideProps extends AppShellBaseProps {\n headerLayout?: 'side';\n /** Brand node in the sidebar header (expanded state). */\n brand?: React.ReactNode;\n /** Brand node shown when the rail is collapsed. Falls back to `brand`. */\n brandCollapsed?: React.ReactNode;\n /** Content of the topbar over the page (search, etc.). */\n topbar?: React.ReactNode;\n /** User slot at the right of the topbar (avatar/menu). */\n user?: React.ReactNode;\n /** Not valid in the `side` layout — the header slots belong to `top`. */\n header?: never;\n}\n\n/**\n * Top-header layout (`headerLayout=\"top\"`, v1.15.0). Full-width header\n * above the body with three slots (`header.{left,center,right}`); the\n * centre slot lands at the **true viewport centre** (1fr·auto·1fr grid).\n * The sidebar has no brand block and `collapsed` hides it entirely (no\n * 72px rail); the header is **invariant** to the collapse. `theme=\"brand\"`\n * tints both bands. The `side`-only props are **not** valid here — put your\n * chrome in `header`.\n */\nexport interface AppShellTopProps extends AppShellBaseProps {\n headerLayout: 'top';\n /** Slots for the full-width header. Brand usually goes in `center`. */\n header?: AppShellHeader;\n /**\n * Theme of the **header band only**, independent of the sidebar (`theme`).\n * Defaults to `theme`, so `theme=\"brand\"` still tints both bands (no\n * change for existing consumers). Set `theme=\"default\" headerTheme=\"brand\"`\n * for a branded top bar over a neutral, legible sidebar — common in\n * data-heavy admin apps.\n */\n headerTheme?: AppShellTheme;\n /**\n * Collapse to an icon rail (72px) instead of hiding the sidebar entirely.\n * Default `false` → `collapsed` hides the sidebar (the original `top`\n * behavior). `true` → `collapsed` keeps a 72px rail showing the nav icons\n * (labels hidden, active-item bar kept) — like the `side` layout — and\n * renders a built-in expand/collapse toggle at the bottom of the rail.\n */\n collapsedRail?: boolean;\n /** Not valid in `top` — use `header.center` for the brand. */\n brand?: never;\n /** Not valid in `top` — the sidebar collapses entirely. */\n brandCollapsed?: never;\n /** Not valid in `top` — use the `header` slots. */\n topbar?: never;\n /** Not valid in `top` — use `header.right`. */\n user?: never;\n}\n\n/**\n * Discriminated union keyed on `headerLayout`. TypeScript enforces that\n * `header` is only accepted with `headerLayout=\"top\"` and that\n * `brand`/`brandCollapsed`/`topbar`/`user` are only accepted with the\n * (default) `side` layout — passing the wrong prop for the layout is a\n * compile error instead of being silently ignored at runtime.\n */\nexport type AppShellProps = AppShellSideProps | AppShellTopProps;\n\n// Recursive nav item, memoized so a single item's parent re-render doesn't\n// churn through every other item in the tree. Stability of `linkAs` and\n// `onCloseMobile` is the parent's responsibility (we stabilize\n// `onCloseMobile` via useCallback below; consumers should memoize `linkAs`\n// if they care about avoiding renders, but for typical Next.js Link usage\n// the inline arrow is rarely a hot path).\ninterface NavItemNodeProps {\n item: NavItem;\n depth: number;\n linkAs?: AppShellBaseProps['linkAs'];\n onCloseMobile: () => void;\n}\n\nconst NavItemNode = React.memo(function NavItemNode({\n item, depth, linkAs, onCloseMobile,\n}: NavItemNodeProps) {\n const klass = cx('appshell__navitem', item.active && 'is-active', `appshell__navitem--depth-${depth}`);\n const inner = (\n <>\n {item.icon && <span className=\"appshell__navicon\" aria-hidden=\"true\">{item.icon}</span>}\n <span className=\"appshell__navlabel\">{item.label}</span>\n {item.badge && <span className=\"appshell__navbadge\">{item.badge}</span>}\n </>\n );\n const node = item.href && linkAs\n ? linkAs(item, inner, klass)\n : (\n <a\n href={item.href ?? '#'}\n className={klass}\n aria-current={item.active ? 'page' : undefined}\n onClick={(e) => {\n if (!item.href) e.preventDefault();\n item.onSelect?.();\n onCloseMobile();\n }}\n >\n {inner}\n </a>\n );\n return (\n <li>\n {node}\n {item.children && item.children.length > 0 && (\n <ul className=\"appshell__navchildren\">\n {item.children.map((c) => (\n <NavItemNode key={c.id} item={c} depth={depth + 1} linkAs={linkAs} onCloseMobile={onCloseMobile} />\n ))}\n </ul>\n )}\n </li>\n );\n});\n\nexport function AppShell(props: AppShellProps) {\n const {\n sections, footer, defaultCollapsed = false,\n collapsed: ctrlCollapsed, onCollapsedChange, persistKey,\n children, className, theme = 'default', linkAs,\n } = props;\n const [internalCollapsed, setInternalCollapsed] = React.useState(defaultCollapsed);\n const [mobileOpen, setMobileOpen] = React.useState(false);\n const t = useLocale();\n const collapsed = ctrlCollapsed ?? internalCollapsed;\n\n // SSR-safe persistence: the initial render uses `defaultCollapsed` (server\n // and first client render agree → no hydration mismatch), then we sync from\n // localStorage after mount. Only in uncontrolled mode; reads can throw\n // (Safari private mode), so they're guarded. Runs once per persistKey.\n React.useEffect(() => {\n if (persistKey == null || ctrlCollapsed !== undefined) return;\n try {\n const stored = window.localStorage.getItem(persistKey);\n if (stored === '0' || stored === '1') setInternalCollapsed(stored === '1');\n } catch {\n /* localStorage unavailable — keep defaultCollapsed */\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- sync once at mount; ctrlCollapsed read intentionally not tracked\n }, [persistKey]);\n\n const setCollapsed = (v: boolean) => {\n if (ctrlCollapsed === undefined) setInternalCollapsed(v);\n if (persistKey != null && ctrlCollapsed === undefined) {\n try {\n window.localStorage.setItem(persistKey, v ? '1' : '0');\n } catch {\n /* localStorage unavailable — persistence is best-effort */\n }\n }\n onCollapsedChange?.(v);\n };\n const closeMobile = React.useCallback(() => setMobileOpen(false), []);\n\n // Top-header variant: full-width header above the body. Topbar is\n // invariant to `collapsed` (only the inner body's columns animate); brand\n // lives in `header.center` at the true viewport centre. Default\n // `headerLayout=\"side\"` falls through to the legacy JSX below\n // (byte-identical for existing consumers). The `props.headerLayout`\n // check narrows the discriminated union, so `header` (top) and\n // `brand`/`topbar`/`user` (side) are each only in scope in their branch.\n if (props.headerLayout === 'top') {\n const { header } = props;\n // Header band themes independently of the sidebar; defaults to `theme`\n // so `theme=\"brand\"` keeps tinting both bands (back-compat).\n const headerTheme = props.headerTheme ?? theme;\n const collapsedRail = props.collapsedRail ?? false;\n // Hand the collapse API to header slots so a consumer trigger (hamburger)\n // can toggle the shell — the only path in uncontrolled `top` (no built-in\n // toggle here). A function slot is called with the API; a node renders as-is.\n const headerApi: AppShellHeaderApi = { collapsed, toggle: () => setCollapsed(!collapsed), setCollapsed };\n const slot = (s: AppShellHeaderSlot): React.ReactNode =>\n typeof s === 'function' ? (s as (api: AppShellHeaderApi) => React.ReactNode)(headerApi) : s;\n return (\n <div className={cx('appshell', `appshell--${theme}`, 'appshell--header-top', `appshell--header-${headerTheme}`, collapsedRail && 'appshell--rail', collapsed && 'is-collapsed', className)}>\n {/* On a brand header the band is dark, so re-scope foreground tokens\n via data-tone=\"inverse\" — anything inside (Avatar, badges, links)\n becomes band-aware automatically without per-call-site colors. */}\n <header className=\"appshell__header\" role=\"banner\" data-tone={headerTheme === 'brand' ? 'inverse' : undefined}>\n <div className=\"appshell__header-left\">{slot(header?.left)}</div>\n <div className=\"appshell__header-center\">{slot(header?.center)}</div>\n <div className=\"appshell__header-right\">{slot(header?.right)}</div>\n </header>\n <div className=\"appshell__body\">\n <aside className=\"appshell__sidebar\" aria-label={t['appshell.mainNav']}>\n <nav className=\"appshell__nav\">\n {sections.map((s, i) => (\n <div key={s.id ?? i} className=\"appshell__navsection\">\n {s.label && <div className=\"appshell__navlabel-section\">{s.label}</div>}\n <ul>{s.items.map((it) => (\n <NavItemNode key={it.id} item={it} depth={0} linkAs={linkAs} onCloseMobile={closeMobile} />\n ))}</ul>\n </div>\n ))}\n </nav>\n {footer != null && (\n <div className=\"appshell__sidebar-foot\">{footer}</div>\n )}\n </aside>\n <main className=\"appshell__content\" role=\"main\">{children}</main>\n </div>\n </div>\n );\n }\n\n const { brand, brandCollapsed, topbar, user } = props;\n return (\n <div className={cx('appshell', `appshell--${theme}`, collapsed && 'is-collapsed', mobileOpen && 'is-mobile-open', className)}>\n <aside className=\"appshell__sidebar\" aria-label={t['appshell.mainNav']}>\n <div className=\"appshell__brand\">\n {collapsed ? (brandCollapsed ?? brand) : brand}\n </div>\n <nav className=\"appshell__nav\">\n {sections.map((s, i) => (\n <div key={s.id ?? i} className=\"appshell__navsection\">\n {s.label && <div className=\"appshell__navlabel-section\">{s.label}</div>}\n <ul>{s.items.map((it) => (\n <NavItemNode key={it.id} item={it} depth={0} linkAs={linkAs} onCloseMobile={closeMobile} />\n ))}</ul>\n </div>\n ))}\n </nav>\n <div className=\"appshell__sidebar-foot\">\n {footer}\n <button\n type=\"button\"\n className=\"appshell__collapse\"\n onClick={() => setCollapsed(!collapsed)}\n aria-expanded={!collapsed}\n aria-label={collapsed ? t['appshell.expandMenu'] : t['appshell.collapseMenu']}\n title={collapsed ? t['appshell.expand'] : t['appshell.collapse']}\n >\n {collapsed ? <ChevronRight size={14} /> : <ChevronLeft size={14} />}\n </button>\n </div>\n </aside>\n\n <div className=\"appshell__main\">\n <header className=\"appshell__topbar\">\n <button\n type=\"button\"\n className=\"appshell__hamburger\"\n aria-label={t['appshell.openMenu']}\n aria-expanded={mobileOpen}\n onClick={() => setMobileOpen((o) => !o)}\n ><MenuIcon size={20} /></button>\n <div className=\"appshell__topbar-content\">{topbar}</div>\n {user && <div className=\"appshell__topbar-user\">{user}</div>}\n </header>\n <main className=\"appshell__content\" role=\"main\">{children}</main>\n </div>\n\n {mobileOpen && (\n <div className=\"appshell__scrim\" onClick={() => setMobileOpen(false)} aria-hidden=\"true\" />\n )}\n </div>\n );\n}\n\n// ---------- PageHeader --------------------------------------------------\nexport interface PageHeaderProps {\n title: React.ReactNode;\n description?: React.ReactNode;\n breadcrumbs?: Array<{ label: React.ReactNode; href?: string }>;\n actions?: React.ReactNode;\n meta?: React.ReactNode;\n className?: string;\n}\n\nexport function PageHeader({ title, description, breadcrumbs, actions, meta, className }: PageHeaderProps) {\n const t = useLocale();\n return (\n <div className={cx('page-header', className)}>\n {breadcrumbs && breadcrumbs.length > 0 && (\n <nav className=\"page-header__crumbs\" aria-label={t['appshell.breadcrumb']}>\n <ol>\n {breadcrumbs.map((c, i) => (\n <li key={i}>\n {c.href ? <a href={c.href}>{c.label}</a> : <span aria-current=\"page\">{c.label}</span>}\n {i < breadcrumbs.length - 1 && <span className=\"page-header__crumb-sep\" aria-hidden=\"true\">/</span>}\n </li>\n ))}\n </ol>\n </nav>\n )}\n <div className=\"page-header__row\">\n <div className=\"page-header__title-wrap\">\n <h1 className=\"page-header__title\">{title}</h1>\n {description && <p className=\"page-header__desc\">{description}</p>}\n </div>\n {actions && <div className=\"page-header__actions\">{actions}</div>}\n </div>\n {meta && <div className=\"page-header__meta\">{meta}</div>}\n </div>\n );\n}\n"]}
|
|
@@ -18,13 +18,35 @@ interface NavSection {
|
|
|
18
18
|
}
|
|
19
19
|
type AppShellTheme = 'default' | 'brand';
|
|
20
20
|
type AppShellHeaderLayout = 'side' | 'top';
|
|
21
|
+
/**
|
|
22
|
+
* Collapse API handed to header-slot render-props so a consumer-placed control
|
|
23
|
+
* (e.g. a hamburger in `left`) can drive the sidebar — crucially in
|
|
24
|
+
* **uncontrolled** mode, where the `top` layout otherwise has no toggle
|
|
25
|
+
* affordance. This is what lets `persistKey` (uncontrolled) coexist with a
|
|
26
|
+
* custom header trigger: the kit owns the state + persistence, the consumer
|
|
27
|
+
* owns the trigger's look and placement.
|
|
28
|
+
*/
|
|
29
|
+
interface AppShellHeaderApi {
|
|
30
|
+
/** Current collapsed state. */
|
|
31
|
+
collapsed: boolean;
|
|
32
|
+
/** Flip the collapsed state (persisted if `persistKey` is set). */
|
|
33
|
+
toggle: () => void;
|
|
34
|
+
/** Set the collapsed state explicitly. */
|
|
35
|
+
setCollapsed: (collapsed: boolean) => void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A header slot: a static node, or a render-prop receiving {@link AppShellHeaderApi}.
|
|
39
|
+
* The function form is the only way to toggle an uncontrolled shell from the
|
|
40
|
+
* header (no built-in toggle exists in `top`).
|
|
41
|
+
*/
|
|
42
|
+
type AppShellHeaderSlot = React.ReactNode | ((api: AppShellHeaderApi) => React.ReactNode);
|
|
21
43
|
interface AppShellHeader {
|
|
22
44
|
/** Left slot — typically a menu/hamburger trigger or back action. */
|
|
23
|
-
left?:
|
|
45
|
+
left?: AppShellHeaderSlot;
|
|
24
46
|
/** Center slot — typically the brand (Logo). Lands at the true viewport centre. */
|
|
25
|
-
center?:
|
|
47
|
+
center?: AppShellHeaderSlot;
|
|
26
48
|
/** Right slot — notifications, user avatar, utilities. */
|
|
27
|
-
right?:
|
|
49
|
+
right?: AppShellHeaderSlot;
|
|
28
50
|
}
|
|
29
51
|
/**
|
|
30
52
|
* Props shared by both layouts. The layout-specific props live in
|
|
@@ -137,4 +159,4 @@ interface PageHeaderProps {
|
|
|
137
159
|
}
|
|
138
160
|
declare function PageHeader({ title, description, breadcrumbs, actions, meta, className }: PageHeaderProps): react_jsx_runtime.JSX.Element;
|
|
139
161
|
|
|
140
|
-
export { AppShell, type AppShellBaseProps, type AppShellHeader, type AppShellHeaderLayout, type AppShellProps, type AppShellSideProps, type AppShellTheme, type AppShellTopProps, type NavItem, type NavSection, PageHeader, type PageHeaderProps };
|
|
162
|
+
export { AppShell, type AppShellBaseProps, type AppShellHeader, type AppShellHeaderApi, type AppShellHeaderLayout, type AppShellHeaderSlot, type AppShellProps, type AppShellSideProps, type AppShellTheme, type AppShellTopProps, type NavItem, type NavSection, PageHeader, type PageHeaderProps };
|
|
@@ -18,13 +18,35 @@ interface NavSection {
|
|
|
18
18
|
}
|
|
19
19
|
type AppShellTheme = 'default' | 'brand';
|
|
20
20
|
type AppShellHeaderLayout = 'side' | 'top';
|
|
21
|
+
/**
|
|
22
|
+
* Collapse API handed to header-slot render-props so a consumer-placed control
|
|
23
|
+
* (e.g. a hamburger in `left`) can drive the sidebar — crucially in
|
|
24
|
+
* **uncontrolled** mode, where the `top` layout otherwise has no toggle
|
|
25
|
+
* affordance. This is what lets `persistKey` (uncontrolled) coexist with a
|
|
26
|
+
* custom header trigger: the kit owns the state + persistence, the consumer
|
|
27
|
+
* owns the trigger's look and placement.
|
|
28
|
+
*/
|
|
29
|
+
interface AppShellHeaderApi {
|
|
30
|
+
/** Current collapsed state. */
|
|
31
|
+
collapsed: boolean;
|
|
32
|
+
/** Flip the collapsed state (persisted if `persistKey` is set). */
|
|
33
|
+
toggle: () => void;
|
|
34
|
+
/** Set the collapsed state explicitly. */
|
|
35
|
+
setCollapsed: (collapsed: boolean) => void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A header slot: a static node, or a render-prop receiving {@link AppShellHeaderApi}.
|
|
39
|
+
* The function form is the only way to toggle an uncontrolled shell from the
|
|
40
|
+
* header (no built-in toggle exists in `top`).
|
|
41
|
+
*/
|
|
42
|
+
type AppShellHeaderSlot = React.ReactNode | ((api: AppShellHeaderApi) => React.ReactNode);
|
|
21
43
|
interface AppShellHeader {
|
|
22
44
|
/** Left slot — typically a menu/hamburger trigger or back action. */
|
|
23
|
-
left?:
|
|
45
|
+
left?: AppShellHeaderSlot;
|
|
24
46
|
/** Center slot — typically the brand (Logo). Lands at the true viewport centre. */
|
|
25
|
-
center?:
|
|
47
|
+
center?: AppShellHeaderSlot;
|
|
26
48
|
/** Right slot — notifications, user avatar, utilities. */
|
|
27
|
-
right?:
|
|
49
|
+
right?: AppShellHeaderSlot;
|
|
28
50
|
}
|
|
29
51
|
/**
|
|
30
52
|
* Props shared by both layouts. The layout-specific props live in
|
|
@@ -137,4 +159,4 @@ interface PageHeaderProps {
|
|
|
137
159
|
}
|
|
138
160
|
declare function PageHeader({ title, description, breadcrumbs, actions, meta, className }: PageHeaderProps): react_jsx_runtime.JSX.Element;
|
|
139
161
|
|
|
140
|
-
export { AppShell, type AppShellBaseProps, type AppShellHeader, type AppShellHeaderLayout, type AppShellProps, type AppShellSideProps, type AppShellTheme, type AppShellTopProps, type NavItem, type NavSection, PageHeader, type PageHeaderProps };
|
|
162
|
+
export { AppShell, type AppShellBaseProps, type AppShellHeader, type AppShellHeaderApi, type AppShellHeaderLayout, type AppShellHeaderSlot, type AppShellProps, type AppShellSideProps, type AppShellTheme, type AppShellTopProps, type NavItem, type NavSection, PageHeader, type PageHeaderProps };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var chunkS4YPRJQQ_js = require('../chunk-S4YPRJQQ.js');
|
|
5
5
|
require('../chunk-4VMQLSHV.js');
|
|
6
6
|
require('../chunk-RQOTH7I7.js');
|
|
7
7
|
require('../chunk-PASF6T4H.js');
|
|
@@ -10,11 +10,11 @@ require('../chunk-PASF6T4H.js');
|
|
|
10
10
|
|
|
11
11
|
Object.defineProperty(exports, "AppShell", {
|
|
12
12
|
enumerable: true,
|
|
13
|
-
get: function () { return
|
|
13
|
+
get: function () { return chunkS4YPRJQQ_js.AppShell; }
|
|
14
14
|
});
|
|
15
15
|
Object.defineProperty(exports, "PageHeader", {
|
|
16
16
|
enumerable: true,
|
|
17
|
-
get: function () { return
|
|
17
|
+
get: function () { return chunkS4YPRJQQ_js.PageHeader; }
|
|
18
18
|
});
|
|
19
19
|
//# sourceMappingURL=AppShell.js.map
|
|
20
20
|
//# sourceMappingURL=AppShell.js.map
|
|
@@ -80,7 +80,7 @@ declare function Grid({ columns, gap, minColWidth, className, style, ...rest }:
|
|
|
80
80
|
* Every Layout). Sugar for `<Stack direction="row" wrap>`. Use for tag
|
|
81
81
|
* lists, button rows, filter chips: items flow and wrap, gap stays even.
|
|
82
82
|
*/
|
|
83
|
-
declare const Cluster: React.ForwardRefExoticComponent<Omit<StackProps, "
|
|
83
|
+
declare const Cluster: React.ForwardRefExoticComponent<Omit<StackProps, "direction" | "wrap"> & React.RefAttributes<HTMLDivElement>>;
|
|
84
84
|
interface SpacerProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
85
85
|
/** Fixed size on a space token. Omit for a flexible spacer (`flex: 1`). */
|
|
86
86
|
size?: SpaceToken;
|
|
@@ -80,7 +80,7 @@ declare function Grid({ columns, gap, minColWidth, className, style, ...rest }:
|
|
|
80
80
|
* Every Layout). Sugar for `<Stack direction="row" wrap>`. Use for tag
|
|
81
81
|
* lists, button rows, filter chips: items flow and wrap, gap stays even.
|
|
82
82
|
*/
|
|
83
|
-
declare const Cluster: React.ForwardRefExoticComponent<Omit<StackProps, "
|
|
83
|
+
declare const Cluster: React.ForwardRefExoticComponent<Omit<StackProps, "direction" | "wrap"> & React.RefAttributes<HTMLDivElement>>;
|
|
84
84
|
interface SpacerProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
85
85
|
/** Fixed size on a space token. Omit for a flexible spacer (`flex: 1`). */
|
|
86
86
|
size?: SpaceToken;
|
package/dist/index.d.mts
CHANGED
|
@@ -15,7 +15,7 @@ export { Accordion, AccordionItem, AccordionProps, BreadcrumbItem, Breadcrumbs,
|
|
|
15
15
|
export { CheckboxGroup, CheckboxGroupProps, MoneyInput, MoneyInputProps, OptionItem, PhoneInput, PhoneInputProps, Progress, ProgressCircle, ProgressCircleProps, ProgressProps, RadioGroup, RadioGroupProps, Slider, SliderProps, TagInput, TagInputProps, TimePicker, TimePickerProps } from './components/InputsExtra.mjs';
|
|
16
16
|
export { Avatar, AvatarGroup, AvatarGroupProps, AvatarProps, Menu, MenuItemProps, MenuProps, Stat, StatProps } from './components/Display2.mjs';
|
|
17
17
|
export { CommandItem, CommandPalette, CommandPaletteProps, DateRange, DateRangePicker, DateRangePickerProps, MultiCombobox, MultiComboboxOption, MultiComboboxProps, UseCommandPaletteOptions, useCommandPalette } from './components/AdvancedPickers.mjs';
|
|
18
|
-
export { AppShell, AppShellBaseProps, AppShellHeader, AppShellHeaderLayout, AppShellProps, AppShellSideProps, AppShellTheme, AppShellTopProps, NavItem, NavSection, PageHeader, PageHeaderProps } from './components/AppShell.mjs';
|
|
18
|
+
export { AppShell, AppShellBaseProps, AppShellHeader, AppShellHeaderApi, AppShellHeaderLayout, AppShellHeaderSlot, AppShellProps, AppShellSideProps, AppShellTheme, AppShellTopProps, NavItem, NavSection, PageHeader, PageHeaderProps } from './components/AppShell.mjs';
|
|
19
19
|
export { AreaChart, AreaChartProps, BarChart, BarChartProps, BaseChartProps, DonutChart, DonutChartProps, LineChart, LineChartProps, RechartsLike, Sparkline, SparklineProps } from './components/Charts.mjs';
|
|
20
20
|
export { AlertCircle, AlertTriangle, AlignCenter, AlignLeft, AlignRight, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Bell, Bold, Building, CalendarIcon, Check, CheckCircle, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Clock, Copy, CreditCard, Download, Edit, ExternalLink, Eye, EyeOff, FileText, Filter, Folder, Globe, Heart, Home, IconProps, Info, Italic, Link, Loader, Lock, LogOut, Mail, MapPin, MenuIcon, Minus, Moon, MoreHorizontal, MoreVertical, Package, Phone, Plus, RefreshCw, Search, Settings, ShoppingCart, Star, Sun, Tag, Tool, Trash, Truck, Underline, Unlock, Upload, User, Users, Wrench, X } from './components/Icons.mjs';
|
|
21
21
|
export { Logo, LogoBg, LogoFormat, LogoProps, LogoVariant } from './components/Logo.mjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export { Accordion, AccordionItem, AccordionProps, BreadcrumbItem, Breadcrumbs,
|
|
|
15
15
|
export { CheckboxGroup, CheckboxGroupProps, MoneyInput, MoneyInputProps, OptionItem, PhoneInput, PhoneInputProps, Progress, ProgressCircle, ProgressCircleProps, ProgressProps, RadioGroup, RadioGroupProps, Slider, SliderProps, TagInput, TagInputProps, TimePicker, TimePickerProps } from './components/InputsExtra.js';
|
|
16
16
|
export { Avatar, AvatarGroup, AvatarGroupProps, AvatarProps, Menu, MenuItemProps, MenuProps, Stat, StatProps } from './components/Display2.js';
|
|
17
17
|
export { CommandItem, CommandPalette, CommandPaletteProps, DateRange, DateRangePicker, DateRangePickerProps, MultiCombobox, MultiComboboxOption, MultiComboboxProps, UseCommandPaletteOptions, useCommandPalette } from './components/AdvancedPickers.js';
|
|
18
|
-
export { AppShell, AppShellBaseProps, AppShellHeader, AppShellHeaderLayout, AppShellProps, AppShellSideProps, AppShellTheme, AppShellTopProps, NavItem, NavSection, PageHeader, PageHeaderProps } from './components/AppShell.js';
|
|
18
|
+
export { AppShell, AppShellBaseProps, AppShellHeader, AppShellHeaderApi, AppShellHeaderLayout, AppShellHeaderSlot, AppShellProps, AppShellSideProps, AppShellTheme, AppShellTopProps, NavItem, NavSection, PageHeader, PageHeaderProps } from './components/AppShell.js';
|
|
19
19
|
export { AreaChart, AreaChartProps, BarChart, BarChartProps, BaseChartProps, DonutChart, DonutChartProps, LineChart, LineChartProps, RechartsLike, Sparkline, SparklineProps } from './components/Charts.js';
|
|
20
20
|
export { AlertCircle, AlertTriangle, AlignCenter, AlignLeft, AlignRight, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, Bell, Bold, Building, CalendarIcon, Check, CheckCircle, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Clock, Copy, CreditCard, Download, Edit, ExternalLink, Eye, EyeOff, FileText, Filter, Folder, Globe, Heart, Home, IconProps, Info, Italic, Link, Loader, Lock, LogOut, Mail, MapPin, MenuIcon, Minus, Moon, MoreHorizontal, MoreVertical, Package, Phone, Plus, RefreshCw, Search, Settings, ShoppingCart, Star, Sun, Tag, Tool, Trash, Truck, Underline, Unlock, Upload, User, Users, Wrench, X } from './components/Icons.js';
|
|
21
21
|
export { Logo, LogoBg, LogoFormat, LogoProps, LogoVariant } from './components/Logo.js';
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
require('./chunk-4REALDR3.js');
|
|
5
4
|
require('./chunk-427NHGTX.js');
|
|
5
|
+
require('./chunk-4REALDR3.js');
|
|
6
6
|
require('./chunk-2LTV7VB5.js');
|
|
7
7
|
var chunkJT2SFHBH_js = require('./chunk-JT2SFHBH.js');
|
|
8
8
|
var chunkMOHWK6FH_js = require('./chunk-MOHWK6FH.js');
|
|
@@ -41,7 +41,7 @@ var chunkXMLBKK7X_js = require('./chunk-XMLBKK7X.js');
|
|
|
41
41
|
var chunk7I5LFBQR_js = require('./chunk-7I5LFBQR.js');
|
|
42
42
|
var chunkWAGWB35Q_js = require('./chunk-WAGWB35Q.js');
|
|
43
43
|
var chunk3PXYCXDW_js = require('./chunk-3PXYCXDW.js');
|
|
44
|
-
var
|
|
44
|
+
var chunkS4YPRJQQ_js = require('./chunk-S4YPRJQQ.js');
|
|
45
45
|
var chunk4VMQLSHV_js = require('./chunk-4VMQLSHV.js');
|
|
46
46
|
var chunkAJ22SXI2_js = require('./chunk-AJ22SXI2.js');
|
|
47
47
|
var chunkEUB4PHPI_js = require('./chunk-EUB4PHPI.js');
|
|
@@ -667,11 +667,11 @@ Object.defineProperty(exports, "resetBrand", {
|
|
|
667
667
|
});
|
|
668
668
|
Object.defineProperty(exports, "AppShell", {
|
|
669
669
|
enumerable: true,
|
|
670
|
-
get: function () { return
|
|
670
|
+
get: function () { return chunkS4YPRJQQ_js.AppShell; }
|
|
671
671
|
});
|
|
672
672
|
Object.defineProperty(exports, "PageHeader", {
|
|
673
673
|
enumerable: true,
|
|
674
|
-
get: function () { return
|
|
674
|
+
get: function () { return chunkS4YPRJQQ_js.PageHeader; }
|
|
675
675
|
});
|
|
676
676
|
Object.defineProperty(exports, "LocaleProvider", {
|
|
677
677
|
enumerable: true,
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import './chunk-CHVTPN3K.mjs';
|
|
3
2
|
import './chunk-BVQRDAR7.mjs';
|
|
3
|
+
import './chunk-CHVTPN3K.mjs';
|
|
4
4
|
import './chunk-XTHC46M2.mjs';
|
|
5
5
|
export { PermissionMatrix } from './chunk-LVLJ75ND.mjs';
|
|
6
6
|
export { Combobox, DatePicker, FileUpload, MonthPicker, YearPicker } from './chunk-L4JQRY4U.mjs';
|
|
@@ -39,7 +39,7 @@ export { useDismiss } from './chunk-6P2TKRTL.mjs';
|
|
|
39
39
|
export { usePopoverPosition } from './chunk-PRML6VEF.mjs';
|
|
40
40
|
export { Portal } from './chunk-FKBQYQQD.mjs';
|
|
41
41
|
export { BRAND_DEFAULTS, configureBrand, getBrand, resetBrand } from './chunk-5GEWIK4T.mjs';
|
|
42
|
-
export { AppShell, PageHeader } from './chunk-
|
|
42
|
+
export { AppShell, PageHeader } from './chunk-HAZDZYF4.mjs';
|
|
43
43
|
export { LocaleProvider, esMessages, useLocale } from './chunk-PQV7HHCJ.mjs';
|
|
44
44
|
export { Button, ButtonGroup } from './chunk-NAL457NQ.mjs';
|
|
45
45
|
export { AspectRatio, ScrollArea, Separator, Slot, Slottable } from './chunk-IEPKSPBX.mjs';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@misael703/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.0",
|
|
4
4
|
"description": "Generic React + TypeScript UI kit, optimized for Next.js. Tokens, accessible components, runtime-configurable theming via presets.",
|
|
5
5
|
"author": "Misael Ocas <misael.ocas@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/AppShell.tsx"],"names":["React","NavItemNode","cx","jsxs","Fragment","jsx","useLocale","ChevronRight","ChevronLeft","MenuIcon"],"mappings":"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","file":"chunk-GGYKVABI.js","sourcesContent":["'use client';\nimport * as React from 'react';\nimport { cx } from '../utils/cx';\nimport { ChevronLeft, ChevronRight, MenuIcon } from './Icons';\nimport { useLocale } from '../locale/LocaleProvider';\n\n// ---------- AppShell (Sidebar + Topbar + Content) -----------------------\n// Designed to drop into a Next.js app/layout.tsx as a Client Component shell.\n\nexport interface NavItem {\n id: string;\n label: React.ReactNode;\n icon?: React.ReactNode;\n href?: string;\n active?: boolean;\n badge?: React.ReactNode;\n onSelect?: () => void;\n children?: NavItem[];\n}\n\nexport interface NavSection {\n id?: string;\n label?: React.ReactNode;\n items: NavItem[];\n}\n\nexport type AppShellTheme = 'default' | 'brand';\n\nexport type AppShellHeaderLayout = 'side' | 'top';\n\nexport interface AppShellHeader {\n /** Left slot — typically a menu/hamburger trigger or back action. */\n left?: React.ReactNode;\n /** Center slot — typically the brand (Logo). Lands at the true viewport centre. */\n center?: React.ReactNode;\n /** Right slot — notifications, user avatar, utilities. */\n right?: React.ReactNode;\n}\n\n/**\n * Props shared by both layouts. The layout-specific props live in\n * `AppShellSideProps` / `AppShellTopProps`; `AppShellProps` is the\n * discriminated union of the two, keyed on `headerLayout`.\n */\nexport interface AppShellBaseProps {\n sections: NavSection[];\n footer?: React.ReactNode;\n defaultCollapsed?: boolean;\n collapsed?: boolean;\n onCollapsedChange?: (c: boolean) => void;\n /**\n * Persist the collapsed state in `localStorage[persistKey]` so it survives\n * reloads. Opt-in: omit it and the shell keeps the default behaviour (resets\n * to `defaultCollapsed` on each mount). SSR-safe — the initial render still\n * uses `defaultCollapsed` (no hydration mismatch); the stored value is read\n * after mount, so a differing value may flash for one frame. Ignored in\n * controlled mode (when `collapsed` is provided): the host owns persistence.\n */\n persistKey?: string;\n children: React.ReactNode;\n className?: string;\n /**\n * Color theme (applies to both layouts):\n * - `default` (light): claro, mejor para apps data-heavy de uso prolongado.\n * - `brand`: superficie azul de marca con texto blanco. Mayor brand recall.\n * En `side` tiñe el sidebar; en `top` tiñe header + sidebar (un solo knob).\n */\n theme?: AppShellTheme;\n /** Render-prop for navigation links so the host app can use Next.js Link, etc. */\n linkAs?: (item: NavItem, content: React.ReactNode, className: string) => React.ReactNode;\n}\n\n/**\n * Sidebar layout (default, `headerLayout=\"side\"` or omitted). The brand\n * block + collapse rail live in the sidebar; the topbar sits over the\n * content with a mobile hamburger. `header` is **not** valid here — that\n * slot belongs to the `top` layout.\n */\nexport interface AppShellSideProps extends AppShellBaseProps {\n headerLayout?: 'side';\n /** Brand node in the sidebar header (expanded state). */\n brand?: React.ReactNode;\n /** Brand node shown when the rail is collapsed. Falls back to `brand`. */\n brandCollapsed?: React.ReactNode;\n /** Content of the topbar over the page (search, etc.). */\n topbar?: React.ReactNode;\n /** User slot at the right of the topbar (avatar/menu). */\n user?: React.ReactNode;\n /** Not valid in the `side` layout — the header slots belong to `top`. */\n header?: never;\n}\n\n/**\n * Top-header layout (`headerLayout=\"top\"`, v1.15.0). Full-width header\n * above the body with three slots (`header.{left,center,right}`); the\n * centre slot lands at the **true viewport centre** (1fr·auto·1fr grid).\n * The sidebar has no brand block and `collapsed` hides it entirely (no\n * 72px rail); the header is **invariant** to the collapse. `theme=\"brand\"`\n * tints both bands. The `side`-only props are **not** valid here — put your\n * chrome in `header`.\n */\nexport interface AppShellTopProps extends AppShellBaseProps {\n headerLayout: 'top';\n /** Slots for the full-width header. Brand usually goes in `center`. */\n header?: AppShellHeader;\n /**\n * Theme of the **header band only**, independent of the sidebar (`theme`).\n * Defaults to `theme`, so `theme=\"brand\"` still tints both bands (no\n * change for existing consumers). Set `theme=\"default\" headerTheme=\"brand\"`\n * for a branded top bar over a neutral, legible sidebar — common in\n * data-heavy admin apps.\n */\n headerTheme?: AppShellTheme;\n /**\n * Collapse to an icon rail (72px) instead of hiding the sidebar entirely.\n * Default `false` → `collapsed` hides the sidebar (the original `top`\n * behavior). `true` → `collapsed` keeps a 72px rail showing the nav icons\n * (labels hidden, active-item bar kept) — like the `side` layout — and\n * renders a built-in expand/collapse toggle at the bottom of the rail.\n */\n collapsedRail?: boolean;\n /** Not valid in `top` — use `header.center` for the brand. */\n brand?: never;\n /** Not valid in `top` — the sidebar collapses entirely. */\n brandCollapsed?: never;\n /** Not valid in `top` — use the `header` slots. */\n topbar?: never;\n /** Not valid in `top` — use `header.right`. */\n user?: never;\n}\n\n/**\n * Discriminated union keyed on `headerLayout`. TypeScript enforces that\n * `header` is only accepted with `headerLayout=\"top\"` and that\n * `brand`/`brandCollapsed`/`topbar`/`user` are only accepted with the\n * (default) `side` layout — passing the wrong prop for the layout is a\n * compile error instead of being silently ignored at runtime.\n */\nexport type AppShellProps = AppShellSideProps | AppShellTopProps;\n\n// Recursive nav item, memoized so a single item's parent re-render doesn't\n// churn through every other item in the tree. Stability of `linkAs` and\n// `onCloseMobile` is the parent's responsibility (we stabilize\n// `onCloseMobile` via useCallback below; consumers should memoize `linkAs`\n// if they care about avoiding renders, but for typical Next.js Link usage\n// the inline arrow is rarely a hot path).\ninterface NavItemNodeProps {\n item: NavItem;\n depth: number;\n linkAs?: AppShellBaseProps['linkAs'];\n onCloseMobile: () => void;\n}\n\nconst NavItemNode = React.memo(function NavItemNode({\n item, depth, linkAs, onCloseMobile,\n}: NavItemNodeProps) {\n const klass = cx('appshell__navitem', item.active && 'is-active', `appshell__navitem--depth-${depth}`);\n const inner = (\n <>\n {item.icon && <span className=\"appshell__navicon\" aria-hidden=\"true\">{item.icon}</span>}\n <span className=\"appshell__navlabel\">{item.label}</span>\n {item.badge && <span className=\"appshell__navbadge\">{item.badge}</span>}\n </>\n );\n const node = item.href && linkAs\n ? linkAs(item, inner, klass)\n : (\n <a\n href={item.href ?? '#'}\n className={klass}\n aria-current={item.active ? 'page' : undefined}\n onClick={(e) => {\n if (!item.href) e.preventDefault();\n item.onSelect?.();\n onCloseMobile();\n }}\n >\n {inner}\n </a>\n );\n return (\n <li>\n {node}\n {item.children && item.children.length > 0 && (\n <ul className=\"appshell__navchildren\">\n {item.children.map((c) => (\n <NavItemNode key={c.id} item={c} depth={depth + 1} linkAs={linkAs} onCloseMobile={onCloseMobile} />\n ))}\n </ul>\n )}\n </li>\n );\n});\n\nexport function AppShell(props: AppShellProps) {\n const {\n sections, footer, defaultCollapsed = false,\n collapsed: ctrlCollapsed, onCollapsedChange, persistKey,\n children, className, theme = 'default', linkAs,\n } = props;\n const [internalCollapsed, setInternalCollapsed] = React.useState(defaultCollapsed);\n const [mobileOpen, setMobileOpen] = React.useState(false);\n const t = useLocale();\n const collapsed = ctrlCollapsed ?? internalCollapsed;\n\n // SSR-safe persistence: the initial render uses `defaultCollapsed` (server\n // and first client render agree → no hydration mismatch), then we sync from\n // localStorage after mount. Only in uncontrolled mode; reads can throw\n // (Safari private mode), so they're guarded. Runs once per persistKey.\n React.useEffect(() => {\n if (persistKey == null || ctrlCollapsed !== undefined) return;\n try {\n const stored = window.localStorage.getItem(persistKey);\n if (stored === '0' || stored === '1') setInternalCollapsed(stored === '1');\n } catch {\n /* localStorage unavailable — keep defaultCollapsed */\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- sync once at mount; ctrlCollapsed read intentionally not tracked\n }, [persistKey]);\n\n const setCollapsed = (v: boolean) => {\n if (ctrlCollapsed === undefined) setInternalCollapsed(v);\n if (persistKey != null && ctrlCollapsed === undefined) {\n try {\n window.localStorage.setItem(persistKey, v ? '1' : '0');\n } catch {\n /* localStorage unavailable — persistence is best-effort */\n }\n }\n onCollapsedChange?.(v);\n };\n const closeMobile = React.useCallback(() => setMobileOpen(false), []);\n\n // Top-header variant: full-width header above the body. Topbar is\n // invariant to `collapsed` (only the inner body's columns animate); brand\n // lives in `header.center` at the true viewport centre. Default\n // `headerLayout=\"side\"` falls through to the legacy JSX below\n // (byte-identical for existing consumers). The `props.headerLayout`\n // check narrows the discriminated union, so `header` (top) and\n // `brand`/`topbar`/`user` (side) are each only in scope in their branch.\n if (props.headerLayout === 'top') {\n const { header } = props;\n // Header band themes independently of the sidebar; defaults to `theme`\n // so `theme=\"brand\"` keeps tinting both bands (back-compat).\n const headerTheme = props.headerTheme ?? theme;\n const collapsedRail = props.collapsedRail ?? false;\n return (\n <div className={cx('appshell', `appshell--${theme}`, 'appshell--header-top', `appshell--header-${headerTheme}`, collapsedRail && 'appshell--rail', collapsed && 'is-collapsed', className)}>\n {/* On a brand header the band is dark, so re-scope foreground tokens\n via data-tone=\"inverse\" — anything inside (Avatar, badges, links)\n becomes band-aware automatically without per-call-site colors. */}\n <header className=\"appshell__header\" role=\"banner\" data-tone={headerTheme === 'brand' ? 'inverse' : undefined}>\n <div className=\"appshell__header-left\">{header?.left}</div>\n <div className=\"appshell__header-center\">{header?.center}</div>\n <div className=\"appshell__header-right\">{header?.right}</div>\n </header>\n <div className=\"appshell__body\">\n <aside className=\"appshell__sidebar\" aria-label={t['appshell.mainNav']}>\n <nav className=\"appshell__nav\">\n {sections.map((s, i) => (\n <div key={s.id ?? i} className=\"appshell__navsection\">\n {s.label && <div className=\"appshell__navlabel-section\">{s.label}</div>}\n <ul>{s.items.map((it) => (\n <NavItemNode key={it.id} item={it} depth={0} linkAs={linkAs} onCloseMobile={closeMobile} />\n ))}</ul>\n </div>\n ))}\n </nav>\n {footer != null && (\n <div className=\"appshell__sidebar-foot\">{footer}</div>\n )}\n </aside>\n <main className=\"appshell__content\" role=\"main\">{children}</main>\n </div>\n </div>\n );\n }\n\n const { brand, brandCollapsed, topbar, user } = props;\n return (\n <div className={cx('appshell', `appshell--${theme}`, collapsed && 'is-collapsed', mobileOpen && 'is-mobile-open', className)}>\n <aside className=\"appshell__sidebar\" aria-label={t['appshell.mainNav']}>\n <div className=\"appshell__brand\">\n {collapsed ? (brandCollapsed ?? brand) : brand}\n </div>\n <nav className=\"appshell__nav\">\n {sections.map((s, i) => (\n <div key={s.id ?? i} className=\"appshell__navsection\">\n {s.label && <div className=\"appshell__navlabel-section\">{s.label}</div>}\n <ul>{s.items.map((it) => (\n <NavItemNode key={it.id} item={it} depth={0} linkAs={linkAs} onCloseMobile={closeMobile} />\n ))}</ul>\n </div>\n ))}\n </nav>\n <div className=\"appshell__sidebar-foot\">\n {footer}\n <button\n type=\"button\"\n className=\"appshell__collapse\"\n onClick={() => setCollapsed(!collapsed)}\n aria-expanded={!collapsed}\n aria-label={collapsed ? t['appshell.expandMenu'] : t['appshell.collapseMenu']}\n title={collapsed ? t['appshell.expand'] : t['appshell.collapse']}\n >\n {collapsed ? <ChevronRight size={14} /> : <ChevronLeft size={14} />}\n </button>\n </div>\n </aside>\n\n <div className=\"appshell__main\">\n <header className=\"appshell__topbar\">\n <button\n type=\"button\"\n className=\"appshell__hamburger\"\n aria-label={t['appshell.openMenu']}\n aria-expanded={mobileOpen}\n onClick={() => setMobileOpen((o) => !o)}\n ><MenuIcon size={20} /></button>\n <div className=\"appshell__topbar-content\">{topbar}</div>\n {user && <div className=\"appshell__topbar-user\">{user}</div>}\n </header>\n <main className=\"appshell__content\" role=\"main\">{children}</main>\n </div>\n\n {mobileOpen && (\n <div className=\"appshell__scrim\" onClick={() => setMobileOpen(false)} aria-hidden=\"true\" />\n )}\n </div>\n );\n}\n\n// ---------- PageHeader --------------------------------------------------\nexport interface PageHeaderProps {\n title: React.ReactNode;\n description?: React.ReactNode;\n breadcrumbs?: Array<{ label: React.ReactNode; href?: string }>;\n actions?: React.ReactNode;\n meta?: React.ReactNode;\n className?: string;\n}\n\nexport function PageHeader({ title, description, breadcrumbs, actions, meta, className }: PageHeaderProps) {\n const t = useLocale();\n return (\n <div className={cx('page-header', className)}>\n {breadcrumbs && breadcrumbs.length > 0 && (\n <nav className=\"page-header__crumbs\" aria-label={t['appshell.breadcrumb']}>\n <ol>\n {breadcrumbs.map((c, i) => (\n <li key={i}>\n {c.href ? <a href={c.href}>{c.label}</a> : <span aria-current=\"page\">{c.label}</span>}\n {i < breadcrumbs.length - 1 && <span className=\"page-header__crumb-sep\" aria-hidden=\"true\">/</span>}\n </li>\n ))}\n </ol>\n </nav>\n )}\n <div className=\"page-header__row\">\n <div className=\"page-header__title-wrap\">\n <h1 className=\"page-header__title\">{title}</h1>\n {description && <p className=\"page-header__desc\">{description}</p>}\n </div>\n {actions && <div className=\"page-header__actions\">{actions}</div>}\n </div>\n {meta && <div className=\"page-header__meta\">{meta}</div>}\n </div>\n );\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/AppShell.tsx"],"names":["NavItemNode"],"mappings":"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","file":"chunk-HSDOGR3N.mjs","sourcesContent":["'use client';\nimport * as React from 'react';\nimport { cx } from '../utils/cx';\nimport { ChevronLeft, ChevronRight, MenuIcon } from './Icons';\nimport { useLocale } from '../locale/LocaleProvider';\n\n// ---------- AppShell (Sidebar + Topbar + Content) -----------------------\n// Designed to drop into a Next.js app/layout.tsx as a Client Component shell.\n\nexport interface NavItem {\n id: string;\n label: React.ReactNode;\n icon?: React.ReactNode;\n href?: string;\n active?: boolean;\n badge?: React.ReactNode;\n onSelect?: () => void;\n children?: NavItem[];\n}\n\nexport interface NavSection {\n id?: string;\n label?: React.ReactNode;\n items: NavItem[];\n}\n\nexport type AppShellTheme = 'default' | 'brand';\n\nexport type AppShellHeaderLayout = 'side' | 'top';\n\nexport interface AppShellHeader {\n /** Left slot — typically a menu/hamburger trigger or back action. */\n left?: React.ReactNode;\n /** Center slot — typically the brand (Logo). Lands at the true viewport centre. */\n center?: React.ReactNode;\n /** Right slot — notifications, user avatar, utilities. */\n right?: React.ReactNode;\n}\n\n/**\n * Props shared by both layouts. The layout-specific props live in\n * `AppShellSideProps` / `AppShellTopProps`; `AppShellProps` is the\n * discriminated union of the two, keyed on `headerLayout`.\n */\nexport interface AppShellBaseProps {\n sections: NavSection[];\n footer?: React.ReactNode;\n defaultCollapsed?: boolean;\n collapsed?: boolean;\n onCollapsedChange?: (c: boolean) => void;\n /**\n * Persist the collapsed state in `localStorage[persistKey]` so it survives\n * reloads. Opt-in: omit it and the shell keeps the default behaviour (resets\n * to `defaultCollapsed` on each mount). SSR-safe — the initial render still\n * uses `defaultCollapsed` (no hydration mismatch); the stored value is read\n * after mount, so a differing value may flash for one frame. Ignored in\n * controlled mode (when `collapsed` is provided): the host owns persistence.\n */\n persistKey?: string;\n children: React.ReactNode;\n className?: string;\n /**\n * Color theme (applies to both layouts):\n * - `default` (light): claro, mejor para apps data-heavy de uso prolongado.\n * - `brand`: superficie azul de marca con texto blanco. Mayor brand recall.\n * En `side` tiñe el sidebar; en `top` tiñe header + sidebar (un solo knob).\n */\n theme?: AppShellTheme;\n /** Render-prop for navigation links so the host app can use Next.js Link, etc. */\n linkAs?: (item: NavItem, content: React.ReactNode, className: string) => React.ReactNode;\n}\n\n/**\n * Sidebar layout (default, `headerLayout=\"side\"` or omitted). The brand\n * block + collapse rail live in the sidebar; the topbar sits over the\n * content with a mobile hamburger. `header` is **not** valid here — that\n * slot belongs to the `top` layout.\n */\nexport interface AppShellSideProps extends AppShellBaseProps {\n headerLayout?: 'side';\n /** Brand node in the sidebar header (expanded state). */\n brand?: React.ReactNode;\n /** Brand node shown when the rail is collapsed. Falls back to `brand`. */\n brandCollapsed?: React.ReactNode;\n /** Content of the topbar over the page (search, etc.). */\n topbar?: React.ReactNode;\n /** User slot at the right of the topbar (avatar/menu). */\n user?: React.ReactNode;\n /** Not valid in the `side` layout — the header slots belong to `top`. */\n header?: never;\n}\n\n/**\n * Top-header layout (`headerLayout=\"top\"`, v1.15.0). Full-width header\n * above the body with three slots (`header.{left,center,right}`); the\n * centre slot lands at the **true viewport centre** (1fr·auto·1fr grid).\n * The sidebar has no brand block and `collapsed` hides it entirely (no\n * 72px rail); the header is **invariant** to the collapse. `theme=\"brand\"`\n * tints both bands. The `side`-only props are **not** valid here — put your\n * chrome in `header`.\n */\nexport interface AppShellTopProps extends AppShellBaseProps {\n headerLayout: 'top';\n /** Slots for the full-width header. Brand usually goes in `center`. */\n header?: AppShellHeader;\n /**\n * Theme of the **header band only**, independent of the sidebar (`theme`).\n * Defaults to `theme`, so `theme=\"brand\"` still tints both bands (no\n * change for existing consumers). Set `theme=\"default\" headerTheme=\"brand\"`\n * for a branded top bar over a neutral, legible sidebar — common in\n * data-heavy admin apps.\n */\n headerTheme?: AppShellTheme;\n /**\n * Collapse to an icon rail (72px) instead of hiding the sidebar entirely.\n * Default `false` → `collapsed` hides the sidebar (the original `top`\n * behavior). `true` → `collapsed` keeps a 72px rail showing the nav icons\n * (labels hidden, active-item bar kept) — like the `side` layout — and\n * renders a built-in expand/collapse toggle at the bottom of the rail.\n */\n collapsedRail?: boolean;\n /** Not valid in `top` — use `header.center` for the brand. */\n brand?: never;\n /** Not valid in `top` — the sidebar collapses entirely. */\n brandCollapsed?: never;\n /** Not valid in `top` — use the `header` slots. */\n topbar?: never;\n /** Not valid in `top` — use `header.right`. */\n user?: never;\n}\n\n/**\n * Discriminated union keyed on `headerLayout`. TypeScript enforces that\n * `header` is only accepted with `headerLayout=\"top\"` and that\n * `brand`/`brandCollapsed`/`topbar`/`user` are only accepted with the\n * (default) `side` layout — passing the wrong prop for the layout is a\n * compile error instead of being silently ignored at runtime.\n */\nexport type AppShellProps = AppShellSideProps | AppShellTopProps;\n\n// Recursive nav item, memoized so a single item's parent re-render doesn't\n// churn through every other item in the tree. Stability of `linkAs` and\n// `onCloseMobile` is the parent's responsibility (we stabilize\n// `onCloseMobile` via useCallback below; consumers should memoize `linkAs`\n// if they care about avoiding renders, but for typical Next.js Link usage\n// the inline arrow is rarely a hot path).\ninterface NavItemNodeProps {\n item: NavItem;\n depth: number;\n linkAs?: AppShellBaseProps['linkAs'];\n onCloseMobile: () => void;\n}\n\nconst NavItemNode = React.memo(function NavItemNode({\n item, depth, linkAs, onCloseMobile,\n}: NavItemNodeProps) {\n const klass = cx('appshell__navitem', item.active && 'is-active', `appshell__navitem--depth-${depth}`);\n const inner = (\n <>\n {item.icon && <span className=\"appshell__navicon\" aria-hidden=\"true\">{item.icon}</span>}\n <span className=\"appshell__navlabel\">{item.label}</span>\n {item.badge && <span className=\"appshell__navbadge\">{item.badge}</span>}\n </>\n );\n const node = item.href && linkAs\n ? linkAs(item, inner, klass)\n : (\n <a\n href={item.href ?? '#'}\n className={klass}\n aria-current={item.active ? 'page' : undefined}\n onClick={(e) => {\n if (!item.href) e.preventDefault();\n item.onSelect?.();\n onCloseMobile();\n }}\n >\n {inner}\n </a>\n );\n return (\n <li>\n {node}\n {item.children && item.children.length > 0 && (\n <ul className=\"appshell__navchildren\">\n {item.children.map((c) => (\n <NavItemNode key={c.id} item={c} depth={depth + 1} linkAs={linkAs} onCloseMobile={onCloseMobile} />\n ))}\n </ul>\n )}\n </li>\n );\n});\n\nexport function AppShell(props: AppShellProps) {\n const {\n sections, footer, defaultCollapsed = false,\n collapsed: ctrlCollapsed, onCollapsedChange, persistKey,\n children, className, theme = 'default', linkAs,\n } = props;\n const [internalCollapsed, setInternalCollapsed] = React.useState(defaultCollapsed);\n const [mobileOpen, setMobileOpen] = React.useState(false);\n const t = useLocale();\n const collapsed = ctrlCollapsed ?? internalCollapsed;\n\n // SSR-safe persistence: the initial render uses `defaultCollapsed` (server\n // and first client render agree → no hydration mismatch), then we sync from\n // localStorage after mount. Only in uncontrolled mode; reads can throw\n // (Safari private mode), so they're guarded. Runs once per persistKey.\n React.useEffect(() => {\n if (persistKey == null || ctrlCollapsed !== undefined) return;\n try {\n const stored = window.localStorage.getItem(persistKey);\n if (stored === '0' || stored === '1') setInternalCollapsed(stored === '1');\n } catch {\n /* localStorage unavailable — keep defaultCollapsed */\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- sync once at mount; ctrlCollapsed read intentionally not tracked\n }, [persistKey]);\n\n const setCollapsed = (v: boolean) => {\n if (ctrlCollapsed === undefined) setInternalCollapsed(v);\n if (persistKey != null && ctrlCollapsed === undefined) {\n try {\n window.localStorage.setItem(persistKey, v ? '1' : '0');\n } catch {\n /* localStorage unavailable — persistence is best-effort */\n }\n }\n onCollapsedChange?.(v);\n };\n const closeMobile = React.useCallback(() => setMobileOpen(false), []);\n\n // Top-header variant: full-width header above the body. Topbar is\n // invariant to `collapsed` (only the inner body's columns animate); brand\n // lives in `header.center` at the true viewport centre. Default\n // `headerLayout=\"side\"` falls through to the legacy JSX below\n // (byte-identical for existing consumers). The `props.headerLayout`\n // check narrows the discriminated union, so `header` (top) and\n // `brand`/`topbar`/`user` (side) are each only in scope in their branch.\n if (props.headerLayout === 'top') {\n const { header } = props;\n // Header band themes independently of the sidebar; defaults to `theme`\n // so `theme=\"brand\"` keeps tinting both bands (back-compat).\n const headerTheme = props.headerTheme ?? theme;\n const collapsedRail = props.collapsedRail ?? false;\n return (\n <div className={cx('appshell', `appshell--${theme}`, 'appshell--header-top', `appshell--header-${headerTheme}`, collapsedRail && 'appshell--rail', collapsed && 'is-collapsed', className)}>\n {/* On a brand header the band is dark, so re-scope foreground tokens\n via data-tone=\"inverse\" — anything inside (Avatar, badges, links)\n becomes band-aware automatically without per-call-site colors. */}\n <header className=\"appshell__header\" role=\"banner\" data-tone={headerTheme === 'brand' ? 'inverse' : undefined}>\n <div className=\"appshell__header-left\">{header?.left}</div>\n <div className=\"appshell__header-center\">{header?.center}</div>\n <div className=\"appshell__header-right\">{header?.right}</div>\n </header>\n <div className=\"appshell__body\">\n <aside className=\"appshell__sidebar\" aria-label={t['appshell.mainNav']}>\n <nav className=\"appshell__nav\">\n {sections.map((s, i) => (\n <div key={s.id ?? i} className=\"appshell__navsection\">\n {s.label && <div className=\"appshell__navlabel-section\">{s.label}</div>}\n <ul>{s.items.map((it) => (\n <NavItemNode key={it.id} item={it} depth={0} linkAs={linkAs} onCloseMobile={closeMobile} />\n ))}</ul>\n </div>\n ))}\n </nav>\n {footer != null && (\n <div className=\"appshell__sidebar-foot\">{footer}</div>\n )}\n </aside>\n <main className=\"appshell__content\" role=\"main\">{children}</main>\n </div>\n </div>\n );\n }\n\n const { brand, brandCollapsed, topbar, user } = props;\n return (\n <div className={cx('appshell', `appshell--${theme}`, collapsed && 'is-collapsed', mobileOpen && 'is-mobile-open', className)}>\n <aside className=\"appshell__sidebar\" aria-label={t['appshell.mainNav']}>\n <div className=\"appshell__brand\">\n {collapsed ? (brandCollapsed ?? brand) : brand}\n </div>\n <nav className=\"appshell__nav\">\n {sections.map((s, i) => (\n <div key={s.id ?? i} className=\"appshell__navsection\">\n {s.label && <div className=\"appshell__navlabel-section\">{s.label}</div>}\n <ul>{s.items.map((it) => (\n <NavItemNode key={it.id} item={it} depth={0} linkAs={linkAs} onCloseMobile={closeMobile} />\n ))}</ul>\n </div>\n ))}\n </nav>\n <div className=\"appshell__sidebar-foot\">\n {footer}\n <button\n type=\"button\"\n className=\"appshell__collapse\"\n onClick={() => setCollapsed(!collapsed)}\n aria-expanded={!collapsed}\n aria-label={collapsed ? t['appshell.expandMenu'] : t['appshell.collapseMenu']}\n title={collapsed ? t['appshell.expand'] : t['appshell.collapse']}\n >\n {collapsed ? <ChevronRight size={14} /> : <ChevronLeft size={14} />}\n </button>\n </div>\n </aside>\n\n <div className=\"appshell__main\">\n <header className=\"appshell__topbar\">\n <button\n type=\"button\"\n className=\"appshell__hamburger\"\n aria-label={t['appshell.openMenu']}\n aria-expanded={mobileOpen}\n onClick={() => setMobileOpen((o) => !o)}\n ><MenuIcon size={20} /></button>\n <div className=\"appshell__topbar-content\">{topbar}</div>\n {user && <div className=\"appshell__topbar-user\">{user}</div>}\n </header>\n <main className=\"appshell__content\" role=\"main\">{children}</main>\n </div>\n\n {mobileOpen && (\n <div className=\"appshell__scrim\" onClick={() => setMobileOpen(false)} aria-hidden=\"true\" />\n )}\n </div>\n );\n}\n\n// ---------- PageHeader --------------------------------------------------\nexport interface PageHeaderProps {\n title: React.ReactNode;\n description?: React.ReactNode;\n breadcrumbs?: Array<{ label: React.ReactNode; href?: string }>;\n actions?: React.ReactNode;\n meta?: React.ReactNode;\n className?: string;\n}\n\nexport function PageHeader({ title, description, breadcrumbs, actions, meta, className }: PageHeaderProps) {\n const t = useLocale();\n return (\n <div className={cx('page-header', className)}>\n {breadcrumbs && breadcrumbs.length > 0 && (\n <nav className=\"page-header__crumbs\" aria-label={t['appshell.breadcrumb']}>\n <ol>\n {breadcrumbs.map((c, i) => (\n <li key={i}>\n {c.href ? <a href={c.href}>{c.label}</a> : <span aria-current=\"page\">{c.label}</span>}\n {i < breadcrumbs.length - 1 && <span className=\"page-header__crumb-sep\" aria-hidden=\"true\">/</span>}\n </li>\n ))}\n </ol>\n </nav>\n )}\n <div className=\"page-header__row\">\n <div className=\"page-header__title-wrap\">\n <h1 className=\"page-header__title\">{title}</h1>\n {description && <p className=\"page-header__desc\">{description}</p>}\n </div>\n {actions && <div className=\"page-header__actions\">{actions}</div>}\n </div>\n {meta && <div className=\"page-header__meta\">{meta}</div>}\n </div>\n );\n}\n"]}
|