@wealthx/shadcn 1.3.0 → 1.3.1
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/.turbo/turbo-build.log +43 -43
- package/CHANGELOG.md +6 -0
- package/dist/chunk-MLNEWRWV.mjs +449 -0
- package/dist/{chunk-FNQXOAYJ.mjs → chunk-Q2BGOAMG.mjs} +61 -28
- package/dist/components/ui/advisor-card.js +61 -28
- package/dist/components/ui/advisor-card.mjs +5 -3
- package/dist/components/ui/sidebar-nav.js +268 -172
- package/dist/components/ui/sidebar-nav.mjs +2 -1
- package/dist/index.js +515 -424
- package/dist/index.mjs +5 -3
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/components/index.tsx +2 -1
- package/src/components/ui/advisor-card.tsx +111 -54
- package/src/components/ui/sidebar-nav.tsx +194 -137
- package/src/styles/styles-css.ts +1 -1
- package/dist/chunk-ZC45IGZO.mjs +0 -388
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* - All icons must be Lucide icons (LucideIcon type).
|
|
11
11
|
* - Supports collapsible sub-items (accordion).
|
|
12
|
-
* - Collapsed state: icon-only, metrics
|
|
12
|
+
* - Collapsed state: icon-only, metrics animated out.
|
|
13
13
|
* - metricsGroups: optional financial summary rows (Frontend sidebar only).
|
|
14
14
|
* - No internal navigation — consumers wire onNavigate / onLogout.
|
|
15
15
|
*/
|
|
@@ -23,8 +23,10 @@ import {
|
|
|
23
23
|
PanelLeftOpen,
|
|
24
24
|
} from "lucide-react";
|
|
25
25
|
import type { LucideIcon } from "lucide-react";
|
|
26
|
+
import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion";
|
|
26
27
|
import { cn } from "@/lib/utils";
|
|
27
28
|
import { formatCurrency } from "@/lib/format-currency";
|
|
29
|
+
import { Accordion, AccordionContent, AccordionItem } from "./accordion";
|
|
28
30
|
import { Button } from "./button";
|
|
29
31
|
import {
|
|
30
32
|
Tooltip,
|
|
@@ -108,7 +110,6 @@ function getInitials(name: string): string {
|
|
|
108
110
|
.slice(0, 2);
|
|
109
111
|
}
|
|
110
112
|
|
|
111
|
-
|
|
112
113
|
function navIconCn(isActive: boolean): string {
|
|
113
114
|
return cn(
|
|
114
115
|
"shrink-0 transition-colors",
|
|
@@ -219,7 +220,7 @@ function SidebarNavItemView({
|
|
|
219
220
|
>
|
|
220
221
|
<Icon
|
|
221
222
|
className={navIconCn(item.isActive ?? false)}
|
|
222
|
-
size={
|
|
223
|
+
size={24}
|
|
223
224
|
strokeWidth={1.75}
|
|
224
225
|
/>
|
|
225
226
|
{!collapsed && <span className="truncate">{item.title}</span>}
|
|
@@ -264,7 +265,7 @@ function CollapsibleNavItem({
|
|
|
264
265
|
>
|
|
265
266
|
<Icon
|
|
266
267
|
className={navIconCn(hasActiveChild)}
|
|
267
|
-
size={
|
|
268
|
+
size={24}
|
|
268
269
|
strokeWidth={1.75}
|
|
269
270
|
/>
|
|
270
271
|
</Button>
|
|
@@ -273,65 +274,71 @@ function CollapsibleNavItem({
|
|
|
273
274
|
}
|
|
274
275
|
|
|
275
276
|
return (
|
|
276
|
-
<
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
className=
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
open && "rotate-180",
|
|
299
|
-
)}
|
|
300
|
-
size={14}
|
|
301
|
-
strokeWidth={2}
|
|
302
|
-
/>
|
|
303
|
-
</Button>
|
|
304
|
-
|
|
305
|
-
{open && item.subItems && (
|
|
306
|
-
<div className="ml-9 border-l border-white/15 pl-3">
|
|
307
|
-
{item.subItems.map((sub) => (
|
|
308
|
-
<Button
|
|
309
|
-
key={sub.href}
|
|
310
|
-
type="button"
|
|
311
|
-
variant="ghost"
|
|
312
|
-
onClick={() => onNavigate?.(sub.href)}
|
|
277
|
+
<Accordion
|
|
278
|
+
value={open ? [item.href] : []}
|
|
279
|
+
onValueChange={(values) => setOpen(values.length > 0)}
|
|
280
|
+
>
|
|
281
|
+
<AccordionItem className="border-none" value={item.href}>
|
|
282
|
+
<AccordionPrimitive.Header className="flex">
|
|
283
|
+
<AccordionPrimitive.Trigger
|
|
284
|
+
className={cn(
|
|
285
|
+
"group flex h-auto w-full items-center justify-start gap-3 px-3 py-2.5 text-base font-medium transition-colors",
|
|
286
|
+
"text-brand-secondary-foreground/70 hover:bg-white/10 hover:text-brand-secondary-foreground",
|
|
287
|
+
"border-l-4 border-transparent",
|
|
288
|
+
hasActiveChild &&
|
|
289
|
+
"bg-white/15 text-brand-secondary-foreground border-primary",
|
|
290
|
+
)}
|
|
291
|
+
>
|
|
292
|
+
<Icon
|
|
293
|
+
className={navIconCn(hasActiveChild)}
|
|
294
|
+
size={24}
|
|
295
|
+
strokeWidth={1.75}
|
|
296
|
+
/>
|
|
297
|
+
<span className="flex-1 truncate text-left">{item.title}</span>
|
|
298
|
+
<ChevronDown
|
|
313
299
|
className={cn(
|
|
314
|
-
"
|
|
315
|
-
"
|
|
316
|
-
sub.isActive && "text-primary font-medium",
|
|
300
|
+
"ml-auto shrink-0 text-brand-secondary-foreground/40 transition-transform duration-200",
|
|
301
|
+
"group-data-[panel-open]:rotate-180",
|
|
317
302
|
)}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
303
|
+
size={14}
|
|
304
|
+
strokeWidth={2}
|
|
305
|
+
/>
|
|
306
|
+
</AccordionPrimitive.Trigger>
|
|
307
|
+
</AccordionPrimitive.Header>
|
|
308
|
+
|
|
309
|
+
{item.subItems && (
|
|
310
|
+
<AccordionContent className="p-0 text-inherit">
|
|
311
|
+
<div className="ml-9 border-l border-white/15 pl-3">
|
|
312
|
+
{item.subItems.map((sub) => (
|
|
313
|
+
<Button
|
|
314
|
+
key={sub.href}
|
|
315
|
+
type="button"
|
|
316
|
+
variant="ghost"
|
|
317
|
+
onClick={() => onNavigate?.(sub.href)}
|
|
318
|
+
className={cn(
|
|
319
|
+
"h-auto w-full justify-start gap-2 py-1.5 pl-1 text-sm transition-colors",
|
|
320
|
+
"text-brand-secondary-foreground/50 hover:text-brand-secondary-foreground",
|
|
321
|
+
sub.isActive && "text-primary font-medium",
|
|
322
|
+
)}
|
|
323
|
+
>
|
|
324
|
+
<ChevronRight
|
|
325
|
+
size={11}
|
|
326
|
+
strokeWidth={2}
|
|
327
|
+
className={cn(
|
|
328
|
+
"shrink-0",
|
|
329
|
+
sub.isActive
|
|
330
|
+
? "text-primary"
|
|
331
|
+
: "text-brand-secondary-foreground/30",
|
|
332
|
+
)}
|
|
333
|
+
/>
|
|
334
|
+
<span className="truncate">{sub.title}</span>
|
|
335
|
+
</Button>
|
|
336
|
+
))}
|
|
337
|
+
</div>
|
|
338
|
+
</AccordionContent>
|
|
339
|
+
)}
|
|
340
|
+
</AccordionItem>
|
|
341
|
+
</Accordion>
|
|
335
342
|
);
|
|
336
343
|
}
|
|
337
344
|
|
|
@@ -350,6 +357,27 @@ export function SidebarNav({
|
|
|
350
357
|
className,
|
|
351
358
|
}: SidebarNavProps) {
|
|
352
359
|
const [userMenuOpen, setUserMenuOpen] = React.useState(false);
|
|
360
|
+
const navScrollRef = React.useRef<HTMLDivElement>(null);
|
|
361
|
+
const expandedScrollRef = React.useRef(0);
|
|
362
|
+
|
|
363
|
+
React.useEffect(() => {
|
|
364
|
+
if (collapsed) setUserMenuOpen(false);
|
|
365
|
+
}, [collapsed]);
|
|
366
|
+
|
|
367
|
+
// Preserve nav items scroll position across collapse/expand transitions.
|
|
368
|
+
// Cleanup saves scrollTop before the DOM changes; setup restores it when expanding.
|
|
369
|
+
React.useLayoutEffect(() => {
|
|
370
|
+
const nav = navScrollRef.current;
|
|
371
|
+
if (!nav) return;
|
|
372
|
+
if (!collapsed) {
|
|
373
|
+
nav.scrollTop = expandedScrollRef.current;
|
|
374
|
+
}
|
|
375
|
+
return () => {
|
|
376
|
+
if (!collapsed && nav) {
|
|
377
|
+
expandedScrollRef.current = nav.scrollTop;
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
}, [collapsed]);
|
|
353
381
|
|
|
354
382
|
return (
|
|
355
383
|
<TooltipProvider>
|
|
@@ -366,96 +394,125 @@ export function SidebarNav({
|
|
|
366
394
|
className,
|
|
367
395
|
)}
|
|
368
396
|
>
|
|
369
|
-
{/* Logo */}
|
|
370
|
-
{
|
|
371
|
-
<div className="flex items-center border-b border-white/15
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
397
|
+
{/* Logo — crossfade between full and icon variant */}
|
|
398
|
+
{(logo || logoCollapsed) && (
|
|
399
|
+
<div className="relative flex items-center border-b border-white/15 py-4 overflow-hidden">
|
|
400
|
+
{logo && (
|
|
401
|
+
<img
|
|
402
|
+
src={logo}
|
|
403
|
+
alt="Logo"
|
|
404
|
+
className={cn(
|
|
405
|
+
"h-8 w-auto object-contain object-left px-5 transition-opacity duration-200",
|
|
406
|
+
collapsed ? "opacity-0" : "opacity-100",
|
|
407
|
+
)}
|
|
408
|
+
style={{ filter: "brightness(0) invert(1)" }}
|
|
409
|
+
/>
|
|
410
|
+
)}
|
|
411
|
+
{logoCollapsed && (
|
|
412
|
+
<img
|
|
413
|
+
src={logoCollapsed}
|
|
414
|
+
alt="Logo"
|
|
415
|
+
className={cn(
|
|
416
|
+
"absolute inset-y-0 left-0 right-0 m-auto h-8 w-8 object-contain transition-opacity duration-200",
|
|
417
|
+
collapsed ? "opacity-100" : "opacity-0",
|
|
418
|
+
)}
|
|
419
|
+
style={{ filter: "brightness(0) invert(1)" }}
|
|
420
|
+
/>
|
|
421
|
+
)}
|
|
378
422
|
</div>
|
|
379
423
|
)}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
424
|
+
|
|
425
|
+
{/* User section — crossfade between expanded and collapsed */}
|
|
426
|
+
<div className="relative border-b border-white/15">
|
|
427
|
+
{/* Expanded — in flow (defines height), no transition (overlay handles it) */}
|
|
428
|
+
<div
|
|
429
|
+
className={cn(
|
|
430
|
+
collapsed ? "opacity-0 pointer-events-none" : "opacity-100",
|
|
431
|
+
)}
|
|
432
|
+
>
|
|
433
|
+
<Accordion
|
|
434
|
+
value={userMenuOpen ? ["user-menu"] : []}
|
|
435
|
+
onValueChange={(values) => setUserMenuOpen(values.length > 0)}
|
|
436
|
+
>
|
|
437
|
+
<AccordionItem className="border-none" value="user-menu">
|
|
438
|
+
<AccordionPrimitive.Header className="flex">
|
|
439
|
+
<AccordionPrimitive.Trigger
|
|
440
|
+
className={cn(
|
|
441
|
+
"group flex h-auto w-full items-center justify-start gap-3 px-5 py-5 text-base transition-colors",
|
|
442
|
+
"text-brand-secondary-foreground hover:bg-white/10",
|
|
443
|
+
)}
|
|
444
|
+
>
|
|
445
|
+
<div className="flex h-8 w-8 shrink-0 items-center justify-center font-semibold text-xs bg-primary text-primary-foreground">
|
|
446
|
+
{getInitials(userName)}
|
|
447
|
+
</div>
|
|
448
|
+
<span className="flex-1 truncate text-left font-medium text-brand-secondary-foreground">
|
|
449
|
+
{userName}
|
|
450
|
+
</span>
|
|
451
|
+
<ChevronDown
|
|
452
|
+
className="ml-auto shrink-0 text-brand-secondary-foreground/50 transition-transform duration-200 group-data-[panel-open]:rotate-180"
|
|
453
|
+
size={16}
|
|
454
|
+
strokeWidth={2}
|
|
455
|
+
/>
|
|
456
|
+
</AccordionPrimitive.Trigger>
|
|
457
|
+
</AccordionPrimitive.Header>
|
|
458
|
+
|
|
459
|
+
<AccordionContent className="p-0 text-inherit">
|
|
460
|
+
<div className="border-t border-white/15 bg-black/20">
|
|
461
|
+
<Button
|
|
462
|
+
type="button"
|
|
463
|
+
variant="ghost"
|
|
464
|
+
onClick={onLogout}
|
|
465
|
+
className={cn(
|
|
466
|
+
"h-auto w-full justify-start gap-3 px-5 py-3 text-base",
|
|
467
|
+
"text-brand-secondary-foreground/70 hover:bg-white/10 hover:text-brand-secondary-foreground transition-colors",
|
|
468
|
+
)}
|
|
469
|
+
>
|
|
470
|
+
<LogOut
|
|
471
|
+
size={16}
|
|
472
|
+
strokeWidth={1.75}
|
|
473
|
+
className="shrink-0 text-destructive"
|
|
474
|
+
/>
|
|
475
|
+
<span>Logout</span>
|
|
476
|
+
</Button>
|
|
477
|
+
</div>
|
|
478
|
+
</AccordionContent>
|
|
479
|
+
</AccordionItem>
|
|
480
|
+
</Accordion>
|
|
388
481
|
</div>
|
|
389
|
-
)}
|
|
390
482
|
|
|
391
|
-
|
|
392
|
-
<div className="border-b border-white/15">
|
|
483
|
+
{/* Collapsed — absolute overlay, centered avatar */}
|
|
393
484
|
<NavTooltip label={userName} collapsed={collapsed}>
|
|
394
|
-
<
|
|
395
|
-
type="button"
|
|
396
|
-
variant="ghost"
|
|
397
|
-
onClick={() => !collapsed && setUserMenuOpen((prev) => !prev)}
|
|
485
|
+
<div
|
|
398
486
|
className={cn(
|
|
399
|
-
"
|
|
400
|
-
"
|
|
401
|
-
collapsed && "justify-center px-2 py-4",
|
|
487
|
+
"absolute inset-0 flex items-center justify-center transition-opacity duration-200",
|
|
488
|
+
collapsed ? "opacity-100" : "opacity-0 pointer-events-none",
|
|
402
489
|
)}
|
|
403
490
|
>
|
|
404
491
|
<div className="flex h-8 w-8 shrink-0 items-center justify-center font-semibold text-xs bg-primary text-primary-foreground">
|
|
405
492
|
{getInitials(userName)}
|
|
406
493
|
</div>
|
|
407
|
-
{!collapsed && (
|
|
408
|
-
<>
|
|
409
|
-
<span className="flex-1 truncate text-left font-medium text-brand-secondary-foreground">
|
|
410
|
-
{userName}
|
|
411
|
-
</span>
|
|
412
|
-
<ChevronDown
|
|
413
|
-
className={cn(
|
|
414
|
-
"shrink-0 text-brand-secondary-foreground/50 transition-transform duration-200",
|
|
415
|
-
userMenuOpen && "rotate-180",
|
|
416
|
-
)}
|
|
417
|
-
size={16}
|
|
418
|
-
strokeWidth={2}
|
|
419
|
-
/>
|
|
420
|
-
</>
|
|
421
|
-
)}
|
|
422
|
-
</Button>
|
|
423
|
-
</NavTooltip>
|
|
424
|
-
|
|
425
|
-
{/* Logout dropdown */}
|
|
426
|
-
{!collapsed && userMenuOpen && (
|
|
427
|
-
<div className="border-t border-white/15 bg-black/20">
|
|
428
|
-
<Button
|
|
429
|
-
type="button"
|
|
430
|
-
variant="ghost"
|
|
431
|
-
onClick={onLogout}
|
|
432
|
-
className={cn(
|
|
433
|
-
"h-auto w-full justify-start gap-3 px-5 py-3 text-base",
|
|
434
|
-
"text-brand-secondary-foreground/70 hover:bg-white/10 hover:text-brand-secondary-foreground transition-colors",
|
|
435
|
-
)}
|
|
436
|
-
>
|
|
437
|
-
<LogOut
|
|
438
|
-
size={16}
|
|
439
|
-
strokeWidth={1.75}
|
|
440
|
-
className="shrink-0 text-destructive"
|
|
441
|
-
/>
|
|
442
|
-
<span>Logout</span>
|
|
443
|
-
</Button>
|
|
444
494
|
</div>
|
|
445
|
-
|
|
495
|
+
</NavTooltip>
|
|
446
496
|
</div>
|
|
447
497
|
|
|
448
|
-
{/* Financial metrics
|
|
449
|
-
{
|
|
450
|
-
<
|
|
451
|
-
{
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
498
|
+
{/* Financial metrics — animated in/out with sidebar collapse to prevent nav items jumping */}
|
|
499
|
+
{!!metricsGroups?.length && (
|
|
500
|
+
<Accordion
|
|
501
|
+
value={!collapsed ? ["metrics"] : []}
|
|
502
|
+
onValueChange={() => {}}
|
|
503
|
+
>
|
|
504
|
+
<AccordionItem className="border-none" value="metrics">
|
|
505
|
+
<AccordionContent className="p-0 text-inherit">
|
|
506
|
+
{metricsGroups.map((group, i) => (
|
|
507
|
+
<MetricsGroup key={i} group={group} />
|
|
508
|
+
))}
|
|
509
|
+
</AccordionContent>
|
|
510
|
+
</AccordionItem>
|
|
511
|
+
</Accordion>
|
|
455
512
|
)}
|
|
456
513
|
|
|
457
514
|
{/* Nav items */}
|
|
458
|
-
<div className="flex flex-col overflow-y-auto py-3">
|
|
515
|
+
<div ref={navScrollRef} className="flex flex-col overflow-y-auto py-3">
|
|
459
516
|
{items.map((item) =>
|
|
460
517
|
item.isCollapsible ? (
|
|
461
518
|
<CollapsibleNavItem
|
|
@@ -487,20 +544,20 @@ export function SidebarNav({
|
|
|
487
544
|
variant="ghost"
|
|
488
545
|
onClick={() => onCollapsedChange(!collapsed)}
|
|
489
546
|
className={cn(
|
|
490
|
-
"h-
|
|
547
|
+
"h-12 w-full justify-start gap-3 px-3 py-3 transition-colors",
|
|
491
548
|
"text-brand-secondary-foreground/80 hover:bg-white/10 hover:text-brand-secondary-foreground",
|
|
492
549
|
collapsed && "justify-center px-2",
|
|
493
550
|
)}
|
|
494
551
|
>
|
|
495
552
|
{collapsed ? (
|
|
496
553
|
<PanelLeftOpen
|
|
497
|
-
size={
|
|
554
|
+
size={24}
|
|
498
555
|
strokeWidth={1.75}
|
|
499
556
|
className="shrink-0"
|
|
500
557
|
/>
|
|
501
558
|
) : (
|
|
502
559
|
<PanelLeftClose
|
|
503
|
-
size={
|
|
560
|
+
size={24}
|
|
504
561
|
strokeWidth={1.75}
|
|
505
562
|
className="shrink-0"
|
|
506
563
|
/>
|