blocks-dusted 0.1.8 → 0.1.10
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/package.json +1 -1
- package/src/commands/header.js +13 -52
- package/templates/components/HeaderDD02/Component.client.tsx +9 -0
- package/templates/components/HeaderDD02/Component.tsx +10 -0
- package/templates/components/HeaderDD02/Header.config.ts +33 -0
- package/templates/components/HeaderDD02/MobileBottomNav/index.tsx +352 -257
- package/templates/components/HeaderDD02/Nav/index.tsx +9 -0
- package/templates/components/HeaderDD02/README.md +23 -16
- package/templates/components/HeaderDD02/hooks/revalidateHeader.ts +11 -0
- package/templates/components/HeaderDD02/index.tsx +313 -229
- package/templates/components/HeaderDD02/manifest.json +32 -16
|
@@ -1,30 +1,39 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
|
|
3
|
-
import { usePathname } from
|
|
4
|
-
import React, { useEffect, useMemo, useState, useRef } from
|
|
5
|
-
import Link from
|
|
3
|
+
import { usePathname } from "next/navigation";
|
|
4
|
+
import React, { useEffect, useMemo, useState, useRef } from "react";
|
|
5
|
+
import Link from "next/link";
|
|
6
6
|
import {
|
|
7
7
|
NavigationMenu,
|
|
8
8
|
NavigationMenuContent,
|
|
9
9
|
NavigationMenuItem,
|
|
10
10
|
NavigationMenuLink,
|
|
11
11
|
NavigationMenuList,
|
|
12
|
-
NavigationMenuTrigger
|
|
13
|
-
} from
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
NavigationMenuTrigger,
|
|
13
|
+
} from "@/components/ui/navigation-menu";
|
|
14
|
+
import {
|
|
15
|
+
CircleCheck,
|
|
16
|
+
CircleHelp,
|
|
17
|
+
Circle,
|
|
18
|
+
Home,
|
|
19
|
+
User,
|
|
20
|
+
Settings,
|
|
21
|
+
Mail,
|
|
22
|
+
Phone,
|
|
23
|
+
} from "lucide-react";
|
|
24
|
+
import type { Header as HeaderType } from "@/payload-types";
|
|
25
|
+
import RichText from "@/components/RichText";
|
|
26
|
+
import { cn } from "@/utilities/ui";
|
|
18
27
|
|
|
19
|
-
import * as LucideIcons from
|
|
20
|
-
import { motion } from
|
|
28
|
+
import * as LucideIcons from "lucide-react";
|
|
29
|
+
import { motion } from "framer-motion";
|
|
21
30
|
|
|
22
|
-
import MobileBottomNav from
|
|
31
|
+
import MobileBottomNav from "./MobileBottomNav";
|
|
23
32
|
|
|
24
33
|
interface HeaderDD02Props {
|
|
25
|
-
data?: HeaderType
|
|
26
|
-
variant?:
|
|
27
|
-
sticky?: boolean
|
|
34
|
+
data?: HeaderType;
|
|
35
|
+
variant?: "default" | "centered" | "fullwidth";
|
|
36
|
+
sticky?: boolean;
|
|
28
37
|
}
|
|
29
38
|
|
|
30
39
|
const iconMap = {
|
|
@@ -35,76 +44,90 @@ const iconMap = {
|
|
|
35
44
|
User,
|
|
36
45
|
Settings,
|
|
37
46
|
Mail,
|
|
38
|
-
Phone
|
|
39
|
-
}
|
|
47
|
+
Phone,
|
|
48
|
+
};
|
|
40
49
|
|
|
41
|
-
type CartVisibilityMode =
|
|
50
|
+
type CartVisibilityMode = "always" | "hide" | "show";
|
|
42
51
|
|
|
43
52
|
type CartVisibilityEntry = {
|
|
44
|
-
path?: string | null
|
|
45
|
-
}
|
|
53
|
+
path?: string | null;
|
|
54
|
+
};
|
|
46
55
|
|
|
47
56
|
type CartVisibilityConfig = {
|
|
48
|
-
mode?: CartVisibilityMode
|
|
49
|
-
paths?: CartVisibilityEntry[]
|
|
50
|
-
}
|
|
57
|
+
mode?: CartVisibilityMode;
|
|
58
|
+
paths?: CartVisibilityEntry[];
|
|
59
|
+
};
|
|
51
60
|
|
|
52
|
-
type
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
type HeaderNavItem = NonNullable<
|
|
62
|
+
NonNullable<HeaderType["header02"]>["navItems"]
|
|
63
|
+
>[number] &
|
|
64
|
+
Record<string, any>;
|
|
65
|
+
type HeaderChildItem = Record<string, any>;
|
|
66
|
+
|
|
67
|
+
type HeaderDD02Data = HeaderType["header02"] & {
|
|
68
|
+
cartButtonVisibility?: CartVisibilityConfig;
|
|
69
|
+
};
|
|
55
70
|
|
|
56
71
|
const normalizePath = (value?: string | null) => {
|
|
57
|
-
if (!value) return
|
|
58
|
-
const [base] = value.split(/[?#]/)
|
|
59
|
-
if (!base) return
|
|
60
|
-
if (base !==
|
|
61
|
-
return base.slice(0, -1)
|
|
72
|
+
if (!value) return "/";
|
|
73
|
+
const [base] = value.split(/[?#]/);
|
|
74
|
+
if (!base) return "/";
|
|
75
|
+
if (base !== "/" && base.endsWith("/")) {
|
|
76
|
+
return base.slice(0, -1);
|
|
62
77
|
}
|
|
63
|
-
return base ||
|
|
64
|
-
}
|
|
78
|
+
return base || "/";
|
|
79
|
+
};
|
|
65
80
|
|
|
66
81
|
// Helper to extract href from link field
|
|
67
82
|
const getLinkHref = (link: any): string => {
|
|
68
|
-
if (!link) return
|
|
69
|
-
if (link.type ===
|
|
70
|
-
const value = link.reference?.value
|
|
71
|
-
if (typeof value ===
|
|
72
|
-
return `/${value.slug}
|
|
83
|
+
if (!link) return "#";
|
|
84
|
+
if (link.type === "reference") {
|
|
85
|
+
const value = link.reference?.value;
|
|
86
|
+
if (typeof value === "object" && "slug" in value) {
|
|
87
|
+
return `/${value.slug}`;
|
|
73
88
|
}
|
|
74
89
|
}
|
|
75
|
-
return link.url ||
|
|
76
|
-
}
|
|
90
|
+
return link.url || "#";
|
|
91
|
+
};
|
|
77
92
|
|
|
78
93
|
const resolveCustomIconUrl = (customIcon?: any): string | null => {
|
|
79
|
-
if (!customIcon) return null
|
|
80
|
-
if (typeof customIcon ===
|
|
81
|
-
if (typeof customIcon ===
|
|
82
|
-
if (
|
|
83
|
-
|
|
94
|
+
if (!customIcon) return null;
|
|
95
|
+
if (typeof customIcon === "string") return customIcon;
|
|
96
|
+
if (typeof customIcon === "number") return null;
|
|
97
|
+
if (
|
|
98
|
+
typeof customIcon === "object" &&
|
|
99
|
+
"url" in customIcon &&
|
|
100
|
+
typeof customIcon.url === "string"
|
|
101
|
+
) {
|
|
102
|
+
return customIcon.url;
|
|
84
103
|
}
|
|
85
|
-
return null
|
|
86
|
-
}
|
|
104
|
+
return null;
|
|
105
|
+
};
|
|
87
106
|
|
|
88
107
|
// Helper to render icon from link field or fallback
|
|
89
|
-
const renderIcon = (
|
|
90
|
-
|
|
108
|
+
const renderIcon = (
|
|
109
|
+
iconName?: string | null,
|
|
110
|
+
className?: string,
|
|
111
|
+
customIcon?: any,
|
|
112
|
+
) => {
|
|
113
|
+
const customUrl = resolveCustomIconUrl(customIcon);
|
|
91
114
|
if (customUrl) {
|
|
92
115
|
return (
|
|
93
116
|
<img
|
|
94
117
|
src={customUrl}
|
|
95
|
-
className={className ||
|
|
118
|
+
className={className || "size-4"}
|
|
96
119
|
alt=""
|
|
97
120
|
aria-hidden="true"
|
|
98
121
|
loading="lazy"
|
|
99
122
|
/>
|
|
100
|
-
)
|
|
123
|
+
);
|
|
101
124
|
}
|
|
102
125
|
|
|
103
|
-
if (!iconName) return null
|
|
104
|
-
const IconComponent = (LucideIcons as any)[iconName]
|
|
105
|
-
if (!IconComponent) return null
|
|
106
|
-
return <IconComponent className={className ||
|
|
107
|
-
}
|
|
126
|
+
if (!iconName) return null;
|
|
127
|
+
const IconComponent = (LucideIcons as any)[iconName];
|
|
128
|
+
if (!IconComponent) return null;
|
|
129
|
+
return <IconComponent className={className || "size-4"} />;
|
|
130
|
+
};
|
|
108
131
|
|
|
109
132
|
const dropdownStyles = `
|
|
110
133
|
.dropdown-richtext-content {
|
|
@@ -129,7 +152,7 @@ const dropdownStyles = `
|
|
|
129
152
|
max-height: var(--radix-navigation-menu-viewport-height);
|
|
130
153
|
overflow: auto;
|
|
131
154
|
}
|
|
132
|
-
|
|
155
|
+
`;
|
|
133
156
|
|
|
134
157
|
function ListItem({
|
|
135
158
|
title,
|
|
@@ -137,14 +160,14 @@ function ListItem({
|
|
|
137
160
|
href,
|
|
138
161
|
className,
|
|
139
162
|
icon,
|
|
140
|
-
iconPosition =
|
|
163
|
+
iconPosition = "before",
|
|
141
164
|
}: {
|
|
142
|
-
title: string
|
|
143
|
-
children?: React.ReactNode
|
|
144
|
-
href: string
|
|
145
|
-
className?: string
|
|
146
|
-
icon?: React.ReactNode
|
|
147
|
-
iconPosition?:
|
|
165
|
+
title: string;
|
|
166
|
+
children?: React.ReactNode;
|
|
167
|
+
href: string;
|
|
168
|
+
className?: string;
|
|
169
|
+
icon?: React.ReactNode;
|
|
170
|
+
iconPosition?: "before" | "after";
|
|
148
171
|
}) {
|
|
149
172
|
return (
|
|
150
173
|
<li>
|
|
@@ -152,19 +175,19 @@ function ListItem({
|
|
|
152
175
|
<Link
|
|
153
176
|
href={href}
|
|
154
177
|
className={cn(
|
|
155
|
-
|
|
156
|
-
className
|
|
178
|
+
"block select-none space-y-1 no-underline outline-none transition-colors hover:bg-zinc-800/50 duration-300 rounded-sm hover:text-accent focus:bg-accent focus:text-stone-200 py-2 px-3",
|
|
179
|
+
className,
|
|
157
180
|
)}
|
|
158
181
|
>
|
|
159
182
|
<div className="text-sm font-medium leading-none flex items-center gap-2">
|
|
160
|
-
{icon && iconPosition ===
|
|
183
|
+
{icon && iconPosition === "before" && icon}
|
|
161
184
|
{title}
|
|
162
|
-
{icon && iconPosition ===
|
|
185
|
+
{icon && iconPosition === "after" && icon}
|
|
163
186
|
</div>
|
|
164
187
|
{children && (
|
|
165
188
|
<p
|
|
166
189
|
className="line-clamp-2 text-xs leading-snug text-stone-400 mt-0 !pt-1 -translate-y-2"
|
|
167
|
-
style={{ lineHeight:
|
|
190
|
+
style={{ lineHeight: "1.3" }}
|
|
168
191
|
>
|
|
169
192
|
{children}
|
|
170
193
|
</p>
|
|
@@ -172,21 +195,21 @@ function ListItem({
|
|
|
172
195
|
</Link>
|
|
173
196
|
</NavigationMenuLink>
|
|
174
197
|
</li>
|
|
175
|
-
)
|
|
198
|
+
);
|
|
176
199
|
}
|
|
177
200
|
|
|
178
201
|
function ListItemWithIcon({
|
|
179
202
|
title,
|
|
180
203
|
href,
|
|
181
204
|
icon,
|
|
182
|
-
className
|
|
205
|
+
className,
|
|
183
206
|
}: {
|
|
184
|
-
title: string
|
|
185
|
-
href: string
|
|
186
|
-
icon: string
|
|
187
|
-
className?: string
|
|
207
|
+
title: string;
|
|
208
|
+
href: string;
|
|
209
|
+
icon: string;
|
|
210
|
+
className?: string;
|
|
188
211
|
}) {
|
|
189
|
-
const IconComponent = iconMap[icon as keyof typeof iconMap]
|
|
212
|
+
const IconComponent = iconMap[icon as keyof typeof iconMap];
|
|
190
213
|
|
|
191
214
|
return (
|
|
192
215
|
<li>
|
|
@@ -194,8 +217,8 @@ function ListItemWithIcon({
|
|
|
194
217
|
<Link
|
|
195
218
|
href={href}
|
|
196
219
|
className={cn(
|
|
197
|
-
|
|
198
|
-
className
|
|
220
|
+
"flex items-center gap-2 select-none no-underline outline-none transition-colors hover:bg-accent hover:text-stone-200 focus:bg-accent focus:text-stone-200 py-2 px-3",
|
|
221
|
+
className,
|
|
199
222
|
)}
|
|
200
223
|
>
|
|
201
224
|
{IconComponent && <IconComponent className="size-4" />}
|
|
@@ -203,55 +226,68 @@ function ListItemWithIcon({
|
|
|
203
226
|
</Link>
|
|
204
227
|
</NavigationMenuLink>
|
|
205
228
|
</li>
|
|
206
|
-
)
|
|
229
|
+
);
|
|
207
230
|
}
|
|
208
231
|
|
|
209
|
-
export default function HeaderDD02({
|
|
232
|
+
export default function HeaderDD02({
|
|
233
|
+
data,
|
|
234
|
+
variant = "default",
|
|
235
|
+
sticky = true,
|
|
236
|
+
}: HeaderDD02Props) {
|
|
210
237
|
// Get scroll settings from data with defaults
|
|
211
|
-
const enableHideOnScroll = data?.header02?.enableHideOnScroll ?? true
|
|
212
|
-
const scrollThreshold = data?.header02?.scrollThreshold ?? 100
|
|
213
|
-
const animationDuration = data?.header02?.animationDuration ?? 0.3
|
|
214
|
-
const [forceOpen, setForceOpen] = useState(true)
|
|
238
|
+
const enableHideOnScroll = data?.header02?.enableHideOnScroll ?? true;
|
|
239
|
+
const scrollThreshold = data?.header02?.scrollThreshold ?? 100;
|
|
240
|
+
const animationDuration = data?.header02?.animationDuration ?? 0.3;
|
|
241
|
+
const [forceOpen, setForceOpen] = useState(true);
|
|
215
242
|
|
|
216
243
|
// State for handling scroll behavior
|
|
217
|
-
const [isHidden, setIsHidden] = useState(false)
|
|
218
|
-
const lastScrollY = useRef(0)
|
|
219
|
-
const pathname = usePathname()
|
|
220
|
-
const normalizedCurrentPathname = useMemo(
|
|
221
|
-
|
|
244
|
+
const [isHidden, setIsHidden] = useState(false);
|
|
245
|
+
const lastScrollY = useRef(0);
|
|
246
|
+
const pathname = usePathname();
|
|
247
|
+
const normalizedCurrentPathname = useMemo(
|
|
248
|
+
() => normalizePath(pathname),
|
|
249
|
+
[pathname],
|
|
250
|
+
);
|
|
251
|
+
const header02Data = data?.header02 as HeaderDD02Data | undefined;
|
|
222
252
|
const cartVisibilityConfig =
|
|
223
|
-
header02Data?.cartButtonVisibility ??
|
|
224
|
-
|
|
225
|
-
const
|
|
253
|
+
header02Data?.cartButtonVisibility ??
|
|
254
|
+
(data as any)?.header02CartButtonVisibility;
|
|
255
|
+
const normalizedPathname = useMemo(() => normalizePath(pathname), [pathname]);
|
|
256
|
+
const cartVisibilityPaths = useMemo<string[]>(
|
|
226
257
|
() =>
|
|
227
|
-
(cartVisibilityConfig?.paths ?? [])
|
|
258
|
+
((cartVisibilityConfig?.paths ?? []) as CartVisibilityEntry[])
|
|
228
259
|
.map((entry) => normalizePath(entry?.path))
|
|
229
260
|
.filter((path): path is string => Boolean(path)),
|
|
230
|
-
[cartVisibilityConfig?.paths]
|
|
231
|
-
)
|
|
232
|
-
const cartVisibilityMode = cartVisibilityConfig?.mode ??
|
|
261
|
+
[cartVisibilityConfig?.paths],
|
|
262
|
+
);
|
|
263
|
+
const cartVisibilityMode = cartVisibilityConfig?.mode ?? "always";
|
|
233
264
|
const showCartButton = useMemo(() => {
|
|
234
|
-
if (cartVisibilityMode ===
|
|
235
|
-
const matches = cartVisibilityPaths.includes(normalizedPathname)
|
|
236
|
-
return cartVisibilityMode ===
|
|
237
|
-
}, [cartVisibilityMode, cartVisibilityPaths, normalizedPathname])
|
|
265
|
+
if (cartVisibilityMode === "always") return true;
|
|
266
|
+
const matches = cartVisibilityPaths.includes(normalizedPathname);
|
|
267
|
+
return cartVisibilityMode === "hide" ? !matches : matches;
|
|
268
|
+
}, [cartVisibilityMode, cartVisibilityPaths, normalizedPathname]);
|
|
238
269
|
|
|
239
270
|
// Effect for scroll hide/show behavior
|
|
240
271
|
useEffect(() => {
|
|
241
|
-
if (!enableHideOnScroll) return
|
|
272
|
+
if (!enableHideOnScroll) return;
|
|
242
273
|
|
|
243
274
|
const handleScroll = () => {
|
|
244
|
-
const currentScrollY = window.scrollY
|
|
245
|
-
setIsHidden(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
275
|
+
const currentScrollY = window.scrollY;
|
|
276
|
+
setIsHidden(
|
|
277
|
+
currentScrollY > lastScrollY.current &&
|
|
278
|
+
currentScrollY > scrollThreshold,
|
|
279
|
+
);
|
|
280
|
+
lastScrollY.current = currentScrollY;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
284
|
+
return () => window.removeEventListener("scroll", handleScroll);
|
|
285
|
+
}, [enableHideOnScroll, scrollThreshold]);
|
|
252
286
|
|
|
253
287
|
// Extract navItems from header02 data
|
|
254
|
-
const navItems = data?.header02?.navItems ||
|
|
288
|
+
const navItems: HeaderNavItem[] = (data?.header02?.navItems ||
|
|
289
|
+
(data as any)?.navItems ||
|
|
290
|
+
[]) as HeaderNavItem[];
|
|
255
291
|
|
|
256
292
|
return (
|
|
257
293
|
<>
|
|
@@ -259,74 +295,85 @@ export default function HeaderDD02({ data, variant = 'default', sticky = true }:
|
|
|
259
295
|
{dropdownStyles}
|
|
260
296
|
</style>
|
|
261
297
|
{/* Desktop Navigation - Hidden on mobile */}
|
|
262
|
-
<header className="header-02 fixed container left-0 right-0 mx-auto w-screen mt-2 justify-
|
|
298
|
+
<header className="header-02 fixed container left-0 right-0 mx-auto w-screen mt-2 justify-center md:flex">
|
|
263
299
|
<motion.nav
|
|
264
300
|
initial={{ opacity: 1, y: 0 }}
|
|
265
301
|
animate={{ opacity: 1, y: enableHideOnScroll && isHidden ? -100 : 0 }}
|
|
266
302
|
transition={{ duration: animationDuration }}
|
|
267
|
-
className={`navbar fixed top-0 z-50 right-0 ${variant ===
|
|
303
|
+
className={`navbar fixed top-0 z-50 right-0 ${variant === "default" ? "text-left" : ""} ${sticky ? "sticky top-0 z-50" : ""} transition-transform duration-300 ease-out`}
|
|
268
304
|
id="topnav"
|
|
269
305
|
>
|
|
270
306
|
<div className="nav">
|
|
271
307
|
<NavigationMenu
|
|
272
308
|
viewport={false}
|
|
273
|
-
className="header02-navigation-menu shadow-glass dark:border-neutral-700 !
|
|
309
|
+
className="header02-navigation-menu shadow-glass dark:border-neutral-700 flex! w-full items-center justify-between rounded-xl border px-6 py-0 backdrop-blur-md relative"
|
|
274
310
|
>
|
|
275
|
-
<NavigationMenuList className="
|
|
311
|
+
<NavigationMenuList className="gap-4 md:gap-6 flex">
|
|
276
312
|
{navItems.map((navItem, index) => {
|
|
277
313
|
// Simple Link - works
|
|
278
|
-
if (navItem.type ===
|
|
279
|
-
const href = getLinkHref(navItem.link)
|
|
280
|
-
const normalizedHref = normalizePath(href)
|
|
281
|
-
const isActive =
|
|
282
|
-
|
|
283
|
-
const
|
|
284
|
-
|
|
314
|
+
if (navItem.type === "link") {
|
|
315
|
+
const href = getLinkHref(navItem.link);
|
|
316
|
+
const normalizedHref = normalizePath(href);
|
|
317
|
+
const isActive =
|
|
318
|
+
normalizedHref === normalizedCurrentPathname;
|
|
319
|
+
const icon = renderIcon(
|
|
320
|
+
navItem.link?.icon,
|
|
321
|
+
undefined,
|
|
322
|
+
(navItem.link as any)?.customIcon,
|
|
323
|
+
);
|
|
324
|
+
const iconPosition =
|
|
325
|
+
(navItem.link as any)?.iconPosition || "before";
|
|
326
|
+
const showLabel = navItem.showLabel ?? true;
|
|
285
327
|
return (
|
|
286
328
|
<NavigationMenuItem key={index}>
|
|
287
329
|
<NavigationMenuLink
|
|
288
330
|
asChild
|
|
289
331
|
className={cn(
|
|
290
|
-
|
|
332
|
+
"header02-nav-btn !flex flex-row items-center justify-center px-0 bg-transparent text-stone-200 dark:text-stone-200 gap-0 text-sm transition-colors focus:outline-none",
|
|
291
333
|
isActive &&
|
|
292
|
-
|
|
334
|
+
"header02-nav-btn-active text-stone-200 dark:text-stone-200 font-semibold",
|
|
293
335
|
)}
|
|
294
336
|
>
|
|
295
337
|
<Link
|
|
296
338
|
href={href}
|
|
297
339
|
{...(navItem.link?.newTab
|
|
298
|
-
? { target:
|
|
340
|
+
? { target: "_blank", rel: "noopener noreferrer" }
|
|
299
341
|
: {})}
|
|
300
342
|
className="flex items-center gap-1 h-[2.5rem] card-bottom-lit"
|
|
301
343
|
>
|
|
302
|
-
{icon && iconPosition ===
|
|
344
|
+
{icon && iconPosition === "before" && icon}
|
|
303
345
|
{showLabel && navItem.link?.label}
|
|
304
|
-
{icon && iconPosition ===
|
|
346
|
+
{icon && iconPosition === "after" && icon}
|
|
305
347
|
</Link>
|
|
306
348
|
</NavigationMenuLink>
|
|
307
349
|
</NavigationMenuItem>
|
|
308
|
-
)
|
|
350
|
+
);
|
|
309
351
|
}
|
|
310
352
|
|
|
311
353
|
// Dropdown with RichText + Links (Variant 1) - works
|
|
312
|
-
if (navItem.type ===
|
|
313
|
-
const triggerIcon = renderIcon(navItem.triggerIcon)
|
|
314
|
-
const triggerIconPosition =
|
|
354
|
+
if (navItem.type === "dropdown-richtext") {
|
|
355
|
+
const triggerIcon = renderIcon(navItem.triggerIcon);
|
|
356
|
+
const triggerIconPosition =
|
|
357
|
+
navItem.triggerIconPosition || "before";
|
|
315
358
|
return (
|
|
316
359
|
<NavigationMenuItem
|
|
317
360
|
key={index}
|
|
318
361
|
className="header02-nav-btn card-bottom-lit bg-transparent text-stone-200 dark:text-stone-200"
|
|
319
362
|
>
|
|
320
363
|
<NavigationMenuTrigger className="flex items-center gap-2">
|
|
321
|
-
{triggerIcon &&
|
|
364
|
+
{triggerIcon &&
|
|
365
|
+
triggerIconPosition === "before" &&
|
|
366
|
+
triggerIcon}
|
|
322
367
|
{navItem.label}
|
|
323
|
-
{triggerIcon &&
|
|
368
|
+
{triggerIcon &&
|
|
369
|
+
triggerIconPosition === "after" &&
|
|
370
|
+
triggerIcon}
|
|
324
371
|
</NavigationMenuTrigger>
|
|
325
372
|
<NavigationMenuContent className="header02-dropdown">
|
|
326
373
|
<motion.div
|
|
327
374
|
initial={{ opacity: 0, x: -50 }}
|
|
328
375
|
animate={{ opacity: 1, x: 0 }}
|
|
329
|
-
transition={{ duration: 0.2, ease:
|
|
376
|
+
transition={{ duration: 0.2, ease: "easeOut" }}
|
|
330
377
|
className="border border-border rounded-md overflow-hidden grid gap-3 pt-0 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]"
|
|
331
378
|
>
|
|
332
379
|
<div className="row-span-4">
|
|
@@ -344,165 +391,202 @@ export default function HeaderDD02({ data, variant = 'default', sticky = true }:
|
|
|
344
391
|
</NavigationMenuLink>
|
|
345
392
|
</div>
|
|
346
393
|
<ul className="flex flex-col gap-2 pt-4 p-1">
|
|
347
|
-
{navItem.v1_children?.map(
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
child.link
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
394
|
+
{navItem.v1_children?.map(
|
|
395
|
+
(
|
|
396
|
+
child: HeaderChildItem,
|
|
397
|
+
childIndex: number,
|
|
398
|
+
) => (
|
|
399
|
+
<ListItem
|
|
400
|
+
key={childIndex}
|
|
401
|
+
title={child.link?.label || ""}
|
|
402
|
+
href={getLinkHref(child.link)}
|
|
403
|
+
icon={renderIcon(
|
|
404
|
+
child.link?.icon,
|
|
405
|
+
undefined,
|
|
406
|
+
child.link?.customIcon,
|
|
407
|
+
)}
|
|
408
|
+
iconPosition={
|
|
409
|
+
child.link?.iconPosition || "before"
|
|
410
|
+
}
|
|
411
|
+
className="text-sm"
|
|
412
|
+
>
|
|
413
|
+
{child.description}
|
|
414
|
+
</ListItem>
|
|
415
|
+
),
|
|
416
|
+
)}
|
|
363
417
|
</ul>
|
|
364
418
|
</motion.div>
|
|
365
419
|
</NavigationMenuContent>
|
|
366
420
|
</NavigationMenuItem>
|
|
367
|
-
)
|
|
421
|
+
);
|
|
368
422
|
}
|
|
369
423
|
|
|
370
424
|
// Dropdown - Single Column with Description (Variant 4) - works
|
|
371
|
-
if (navItem.type ===
|
|
372
|
-
const triggerIcon = renderIcon(navItem.triggerIcon)
|
|
373
|
-
const triggerIconPosition =
|
|
425
|
+
if (navItem.type === "dropdown-single-desc") {
|
|
426
|
+
const triggerIcon = renderIcon(navItem.triggerIcon);
|
|
427
|
+
const triggerIconPosition =
|
|
428
|
+
navItem.triggerIconPosition || "before";
|
|
374
429
|
return (
|
|
375
430
|
<NavigationMenuItem
|
|
376
431
|
key={index}
|
|
377
432
|
className="header02-nav-btn card-bottom-lit text-stone-200 dark:text-stone-200"
|
|
378
433
|
>
|
|
379
434
|
<NavigationMenuTrigger className="flex items-center gap-2">
|
|
380
|
-
{triggerIcon &&
|
|
435
|
+
{triggerIcon &&
|
|
436
|
+
triggerIconPosition === "before" &&
|
|
437
|
+
triggerIcon}
|
|
381
438
|
{navItem.label}
|
|
382
|
-
{triggerIcon &&
|
|
439
|
+
{triggerIcon &&
|
|
440
|
+
triggerIconPosition === "after" &&
|
|
441
|
+
triggerIcon}
|
|
383
442
|
</NavigationMenuTrigger>
|
|
384
443
|
<NavigationMenuContent className="p-0">
|
|
385
444
|
<motion.ul
|
|
386
445
|
initial={{ opacity: 0, x: -50 }}
|
|
387
446
|
animate={{ opacity: 1, x: 0 }}
|
|
388
|
-
transition={{ duration: 0.2, ease:
|
|
447
|
+
transition={{ duration: 0.2, ease: "easeOut" }}
|
|
389
448
|
className="bg-7 border border-border rounded-md overflow-hidden grid gap-3 p-1 w-fit md:w-[14rem]"
|
|
390
449
|
>
|
|
391
|
-
{navItem.
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
450
|
+
{navItem.v4_children?.map(
|
|
451
|
+
(child: HeaderChildItem, childIndex: number) => (
|
|
452
|
+
<ListItem
|
|
453
|
+
key={childIndex}
|
|
454
|
+
title={child.link?.label || ""}
|
|
455
|
+
href={getLinkHref(child.link)}
|
|
456
|
+
icon={renderIcon(
|
|
457
|
+
child.link?.icon,
|
|
458
|
+
undefined,
|
|
459
|
+
child.link?.customIcon,
|
|
460
|
+
)}
|
|
461
|
+
iconPosition={
|
|
462
|
+
child.link?.iconPosition || "before"
|
|
463
|
+
}
|
|
464
|
+
>
|
|
465
|
+
{child.description}
|
|
466
|
+
</ListItem>
|
|
467
|
+
),
|
|
468
|
+
)}
|
|
406
469
|
</motion.ul>
|
|
407
470
|
</NavigationMenuContent>
|
|
408
471
|
</NavigationMenuItem>
|
|
409
|
-
)
|
|
472
|
+
);
|
|
410
473
|
}
|
|
411
474
|
|
|
412
475
|
// Dropdown - Single Column no Description (Variant 5) - works
|
|
413
|
-
if (navItem.type ===
|
|
414
|
-
const triggerIcon = renderIcon(navItem.triggerIcon)
|
|
415
|
-
const triggerIconPosition =
|
|
476
|
+
if (navItem.type === "dropdown-single") {
|
|
477
|
+
const triggerIcon = renderIcon(navItem.triggerIcon);
|
|
478
|
+
const triggerIconPosition =
|
|
479
|
+
navItem.triggerIconPosition || "before";
|
|
416
480
|
return (
|
|
417
481
|
<NavigationMenuItem
|
|
418
482
|
key={index}
|
|
419
483
|
className="header02-nav-btn card-bottom-lit bg-transparent text-stone-200 dark:text-stone-200"
|
|
420
484
|
>
|
|
421
485
|
<NavigationMenuTrigger className="flex items-center gap-2">
|
|
422
|
-
{triggerIcon &&
|
|
486
|
+
{triggerIcon &&
|
|
487
|
+
triggerIconPosition === "before" &&
|
|
488
|
+
triggerIcon}
|
|
423
489
|
{navItem.label}
|
|
424
|
-
{triggerIcon &&
|
|
490
|
+
{triggerIcon &&
|
|
491
|
+
triggerIconPosition === "after" &&
|
|
492
|
+
triggerIcon}
|
|
425
493
|
</NavigationMenuTrigger>
|
|
426
494
|
<NavigationMenuContent className="p-0">
|
|
427
495
|
<motion.ul
|
|
428
496
|
initial={{ opacity: 0, x: -50 }}
|
|
429
497
|
animate={{ opacity: 1, x: 0 }}
|
|
430
|
-
transition={{ duration: 0.2, ease:
|
|
498
|
+
transition={{ duration: 0.2, ease: "easeOut" }}
|
|
431
499
|
className="bg-7 border border-border rounded-md overflow-hidden flex flex-col gap-2 p-1 w-fit"
|
|
432
500
|
>
|
|
433
|
-
{navItem.v5_children?.map(
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
501
|
+
{navItem.v5_children?.map(
|
|
502
|
+
(child: HeaderChildItem, childIndex: number) => (
|
|
503
|
+
<ListItem
|
|
504
|
+
key={childIndex}
|
|
505
|
+
title={child.link?.label || ""}
|
|
506
|
+
href={getLinkHref(child.link)}
|
|
507
|
+
icon={renderIcon(
|
|
508
|
+
child.link?.icon,
|
|
509
|
+
undefined,
|
|
510
|
+
child.link?.customIcon,
|
|
511
|
+
)}
|
|
512
|
+
iconPosition={
|
|
513
|
+
child.link?.iconPosition || "before"
|
|
514
|
+
}
|
|
515
|
+
/>
|
|
516
|
+
),
|
|
517
|
+
)}
|
|
446
518
|
</motion.ul>
|
|
447
519
|
</NavigationMenuContent>
|
|
448
520
|
</NavigationMenuItem>
|
|
449
|
-
)
|
|
521
|
+
);
|
|
450
522
|
}
|
|
451
523
|
|
|
452
524
|
// Dropdown - Single Column with Icons (Variant 6) - works
|
|
453
|
-
if (navItem.type ===
|
|
454
|
-
const triggerIcon = renderIcon(navItem.triggerIcon)
|
|
455
|
-
const triggerIconPosition =
|
|
525
|
+
if (navItem.type === "dropdown-icons") {
|
|
526
|
+
const triggerIcon = renderIcon(navItem.triggerIcon);
|
|
527
|
+
const triggerIconPosition =
|
|
528
|
+
navItem.triggerIconPosition || "before";
|
|
456
529
|
return (
|
|
457
530
|
<NavigationMenuItem
|
|
458
531
|
key={index}
|
|
459
532
|
className="header02-nav-btn card-bottom-lit bg-transparent text-stone-200 dark:text-stone-200"
|
|
460
533
|
>
|
|
461
534
|
<NavigationMenuTrigger className="flex items-center gap-2">
|
|
462
|
-
{triggerIcon &&
|
|
535
|
+
{triggerIcon &&
|
|
536
|
+
triggerIconPosition === "before" &&
|
|
537
|
+
triggerIcon}
|
|
463
538
|
{navItem.label}
|
|
464
|
-
{triggerIcon &&
|
|
539
|
+
{triggerIcon &&
|
|
540
|
+
triggerIconPosition === "after" &&
|
|
541
|
+
triggerIcon}
|
|
465
542
|
</NavigationMenuTrigger>
|
|
466
543
|
<NavigationMenuContent className="p-0">
|
|
467
544
|
<motion.ul
|
|
468
545
|
initial={{ opacity: 0, x: -50 }}
|
|
469
546
|
animate={{ opacity: 1, x: 0 }}
|
|
470
|
-
transition={{ duration: 0.2, ease:
|
|
547
|
+
transition={{ duration: 0.2, ease: "easeOut" }}
|
|
471
548
|
className="bg-7 border border-border rounded-md overflow-hidden flex flex-col gap-2 p-1 w-fit"
|
|
472
549
|
>
|
|
473
|
-
{navItem.v6_children?.map(
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
{
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
550
|
+
{navItem.v6_children?.map(
|
|
551
|
+
(child: HeaderChildItem, childIndex: number) => {
|
|
552
|
+
const icon = renderIcon(
|
|
553
|
+
child.link?.icon,
|
|
554
|
+
"size-4",
|
|
555
|
+
child.link?.customIcon,
|
|
556
|
+
);
|
|
557
|
+
const iconPosition =
|
|
558
|
+
child.link?.iconPosition || "before";
|
|
559
|
+
return (
|
|
560
|
+
<li key={childIndex}>
|
|
561
|
+
<NavigationMenuLink asChild>
|
|
562
|
+
<Link
|
|
563
|
+
href={getLinkHref(child.link)}
|
|
564
|
+
className={cn(
|
|
565
|
+
"flex items-center gap-2 select-none no-underline outline-none transition-colors rounded-sm group bg-8 hover:bg-zinc-600/50 !hover:text-stone-200 focus:bg-accent focus:text-stone-200 py-2 px-3",
|
|
566
|
+
)}
|
|
567
|
+
>
|
|
568
|
+
{" "}
|
|
569
|
+
{icon &&
|
|
570
|
+
iconPosition === "before" &&
|
|
571
|
+
icon}
|
|
572
|
+
<span className="!text-xs group font-medium leading-none scale-75 !hover:text-stone-200 ">
|
|
573
|
+
{child.link?.label}
|
|
574
|
+
</span>
|
|
575
|
+
{icon &&
|
|
576
|
+
iconPosition === "after" &&
|
|
577
|
+
icon}
|
|
578
|
+
</Link>
|
|
579
|
+
</NavigationMenuLink>
|
|
580
|
+
</li>
|
|
581
|
+
);
|
|
582
|
+
},
|
|
583
|
+
)}
|
|
500
584
|
</motion.ul>
|
|
501
585
|
</NavigationMenuContent>
|
|
502
586
|
</NavigationMenuItem>
|
|
503
|
-
)
|
|
587
|
+
);
|
|
504
588
|
}
|
|
505
|
-
return null
|
|
589
|
+
return null;
|
|
506
590
|
})}
|
|
507
591
|
</NavigationMenuList>
|
|
508
592
|
</NavigationMenu>
|
|
@@ -513,5 +597,5 @@ export default function HeaderDD02({ data, variant = 'default', sticky = true }:
|
|
|
513
597
|
{/* Mobile Bottom Navigation - Visible only on mobile */}
|
|
514
598
|
<MobileBottomNav data={data} />
|
|
515
599
|
</>
|
|
516
|
-
)
|
|
600
|
+
);
|
|
517
601
|
}
|