@otl-core/next-navigation 1.1.19 → 1.1.21

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.
Files changed (70) hide show
  1. package/dist/components/icons/chevron-icon.d.ts +10 -0
  2. package/dist/components/icons/chevron-icon.js +30 -0
  3. package/dist/components/icons/chevron-icon.js.map +1 -0
  4. package/dist/components/icons/grid-icon.d.ts +12 -0
  5. package/dist/components/icons/grid-icon.js +95 -0
  6. package/dist/components/icons/grid-icon.js.map +1 -0
  7. package/dist/components/icons/hamburger-icon.d.ts +12 -0
  8. package/dist/components/icons/hamburger-icon.js +78 -0
  9. package/dist/components/icons/hamburger-icon.js.map +1 -0
  10. package/dist/components/icons/kebab-icon.d.ts +12 -0
  11. package/dist/components/icons/kebab-icon.js +81 -0
  12. package/dist/components/icons/kebab-icon.js.map +1 -0
  13. package/dist/components/icons/meatballs-icon.d.ts +12 -0
  14. package/dist/components/icons/meatballs-icon.js +81 -0
  15. package/dist/components/icons/meatballs-icon.js.map +1 -0
  16. package/dist/components/icons/plus-icon.d.ts +12 -0
  17. package/dist/components/icons/plus-icon.js +64 -0
  18. package/dist/components/icons/plus-icon.js.map +1 -0
  19. package/dist/components/icons/toggle-icon-map.d.ts +14 -0
  20. package/dist/components/icons/toggle-icon-map.js +22 -0
  21. package/dist/components/icons/toggle-icon-map.js.map +1 -0
  22. package/dist/components/icons/toggle-icon.d.ts +14 -0
  23. package/dist/components/icons/toggle-icon.js +39 -0
  24. package/dist/components/icons/toggle-icon.js.map +1 -0
  25. package/dist/components/items/animated-toggle-icon.d.ts +15 -0
  26. package/dist/components/items/animated-toggle-icon.js +39 -0
  27. package/dist/components/items/animated-toggle-icon.js.map +1 -0
  28. package/dist/components/items/dropdown-content-item.d.ts +12 -0
  29. package/dist/components/items/dropdown-content-item.js +223 -0
  30. package/dist/components/items/dropdown-content-item.js.map +1 -0
  31. package/dist/components/items/logo.d.ts +11 -0
  32. package/dist/components/items/logo.js +42 -0
  33. package/dist/components/items/logo.js.map +1 -0
  34. package/dist/components/mobile/mobile-menu-toggle.d.ts +12 -0
  35. package/dist/components/mobile/mobile-menu-toggle.js +53 -0
  36. package/dist/components/mobile/mobile-menu-toggle.js.map +1 -0
  37. package/dist/components/mobile/navigation-header-wrapper.d.ts +11 -0
  38. package/dist/components/mobile/navigation-header-wrapper.js +15 -0
  39. package/dist/components/mobile/navigation-header-wrapper.js.map +1 -0
  40. package/dist/components/navigation/dropdown.d.ts +19 -0
  41. package/dist/components/navigation/dropdown.js +258 -0
  42. package/dist/components/navigation/dropdown.js.map +1 -0
  43. package/dist/components/navigation/header.d.ts +13 -0
  44. package/dist/components/navigation/header.js +305 -0
  45. package/dist/components/navigation/header.js.map +1 -0
  46. package/dist/components/navigation/navbar.d.ts +21 -0
  47. package/dist/components/navigation/navbar.js +66 -0
  48. package/dist/components/navigation/navbar.js.map +1 -0
  49. package/dist/components/sections/navbar-sections.d.ts +23 -0
  50. package/dist/components/sections/navbar-sections.js +103 -0
  51. package/dist/components/sections/navbar-sections.js.map +1 -0
  52. package/dist/components/sections/navigation-item.d.ts +13 -0
  53. package/dist/components/sections/navigation-item.js +171 -0
  54. package/dist/components/sections/navigation-item.js.map +1 -0
  55. package/dist/components/ui/button.d.ts +14 -0
  56. package/dist/components/ui/button.js +51 -0
  57. package/dist/components/ui/button.js.map +1 -0
  58. package/dist/context/navigation-context.d.ts +17 -0
  59. package/dist/context/navigation-context.js +93 -0
  60. package/dist/context/navigation-context.js.map +1 -0
  61. package/dist/index.d.ts +12 -86
  62. package/dist/index.js +19 -2077
  63. package/dist/index.js.map +1 -1
  64. package/dist/lib/footer.utils.d.ts +20 -0
  65. package/dist/lib/footer.utils.js +84 -0
  66. package/dist/lib/footer.utils.js.map +1 -0
  67. package/dist/lib/navigation.utils.d.ts +34 -0
  68. package/dist/lib/navigation.utils.js +387 -0
  69. package/dist/lib/navigation.utils.js.map +1 -0
  70. package/package.json +5 -5
