@stackshift-ui/navigation 6.0.13 → 6.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-4N6ET2I6.mjs +1 -0
- package/dist/{chunk-YNDJK4RH.mjs → chunk-67XVPRGX.mjs} +1 -1
- package/dist/chunk-6QSBJPEW.mjs +1 -0
- package/dist/{chunk-6XDFQS4J.mjs → chunk-EASZP4S2.mjs} +1 -1
- package/dist/{chunk-DJV2UTMZ.mjs → chunk-LBUYM4NV.mjs} +1 -1
- package/dist/chunk-MSVUIX4E.mjs +1 -0
- package/dist/chunk-OXF5VEOU.mjs +1 -0
- package/dist/chunk-P3TZNIE6.mjs +1 -0
- package/dist/chunk-PCTXJCVP.mjs +1 -0
- package/dist/{chunk-KB2HS5UA.mjs → chunk-SVLHEWIY.mjs} +1 -1
- package/dist/chunk-V7KKZJMX.mjs +1 -0
- package/dist/context/megaNavContext.d.ts +14 -0
- package/dist/context/megaNavContext.mjs +1 -0
- package/dist/helper/index.d.ts +2 -1
- package/dist/helper/index.mjs +1 -1
- package/dist/helper.d.ts +6 -0
- package/dist/helper.mjs +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +1 -1
- package/dist/navigation.d.ts +9 -1
- package/dist/navigation.mjs +1 -1
- package/dist/navigation_a.mjs +1 -1
- package/dist/navigation_b.mjs +1 -1
- package/dist/navigation_c.mjs +1 -1
- package/dist/navigation_d.mjs +1 -1
- package/dist/navigation_e.mjs +1 -1
- package/dist/navigation_f.d.ts +3 -0
- package/dist/navigation_f.mjs +1 -0
- package/dist/navigation_g.d.ts +3 -0
- package/dist/navigation_g.mjs +1 -0
- package/dist/navigation_h.d.ts +3 -0
- package/dist/navigation_h.mjs +1 -0
- package/dist/types.d.ts +104 -1
- package/package.json +14 -6
- package/src/context/megaNavContext.tsx +78 -0
- package/src/helper/index.tsx +130 -0
- package/src/helper.ts +14 -0
- package/src/index.ts +3 -0
- package/src/navigation.tsx +29 -1
- package/src/navigation_f.tsx +531 -0
- package/src/navigation_g.tsx +607 -0
- package/src/navigation_h.tsx +742 -0
- package/src/types.ts +113 -1
- package/dist/chunk-KQFO7OZP.mjs +0 -1
- package/dist/chunk-TTJMPYYC.mjs +0 -1
- package/dist/helper/blockStyle.js +0 -1
- package/dist/helper/index.js +0 -1
- package/dist/index.js +0 -2
- package/dist/navigation.js +0 -1
- package/dist/navigation_a.js +0 -1
- package/dist/navigation_b.js +0 -1
- package/dist/navigation_c.js +0 -1
- package/dist/navigation_d.js +0 -1
- package/dist/navigation_e.js +0 -1
- package/dist/types.js +0 -1
- package/src/helper/index.ts +0 -14
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
import { Container } from "@stackshift-ui/container";
|
|
2
|
+
import { Flex } from "@stackshift-ui/flex";
|
|
3
|
+
import { Image } from "@stackshift-ui/image";
|
|
4
|
+
import { Link } from "@stackshift-ui/link";
|
|
5
|
+
import { Section } from "@stackshift-ui/section";
|
|
6
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
7
|
+
import { BiArrowBack, BiChevronDown, BiMenu } from "react-icons/bi";
|
|
8
|
+
import { BsFillTelephoneFill, BsTelephone } from "react-icons/bs";
|
|
9
|
+
import { MdClose } from "react-icons/md";
|
|
10
|
+
import { NavigationProps } from ".";
|
|
11
|
+
import { ConditionalLink, logoLink } from "./helper/index";
|
|
12
|
+
import { LabeledRouteWithKey } from "./types";
|
|
13
|
+
|
|
14
|
+
// Types
|
|
15
|
+
type DropdownRef = React.RefObject<HTMLDivElement>;
|
|
16
|
+
|
|
17
|
+
interface DropdownState {
|
|
18
|
+
previous: number | null;
|
|
19
|
+
index: number | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Components
|
|
23
|
+
const NavigationLogo = ({ logo, hasScrolled }: { logo: any; hasScrolled: boolean }) => {
|
|
24
|
+
if (!logo?.image) return null;
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Link
|
|
28
|
+
aria-label={`Go to ${logoLink(logo) === "/" ? "home page" : logoLink(logo)}`}
|
|
29
|
+
className="text-3xl"
|
|
30
|
+
href={logoLink(logo)}
|
|
31
|
+
target={logo?.linkTarget}
|
|
32
|
+
rel={logo?.linkTarget === "_blank" ? "noopener noreferrer" : ""}>
|
|
33
|
+
<Image
|
|
34
|
+
src={
|
|
35
|
+
hasScrolled
|
|
36
|
+
? logo?.image
|
|
37
|
+
: "https://cdn.sanity.io/images/9itgab5x/staging/7f9353c628ae4dd0bdd479d3b1407a3c242755e8-1963x833.png?fm=webp&w=96&q=75"
|
|
38
|
+
}
|
|
39
|
+
width={500}
|
|
40
|
+
height={500}
|
|
41
|
+
alt={logo?.alt ?? "navigation-logo"}
|
|
42
|
+
className={`transition-all duration-500 w-28 ${hasScrolled ? "md:w-40" : "md:w-44"}`}
|
|
43
|
+
/>
|
|
44
|
+
</Link>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const NavigationButton = ({ button, hasScrolled }: { button: any; hasScrolled: boolean }) => {
|
|
49
|
+
if (!button?.label) return null;
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<ConditionalLink
|
|
53
|
+
ariaLabel={button?.label}
|
|
54
|
+
link={button}
|
|
55
|
+
target={button?.linkTarget}
|
|
56
|
+
className={`font-body ml-auto p-2.5 md:p-3 transition duration-200 font-medium flex items-center space-x-2 text-left ${
|
|
57
|
+
hasScrolled
|
|
58
|
+
? "text-white border border-white bg-transparent hover:bg-white hover:text-black"
|
|
59
|
+
: "text-white bg-secondary hover:text-secondary hover:border hover:border-black/40 hover:bg-white"
|
|
60
|
+
}`}>
|
|
61
|
+
<span className="flex-none text-3xl sm:text-xl">
|
|
62
|
+
{hasScrolled ? (
|
|
63
|
+
<BsTelephone className="text-lg md:text-xl" />
|
|
64
|
+
) : (
|
|
65
|
+
<BsFillTelephoneFill className="text-lg md:text-xl" />
|
|
66
|
+
)}
|
|
67
|
+
</span>
|
|
68
|
+
<span className="text-sm md:text-base sm:block hidden whitespace-nowrap">
|
|
69
|
+
{button?.label}
|
|
70
|
+
</span>
|
|
71
|
+
</ConditionalLink>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const MobileMenuButton = ({
|
|
76
|
+
hasScrolled,
|
|
77
|
+
menu,
|
|
78
|
+
showMenu,
|
|
79
|
+
}: {
|
|
80
|
+
hasScrolled: boolean;
|
|
81
|
+
menu: boolean;
|
|
82
|
+
showMenu: (e: React.MouseEvent) => void;
|
|
83
|
+
}) => (
|
|
84
|
+
<div>
|
|
85
|
+
<BiMenu
|
|
86
|
+
color={hasScrolled ? "white" : "black"}
|
|
87
|
+
size={40}
|
|
88
|
+
onClick={showMenu}
|
|
89
|
+
className={`cursor-pointer hover:scale-105 transition lg:hidden block ${
|
|
90
|
+
menu ? "hidden" : "block"
|
|
91
|
+
}`}
|
|
92
|
+
/>
|
|
93
|
+
<MdClose
|
|
94
|
+
color={hasScrolled ? "white" : "black"}
|
|
95
|
+
size={40}
|
|
96
|
+
onClick={showMenu}
|
|
97
|
+
className={`cursor-pointer hover:scale-105 transition lg:hidden block ${
|
|
98
|
+
menu ? "block" : "hidden"
|
|
99
|
+
}`}
|
|
100
|
+
/>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
export default function Navigation_F({
|
|
105
|
+
primaryButton,
|
|
106
|
+
secondaryButton,
|
|
107
|
+
logo,
|
|
108
|
+
multipleLinks,
|
|
109
|
+
title,
|
|
110
|
+
}: NavigationProps) {
|
|
111
|
+
const [dropdown, setDropdown] = useState<number | null>(null);
|
|
112
|
+
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
113
|
+
const [menu, setMenu] = useState(false);
|
|
114
|
+
const [hasScrolled, setHasScrolled] = useState(false);
|
|
115
|
+
|
|
116
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
117
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
|
118
|
+
setDropdown(null);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
const handleDocumentClick = (event: MouseEvent) => {
|
|
124
|
+
handleClickOutside(event);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
document.addEventListener("click", handleDocumentClick);
|
|
128
|
+
return () => {
|
|
129
|
+
document.removeEventListener("click", handleDocumentClick);
|
|
130
|
+
};
|
|
131
|
+
}, []);
|
|
132
|
+
|
|
133
|
+
const showMenu = (e: React.MouseEvent) => {
|
|
134
|
+
if (e) {
|
|
135
|
+
e.stopPropagation();
|
|
136
|
+
}
|
|
137
|
+
setMenu(prevState => !prevState);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
const handleScroll = () => {
|
|
142
|
+
setHasScrolled(window.scrollY > 0);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
window.addEventListener("scroll", handleScroll);
|
|
146
|
+
return () => window.removeEventListener("scroll", handleScroll);
|
|
147
|
+
}, []);
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<>
|
|
151
|
+
<Section
|
|
152
|
+
className={`fixed !z-[999999] w-full left-0 transition-all top-0 ${
|
|
153
|
+
hasScrolled ? "bg-black/80" : "bg-background"
|
|
154
|
+
}`}>
|
|
155
|
+
<Container>
|
|
156
|
+
<div className="flex justify-between space-x-5 py-2 items-center">
|
|
157
|
+
<NavigationLogo logo={logo} hasScrolled={hasScrolled} />
|
|
158
|
+
|
|
159
|
+
<Flex direction="row" align="center" className="hidden lg:flex gap-x-8">
|
|
160
|
+
{multipleLinks?.map((link: LabeledRouteWithKey, index: number) => (
|
|
161
|
+
<NavigationLink
|
|
162
|
+
key={index}
|
|
163
|
+
link={link}
|
|
164
|
+
index={index}
|
|
165
|
+
dropdown={dropdown}
|
|
166
|
+
setDropdown={setDropdown}
|
|
167
|
+
hasScrolled={hasScrolled}
|
|
168
|
+
dropdownRef={dropdownRef}
|
|
169
|
+
/>
|
|
170
|
+
))}
|
|
171
|
+
</Flex>
|
|
172
|
+
|
|
173
|
+
<Flex direction="row" align="center" gap={2}>
|
|
174
|
+
{(primaryButton?.label || secondaryButton?.label) && (
|
|
175
|
+
<div className="flex space-x-4 sm:space-x-6 items-center">
|
|
176
|
+
<NavigationButton button={primaryButton} hasScrolled={hasScrolled} />
|
|
177
|
+
</div>
|
|
178
|
+
)}
|
|
179
|
+
<MobileMenuButton hasScrolled={hasScrolled} menu={menu} showMenu={showMenu} />
|
|
180
|
+
</Flex>
|
|
181
|
+
</div>
|
|
182
|
+
</Container>
|
|
183
|
+
<Drawer links={multipleLinks} open={menu} showMenu={showMenu} />
|
|
184
|
+
</Section>
|
|
185
|
+
</>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const NavigationLink = ({
|
|
190
|
+
link,
|
|
191
|
+
index,
|
|
192
|
+
dropdown,
|
|
193
|
+
setDropdown,
|
|
194
|
+
hasScrolled,
|
|
195
|
+
dropdownRef,
|
|
196
|
+
}: {
|
|
197
|
+
link: LabeledRouteWithKey;
|
|
198
|
+
index: number;
|
|
199
|
+
dropdown: number | null;
|
|
200
|
+
setDropdown: (value: number | null) => void;
|
|
201
|
+
hasScrolled: boolean;
|
|
202
|
+
dropdownRef: DropdownRef;
|
|
203
|
+
}) => {
|
|
204
|
+
if (!link?.multipleRoutes) {
|
|
205
|
+
return (
|
|
206
|
+
<ConditionalLink
|
|
207
|
+
ariaLabel={link?.label || ""}
|
|
208
|
+
link={link}
|
|
209
|
+
target={link?.linkTarget}
|
|
210
|
+
className={`hover:text-secondary font-body text-lg whitespace-nowrap ${
|
|
211
|
+
hasScrolled ? "text-white" : "text-black"
|
|
212
|
+
}`}>
|
|
213
|
+
{link?.label}
|
|
214
|
+
</ConditionalLink>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const handleDropdownChange = (newIndex: number | null) => {
|
|
219
|
+
setDropdown(newIndex);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<div className="relative">
|
|
224
|
+
{link?.internalLink || link?.externalLink ? (
|
|
225
|
+
<Flex
|
|
226
|
+
onMouseEnter={(e: React.MouseEvent) => {
|
|
227
|
+
e.stopPropagation();
|
|
228
|
+
handleDropdownChange(dropdown === index ? null : index);
|
|
229
|
+
}}>
|
|
230
|
+
<ConditionalLink
|
|
231
|
+
ariaLabel={link?.label || ""}
|
|
232
|
+
link={link}
|
|
233
|
+
target={link?.linkTarget}
|
|
234
|
+
className={`hover:text-secondary font-body text-lg cursor-pointer ${
|
|
235
|
+
hasScrolled ? "text-white" : "text-black"
|
|
236
|
+
}`}>
|
|
237
|
+
{link?.label}
|
|
238
|
+
</ConditionalLink>
|
|
239
|
+
|
|
240
|
+
<BiChevronDown
|
|
241
|
+
size={24}
|
|
242
|
+
className={`font-bold mt-0.5 font-body cursor-pointer hover:text-secondary hover:translate-y-0.5 transition ${
|
|
243
|
+
hasScrolled ? "text-white" : "text-black"
|
|
244
|
+
} ${dropdown === index && "translate-y-0.5 !text-secondary"}`}
|
|
245
|
+
onClick={(e: React.MouseEvent) => {
|
|
246
|
+
e.stopPropagation();
|
|
247
|
+
handleDropdownChange(index);
|
|
248
|
+
}}
|
|
249
|
+
/>
|
|
250
|
+
</Flex>
|
|
251
|
+
) : (
|
|
252
|
+
<p
|
|
253
|
+
className={`hover:text-secondary font-body text-lg cursor-pointer flex items-center group ${
|
|
254
|
+
hasScrolled ? "text-white" : "text-black"
|
|
255
|
+
}`}
|
|
256
|
+
onClick={(e: React.MouseEvent) => {
|
|
257
|
+
e.stopPropagation();
|
|
258
|
+
handleDropdownChange(dropdown === index ? null : index);
|
|
259
|
+
}}
|
|
260
|
+
onMouseEnter={(e: React.MouseEvent) => {
|
|
261
|
+
e.stopPropagation();
|
|
262
|
+
handleDropdownChange(dropdown === index ? null : index);
|
|
263
|
+
}}>
|
|
264
|
+
{link?.label}
|
|
265
|
+
<BiChevronDown
|
|
266
|
+
size={24}
|
|
267
|
+
className={`font-bold mt-0.5 group-hover:translate-y-0.5 transition ${
|
|
268
|
+
dropdown === index && "translate-y-0.5 text-secondary"
|
|
269
|
+
}`}
|
|
270
|
+
/>
|
|
271
|
+
</p>
|
|
272
|
+
)}
|
|
273
|
+
|
|
274
|
+
{dropdown === index && (
|
|
275
|
+
<DropdownMenu
|
|
276
|
+
link={link}
|
|
277
|
+
dropdownRef={dropdownRef}
|
|
278
|
+
setDropdown={setDropdown}
|
|
279
|
+
hasScrolled={hasScrolled}
|
|
280
|
+
/>
|
|
281
|
+
)}
|
|
282
|
+
</div>
|
|
283
|
+
);
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const DropdownMenu = ({
|
|
287
|
+
link,
|
|
288
|
+
dropdownRef,
|
|
289
|
+
setDropdown,
|
|
290
|
+
hasScrolled,
|
|
291
|
+
}: {
|
|
292
|
+
link: LabeledRouteWithKey;
|
|
293
|
+
dropdownRef: DropdownRef;
|
|
294
|
+
setDropdown: (value: number | null) => void;
|
|
295
|
+
hasScrolled: boolean;
|
|
296
|
+
}) => (
|
|
297
|
+
<div
|
|
298
|
+
ref={dropdownRef}
|
|
299
|
+
className="absolute flex flex-col top-8 bg-primary rounded rounded-tl-none"
|
|
300
|
+
onMouseLeave={() => setDropdown(null)}>
|
|
301
|
+
<span className="absolute top-0 w-0 h-0 border-x-transparent border-t-transparent border-b-primary border-[6px] transform -translate-y-3" />
|
|
302
|
+
{link?.multipleRoutes?.map((mLink, idx) => (
|
|
303
|
+
<DropdownMenuItem key={idx} mLink={mLink} hasScrolled={hasScrolled} />
|
|
304
|
+
))}
|
|
305
|
+
</div>
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
const DropdownMenuItem = ({
|
|
309
|
+
mLink,
|
|
310
|
+
hasScrolled,
|
|
311
|
+
}: {
|
|
312
|
+
mLink: LabeledRouteWithKey;
|
|
313
|
+
hasScrolled: boolean;
|
|
314
|
+
}) => {
|
|
315
|
+
if (mLink?.multipleInnerRoutes) {
|
|
316
|
+
return (
|
|
317
|
+
<div className="relative group">
|
|
318
|
+
{mLink?.internalLink || mLink?.externalLink ? (
|
|
319
|
+
<Flex align="center">
|
|
320
|
+
<ConditionalLink
|
|
321
|
+
ariaLabel={mLink?.label || ""}
|
|
322
|
+
link={mLink}
|
|
323
|
+
target={mLink?.linkTarget}
|
|
324
|
+
className="font-body hover:text-secondary text-lg cursor-pointer py-2 pl-4 text-white">
|
|
325
|
+
{mLink?.label}
|
|
326
|
+
</ConditionalLink>
|
|
327
|
+
<BiChevronDown
|
|
328
|
+
size={24}
|
|
329
|
+
className="font-bold mt-0.5 cursor-pointer group-hover:translate-y-0.5 transition group-hover:text-secondary text-white"
|
|
330
|
+
/>
|
|
331
|
+
</Flex>
|
|
332
|
+
) : (
|
|
333
|
+
<p
|
|
334
|
+
className={`hover:text-secondary text-lg cursor-pointer flex items-center group py-2 px-4 ${
|
|
335
|
+
hasScrolled ? "text-white" : "text-black"
|
|
336
|
+
}`}>
|
|
337
|
+
{mLink?.label}
|
|
338
|
+
<BiChevronDown
|
|
339
|
+
size={24}
|
|
340
|
+
className="font-bold mt-0.5 group-hover:translate-y-0.5 transition"
|
|
341
|
+
/>
|
|
342
|
+
</p>
|
|
343
|
+
)}
|
|
344
|
+
<div className="absolute hidden flex-col top-0 left-full bg-primary rounded-r rounded-r-b group-hover:flex hover:flex">
|
|
345
|
+
{mLink?.multipleInnerRoutes?.map((innerLink: LabeledRouteWithKey, idx: number) => (
|
|
346
|
+
<ConditionalLink
|
|
347
|
+
key={idx}
|
|
348
|
+
ariaLabel={innerLink?.label || ""}
|
|
349
|
+
link={innerLink}
|
|
350
|
+
target={innerLink?.linkTarget}
|
|
351
|
+
className="hover:text-secondary font-body text-lg py-2 px-4 whitespace-nowrap text-white">
|
|
352
|
+
{innerLink?.label}
|
|
353
|
+
</ConditionalLink>
|
|
354
|
+
))}
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return (
|
|
361
|
+
<ConditionalLink
|
|
362
|
+
ariaLabel={mLink?.label || ""}
|
|
363
|
+
link={mLink}
|
|
364
|
+
target={mLink?.linkTarget}
|
|
365
|
+
className="hover:text-secondary font-body text-lg py-2 px-4 whitespace-nowrap text-white">
|
|
366
|
+
{mLink?.label}
|
|
367
|
+
</ConditionalLink>
|
|
368
|
+
);
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
const Drawer = ({
|
|
372
|
+
links,
|
|
373
|
+
open,
|
|
374
|
+
showMenu,
|
|
375
|
+
}: {
|
|
376
|
+
links: LabeledRouteWithKey[];
|
|
377
|
+
open: boolean;
|
|
378
|
+
showMenu: (e: React.MouseEvent) => void;
|
|
379
|
+
}) => {
|
|
380
|
+
const [hasScrolled, setHasScrolled] = useState(false);
|
|
381
|
+
const drawerRef = useRef<HTMLDivElement>(null);
|
|
382
|
+
|
|
383
|
+
useEffect(() => {
|
|
384
|
+
const handleScroll = () => setHasScrolled(window.scrollY > 0);
|
|
385
|
+
window.addEventListener("scroll", handleScroll);
|
|
386
|
+
return () => window.removeEventListener("scroll", handleScroll);
|
|
387
|
+
}, []);
|
|
388
|
+
|
|
389
|
+
useEffect(() => {
|
|
390
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
391
|
+
if (
|
|
392
|
+
drawerRef.current &&
|
|
393
|
+
!drawerRef.current.contains(event.target as Node) &&
|
|
394
|
+
!(event.target as Element).closest(".BiMenu")
|
|
395
|
+
) {
|
|
396
|
+
if (open) {
|
|
397
|
+
showMenu(event as unknown as React.MouseEvent);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
document.addEventListener("click", handleClickOutside);
|
|
403
|
+
return () => document.removeEventListener("click", handleClickOutside);
|
|
404
|
+
}, [open, showMenu]);
|
|
405
|
+
|
|
406
|
+
const [showInnerRoutes, setShowInnerRoutes] = useState<DropdownState>({
|
|
407
|
+
previous: null,
|
|
408
|
+
index: null,
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
const handleRouteClick = (previous: number | null, index: number | null) => {
|
|
412
|
+
setShowInnerRoutes({ previous, index });
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
const renderLink = (
|
|
416
|
+
link: LabeledRouteWithKey,
|
|
417
|
+
idx: number,
|
|
418
|
+
hasChevron = false,
|
|
419
|
+
onClick?: () => void,
|
|
420
|
+
) => {
|
|
421
|
+
const isNewConstruction = link?.label === "New Construction";
|
|
422
|
+
const isRenovation = link?.label === "Renovations" || link?.label === "Renovation";
|
|
423
|
+
|
|
424
|
+
return (
|
|
425
|
+
<div className="flex items-center py-2 px-4" key={idx}>
|
|
426
|
+
{link?.internalLink || link?.externalLink ? (
|
|
427
|
+
isNewConstruction || isRenovation ? (
|
|
428
|
+
<p
|
|
429
|
+
className={`text-xl font-body ${
|
|
430
|
+
hasScrolled ? "text-white" : "text-black"
|
|
431
|
+
} hover:text-secondary cursor-pointer`}
|
|
432
|
+
onClick={() =>
|
|
433
|
+
(window.location.href = `/projects/residential?filter=${
|
|
434
|
+
isNewConstruction ? "new_construction" : "renovations"
|
|
435
|
+
}`)
|
|
436
|
+
}>
|
|
437
|
+
{link?.label}
|
|
438
|
+
</p>
|
|
439
|
+
) : (
|
|
440
|
+
<ConditionalLink
|
|
441
|
+
ariaLabel={link?.label || ""}
|
|
442
|
+
link={link}
|
|
443
|
+
target={link?.linkTarget}
|
|
444
|
+
className={`text-xl font-body ${
|
|
445
|
+
hasScrolled ? "text-white" : "text-black"
|
|
446
|
+
} hover:text-secondary`}>
|
|
447
|
+
<p className="w-full h-full" onClick={showMenu}>
|
|
448
|
+
{link?.label}
|
|
449
|
+
</p>
|
|
450
|
+
</ConditionalLink>
|
|
451
|
+
)
|
|
452
|
+
) : (
|
|
453
|
+
<p
|
|
454
|
+
className={`text-xl font-body peer cursor-pointer ${
|
|
455
|
+
hasScrolled ? "text-white" : "text-black"
|
|
456
|
+
} hover:text-secondary`}
|
|
457
|
+
onClick={onClick}>
|
|
458
|
+
{link?.label}
|
|
459
|
+
</p>
|
|
460
|
+
)}
|
|
461
|
+
{hasChevron && (
|
|
462
|
+
<BiChevronDown
|
|
463
|
+
fontSize={24}
|
|
464
|
+
className={`hover:-rotate-90 hover:text-secondary peer-hover:-rotate-90 peer-hover:text-secondary transition duration-200 cursor-pointer ${
|
|
465
|
+
hasScrolled ? "text-white" : "text-black"
|
|
466
|
+
}`}
|
|
467
|
+
onClick={onClick}
|
|
468
|
+
/>
|
|
469
|
+
)}
|
|
470
|
+
</div>
|
|
471
|
+
);
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
const renderLinks = (linksArray: LabeledRouteWithKey[], isInnerRoute = false) =>
|
|
475
|
+
linksArray.map((link, idx) => {
|
|
476
|
+
const isDropdown = Boolean(link?.multipleRoutes || link?.multipleInnerRoutes);
|
|
477
|
+
const onClick = isDropdown
|
|
478
|
+
? () => handleRouteClick(isInnerRoute ? showInnerRoutes.index : null, idx)
|
|
479
|
+
: undefined;
|
|
480
|
+
|
|
481
|
+
return renderLink(link, idx, isDropdown, onClick);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
const renderBackButton = (onClick: () => void) => (
|
|
485
|
+
<p
|
|
486
|
+
className={`hover:text-secondary text-xl font-body py-2 px-4 flex items-center gap-2 cursor-pointer group ${
|
|
487
|
+
hasScrolled ? "text-white" : "text-black"
|
|
488
|
+
}`}
|
|
489
|
+
onClick={onClick}>
|
|
490
|
+
<BiArrowBack />
|
|
491
|
+
Back
|
|
492
|
+
</p>
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
return (
|
|
496
|
+
<Flex
|
|
497
|
+
className={`flex sm:block text-white overflow-hidden transition lg:hidden columns-2 ${
|
|
498
|
+
open ? "animate-curtain-down mt-5" : "animate-curtain-up h-0"
|
|
499
|
+
}`}>
|
|
500
|
+
<div ref={drawerRef} onClick={(e: React.MouseEvent) => e.stopPropagation()}>
|
|
501
|
+
{open && (
|
|
502
|
+
<>
|
|
503
|
+
{links && showInnerRoutes.index === null && renderLinks(links)}
|
|
504
|
+
{showInnerRoutes.previous === null && showInnerRoutes.index !== null && (
|
|
505
|
+
<div className="flex flex-col">
|
|
506
|
+
{renderBackButton(() => handleRouteClick(null, null))}
|
|
507
|
+
{renderLinks(links[showInnerRoutes.index]?.multipleRoutes || [], true)}
|
|
508
|
+
</div>
|
|
509
|
+
)}
|
|
510
|
+
{showInnerRoutes.previous !== null && showInnerRoutes.index !== null && (
|
|
511
|
+
<div className="flex flex-col">
|
|
512
|
+
{renderBackButton(() =>
|
|
513
|
+
setShowInnerRoutes({
|
|
514
|
+
previous: null,
|
|
515
|
+
index: showInnerRoutes.previous,
|
|
516
|
+
}),
|
|
517
|
+
)}
|
|
518
|
+
{renderLinks(
|
|
519
|
+
links[showInnerRoutes.previous]?.multipleRoutes?.[showInnerRoutes.index]
|
|
520
|
+
?.multipleInnerRoutes || [],
|
|
521
|
+
)}
|
|
522
|
+
</div>
|
|
523
|
+
)}
|
|
524
|
+
</>
|
|
525
|
+
)}
|
|
526
|
+
</div>
|
|
527
|
+
</Flex>
|
|
528
|
+
);
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
export { Navigation_F };
|