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.
@@ -1,30 +1,39 @@
1
- 'use client'
1
+ "use client";
2
2
 
3
- import { usePathname } from 'next/navigation'
4
- import React, { useEffect, useMemo, useState, useRef } from 'react'
5
- import Link from 'next/link'
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 '@/components/ui/navigation-menu'
14
- import { CircleCheck, CircleHelp, Circle, Home, User, Settings, Mail, Phone } from 'lucide-react'
15
- import type { Header as HeaderType } from '@/payload-types'
16
- import RichText from '@/components/RichText'
17
- import { cn } from '@/utilities/ui'
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 'lucide-react'
20
- import { motion } from 'framer-motion'
28
+ import * as LucideIcons from "lucide-react";
29
+ import { motion } from "framer-motion";
21
30
 
22
- import MobileBottomNav from './MobileBottomNav'
31
+ import MobileBottomNav from "./MobileBottomNav";
23
32
 
24
33
  interface HeaderDD02Props {
25
- data?: HeaderType
26
- variant?: 'default' | 'centered' | 'fullwidth'
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 = 'always' | 'hide' | 'show'
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 HeaderDD02Data = HeaderType['header02'] & {
53
- cartButtonVisibility?: CartVisibilityConfig
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 !== '/' && base.endsWith('/')) {
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 === 'reference') {
70
- const value = link.reference?.value
71
- if (typeof value === 'object' && 'slug' in 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 === 'string') return customIcon
81
- if (typeof customIcon === 'number') return null
82
- if (typeof customIcon === 'object' && 'url' in customIcon && typeof customIcon.url === 'string') {
83
- return customIcon.url
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 = (iconName?: string | null, className?: string, customIcon?: any) => {
90
- const customUrl = resolveCustomIconUrl(customIcon)
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 || 'size-4'}
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 || 'size-4'} />
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 = 'before'
163
+ iconPosition = "before",
141
164
  }: {
142
- title: string
143
- children?: React.ReactNode
144
- href: string
145
- className?: string
146
- icon?: React.ReactNode
147
- iconPosition?: 'before' | 'after'
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
- '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',
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 === 'before' && icon}
183
+ {icon && iconPosition === "before" && icon}
161
184
  {title}
162
- {icon && iconPosition === 'after' && icon}
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: '1.3' }}
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
- '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',
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({ data, variant = 'default', sticky = true }: HeaderDD02Props) {
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(() => normalizePath(pathname), [pathname])
221
- const header02Data = data?.header02 as HeaderDD02Data | undefined
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 ?? (data as any)?.header02CartButtonVisibility
224
- const normalizedPathname = useMemo(() => normalizePath(pathname), [pathname])
225
- const cartVisibilityPaths = useMemo(
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 ?? 'always'
261
+ [cartVisibilityConfig?.paths],
262
+ );
263
+ const cartVisibilityMode = cartVisibilityConfig?.mode ?? "always";
233
264
  const showCartButton = useMemo(() => {
234
- if (cartVisibilityMode === 'always') return true
235
- const matches = cartVisibilityPaths.includes(normalizedPathname)
236
- return cartVisibilityMode === 'hide' ? !matches : matches
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(currentScrollY > lastScrollY.current && currentScrollY > scrollThreshold)
246
- lastScrollY.current = currentScrollY
247
- }
248
-
249
- window.addEventListener('scroll', handleScroll, { passive: true })
250
- return () => window.removeEventListener('scroll', handleScroll)
251
- }, [enableHideOnScroll, scrollThreshold])
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 || (data as any)?.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-between hidden md:flex">
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 === 'default' ? 'text-left' : ''} ${sticky ? 'sticky top-0 z-50' : ''} transition-transform duration-300 ease-out`}
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 !flex w-full items-center justify-between rounded-2xl border px-5 py-0 pr-2.5 backdrop-blur-md relative"
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="ml-2 gap-4 md:gap-6 flex">
311
+ <NavigationMenuList className="gap-4 md:gap-6 flex">
276
312
  {navItems.map((navItem, index) => {
277
313
  // Simple Link - works
278
- if (navItem.type === 'link') {
279
- const href = getLinkHref(navItem.link)
280
- const normalizedHref = normalizePath(href)
281
- const isActive = normalizedHref === normalizedCurrentPathname
282
- const icon = renderIcon(navItem.link?.icon, undefined, navItem.link?.customIcon)
283
- const iconPosition = navItem.link?.iconPosition || 'before'
284
- const showLabel = navItem.showLabel ?? true
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
- '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',
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
- 'header02-nav-btn-active text-stone-200 dark:text-stone-200 font-semibold'
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: '_blank', rel: 'noopener noreferrer' }
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 === 'before' && icon}
344
+ {icon && iconPosition === "before" && icon}
303
345
  {showLabel && navItem.link?.label}
304
- {icon && iconPosition === 'after' && icon}
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 === 'dropdown-richtext') {
313
- const triggerIcon = renderIcon(navItem.triggerIcon)
314
- const triggerIconPosition = navItem.triggerIconPosition || 'before'
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 && triggerIconPosition === 'before' && triggerIcon}
364
+ {triggerIcon &&
365
+ triggerIconPosition === "before" &&
366
+ triggerIcon}
322
367
  {navItem.label}
323
- {triggerIcon && triggerIconPosition === 'after' && 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: 'easeOut' }}
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((child, childIndex) => (
348
- <ListItem
349
- key={childIndex}
350
- title={child.link?.label || ''}
351
- href={getLinkHref(child.link)}
352
- icon={renderIcon(
353
- child.link?.icon,
354
- undefined,
355
- child.link?.customIcon
356
- )}
357
- iconPosition={child.link?.iconPosition || 'before'}
358
- className="text-sm"
359
- >
360
- {child.description}
361
- </ListItem>
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 === 'dropdown-single-desc') {
372
- const triggerIcon = renderIcon(navItem.triggerIcon)
373
- const triggerIconPosition = navItem.triggerIconPosition || 'before'
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 && triggerIconPosition === 'before' && triggerIcon}
435
+ {triggerIcon &&
436
+ triggerIconPosition === "before" &&
437
+ triggerIcon}
381
438
  {navItem.label}
382
- {triggerIcon && triggerIconPosition === 'after' && 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: 'easeOut' }}
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.v1_children?.map((child, childIndex) => (
392
- <ListItem
393
- key={childIndex}
394
- title={child.link?.label || ''}
395
- href={getLinkHref(child.link)}
396
- icon={renderIcon(
397
- child.link?.icon,
398
- undefined,
399
- child.link?.customIcon
400
- )}
401
- iconPosition={child.link?.iconPosition || 'before'}
402
- >
403
- {child.description}
404
- </ListItem>
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 === 'dropdown-single') {
414
- const triggerIcon = renderIcon(navItem.triggerIcon)
415
- const triggerIconPosition = navItem.triggerIconPosition || 'before'
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 && triggerIconPosition === 'before' && triggerIcon}
486
+ {triggerIcon &&
487
+ triggerIconPosition === "before" &&
488
+ triggerIcon}
423
489
  {navItem.label}
424
- {triggerIcon && triggerIconPosition === 'after' && 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: 'easeOut' }}
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((child, childIndex) => (
434
- <ListItem
435
- key={childIndex}
436
- title={child.link?.label || ''}
437
- href={getLinkHref(child.link)}
438
- icon={renderIcon(
439
- child.link?.icon,
440
- undefined,
441
- child.link?.customIcon
442
- )}
443
- iconPosition={child.link?.iconPosition || 'before'}
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 === 'dropdown-icons') {
454
- const triggerIcon = renderIcon(navItem.triggerIcon)
455
- const triggerIconPosition = navItem.triggerIconPosition || 'before'
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 && triggerIconPosition === 'before' && triggerIcon}
535
+ {triggerIcon &&
536
+ triggerIconPosition === "before" &&
537
+ triggerIcon}
463
538
  {navItem.label}
464
- {triggerIcon && triggerIconPosition === 'after' && 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: 'easeOut' }}
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((child, childIndex) => {
474
- const icon = renderIcon(
475
- child.link?.icon,
476
- 'size-4',
477
- child.link?.customIcon
478
- )
479
- const iconPosition = child.link?.iconPosition || 'before'
480
- return (
481
- <li key={childIndex}>
482
- <NavigationMenuLink asChild>
483
- <Link
484
- href={getLinkHref(child.link)}
485
- className={cn(
486
- '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'
487
- )}
488
- >
489
- {' '}
490
- {icon && iconPosition === 'before' && icon}
491
- <span className="!text-xs group font-medium leading-none scale-75 !hover:text-stone-200 ">
492
- {child.link?.label}
493
- </span>
494
- {icon && iconPosition === 'after' && icon}
495
- </Link>
496
- </NavigationMenuLink>
497
- </li>
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
  }