ResponsiveLayout.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. "use client";
  2. import * as React from "react";
  3. import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
  4. import { Button } from "@/components/ui/button";
  5. import { Menu } from "lucide-react";
  6. import { LAYOUT } from "@/lib/constants/breakpoints";
  7. /**
  8. * ResponsiveLayout - 响应式布局组件
  9. *
  10. * 提供统一的响应式布局,支持桌面端和移动端自适应。
  11. *
  12. * @example
  13. * ```tsx
  14. * <ResponsiveLayout
  15. * sidebar={<ConversationSidebar />}
  16. * main={<MessageList />}
  17. * rightPanel={<VisitorDetailPanel />}
  18. * />
  19. * ```
  20. *
  21. * @param sidebar - 侧边栏内容(桌面端显示,移动端可折叠)
  22. * @param main - 主内容区(所有设备都显示)
  23. * @param rightPanel - 右侧面板(大屏幕显示,小屏幕隐藏或折叠)
  24. * @param header - 顶部栏(可选)
  25. * @param className - 额外的 CSS 类名
  26. */
  27. export interface ResponsiveLayoutProps {
  28. sidebar?: React.ReactNode;
  29. main: React.ReactNode;
  30. rightPanel?: React.ReactNode;
  31. header?: React.ReactNode;
  32. className?: string;
  33. sidebarWidth?: string; // 侧边栏宽度(可选,默认使用 LAYOUT.sidebarWidth)
  34. }
  35. export function ResponsiveLayout({
  36. sidebar,
  37. main,
  38. rightPanel,
  39. header,
  40. className,
  41. sidebarWidth,
  42. }: ResponsiveLayoutProps) {
  43. const actualSidebarWidth = sidebarWidth || LAYOUT.sidebarWidth;
  44. return (
  45. <div className={`flex h-screen bg-background overflow-hidden ${className || ""}`}>
  46. {/* 桌面端侧边栏:中等屏幕及以上显示 */}
  47. {sidebar && (
  48. <aside className={`hidden md:block border-r bg-background flex-shrink-0`} style={{ width: actualSidebarWidth }}>
  49. {sidebar}
  50. </aside>
  51. )}
  52. {/* 移动端侧边栏:使用 Sheet 组件实现可折叠侧边栏 */}
  53. {sidebar && (
  54. <Sheet>
  55. <SheetTrigger asChild>
  56. <Button
  57. variant="ghost"
  58. size="icon"
  59. className="fixed top-4 left-4 z-50 md:hidden bg-background/80 backdrop-blur-sm"
  60. >
  61. <Menu className="h-6 w-6" />
  62. <span className="sr-only">打开菜单</span>
  63. </Button>
  64. </SheetTrigger>
  65. <SheetContent side="left" className="w-80 p-0" style={{ width: actualSidebarWidth }}>
  66. {sidebar}
  67. </SheetContent>
  68. </Sheet>
  69. )}
  70. {/* 主内容区 */}
  71. <div className="flex-1 flex flex-col min-h-0 overflow-hidden">
  72. {/* 顶部栏(如果提供) */}
  73. {header && (
  74. <header className="flex-shrink-0 border-b bg-background">
  75. {header}
  76. </header>
  77. )}
  78. {/* 主内容区和右侧面板容器 */}
  79. <div className="flex flex-1 min-h-0 overflow-hidden">
  80. {/* 主内容区 */}
  81. <main className="flex-1 flex flex-col min-h-0 overflow-hidden">
  82. {main}
  83. </main>
  84. {/* 右侧面板:大屏幕显示,小屏幕隐藏 */}
  85. {rightPanel && (
  86. <>
  87. <aside className={`hidden lg:block border-l bg-background flex-shrink-0`} style={{ width: LAYOUT.rightPanelWidth }}>
  88. {rightPanel}
  89. </aside>
  90. {/* 移动端右侧面板:使用 Sheet 组件实现可折叠面板 */}
  91. <Sheet>
  92. <SheetTrigger asChild>
  93. <Button
  94. variant="ghost"
  95. size="icon"
  96. className="fixed top-4 right-4 z-50 lg:hidden bg-background/80 backdrop-blur-sm"
  97. >
  98. <Menu className="h-6 w-6" />
  99. <span className="sr-only">打开详情</span>
  100. </Button>
  101. </SheetTrigger>
  102. <SheetContent side="right" className="w-80 p-0" style={{ width: LAYOUT.rightPanelWidth }}>
  103. {rightPanel}
  104. </SheetContent>
  105. </Sheet>
  106. </>
  107. )}
  108. </div>
  109. </div>
  110. </div>
  111. );
  112. }