All files / islands / LayoutManager.tsx

100.00% Branches 0/0
6.06% Lines 6/99
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
x2
x2
x2
 
x2
x2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x2
















































































































































































import Footer from "@components/layout/Footer.tsx";
import Header from "@components/layout/Header.tsx";
import Sidebar from "@components/layout/Sidebar.tsx";
import { JSX } from "preact";
import { useEffect, useState } from "preact/hooks";
import BackToTop from "./BackToTop.tsx";

interface LayoutManagerProps {
  children: JSX.Element | JSX.Element[] | string;
  title?: string;
  showSidebar?: boolean;
  /** 是否显示返回顶部按钮,默认 true */
  showBackToTop?: boolean;
  /** 返回顶部按钮样式变体 */
  backToTopVariant?:
    | "default"
    | "primary"
    | "secondary"
    | "success"
    | "warning"
    | "danger";
  /** 返回顶部按钮显示阈值,默认 300px */
  backToTopThreshold?: number;
  /** 强制显示返回顶部按钮(用于调试),默认 false */
  backToTopForceVisible?: boolean;
}

const LayoutManager = ({
  children,
  title,
  showSidebar = true,
  showBackToTop = true,
  backToTopVariant = "primary",
  backToTopThreshold = 300,
  backToTopForceVisible = false,
}: LayoutManagerProps) => {
  const [sidebarOpen, setSidebarOpen] = useState(false);
  const [sidebarVisible, setSidebarVisible] = useState(showSidebar);
  const [isLargeScreen, setIsLargeScreen] = useState(true);

  // 初始化主题
  useEffect(() => {
    // 初始化主题逻辑
    const initTheme = () => {
      const savedTheme = localStorage.getItem("theme-storage");
      if (savedTheme) {
        try {
          const themeData = JSON.parse(savedTheme);
          const theme = themeData.state?.theme || "system";

          let isDark = false;
          if (theme === "system") {
            isDark =
              globalThis.window?.matchMedia("(prefers-color-scheme: dark)")
                .matches || false;
          } else {
            isDark = theme === "dark";
          }

          if (isDark) {
            document.documentElement.classList.add("dark");
          } else {
            document.documentElement.classList.remove("dark");
          }
        } catch (error) {
          console.warn("Failed to parse theme data:", error);
        }
      }
    };

    initTheme();
  }, []);

  // 检测屏幕尺寸和侧边栏状态
  useEffect(() => {
    const checkScreenSize = () => {
      const isLarge = globalThis.innerWidth >= 1024; // lg断点
      setIsLargeScreen(isLarge);

      if (isLarge) {
        // 大屏幕下,侧边栏默认显示,但可以通过sidebarVisible控制
        setSidebarOpen(false); // 重置移动端状态
      } else {
        // 小屏幕下,侧边栏默认隐藏
        setSidebarVisible(true); // 小屏幕下总是显示切换按钮
        setSidebarOpen(false);
      }
    };

    checkScreenSize();
    globalThis.addEventListener("resize", checkScreenSize);
    return () => globalThis.removeEventListener("resize", checkScreenSize);
  }, []);

  const toggleSidebar = () => {
    if (isLargeScreen) {
      // 大屏幕下切换侧边栏显示/隐藏
      setSidebarVisible(!sidebarVisible);
    } else {
      // 小屏幕下切换移动端侧边栏
      setSidebarOpen(!sidebarOpen);
    }
  };

  const closeSidebar = () => {
    setSidebarOpen(false);
  };

  return (
    <>
      <div className="
        h-screen max-h-screen flex flex-col
        bg-gradient-to-br from-neutral-50 via-white to-neutral-50
        dark:from-neutral-900 dark:via-neutral-800 dark:to-neutral-900
        transition-all duration-500 ease-out
        overflow-hidden
      ">
        {/* 固定头部 */}
        <Header
          title={title}
          showSidebarToggle
          onSidebarToggle={toggleSidebar}
          className="flex-shrink-0 z-30"
        />

        {/* 中间内容区域 - 受限高度 */}
        <div className="flex flex-1 min-h-0 overflow-hidden">
          {/* 侧边栏 */}
          {sidebarVisible && (
            <Sidebar
              isOpen={sidebarOpen}
              onClose={closeSidebar}
            />
          )}

          {/* 主内容区域 - 严格控制高度 */}
          <main className="
            flex-1 overflow-y-auto
            transition-all duration-300 ease-in-out
            h-full max-h-full
            scrollbar-hide scroll-animate
          ">
            <div
              className={`
                py-6 px-4 md:px-6
                h-full min-h-full
                transition-all duration-300 ease-in-out
                ${sidebarVisible ? "lg:pl-6 lg:pr-6" : ""}
                max-w-none w-full
                flex flex-col
              `}
            >
              <div className="flex-1 animate-fade-in animate-delay-100">
                {children}
              </div>
            </div>
          </main>
        </div>

        {/* 固定底部 */}
        <Footer className="flex-shrink-0 z-20" />
      </div>

      {/* 返回顶部按钮 - 放在外层避免被overflow-hidden影响 */}
      {showBackToTop && (
        <BackToTop
          variant={backToTopVariant}
          threshold={backToTopThreshold}
          size="md"
          position="bottom-right"
          forceVisible={backToTopForceVisible}
        />
      )}
    </>
  );
};

export default LayoutManager;