@syscore/ui-library 1.3.6 → 1.4.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/client/components/icons/ConceptIcons.tsx +2 -74
- package/client/components/ui/hero-section.tsx +45 -0
- package/client/components/ui/navigation.tsx +237 -76
- package/client/components/ui/standard-navigation.tsx +578 -0
- package/client/global.css +15 -3
- package/client/lib/concept-colors.ts +115 -0
- package/client/lib/concept-icons.ts +45 -0
- package/client/ui/Card.stories.tsx +2 -98
- package/client/ui/Hero.stories.tsx +72 -0
- package/client/ui/Navigation.stories.tsx +30 -21
- package/client/ui/Panel.stories.tsx +480 -0
- package/client/ui/StandardNavigation.stories.tsx +399 -0
- package/dist/img/half-sphere.png +0 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.es.js +764 -189
- package/package.json +1 -1
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { AnimatePresence, motion } from "motion/react";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import { UtilityClose } from "../icons/UtilityClose";
|
|
5
|
+
import { NavBullet } from "../icons/NavBullet";
|
|
6
|
+
import { Button } from "@/components/ui/button";
|
|
7
|
+
import { StandardLogo } from "../icons/StandardLogo";
|
|
8
|
+
import { NavLogo } from "../icons/NavLogo";
|
|
9
|
+
|
|
10
|
+
const ALPHA_PATH =
|
|
11
|
+
"M5.3387 0.0229882C5.37971 0.0229882 5.45295 0.0199228 5.5584 0.0137925C5.66386 0.00459714 5.74442 0 5.80007 0C6.72867 0 7.49908 0.269732 8.11131 0.809196C8.72354 1.34559 9.14097 2.09195 9.3636 3.04828C9.44855 3.47433 9.49835 3.96628 9.513 4.52414V5.16782C10.2014 4.07663 10.7287 2.85517 11.0948 1.50345C11.1505 1.29808 11.1959 1.17701 11.2311 1.14023C11.2662 1.10345 11.3761 1.08506 11.5606 1.08506C11.8535 1.08506 12 1.13563 12 1.23678C12 1.25211 11.9722 1.37778 11.9165 1.61379C11.5093 3.24751 10.7931 4.77701 9.76785 6.2023L9.53497 6.51034L9.55694 7.04368C9.59795 8.23295 9.72391 8.9318 9.93482 9.14023C9.97583 9.16782 10.0388 9.18161 10.1238 9.18161C10.32 9.15402 10.5031 9.06973 10.673 8.92874C10.8429 8.78774 10.963 8.62222 11.0333 8.43218C11.0597 8.32797 11.0948 8.26513 11.1388 8.24368C11.1798 8.22222 11.2852 8.21149 11.4551 8.21149C11.7364 8.21149 11.877 8.27739 11.877 8.40919C11.877 8.49808 11.8345 8.63142 11.7495 8.8092C11.5943 9.13103 11.3687 9.4069 11.0729 9.63678C10.777 9.8636 10.4475 9.97701 10.0842 9.97701H9.93482C8.97986 9.97701 8.3398 9.44674 8.01465 8.38621L7.95313 8.23448C7.20615 8.74942 6.79165 9.02835 6.70963 9.07126C5.66679 9.69042 4.61809 10 3.56353 10C2.56756 10 1.76346 9.70575 1.15123 9.11724C0.538997 8.52874 0.162578 7.75632 0.02197 6.8C0.0073233 6.71111 0 6.54866 0 6.31264C0 5.9295 0.02197 5.62146 0.0659099 5.38851C0.276822 4.06437 0.887587 2.8751 1.89821 1.82069C2.91175 0.769348 4.05859 0.170115 5.3387 0.0229882ZM1.83669 7.10805C1.83669 7.73946 1.9978 8.24368 2.32003 8.62069C2.64518 8.99464 3.09484 9.18161 3.66899 9.18161C4.03515 9.18161 4.44379 9.12337 4.89491 9.0069C5.6829 8.80153 6.44892 8.4046 7.19297 7.81609C7.29257 7.74253 7.38777 7.66437 7.47858 7.58161C7.56939 7.50192 7.64262 7.43295 7.69828 7.37471L7.78176 7.28276L7.76419 7.08506C7.76419 6.95326 7.75979 6.75862 7.75101 6.50115C7.74515 6.24368 7.74222 5.98927 7.74222 5.73793C7.72757 5.18008 7.71732 4.74636 7.71146 4.43678C7.70267 4.1272 7.67338 3.74866 7.62358 3.30115C7.57671 2.85364 7.5108 2.50728 7.42585 2.26207C7.3409 2.01992 7.22519 1.77471 7.07873 1.52644C6.92933 1.2751 6.73892 1.09425 6.50751 0.983908C6.27609 0.873563 6.00513 0.81839 5.69462 0.81839C4.72501 0.81839 3.87404 1.35479 3.14171 2.42759C2.79019 2.97318 2.49579 3.71647 2.25851 4.65747C1.9773 5.73333 1.83669 6.55019 1.83669 7.10805Z";
|
|
12
|
+
|
|
13
|
+
const PATH_LENGTH = 60;
|
|
14
|
+
|
|
15
|
+
const AlphaIcon = ({ dark }: { dark?: boolean }) => {
|
|
16
|
+
if (dark) {
|
|
17
|
+
return (
|
|
18
|
+
<svg
|
|
19
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
20
|
+
width="12"
|
|
21
|
+
height="10"
|
|
22
|
+
viewBox="0 0 12 10"
|
|
23
|
+
fill="none"
|
|
24
|
+
className="mt-1.5"
|
|
25
|
+
style={{ overflow: "visible" }}
|
|
26
|
+
>
|
|
27
|
+
<path d={ALPHA_PATH} fill="currentColor" />
|
|
28
|
+
{/* Animated stroke trace */}
|
|
29
|
+
{/* <motion.path
|
|
30
|
+
d={ALPHA_PATH}
|
|
31
|
+
fill="none"
|
|
32
|
+
stroke="#282A31"
|
|
33
|
+
strokeWidth="0.4"
|
|
34
|
+
strokeLinecap="round"
|
|
35
|
+
strokeLinejoin="round"
|
|
36
|
+
initial={{ strokeDashoffset: PATH_LENGTH }}
|
|
37
|
+
animate={{ strokeDashoffset: -PATH_LENGTH }}
|
|
38
|
+
style={{ strokeDasharray: `8 ${PATH_LENGTH - 8}` }}
|
|
39
|
+
transition={{
|
|
40
|
+
duration: 4,
|
|
41
|
+
repeat: Infinity,
|
|
42
|
+
ease: "linear",
|
|
43
|
+
}}
|
|
44
|
+
/> */}
|
|
45
|
+
</svg>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<svg
|
|
51
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
52
|
+
width="12"
|
|
53
|
+
height="10"
|
|
54
|
+
viewBox="0 0 12 10"
|
|
55
|
+
fill="none"
|
|
56
|
+
className="mt-1.5"
|
|
57
|
+
style={{ overflow: "visible" }}
|
|
58
|
+
>
|
|
59
|
+
{/* Base fill with gradient */}
|
|
60
|
+
<path
|
|
61
|
+
d={ALPHA_PATH}
|
|
62
|
+
fill="url(#paint0_linear_10081_30848)"
|
|
63
|
+
opacity="0.4"
|
|
64
|
+
/>
|
|
65
|
+
{/* Animated stroke trace with gradient */}
|
|
66
|
+
<motion.path
|
|
67
|
+
d={ALPHA_PATH}
|
|
68
|
+
fill="none"
|
|
69
|
+
stroke="url(#stroke_gradient)"
|
|
70
|
+
strokeWidth="0.4"
|
|
71
|
+
strokeLinecap="round"
|
|
72
|
+
strokeLinejoin="round"
|
|
73
|
+
initial={{ strokeDashoffset: PATH_LENGTH }}
|
|
74
|
+
animate={{ strokeDashoffset: -PATH_LENGTH }}
|
|
75
|
+
style={{ strokeDasharray: `8 ${PATH_LENGTH - 8}` }}
|
|
76
|
+
transition={{
|
|
77
|
+
duration: 2,
|
|
78
|
+
repeat: Infinity,
|
|
79
|
+
repeatDelay: 8,
|
|
80
|
+
ease: "linear",
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
83
|
+
<defs>
|
|
84
|
+
<linearGradient
|
|
85
|
+
id="paint0_linear_10081_30848"
|
|
86
|
+
x1="-3.27254"
|
|
87
|
+
y1="4.99923"
|
|
88
|
+
x2="6.36738"
|
|
89
|
+
y2="-2.36962"
|
|
90
|
+
gradientUnits="userSpaceOnUse"
|
|
91
|
+
>
|
|
92
|
+
<stop stopColor="#8AEFDB" />
|
|
93
|
+
<stop offset="0.5" stopColor="#D4BACE" />
|
|
94
|
+
<stop offset="1" stopColor="#CBE0F1" />
|
|
95
|
+
</linearGradient>
|
|
96
|
+
<linearGradient
|
|
97
|
+
id="stroke_gradient"
|
|
98
|
+
x1="0"
|
|
99
|
+
y1="0"
|
|
100
|
+
x2="12"
|
|
101
|
+
y2="10"
|
|
102
|
+
gradientUnits="userSpaceOnUse"
|
|
103
|
+
>
|
|
104
|
+
<stop stopColor="#8AEFDB" />
|
|
105
|
+
<stop offset="0.5" stopColor="#D4BACE" />
|
|
106
|
+
<stop offset="1" stopColor="#CBE0F1" />
|
|
107
|
+
</linearGradient>
|
|
108
|
+
</defs>
|
|
109
|
+
</svg>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Define a Link component type that matches common routing patterns
|
|
114
|
+
export type LinkComponent = React.ComponentType<{
|
|
115
|
+
href: string;
|
|
116
|
+
className?: string;
|
|
117
|
+
"aria-label"?: string;
|
|
118
|
+
children: React.ReactNode;
|
|
119
|
+
onClick?: (e: React.MouseEvent) => void;
|
|
120
|
+
}>;
|
|
121
|
+
|
|
122
|
+
// Navigation link structure supporting nested links
|
|
123
|
+
export interface NavLinkItem {
|
|
124
|
+
label: string;
|
|
125
|
+
href?: string;
|
|
126
|
+
badge?: string;
|
|
127
|
+
bulletColor?: string;
|
|
128
|
+
children?: NavLinkItem[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Navigation section structure for tray content
|
|
132
|
+
export interface NavSection {
|
|
133
|
+
heading?: string;
|
|
134
|
+
headingGradient?: string;
|
|
135
|
+
columns?: NavColumn[];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface NavColumn {
|
|
139
|
+
title?: string;
|
|
140
|
+
links: NavLinkItem[];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Main navigation item that can have nested content
|
|
144
|
+
export interface NavItem {
|
|
145
|
+
label: string;
|
|
146
|
+
href?: string;
|
|
147
|
+
section?: NavSection;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
interface NavigationProps {
|
|
151
|
+
navLinks?: NavItem[];
|
|
152
|
+
logo?: React.ComponentType<{ className?: string; dark?: boolean }>;
|
|
153
|
+
userIcon?: React.ComponentType<{ className?: string }>;
|
|
154
|
+
beforeActions?: React.ReactNode; // Slot for custom items before user icon (e.g., language switcher)
|
|
155
|
+
userDropdown?: React.ReactNode; // Dropdown content to show when user icon is clicked
|
|
156
|
+
isStrategy?: boolean;
|
|
157
|
+
Link?: LinkComponent; // Optional Link component from routing library
|
|
158
|
+
onLinkClick?: (href: string) => void; // Fallback callback for navigation
|
|
159
|
+
onClose?: () => void;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export const StandardNavigation: React.FC<NavigationProps> = ({
|
|
163
|
+
navLinks = [],
|
|
164
|
+
logo: Logo,
|
|
165
|
+
userIcon: UserIcon,
|
|
166
|
+
beforeActions,
|
|
167
|
+
userDropdown,
|
|
168
|
+
isStrategy = false,
|
|
169
|
+
Link: LinkComponent,
|
|
170
|
+
onLinkClick,
|
|
171
|
+
onClose,
|
|
172
|
+
}) => {
|
|
173
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
174
|
+
const [activeItem, setActiveItem] = React.useState<number | null>(null);
|
|
175
|
+
const [isUserDropdownOpen, setIsUserDropdownOpen] = React.useState(false);
|
|
176
|
+
|
|
177
|
+
const containerRef = React.useRef<HTMLElement>(null);
|
|
178
|
+
const menuRef = React.useRef<HTMLDivElement>(null);
|
|
179
|
+
const menuItemsRef = React.useRef<HTMLDivElement>(null);
|
|
180
|
+
const overlayRef = React.useRef<HTMLDivElement>(null);
|
|
181
|
+
const userDropdownRef = React.useRef<HTMLDivElement>(null);
|
|
182
|
+
|
|
183
|
+
const handleMouseLeave = () => {
|
|
184
|
+
setIsOpen(false);
|
|
185
|
+
setActiveItem(null);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const handleNavItemMouseEnter = (index: number) => {
|
|
189
|
+
const item = navLinks[index];
|
|
190
|
+
if (item?.section) {
|
|
191
|
+
setIsOpen(true);
|
|
192
|
+
setActiveItem(index + 1);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const handleUserIconClick = (e: React.MouseEvent) => {
|
|
197
|
+
e.preventDefault();
|
|
198
|
+
if (userDropdown) {
|
|
199
|
+
setIsUserDropdownOpen(!isUserDropdownOpen);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// Close user dropdown when clicking outside
|
|
204
|
+
React.useEffect(() => {
|
|
205
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
206
|
+
const target = event.target as Node;
|
|
207
|
+
if (
|
|
208
|
+
userDropdownRef.current &&
|
|
209
|
+
!userDropdownRef.current.contains(target)
|
|
210
|
+
) {
|
|
211
|
+
setIsUserDropdownOpen(false);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
if (isUserDropdownOpen) {
|
|
216
|
+
// Use setTimeout to avoid immediate closure on the click that opened it
|
|
217
|
+
setTimeout(() => {
|
|
218
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
219
|
+
}, 0);
|
|
220
|
+
return () => {
|
|
221
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}, [isUserDropdownOpen]);
|
|
225
|
+
|
|
226
|
+
// Internal NavLink component that handles different routing scenarios
|
|
227
|
+
const NavLink: React.FC<{
|
|
228
|
+
href: string;
|
|
229
|
+
className?: string;
|
|
230
|
+
"aria-label"?: string;
|
|
231
|
+
children: React.ReactNode;
|
|
232
|
+
}> = ({ href, className, "aria-label": ariaLabel, children }) => {
|
|
233
|
+
// If Link component is provided, use it
|
|
234
|
+
if (LinkComponent) {
|
|
235
|
+
return (
|
|
236
|
+
<LinkComponent href={href} className={className} aria-label={ariaLabel}>
|
|
237
|
+
{children}
|
|
238
|
+
</LinkComponent>
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// If onLinkClick callback is provided, use anchor with click handler
|
|
243
|
+
if (onLinkClick) {
|
|
244
|
+
return (
|
|
245
|
+
<a
|
|
246
|
+
href={href}
|
|
247
|
+
className={className}
|
|
248
|
+
aria-label={ariaLabel}
|
|
249
|
+
onClick={(e) => {
|
|
250
|
+
e.preventDefault();
|
|
251
|
+
onLinkClick(href);
|
|
252
|
+
}}
|
|
253
|
+
>
|
|
254
|
+
{children}
|
|
255
|
+
</a>
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Default: plain anchor tag (browser navigation)
|
|
260
|
+
return (
|
|
261
|
+
<a href={href} className={className} aria-label={ariaLabel}>
|
|
262
|
+
{children}
|
|
263
|
+
</a>
|
|
264
|
+
);
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// Render a navigation link item
|
|
268
|
+
const renderNavLinkItem = (link: NavLinkItem) => {
|
|
269
|
+
const href = link.href || "#";
|
|
270
|
+
const hasBullet = link.bulletColor !== undefined;
|
|
271
|
+
const hasBadge = !!link.badge;
|
|
272
|
+
const isSmall = hasBullet || link.children;
|
|
273
|
+
|
|
274
|
+
if (hasBullet) {
|
|
275
|
+
return (
|
|
276
|
+
<div className="navigation-tray-bullet-item">
|
|
277
|
+
{link.bulletColor && link.bulletColor !== "" ? (
|
|
278
|
+
<NavBullet color={link.bulletColor} />
|
|
279
|
+
) : (
|
|
280
|
+
<div className="navigation-tray-bullet" />
|
|
281
|
+
)}
|
|
282
|
+
<NavLink
|
|
283
|
+
href={href}
|
|
284
|
+
className="navigation-tray-link-small"
|
|
285
|
+
>
|
|
286
|
+
{link.label}
|
|
287
|
+
</NavLink>
|
|
288
|
+
</div>
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (hasBadge) {
|
|
293
|
+
return (
|
|
294
|
+
<NavLink
|
|
295
|
+
href={href}
|
|
296
|
+
className="navigation-tray-link navigation-tray-link--with-badge"
|
|
297
|
+
>
|
|
298
|
+
{link.label}
|
|
299
|
+
<span className="navigation-tray-badge">{link.badge}</span>
|
|
300
|
+
</NavLink>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return (
|
|
305
|
+
<NavLink
|
|
306
|
+
href={href}
|
|
307
|
+
className={isSmall ? "navigation-tray-link-small" : "navigation-tray-link"}
|
|
308
|
+
>
|
|
309
|
+
{link.label}
|
|
310
|
+
</NavLink>
|
|
311
|
+
);
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
// Render a navigation column
|
|
315
|
+
const renderNavColumn = (column: NavColumn) => {
|
|
316
|
+
if (!column.links || column.links.length === 0) return null;
|
|
317
|
+
|
|
318
|
+
const hasSpacing = column.links.some(
|
|
319
|
+
(link) => !link.bulletColor && !link.children,
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
return (
|
|
323
|
+
<div key={column.title || "column"} className="navigation-tray-column">
|
|
324
|
+
{column.title && (
|
|
325
|
+
<h3 className="navigation-tray-column-title">{column.title}</h3>
|
|
326
|
+
)}
|
|
327
|
+
<ul
|
|
328
|
+
className={cn(
|
|
329
|
+
"navigation-tray-column-list",
|
|
330
|
+
hasSpacing && "navigation-tray-column-list--spacing-5",
|
|
331
|
+
)}
|
|
332
|
+
>
|
|
333
|
+
{column.links.map((link, idx) => (
|
|
334
|
+
<li key={idx}>{renderNavLinkItem(link)}</li>
|
|
335
|
+
))}
|
|
336
|
+
</ul>
|
|
337
|
+
</div>
|
|
338
|
+
);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// Render the active tray section
|
|
342
|
+
const renderTraySection = () => {
|
|
343
|
+
if (!activeItem) return null;
|
|
344
|
+
|
|
345
|
+
const activeNavItem = navLinks[activeItem - 1];
|
|
346
|
+
if (!activeNavItem?.section) return null;
|
|
347
|
+
|
|
348
|
+
const section = activeNavItem.section;
|
|
349
|
+
|
|
350
|
+
return (
|
|
351
|
+
<div className="navigation-tray-section">
|
|
352
|
+
{section.heading && (
|
|
353
|
+
<h2
|
|
354
|
+
className="navigation-tray-heading"
|
|
355
|
+
style={
|
|
356
|
+
section.headingGradient
|
|
357
|
+
? {
|
|
358
|
+
background: section.headingGradient,
|
|
359
|
+
WebkitBackgroundClip: "text",
|
|
360
|
+
WebkitTextFillColor: "transparent",
|
|
361
|
+
backgroundClip: "text",
|
|
362
|
+
}
|
|
363
|
+
: undefined
|
|
364
|
+
}
|
|
365
|
+
>
|
|
366
|
+
{section.heading}
|
|
367
|
+
</h2>
|
|
368
|
+
)}
|
|
369
|
+
|
|
370
|
+
{section.columns && section.columns.length > 0 && (
|
|
371
|
+
<div className="navigation-tray-columns">
|
|
372
|
+
{section.columns.map((column, idx) => renderNavColumn(column))}
|
|
373
|
+
</div>
|
|
374
|
+
)}
|
|
375
|
+
</div>
|
|
376
|
+
);
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
return (
|
|
380
|
+
<nav
|
|
381
|
+
ref={containerRef}
|
|
382
|
+
className={cn("navigation", isStrategy && "navigation--strategy")}
|
|
383
|
+
data-strategy={isStrategy}
|
|
384
|
+
onMouseLeave={handleMouseLeave}
|
|
385
|
+
>
|
|
386
|
+
{!isStrategy && (
|
|
387
|
+
<div
|
|
388
|
+
ref={overlayRef}
|
|
389
|
+
className="navigation-overlay"
|
|
390
|
+
data-open={isOpen}
|
|
391
|
+
/>
|
|
392
|
+
)}
|
|
393
|
+
|
|
394
|
+
<div className="navigation-container">
|
|
395
|
+
<div className="navigation-logo-container">
|
|
396
|
+
{isStrategy ? (
|
|
397
|
+
<Button
|
|
398
|
+
className="navigation-close-button"
|
|
399
|
+
size="icon"
|
|
400
|
+
onClick={(e) => {
|
|
401
|
+
e.preventDefault();
|
|
402
|
+
if (onClose) {
|
|
403
|
+
onClose();
|
|
404
|
+
} else if (onLinkClick) {
|
|
405
|
+
onLinkClick("/");
|
|
406
|
+
}
|
|
407
|
+
}}
|
|
408
|
+
>
|
|
409
|
+
<UtilityClose className="navigation-close-icon" />
|
|
410
|
+
</Button>
|
|
411
|
+
) : null}
|
|
412
|
+
|
|
413
|
+
<NavLink
|
|
414
|
+
href="/"
|
|
415
|
+
className={cn(
|
|
416
|
+
"navigation-logo-link",
|
|
417
|
+
isStrategy && "navigation-logo-link--strategy",
|
|
418
|
+
)}
|
|
419
|
+
data-strategy={isStrategy}
|
|
420
|
+
aria-label="Home"
|
|
421
|
+
>
|
|
422
|
+
{Logo ? (
|
|
423
|
+
<>
|
|
424
|
+
|
|
425
|
+
<NavLogo dark={isStrategy} />
|
|
426
|
+
<StandardLogo className={cn(!isStrategy ? "text-white" : "text-black")} />
|
|
427
|
+
<AlphaIcon dark={isStrategy} />
|
|
428
|
+
</>
|
|
429
|
+
|
|
430
|
+
) : null}
|
|
431
|
+
</NavLink>
|
|
432
|
+
</div>
|
|
433
|
+
|
|
434
|
+
{!isStrategy ? (
|
|
435
|
+
<div className="navigation-nav-container">
|
|
436
|
+
<ul className="navigation-nav-list">
|
|
437
|
+
{navLinks.map((item, index) => (
|
|
438
|
+
<li
|
|
439
|
+
key={index}
|
|
440
|
+
className="navigation-nav-item"
|
|
441
|
+
onMouseEnter={() => handleNavItemMouseEnter(index)}
|
|
442
|
+
>
|
|
443
|
+
{item.href ? (
|
|
444
|
+
<NavLink href={item.href} className="navigation-nav-link">
|
|
445
|
+
{item.label}
|
|
446
|
+
</NavLink>
|
|
447
|
+
) : (
|
|
448
|
+
<span className="navigation-nav-link">{item.label}</span>
|
|
449
|
+
)}
|
|
450
|
+
|
|
451
|
+
{/* Underline */}
|
|
452
|
+
{activeItem === index + 1 && (
|
|
453
|
+
<motion.div
|
|
454
|
+
id="underline"
|
|
455
|
+
className="navigation-underline"
|
|
456
|
+
layoutId="underline"
|
|
457
|
+
transition={{ duration: 0.2, ease: "easeOut" }}
|
|
458
|
+
>
|
|
459
|
+
<div
|
|
460
|
+
className="navigation-underline-circle navigation-underline-circle--gradient"
|
|
461
|
+
style={{
|
|
462
|
+
width: "10px",
|
|
463
|
+
height: "5px",
|
|
464
|
+
borderBottomLeftRadius: "100px",
|
|
465
|
+
borderBottomRightRadius: "100px",
|
|
466
|
+
borderBottom: "0",
|
|
467
|
+
boxSizing: "border-box",
|
|
468
|
+
}}
|
|
469
|
+
/>
|
|
470
|
+
</motion.div>
|
|
471
|
+
)}
|
|
472
|
+
{index === 0 && (
|
|
473
|
+
<div
|
|
474
|
+
className="navigation-underline navigation-underline--static"
|
|
475
|
+
style={{ transition: "all 0.2s ease-out" }}
|
|
476
|
+
>
|
|
477
|
+
<div
|
|
478
|
+
className="navigation-underline-circle navigation-underline-circle--white"
|
|
479
|
+
style={{
|
|
480
|
+
width: "10px",
|
|
481
|
+
height: "5px",
|
|
482
|
+
borderTopLeftRadius: "100px",
|
|
483
|
+
borderTopRightRadius: "100px",
|
|
484
|
+
borderBottom: "0",
|
|
485
|
+
boxSizing: "border-box",
|
|
486
|
+
}}
|
|
487
|
+
/>
|
|
488
|
+
</div>
|
|
489
|
+
)}
|
|
490
|
+
</li>
|
|
491
|
+
))}
|
|
492
|
+
</ul>
|
|
493
|
+
</div>
|
|
494
|
+
) : null}
|
|
495
|
+
|
|
496
|
+
<div className="navigation-actions">
|
|
497
|
+
{beforeActions && (
|
|
498
|
+
<div className="navigation-actions-before">{beforeActions}</div>
|
|
499
|
+
)}
|
|
500
|
+
<div className="navigation-account-wrapper" ref={userDropdownRef}>
|
|
501
|
+
{userDropdown ? (
|
|
502
|
+
<button
|
|
503
|
+
type="button"
|
|
504
|
+
aria-label="Account menu"
|
|
505
|
+
aria-expanded={isUserDropdownOpen}
|
|
506
|
+
className={cn(
|
|
507
|
+
"navigation-account-link",
|
|
508
|
+
isUserDropdownOpen && "navigation-account-link--open",
|
|
509
|
+
)}
|
|
510
|
+
data-open={isOpen}
|
|
511
|
+
data-strategy={isStrategy}
|
|
512
|
+
onClick={handleUserIconClick}
|
|
513
|
+
>
|
|
514
|
+
{UserIcon ? (
|
|
515
|
+
<UserIcon
|
|
516
|
+
className={cn(
|
|
517
|
+
"navigation-account-icon",
|
|
518
|
+
isStrategy
|
|
519
|
+
? "navigation-account-icon--strategy"
|
|
520
|
+
: "navigation-account-icon--default",
|
|
521
|
+
)}
|
|
522
|
+
/>
|
|
523
|
+
) : (
|
|
524
|
+
<div className="navigation-account-icon-placeholder">User</div>
|
|
525
|
+
)}
|
|
526
|
+
</button>
|
|
527
|
+
) : (
|
|
528
|
+
<NavLink
|
|
529
|
+
href="/"
|
|
530
|
+
aria-label="Account link"
|
|
531
|
+
className="navigation-account-link"
|
|
532
|
+
data-open={isOpen}
|
|
533
|
+
data-strategy={isStrategy}
|
|
534
|
+
>
|
|
535
|
+
{UserIcon ? (
|
|
536
|
+
<UserIcon
|
|
537
|
+
className={cn(
|
|
538
|
+
"navigation-account-icon",
|
|
539
|
+
isStrategy
|
|
540
|
+
? "navigation-account-icon--strategy"
|
|
541
|
+
: "navigation-account-icon--default",
|
|
542
|
+
)}
|
|
543
|
+
/>
|
|
544
|
+
) : (
|
|
545
|
+
<div className="navigation-account-icon-placeholder">User</div>
|
|
546
|
+
)}
|
|
547
|
+
</NavLink>
|
|
548
|
+
)}
|
|
549
|
+
{isUserDropdownOpen && userDropdown && (
|
|
550
|
+
<div className="navigation-user-dropdown">{userDropdown}</div>
|
|
551
|
+
)}
|
|
552
|
+
</div>
|
|
553
|
+
</div>
|
|
554
|
+
</div>
|
|
555
|
+
|
|
556
|
+
{isOpen && <div className="navigation-divider" />}
|
|
557
|
+
|
|
558
|
+
{/* Tray */}
|
|
559
|
+
<div ref={menuRef} className="navigation-tray" data-open={isOpen}>
|
|
560
|
+
<div className="navigation-tray-content">
|
|
561
|
+
<div ref={menuItemsRef} className="navigation-tray-grid">
|
|
562
|
+
<AnimatePresence mode="wait">
|
|
563
|
+
<motion.div
|
|
564
|
+
key={activeItem ? activeItem : "empty"}
|
|
565
|
+
initial={{ y: 10, opacity: 0 }}
|
|
566
|
+
animate={{ y: 0, opacity: 1 }}
|
|
567
|
+
exit={{ y: -10, opacity: 0 }}
|
|
568
|
+
transition={{ duration: 0.2 }}
|
|
569
|
+
>
|
|
570
|
+
{renderTraySection()}
|
|
571
|
+
</motion.div>
|
|
572
|
+
</AnimatePresence>
|
|
573
|
+
</div>
|
|
574
|
+
</div>
|
|
575
|
+
</div>
|
|
576
|
+
</nav>
|
|
577
|
+
);
|
|
578
|
+
};
|
package/client/global.css
CHANGED
|
@@ -6054,7 +6054,7 @@ body {
|
|
|
6054
6054
|
}
|
|
6055
6055
|
|
|
6056
6056
|
.navigation-overlay[data-open="false"] {
|
|
6057
|
-
transform: translateY(calc(-100% +
|
|
6057
|
+
transform: translateY(calc(-100% + 64px));
|
|
6058
6058
|
background-color: rgba(0, 0, 0, 0.16);
|
|
6059
6059
|
}
|
|
6060
6060
|
|
|
@@ -6066,11 +6066,16 @@ body {
|
|
|
6066
6066
|
padding-left: 2rem;
|
|
6067
6067
|
padding-right: 2rem;
|
|
6068
6068
|
display: flex;
|
|
6069
|
-
height:
|
|
6069
|
+
height: 4rem;
|
|
6070
6070
|
align-items: center;
|
|
6071
6071
|
justify-content: space-between;
|
|
6072
6072
|
}
|
|
6073
6073
|
|
|
6074
|
+
.navigation--strategy {
|
|
6075
|
+
background-color: var(--color-gray-50, #f9f9fa);
|
|
6076
|
+
border-bottom: 1px solid var(--color-gray-100, #eff1f2);
|
|
6077
|
+
}
|
|
6078
|
+
|
|
6074
6079
|
.navigation-logo-container {
|
|
6075
6080
|
display: flex;
|
|
6076
6081
|
height: 100%;
|
|
@@ -6088,6 +6093,7 @@ body {
|
|
|
6088
6093
|
align-items: center;
|
|
6089
6094
|
transition: transform 300ms ease-in-out;
|
|
6090
6095
|
will-change: transform;
|
|
6096
|
+
gap: 6px;
|
|
6091
6097
|
}
|
|
6092
6098
|
|
|
6093
6099
|
.navigation-logo-link[data-strategy="true"] {
|
|
@@ -6154,10 +6160,16 @@ body {
|
|
|
6154
6160
|
justify-content: center;
|
|
6155
6161
|
border-radius: 9999px;
|
|
6156
6162
|
transition: background-color 200ms;
|
|
6163
|
+
background-color: rgba(255, 255, 255, 0.12);
|
|
6164
|
+
}
|
|
6165
|
+
|
|
6166
|
+
.navigation-account-link--strategy {
|
|
6167
|
+
background-color: var(--color-white, #fff);
|
|
6168
|
+
border: 1px solid var(--color-gray-100, #eff1f2);
|
|
6157
6169
|
}
|
|
6158
6170
|
|
|
6159
6171
|
.navigation-account-link[data-open="false"] {
|
|
6160
|
-
background-color: rgba(
|
|
6172
|
+
background-color: rgba(255, 255, 255, 0.12);
|
|
6161
6173
|
}
|
|
6162
6174
|
|
|
6163
6175
|
.navigation-account-link[data-open="true"] {
|