@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,742 @@
|
|
|
1
|
+
import { Button } from "@stackshift-ui/button";
|
|
2
|
+
import { Flex } from "@stackshift-ui/flex";
|
|
3
|
+
import { Grid } from "@stackshift-ui/grid";
|
|
4
|
+
import { Heading } from "@stackshift-ui/heading";
|
|
5
|
+
import { Image } from "@stackshift-ui/image";
|
|
6
|
+
import { Link } from "@stackshift-ui/link";
|
|
7
|
+
import { Section } from "@stackshift-ui/section";
|
|
8
|
+
import { Text } from "@stackshift-ui/text";
|
|
9
|
+
import { useClickAway, useWindowScroll } from "@uidotdev/usehooks";
|
|
10
|
+
import cn from "classnames";
|
|
11
|
+
import React, { createContext, forwardRef, Fragment, SVGProps, useMemo, useState } from "react";
|
|
12
|
+
import { GoPerson } from "react-icons/go";
|
|
13
|
+
import { LiaSearchSolid } from "react-icons/lia";
|
|
14
|
+
import { SlLocationPin } from "react-icons/sl";
|
|
15
|
+
import { NavigationProps } from ".";
|
|
16
|
+
import { MegaNavContextProvider, useMegaNavContext } from "./context/megaNavContext";
|
|
17
|
+
import { getImageProperty, logoLink } from "./helper";
|
|
18
|
+
import { LabeledRouteWithKey, Logo, MegaMenu } from "./types";
|
|
19
|
+
|
|
20
|
+
// Accordion Types
|
|
21
|
+
interface AccordionProps {
|
|
22
|
+
className?: string;
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
defaultValue?: string[];
|
|
25
|
+
onValueChange?: (value: string[]) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface AccordionItemProps {
|
|
29
|
+
value: string;
|
|
30
|
+
className?: string;
|
|
31
|
+
children: React.ReactNode;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface AccordionTriggerProps {
|
|
35
|
+
className?: string;
|
|
36
|
+
children: React.ReactNode;
|
|
37
|
+
onClick?: () => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface AccordionContentProps {
|
|
41
|
+
className?: string;
|
|
42
|
+
children: React.ReactNode;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Accordion Context
|
|
46
|
+
interface AccordionContextType {
|
|
47
|
+
openItems: string[];
|
|
48
|
+
toggleItem: (value: string) => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const AccordionContext = createContext<AccordionContextType | undefined>(undefined);
|
|
52
|
+
|
|
53
|
+
const useAccordion = () => {
|
|
54
|
+
const context = React.useContext(AccordionContext);
|
|
55
|
+
if (!context) {
|
|
56
|
+
throw new Error("useAccordion must be used within an Accordion");
|
|
57
|
+
}
|
|
58
|
+
return context;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const Accordion = ({ className, children, defaultValue = [], onValueChange }: AccordionProps) => {
|
|
62
|
+
const [openItems, setOpenItems] = useState<string[]>(defaultValue);
|
|
63
|
+
|
|
64
|
+
const toggleItem = React.useCallback(
|
|
65
|
+
(value: string) => {
|
|
66
|
+
setOpenItems(prev => {
|
|
67
|
+
const newValue = prev.includes(value)
|
|
68
|
+
? prev.filter(item => item !== value)
|
|
69
|
+
: [...prev, value];
|
|
70
|
+
onValueChange?.(newValue);
|
|
71
|
+
return newValue;
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
[onValueChange],
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const value = React.useMemo(
|
|
78
|
+
() => ({
|
|
79
|
+
openItems,
|
|
80
|
+
toggleItem,
|
|
81
|
+
}),
|
|
82
|
+
[openItems, toggleItem],
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<AccordionContext.Provider value={value}>
|
|
87
|
+
<div className={cn("w-full", className)}>{children}</div>
|
|
88
|
+
</AccordionContext.Provider>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const AccordionItem = ({ value, className, children }: AccordionItemProps) => {
|
|
93
|
+
const { openItems, toggleItem } = useAccordion();
|
|
94
|
+
const isOpen = openItems.includes(value);
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div className={cn("border-b border-gray-200", className)}>
|
|
98
|
+
{React.Children.map(children, child => {
|
|
99
|
+
if (!React.isValidElement(child)) return child;
|
|
100
|
+
|
|
101
|
+
return React.cloneElement(child as React.ReactElement<any>, {
|
|
102
|
+
isOpen,
|
|
103
|
+
onClick: () => toggleItem(value),
|
|
104
|
+
});
|
|
105
|
+
})}
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const AccordionTrigger = React.forwardRef<
|
|
111
|
+
HTMLButtonElement,
|
|
112
|
+
AccordionTriggerProps & { isOpen?: boolean }
|
|
113
|
+
>(({ className, children, isOpen, onClick, ...props }, ref) => {
|
|
114
|
+
return (
|
|
115
|
+
<button
|
|
116
|
+
ref={ref}
|
|
117
|
+
type="button"
|
|
118
|
+
onClick={onClick}
|
|
119
|
+
className={cn("flex w-full items-center justify-between py-4 text-left", className)}
|
|
120
|
+
{...props}>
|
|
121
|
+
{children}
|
|
122
|
+
<svg
|
|
123
|
+
className={cn(
|
|
124
|
+
"h-5 w-5 transform transition-transform duration-200",
|
|
125
|
+
isOpen ? "rotate-180" : "",
|
|
126
|
+
)}
|
|
127
|
+
fill="none"
|
|
128
|
+
viewBox="0 0 24 24"
|
|
129
|
+
stroke="currentColor">
|
|
130
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
|
131
|
+
</svg>
|
|
132
|
+
</button>
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const AccordionContent = React.forwardRef<
|
|
137
|
+
HTMLDivElement,
|
|
138
|
+
AccordionContentProps & { isOpen?: boolean }
|
|
139
|
+
>(({ className, children, isOpen }, ref) => {
|
|
140
|
+
return (
|
|
141
|
+
<div
|
|
142
|
+
ref={ref}
|
|
143
|
+
className={cn(
|
|
144
|
+
"overflow-hidden transition-all duration-200 ease-in-out",
|
|
145
|
+
isOpen ? "max-h-[1000px] opacity-100" : "max-h-0 opacity-0",
|
|
146
|
+
className,
|
|
147
|
+
)}>
|
|
148
|
+
<div className="pb-4">{children}</div>
|
|
149
|
+
</div>
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
interface MobileMenuContentProps {
|
|
154
|
+
data?: MegaMenu[];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const MobileMenuContent = ({ data }: MobileMenuContentProps) => {
|
|
158
|
+
const { showMobileMenu, setShowMobileMenu } = useMegaNavContext();
|
|
159
|
+
const ref = useClickAway<HTMLDivElement>(() => {
|
|
160
|
+
if (showMobileMenu) {
|
|
161
|
+
setShowMobileMenu(false);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
if (!showMobileMenu) return null;
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<div
|
|
169
|
+
ref={ref}
|
|
170
|
+
className="fixed w-[75%] h-screen top-0 right-0 bg-white md:hidden z-50 py-10 px-5 overflow-y-auto">
|
|
171
|
+
<Accordion>
|
|
172
|
+
{data?.map(item => <MobileMenuContentItem key={item._key} megaMenu={item} />)}
|
|
173
|
+
</Accordion>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
interface MobileMenuContentItemProps {
|
|
179
|
+
megaMenu: MegaMenu;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const MobileMenuContentItem = React.memo(({ megaMenu }: MobileMenuContentItemProps) => {
|
|
183
|
+
if (megaMenu._type === "link") {
|
|
184
|
+
return (
|
|
185
|
+
<Link
|
|
186
|
+
href={megaMenu.linkExternal || "#"}
|
|
187
|
+
target={megaMenu.linkTarget}
|
|
188
|
+
className="block py-4 text-base font-medium text-gray-900 hover:text-gray-700 uppercase">
|
|
189
|
+
{megaMenu.label}
|
|
190
|
+
</Link>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<AccordionItem value={megaMenu._key}>
|
|
196
|
+
<AccordionTrigger className="text-base font-medium text-gray-900 uppercase w-full py-4">
|
|
197
|
+
{megaMenu.title}
|
|
198
|
+
</AccordionTrigger>
|
|
199
|
+
<AccordionContent>
|
|
200
|
+
{megaMenu.groupOfLinks?.map(group => (
|
|
201
|
+
<div key={group._key} className="mt-4 pl-4">
|
|
202
|
+
<Text className="text-black font-medium leading-[30px]">{group.title}</Text>
|
|
203
|
+
<Flex
|
|
204
|
+
direction="col"
|
|
205
|
+
align="start"
|
|
206
|
+
justify="start"
|
|
207
|
+
gap={1}
|
|
208
|
+
className="relative w-full pl-4">
|
|
209
|
+
{group.links?.[0]?.links?.map(link => (
|
|
210
|
+
<Button
|
|
211
|
+
key={`MobileMenuContent-Item-Group-Link-${link._key}`}
|
|
212
|
+
ariaLabel={link.label ?? ""}
|
|
213
|
+
as="link"
|
|
214
|
+
link={link ?? {}}
|
|
215
|
+
variant="unstyled"
|
|
216
|
+
className="text-black text-sm font-normal font-heading-kb leading-[30px] hover:underline">
|
|
217
|
+
{link.label}
|
|
218
|
+
</Button>
|
|
219
|
+
))}
|
|
220
|
+
</Flex>
|
|
221
|
+
</div>
|
|
222
|
+
))}
|
|
223
|
+
|
|
224
|
+
{megaMenu.showcaseLink?.map((link, i) => (
|
|
225
|
+
<div key={`MobileMenuContent-Item-Images-${link._key}-${i}`} className="mt-4 pl-4">
|
|
226
|
+
<Button
|
|
227
|
+
ariaLabel={link.primaryButton?.label ?? ""}
|
|
228
|
+
as="link"
|
|
229
|
+
link={link.primaryButton ?? {}}
|
|
230
|
+
variant="unstyled"
|
|
231
|
+
className="text-black text-sm font-normal font-heading-kb leading-[30px]">
|
|
232
|
+
<Flex direction="col" gap={3}>
|
|
233
|
+
<Image
|
|
234
|
+
key={i}
|
|
235
|
+
src={link.mainImage?.image}
|
|
236
|
+
alt={link.mainImage?.alt ?? ""}
|
|
237
|
+
width={200}
|
|
238
|
+
height={150}
|
|
239
|
+
className="w-[188px] h-[147px] object-cover object-center"
|
|
240
|
+
/>
|
|
241
|
+
{link.primaryButton?.label}
|
|
242
|
+
</Flex>
|
|
243
|
+
</Button>
|
|
244
|
+
</div>
|
|
245
|
+
))}
|
|
246
|
+
</AccordionContent>
|
|
247
|
+
</AccordionItem>
|
|
248
|
+
);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
MobileMenuContentItem.displayName = "MobileMenuContentItem";
|
|
252
|
+
|
|
253
|
+
export default function Navigation_H({ logo, iconLinks, megaMenu }: NavigationProps) {
|
|
254
|
+
const [{ y }] = useWindowScroll();
|
|
255
|
+
const sticky =
|
|
256
|
+
y && y > 80
|
|
257
|
+
? "fixed w-full transition-all duration-300 ease-in-out"
|
|
258
|
+
: "sticky transition-all duration-300 ease-in-out";
|
|
259
|
+
|
|
260
|
+
return (
|
|
261
|
+
<MegaNavContextProvider>
|
|
262
|
+
<header
|
|
263
|
+
className={cn(
|
|
264
|
+
"relative top-0 left-0 border-t md:pb-3 z-50 bg-white transition-all ease-in-out duration-300",
|
|
265
|
+
sticky,
|
|
266
|
+
)}>
|
|
267
|
+
<NavContainer logo={logo} iconLinks={iconLinks} megaMenu={megaMenu} />
|
|
268
|
+
</header>
|
|
269
|
+
<MobileMenuContent data={megaMenu} />
|
|
270
|
+
</MegaNavContextProvider>
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
interface NavContainerProps {
|
|
275
|
+
logo?: Logo;
|
|
276
|
+
iconLinks?: LabeledRouteWithKey[];
|
|
277
|
+
megaMenu?: MegaMenu[];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const NavContainer = ({ logo, iconLinks, megaMenu }: NavContainerProps) => {
|
|
281
|
+
const { setShowMobileMenu } = useMegaNavContext();
|
|
282
|
+
const [{ x: _, y }] = useWindowScroll();
|
|
283
|
+
const hideLogo = y && y > 80 ? "md:hidden" : "";
|
|
284
|
+
|
|
285
|
+
return (
|
|
286
|
+
<div className="px-0 w-full h-full transition-all duration-300 ease-in-out overflow-y-auto">
|
|
287
|
+
<Grid
|
|
288
|
+
columns={2}
|
|
289
|
+
align="center"
|
|
290
|
+
justify="between"
|
|
291
|
+
gap={5}
|
|
292
|
+
className={cn(
|
|
293
|
+
"lg:pt-4 py-2 grid-cols-2 md:grid-cols-3 w-full h-full transition-all ease-in-out duration-300 px-4",
|
|
294
|
+
hideLogo,
|
|
295
|
+
)}>
|
|
296
|
+
{logo?.image && (
|
|
297
|
+
<div className="relative w-20 flex items-center h-20 md:w-fit md:h-full place-self-start md:place-self-center col-start-1 md:col-start-2">
|
|
298
|
+
<Link
|
|
299
|
+
aria-label={`Go to ${logoLink(logo) === "/" ? "home page" : logoLink(logo)}`}
|
|
300
|
+
className="text-3xl font-bold leading-none"
|
|
301
|
+
href={logoLink(logo)}
|
|
302
|
+
target={logo?.linkTarget}
|
|
303
|
+
rel={logo?.linkTarget === "_blank" ? "noopener noreferrer" : ""}>
|
|
304
|
+
<Image
|
|
305
|
+
src={logo?.image}
|
|
306
|
+
width={135}
|
|
307
|
+
height={135}
|
|
308
|
+
className="object-cover transition-all ease-in-out duration-300"
|
|
309
|
+
alt={logo?.alt ?? "navigation-logo"}
|
|
310
|
+
/>
|
|
311
|
+
</Link>
|
|
312
|
+
</div>
|
|
313
|
+
)}
|
|
314
|
+
<button
|
|
315
|
+
onClick={() => setShowMobileMenu(true)}
|
|
316
|
+
className="md:hidden h-fit self-center place-self-end">
|
|
317
|
+
<MdiLightMenu className="w-8 h-8" />
|
|
318
|
+
</button>
|
|
319
|
+
{iconLinks && iconLinks.length > 0 && (
|
|
320
|
+
<Flex
|
|
321
|
+
align="center"
|
|
322
|
+
gap={5}
|
|
323
|
+
className="hidden md:flex place-self-center max-w-[144px] h-32 w-full">
|
|
324
|
+
<ul className="flex gap-5">
|
|
325
|
+
{iconLinks?.map(link =>
|
|
326
|
+
link && link.label ? <NavItem key={link._key} link={link} isIcon={true} /> : null,
|
|
327
|
+
)}
|
|
328
|
+
</ul>
|
|
329
|
+
</Flex>
|
|
330
|
+
)}
|
|
331
|
+
</Grid>
|
|
332
|
+
<MegaMenuNavLinks data={megaMenu ?? []} />
|
|
333
|
+
<MegaMenuDropdownContents data={megaMenu ?? []} />
|
|
334
|
+
</div>
|
|
335
|
+
);
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
interface NavItemProps {
|
|
339
|
+
link: LabeledRouteWithKey;
|
|
340
|
+
isIcon: boolean;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const NavItem = ({ link, isIcon }: NavItemProps) => {
|
|
344
|
+
const icon = navItemIcons[link?.label as keyof typeof navItemIcons] || null;
|
|
345
|
+
|
|
346
|
+
return (
|
|
347
|
+
<li>
|
|
348
|
+
<Button
|
|
349
|
+
as="link"
|
|
350
|
+
ariaLabel={link?.label}
|
|
351
|
+
link={link}
|
|
352
|
+
className="text-sm font-label tracking-wide">
|
|
353
|
+
{!isIcon ? link?.label : icon}
|
|
354
|
+
</Button>
|
|
355
|
+
</li>
|
|
356
|
+
);
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
const navItemIcons = {
|
|
360
|
+
search: <LiaSearchSolid className="w-4 h-4" />,
|
|
361
|
+
location: <SlLocationPin className="w-4 h-4" />,
|
|
362
|
+
person: <GoPerson className="w-4 h-4" />,
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
interface MegaMenuNavLinksProps {
|
|
366
|
+
data: MegaMenu[];
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const MegaMenuNavLinks = ({ data }: MegaMenuNavLinksProps) => {
|
|
370
|
+
const { currentDropdown, setCurrentDropdown } = useMegaNavContext();
|
|
371
|
+
const [{ x: _, y }] = useWindowScroll();
|
|
372
|
+
const hide = (y && y < 80) || y === 0 ? "hidden" : "";
|
|
373
|
+
|
|
374
|
+
return (
|
|
375
|
+
<Flex
|
|
376
|
+
align="center"
|
|
377
|
+
justify="between"
|
|
378
|
+
gap={5}
|
|
379
|
+
className="relative w-full h-full max-w-7xl mx-auto hidden md:flex pt-4 px-4 transition-all duration-300 ease-in-out">
|
|
380
|
+
{data && data.length > 0
|
|
381
|
+
? data
|
|
382
|
+
?.slice(0, 3)
|
|
383
|
+
.map(megaMenuItem => (
|
|
384
|
+
<Fragment key={`MegaMenuNavLink-${megaMenuItem._key}`}>
|
|
385
|
+
{megaMenuItem._type === "dropdown" ? (
|
|
386
|
+
<MegaMenuDropdownTrigger
|
|
387
|
+
key={`MegaMenuDropdownTrigger-${megaMenuItem._key}`}
|
|
388
|
+
dropdown={megaMenuItem}
|
|
389
|
+
aria-expanded={currentDropdown === megaMenuItem.title}
|
|
390
|
+
onMouseEnter={() => setCurrentDropdown(megaMenuItem.title ?? "")}
|
|
391
|
+
onMouseLeave={() => setCurrentDropdown("")}
|
|
392
|
+
/>
|
|
393
|
+
) : (
|
|
394
|
+
<MegaMenuNavLink key={`MegaMenuLink-${megaMenuItem._key}`} link={megaMenuItem} />
|
|
395
|
+
)}
|
|
396
|
+
</Fragment>
|
|
397
|
+
))
|
|
398
|
+
: null}
|
|
399
|
+
{data && data.length > 0
|
|
400
|
+
? data
|
|
401
|
+
?.slice(3, 6)
|
|
402
|
+
.map(megaMenuItem => (
|
|
403
|
+
<Fragment key={`MegaMenuNavLink-${megaMenuItem._key}`}>
|
|
404
|
+
{megaMenuItem._type === "dropdown" ? (
|
|
405
|
+
<MegaMenuDropdownTrigger
|
|
406
|
+
key={`MegaMenuDropdownTrigger-${megaMenuItem._key}`}
|
|
407
|
+
dropdown={megaMenuItem}
|
|
408
|
+
aria-expanded={currentDropdown === megaMenuItem.title}
|
|
409
|
+
onMouseEnter={() => setCurrentDropdown(megaMenuItem.title ?? "")}
|
|
410
|
+
onMouseLeave={() => setCurrentDropdown("")}
|
|
411
|
+
/>
|
|
412
|
+
) : (
|
|
413
|
+
<MegaMenuNavLink key={`MegaMenuLink-${megaMenuItem._key}`} link={megaMenuItem} />
|
|
414
|
+
)}
|
|
415
|
+
</Fragment>
|
|
416
|
+
))
|
|
417
|
+
: null}
|
|
418
|
+
</Flex>
|
|
419
|
+
);
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
interface MegaMenuNavLinkProps {
|
|
423
|
+
link: MegaMenu;
|
|
424
|
+
className?: string;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const MegaMenuNavLink = ({ link, className }: MegaMenuNavLinkProps) => {
|
|
428
|
+
return (
|
|
429
|
+
<Button
|
|
430
|
+
ariaLabel={`Go to ${link.label}`}
|
|
431
|
+
as="link"
|
|
432
|
+
variant="unstyled"
|
|
433
|
+
link={link}
|
|
434
|
+
className={cn(
|
|
435
|
+
"relative text-black text-sm font-normal font-label uppercase tracking-widest group",
|
|
436
|
+
className,
|
|
437
|
+
)}>
|
|
438
|
+
{link.label}
|
|
439
|
+
</Button>
|
|
440
|
+
);
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
interface MegaMenuDropdownTriggerProps {
|
|
444
|
+
dropdown: MegaMenu;
|
|
445
|
+
onClick?: () => void;
|
|
446
|
+
onMouseEnter?: () => void;
|
|
447
|
+
onMouseLeave?: () => void;
|
|
448
|
+
className?: string;
|
|
449
|
+
"aria-expanded"?: boolean;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const MegaMenuDropdownTrigger = forwardRef<HTMLButtonElement, MegaMenuDropdownTriggerProps>(
|
|
453
|
+
(props, ref) => {
|
|
454
|
+
const { dropdown, onClick, onMouseEnter, onMouseLeave, className, ...restProps } = props;
|
|
455
|
+
|
|
456
|
+
return dropdown.linkExternal ? (
|
|
457
|
+
<Link
|
|
458
|
+
href={dropdown.linkExternal}
|
|
459
|
+
onClick={onClick}
|
|
460
|
+
onMouseEnter={onMouseEnter}
|
|
461
|
+
onMouseLeave={onMouseLeave}
|
|
462
|
+
aria-label={`Open the ${dropdown.title} menu`}
|
|
463
|
+
className={cn(
|
|
464
|
+
"relative text-black text-sm font-normal font-label uppercase tracking-widest group",
|
|
465
|
+
className,
|
|
466
|
+
)}
|
|
467
|
+
{...restProps}>
|
|
468
|
+
{dropdown.title}
|
|
469
|
+
</Link>
|
|
470
|
+
) : (
|
|
471
|
+
<button
|
|
472
|
+
ref={ref}
|
|
473
|
+
onClick={onClick}
|
|
474
|
+
onMouseEnter={onMouseEnter}
|
|
475
|
+
onMouseLeave={onMouseLeave}
|
|
476
|
+
aria-label={`Open the ${dropdown.title} menu`}
|
|
477
|
+
className={cn(
|
|
478
|
+
"relative text-black text-sm font-normal font-label uppercase tracking-widest group",
|
|
479
|
+
className,
|
|
480
|
+
)}
|
|
481
|
+
{...restProps}>
|
|
482
|
+
{dropdown.title}
|
|
483
|
+
</button>
|
|
484
|
+
);
|
|
485
|
+
},
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
interface MegaMenuDropdownContentsProps {
|
|
489
|
+
data: MegaMenu[];
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const MegaMenuDropdownContents = ({ data }: MegaMenuDropdownContentsProps) => {
|
|
493
|
+
const { currentDropdown } = useMegaNavContext();
|
|
494
|
+
|
|
495
|
+
return (
|
|
496
|
+
<>
|
|
497
|
+
{data && data.length > 0
|
|
498
|
+
? data?.map(megaMenuDropdown => {
|
|
499
|
+
const show =
|
|
500
|
+
currentDropdown === megaMenuDropdown.title && megaMenuDropdown.title !== "";
|
|
501
|
+
return (
|
|
502
|
+
<MegaDropdownContent
|
|
503
|
+
key={`MegaMenuDropdown-${megaMenuDropdown._key}`}
|
|
504
|
+
label={megaMenuDropdown.title ?? ""}
|
|
505
|
+
links={megaMenuDropdown.links ?? []}
|
|
506
|
+
showcaseLink={megaMenuDropdown.showcaseLink ?? []}
|
|
507
|
+
groupedLinks={megaMenuDropdown.groupOfLinks ?? []}
|
|
508
|
+
/>
|
|
509
|
+
);
|
|
510
|
+
})
|
|
511
|
+
: null}
|
|
512
|
+
</>
|
|
513
|
+
);
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
interface MegaDropdownContentProps {
|
|
517
|
+
label: string;
|
|
518
|
+
links: any[];
|
|
519
|
+
showcaseLink: any[];
|
|
520
|
+
groupedLinks: any[];
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function MegaDropdownContent({
|
|
524
|
+
label,
|
|
525
|
+
links,
|
|
526
|
+
showcaseLink,
|
|
527
|
+
groupedLinks,
|
|
528
|
+
}: MegaDropdownContentProps) {
|
|
529
|
+
const { currentDropdown, setCurrentDropdown } = useMegaNavContext();
|
|
530
|
+
const show = currentDropdown === label && label !== "";
|
|
531
|
+
|
|
532
|
+
const isGroupLinks = groupedLinks.length > 0 && links.length === 0;
|
|
533
|
+
const hasShowcaseLinks = showcaseLink.length > 0;
|
|
534
|
+
|
|
535
|
+
const groupedLinksGridColumns = useMemo(() => {
|
|
536
|
+
if (groupedLinks.length > 0) {
|
|
537
|
+
return groupedLinks
|
|
538
|
+
.map(group => {
|
|
539
|
+
return group.links.length > 2 ? "3fr" : "1fr";
|
|
540
|
+
})
|
|
541
|
+
.join(" ");
|
|
542
|
+
} else {
|
|
543
|
+
return "1fr";
|
|
544
|
+
}
|
|
545
|
+
}, [groupedLinks]);
|
|
546
|
+
|
|
547
|
+
const normalLinksGridColumns = links.length > 0 ? `repeat(${links.length}, 1fr)` : "1fr";
|
|
548
|
+
const gridColumns =
|
|
549
|
+
links.length > 0 || groupedLinks.length > 0
|
|
550
|
+
? `${isGroupLinks ? groupedLinksGridColumns : normalLinksGridColumns} ${
|
|
551
|
+
hasShowcaseLinks ? "2fr" : ""
|
|
552
|
+
}`
|
|
553
|
+
: "1fr";
|
|
554
|
+
|
|
555
|
+
return (
|
|
556
|
+
<Section
|
|
557
|
+
data-show={show}
|
|
558
|
+
onMouseEnter={() => setCurrentDropdown(label)}
|
|
559
|
+
onMouseLeave={() => setCurrentDropdown("")}
|
|
560
|
+
className={`relative top-6 left-0 w-full overflow-hidden z-50 bg-primary/5 !transition-[max-height] !duration-300 !ease-in-out ${
|
|
561
|
+
show ? "max-h-[500px]" : "max-h-0"
|
|
562
|
+
}`}>
|
|
563
|
+
<div
|
|
564
|
+
style={{
|
|
565
|
+
display: "grid",
|
|
566
|
+
gridTemplateColumns: gridColumns,
|
|
567
|
+
gridTemplateRows: "1fr",
|
|
568
|
+
}}
|
|
569
|
+
className="relative w-full h-auto max-w-[90rem] mx-auto">
|
|
570
|
+
<MegaDropDownLinks links={links} />
|
|
571
|
+
<MegaDropdownGroupedLinks groupedLinks={groupedLinks} />
|
|
572
|
+
<MegaDropdownShowcaseLinks
|
|
573
|
+
hasShowcaseLinks={hasShowcaseLinks}
|
|
574
|
+
showcaseLink={showcaseLink}
|
|
575
|
+
/>
|
|
576
|
+
</div>
|
|
577
|
+
</Section>
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function MegaDropDownLinks({ links }: { links: LabeledRouteWithKey[] }) {
|
|
582
|
+
return (
|
|
583
|
+
<Fragment>
|
|
584
|
+
{links &&
|
|
585
|
+
links.length > 0 &&
|
|
586
|
+
links?.map((item: any, index: number) => {
|
|
587
|
+
return (
|
|
588
|
+
<div
|
|
589
|
+
key={`MegaDropdownContent-Item-${item._key}-${index}`}
|
|
590
|
+
className="relative pt-10 pl-5 flex flex-col items-end justify-start w-full h-full border-r border-black last:border-r-0">
|
|
591
|
+
{item.primaryButton?.label ? (
|
|
592
|
+
<Button
|
|
593
|
+
as="link"
|
|
594
|
+
ariaLabel={item.primaryButton?.label}
|
|
595
|
+
link={item.primaryButton}
|
|
596
|
+
className="text-sm text-black font-normal uppercase tracking-widest font-label self-start mb-4 border-b border-transparent hover:border-black">
|
|
597
|
+
{item.primaryButton?.label}
|
|
598
|
+
</Button>
|
|
599
|
+
) : (
|
|
600
|
+
<Heading
|
|
601
|
+
fontSize="sm"
|
|
602
|
+
className="text-black font-normal uppercase tracking-widest font-label self-start pb-4">
|
|
603
|
+
{item.title}
|
|
604
|
+
</Heading>
|
|
605
|
+
)}
|
|
606
|
+
<Flex direction="col" align="start" justify="start" gap={2} className="w-full h-full">
|
|
607
|
+
{item.links?.map((link: any, i: number) => {
|
|
608
|
+
return (
|
|
609
|
+
<Button
|
|
610
|
+
key={`MegaDropdownContent-Item-Link-${item._key}-${i}`}
|
|
611
|
+
ariaLabel={link.label ?? ""}
|
|
612
|
+
as="link"
|
|
613
|
+
link={link ?? {}}
|
|
614
|
+
variant="unstyled"
|
|
615
|
+
className="text-black text-sm font-normal font-heading-kb leading-[30px] hover:underline">
|
|
616
|
+
{link?.label}
|
|
617
|
+
</Button>
|
|
618
|
+
);
|
|
619
|
+
})}
|
|
620
|
+
</Flex>
|
|
621
|
+
</div>
|
|
622
|
+
);
|
|
623
|
+
})}
|
|
624
|
+
</Fragment>
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function MegaDropdownGroupedLinks({ groupedLinks }: { groupedLinks: LabeledRouteWithKey[] }) {
|
|
629
|
+
return (
|
|
630
|
+
<Fragment>
|
|
631
|
+
{groupedLinks &&
|
|
632
|
+
groupedLinks.length > 0 &&
|
|
633
|
+
groupedLinks?.map((groupedLink: any, i: number) => {
|
|
634
|
+
return (
|
|
635
|
+
<div
|
|
636
|
+
key={`MegaDropdownContent-Item-${groupedLink._key}-${i}`}
|
|
637
|
+
className="relative py-10 pl-5 flex flex-col items-end justify-start h-full w-full border-r border-black last:border-r-0">
|
|
638
|
+
<Heading
|
|
639
|
+
fontSize="sm"
|
|
640
|
+
className="text-black font-normal uppercase tracking-widest font-label self-start pb-4">
|
|
641
|
+
{groupedLink.title}
|
|
642
|
+
</Heading>
|
|
643
|
+
<Flex
|
|
644
|
+
direction="row"
|
|
645
|
+
align="start"
|
|
646
|
+
justify="start"
|
|
647
|
+
gap={4}
|
|
648
|
+
className="relative w-full h-full grid-flow-row">
|
|
649
|
+
{groupedLink.links?.map((link: any, i: number) => {
|
|
650
|
+
return (
|
|
651
|
+
<Flex
|
|
652
|
+
key={`MegaDropdownContent-Item-Link-${link._key}-${i}`}
|
|
653
|
+
direction="col"
|
|
654
|
+
className="h-fit w-fit">
|
|
655
|
+
<Button
|
|
656
|
+
ariaLabel={link.title ?? ""}
|
|
657
|
+
as="link"
|
|
658
|
+
link={link?.primaryButton ?? {}}
|
|
659
|
+
variant="unstyled"
|
|
660
|
+
className="text-black text-sm font-normal font-heading-kb leading-[30px] underline uppercase underline-offset-2">
|
|
661
|
+
{link?.title}
|
|
662
|
+
</Button>
|
|
663
|
+
<Flex
|
|
664
|
+
direction="col"
|
|
665
|
+
align="start"
|
|
666
|
+
justify="start"
|
|
667
|
+
gap={2}
|
|
668
|
+
className="w-fit h-full">
|
|
669
|
+
{link.links?.map((link: any, i: number) => {
|
|
670
|
+
return (
|
|
671
|
+
<Button
|
|
672
|
+
key={`MegaDropdownContent-Item-Link-${link._key}-${i}`}
|
|
673
|
+
ariaLabel={link.label ?? ""}
|
|
674
|
+
as="link"
|
|
675
|
+
link={link ?? {}}
|
|
676
|
+
variant="unstyled"
|
|
677
|
+
className="text-black text-sm font-normal font-heading-kb leading-[30px] hover:underline">
|
|
678
|
+
{link?.label}
|
|
679
|
+
</Button>
|
|
680
|
+
);
|
|
681
|
+
})}
|
|
682
|
+
</Flex>
|
|
683
|
+
</Flex>
|
|
684
|
+
);
|
|
685
|
+
})}
|
|
686
|
+
</Flex>
|
|
687
|
+
</div>
|
|
688
|
+
);
|
|
689
|
+
})}
|
|
690
|
+
</Fragment>
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function MegaDropdownShowcaseLinks({
|
|
695
|
+
hasShowcaseLinks,
|
|
696
|
+
showcaseLink,
|
|
697
|
+
}: {
|
|
698
|
+
hasShowcaseLinks: boolean;
|
|
699
|
+
showcaseLink: LabeledRouteWithKey[];
|
|
700
|
+
}) {
|
|
701
|
+
if (!hasShowcaseLinks) return null;
|
|
702
|
+
|
|
703
|
+
return (
|
|
704
|
+
<Flex direction="row" align="start" justify="start" className="w-full h-full gap-6 pl-5 py-10">
|
|
705
|
+
{showcaseLink?.map((link: any, i: number) => {
|
|
706
|
+
const imageUrl = link.mainImage?.image;
|
|
707
|
+
const imageProperty = getImageProperty(link.mainImage?.image.asset);
|
|
708
|
+
|
|
709
|
+
return (
|
|
710
|
+
<Button
|
|
711
|
+
key={`MegaDropdownContent-Item-Images-${link._key}-${i}`}
|
|
712
|
+
ariaLabel={link.primaryButton?.label ?? ""}
|
|
713
|
+
as="link"
|
|
714
|
+
link={link.primaryButton ?? {}}
|
|
715
|
+
variant="unstyled"
|
|
716
|
+
className="text-center text-black text-sm font-normal font-heading-kb leading-[30px] hover:underline">
|
|
717
|
+
<Flex direction="col" align="center" justify="center" gap={3}>
|
|
718
|
+
<Image
|
|
719
|
+
key={i}
|
|
720
|
+
src={imageUrl}
|
|
721
|
+
alt={link.mainImage?.alt ?? ""}
|
|
722
|
+
width={imageProperty?.width ?? 0}
|
|
723
|
+
height={imageProperty?.height ?? 0}
|
|
724
|
+
className="w-[188px] h-[147px] object-cover object-center"></Image>
|
|
725
|
+
{link.primaryButton?.label}
|
|
726
|
+
</Flex>
|
|
727
|
+
</Button>
|
|
728
|
+
);
|
|
729
|
+
})}
|
|
730
|
+
</Flex>
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function MdiLightMenu(props: SVGProps<SVGSVGElement>) {
|
|
735
|
+
return (
|
|
736
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" {...props}>
|
|
737
|
+
<path fill="currentColor" d="M3 8V7h17v1zm17 4v1H3v-1zM3 17h17v1H3z"></path>
|
|
738
|
+
</svg>
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
export { Navigation_H };
|