@@ -0,0 +1,21 @@
1
+ import { HeaderSection, HeaderConfig, Site } from '@otl-core/cms-types';
2
+ import { ReactNode } from 'react';
3
+
4
+ interface NavbarProps {
5
+ id: string;
6
+ className: string;
7
+ headerStyles: React.CSSProperties;
8
+ sortedSections: HeaderSection[];
9
+ navigation: HeaderConfig;
10
+ resolvedColors: Record<string, string | undefined>;
11
+ itemsShowClass: string;
12
+ togglerHideClass: string;
13
+ togglerSectionId: string;
14
+ site: Site;
15
+ mobileMenuId?: string;
16
+ containerContent?: boolean;
17
+ isSameLayer?: boolean;
18
+ }
19
+ declare function Navbar({ id, className, headerStyles, sortedSections, navigation, resolvedColors, itemsShowClass, togglerHideClass, togglerSectionId, site, mobileMenuId, containerContent, isSameLayer, }: NavbarProps): ReactNode;
20
+
21
+ export { Navbar };
@@ -0,0 +1,66 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "@otl-core/style-utils";
3
+ import { NavbarSections } from "../sections/navbar-sections";
4
+ function Navbar({
5
+ id,
6
+ className,
7
+ headerStyles,
8
+ sortedSections,
9
+ navigation,
10
+ resolvedColors,
11
+ itemsShowClass,
12
+ togglerHideClass,
13
+ togglerSectionId,
14
+ site,
15
+ mobileMenuId,
16
+ containerContent = false,
17
+ isSameLayer = false
18
+ }) {
19
+ const dropdownLayer = navigation.style?.dropdown?.layer || "below";
20
+ function getNavbarZ() {
21
+ if (isSameLayer) return "";
22
+ return dropdownLayer === "above" ? "z-[1]" : "z-[9999]";
23
+ }
24
+ const navbarZ = getNavbarZ();
25
+ const sectionsContent = sortedSections.map((section) => /* @__PURE__ */ jsx(
26
+ NavbarSections,
27
+ {
28
+ section,
29
+ site,
30
+ navigation,
31
+ resolvedColors,
32
+ itemsShowClass,
33
+ togglerHideClass,
34
+ isTogglerSection: section.id === togglerSectionId,
35
+ mobileMenuId
36
+ },
37
+ section.id
38
+ ));
39
+ return /* @__PURE__ */ jsx(
40
+ "div",
41
+ {
42
+ className: cn(
43
+ `navbar-${id}`,
44
+ `relative flex items-center justify-between`,
45
+ navbarZ,
46
+ !containerContent && `navbar-inner-${id}`,
47
+ className
48
+ ),
49
+ style: isSameLayer ? void 0 : headerStyles,
50
+ children: containerContent ? /* @__PURE__ */ jsx(
51
+ "div",
52
+ {
53
+ className: cn(
54
+ "container mx-auto flex items-center justify-between w-full",
55
+ `navbar-inner-${id}`
56
+ ),
57
+ children: sectionsContent
58
+ }
59
+ ) : sectionsContent
60
+ }
61
+ );
62
+ }
63
+ export {
64
+ Navbar
65
+ };
66
+ //# sourceMappingURL=navbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/navigation/navbar.tsx"],"sourcesContent":["import { Site, HeaderConfig, HeaderSection } from \"@otl-core/cms-types\";\nimport { cn } from \"@otl-core/style-utils\";\nimport { ReactNode } from \"react\";\nimport { NavbarSections } from \"../sections/navbar-sections\";\n\ninterface NavbarProps {\n id: string;\n className: string;\n headerStyles: React.CSSProperties;\n sortedSections: HeaderSection[];\n navigation: HeaderConfig;\n resolvedColors: Record<string, string | undefined>;\n itemsShowClass: string;\n togglerHideClass: string;\n togglerSectionId: string;\n site: Site;\n mobileMenuId?: string;\n containerContent?: boolean;\n isSameLayer?: boolean;\n}\n\nexport function Navbar({\n id,\n className,\n headerStyles,\n sortedSections,\n navigation,\n resolvedColors,\n itemsShowClass,\n togglerHideClass,\n togglerSectionId,\n site,\n mobileMenuId,\n containerContent = false,\n isSameLayer = false,\n}: NavbarProps): ReactNode {\n const dropdownLayer = navigation.style?.dropdown?.layer || \"below\";\n\n function getNavbarZ(): string {\n if (isSameLayer) return \"\";\n return dropdownLayer === \"above\" ? \"z-[1]\" : \"z-[9999]\";\n }\n const navbarZ = getNavbarZ();\n\n const sectionsContent = sortedSections.map((section) => (\n <NavbarSections\n key={section.id}\n section={section}\n site={site}\n navigation={navigation}\n resolvedColors={resolvedColors}\n itemsShowClass={itemsShowClass}\n togglerHideClass={togglerHideClass}\n isTogglerSection={section.id === togglerSectionId}\n mobileMenuId={mobileMenuId}\n />\n ));\n\n return (\n <div\n className={cn(\n `navbar-${id}`,\n `relative flex items-center justify-between`,\n navbarZ,\n !containerContent && `navbar-inner-${id}`,\n className,\n )}\n style={isSameLayer ? undefined : headerStyles}\n >\n {containerContent ? (\n <div\n className={cn(\n \"container mx-auto flex items-center justify-between w-full\",\n `navbar-inner-${id}`,\n )}\n >\n {sectionsContent}\n </div>\n ) : (\n sectionsContent\n )}\n </div>\n );\n}\n"],"mappings":"AA6CI;AA5CJ,SAAS,UAAU;AAEnB,SAAS,sBAAsB;AAkBxB,SAAS,OAAO;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AAAA,EACnB,cAAc;AAChB,GAA2B;AACzB,QAAM,gBAAgB,WAAW,OAAO,UAAU,SAAS;AAE3D,WAAS,aAAqB;AAC5B,QAAI,YAAa,QAAO;AACxB,WAAO,kBAAkB,UAAU,UAAU;AAAA,EAC/C;AACA,QAAM,UAAU,WAAW;AAE3B,QAAM,kBAAkB,eAAe,IAAI,CAAC,YAC1C;AAAA,IAAC;AAAA;AAAA,MAEC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,kBAAkB,QAAQ,OAAO;AAAA,MACjC;AAAA;AAAA,IARK,QAAQ;AAAA,EASf,CACD;AAED,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT,UAAU,EAAE;AAAA,QACZ;AAAA,QACA;AAAA,QACA,CAAC,oBAAoB,gBAAgB,EAAE;AAAA,QACvC;AAAA,MACF;AAAA,MACA,OAAO,cAAc,SAAY;AAAA,MAEhC,6BACC;AAAA,QAAC;AAAA;AAAA,UACC,WAAW;AAAA,YACT;AAAA,YACA,gBAAgB,EAAE;AAAA,UACpB;AAAA,UAEC;AAAA;AAAA,MACH,IAEA;AAAA;AAAA,EAEJ;AAEJ;","names":[]}
@@ -0,0 +1,23 @@
1
+ import { HeaderSection, HeaderConfig, Site } from '@otl-core/cms-types';
2
+ import React__default from 'react';
3
+
4
+ interface NavbarSectionProps {
5
+ section: HeaderSection;
6
+ logo?: {
7
+ text?: string;
8
+ url?: string;
9
+ alt?: string;
10
+ width?: number;
11
+ height?: number;
12
+ };
13
+ navigation?: HeaderConfig;
14
+ resolvedColors: Record<string, string | undefined>;
15
+ itemsShowClass?: string;
16
+ togglerHideClass?: string;
17
+ isTogglerSection?: boolean;
18
+ site: Site;
19
+ mobileMenuId?: string;
20
+ }
21
+ declare const NavbarSections: React__default.FC<NavbarSectionProps>;
22
+
23
+ export { NavbarSections };
@@ -0,0 +1,103 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
+ import { resolveItemVisibility } from "../../lib/navigation.utils";
4
+ import { MobileMenuToggle } from "../mobile/mobile-menu-toggle";
5
+ import { NavigationItem } from "./navigation-item";
6
+ const NavbarSections = ({
7
+ section,
8
+ navigation,
9
+ resolvedColors,
10
+ itemsShowClass,
11
+ togglerHideClass,
12
+ isTogglerSection,
13
+ site,
14
+ mobileMenuId
15
+ }) => {
16
+ const hasLogo = useMemo(() => {
17
+ return section?.items?.some(
18
+ (item) => item.type === "logo"
19
+ );
20
+ }, [section.items]);
21
+ const sectionStyle = useMemo(() => {
22
+ let flexValue = section.flex;
23
+ if (flexValue === "0" || flexValue === void 0) {
24
+ if (hasLogo) {
25
+ flexValue = "0 0 auto";
26
+ } else {
27
+ flexValue = "0 1 auto";
28
+ }
29
+ }
30
+ return {
31
+ flex: flexValue,
32
+ justifyContent: section.justify || "flex-start",
33
+ alignItems: section.align || "center",
34
+ gap: section.gap || "0",
35
+ // For non-logo sections, ensure they can shrink below content width
36
+ ...!hasLogo ? { minWidth: 0 } : {}
37
+ };
38
+ }, [section, hasLogo]);
39
+ const shouldHide = useMemo(() => {
40
+ if (!section.hideWhenEmpty) return false;
41
+ const hasLogo2 = section.items?.some(
42
+ (item) => item.type === "logo"
43
+ );
44
+ if (hasLogo2) return false;
45
+ const hasAlwaysVisibleItems = section.items?.some(
46
+ (item) => resolveItemVisibility(item) === "navbar-only" || resolveItemVisibility(item) === "both"
47
+ );
48
+ if (hasAlwaysVisibleItems) return false;
49
+ const collapsibleItems = section.items?.filter(
50
+ (item) => item.type !== "logo" && resolveItemVisibility(item) !== "navbar-only" && resolveItemVisibility(item) !== "both"
51
+ );
52
+ if (collapsibleItems && collapsibleItems.length > 0) return false;
53
+ if (isTogglerSection) return false;
54
+ return true;
55
+ }, [section.hideWhenEmpty, section.items, isTogglerSection]);
56
+ const sectionVisibilityClass = useMemo(() => {
57
+ if (!section.hideWhenEmpty) return "";
58
+ const alwaysVisibleItems = section.items?.filter(
59
+ (item) => item.type === "logo" || resolveItemVisibility(item) === "navbar-only" || resolveItemVisibility(item) === "both"
60
+ );
61
+ if (alwaysVisibleItems && alwaysVisibleItems.length > 0) return "";
62
+ return itemsShowClass;
63
+ }, [section.hideWhenEmpty, section.items, itemsShowClass]);
64
+ if (shouldHide) {
65
+ return null;
66
+ }
67
+ return /* @__PURE__ */ jsxs(
68
+ "div",
69
+ {
70
+ className: `flex flex-row ${sectionVisibilityClass}`,
71
+ style: sectionStyle,
72
+ "data-section-id": section.id,
73
+ children: [
74
+ section.items?.map((item) => {
75
+ return /* @__PURE__ */ jsx(
76
+ NavigationItem,
77
+ {
78
+ item,
79
+ navigation,
80
+ resolvedColors,
81
+ itemsShowClass,
82
+ site
83
+ },
84
+ item.id
85
+ );
86
+ }),
87
+ isTogglerSection && navigation && mobileMenuId && /* @__PURE__ */ jsx("div", { className: `flex flex-shrink-0 ${togglerHideClass}`, children: /* @__PURE__ */ jsx(
88
+ MobileMenuToggle,
89
+ {
90
+ navigation,
91
+ resolvedColors,
92
+ toggleId: `toggler-${section.id}`,
93
+ mobileMenuId
94
+ }
95
+ ) })
96
+ ]
97
+ }
98
+ );
99
+ };
100
+ export {
101
+ NavbarSections
102
+ };
103
+ //# sourceMappingURL=navbar-sections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/sections/navbar-sections.tsx"],"sourcesContent":["import {\n Site,\n HeaderConfig,\n HeaderNavigationItem,\n HeaderSection,\n} from \"@otl-core/cms-types\";\nimport React, { useMemo } from \"react\";\nimport { resolveItemVisibility } from \"../../lib/navigation.utils\";\nimport { MobileMenuToggle } from \"../mobile/mobile-menu-toggle\";\nimport { NavigationItem } from \"./navigation-item\";\n\ninterface NavbarSectionProps {\n section: HeaderSection;\n logo?: {\n text?: string;\n url?: string;\n alt?: string;\n width?: number;\n height?: number;\n };\n navigation?: HeaderConfig;\n resolvedColors: Record<string, string | undefined>;\n itemsShowClass?: string;\n togglerHideClass?: string;\n isTogglerSection?: boolean;\n site: Site;\n mobileMenuId?: string;\n}\n\nexport const NavbarSections: React.FC<NavbarSectionProps> = ({\n section,\n navigation,\n resolvedColors,\n itemsShowClass,\n togglerHideClass,\n isTogglerSection,\n site,\n mobileMenuId,\n}) => {\n // Check if this section contains a logo\n const hasLogo = useMemo(() => {\n return section?.items?.some(\n (item: HeaderNavigationItem) => item.type === \"logo\",\n );\n }, [section.items]);\n\n const sectionStyle: React.CSSProperties = useMemo(() => {\n let flexValue = section.flex;\n\n // Smart flex handling for better logo and navigation behavior\n if (flexValue === \"0\" || flexValue === undefined) {\n if (hasLogo) {\n // Logo section: Don't shrink, take natural size\n flexValue = \"0 0 auto\";\n } else {\n // Other sections with flex: 0 - allow shrinking if needed\n flexValue = \"0 1 auto\";\n }\n }\n\n return {\n flex: flexValue,\n justifyContent: section.justify || \"flex-start\",\n alignItems: section.align || \"center\",\n gap: section.gap || \"0\",\n // For non-logo sections, ensure they can shrink below content width\n ...(!hasLogo ? { minWidth: 0 } : {}),\n };\n }, [section, hasLogo]);\n\n // Check if section should be hidden when empty\n const shouldHide = useMemo(() => {\n if (!section.hideWhenEmpty) return false;\n\n // Never hide if there's a logo - logos are always visible\n const hasLogo = section.items?.some(\n (item: HeaderNavigationItem) => item.type === \"logo\",\n );\n if (hasLogo) return false;\n\n // Never hide if there are items with visibility: navbar-only or both - they're always visible\n const hasAlwaysVisibleItems = section.items?.some(\n (item: HeaderNavigationItem) =>\n resolveItemVisibility(item) === \"navbar-only\" ||\n resolveItemVisibility(item) === \"both\",\n );\n if (hasAlwaysVisibleItems) return false;\n\n // Check if there are any collapsible items (responsive or mobile-only)\n const collapsibleItems = section.items?.filter(\n (item: HeaderNavigationItem) =>\n item.type !== \"logo\" &&\n resolveItemVisibility(item) !== \"navbar-only\" &&\n resolveItemVisibility(item) !== \"both\",\n );\n\n // If there are collapsible items, section is not empty\n if (collapsibleItems && collapsibleItems.length > 0) return false;\n\n // Don't hide if this is the toggler section (toggler button should show)\n if (isTogglerSection) return false;\n\n return true;\n }, [section.hideWhenEmpty, section.items, isTogglerSection]);\n\n // Apply the same visibility class as items if hideWhenEmpty is enabled\n const sectionVisibilityClass = useMemo(() => {\n if (!section.hideWhenEmpty) return \"\";\n\n // Check if there are any items that are always visible (logos or navbar-only/both)\n const alwaysVisibleItems = section.items?.filter(\n (item: HeaderNavigationItem) =>\n item.type === \"logo\" ||\n resolveItemVisibility(item) === \"navbar-only\" ||\n resolveItemVisibility(item) === \"both\",\n );\n\n // If there are always visible items, don't apply visibility class\n if (alwaysVisibleItems && alwaysVisibleItems.length > 0) return \"\";\n\n // If all items collapse, apply the same visibility class\n return itemsShowClass;\n }, [section.hideWhenEmpty, section.items, itemsShowClass]);\n\n if (shouldHide) {\n return null;\n }\n\n return (\n <div\n className={`flex flex-row ${sectionVisibilityClass}`}\n style={sectionStyle}\n data-section-id={section.id}\n >\n {section.items?.map((item: HeaderNavigationItem) => {\n return (\n <NavigationItem\n key={item.id}\n item={item}\n navigation={navigation}\n resolvedColors={resolvedColors}\n itemsShowClass={itemsShowClass}\n site={site}\n />\n );\n })}\n {isTogglerSection && navigation && mobileMenuId && (\n <div className={`flex flex-shrink-0 ${togglerHideClass}`}>\n <MobileMenuToggle\n navigation={navigation}\n resolvedColors={resolvedColors}\n toggleId={`toggler-${section.id}`}\n mobileMenuId={mobileMenuId}\n />\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"AAiII,SAOM,KAPN;AA3HJ,SAAgB,eAAe;AAC/B,SAAS,6BAA6B;AACtC,SAAS,wBAAwB;AACjC,SAAS,sBAAsB;AAoBxB,MAAM,iBAA+C,CAAC;AAAA,EAC3D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AAEJ,QAAM,UAAU,QAAQ,MAAM;AAC5B,WAAO,SAAS,OAAO;AAAA,MACrB,CAAC,SAA+B,KAAK,SAAS;AAAA,IAChD;AAAA,EACF,GAAG,CAAC,QAAQ,KAAK,CAAC;AAElB,QAAM,eAAoC,QAAQ,MAAM;AACtD,QAAI,YAAY,QAAQ;AAGxB,QAAI,cAAc,OAAO,cAAc,QAAW;AAChD,UAAI,SAAS;AAEX,oBAAY;AAAA,MACd,OAAO;AAEL,oBAAY;AAAA,MACd;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,gBAAgB,QAAQ,WAAW;AAAA,MACnC,YAAY,QAAQ,SAAS;AAAA,MAC7B,KAAK,QAAQ,OAAO;AAAA;AAAA,MAEpB,GAAI,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,CAAC;AAAA,IACpC;AAAA,EACF,GAAG,CAAC,SAAS,OAAO,CAAC;AAGrB,QAAM,aAAa,QAAQ,MAAM;AAC/B,QAAI,CAAC,QAAQ,cAAe,QAAO;AAGnC,UAAMA,WAAU,QAAQ,OAAO;AAAA,MAC7B,CAAC,SAA+B,KAAK,SAAS;AAAA,IAChD;AACA,QAAIA,SAAS,QAAO;AAGpB,UAAM,wBAAwB,QAAQ,OAAO;AAAA,MAC3C,CAAC,SACC,sBAAsB,IAAI,MAAM,iBAChC,sBAAsB,IAAI,MAAM;AAAA,IACpC;AACA,QAAI,sBAAuB,QAAO;AAGlC,UAAM,mBAAmB,QAAQ,OAAO;AAAA,MACtC,CAAC,SACC,KAAK,SAAS,UACd,sBAAsB,IAAI,MAAM,iBAChC,sBAAsB,IAAI,MAAM;AAAA,IACpC;AAGA,QAAI,oBAAoB,iBAAiB,SAAS,EAAG,QAAO;AAG5D,QAAI,iBAAkB,QAAO;AAE7B,WAAO;AAAA,EACT,GAAG,CAAC,QAAQ,eAAe,QAAQ,OAAO,gBAAgB,CAAC;AAG3D,QAAM,yBAAyB,QAAQ,MAAM;AAC3C,QAAI,CAAC,QAAQ,cAAe,QAAO;AAGnC,UAAM,qBAAqB,QAAQ,OAAO;AAAA,MACxC,CAAC,SACC,KAAK,SAAS,UACd,sBAAsB,IAAI,MAAM,iBAChC,sBAAsB,IAAI,MAAM;AAAA,IACpC;AAGA,QAAI,sBAAsB,mBAAmB,SAAS,EAAG,QAAO;AAGhE,WAAO;AAAA,EACT,GAAG,CAAC,QAAQ,eAAe,QAAQ,OAAO,cAAc,CAAC;AAEzD,MAAI,YAAY;AACd,WAAO;AAAA,EACT;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,iBAAiB,sBAAsB;AAAA,MAClD,OAAO;AAAA,MACP,mBAAiB,QAAQ;AAAA,MAExB;AAAA,gBAAQ,OAAO,IAAI,CAAC,SAA+B;AAClD,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA;AAAA,YALK,KAAK;AAAA,UAMZ;AAAA,QAEJ,CAAC;AAAA,QACA,oBAAoB,cAAc,gBACjC,oBAAC,SAAI,WAAW,sBAAsB,gBAAgB,IACpD;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA,UAAU,WAAW,QAAQ,EAAE;AAAA,YAC/B;AAAA;AAAA,QACF,GACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;","names":["hasLogo"]}
@@ -0,0 +1,13 @@
1
+ import { HeaderNavigationItem, HeaderConfig, Site } from '@otl-core/cms-types';
2
+ import React__default from 'react';
3
+
4
+ interface NavigationItemProps {
5
+ item: HeaderNavigationItem;
6
+ navigation?: HeaderConfig;
7
+ resolvedColors: Record<string, string | undefined>;
8
+ itemsShowClass?: string;
9
+ site: Site;
10
+ }
11
+ declare const NavigationItem: React__default.FC<NavigationItemProps>;
12
+
13
+ export { NavigationItem };
@@ -0,0 +1,171 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import Link from "next/link";
4
+ import { useNavigation } from "../../context/navigation-context";
5
+ import {
6
+ getLocalizedString,
7
+ getVisibilityClass,
8
+ parseMarkdownToHTML
9
+ } from "../../lib/navigation.utils";
10
+ import { ChevronIcon } from "../icons/chevron-icon";
11
+ import { Logo } from "../items/logo";
12
+ import { Button } from "../ui/button";
13
+ const variantMap = {
14
+ primary: "default",
15
+ secondary: "secondary",
16
+ outline: "outline",
17
+ ghost: "ghost"
18
+ };
19
+ const sizeMap = {
20
+ sm: "sm",
21
+ md: "default",
22
+ lg: "lg"
23
+ };
24
+ const NavigationItem = ({
25
+ item,
26
+ navigation,
27
+ resolvedColors,
28
+ itemsShowClass,
29
+ site
30
+ }) => {
31
+ const label = getLocalizedString(item.label, site);
32
+ const visibilityClass = getVisibilityClass(item, itemsShowClass);
33
+ if (item.type === "logo") {
34
+ if (!navigation?.logo) return null;
35
+ return /* @__PURE__ */ jsx(
36
+ Logo,
37
+ {
38
+ navigation,
39
+ siteName: navigation.logo.alt || "Logo",
40
+ logoTextColor: resolvedColors.logoText
41
+ }
42
+ );
43
+ }
44
+ if (item.type === "link") {
45
+ const config = item.config;
46
+ const linkStyle = {
47
+ color: resolvedColors.linkColor
48
+ };
49
+ return /* @__PURE__ */ jsxs(
50
+ Link,
51
+ {
52
+ href: config.href,
53
+ style: linkStyle,
54
+ target: config.external ? "_blank" : void 0,
55
+ rel: config.external ? "noopener noreferrer" : void 0,
56
+ className: `${visibilityClass} items-center whitespace-nowrap`,
57
+ children: [
58
+ config.icon && /* @__PURE__ */ jsx("span", { children: config.icon }),
59
+ label
60
+ ]
61
+ }
62
+ );
63
+ }
64
+ if (item.type === "button") {
65
+ const config = item.config;
66
+ return /* @__PURE__ */ jsx("div", { className: `${visibilityClass} items-center`, children: /* @__PURE__ */ jsx(
67
+ Button,
68
+ {
69
+ asChild: true,
70
+ variant: config.variant ? variantMap[config.variant] : "default",
71
+ size: config.size ? sizeMap[config.size] : "default",
72
+ className: "whitespace-nowrap",
73
+ style: {
74
+ fontSize: `var(--btn-font-${config.size || "md"})`
75
+ },
76
+ children: /* @__PURE__ */ jsxs(
77
+ Link,
78
+ {
79
+ href: config.href,
80
+ target: config.external ? "_blank" : void 0,
81
+ rel: config.external ? "noopener noreferrer" : void 0,
82
+ children: [
83
+ config.icon && /* @__PURE__ */ jsx("span", { children: config.icon }),
84
+ label
85
+ ]
86
+ }
87
+ )
88
+ }
89
+ ) });
90
+ }
91
+ if (item.type === "dropdown") {
92
+ const { toggleDropdown, activeDropdown } = useNavigation();
93
+ const isOpen = activeDropdown === item.id;
94
+ const linkStyle = {
95
+ color: resolvedColors.linkColor
96
+ };
97
+ return /* @__PURE__ */ jsxs(
98
+ "button",
99
+ {
100
+ onClick: () => toggleDropdown(item.id),
101
+ style: linkStyle,
102
+ "aria-expanded": isOpen,
103
+ "aria-haspopup": "true",
104
+ className: `${visibilityClass} items-center gap-1 whitespace-nowrap`,
105
+ children: [
106
+ label,
107
+ /* @__PURE__ */ jsx(ChevronIcon, { isOpen, size: 16 })
108
+ ]
109
+ }
110
+ );
111
+ }
112
+ if (item.type === "markdown") {
113
+ const config = item.config;
114
+ const content = getLocalizedString(config.content, site);
115
+ const html = parseMarkdownToHTML(content);
116
+ const textStyle = {};
117
+ if (config.color) {
118
+ if (typeof config.color === "string") {
119
+ textStyle.color = config.color;
120
+ } else if (config.color.type === "custom" && config.color.value) {
121
+ textStyle.color = typeof config.color.value === "string" ? config.color.value : config.color.value.background;
122
+ } else if (config.color.type === "variable" && config.color.value) {
123
+ textStyle.color = `var(${config.color.value})`;
124
+ } else if (config.color.type === "theme" && config.color.value) {
125
+ textStyle.color = `var(--${config.color.value})`;
126
+ }
127
+ } else {
128
+ textStyle.color = resolvedColors.text;
129
+ }
130
+ return /* @__PURE__ */ jsx(
131
+ "div",
132
+ {
133
+ dangerouslySetInnerHTML: { __html: html },
134
+ className: `${visibilityClass} items-center whitespace-nowrap [&_*]:text-inherit`,
135
+ style: textStyle
136
+ }
137
+ );
138
+ }
139
+ if (item.type === "image") {
140
+ const config = item.config;
141
+ if (!config.src) return null;
142
+ const imgElement = /* @__PURE__ */ jsx(
143
+ "img",
144
+ {
145
+ src: config.src,
146
+ alt: config.alt || "",
147
+ style: {
148
+ display: "block",
149
+ width: config.width || "auto",
150
+ height: config.height || "auto",
151
+ objectFit: config.objectFit || "contain"
152
+ }
153
+ }
154
+ );
155
+ const wrapped = config.href ? /* @__PURE__ */ jsx(
156
+ Link,
157
+ {
158
+ href: config.href,
159
+ target: config.external ? "_blank" : void 0,
160
+ rel: config.external ? "noopener noreferrer" : void 0,
161
+ children: imgElement
162
+ }
163
+ ) : imgElement;
164
+ return /* @__PURE__ */ jsx("div", { className: `${visibilityClass} items-center`, children: wrapped });
165
+ }
166
+ return null;
167
+ };
168
+ export {
169
+ NavigationItem
170
+ };
171
+ //# sourceMappingURL=navigation-item.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/sections/navigation-item.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n Site,\n HeaderConfig,\n HeaderNavigationItem,\n HeaderNavigationItemButtonConfig,\n HeaderNavigationItemImageConfig,\n HeaderNavigationItemLinkConfig,\n HeaderNavigationItemMarkdownConfig,\n} from \"@otl-core/cms-types\";\nimport Link from \"next/link\";\nimport React from \"react\";\nimport { useNavigation } from \"../../context/navigation-context\";\nimport {\n getLocalizedString,\n getVisibilityClass,\n parseMarkdownToHTML,\n} from \"../../lib/navigation.utils\";\nimport { ChevronIcon } from \"../icons/chevron-icon\";\nimport { Logo } from \"../items/logo\";\nimport { Button } from \"../ui/button\";\n\ninterface NavigationItemProps {\n item: HeaderNavigationItem;\n navigation?: HeaderConfig;\n resolvedColors: Record<string, string | undefined>;\n itemsShowClass?: string;\n site: Site;\n}\n\n// Map button variants to Button component variants\nconst variantMap: Record<\n string,\n \"default\" | \"secondary\" | \"outline\" | \"ghost\"\n> = {\n primary: \"default\",\n secondary: \"secondary\",\n outline: \"outline\",\n ghost: \"ghost\",\n};\n\n// Map button sizes to Button component sizes\nconst sizeMap: Record<string, \"default\" | \"sm\" | \"lg\"> = {\n sm: \"sm\",\n md: \"default\",\n lg: \"lg\",\n};\n\nexport const NavigationItem: React.FC<NavigationItemProps> = ({\n item,\n navigation,\n resolvedColors,\n itemsShowClass,\n site,\n}) => {\n const label = getLocalizedString(item.label, site);\n\n const visibilityClass = getVisibilityClass(item, itemsShowClass);\n\n // Render logo\n if (item.type === \"logo\") {\n if (!navigation?.logo) return null;\n return (\n <Logo\n navigation={navigation}\n siteName={navigation.logo.alt || \"Logo\"}\n logoTextColor={resolvedColors.logoText}\n />\n );\n }\n\n if (item.type === \"link\") {\n const config = item.config as HeaderNavigationItemLinkConfig;\n const linkStyle: React.CSSProperties = {\n color: resolvedColors.linkColor,\n };\n\n return (\n <Link\n href={config.href}\n style={linkStyle}\n target={config.external ? \"_blank\" : undefined}\n rel={config.external ? \"noopener noreferrer\" : undefined}\n className={`${visibilityClass} items-center whitespace-nowrap`}\n >\n {config.icon && <span>{config.icon}</span>}\n {label}\n </Link>\n );\n }\n\n // Render button — wrapped in a div to avoid CSS specificity conflict\n // between Button's base `inline-flex` and visibility `hidden` classes\n if (item.type === \"button\") {\n const config = item.config as HeaderNavigationItemButtonConfig;\n\n return (\n <div className={`${visibilityClass} items-center`}>\n <Button\n asChild\n variant={config.variant ? variantMap[config.variant] : \"default\"}\n size={config.size ? sizeMap[config.size] : \"default\"}\n className=\"whitespace-nowrap\"\n style={{\n fontSize: `var(--btn-font-${config.size || \"md\"})`,\n }}\n >\n <Link\n href={config.href}\n target={config.external ? \"_blank\" : undefined}\n rel={config.external ? \"noopener noreferrer\" : undefined}\n >\n {config.icon && <span>{config.icon}</span>}\n {label}\n </Link>\n </Button>\n </div>\n );\n }\n\n // Render dropdown trigger\n if (item.type === \"dropdown\") {\n const { toggleDropdown, activeDropdown } = useNavigation();\n const isOpen = activeDropdown === item.id;\n\n const linkStyle: React.CSSProperties = {\n color: resolvedColors.linkColor,\n };\n\n return (\n <button\n onClick={() => toggleDropdown(item.id)}\n style={linkStyle}\n aria-expanded={isOpen}\n aria-haspopup=\"true\"\n className={`${visibilityClass} items-center gap-1 whitespace-nowrap`}\n >\n {label}\n <ChevronIcon isOpen={isOpen} size={16} />\n </button>\n );\n }\n\n // Render markdown\n if (item.type === \"markdown\") {\n const config = item.config as HeaderNavigationItemMarkdownConfig;\n const content = getLocalizedString(config.content, site);\n const html = parseMarkdownToHTML(content);\n\n const textStyle: React.CSSProperties = {};\n if (config.color) {\n if (typeof config.color === \"string\") {\n textStyle.color = config.color;\n } else if (config.color.type === \"custom\" && config.color.value) {\n textStyle.color =\n typeof config.color.value === \"string\"\n ? config.color.value\n : config.color.value.background;\n } else if (config.color.type === \"variable\" && config.color.value) {\n textStyle.color = `var(${config.color.value})`;\n } else if (config.color.type === \"theme\" && config.color.value) {\n textStyle.color = `var(--${config.color.value})`;\n }\n } else {\n textStyle.color = resolvedColors.text;\n }\n\n return (\n <div\n dangerouslySetInnerHTML={{ __html: html }}\n className={`${visibilityClass} items-center whitespace-nowrap [&_*]:text-inherit`}\n style={textStyle}\n />\n );\n }\n\n // Render image\n if (item.type === \"image\") {\n const config = item.config as HeaderNavigationItemImageConfig;\n if (!config.src) return null;\n\n const imgElement = (\n <img\n src={config.src}\n alt={config.alt || \"\"}\n style={{\n display: \"block\",\n width: config.width || \"auto\",\n height: config.height || \"auto\",\n objectFit: config.objectFit || \"contain\",\n }}\n />\n );\n\n const wrapped = config.href ? (\n <Link\n href={config.href}\n target={config.external ? \"_blank\" : undefined}\n rel={config.external ? \"noopener noreferrer\" : undefined}\n >\n {imgElement}\n </Link>\n ) : (\n imgElement\n );\n\n return <div className={`${visibilityClass} items-center`}>{wrapped}</div>;\n }\n\n return null;\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,14 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as class_variance_authority_types from 'class-variance-authority/types';
3
+ import * as React from 'react';
4
+ import { VariantProps } from 'class-variance-authority';
5
+
6
+ declare const buttonVariants: (props?: ({
7
+ variant?: "link" | "default" | "secondary" | "outline" | "ghost" | "destructive" | null | undefined;
8
+ size?: "default" | "sm" | "lg" | "icon" | null | undefined;
9
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
10
+ declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
11
+ asChild?: boolean;
12
+ }): react_jsx_runtime.JSX.Element;
13
+
14
+ export { Button, buttonVariants };
@@ -0,0 +1,51 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cva } from "class-variance-authority";
4
+ import { cn } from "@otl-core/style-utils";
5
+ const buttonVariants = cva(
6
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
11
+ destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
12
+ outline: "border bg-surface shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-gray-200 dark:hover:bg-input/50",
13
+ secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
14
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
15
+ link: "text-primary underline-offset-4 hover:underline"
16
+ },
17
+ size: {
18
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
19
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
20
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
21
+ icon: "size-9"
22
+ }
23
+ },
24
+ defaultVariants: {
25
+ variant: "default",
26
+ size: "default"
27
+ }
28
+ }
29
+ );
30
+ function Button({
31
+ className,
32
+ variant,
33
+ size,
34
+ asChild = false,
35
+ ...props
36
+ }) {
37
+ const Comp = asChild ? Slot : "button";
38
+ return /* @__PURE__ */ jsx(
39
+ Comp,
40
+ {
41
+ "data-slot": "button",
42
+ className: cn(buttonVariants({ variant, size, className })),
43
+ ...props
44
+ }
45
+ );
46
+ }
47
+ export {
48
+ Button,
49
+ buttonVariants
50
+ };
51
+ //# sourceMappingURL=button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/ui/button.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@otl-core/style-utils\";\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n outline:\n \"border bg-surface shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-gray-200 dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nfunction Button({\n className,\n variant,\n size,\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { Button, buttonVariants };\n"],"mappings":"AAkDI;AAjDJ,SAAS,YAAY;AACrB,SAAS,WAA8B;AAEvC,SAAS,UAAU;AAEnB,MAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SACE;AAAA,QACF,aACE;AAAA,QACF,SACE;AAAA,QACF,WACE;AAAA,QACF,OACE;AAAA,QACF,MAAM;AAAA,MACR;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,SAAS,OAAO;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,GAAG;AACL,GAGK;AACH,QAAM,OAAO,UAAU,OAAO;AAE9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,eAAe,EAAE,SAAS,MAAM,UAAU,CAAC,CAAC;AAAA,MACzD,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
@@ -0,0 +1,17 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React__default from 'react';
3
+
4
+ interface NavigationContextType {
5
+ headerRef: React__default.RefObject<HTMLElement | null>;
6
+ isDropdownOpen: boolean;
7
+ activeDropdown: string | null;
8
+ openDropdown: (dropdownId: string) => void;
9
+ closeDropdown: () => void;
10
+ toggleDropdown: (dropdownId: string) => void;
11
+ }
12
+ declare function NavigationProvider({ children, }: {
13
+ children: React__default.ReactNode;
14
+ }): react_jsx_runtime.JSX.Element;
15
+ declare function useNavigation(): NavigationContextType;
16
+
17
+ export { NavigationProvider, useNavigation };
@@ -0,0 +1,93 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { usePathname } from "next/navigation";
4
+ import {
5
+ createContext,
6
+ useCallback,
7
+ useContext,
8
+ useEffect,
9
+ useMemo,
10
+ useRef,
11
+ useState
12
+ } from "react";
13
+ const NavigationContext = createContext(
14
+ void 0
15
+ );
16
+ function NavigationProvider({
17
+ children
18
+ }) {
19
+ const [activeDropdown, setActiveDropdown] = useState(null);
20
+ const headerRef = useRef(null);
21
+ const pathname = usePathname();
22
+ const openDropdown = useCallback(
23
+ (dropdownId) => setActiveDropdown(dropdownId),
24
+ []
25
+ );
26
+ const closeDropdown = useCallback(() => setActiveDropdown(null), []);
27
+ const toggleDropdown = useCallback(
28
+ (dropdownId) => setActiveDropdown((prev) => prev === dropdownId ? null : dropdownId),
29
+ []
30
+ );
31
+ const prevPathname = useRef(pathname);
32
+ if (pathname !== prevPathname.current) {
33
+ prevPathname.current = pathname;
34
+ if (activeDropdown) {
35
+ setActiveDropdown(null);
36
+ }
37
+ }
38
+ useEffect(() => {
39
+ const handleEscape = (e) => {
40
+ if (e.key === "Escape" && activeDropdown) {
41
+ closeDropdown();
42
+ }
43
+ };
44
+ document.addEventListener("keydown", handleEscape);
45
+ return () => document.removeEventListener("keydown", handleEscape);
46
+ }, [activeDropdown, closeDropdown]);
47
+ useEffect(() => {
48
+ if (!activeDropdown) return;
49
+ const handleClickOutside = (e) => {
50
+ const target = e.target;
51
+ if (target.closest("[data-navigation-internal]")) {
52
+ return;
53
+ }
54
+ if (!headerRef.current?.contains(target)) {
55
+ closeDropdown();
56
+ }
57
+ };
58
+ document.addEventListener("click", handleClickOutside, true);
59
+ return () => {
60
+ document.removeEventListener("click", handleClickOutside, true);
61
+ };
62
+ }, [activeDropdown, closeDropdown]);
63
+ const isDropdownOpen = useMemo(
64
+ () => activeDropdown !== null,
65
+ [activeDropdown]
66
+ );
67
+ return /* @__PURE__ */ jsx(
68
+ NavigationContext.Provider,
69
+ {
70
+ value: {
71
+ headerRef,
72
+ isDropdownOpen,
73
+ activeDropdown,
74
+ openDropdown,
75
+ closeDropdown,
76
+ toggleDropdown
77
+ },
78
+ children
79
+ }
80
+ );
81
+ }
82
+ function useNavigation() {
83
+ const context = useContext(NavigationContext);
84
+ if (!context) {
85
+ throw new Error("useNavigation must be used within NavigationProvider");
86
+ }
87
+ return context;
88
+ }
89
+ export {
90
+ NavigationProvider,
91
+ useNavigation
92
+ };
93
+ //# sourceMappingURL=navigation-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/context/navigation-context.tsx"],"sourcesContent":["\"use client\";\n\nimport { usePathname } from \"next/navigation\";\nimport React, {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\ninterface NavigationContextType {\n headerRef: React.RefObject<HTMLElement | null>;\n isDropdownOpen: boolean;\n activeDropdown: string | null;\n openDropdown: (dropdownId: string) => void;\n closeDropdown: () => void;\n toggleDropdown: (dropdownId: string) => void;\n}\n\nconst NavigationContext = createContext<NavigationContextType | undefined>(\n undefined,\n);\n\nexport function NavigationProvider({\n children,\n}: {\n children: React.ReactNode;\n}) {\n const [activeDropdown, setActiveDropdown] = useState<string | null>(null);\n const headerRef = useRef<HTMLElement | null>(null);\n const pathname = usePathname();\n\n const openDropdown = useCallback(\n (dropdownId: string) => setActiveDropdown(dropdownId),\n [],\n );\n const closeDropdown = useCallback(() => setActiveDropdown(null), []);\n const toggleDropdown = useCallback(\n (dropdownId: string) =>\n setActiveDropdown((prev) => (prev === dropdownId ? null : dropdownId)),\n [],\n );\n\n // Close dropdown on route change (render-time derivation, not an effect)\n const prevPathname = useRef(pathname);\n if (pathname !== prevPathname.current) {\n prevPathname.current = pathname;\n if (activeDropdown) {\n setActiveDropdown(null);\n }\n }\n\n useEffect(() => {\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && activeDropdown) {\n closeDropdown();\n }\n };\n\n document.addEventListener(\"keydown\", handleEscape);\n return () => document.removeEventListener(\"keydown\", handleEscape);\n }, [activeDropdown, closeDropdown]);\n\n useEffect(() => {\n if (!activeDropdown) return;\n\n const handleClickOutside = (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n\n // Check if click is on an internal navigation element (back button, nested dropdown triggers)\n if (target.closest(\"[data-navigation-internal]\")) {\n return;\n }\n\n // Check if click is outside the header\n if (!headerRef.current?.contains(target)) {\n closeDropdown();\n }\n };\n\n // Use capture phase to handle clicks before they bubble\n document.addEventListener(\"click\", handleClickOutside, true);\n\n return () => {\n document.removeEventListener(\"click\", handleClickOutside, true);\n };\n }, [activeDropdown, closeDropdown]);\n\n const isDropdownOpen = useMemo(\n () => activeDropdown !== null,\n [activeDropdown],\n );\n\n return (\n <NavigationContext.Provider\n value={{\n headerRef,\n isDropdownOpen,\n activeDropdown,\n openDropdown,\n closeDropdown,\n toggleDropdown,\n }}\n >\n {children}\n </NavigationContext.Provider>\n );\n}\n\nexport function useNavigation() {\n const context = useContext(NavigationContext);\n if (!context) {\n throw new Error(\"useNavigation must be used within NavigationProvider\");\n }\n return context;\n}\n"],"mappings":";AAiGI;AA/FJ,SAAS,mBAAmB;AAC5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAWP,MAAM,oBAAoB;AAAA,EACxB;AACF;AAEO,SAAS,mBAAmB;AAAA,EACjC;AACF,GAEG;AACD,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAwB,IAAI;AACxE,QAAM,YAAY,OAA2B,IAAI;AACjD,QAAM,WAAW,YAAY;AAE7B,QAAM,eAAe;AAAA,IACnB,CAAC,eAAuB,kBAAkB,UAAU;AAAA,IACpD,CAAC;AAAA,EACH;AACA,QAAM,gBAAgB,YAAY,MAAM,kBAAkB,IAAI,GAAG,CAAC,CAAC;AACnE,QAAM,iBAAiB;AAAA,IACrB,CAAC,eACC,kBAAkB,CAAC,SAAU,SAAS,aAAa,OAAO,UAAW;AAAA,IACvE,CAAC;AAAA,EACH;AAGA,QAAM,eAAe,OAAO,QAAQ;AACpC,MAAI,aAAa,aAAa,SAAS;AACrC,iBAAa,UAAU;AACvB,QAAI,gBAAgB;AAClB,wBAAkB,IAAI;AAAA,IACxB;AAAA,EACF;AAEA,YAAU,MAAM;AACd,UAAM,eAAe,CAAC,MAAqB;AACzC,UAAI,EAAE,QAAQ,YAAY,gBAAgB;AACxC,sBAAc;AAAA,MAChB;AAAA,IACF;AAEA,aAAS,iBAAiB,WAAW,YAAY;AACjD,WAAO,MAAM,SAAS,oBAAoB,WAAW,YAAY;AAAA,EACnE,GAAG,CAAC,gBAAgB,aAAa,CAAC;AAElC,YAAU,MAAM;AACd,QAAI,CAAC,eAAgB;AAErB,UAAM,qBAAqB,CAAC,MAAkB;AAC5C,YAAM,SAAS,EAAE;AAGjB,UAAI,OAAO,QAAQ,4BAA4B,GAAG;AAChD;AAAA,MACF;AAGA,UAAI,CAAC,UAAU,SAAS,SAAS,MAAM,GAAG;AACxC,sBAAc;AAAA,MAChB;AAAA,IACF;AAGA,aAAS,iBAAiB,SAAS,oBAAoB,IAAI;AAE3D,WAAO,MAAM;AACX,eAAS,oBAAoB,SAAS,oBAAoB,IAAI;AAAA,IAChE;AAAA,EACF,GAAG,CAAC,gBAAgB,aAAa,CAAC;AAElC,QAAM,iBAAiB;AAAA,IACrB,MAAM,mBAAmB;AAAA,IACzB,CAAC,cAAc;AAAA,EACjB;AAEA,SACE;AAAA,IAAC,kBAAkB;AAAA,IAAlB;AAAA,MACC,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAEO,SAAS,gBAAgB;AAC9B,QAAM,UAAU,WAAW,iBAAiB;AAC5C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,sDAAsD;AAAA,EACxE;AACA,SAAO;AACT;","names":[]}