Fszontagh 3 mesiacov pred
rodič
commit
f7982eca33

+ 59 - 0
webui/components/layout/app-layout.tsx

@@ -0,0 +1,59 @@
+'use client';
+
+import { ReactNode } from 'react';
+import { Sidebar } from '../sidebar';
+import { ModelStatusBar } from '../model-status-bar';
+import { LayoutProvider } from './layout-context';
+
+/**
+ * AppLayout - Pure inline style layout (no CSS modules)
+ * Using inline styles to avoid any CSS loading/cascade issues
+ */
+
+interface AppLayoutProps {
+  children: ReactNode;
+}
+
+export function AppLayout({ children }: AppLayoutProps) {
+  return (
+    <LayoutProvider>
+      <div style={{
+        display: 'grid',
+        gridTemplateColumns: '16rem 1fr',
+        gridTemplateRows: '1fr auto',
+        gridTemplateAreas: '"sidebar main" "sidebar footer"',
+        minHeight: '100vh',
+        width: '100vw',
+        overflowX: 'hidden',
+      }}>
+        <aside style={{
+          gridArea: 'sidebar',
+          position: 'sticky',
+          top: 0,
+          height: '100vh',
+          overflowY: 'auto',
+          borderRight: '1px solid var(--color-border)',
+          backgroundColor: 'var(--color-card)',
+          zIndex: 10000,
+        }}>
+          <Sidebar />
+        </aside>
+        <main style={{
+          gridArea: 'main',
+          minHeight: '100vh',
+          maxWidth: 'calc(100vw - 16rem)',
+          overflowX: 'hidden',
+          backgroundColor: 'var(--color-background)',
+        }}>
+          {children}
+        </main>
+        <footer style={{
+          gridArea: 'footer',
+          minHeight: '3rem',
+        }}>
+          <ModelStatusBar />
+        </footer>
+      </div>
+    </LayoutProvider>
+  );
+}

+ 11 - 0
webui/components/layout/index.ts

@@ -0,0 +1,11 @@
+/**
+ * Layout Module - Modern component-based layout system
+ *
+ * Exports:
+ * - AppLayout: Main grid-based layout component
+ * - LayoutProvider: Context provider for layout state
+ * - useLayout: Hook for accessing layout context
+ */
+
+export { AppLayout } from './app-layout';
+export { LayoutProvider, useLayout } from './layout-context';

+ 48 - 0
webui/components/layout/layout-context.tsx

@@ -0,0 +1,48 @@
+'use client';
+
+import { createContext, useContext, useState, ReactNode } from 'react';
+
+/**
+ * Layout Context - Manages global layout state
+ * Modern OOP pattern using Context API for shared state management
+ */
+
+interface LayoutContextValue {
+  sidebarCollapsed: boolean;
+  toggleSidebar: () => void;
+  setSidebarCollapsed: (collapsed: boolean) => void;
+}
+
+const LayoutContext = createContext<LayoutContextValue | undefined>(undefined);
+
+export function useLayout() {
+  const context = useContext(LayoutContext);
+  if (!context) {
+    throw new Error('useLayout must be used within LayoutProvider');
+  }
+  return context;
+}
+
+interface LayoutProviderProps {
+  children: ReactNode;
+}
+
+export function LayoutProvider({ children }: LayoutProviderProps) {
+  const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
+
+  const toggleSidebar = () => {
+    setSidebarCollapsed(prev => !prev);
+  };
+
+  const value: LayoutContextValue = {
+    sidebarCollapsed,
+    toggleSidebar,
+    setSidebarCollapsed,
+  };
+
+  return (
+    <LayoutContext.Provider value={value}>
+      {children}
+    </LayoutContext.Provider>
+  );
+}

+ 61 - 0
webui/components/layout/layout.module.css

@@ -0,0 +1,61 @@
+/* Modern Grid-based Layout System */
+
+.appGrid {
+  display: grid;
+  grid-template-columns: 16rem 1fr;
+  grid-template-rows: 1fr auto;
+  grid-template-areas:
+    "sidebar main"
+    "sidebar footer";
+  min-height: 100vh;
+  width: 100vw;
+  overflow-x: hidden;
+}
+
+.sidebar {
+  grid-area: sidebar;
+  position: sticky;
+  top: 0;
+  height: 100vh;
+  overflow-y: auto;
+  border-right: 1px solid var(--color-border);
+  background-color: var(--color-card);
+  z-index: 9999;
+  pointer-events: auto;
+}
+
+.main {
+  grid-area: main;
+  min-height: 100vh;
+  max-width: calc(100vw - 16rem);
+  overflow-x: hidden;
+  background-color: var(--color-background);
+  isolation: isolate;
+}
+
+.footer {
+  grid-area: footer;
+  min-height: 3rem;
+}
+
+/* Ensure sidebar is always interactive */
+.sidebar * {
+  pointer-events: auto;
+}
+
+/* Responsive: Stack on mobile */
+@media (max-width: 768px) {
+  .appGrid {
+    grid-template-columns: 1fr;
+    grid-template-rows: auto 1fr auto;
+    grid-template-areas:
+      "sidebar"
+      "main"
+      "footer";
+  }
+
+  .sidebar {
+    position: relative;
+    height: auto;
+  }
+}