@sofidevo/astro-dynamic-header 3.0.1 → 4.0.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.
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Positioning + interaction logic for 2nd/3rd level submenus.
3
+ * Extracted from `NavMenu.astro` so it can be unit tested.
4
+ */
5
+
6
+ export function adjustSubsubmenuPositionOnResize(
7
+ doc: Document = document,
8
+ win: Window = window,
9
+ ): void {
10
+ const subsubmenus = doc.querySelectorAll<HTMLElement>(".subsubmenu");
11
+
12
+ subsubmenus.forEach((subsubmenu) => {
13
+ const rect = subsubmenu.getBoundingClientRect();
14
+ const viewportWidth = win.innerWidth;
15
+
16
+ if (rect.right > viewportWidth) {
17
+ subsubmenu.style.right = "100%";
18
+ subsubmenu.style.left = "0";
19
+ } else {
20
+ subsubmenu.style.left = "100%";
21
+ subsubmenu.style.right = "0";
22
+ }
23
+ });
24
+ }
25
+
26
+ export function handleSubsubmenuInteraction(doc: Document = document): void {
27
+ const parentItems = doc.querySelectorAll(".submenu__item--secondary");
28
+
29
+ parentItems.forEach((parentItem) => {
30
+ const subsubmenu = parentItem.querySelector<HTMLElement>(".subsubmenu");
31
+ if (!subsubmenu) return;
32
+
33
+ const adjustPosition = () => {
34
+ const viewportWidth = window.innerWidth;
35
+
36
+ const originalDisplay = subsubmenu.style.display;
37
+ const originalVisibility = subsubmenu.style.visibility;
38
+
39
+ subsubmenu.style.display = "block";
40
+ subsubmenu.style.visibility = "hidden";
41
+ subsubmenu.style.left = "100%";
42
+ subsubmenu.style.transform = "auto";
43
+
44
+ const rect = subsubmenu.getBoundingClientRect();
45
+
46
+ if (rect.right > viewportWidth) {
47
+ subsubmenu.style.left = "-100%";
48
+ subsubmenu.style.right = "auto";
49
+ const leftRect = subsubmenu.getBoundingClientRect();
50
+
51
+ if (leftRect.left < 0) {
52
+ subsubmenu.style.left = "100%";
53
+ const overflow = rect.right - viewportWidth + 30;
54
+ subsubmenu.style.transform = `translateX(-${overflow}px)`;
55
+ }
56
+ }
57
+ subsubmenu.style.display = originalDisplay;
58
+ subsubmenu.style.visibility = originalVisibility;
59
+ };
60
+
61
+ const showSubsubmenu = () => {
62
+ adjustPosition();
63
+
64
+ subsubmenu.style.opacity = "1";
65
+ subsubmenu.style.pointerEvents = "all";
66
+
67
+ const isPositionedLeft = subsubmenu.style.left === "-100%";
68
+ const transformDirection = isPositionedLeft ? "-32px" : "32px";
69
+
70
+ const currentTransform = subsubmenu.style.transform;
71
+ if (currentTransform && currentTransform.includes("translateX")) {
72
+ const existingTranslate = currentTransform.match(
73
+ /translateX\(([^)]+)\)/,
74
+ );
75
+ if (existingTranslate) {
76
+ const existingValue = existingTranslate[1];
77
+ subsubmenu.style.transform =
78
+ `translateX(calc(${existingValue} + ${transformDirection}))`;
79
+ }
80
+ } else {
81
+ subsubmenu.style.transform =
82
+ `${currentTransform} translateX(${transformDirection})`.trim();
83
+ }
84
+ };
85
+
86
+ const hideSubsubmenu = () => {
87
+ subsubmenu.style.opacity = "";
88
+ subsubmenu.style.pointerEvents = "";
89
+ subsubmenu.style.transform = "";
90
+ };
91
+
92
+ parentItem.addEventListener("mouseenter", showSubsubmenu);
93
+ parentItem.addEventListener("mouseleave", hideSubsubmenu);
94
+ });
95
+ }
96
+
97
+ /**
98
+ * Wires submenu behaviour as soon as the DOM is ready (module scripts may run
99
+ * before or after `DOMContentLoaded`, so both paths are handled).
100
+ */
101
+ export function initNavMenu(
102
+ doc: Document = document,
103
+ win: Window = window,
104
+ ): void {
105
+ win.addEventListener("resize", () =>
106
+ adjustSubsubmenuPositionOnResize(doc, win),
107
+ );
108
+
109
+ const start = () => {
110
+ adjustSubsubmenuPositionOnResize(doc, win);
111
+ handleSubsubmenuInteraction(doc);
112
+ };
113
+
114
+ if (doc.readyState === "loading") {
115
+ doc.addEventListener("DOMContentLoaded", start, { once: true });
116
+ } else {
117
+ start();
118
+ }
119
+ }