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.
Files changed (30) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/components/data-table/data-table.cjs +1 -1
  3. package/dist/components/data-table/data-table.js +68 -68
  4. package/dist/components/layout/app-sidebar.cjs +1 -1
  5. package/dist/components/layout/app-sidebar.d.ts +19 -1
  6. package/dist/components/layout/app-sidebar.js +253 -126
  7. package/dist/hooks/index.cjs +1 -1
  8. package/dist/hooks/index.js +4 -4
  9. package/dist/index.cjs +1 -1
  10. package/dist/index.js +134 -134
  11. package/dist/showcase/package-meta.cjs +1 -1
  12. package/dist/showcase/package-meta.d.ts +3 -3
  13. package/dist/showcase/package-meta.js +1 -1
  14. package/dist/showcase/premium/app-shell/showcase.cjs +1 -1
  15. package/dist/showcase/premium/app-shell/showcase.js +5 -1
  16. package/dist/showcase/premium/app-sidebar/showcase.cjs +1 -1
  17. package/dist/showcase/premium/app-sidebar/showcase.js +91 -70
  18. package/dist/showcase/site-data.cjs +1 -1
  19. package/dist/showcase/site-data.js +1 -1
  20. package/dist/showcase/tembro-registry.json.cjs +1 -1
  21. package/dist/showcase/tembro-registry.json.js +1 -1
  22. package/package.json +1 -1
  23. package/packages/cli/dist/index.cjs +1 -1
  24. package/packages/cli/vendor/src/components/data-table/data-table.tsx +22 -13
  25. package/packages/cli/vendor/src/components/layout/app-sidebar.tsx +259 -21
  26. package/packages/cli/vendor/src/showcase/package-meta.ts +2 -2
  27. package/packages/cli/vendor/src/showcase/premium/app-shell/showcase.tsx +5 -1
  28. package/packages/cli/vendor/src/showcase/premium/app-sidebar/showcase.tsx +18 -4
  29. package/packages/cli/vendor/src/showcase/tembro-registry.json +1 -1
  30. 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.onSelect?.()
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.onSelect?.()
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.onSelect?.()
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.onSelect?.()
172
- onItemSelect?.(item)
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 && <span data-slot="app-sidebar-group-chevron" className="ml-auto text-xs text-muted-foreground transition-transform group-open/app-sidebar-details:rotate-90">›</span>}
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.onSelect?.()
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 AppSidebar({
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 = true,
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 && <div data-slot="app-sidebar-header" className="shrink-0 border-b p-3">{header}</div>}
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={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={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={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 account={footerAccount} collapsed />
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.0"
2
- export const PACKAGE_LATEST_RELEASE_DATE = "July 7, 2026"
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
- className={collapsed ? "hidden lg:block" : ""}
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">Collapsed rail</Badge>
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">Mode</p>
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>
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://tembro.dev/registry.schema.json",
3
3
  "name": "tembro",
4
- "version": "2.0.0",
4
+ "version": "2.0.2",
5
5
  "description": "Reusable React + TypeScript UI kit registry",
6
6
  "groups": {
7
7
  "ui": [
package/registry.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://tembro.dev/registry.schema.json",
3
3
  "name": "tembro",
4
- "version": "2.0.0",
4
+ "version": "2.0.2",
5
5
  "description": "Reusable React + TypeScript UI kit registry",
6
6
  "groups": {
7
7
  "ui": [