@alfadocs/ui-kit-debug 0.14.1 → 0.15.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/_chunks/carousel.agent-2dXpQoqp.js +1661 -0
- package/dist/_chunks/carousel.agent-2dXpQoqp.js.map +1 -0
- package/dist/_chunks/{header-D0ULgQl3.js → header-DqmKROIY.js} +42 -52
- package/dist/_chunks/header-DqmKROIY.js.map +1 -0
- package/dist/_chunks/{map-view-Dd48BxVB.js → map-view-DVP-Kp9l.js} +445 -444
- package/dist/_chunks/{map-view-Dd48BxVB.js.map → map-view-DVP-Kp9l.js.map} +1 -1
- package/dist/_chunks/menu-XRhW3_99.js +16 -0
- package/dist/_chunks/menu-XRhW3_99.js.map +1 -0
- package/dist/_chunks/{patient-shell-CL20JnVJ.js → patient-shell-BE0CdPOJ.js} +2 -2
- package/dist/_chunks/{patient-shell-CL20JnVJ.js.map → patient-shell-BE0CdPOJ.js.map} +1 -1
- package/dist/_chunks/public-header.agent-AzJSINlU.js +237 -0
- package/dist/_chunks/public-header.agent-AzJSINlU.js.map +1 -0
- package/dist/_chunks/stat-DEkZx0Mx.js +318 -0
- package/dist/_chunks/stat-DEkZx0Mx.js.map +1 -0
- package/dist/_chunks/use-count-up-BLLetaZ8.js +109 -0
- package/dist/_chunks/use-count-up-BLLetaZ8.js.map +1 -0
- package/dist/agent-catalog.json +92 -1
- package/dist/components/carousel/carousel.agent.d.ts +4 -0
- package/dist/components/carousel/carousel.agent.d.ts.map +1 -0
- package/dist/components/carousel/carousel.d.ts +45 -0
- package/dist/components/carousel/carousel.d.ts.map +1 -0
- package/dist/components/carousel/index.d.ts +3 -0
- package/dist/components/carousel/index.d.ts.map +1 -0
- package/dist/components/carousel/index.js +6 -0
- package/dist/components/carousel/index.js.map +1 -0
- package/dist/components/header/index.js +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/map-view/index.js +1 -1
- package/dist/components/map-view/map-view.d.ts +8 -0
- package/dist/components/map-view/map-view.d.ts.map +1 -1
- package/dist/components/public-header/index.d.ts +3 -0
- package/dist/components/public-header/index.d.ts.map +1 -0
- package/dist/components/public-header/index.js +6 -0
- package/dist/components/public-header/index.js.map +1 -0
- package/dist/components/public-header/public-header.agent.d.ts +4 -0
- package/dist/components/public-header/public-header.agent.d.ts.map +1 -0
- package/dist/components/public-header/public-header.d.ts +43 -0
- package/dist/components/public-header/public-header.d.ts.map +1 -0
- package/dist/components/stat/index.js +1 -1
- package/dist/components/stat/stat.d.ts +31 -0
- package/dist/components/stat/stat.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +10 -8
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/use-count-up.d.ts +88 -0
- package/dist/hooks/use-count-up.d.ts.map +1 -0
- package/dist/i18n/config.js +51 -0
- package/dist/i18n/config.js.map +1 -1
- package/dist/i18n/resources.d.ts +51 -0
- package/dist/i18n/resources.d.ts.map +1 -1
- package/dist/index.js +367 -361
- package/dist/index.js.map +1 -1
- package/dist/locales/de.json +17 -0
- package/dist/locales/en.json +17 -0
- package/dist/locales/it.json +17 -0
- package/dist/patterns/patient-shell/index.js +1 -1
- package/dist/tokens.css +1 -1
- package/package.json +10 -1
- package/dist/_chunks/header-D0ULgQl3.js.map +0 -1
- package/dist/_chunks/stat-CDQ_a0vk.js +0 -228
- package/dist/_chunks/stat-CDQ_a0vk.js.map +0 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { c as e } from "./createLucideIcon-CrFbzy84.js";
|
|
2
|
+
/**
|
|
3
|
+
* @license lucide-react v1.8.0 - ISC
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the ISC license.
|
|
6
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
const t = [
|
|
9
|
+
["path", { d: "M4 5h16", key: "1tepv9" }],
|
|
10
|
+
["path", { d: "M4 12h16", key: "1lakjw" }],
|
|
11
|
+
["path", { d: "M4 19h16", key: "1djgab" }]
|
|
12
|
+
], o = e("menu", t);
|
|
13
|
+
export {
|
|
14
|
+
o as M
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=menu-XRhW3_99.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu-XRhW3_99.js","sources":["../../node_modules/lucide-react/dist/esm/icons/menu.js"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"path\", { d: \"M4 5h16\", key: \"1tepv9\" }],\n [\"path\", { d: \"M4 12h16\", key: \"1lakjw\" }],\n [\"path\", { d: \"M4 19h16\", key: \"1djgab\" }]\n];\nconst Menu = createLucideIcon(\"menu\", __iconNode);\n\nexport { __iconNode, Menu as default };\n//# sourceMappingURL=menu.js.map\n"],"names":["__iconNode","Menu","createLucideIcon"],"mappings":";AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASA,MAAMA,IAAa;AAAA,EACjB,CAAC,QAAQ,EAAE,GAAG,WAAW,KAAK,SAAQ,CAAE;AAAA,EACxC,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAQ,CAAE;AAAA,EACzC,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,SAAQ,CAAE;AAC3C,GACMC,IAAOC,EAAiB,QAAQF,CAAU;","x_google_ignoreList":[0]}
|
|
@@ -4,7 +4,7 @@ import { useTranslation as c } from "react-i18next";
|
|
|
4
4
|
import { A as y } from "./app-frame-BYx1gcV7.js";
|
|
5
5
|
import { I } from "./icon-button-C4CGcYuz.js";
|
|
6
6
|
import { D as n } from "./dropdown-menu-dyV7gHh_.js";
|
|
7
|
-
import { H as L, g as A, f as H, e as x, a as C, c as M, d as B } from "./header-
|
|
7
|
+
import { H as L, g as A, f as H, e as x, a as C, c as M, d as B } from "./header-DqmKROIY.js";
|
|
8
8
|
import { L as N } from "./logo-_Z-jLq80.js";
|
|
9
9
|
import { S as T, a as _, h as O, j as P, k as W, i as j } from "./sidebar-D8Lq065m.js";
|
|
10
10
|
import { T as z } from "./theme-root-CSKD5ZRm.js";
|
|
@@ -185,4 +185,4 @@ V.displayName = "PatientShell";
|
|
|
185
185
|
export {
|
|
186
186
|
V as P
|
|
187
187
|
};
|
|
188
|
-
//# sourceMappingURL=patient-shell-
|
|
188
|
+
//# sourceMappingURL=patient-shell-BE0CdPOJ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patient-shell-CL20JnVJ.js","sources":["../../node_modules/lucide-react/dist/esm/icons/maximize-2.js","../../node_modules/lucide-react/dist/esm/icons/settings.js","../../src/patterns/patient-shell/patient-shell.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"path\", { d: \"M15 3h6v6\", key: \"1q9fwt\" }],\n [\"path\", { d: \"m21 3-7 7\", key: \"1l2asr\" }],\n [\"path\", { d: \"m3 21 7-7\", key: \"tjx5ai\" }],\n [\"path\", { d: \"M9 21H3v-6\", key: \"wtvkvv\" }]\n];\nconst Maximize2 = createLucideIcon(\"maximize-2\", __iconNode);\n\nexport { __iconNode, Maximize2 as default };\n//# sourceMappingURL=maximize-2.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915\",\n key: \"1i5ecw\"\n }\n ],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\", key: \"1v7zrd\" }]\n];\nconst Settings = createLucideIcon(\"settings\", __iconNode);\n\nexport { __iconNode, Settings as default };\n//# sourceMappingURL=settings.js.map\n","/**\n * Patient Shell — the chrome for AlfaDocs's patient-facing users\n * (patients managing their own appointments and records).\n *\n * Deliberately flatter than the clinician `AppFrame` composition: no\n * command palette, no favourites, no practice/chain affordances, no\n * Leo, no privacy lock. Mirrors the legacy `_header.html.twig`\n * `app.user.type == 'patient'` branch and the `knp_menu_render('pcp')`\n * flat nav menu.\n *\n * Unlike most patterns in this folder, PatientShell exports a runtime\n * component because the consuming patient-portal app mounts it in\n * production — the exports are surfaced via `src/patterns/index.ts`\n * and included in the library bundle.\n */\nimport { forwardRef, useState, type ReactNode } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { LogOut, Maximize2, Settings as SettingsIcon } from 'lucide-react';\n\nimport { AppFrame } from '../../components/app-frame';\nimport { IconButton } from '../../components/button';\nimport { DropdownMenu } from '../../components/dropdown-menu';\nimport {\n Header,\n HeaderBrand,\n HeaderCenter,\n HeaderEnd,\n HeaderMenuButton,\n HeaderSkipLink,\n HeaderStart,\n} from '../../components/header';\nimport { Logo } from '../../components/logo';\nimport {\n Sidebar,\n SidebarBody,\n SidebarItem,\n SidebarItemBadge,\n SidebarItemIcon,\n SidebarItemLabel,\n type SidebarMode,\n} from '../../components/sidebar';\nimport { ThemeRoot } from '../../components/theme-root';\nimport { TooltipProvider } from '../../components/tooltip';\nimport { useTheme } from '../../hooks/use-theme';\n\n/* ------------------------------------------------------------------ */\n/* Public API */\n/* ------------------------------------------------------------------ */\n\nexport interface PatientNavItem {\n id: string;\n /** Pre-translated label — patients' nav labels are emitted already\n * translated by the consuming app's backend (legacy `knp_menu`). */\n label: string;\n href: string;\n /** Optional leading icon. Pass a lucide-react or equivalent element. */\n icon?: ReactNode;\n /** Optional unseen-item count rendered as a pill badge. */\n badgeCount?: number;\n /** Mark the item as the current page (`aria-current='page'`). */\n isActive?: boolean;\n}\n\nexport interface PatientShellProps {\n /** Patient's display name. Used as the avatar fallback by consumers\n * that read the name through a slot; not rendered inside this shell\n * today because the legacy patient chrome carries no profile pill. */\n userName: string;\n /** Optional avatar URL. Reserved for future profile-pill surfaces;\n * no visible effect today. */\n userAvatarSrc?: string;\n /** Flat nav items rendered in the sidebar, in order. */\n menuItems: PatientNavItem[];\n /** URL the \"Logout\" action navigates to — typically `/logout`. */\n logoutHref: string;\n /** Fires when the user toggles fullscreen from the settings menu. */\n onToggleFullscreen?: () => void;\n /** Optional brand logo. Defaults to\n * `<Logo variant=\"monochrome\" size=\"md\" decorative />`. */\n brandLogo?: ReactNode;\n /** Link target for the brand — typically the patient dashboard. */\n brandHref?: string;\n /** Override the accessible name of the `<main>` landmark. */\n mainAriaLabel?: string;\n /**\n * Force the sidebar into a specific mode. Consuming apps typically\n * flip to `'overlay'` on narrow viewports and leave this undefined\n * elsewhere (the sidebar then defaults to `'expanded'`).\n */\n sidebarState?: SidebarMode;\n /** Children render inside the main content region. */\n children: ReactNode;\n}\n\n/* ------------------------------------------------------------------ */\n/* Internals */\n/* ------------------------------------------------------------------ */\n\nfunction formatBadgeCount(count: number): string {\n return count > 99 ? '99+' : String(count);\n}\n\n/**\n * Guard against `javascript:` and other non-navigation schemes before\n * handing the URL to `window.location.assign`. A consuming app that lets\n * attacker-controlled input reach `logoutHref` would otherwise execute\n * script. Allow only relative paths, hash fragments, and `http(s)` URLs.\n */\nfunction isSafeLogoutHref(href: string): boolean {\n if (href.startsWith('/') || href.startsWith('#') || href.startsWith('?')) {\n return true;\n }\n try {\n const url = new URL(href, window.location.origin);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n}\n\nfunction SettingsMenu({\n logoutHref,\n onToggleFullscreen,\n}: {\n logoutHref: string;\n onToggleFullscreen?: () => void;\n}) {\n const { t } = useTranslation();\n return (\n <DropdownMenu.Root>\n <DropdownMenu.Trigger asChild>\n <IconButton\n icon={<SettingsIcon aria-hidden=\"true\" />}\n aria-label={t('patientShell.settings.triggerAria')}\n tooltip={t('patientShell.settings.triggerAria')}\n data-testid=\"patient-shell-settings-trigger\"\n />\n </DropdownMenu.Trigger>\n <DropdownMenu.Content align=\"end\">\n <DropdownMenu.Item\n startIcon={<Maximize2 aria-hidden=\"true\" />}\n onSelect={onToggleFullscreen}\n data-testid=\"patient-shell-fullscreen\"\n >\n {t('patientShell.settings.fullscreen')}\n </DropdownMenu.Item>\n <DropdownMenu.Separator />\n <DropdownMenu.Item\n startIcon={<LogOut aria-hidden=\"true\" />}\n onSelect={() => {\n if (typeof window === 'undefined') return;\n if (!isSafeLogoutHref(logoutHref)) return;\n window.location.assign(logoutHref);\n }}\n data-testid=\"patient-shell-logout\"\n >\n {t('patientShell.settings.logout')}\n </DropdownMenu.Item>\n </DropdownMenu.Content>\n </DropdownMenu.Root>\n );\n}\n\nfunction PatientSidebarNav({ menuItems }: { menuItems: PatientNavItem[] }) {\n return (\n <SidebarBody>\n {menuItems.map((item) => {\n const badge =\n typeof item.badgeCount === 'number' && item.badgeCount > 0\n ? item.badgeCount\n : undefined;\n return (\n <SidebarItem\n key={item.id}\n href={item.href}\n aria-label={item.label}\n isActive={item.isActive}\n >\n {item.icon ? <SidebarItemIcon>{item.icon}</SidebarItemIcon> : null}\n <SidebarItemLabel>{item.label}</SidebarItemLabel>\n {badge !== undefined ? (\n <SidebarItemBadge>{formatBadgeCount(badge)}</SidebarItemBadge>\n ) : null}\n </SidebarItem>\n );\n })}\n </SidebarBody>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Root */\n/* ------------------------------------------------------------------ */\n\nexport const PatientShell = forwardRef<HTMLDivElement, PatientShellProps>(\n (\n {\n menuItems,\n logoutHref,\n onToggleFullscreen,\n brandLogo,\n brandHref = '/',\n mainAriaLabel,\n sidebarState,\n children,\n // `userName` and `userAvatarSrc` are declared on the public API\n // for forward-compatibility (future profile-pill work) — they are\n // intentionally unused today because the legacy patient chrome\n // carries no profile pill.\n userName: _userName,\n userAvatarSrc: _userAvatarSrc,\n },\n ref,\n ) => {\n const { t } = useTranslation();\n const { resolvedTheme } = useTheme();\n const themeBase = resolvedTheme.startsWith('dark') ? 'dark' : 'light';\n const themeAccessible = resolvedTheme.endsWith('-accessible');\n\n // Narrow-viewport overlay: `HeaderMenuButton` only renders below the\n // `md` breakpoint, and toggling opens the Sidebar's overlay Dialog.\n const [sidebarOpen, setSidebarOpen] = useState(false);\n\n const resolvedLogo = brandLogo ?? (\n <Logo variant=\"monochrome\" size=\"md\" decorative />\n );\n\n return (\n <ThemeRoot\n ref={ref}\n theme={themeBase}\n accessible={themeAccessible}\n className=\"ds:contents\"\n >\n <TooltipProvider>\n <AppFrame\n mainAriaLabel={mainAriaLabel}\n header={\n <Header>\n <HeaderStart>\n <HeaderSkipLink href=\"#main-content\" />\n <HeaderMenuButton onMenuOpen={() => setSidebarOpen(true)} />\n <HeaderBrand\n logo={resolvedLogo}\n href={brandHref}\n aria-label={t('patientShell.brand.homeAria')}\n />\n </HeaderStart>\n <HeaderCenter />\n <HeaderEnd>\n <SettingsMenu\n logoutHref={logoutHref}\n onToggleFullscreen={onToggleFullscreen}\n />\n </HeaderEnd>\n </Header>\n }\n sidebar={\n <Sidebar\n {...(sidebarState !== undefined\n ? { state: sidebarState }\n : { defaultState: 'expanded' as const })}\n open={sidebarOpen}\n onOpenChange={setSidebarOpen}\n aria-label={t('patientShell.sidebar.panelLabel')}\n data-testid=\"patient-shell-sidebar\"\n >\n <PatientSidebarNav menuItems={menuItems} />\n </Sidebar>\n }\n >\n {children}\n </AppFrame>\n </TooltipProvider>\n </ThemeRoot>\n );\n },\n);\n\nPatientShell.displayName = 'PatientShell';\n"],"names":["__iconNode","Maximize2","createLucideIcon","Settings","formatBadgeCount","count","isSafeLogoutHref","href","url","SettingsMenu","logoutHref","onToggleFullscreen","t","useTranslation","jsxs","DropdownMenu","jsx","IconButton","SettingsIcon","LogOut","PatientSidebarNav","menuItems","SidebarBody","item","badge","SidebarItem","SidebarItemIcon","SidebarItemLabel","SidebarItemBadge","PatientShell","forwardRef","brandLogo","brandHref","mainAriaLabel","sidebarState","children","_userName","_userAvatarSrc","ref","resolvedTheme","useTheme","themeBase","themeAccessible","sidebarOpen","setSidebarOpen","useState","ThemeRoot","TooltipProvider","AppFrame","Header","HeaderStart","HeaderSkipLink","HeaderMenuButton","HeaderBrand","Logo","HeaderCenter","HeaderEnd","Sidebar"],"mappings":"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","x_google_ignoreList":[0,1]}
|
|
1
|
+
{"version":3,"file":"patient-shell-BE0CdPOJ.js","sources":["../../node_modules/lucide-react/dist/esm/icons/maximize-2.js","../../node_modules/lucide-react/dist/esm/icons/settings.js","../../src/patterns/patient-shell/patient-shell.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"path\", { d: \"M15 3h6v6\", key: \"1q9fwt\" }],\n [\"path\", { d: \"m21 3-7 7\", key: \"1l2asr\" }],\n [\"path\", { d: \"m3 21 7-7\", key: \"tjx5ai\" }],\n [\"path\", { d: \"M9 21H3v-6\", key: \"wtvkvv\" }]\n];\nconst Maximize2 = createLucideIcon(\"maximize-2\", __iconNode);\n\nexport { __iconNode, Maximize2 as default };\n//# sourceMappingURL=maximize-2.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915\",\n key: \"1i5ecw\"\n }\n ],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\", key: \"1v7zrd\" }]\n];\nconst Settings = createLucideIcon(\"settings\", __iconNode);\n\nexport { __iconNode, Settings as default };\n//# sourceMappingURL=settings.js.map\n","/**\n * Patient Shell — the chrome for AlfaDocs's patient-facing users\n * (patients managing their own appointments and records).\n *\n * Deliberately flatter than the clinician `AppFrame` composition: no\n * command palette, no favourites, no practice/chain affordances, no\n * Leo, no privacy lock. Mirrors the legacy `_header.html.twig`\n * `app.user.type == 'patient'` branch and the `knp_menu_render('pcp')`\n * flat nav menu.\n *\n * Unlike most patterns in this folder, PatientShell exports a runtime\n * component because the consuming patient-portal app mounts it in\n * production — the exports are surfaced via `src/patterns/index.ts`\n * and included in the library bundle.\n */\nimport { forwardRef, useState, type ReactNode } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { LogOut, Maximize2, Settings as SettingsIcon } from 'lucide-react';\n\nimport { AppFrame } from '../../components/app-frame';\nimport { IconButton } from '../../components/button';\nimport { DropdownMenu } from '../../components/dropdown-menu';\nimport {\n Header,\n HeaderBrand,\n HeaderCenter,\n HeaderEnd,\n HeaderMenuButton,\n HeaderSkipLink,\n HeaderStart,\n} from '../../components/header';\nimport { Logo } from '../../components/logo';\nimport {\n Sidebar,\n SidebarBody,\n SidebarItem,\n SidebarItemBadge,\n SidebarItemIcon,\n SidebarItemLabel,\n type SidebarMode,\n} from '../../components/sidebar';\nimport { ThemeRoot } from '../../components/theme-root';\nimport { TooltipProvider } from '../../components/tooltip';\nimport { useTheme } from '../../hooks/use-theme';\n\n/* ------------------------------------------------------------------ */\n/* Public API */\n/* ------------------------------------------------------------------ */\n\nexport interface PatientNavItem {\n id: string;\n /** Pre-translated label — patients' nav labels are emitted already\n * translated by the consuming app's backend (legacy `knp_menu`). */\n label: string;\n href: string;\n /** Optional leading icon. Pass a lucide-react or equivalent element. */\n icon?: ReactNode;\n /** Optional unseen-item count rendered as a pill badge. */\n badgeCount?: number;\n /** Mark the item as the current page (`aria-current='page'`). */\n isActive?: boolean;\n}\n\nexport interface PatientShellProps {\n /** Patient's display name. Used as the avatar fallback by consumers\n * that read the name through a slot; not rendered inside this shell\n * today because the legacy patient chrome carries no profile pill. */\n userName: string;\n /** Optional avatar URL. Reserved for future profile-pill surfaces;\n * no visible effect today. */\n userAvatarSrc?: string;\n /** Flat nav items rendered in the sidebar, in order. */\n menuItems: PatientNavItem[];\n /** URL the \"Logout\" action navigates to — typically `/logout`. */\n logoutHref: string;\n /** Fires when the user toggles fullscreen from the settings menu. */\n onToggleFullscreen?: () => void;\n /** Optional brand logo. Defaults to\n * `<Logo variant=\"monochrome\" size=\"md\" decorative />`. */\n brandLogo?: ReactNode;\n /** Link target for the brand — typically the patient dashboard. */\n brandHref?: string;\n /** Override the accessible name of the `<main>` landmark. */\n mainAriaLabel?: string;\n /**\n * Force the sidebar into a specific mode. Consuming apps typically\n * flip to `'overlay'` on narrow viewports and leave this undefined\n * elsewhere (the sidebar then defaults to `'expanded'`).\n */\n sidebarState?: SidebarMode;\n /** Children render inside the main content region. */\n children: ReactNode;\n}\n\n/* ------------------------------------------------------------------ */\n/* Internals */\n/* ------------------------------------------------------------------ */\n\nfunction formatBadgeCount(count: number): string {\n return count > 99 ? '99+' : String(count);\n}\n\n/**\n * Guard against `javascript:` and other non-navigation schemes before\n * handing the URL to `window.location.assign`. A consuming app that lets\n * attacker-controlled input reach `logoutHref` would otherwise execute\n * script. Allow only relative paths, hash fragments, and `http(s)` URLs.\n */\nfunction isSafeLogoutHref(href: string): boolean {\n if (href.startsWith('/') || href.startsWith('#') || href.startsWith('?')) {\n return true;\n }\n try {\n const url = new URL(href, window.location.origin);\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n}\n\nfunction SettingsMenu({\n logoutHref,\n onToggleFullscreen,\n}: {\n logoutHref: string;\n onToggleFullscreen?: () => void;\n}) {\n const { t } = useTranslation();\n return (\n <DropdownMenu.Root>\n <DropdownMenu.Trigger asChild>\n <IconButton\n icon={<SettingsIcon aria-hidden=\"true\" />}\n aria-label={t('patientShell.settings.triggerAria')}\n tooltip={t('patientShell.settings.triggerAria')}\n data-testid=\"patient-shell-settings-trigger\"\n />\n </DropdownMenu.Trigger>\n <DropdownMenu.Content align=\"end\">\n <DropdownMenu.Item\n startIcon={<Maximize2 aria-hidden=\"true\" />}\n onSelect={onToggleFullscreen}\n data-testid=\"patient-shell-fullscreen\"\n >\n {t('patientShell.settings.fullscreen')}\n </DropdownMenu.Item>\n <DropdownMenu.Separator />\n <DropdownMenu.Item\n startIcon={<LogOut aria-hidden=\"true\" />}\n onSelect={() => {\n if (typeof window === 'undefined') return;\n if (!isSafeLogoutHref(logoutHref)) return;\n window.location.assign(logoutHref);\n }}\n data-testid=\"patient-shell-logout\"\n >\n {t('patientShell.settings.logout')}\n </DropdownMenu.Item>\n </DropdownMenu.Content>\n </DropdownMenu.Root>\n );\n}\n\nfunction PatientSidebarNav({ menuItems }: { menuItems: PatientNavItem[] }) {\n return (\n <SidebarBody>\n {menuItems.map((item) => {\n const badge =\n typeof item.badgeCount === 'number' && item.badgeCount > 0\n ? item.badgeCount\n : undefined;\n return (\n <SidebarItem\n key={item.id}\n href={item.href}\n aria-label={item.label}\n isActive={item.isActive}\n >\n {item.icon ? <SidebarItemIcon>{item.icon}</SidebarItemIcon> : null}\n <SidebarItemLabel>{item.label}</SidebarItemLabel>\n {badge !== undefined ? (\n <SidebarItemBadge>{formatBadgeCount(badge)}</SidebarItemBadge>\n ) : null}\n </SidebarItem>\n );\n })}\n </SidebarBody>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Root */\n/* ------------------------------------------------------------------ */\n\nexport const PatientShell = forwardRef<HTMLDivElement, PatientShellProps>(\n (\n {\n menuItems,\n logoutHref,\n onToggleFullscreen,\n brandLogo,\n brandHref = '/',\n mainAriaLabel,\n sidebarState,\n children,\n // `userName` and `userAvatarSrc` are declared on the public API\n // for forward-compatibility (future profile-pill work) — they are\n // intentionally unused today because the legacy patient chrome\n // carries no profile pill.\n userName: _userName,\n userAvatarSrc: _userAvatarSrc,\n },\n ref,\n ) => {\n const { t } = useTranslation();\n const { resolvedTheme } = useTheme();\n const themeBase = resolvedTheme.startsWith('dark') ? 'dark' : 'light';\n const themeAccessible = resolvedTheme.endsWith('-accessible');\n\n // Narrow-viewport overlay: `HeaderMenuButton` only renders below the\n // `md` breakpoint, and toggling opens the Sidebar's overlay Dialog.\n const [sidebarOpen, setSidebarOpen] = useState(false);\n\n const resolvedLogo = brandLogo ?? (\n <Logo variant=\"monochrome\" size=\"md\" decorative />\n );\n\n return (\n <ThemeRoot\n ref={ref}\n theme={themeBase}\n accessible={themeAccessible}\n className=\"ds:contents\"\n >\n <TooltipProvider>\n <AppFrame\n mainAriaLabel={mainAriaLabel}\n header={\n <Header>\n <HeaderStart>\n <HeaderSkipLink href=\"#main-content\" />\n <HeaderMenuButton onMenuOpen={() => setSidebarOpen(true)} />\n <HeaderBrand\n logo={resolvedLogo}\n href={brandHref}\n aria-label={t('patientShell.brand.homeAria')}\n />\n </HeaderStart>\n <HeaderCenter />\n <HeaderEnd>\n <SettingsMenu\n logoutHref={logoutHref}\n onToggleFullscreen={onToggleFullscreen}\n />\n </HeaderEnd>\n </Header>\n }\n sidebar={\n <Sidebar\n {...(sidebarState !== undefined\n ? { state: sidebarState }\n : { defaultState: 'expanded' as const })}\n open={sidebarOpen}\n onOpenChange={setSidebarOpen}\n aria-label={t('patientShell.sidebar.panelLabel')}\n data-testid=\"patient-shell-sidebar\"\n >\n <PatientSidebarNav menuItems={menuItems} />\n </Sidebar>\n }\n >\n {children}\n </AppFrame>\n </TooltipProvider>\n </ThemeRoot>\n );\n },\n);\n\nPatientShell.displayName = 'PatientShell';\n"],"names":["__iconNode","Maximize2","createLucideIcon","Settings","formatBadgeCount","count","isSafeLogoutHref","href","url","SettingsMenu","logoutHref","onToggleFullscreen","t","useTranslation","jsxs","DropdownMenu","jsx","IconButton","SettingsIcon","LogOut","PatientSidebarNav","menuItems","SidebarBody","item","badge","SidebarItem","SidebarItemIcon","SidebarItemLabel","SidebarItemBadge","PatientShell","forwardRef","brandLogo","brandHref","mainAriaLabel","sidebarState","children","_userName","_userAvatarSrc","ref","resolvedTheme","useTheme","themeBase","themeAccessible","sidebarOpen","setSidebarOpen","useState","ThemeRoot","TooltipProvider","AppFrame","Header","HeaderStart","HeaderSkipLink","HeaderMenuButton","HeaderBrand","Logo","HeaderCenter","HeaderEnd","Sidebar"],"mappings":"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","x_google_ignoreList":[0,1]}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { jsxs as g, jsx as s } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as P, useId as Y, useState as b, useCallback as N, useImperativeHandle as q, useEffect as j, useRef as L } from "react";
|
|
3
|
+
import { c as C } from "./index-D2ZczOXr.js";
|
|
4
|
+
import { useTranslation as S } from "react-i18next";
|
|
5
|
+
import { I as D } from "./icon-button-C4CGcYuz.js";
|
|
6
|
+
import { S as c } from "./sheet-BT0izeoI.js";
|
|
7
|
+
import { X as F } from "./x-CCcI3eJp.js";
|
|
8
|
+
import { M as W } from "./menu-XRhW3_99.js";
|
|
9
|
+
const X = C(
|
|
10
|
+
[
|
|
11
|
+
"ds:flex ds:items-center ds:gap-[var(--spacing-md)]",
|
|
12
|
+
"ds:w-full ds:z-[var(--z-sticky)]",
|
|
13
|
+
"ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:sm:ps-[var(--spacing-lg)] ds:sm:pe-[var(--spacing-lg)]",
|
|
14
|
+
"ds:h-14 ds:md:h-16",
|
|
15
|
+
"ds:transition-[background-color,box-shadow,transform] ds:duration-[var(--animation-duration)]",
|
|
16
|
+
"ds:motion-reduce:transition-none"
|
|
17
|
+
].join(" "),
|
|
18
|
+
{
|
|
19
|
+
variants: {
|
|
20
|
+
variant: {
|
|
21
|
+
default: "ds:bg-[var(--card)] ds:text-[var(--foreground)] ds:border-b ds:border-[color:var(--border)]",
|
|
22
|
+
// Transparent fades to default once data-stuck="true" (set by the
|
|
23
|
+
// rAF scroll listener). The fade is handled via the same
|
|
24
|
+
// background utility so `data-stuck` swaps cleanly between states.
|
|
25
|
+
transparent: [
|
|
26
|
+
"ds:bg-transparent ds:text-[var(--foreground)]",
|
|
27
|
+
"ds:data-[stuck=true]:bg-[var(--card)]",
|
|
28
|
+
"ds:data-[stuck=true]:shadow-[var(--shadow-md)]",
|
|
29
|
+
"ds:forced-colors:border-b ds:forced-colors:border-[CanvasText]"
|
|
30
|
+
].join(" "),
|
|
31
|
+
dark: "ds:bg-[var(--color-blue-800)] ds:text-[var(--primary-foreground)]"
|
|
32
|
+
},
|
|
33
|
+
sticky: {
|
|
34
|
+
none: "",
|
|
35
|
+
top: "ds:sticky ds:top-0",
|
|
36
|
+
// `scroll-up` is sticky but translates off-screen when scrolling
|
|
37
|
+
// down past `stickyOffset` (handled by data-hidden="true") and
|
|
38
|
+
// slides back in when scrolling up.
|
|
39
|
+
"scroll-up": [
|
|
40
|
+
"ds:sticky ds:top-0",
|
|
41
|
+
"ds:data-[hidden=true]:-translate-y-full"
|
|
42
|
+
].join(" ")
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
defaultVariants: { variant: "default", sticky: "none" }
|
|
46
|
+
}
|
|
47
|
+
), $ = C(
|
|
48
|
+
[
|
|
49
|
+
"ds:inline-flex ds:items-center",
|
|
50
|
+
"ds:type-body-sm ds:font-medium",
|
|
51
|
+
"ds:text-[var(--foreground)]",
|
|
52
|
+
"ds:rounded-[var(--radius-sm)]",
|
|
53
|
+
"ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:py-[var(--spacing-2xs)]",
|
|
54
|
+
"ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid",
|
|
55
|
+
"ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]",
|
|
56
|
+
"ds:hover:text-[var(--primary)]",
|
|
57
|
+
"ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none",
|
|
58
|
+
"ds:data-[active=true]:text-[var(--primary)]",
|
|
59
|
+
"ds:data-[active=true]:[box-shadow:inset_0_-2px_0_var(--primary)]"
|
|
60
|
+
].join(" ")
|
|
61
|
+
);
|
|
62
|
+
function G() {
|
|
63
|
+
const [e, d] = b(() => typeof window > "u" || !window.matchMedia ? !1 : window.matchMedia("(prefers-reduced-motion: reduce)").matches);
|
|
64
|
+
return j(() => {
|
|
65
|
+
if (typeof window > "u" || !window.matchMedia) return;
|
|
66
|
+
const r = window.matchMedia("(prefers-reduced-motion: reduce)"), n = () => d(r.matches);
|
|
67
|
+
return r.addEventListener("change", n), () => r.removeEventListener("change", n);
|
|
68
|
+
}, []), e;
|
|
69
|
+
}
|
|
70
|
+
function J(e, d, r) {
|
|
71
|
+
const [n, a] = b(!1), [m, o] = b(!1), l = L(0), u = L(!1);
|
|
72
|
+
return j(() => {
|
|
73
|
+
if (!e || typeof window > "u") return;
|
|
74
|
+
l.current = window.scrollY;
|
|
75
|
+
const f = () => {
|
|
76
|
+
const t = window.scrollY;
|
|
77
|
+
if (a(t > r), d === "scroll-up") {
|
|
78
|
+
const v = t - l.current;
|
|
79
|
+
v > 4 && t > r ? o(!0) : (v < -4 || t <= r) && o(!1);
|
|
80
|
+
}
|
|
81
|
+
l.current = t, u.current = !1;
|
|
82
|
+
}, p = () => {
|
|
83
|
+
u.current || (u.current = !0, window.requestAnimationFrame(f));
|
|
84
|
+
};
|
|
85
|
+
return window.addEventListener("scroll", p, { passive: !0 }), f(), () => {
|
|
86
|
+
window.removeEventListener("scroll", p);
|
|
87
|
+
};
|
|
88
|
+
}, [e, d, r]), { stuck: n, hidden: m };
|
|
89
|
+
}
|
|
90
|
+
const I = P(
|
|
91
|
+
({
|
|
92
|
+
logo: e,
|
|
93
|
+
homeHref: d = "/",
|
|
94
|
+
homeLabel: r,
|
|
95
|
+
navSlot: n,
|
|
96
|
+
actionsSlot: a,
|
|
97
|
+
variant: m = "default",
|
|
98
|
+
sticky: o = "none",
|
|
99
|
+
stickyOffset: l = 80,
|
|
100
|
+
menuLabel: u,
|
|
101
|
+
navLabel: f,
|
|
102
|
+
id: p,
|
|
103
|
+
className: t,
|
|
104
|
+
...v
|
|
105
|
+
}, E) => {
|
|
106
|
+
const { t: h } = S(), y = G(), O = Y(), H = `${p ?? O}-sheet`, [i, w] = b(!1), _ = m === "transparent" || o === "scroll-up", { stuck: z, hidden: T } = J(
|
|
107
|
+
_,
|
|
108
|
+
o,
|
|
109
|
+
l
|
|
110
|
+
), A = y ? !1 : T, M = N(() => w(!0), []), k = N(() => w(!1), []);
|
|
111
|
+
q(
|
|
112
|
+
E,
|
|
113
|
+
() => ({
|
|
114
|
+
openMenu: M,
|
|
115
|
+
closeMenu: k,
|
|
116
|
+
getMenuOpen: () => i
|
|
117
|
+
}),
|
|
118
|
+
[M, k, i]
|
|
119
|
+
);
|
|
120
|
+
const B = r ?? h("publicHeader.homeLabel", "AlfaDocs — home"), x = f ?? h("publicHeader.primaryNavLabel", "Primary"), V = u ?? (i ? h("publicHeader.closeMenu", "Close menu") : h("publicHeader.openMenu", "Open menu"));
|
|
121
|
+
return /* @__PURE__ */ g(
|
|
122
|
+
"header",
|
|
123
|
+
{
|
|
124
|
+
...v,
|
|
125
|
+
"data-component": "public-header",
|
|
126
|
+
"data-component-id": p,
|
|
127
|
+
"data-stuck": z ? "true" : void 0,
|
|
128
|
+
"data-hidden": A ? "true" : void 0,
|
|
129
|
+
"data-state": y ? "reduced-motion" : void 0,
|
|
130
|
+
className: X({ variant: m, sticky: o, className: t }),
|
|
131
|
+
children: [
|
|
132
|
+
/* @__PURE__ */ s(
|
|
133
|
+
"a",
|
|
134
|
+
{
|
|
135
|
+
href: d,
|
|
136
|
+
"aria-label": B,
|
|
137
|
+
className: "ds:inline-flex ds:items-center ds:rounded-[var(--radius-sm)] ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]",
|
|
138
|
+
children: e
|
|
139
|
+
}
|
|
140
|
+
),
|
|
141
|
+
n ? /* @__PURE__ */ s(
|
|
142
|
+
"nav",
|
|
143
|
+
{
|
|
144
|
+
"aria-label": x,
|
|
145
|
+
className: "ds:hidden ds:md:flex ds:items-center ds:gap-[var(--spacing-md)] ds:ms-[var(--spacing-lg)]",
|
|
146
|
+
children: n
|
|
147
|
+
}
|
|
148
|
+
) : null,
|
|
149
|
+
a ? /* @__PURE__ */ s("div", { className: "ds:hidden ds:md:flex ds:items-center ds:gap-[var(--spacing-sm)] ds:ms-auto", children: a }) : null,
|
|
150
|
+
/* @__PURE__ */ g(c, { open: i, onOpenChange: w, children: [
|
|
151
|
+
/* @__PURE__ */ s(c.Trigger, { asChild: !0, children: /* @__PURE__ */ s(
|
|
152
|
+
D,
|
|
153
|
+
{
|
|
154
|
+
size: "md",
|
|
155
|
+
intent: "ghost",
|
|
156
|
+
className: "ds:ms-auto ds:md:hidden",
|
|
157
|
+
icon: i ? /* @__PURE__ */ s(F, { "aria-hidden": "true" }) : /* @__PURE__ */ s(W, { "aria-hidden": "true" }),
|
|
158
|
+
"aria-controls": H,
|
|
159
|
+
"aria-expanded": i,
|
|
160
|
+
"aria-label": V
|
|
161
|
+
}
|
|
162
|
+
) }),
|
|
163
|
+
/* @__PURE__ */ g(c.Content, { side: "end", size: "md", id: H, children: [
|
|
164
|
+
/* @__PURE__ */ s(c.Header, { children: /* @__PURE__ */ s(c.Title, { children: x }) }),
|
|
165
|
+
/* @__PURE__ */ s(c.Body, { children: /* @__PURE__ */ g("div", { className: "ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]", children: [
|
|
166
|
+
n ? /* @__PURE__ */ s(
|
|
167
|
+
"nav",
|
|
168
|
+
{
|
|
169
|
+
"aria-label": x,
|
|
170
|
+
className: "ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]",
|
|
171
|
+
children: n
|
|
172
|
+
}
|
|
173
|
+
) : null,
|
|
174
|
+
a ? /* @__PURE__ */ s("div", { className: "ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:mt-[var(--spacing-md)]", children: a }) : null
|
|
175
|
+
] }) })
|
|
176
|
+
] })
|
|
177
|
+
] })
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
I.displayName = "PublicHeader";
|
|
184
|
+
const R = P(({ active: e, className: d, children: r, ...n }, a) => /* @__PURE__ */ s(
|
|
185
|
+
"a",
|
|
186
|
+
{
|
|
187
|
+
ref: a,
|
|
188
|
+
...n,
|
|
189
|
+
"data-active": e ? "true" : void 0,
|
|
190
|
+
"aria-current": e ? "page" : void 0,
|
|
191
|
+
className: [$(), d].filter(Boolean).join(" "),
|
|
192
|
+
children: r
|
|
193
|
+
}
|
|
194
|
+
));
|
|
195
|
+
R.displayName = "PublicHeader.NavLink";
|
|
196
|
+
const de = Object.assign(I, {
|
|
197
|
+
NavLink: R
|
|
198
|
+
}), ae = {
|
|
199
|
+
id: "public-header",
|
|
200
|
+
capabilities: ["open", "close"],
|
|
201
|
+
state: {
|
|
202
|
+
menuOpen: {
|
|
203
|
+
type: "boolean",
|
|
204
|
+
description: "Whether the mobile drawer is currently open.",
|
|
205
|
+
read: (e) => e.getMenuOpen()
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
actions: {
|
|
209
|
+
open_menu: {
|
|
210
|
+
safety: "read",
|
|
211
|
+
description: "Open the mobile drawer.",
|
|
212
|
+
invoke: (e) => {
|
|
213
|
+
e.openMenu();
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
close_menu: {
|
|
217
|
+
safety: "read",
|
|
218
|
+
description: "Close the mobile drawer.",
|
|
219
|
+
invoke: (e) => {
|
|
220
|
+
e.closeMenu();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
domHooks: {
|
|
225
|
+
root: { attr: "data-component", value: "public-header" },
|
|
226
|
+
instanceId: {
|
|
227
|
+
attr: "data-component-id",
|
|
228
|
+
sourceProp: "id",
|
|
229
|
+
description: "Sourced from the id prop on PublicHeader."
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
export {
|
|
234
|
+
de as P,
|
|
235
|
+
ae as p
|
|
236
|
+
};
|
|
237
|
+
//# sourceMappingURL=public-header.agent-AzJSINlU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-header.agent-AzJSINlU.js","sources":["../../src/components/public-header/public-header.tsx","../../src/components/public-header/public-header.agent.ts"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* PublicHeader — anonymous / pre-auth site header. */\n/* */\n/* Distinct from the kit's `<Header>` (authenticated app shell): this */\n/* one is for booking-website, signature-website, marketing surfaces — */\n/* anywhere the chrome should read as continuous with `alfadocs.com`, */\n/* not as the logged-in app. */\n/* */\n/* DOM: */\n/* <header role=banner data-component=public-header data-stuck=…> */\n/* <a aria-label=homeLabel>{logo}</a> */\n/* <nav aria-label=primaryNav>{navSlot}</nav> ≥ md only */\n/* <div class=actions>{actionsSlot}</div> ≥ md only */\n/* <IconButton menu trigger /> ↔ Sheet (partial drawer) < md only */\n/* </header> */\n/* ------------------------------------------------------------------ */\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useRef,\n useState,\n type AnchorHTMLAttributes,\n type ComponentPropsWithoutRef,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { Menu, X } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { IconButton } from '../button/icon-button';\nimport { Sheet } from '../sheet';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport interface PublicHeaderHandle {\n openMenu: () => void;\n closeMenu: () => void;\n getMenuOpen: () => boolean;\n}\n\nexport interface PublicHeaderProps\n extends\n Omit<ComponentPropsWithoutRef<'header'>, 'children'>,\n VariantProps<typeof rootVariants> {\n /** Logo lockup, typically `<Logo variant=\"wordmark\" tone=\"primary\" size=\"md\" />`. */\n logo: ReactNode;\n /** Optional href for the logo link. Defaults to `/`. */\n homeHref?: string;\n /** Accessible label for the logo link (falls back to `t('publicHeader.homeLabel')`). */\n homeLabel?: string;\n /** Primary nav links rendered inline ≥ md, collapsed into the mobile sheet below md. */\n navSlot?: ReactNode;\n /** Right-side actions (sign-in / register / role-switch CTAs). */\n actionsSlot?: ReactNode;\n /** Sticky behaviour. Default `none`. */\n sticky?: 'none' | 'top' | 'scroll-up';\n /** Pixel offset above which `sticky=\"scroll-up\"` engages and `transparent` fades to default. */\n stickyOffset?: number;\n /** Override the mobile menu trigger's accessible label (`t('publicHeader.openMenu')`). */\n menuLabel?: string;\n /** Override the navigation landmark label (`t('publicHeader.primaryNavLabel')`). */\n navLabel?: string;\n /** Consumer-supplied instance id, surfaced as `data-component-id`. */\n id?: string;\n}\n\nexport interface PublicHeaderNavLinkProps extends Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n 'children'\n> {\n href: string;\n active?: boolean;\n children: ReactNode;\n}\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst rootVariants = cva(\n [\n 'ds:flex ds:items-center ds:gap-[var(--spacing-md)]',\n 'ds:w-full ds:z-[var(--z-sticky)]',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)] ds:sm:ps-[var(--spacing-lg)] ds:sm:pe-[var(--spacing-lg)]',\n 'ds:h-14 ds:md:h-16',\n 'ds:transition-[background-color,box-shadow,transform] ds:duration-[var(--animation-duration)]',\n 'ds:motion-reduce:transition-none',\n ].join(' '),\n {\n variants: {\n variant: {\n default:\n 'ds:bg-[var(--card)] ds:text-[var(--foreground)] ds:border-b ds:border-[color:var(--border)]',\n // Transparent fades to default once data-stuck=\"true\" (set by the\n // rAF scroll listener). The fade is handled via the same\n // background utility so `data-stuck` swaps cleanly between states.\n transparent: [\n 'ds:bg-transparent ds:text-[var(--foreground)]',\n 'ds:data-[stuck=true]:bg-[var(--card)]',\n 'ds:data-[stuck=true]:shadow-[var(--shadow-md)]',\n 'ds:forced-colors:border-b ds:forced-colors:border-[CanvasText]',\n ].join(' '),\n dark: 'ds:bg-[var(--color-blue-800)] ds:text-[var(--primary-foreground)]',\n },\n sticky: {\n none: '',\n top: 'ds:sticky ds:top-0',\n // `scroll-up` is sticky but translates off-screen when scrolling\n // down past `stickyOffset` (handled by data-hidden=\"true\") and\n // slides back in when scrolling up.\n 'scroll-up': [\n 'ds:sticky ds:top-0',\n 'ds:data-[hidden=true]:-translate-y-full',\n ].join(' '),\n },\n },\n defaultVariants: { variant: 'default', sticky: 'none' },\n },\n);\n\nconst navLinkVariants = cva(\n [\n 'ds:inline-flex ds:items-center',\n 'ds:type-body-sm ds:font-medium',\n 'ds:text-[var(--foreground)]',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:py-[var(--spacing-2xs)]',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:hover:text-[var(--primary)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n 'ds:data-[active=true]:text-[var(--primary)]',\n 'ds:data-[active=true]:[box-shadow:inset_0_-2px_0_var(--primary)]',\n ].join(' '),\n);\n\n/* ------------------------------------------------------------------ */\n/* Reduced motion */\n/* ------------------------------------------------------------------ */\n\nfunction usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = useState(() => {\n if (typeof window === 'undefined' || !window.matchMedia) return false;\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n });\n useEffect(() => {\n if (typeof window === 'undefined' || !window.matchMedia) return;\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n const onChange = (): void => setReduced(mq.matches);\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n }, []);\n return reduced;\n}\n\n/* ------------------------------------------------------------------ */\n/* Scroll listener */\n/* */\n/* rAF-throttled tracker for two flags on the header root: */\n/* - `data-stuck`: scrollY > stickyOffset (drives the transparent → */\n/* solid fade + the scroll-up shadow lift). */\n/* - `data-hidden`: scrolling DOWN past stickyOffset hides the header */\n/* via transform; scrolling UP slides it back. Only emitted when */\n/* `sticky === 'scroll-up'`. */\n/* ------------------------------------------------------------------ */\n\nfunction useHeaderScrollState(\n enabled: boolean,\n sticky: NonNullable<PublicHeaderProps['sticky']>,\n stickyOffset: number,\n): {\n stuck: boolean;\n hidden: boolean;\n} {\n const [stuck, setStuck] = useState(false);\n const [hidden, setHidden] = useState(false);\n const lastYRef = useRef(0);\n const tickingRef = useRef(false);\n\n useEffect(() => {\n if (!enabled || typeof window === 'undefined') return;\n lastYRef.current = window.scrollY;\n\n const update = (): void => {\n const y = window.scrollY;\n setStuck(y > stickyOffset);\n if (sticky === 'scroll-up') {\n const delta = y - lastYRef.current;\n // Hide on a meaningful downward delta past the offset; show on\n // any upward delta. 4px deadband keeps minor wheel inertia from\n // flapping the bar.\n if (delta > 4 && y > stickyOffset) setHidden(true);\n else if (delta < -4 || y <= stickyOffset) setHidden(false);\n }\n lastYRef.current = y;\n tickingRef.current = false;\n };\n const onScroll = (): void => {\n if (tickingRef.current) return;\n tickingRef.current = true;\n window.requestAnimationFrame(update);\n };\n window.addEventListener('scroll', onScroll, { passive: true });\n // Initial sync so we don't miss state when mounted past the offset.\n update();\n return () => {\n window.removeEventListener('scroll', onScroll);\n };\n }, [enabled, sticky, stickyOffset]);\n\n return { stuck, hidden };\n}\n\n/* ------------------------------------------------------------------ */\n/* Root */\n/* ------------------------------------------------------------------ */\n\nconst PublicHeaderRoot = forwardRef<HTMLElement, PublicHeaderProps>(\n (\n {\n logo,\n homeHref = '/',\n homeLabel,\n navSlot,\n actionsSlot,\n variant = 'default',\n sticky = 'none',\n stickyOffset = 80,\n menuLabel,\n navLabel,\n id,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation();\n const reduced = usePrefersReducedMotion();\n const autoId = useId();\n const sheetId = `${id ?? autoId}-sheet`;\n const [menuOpen, setMenuOpen] = useState(false);\n\n // Scroll listener is only active when `transparent` or `scroll-up`\n // need it. Hidden state is disabled under reduced motion (the bar\n // stays visible — translating off-screen is precisely the motion\n // class of user the preference targets).\n const scrollEnabled = variant === 'transparent' || sticky === 'scroll-up';\n const { stuck, hidden } = useHeaderScrollState(\n scrollEnabled,\n sticky,\n stickyOffset,\n );\n const effectiveHidden = reduced ? false : hidden;\n\n const openMenu = useCallback((): void => setMenuOpen(true), []);\n const closeMenu = useCallback((): void => setMenuOpen(false), []);\n\n useImperativeHandle(\n ref as React.Ref<PublicHeaderHandle> | null | undefined,\n () => ({\n openMenu,\n closeMenu,\n getMenuOpen: () => menuOpen,\n }),\n [openMenu, closeMenu, menuOpen],\n );\n\n const resolvedHomeLabel =\n homeLabel ?? t('publicHeader.homeLabel', 'AlfaDocs — home');\n const resolvedNavLabel =\n navLabel ?? t('publicHeader.primaryNavLabel', 'Primary');\n const resolvedMenuLabel =\n menuLabel ??\n (menuOpen\n ? t('publicHeader.closeMenu', 'Close menu')\n : t('publicHeader.openMenu', 'Open menu'));\n\n return (\n <header\n {...rest}\n data-component=\"public-header\"\n data-component-id={id}\n data-stuck={stuck ? 'true' : undefined}\n data-hidden={effectiveHidden ? 'true' : undefined}\n data-state={reduced ? 'reduced-motion' : undefined}\n className={rootVariants({ variant, sticky, className })}\n >\n <a\n href={homeHref}\n aria-label={resolvedHomeLabel}\n className=\"ds:inline-flex ds:items-center ds:rounded-[var(--radius-sm)] ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]\"\n >\n {logo}\n </a>\n\n {navSlot ? (\n <nav\n aria-label={resolvedNavLabel}\n className=\"ds:hidden ds:md:flex ds:items-center ds:gap-[var(--spacing-md)] ds:ms-[var(--spacing-lg)]\"\n >\n {navSlot}\n </nav>\n ) : null}\n\n {actionsSlot ? (\n <div className=\"ds:hidden ds:md:flex ds:items-center ds:gap-[var(--spacing-sm)] ds:ms-auto\">\n {actionsSlot}\n </div>\n ) : null}\n\n {/* Mobile menu trigger (< md). Wired to a Sheet (partial drawer\n from inline-end) so navSlot + actionsSlot collapse there. */}\n <Sheet open={menuOpen} onOpenChange={setMenuOpen}>\n <Sheet.Trigger asChild>\n <IconButton\n size=\"md\"\n intent=\"ghost\"\n className=\"ds:ms-auto ds:md:hidden\"\n icon={\n menuOpen ? (\n <X aria-hidden=\"true\" />\n ) : (\n <Menu aria-hidden=\"true\" />\n )\n }\n aria-controls={sheetId}\n aria-expanded={menuOpen}\n aria-label={resolvedMenuLabel}\n />\n </Sheet.Trigger>\n <Sheet.Content side=\"end\" size=\"md\" id={sheetId}>\n <Sheet.Header>\n <Sheet.Title>{resolvedNavLabel}</Sheet.Title>\n </Sheet.Header>\n <Sheet.Body>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\">\n {navSlot ? (\n <nav\n aria-label={resolvedNavLabel}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\"\n >\n {navSlot}\n </nav>\n ) : null}\n {actionsSlot ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:mt-[var(--spacing-md)]\">\n {actionsSlot}\n </div>\n ) : null}\n </div>\n </Sheet.Body>\n </Sheet.Content>\n </Sheet>\n </header>\n );\n },\n);\nPublicHeaderRoot.displayName = 'PublicHeader';\n\n/* ------------------------------------------------------------------ */\n/* NavLink */\n/* ------------------------------------------------------------------ */\n\nconst PublicHeaderNavLink = forwardRef<\n HTMLAnchorElement,\n PublicHeaderNavLinkProps\n>(({ active, className, children, ...rest }, ref) => (\n <a\n ref={ref}\n {...rest}\n data-active={active ? 'true' : undefined}\n aria-current={active ? 'page' : undefined}\n className={[navLinkVariants(), className].filter(Boolean).join(' ')}\n >\n {children}\n </a>\n));\nPublicHeaderNavLink.displayName = 'PublicHeader.NavLink';\n\n/* ------------------------------------------------------------------ */\n/* Public surface */\n/* ------------------------------------------------------------------ */\n\nexport const PublicHeader = Object.assign(PublicHeaderRoot, {\n NavLink: PublicHeaderNavLink,\n});\n","import type { AgentAdapter } from '../../agent/types';\nimport type { PublicHeaderHandle } from './public-header';\n\nexport const publicHeaderAgent: AgentAdapter<PublicHeaderHandle> = {\n id: 'public-header',\n capabilities: ['open', 'close'],\n state: {\n menuOpen: {\n type: 'boolean',\n description: 'Whether the mobile drawer is currently open.',\n read: (handle) => handle.getMenuOpen(),\n },\n },\n actions: {\n open_menu: {\n safety: 'read',\n description: 'Open the mobile drawer.',\n invoke: (handle) => {\n handle.openMenu();\n },\n },\n close_menu: {\n safety: 'read',\n description: 'Close the mobile drawer.',\n invoke: (handle) => {\n handle.closeMenu();\n },\n },\n },\n domHooks: {\n root: { attr: 'data-component', value: 'public-header' },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop on PublicHeader.',\n },\n },\n};\n"],"names":["rootVariants","cva","navLinkVariants","usePrefersReducedMotion","reduced","setReduced","useState","useEffect","mq","onChange","useHeaderScrollState","enabled","sticky","stickyOffset","stuck","setStuck","hidden","setHidden","lastYRef","useRef","tickingRef","update","y","delta","onScroll","PublicHeaderRoot","forwardRef","logo","homeHref","homeLabel","navSlot","actionsSlot","variant","menuLabel","navLabel","id","className","rest","ref","t","useTranslation","autoId","useId","sheetId","menuOpen","setMenuOpen","scrollEnabled","effectiveHidden","openMenu","useCallback","closeMenu","useImperativeHandle","resolvedHomeLabel","resolvedNavLabel","resolvedMenuLabel","jsxs","jsx","Sheet","IconButton","X","Menu","PublicHeaderNavLink","active","children","PublicHeader","publicHeaderAgent","handle"],"mappings":"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"}
|