tembro 2.0.0 → 2.0.2
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/CHANGELOG.md +8 -0
- package/dist/components/data-table/data-table.cjs +1 -1
- package/dist/components/data-table/data-table.js +68 -68
- package/dist/components/layout/app-sidebar.cjs +1 -1
- package/dist/components/layout/app-sidebar.d.ts +19 -1
- package/dist/components/layout/app-sidebar.js +253 -126
- package/dist/hooks/index.cjs +1 -1
- package/dist/hooks/index.js +4 -4
- package/dist/index.cjs +1 -1
- package/dist/index.js +134 -134
- package/dist/showcase/package-meta.cjs +1 -1
- package/dist/showcase/package-meta.d.ts +3 -3
- package/dist/showcase/package-meta.js +1 -1
- package/dist/showcase/premium/app-shell/showcase.cjs +1 -1
- package/dist/showcase/premium/app-shell/showcase.js +5 -1
- package/dist/showcase/premium/app-sidebar/showcase.cjs +1 -1
- package/dist/showcase/premium/app-sidebar/showcase.js +91 -70
- package/dist/showcase/site-data.cjs +1 -1
- package/dist/showcase/site-data.js +1 -1
- package/dist/showcase/tembro-registry.json.cjs +1 -1
- package/dist/showcase/tembro-registry.json.js +1 -1
- package/package.json +1 -1
- package/packages/cli/dist/index.cjs +1 -1
- package/packages/cli/vendor/src/components/data-table/data-table.tsx +22 -13
- package/packages/cli/vendor/src/components/layout/app-sidebar.tsx +259 -21
- package/packages/cli/vendor/src/showcase/package-meta.ts +2 -2
- package/packages/cli/vendor/src/showcase/premium/app-shell/showcase.tsx +5 -1
- package/packages/cli/vendor/src/showcase/premium/app-sidebar/showcase.tsx +18 -4
- package/packages/cli/vendor/src/showcase/tembro-registry.json +1 -1
- package/registry.json +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as React from "react"
|
|
2
|
+
import { MenuIcon, XIcon } from "lucide-react"
|
|
2
3
|
|
|
4
|
+
import { useIsMobile } from "@/hooks/use-is-mobile"
|
|
3
5
|
import { Tooltip } from "@/components/ui/tooltip"
|
|
4
6
|
import { cn } from "@/lib/utils"
|
|
5
7
|
|
|
@@ -40,11 +42,28 @@ export type AppSidebarProps = React.ComponentProps<"aside"> & {
|
|
|
40
42
|
secondaryActions?: AppSidebarNavItem[]
|
|
41
43
|
footerSecondary?: React.ReactNode
|
|
42
44
|
tooltipOnCollapsed?: boolean
|
|
45
|
+
responsive?: boolean
|
|
46
|
+
mobileBreakpoint?: number
|
|
47
|
+
mobileOpen?: boolean
|
|
48
|
+
defaultMobileOpen?: boolean
|
|
49
|
+
onMobileOpenChange?: (open: boolean) => void
|
|
50
|
+
mobileTitle?: React.ReactNode
|
|
51
|
+
mobileDescription?: React.ReactNode
|
|
52
|
+
mobileToggleLabel?: string
|
|
53
|
+
mobileToggleIcon?: React.ReactNode
|
|
54
|
+
showMobileToggle?: boolean
|
|
55
|
+
closeOnSelect?: boolean
|
|
56
|
+
mobileToggleClassName?: string
|
|
57
|
+
mobilePanelClassName?: string
|
|
58
|
+
mobileOverlayClassName?: string
|
|
59
|
+
renderMobileToggle?: (state: { open: boolean; setOpen: (open: boolean) => void }) => React.ReactNode
|
|
43
60
|
onItemSelect?: (item: AppSidebarNavItem) => void
|
|
44
61
|
renderItem?: (item: AppSidebarNavItem, state: { collapsed: boolean }) => React.ReactNode
|
|
45
62
|
renderLink?: (props: React.ComponentProps<"a"> & { item: AppSidebarNavItem; [key: `data-${string}`]: string | boolean | undefined }) => React.ReactNode
|
|
46
63
|
}
|
|
47
64
|
|
|
65
|
+
const DEFAULT_APP_SIDEBAR_BREAKPOINT = 1024
|
|
66
|
+
|
|
48
67
|
function hasVisibleSidebarChildren(item: AppSidebarNavItem) {
|
|
49
68
|
return item.items?.some((child) => !child.hidden) ?? false
|
|
50
69
|
}
|
|
@@ -54,6 +73,11 @@ function isSidebarItemActive(item: AppSidebarNavItem): boolean {
|
|
|
54
73
|
return item.items?.some((child) => isSidebarItemActive(child)) ?? false
|
|
55
74
|
}
|
|
56
75
|
|
|
76
|
+
function triggerSidebarItem(item: AppSidebarNavItem, onItemSelect?: (item: AppSidebarNavItem) => void) {
|
|
77
|
+
item.onSelect?.()
|
|
78
|
+
onItemSelect?.(item)
|
|
79
|
+
}
|
|
80
|
+
|
|
57
81
|
function SidebarLeafItem({
|
|
58
82
|
item,
|
|
59
83
|
collapsed,
|
|
@@ -111,8 +135,7 @@ function SidebarLeafItem({
|
|
|
111
135
|
event.preventDefault()
|
|
112
136
|
return
|
|
113
137
|
}
|
|
114
|
-
item
|
|
115
|
-
onItemSelect?.(item)
|
|
138
|
+
triggerSidebarItem(item, onItemSelect)
|
|
116
139
|
},
|
|
117
140
|
children: content,
|
|
118
141
|
})}
|
|
@@ -129,8 +152,7 @@ function SidebarLeafItem({
|
|
|
129
152
|
event.preventDefault()
|
|
130
153
|
return
|
|
131
154
|
}
|
|
132
|
-
item
|
|
133
|
-
onItemSelect?.(item)
|
|
155
|
+
triggerSidebarItem(item, onItemSelect)
|
|
134
156
|
}}
|
|
135
157
|
>
|
|
136
158
|
{content}
|
|
@@ -148,8 +170,7 @@ function SidebarLeafItem({
|
|
|
148
170
|
if (item.disabled) return
|
|
149
171
|
const href = item.href
|
|
150
172
|
if (!href) return
|
|
151
|
-
item
|
|
152
|
-
onItemSelect?.(item)
|
|
173
|
+
triggerSidebarItem(item, onItemSelect)
|
|
153
174
|
if (href.startsWith("http")) {
|
|
154
175
|
window.open(href, "_blank", "noopener,noreferrer")
|
|
155
176
|
return
|
|
@@ -168,8 +189,8 @@ function SidebarLeafItem({
|
|
|
168
189
|
disabled={item.disabled}
|
|
169
190
|
{...commonProps}
|
|
170
191
|
onClick={() => {
|
|
171
|
-
item.
|
|
172
|
-
|
|
192
|
+
if (item.disabled) return
|
|
193
|
+
triggerSidebarItem(item, onItemSelect)
|
|
173
194
|
}}
|
|
174
195
|
>
|
|
175
196
|
{content}
|
|
@@ -250,7 +271,14 @@ function SidebarTree({
|
|
|
250
271
|
) : null}
|
|
251
272
|
{!collapsed && <span className="min-w-0 flex-1 truncate">{item.label}</span>}
|
|
252
273
|
{!collapsed && item.badge && <span className="shrink-0">{item.badge}</span>}
|
|
253
|
-
{!collapsed &&
|
|
274
|
+
{!collapsed && (
|
|
275
|
+
<span
|
|
276
|
+
data-slot="app-sidebar-group-chevron"
|
|
277
|
+
className="ml-auto text-xs text-muted-foreground transition-transform group-open/app-sidebar-details:rotate-90"
|
|
278
|
+
>
|
|
279
|
+
›
|
|
280
|
+
</span>
|
|
281
|
+
)}
|
|
254
282
|
</summary>
|
|
255
283
|
<div data-slot="app-sidebar-group-content" className={cn("mt-1 space-y-1", !collapsed && "pl-3")}>
|
|
256
284
|
<SidebarTree
|
|
@@ -289,8 +317,7 @@ function SidebarActionButton({
|
|
|
289
317
|
disabled={item.disabled}
|
|
290
318
|
onClick={() => {
|
|
291
319
|
if (item.disabled) return
|
|
292
|
-
item
|
|
293
|
-
onItemSelect?.(item)
|
|
320
|
+
triggerSidebarItem(item, onItemSelect)
|
|
294
321
|
}}
|
|
295
322
|
>
|
|
296
323
|
{item.icon ? <span className="shrink-0">{item.icon}</span> : null}
|
|
@@ -311,9 +338,11 @@ function SidebarActionButton({
|
|
|
311
338
|
function SidebarFooterAccount({
|
|
312
339
|
account,
|
|
313
340
|
collapsed,
|
|
341
|
+
onAfterSelect,
|
|
314
342
|
}: {
|
|
315
343
|
account: AppSidebarFooterAccount
|
|
316
344
|
collapsed: boolean
|
|
345
|
+
onAfterSelect?: () => void
|
|
317
346
|
}) {
|
|
318
347
|
const body = (
|
|
319
348
|
<button
|
|
@@ -325,6 +354,7 @@ function SidebarFooterAccount({
|
|
|
325
354
|
)}
|
|
326
355
|
onClick={() => {
|
|
327
356
|
account.onSelect?.()
|
|
357
|
+
onAfterSelect?.()
|
|
328
358
|
if (!account.href) return
|
|
329
359
|
if (account.href.startsWith("http")) {
|
|
330
360
|
window.open(account.href, "_blank", "noopener,noreferrer")
|
|
@@ -365,7 +395,7 @@ function SidebarFooterAccount({
|
|
|
365
395
|
)
|
|
366
396
|
}
|
|
367
397
|
|
|
368
|
-
function
|
|
398
|
+
function SidebarSurface({
|
|
369
399
|
className,
|
|
370
400
|
header,
|
|
371
401
|
footer,
|
|
@@ -376,25 +406,70 @@ function AppSidebar({
|
|
|
376
406
|
footerAccount,
|
|
377
407
|
secondaryActions = [],
|
|
378
408
|
footerSecondary,
|
|
379
|
-
tooltipOnCollapsed
|
|
409
|
+
tooltipOnCollapsed,
|
|
380
410
|
onItemSelect,
|
|
381
411
|
renderItem,
|
|
382
412
|
renderLink,
|
|
383
413
|
children,
|
|
414
|
+
mobile,
|
|
415
|
+
mobileTitle,
|
|
416
|
+
mobileDescription,
|
|
417
|
+
onRequestClose,
|
|
418
|
+
closeOnSelect = true,
|
|
384
419
|
...props
|
|
385
|
-
}: AppSidebarProps
|
|
420
|
+
}: Omit<AppSidebarProps, "responsive" | "mobileBreakpoint" | "mobileOpen" | "defaultMobileOpen" | "onMobileOpenChange" | "mobileToggleLabel" | "mobileToggleIcon" | "showMobileToggle" | "mobileToggleClassName" | "mobilePanelClassName" | "mobileOverlayClassName" | "renderMobileToggle"> & {
|
|
421
|
+
mobile?: boolean
|
|
422
|
+
onRequestClose?: () => void
|
|
423
|
+
}) {
|
|
386
424
|
const visibleItems = items.filter((item) => !item.hidden)
|
|
387
425
|
const visibleRailItems = railItems.filter((item) => !item.hidden)
|
|
388
426
|
const visibleSecondaryActions = secondaryActions.filter((item) => !item.hidden)
|
|
389
427
|
|
|
428
|
+
const handleSelect = React.useCallback((item: AppSidebarNavItem) => {
|
|
429
|
+
onItemSelect?.(item)
|
|
430
|
+
if (mobile && closeOnSelect) onRequestClose?.()
|
|
431
|
+
}, [closeOnSelect, mobile, onItemSelect, onRequestClose])
|
|
432
|
+
|
|
433
|
+
const showMobileHeader = mobile && (mobileTitle || mobileDescription || onRequestClose)
|
|
434
|
+
|
|
390
435
|
return (
|
|
391
436
|
<aside
|
|
392
437
|
data-slot="app-sidebar"
|
|
393
438
|
data-collapsed={collapsed || undefined}
|
|
439
|
+
data-mobile={mobile || undefined}
|
|
394
440
|
className={cn("flex h-full min-h-0 flex-col overflow-hidden", className)}
|
|
395
441
|
{...props}
|
|
396
442
|
>
|
|
397
|
-
{header
|
|
443
|
+
{(header || showMobileHeader) && (
|
|
444
|
+
<div data-slot="app-sidebar-header" className="shrink-0 border-b p-3">
|
|
445
|
+
<div className="flex items-start gap-3">
|
|
446
|
+
<div className="min-w-0 flex-1">
|
|
447
|
+
{header ?? null}
|
|
448
|
+
{showMobileHeader ? (
|
|
449
|
+
<div className={cn(header && "mt-3")}>
|
|
450
|
+
{mobileTitle ? (
|
|
451
|
+
<p className="text-sm font-semibold text-foreground">{mobileTitle}</p>
|
|
452
|
+
) : null}
|
|
453
|
+
{mobileDescription ? (
|
|
454
|
+
<p className="mt-1 text-xs leading-5 text-muted-foreground">{mobileDescription}</p>
|
|
455
|
+
) : null}
|
|
456
|
+
</div>
|
|
457
|
+
) : null}
|
|
458
|
+
</div>
|
|
459
|
+
{mobile && onRequestClose ? (
|
|
460
|
+
<button
|
|
461
|
+
type="button"
|
|
462
|
+
aria-label="Close navigation"
|
|
463
|
+
data-slot="app-sidebar-mobile-close"
|
|
464
|
+
className="inline-flex size-9 shrink-0 items-center justify-center rounded-xl border border-[color:var(--aui-divider)] bg-[color:var(--aui-page-bg-alt)] text-[color:var(--aui-page-foreground)] transition hover:bg-[color:var(--aui-control-bg)]"
|
|
465
|
+
onClick={onRequestClose}
|
|
466
|
+
>
|
|
467
|
+
<XIcon className="size-4" />
|
|
468
|
+
</button>
|
|
469
|
+
) : null}
|
|
470
|
+
</div>
|
|
471
|
+
</div>
|
|
472
|
+
)}
|
|
398
473
|
|
|
399
474
|
<nav data-slot="app-sidebar-nav" className="min-h-0 flex-1 space-y-1 overflow-y-auto overscroll-contain p-2">
|
|
400
475
|
{children ??
|
|
@@ -417,7 +492,7 @@ function AppSidebar({
|
|
|
417
492
|
items={visibleItems}
|
|
418
493
|
collapsed={collapsed}
|
|
419
494
|
depth={0}
|
|
420
|
-
onItemSelect={
|
|
495
|
+
onItemSelect={handleSelect}
|
|
421
496
|
renderLink={renderLink}
|
|
422
497
|
/>
|
|
423
498
|
)}
|
|
@@ -434,7 +509,7 @@ function AppSidebar({
|
|
|
434
509
|
key={item.key}
|
|
435
510
|
item={item}
|
|
436
511
|
collapsed
|
|
437
|
-
onItemSelect={
|
|
512
|
+
onItemSelect={handleSelect}
|
|
438
513
|
/>
|
|
439
514
|
))}
|
|
440
515
|
</div>
|
|
@@ -444,7 +519,9 @@ function AppSidebar({
|
|
|
444
519
|
) : null}
|
|
445
520
|
{!collapsed && footerAccount ? (
|
|
446
521
|
<div data-slot="app-sidebar-account-wrap" className="mb-3">
|
|
447
|
-
<SidebarFooterAccount account={footerAccount} collapsed={false}
|
|
522
|
+
<SidebarFooterAccount account={footerAccount} collapsed={false} onAfterSelect={() => {
|
|
523
|
+
if (mobile && closeOnSelect) onRequestClose?.()
|
|
524
|
+
}} />
|
|
448
525
|
</div>
|
|
449
526
|
) : null}
|
|
450
527
|
{!collapsed && visibleSecondaryActions.length > 0 ? (
|
|
@@ -454,7 +531,7 @@ function AppSidebar({
|
|
|
454
531
|
key={item.key}
|
|
455
532
|
item={item}
|
|
456
533
|
collapsed={false}
|
|
457
|
-
onItemSelect={
|
|
534
|
+
onItemSelect={handleSelect}
|
|
458
535
|
/>
|
|
459
536
|
))}
|
|
460
537
|
</div>
|
|
@@ -466,7 +543,13 @@ function AppSidebar({
|
|
|
466
543
|
) : null}
|
|
467
544
|
{collapsed && footerAccount ? (
|
|
468
545
|
<div data-slot="app-sidebar-account-wrap">
|
|
469
|
-
<SidebarFooterAccount
|
|
546
|
+
<SidebarFooterAccount
|
|
547
|
+
account={footerAccount}
|
|
548
|
+
collapsed
|
|
549
|
+
onAfterSelect={() => {
|
|
550
|
+
if (mobile && closeOnSelect) onRequestClose?.()
|
|
551
|
+
}}
|
|
552
|
+
/>
|
|
470
553
|
</div>
|
|
471
554
|
) : null}
|
|
472
555
|
{!collapsed && footer}
|
|
@@ -475,7 +558,162 @@ function AppSidebar({
|
|
|
475
558
|
</aside>
|
|
476
559
|
)
|
|
477
560
|
}
|
|
478
|
-
|
|
561
|
+
|
|
562
|
+
function AppSidebar({
|
|
563
|
+
className,
|
|
564
|
+
header,
|
|
565
|
+
footer,
|
|
566
|
+
items = [],
|
|
567
|
+
collapsed = false,
|
|
568
|
+
collapsedRail,
|
|
569
|
+
railItems = [],
|
|
570
|
+
footerAccount,
|
|
571
|
+
secondaryActions = [],
|
|
572
|
+
footerSecondary,
|
|
573
|
+
tooltipOnCollapsed = true,
|
|
574
|
+
responsive = true,
|
|
575
|
+
mobileBreakpoint = DEFAULT_APP_SIDEBAR_BREAKPOINT,
|
|
576
|
+
mobileOpen: mobileOpenProp,
|
|
577
|
+
defaultMobileOpen = false,
|
|
578
|
+
onMobileOpenChange,
|
|
579
|
+
mobileTitle,
|
|
580
|
+
mobileDescription,
|
|
581
|
+
mobileToggleLabel = "Open navigation",
|
|
582
|
+
mobileToggleIcon,
|
|
583
|
+
showMobileToggle = true,
|
|
584
|
+
closeOnSelect = true,
|
|
585
|
+
mobileToggleClassName,
|
|
586
|
+
mobilePanelClassName,
|
|
587
|
+
mobileOverlayClassName,
|
|
588
|
+
renderMobileToggle,
|
|
589
|
+
onItemSelect,
|
|
590
|
+
renderItem,
|
|
591
|
+
renderLink,
|
|
592
|
+
children,
|
|
593
|
+
...props
|
|
594
|
+
}: AppSidebarProps) {
|
|
595
|
+
const isMobile = responsive ? useIsMobile(mobileBreakpoint) : false
|
|
596
|
+
const [uncontrolledMobileOpen, setUncontrolledMobileOpen] = React.useState(defaultMobileOpen)
|
|
597
|
+
const mobileOpen = mobileOpenProp ?? uncontrolledMobileOpen
|
|
598
|
+
|
|
599
|
+
const setMobileOpen = React.useCallback((nextOpen: boolean) => {
|
|
600
|
+
if (mobileOpenProp == null) {
|
|
601
|
+
setUncontrolledMobileOpen(nextOpen)
|
|
602
|
+
}
|
|
603
|
+
onMobileOpenChange?.(nextOpen)
|
|
604
|
+
}, [mobileOpenProp, onMobileOpenChange])
|
|
605
|
+
|
|
606
|
+
React.useEffect(() => {
|
|
607
|
+
if (!isMobile && mobileOpen) {
|
|
608
|
+
setMobileOpen(false)
|
|
609
|
+
}
|
|
610
|
+
}, [isMobile, mobileOpen, setMobileOpen])
|
|
611
|
+
|
|
612
|
+
React.useEffect(() => {
|
|
613
|
+
if (!isMobile || !mobileOpen) return
|
|
614
|
+
|
|
615
|
+
const previousOverflow = document.body.style.overflow
|
|
616
|
+
document.body.style.overflow = "hidden"
|
|
617
|
+
|
|
618
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
619
|
+
if (event.key === "Escape") {
|
|
620
|
+
setMobileOpen(false)
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
window.addEventListener("keydown", handleKeyDown)
|
|
625
|
+
return () => {
|
|
626
|
+
document.body.style.overflow = previousOverflow
|
|
627
|
+
window.removeEventListener("keydown", handleKeyDown)
|
|
628
|
+
}
|
|
629
|
+
}, [isMobile, mobileOpen, setMobileOpen])
|
|
630
|
+
|
|
631
|
+
const baseProps = {
|
|
632
|
+
className,
|
|
633
|
+
header,
|
|
634
|
+
footer,
|
|
635
|
+
items,
|
|
636
|
+
collapsed,
|
|
637
|
+
collapsedRail,
|
|
638
|
+
railItems,
|
|
639
|
+
footerAccount,
|
|
640
|
+
secondaryActions,
|
|
641
|
+
footerSecondary,
|
|
642
|
+
tooltipOnCollapsed,
|
|
643
|
+
closeOnSelect,
|
|
644
|
+
onItemSelect,
|
|
645
|
+
renderItem,
|
|
646
|
+
renderLink,
|
|
647
|
+
children,
|
|
648
|
+
...props,
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
if (!responsive || !isMobile) {
|
|
652
|
+
return <SidebarSurface {...baseProps} />
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const defaultTrigger = (
|
|
656
|
+
<button
|
|
657
|
+
type="button"
|
|
658
|
+
aria-label={mobileOpen ? "Close navigation" : mobileToggleLabel}
|
|
659
|
+
data-slot="app-sidebar-mobile-trigger"
|
|
660
|
+
data-state={mobileOpen ? "open" : "closed"}
|
|
661
|
+
className={cn(
|
|
662
|
+
"inline-flex min-h-10 items-center gap-2 rounded-xl border border-[color:var(--aui-divider)] bg-[color:var(--aui-page-bg)] px-3 text-sm font-medium text-[color:var(--aui-page-foreground)] shadow-sm transition hover:bg-[color:var(--aui-page-bg-alt)]",
|
|
663
|
+
mobileToggleClassName
|
|
664
|
+
)}
|
|
665
|
+
onClick={() => setMobileOpen(!mobileOpen)}
|
|
666
|
+
>
|
|
667
|
+
<span className="inline-flex size-8 items-center justify-center rounded-lg bg-[color:var(--aui-page-bg-alt)]">
|
|
668
|
+
{mobileToggleIcon ?? <MenuIcon className="size-4" />}
|
|
669
|
+
</span>
|
|
670
|
+
<span>{mobileOpen ? "Close navigation" : mobileToggleLabel}</span>
|
|
671
|
+
</button>
|
|
672
|
+
)
|
|
673
|
+
|
|
674
|
+
return (
|
|
675
|
+
<>
|
|
676
|
+
{showMobileToggle
|
|
677
|
+
? renderMobileToggle
|
|
678
|
+
? renderMobileToggle({ open: mobileOpen, setOpen: setMobileOpen })
|
|
679
|
+
: defaultTrigger
|
|
680
|
+
: null}
|
|
681
|
+
<div data-slot="app-sidebar-mobile-root" className="relative z-40">
|
|
682
|
+
<button
|
|
683
|
+
type="button"
|
|
684
|
+
aria-label="Dismiss navigation"
|
|
685
|
+
data-slot="app-sidebar-mobile-overlay"
|
|
686
|
+
data-state={mobileOpen ? "open" : "closed"}
|
|
687
|
+
className={cn(
|
|
688
|
+
"fixed inset-0 z-40 bg-black/45 transition-opacity duration-200",
|
|
689
|
+
mobileOpen ? "opacity-100" : "pointer-events-none opacity-0",
|
|
690
|
+
mobileOverlayClassName
|
|
691
|
+
)}
|
|
692
|
+
onClick={() => setMobileOpen(false)}
|
|
693
|
+
/>
|
|
694
|
+
<SidebarSurface
|
|
695
|
+
{...baseProps}
|
|
696
|
+
mobile
|
|
697
|
+
collapsed={false}
|
|
698
|
+
mobileTitle={mobileTitle}
|
|
699
|
+
mobileDescription={mobileDescription}
|
|
700
|
+
onRequestClose={() => setMobileOpen(false)}
|
|
701
|
+
data-state={mobileOpen ? "open" : "closed"}
|
|
702
|
+
role="dialog"
|
|
703
|
+
aria-modal="true"
|
|
704
|
+
aria-label={typeof mobileTitle === "string" ? mobileTitle : "Navigation"}
|
|
705
|
+
className={cn(
|
|
706
|
+
"fixed inset-y-0 left-0 z-50 w-[min(88vw,22rem)] max-w-[22rem] border-r border-[color:var(--aui-divider)] bg-[color:var(--aui-page-bg)] shadow-2xl transition-transform duration-200 ease-out",
|
|
707
|
+
mobileOpen ? "translate-x-0" : "-translate-x-full",
|
|
708
|
+
mobilePanelClassName,
|
|
709
|
+
className
|
|
710
|
+
)}
|
|
711
|
+
/>
|
|
712
|
+
</div>
|
|
713
|
+
</>
|
|
714
|
+
)
|
|
715
|
+
}
|
|
716
|
+
|
|
479
717
|
/**
|
|
480
718
|
* Canonical sidebar surface for new usage.
|
|
481
719
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const PACKAGE_LATEST_VERSION = "2.0.
|
|
2
|
-
export const PACKAGE_LATEST_RELEASE_DATE = "July
|
|
1
|
+
export const PACKAGE_LATEST_VERSION = "2.0.2"
|
|
2
|
+
export const PACKAGE_LATEST_RELEASE_DATE = "July 8, 2026"
|
|
3
3
|
export const PACKAGE_DEPENDENCY_RANGE = `^${PACKAGE_LATEST_VERSION}`
|
|
4
4
|
|
|
@@ -69,9 +69,13 @@ export function AppShellShowcase({ mode }: ComponentDemoProps) {
|
|
|
69
69
|
|
|
70
70
|
<div className="grid min-h-[420px] lg:grid-cols-[250px_minmax(0,1fr)_260px]">
|
|
71
71
|
<AppSidebar
|
|
72
|
-
|
|
72
|
+
responsive
|
|
73
|
+
mobileTitle="Workspace navigation"
|
|
74
|
+
mobileDescription="Open the shell menu first, then move through app routes without squeezing the main content."
|
|
73
75
|
header={<div className="px-3 py-2 text-sm font-semibold">AZA Studio</div>}
|
|
74
76
|
footer={<div className="aui-text-muted px-3 py-2 text-xs">Shell footer</div>}
|
|
77
|
+
className={collapsed ? "lg:w-[92px]" : ""}
|
|
78
|
+
collapsed={collapsed}
|
|
75
79
|
>
|
|
76
80
|
<SidebarNav items={navItems} />
|
|
77
81
|
</AppSidebar>
|
|
@@ -2,6 +2,7 @@ import * as React from "react"
|
|
|
2
2
|
import { BarChart3Icon, CreditCardIcon, FolderIcon, HomeIcon, SettingsIcon } from "lucide-react"
|
|
3
3
|
|
|
4
4
|
import { AppSidebar, Badge, Button } from "@/index"
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
5
6
|
|
|
6
7
|
import type { ComponentDemoProps } from "../types"
|
|
7
8
|
|
|
@@ -18,6 +19,7 @@ const panelClass = "rounded-[22px] border border-[color:var(--aui-divider)] bg-[
|
|
|
18
19
|
export function AppSidebarShowcase({ mode }: ComponentDemoProps) {
|
|
19
20
|
const [activeKey, setActiveKey] = React.useState("overview")
|
|
20
21
|
const [collapsed, setCollapsed] = React.useState(mode === "playground")
|
|
22
|
+
const [previewSurface, setPreviewSurface] = React.useState<"desktop" | "mobile">("desktop")
|
|
21
23
|
|
|
22
24
|
const items = React.useMemo(
|
|
23
25
|
() =>
|
|
@@ -30,6 +32,7 @@ export function AppSidebarShowcase({ mode }: ComponentDemoProps) {
|
|
|
30
32
|
)
|
|
31
33
|
|
|
32
34
|
const activeItem = navItems.find((item) => item.key === activeKey)
|
|
35
|
+
const mobilePreview = previewSurface === "mobile"
|
|
33
36
|
|
|
34
37
|
return (
|
|
35
38
|
<div className="space-y-5">
|
|
@@ -45,7 +48,7 @@ export function AppSidebarShowcase({ mode }: ComponentDemoProps) {
|
|
|
45
48
|
<div className="flex flex-wrap gap-2">
|
|
46
49
|
<Badge variant="outline" className="rounded-full">Header slot</Badge>
|
|
47
50
|
<Badge variant="outline" className="rounded-full">Badges</Badge>
|
|
48
|
-
<Badge variant="outline" className="rounded-full">
|
|
51
|
+
<Badge variant="outline" className="rounded-full">Mobile drawer</Badge>
|
|
49
52
|
</div>
|
|
50
53
|
</div>
|
|
51
54
|
</section>
|
|
@@ -58,14 +61,25 @@ export function AppSidebarShowcase({ mode }: ComponentDemoProps) {
|
|
|
58
61
|
<Button size="sm" variant="outline" onClick={() => setActiveKey("reports")}>
|
|
59
62
|
Jump to reports
|
|
60
63
|
</Button>
|
|
64
|
+
<Button size="sm" variant={mobilePreview ? "default" : "outline"} onClick={() => setPreviewSurface("mobile")}>
|
|
65
|
+
Mobile drawer
|
|
66
|
+
</Button>
|
|
67
|
+
<Button size="sm" variant={!mobilePreview ? "default" : "outline"} onClick={() => setPreviewSurface("desktop")}>
|
|
68
|
+
Desktop rail
|
|
69
|
+
</Button>
|
|
61
70
|
</div>
|
|
62
71
|
) : null}
|
|
63
72
|
|
|
64
73
|
<div className="grid gap-4 xl:grid-cols-[320px_minmax(0,1fr)]">
|
|
65
|
-
<section className={panelClass}>
|
|
74
|
+
<section className={cn(panelClass, mobilePreview && "mx-auto w-full max-w-[420px]")}>
|
|
66
75
|
<AppSidebar
|
|
67
76
|
collapsed={collapsed}
|
|
68
77
|
items={items}
|
|
78
|
+
responsive
|
|
79
|
+
mobileBreakpoint={mobilePreview ? 10000 : 0}
|
|
80
|
+
mobileTitle="Workspace navigation"
|
|
81
|
+
mobileDescription="Open routes, check badges, and move between sections from one compact drawer."
|
|
82
|
+
mobileToggleLabel="Open workspace menu"
|
|
69
83
|
header={<div className="px-3 py-2 text-sm font-semibold">Azamat Workspace</div>}
|
|
70
84
|
footer={<div className="aui-text-muted px-3 py-2 text-xs">Starter plan • 3 editors</div>}
|
|
71
85
|
className="min-h-[440px] rounded-[22px] border border-[color:var(--aui-surface-border)] bg-[color:var(--aui-surface)]"
|
|
@@ -81,8 +95,8 @@ export function AppSidebarShowcase({ mode }: ComponentDemoProps) {
|
|
|
81
95
|
|
|
82
96
|
<div className="mt-6 grid gap-3 sm:grid-cols-3">
|
|
83
97
|
<div className="rounded-[20px] border border-[color:var(--aui-divider)] bg-[color:var(--aui-page-bg-alt)] px-4 py-3">
|
|
84
|
-
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] aui-text-muted">
|
|
85
|
-
<p className="mt-2 text-lg font-semibold aui-text-strong">{collapsed ? "Collapsed" : "Expanded"}</p>
|
|
98
|
+
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] aui-text-muted">Shell</p>
|
|
99
|
+
<p className="mt-2 text-lg font-semibold aui-text-strong">{mobilePreview ? "Mobile drawer" : (collapsed ? "Collapsed" : "Expanded")}</p>
|
|
86
100
|
</div>
|
|
87
101
|
<div className="rounded-[20px] border border-[color:var(--aui-divider)] bg-[color:var(--aui-page-bg-alt)] px-4 py-3">
|
|
88
102
|
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] aui-text-muted">Visible items</p>
|