Files
Multi-Debrid-Downloader/src/renderer/shell/AppShell.tsx
T

109 lines
3.5 KiB
TypeScript

import { useState, useSyncExternalStore, type ReactElement, type ReactNode } from "react";
import { ContextInfoButton } from "../ui/ContextInfoButton";
import { AppHeader } from "./AppHeader";
import { AppSidebar } from "./AppSidebar";
import {
getMainViewLabel,
getResponsiveShellMode,
type MainView,
type ResponsiveShellMode
} from "./shell-model";
import "./shell.css";
function subscribeToViewport(callback: () => void): () => void {
if (typeof window === "undefined") {
return () => undefined;
}
window.addEventListener("resize", callback);
return () => window.removeEventListener("resize", callback);
}
function getViewportShellMode(): ResponsiveShellMode {
return typeof window === "undefined" ? "full" : getResponsiveShellMode(window.innerWidth);
}
function getServerShellMode(): ResponsiveShellMode {
return "full";
}
export interface AppShellProps {
activeView: MainView;
animationsEnabled?: boolean;
onViewChange: (view: MainView) => void;
sidebar: ReactNode;
sidebarStatus: ReactNode;
headerActions: ReactNode;
toolbar: ReactNode;
children: ReactNode;
footer: ReactNode;
contextInfo: ReactNode;
sidebarCollapsed: boolean;
onSidebarCollapsedChange: (collapsed: boolean) => void;
}
export function AppShell({
activeView,
animationsEnabled = true,
onViewChange,
sidebar,
sidebarStatus,
headerActions,
toolbar,
children,
footer,
contextInfo,
sidebarCollapsed,
onSidebarCollapsedChange
}: AppShellProps): ReactElement {
const [infoOpen, setInfoOpen] = useState(false);
const [responsiveSidebarExpanded, setResponsiveSidebarExpanded] = useState(false);
const responsiveMode = useSyncExternalStore(
subscribeToViewport,
getViewportShellMode,
getServerShellMode
);
const hasSidebar = Boolean(sidebar || sidebarStatus);
const responsiveSidebarCollapsed = responsiveMode !== "full" && !responsiveSidebarExpanded;
const effectiveSidebarCollapsed = sidebarCollapsed || responsiveSidebarCollapsed;
const setSidebarCollapsed = (collapsed: boolean): void => {
if (responsiveMode !== "full") {
setResponsiveSidebarExpanded(!collapsed);
}
onSidebarCollapsedChange(collapsed);
};
return (
<div
className={`md-shell is-${responsiveMode}${animationsEnabled ? " is-runtime-motion-enabled" : " is-runtime-motion-disabled"}${hasSidebar ? " has-sidebar" : ""}${hasSidebar && effectiveSidebarCollapsed ? " has-collapsed-sidebar" : ""}`}
data-responsive-mode={responsiveMode}
>
<AppHeader activeView={activeView} actions={headerActions} onViewChange={onViewChange} />
<div className="md-shell-workspace">
{hasSidebar ? (
<AppSidebar
collapsed={effectiveSidebarCollapsed}
onCollapsedChange={setSidebarCollapsed}
responsiveRail={responsiveSidebarCollapsed}
status={sidebarStatus}
>
{sidebar}
</AppSidebar>
) : null}
<section className="md-shell-main" data-ui-region="main">
{toolbar ? <div className="md-shell-toolbar" data-ui-region="toolbar">{toolbar}</div> : null}
<div className="md-shell-content">{children}</div>
{footer ? <div className="md-shell-footer" data-ui-region="footer">{footer}</div> : null}
<ContextInfoButton
content={contextInfo}
contextName={getMainViewLabel(activeView)}
onOpenChange={setInfoOpen}
open={infoOpen}
/>
</section>
</div>
</div>
);
}