@olympusoss/canvas 2.9.0 → 2.10.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/package.json
CHANGED
|
@@ -16,6 +16,13 @@ export interface NavBarProps extends React.HTMLAttributes<HTMLElement> {
|
|
|
16
16
|
logo?: React.ReactNode;
|
|
17
17
|
links?: NavLink[];
|
|
18
18
|
actions?: React.ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* When `true` (default) the bar uses `position: sticky` + `top: 0` so it
|
|
21
|
+
* stays anchored to the top of its scroll container while page content
|
|
22
|
+
* scrolls underneath. The translucent background + `backdrop-blur` creates
|
|
23
|
+
* a frosted-glass effect through which the scrolling content reads.
|
|
24
|
+
* Set `false` to render inline (e.g. for examples or non-sticky layouts).
|
|
25
|
+
*/
|
|
19
26
|
sticky?: boolean;
|
|
20
27
|
/** Custom link component (e.g. Next.js Link) for client-side navigation */
|
|
21
28
|
linkComponent?: React.ElementType;
|
|
@@ -27,72 +34,68 @@ const NavBar = React.forwardRef<HTMLElement, NavBarProps>(
|
|
|
27
34
|
const LinkEl = linkComponent || "a";
|
|
28
35
|
|
|
29
36
|
return (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
<nav
|
|
38
|
+
ref={ref}
|
|
39
|
+
className={cn(
|
|
40
|
+
"z-50 border-b border-border bg-background/80 backdrop-blur",
|
|
41
|
+
sticky && "sticky top-0",
|
|
42
|
+
className,
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
>
|
|
46
|
+
<div className="mx-auto flex h-14 max-w-6xl items-center justify-between px-4 sm:px-6">
|
|
47
|
+
{/* Logo */}
|
|
48
|
+
{logo && <div className="shrink-0">{logo}</div>}
|
|
49
|
+
|
|
50
|
+
{/* Desktop links */}
|
|
51
|
+
<div className="hidden items-center gap-6 md:flex">
|
|
52
|
+
{links.map((link) => (
|
|
53
|
+
<LinkEl
|
|
54
|
+
key={link.href}
|
|
55
|
+
href={link.href}
|
|
56
|
+
className="text-sm font-medium text-muted-foreground no-underline transition-colors hover:text-brand"
|
|
57
|
+
{...(link.external ? { target: "_blank", rel: "noopener noreferrer" } : {})}
|
|
58
|
+
>
|
|
59
|
+
{link.label}
|
|
60
|
+
</LinkEl>
|
|
61
|
+
))}
|
|
62
|
+
{actions}
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
{/* Mobile hamburger */}
|
|
66
|
+
{(links.length > 0 || actions) && (
|
|
67
|
+
<Button
|
|
68
|
+
variant="ghost"
|
|
69
|
+
size="icon"
|
|
70
|
+
className="md:hidden"
|
|
71
|
+
onClick={() => setMobileOpen((v) => !v)}
|
|
72
|
+
aria-label={mobileOpen ? "Close menu" : "Open menu"}
|
|
73
|
+
>
|
|
74
|
+
{mobileOpen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
|
|
75
|
+
</Button>
|
|
37
76
|
)}
|
|
38
|
-
|
|
39
|
-
>
|
|
40
|
-
<div className="mx-auto flex h-14 max-w-6xl items-center justify-between px-4 sm:px-6">
|
|
41
|
-
{/* Logo */}
|
|
42
|
-
{logo && <div className="shrink-0">{logo}</div>}
|
|
77
|
+
</div>
|
|
43
78
|
|
|
44
|
-
|
|
45
|
-
|
|
79
|
+
{/* Mobile dropdown */}
|
|
80
|
+
{mobileOpen && (
|
|
81
|
+
<div className="border-t md:hidden">
|
|
82
|
+
<div className="flex flex-col gap-1 px-4 py-3 sm:px-6">
|
|
46
83
|
{links.map((link) => (
|
|
47
84
|
<LinkEl
|
|
48
85
|
key={link.href}
|
|
49
86
|
href={link.href}
|
|
50
|
-
|
|
87
|
+
onClick={() => setMobileOpen(false)}
|
|
88
|
+
className="rounded-md px-3 py-2.5 text-sm font-medium text-muted-foreground no-underline transition-colors hover:bg-accent hover:text-brand"
|
|
51
89
|
{...(link.external ? { target: "_blank", rel: "noopener noreferrer" } : {})}
|
|
52
90
|
>
|
|
53
91
|
{link.label}
|
|
54
92
|
</LinkEl>
|
|
55
93
|
))}
|
|
56
|
-
{actions}
|
|
94
|
+
{actions && <div className="px-3 py-2.5">{actions}</div>}
|
|
57
95
|
</div>
|
|
58
|
-
|
|
59
|
-
{/* Mobile hamburger */}
|
|
60
|
-
{(links.length > 0 || actions) && (
|
|
61
|
-
<Button
|
|
62
|
-
variant="ghost"
|
|
63
|
-
size="icon"
|
|
64
|
-
className="md:hidden"
|
|
65
|
-
onClick={() => setMobileOpen((v) => !v)}
|
|
66
|
-
aria-label={mobileOpen ? "Close menu" : "Open menu"}
|
|
67
|
-
>
|
|
68
|
-
{mobileOpen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
|
|
69
|
-
</Button>
|
|
70
|
-
)}
|
|
71
96
|
</div>
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
{mobileOpen && (
|
|
75
|
-
<div className="border-t md:hidden">
|
|
76
|
-
<div className="flex flex-col gap-1 px-4 py-3 sm:px-6">
|
|
77
|
-
{links.map((link) => (
|
|
78
|
-
<LinkEl
|
|
79
|
-
key={link.href}
|
|
80
|
-
href={link.href}
|
|
81
|
-
onClick={() => setMobileOpen(false)}
|
|
82
|
-
className="rounded-md px-3 py-2.5 text-sm font-medium text-muted-foreground no-underline transition-colors hover:bg-accent hover:text-brand"
|
|
83
|
-
{...(link.external ? { target: "_blank", rel: "noopener noreferrer" } : {})}
|
|
84
|
-
>
|
|
85
|
-
{link.label}
|
|
86
|
-
</LinkEl>
|
|
87
|
-
))}
|
|
88
|
-
{actions && <div className="px-3 py-2.5">{actions}</div>}
|
|
89
|
-
</div>
|
|
90
|
-
</div>
|
|
91
|
-
)}
|
|
92
|
-
</nav>
|
|
93
|
-
{/* Spacer when sticky to prevent content from hiding behind navbar */}
|
|
94
|
-
{sticky && <div className="h-14" />}
|
|
95
|
-
</>
|
|
97
|
+
)}
|
|
98
|
+
</nav>
|
|
96
99
|
);
|
|
97
100
|
},
|
|
98
101
|
);
|