mirror of
https://github.com/lordmathis/lemma.git
synced 2025-11-06 07:54:22 +00:00
Update layout
This commit is contained in:
@@ -1,14 +1,8 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
MantineProvider,
|
||||
ColorSchemeScript,
|
||||
AppShell,
|
||||
Container,
|
||||
} from '@mantine/core';
|
||||
import { MantineProvider, ColorSchemeScript } from '@mantine/core';
|
||||
import { Notifications } from '@mantine/notifications';
|
||||
import { ModalsProvider } from '@mantine/modals';
|
||||
import Header from './components/Header';
|
||||
import MainContent from './components/MainContent';
|
||||
import Layout from './components/Layout';
|
||||
import { SettingsProvider, useSettings } from './contexts/SettingsContext';
|
||||
import { ModalProvider } from './contexts/ModalContext';
|
||||
import '@mantine/core/styles.css';
|
||||
@@ -22,18 +16,7 @@ function AppContent() {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<AppShell header={{ height: 60 }} padding="md">
|
||||
<AppShell.Header>
|
||||
<Header />
|
||||
</AppShell.Header>
|
||||
<AppShell.Main>
|
||||
<Container size="xl">
|
||||
<MainContent />
|
||||
</Container>
|
||||
</AppShell.Main>
|
||||
</AppShell>
|
||||
);
|
||||
return <Layout />;
|
||||
}
|
||||
|
||||
function App() {
|
||||
|
||||
42
frontend/src/components/Layout.js
Normal file
42
frontend/src/components/Layout.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import { AppShell, Container } from '@mantine/core';
|
||||
import Header from './Header';
|
||||
import Sidebar from './Sidebar';
|
||||
import MainContent from './MainContent';
|
||||
import { useFileNavigation } from '../hooks/useFileNavigation';
|
||||
|
||||
const Layout = () => {
|
||||
const { selectedFile, handleFileSelect, handleLinkClick } =
|
||||
useFileNavigation();
|
||||
|
||||
return (
|
||||
<AppShell header={{ height: 60 }} padding="md">
|
||||
<AppShell.Header>
|
||||
<Header />
|
||||
</AppShell.Header>
|
||||
<AppShell.Main>
|
||||
<Container
|
||||
size="xl"
|
||||
p={0}
|
||||
style={{
|
||||
display: 'flex',
|
||||
height: 'calc(100vh - 60px - 2rem)', // Subtracting header height and vertical padding
|
||||
overflow: 'hidden', // Prevent scrolling in the container
|
||||
}}
|
||||
>
|
||||
<Sidebar
|
||||
selectedFile={selectedFile}
|
||||
handleFileSelect={handleFileSelect}
|
||||
/>
|
||||
<MainContent
|
||||
selectedFile={selectedFile}
|
||||
handleFileSelect={handleFileSelect}
|
||||
handleLinkClick={handleLinkClick}
|
||||
/>
|
||||
</Container>
|
||||
</AppShell.Main>
|
||||
</AppShell>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -1,27 +1,20 @@
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import { Tabs, Breadcrumbs, Group, Box, Text, Flex } from '@mantine/core';
|
||||
import { IconCode, IconEye, IconPointFilled } from '@tabler/icons-react';
|
||||
|
||||
import FileActions from './FileActions';
|
||||
import FileTree from './FileTree';
|
||||
import ContentView from './ContentView';
|
||||
import CreateFileModal from './modals/CreateFileModal';
|
||||
import DeleteFileModal from './modals/DeleteFileModal';
|
||||
import CommitMessageModal from './modals/CommitMessageModal';
|
||||
|
||||
import { useFileContent } from '../hooks/useFileContent';
|
||||
import { useFileList } from '../hooks/useFileList';
|
||||
import { useFileOperations } from '../hooks/useFileOperations';
|
||||
import { useGitOperations } from '../hooks/useGitOperations';
|
||||
import { useFileNavigation } from '../hooks/useFileNavigation';
|
||||
import { useSettings } from '../contexts/SettingsContext';
|
||||
|
||||
const MainContent = () => {
|
||||
const MainContent = ({ selectedFile, handleFileSelect, handleLinkClick }) => {
|
||||
const [activeTab, setActiveTab] = useState('source');
|
||||
const { settings } = useSettings();
|
||||
const { files, loadFileList } = useFileList();
|
||||
const { handleLinkClick, selectedFile, handleFileSelect } =
|
||||
useFileNavigation();
|
||||
const {
|
||||
content,
|
||||
hasUnsavedChanges,
|
||||
@@ -29,15 +22,11 @@ const MainContent = () => {
|
||||
handleContentChange,
|
||||
} = useFileContent(selectedFile);
|
||||
const { handleSave, handleCreate, handleDelete } = useFileOperations();
|
||||
const { handleCommitAndPush, handlePull } = useGitOperations();
|
||||
const { handleCommitAndPush } = useGitOperations(settings.gitEnabled);
|
||||
|
||||
useEffect(() => {
|
||||
loadFileList();
|
||||
}, [settings.gitEnabled]);
|
||||
|
||||
const handleTabChange = (value) => {
|
||||
const handleTabChange = useCallback((value) => {
|
||||
setActiveTab(value);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleSaveFile = useCallback(
|
||||
async (filePath, content) => {
|
||||
@@ -54,25 +43,23 @@ const MainContent = () => {
|
||||
async (fileName) => {
|
||||
const success = await handleCreate(fileName);
|
||||
if (success) {
|
||||
await loadFileList();
|
||||
handleFileSelect(fileName);
|
||||
}
|
||||
},
|
||||
[handleCreate, loadFileList, handleFileSelect]
|
||||
[handleCreate, handleFileSelect]
|
||||
);
|
||||
|
||||
const handleDeleteFile = useCallback(
|
||||
async (filePath) => {
|
||||
const success = await handleDelete(filePath);
|
||||
if (success) {
|
||||
await loadFileList();
|
||||
handleFileSelect(null);
|
||||
}
|
||||
},
|
||||
[handleDelete, loadFileList, handleFileSelect]
|
||||
[handleDelete, handleFileSelect]
|
||||
);
|
||||
|
||||
const renderBreadcrumbs = () => {
|
||||
const renderBreadcrumbs = useMemo(() => {
|
||||
if (!selectedFile) return null;
|
||||
const pathParts = selectedFile.split('/');
|
||||
const items = pathParts.map((part, index) => (
|
||||
@@ -92,23 +79,9 @@ const MainContent = () => {
|
||||
)}
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
}, [selectedFile, hasUnsavedChanges]);
|
||||
|
||||
return (
|
||||
<Box style={{ height: 'calc(100vh - 60px)', display: 'flex' }}>
|
||||
<Box
|
||||
style={{
|
||||
width: '300px',
|
||||
borderRight: '1px solid var(--mantine-color-gray-3)',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<FileActions
|
||||
handlePullChanges={handlePull}
|
||||
selectedFile={selectedFile}
|
||||
/>
|
||||
<FileTree files={files} handleFileSelect={handleFileSelect} />
|
||||
</Box>
|
||||
<Box
|
||||
style={{
|
||||
flex: 1,
|
||||
@@ -118,17 +91,11 @@ const MainContent = () => {
|
||||
}}
|
||||
>
|
||||
<Flex justify="space-between" align="center" p="md">
|
||||
{renderBreadcrumbs()}
|
||||
{renderBreadcrumbs}
|
||||
<Tabs value={activeTab} onChange={handleTabChange}>
|
||||
<Tabs.List>
|
||||
<Tabs.Tab
|
||||
value="source"
|
||||
leftSection={<IconCode size="0.8rem" />}
|
||||
/>
|
||||
<Tabs.Tab
|
||||
value="preview"
|
||||
leftSection={<IconEye size="0.8rem" />}
|
||||
/>
|
||||
<Tabs.Tab value="source" leftSection={<IconCode size="0.8rem" />} />
|
||||
<Tabs.Tab value="preview" leftSection={<IconEye size="0.8rem" />} />
|
||||
</Tabs.List>
|
||||
</Tabs>
|
||||
</Flex>
|
||||
@@ -142,7 +109,6 @@ const MainContent = () => {
|
||||
handleLinkClick={handleLinkClick}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<CreateFileModal onCreateFile={handleCreateFile} />
|
||||
<DeleteFileModal
|
||||
onDeleteFile={handleDeleteFile}
|
||||
|
||||
39
frontend/src/components/Sidebar.js
Normal file
39
frontend/src/components/Sidebar.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Box } from '@mantine/core';
|
||||
import FileActions from './FileActions';
|
||||
import FileTree from './FileTree';
|
||||
import { useFileList } from '../hooks/useFileList';
|
||||
import { useGitOperations } from '../hooks/useGitOperations';
|
||||
import { useSettings } from '../contexts/SettingsContext';
|
||||
|
||||
const Sidebar = ({ selectedFile, handleFileSelect }) => {
|
||||
const { settings } = useSettings();
|
||||
const { files, loadFileList } = useFileList();
|
||||
const { handlePull } = useGitOperations(settings.gitEnabled);
|
||||
|
||||
useEffect(() => {
|
||||
loadFileList();
|
||||
}, [settings.gitEnabled, loadFileList]);
|
||||
|
||||
return (
|
||||
<Box
|
||||
style={{
|
||||
width: '25%',
|
||||
minWidth: '200px',
|
||||
maxWidth: '300px',
|
||||
borderRight: '1px solid var(--app-shell-border-color)',
|
||||
height: '100%',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<FileActions handlePullChanges={handlePull} selectedFile={selectedFile} />
|
||||
<FileTree
|
||||
files={files}
|
||||
handleFileSelect={handleFileSelect}
|
||||
selectedFile={selectedFile}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sidebar;
|
||||
@@ -1,4 +1,11 @@
|
||||
import React, { createContext, useContext, useEffect, useMemo } from 'react';
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useCallback,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useMantineColorScheme } from '@mantine/core';
|
||||
import { fetchUserSettings, saveUserSettings } from '../services/api';
|
||||
import { DEFAULT_SETTINGS } from '../utils/constants';
|
||||
@@ -9,8 +16,8 @@ export const useSettings = () => useContext(SettingsContext);
|
||||
|
||||
export const SettingsProvider = ({ children }) => {
|
||||
const { colorScheme, setColorScheme } = useMantineColorScheme();
|
||||
const [settings, setSettings] = React.useState(DEFAULT_SETTINGS);
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const [settings, setSettings] = useState(DEFAULT_SETTINGS);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const loadSettings = async () => {
|
||||
@@ -26,16 +33,16 @@ export const SettingsProvider = ({ children }) => {
|
||||
};
|
||||
|
||||
loadSettings();
|
||||
}, [setColorScheme]);
|
||||
}, []);
|
||||
|
||||
const updateSettings = async (newSettings) => {
|
||||
const updateSettings = useCallback(
|
||||
async (newSettings) => {
|
||||
try {
|
||||
await saveUserSettings({
|
||||
userId: 1,
|
||||
settings: newSettings,
|
||||
});
|
||||
setSettings(newSettings);
|
||||
// Ensure the color scheme is updated when settings are saved
|
||||
if (newSettings.theme) {
|
||||
setColorScheme(newSettings.theme);
|
||||
}
|
||||
@@ -43,13 +50,15 @@ export const SettingsProvider = ({ children }) => {
|
||||
console.error('Failed to save settings:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
},
|
||||
[setColorScheme]
|
||||
);
|
||||
|
||||
const toggleColorScheme = () => {
|
||||
const toggleColorScheme = useCallback(() => {
|
||||
const newTheme = colorScheme === 'dark' ? 'light' : 'dark';
|
||||
setColorScheme(newTheme);
|
||||
updateSettings({ ...settings, theme: newTheme });
|
||||
};
|
||||
}, [colorScheme, settings, setColorScheme, updateSettings]);
|
||||
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
@@ -59,7 +68,7 @@ export const SettingsProvider = ({ children }) => {
|
||||
loading,
|
||||
colorScheme,
|
||||
}),
|
||||
[settings, loading, colorScheme]
|
||||
[settings, updateSettings, toggleColorScheme, loading, colorScheme]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user