another step for constructing base project

This commit is contained in:
hamid
2026-06-17 22:53:49 +03:30
parent 5b4c0d183f
commit 5388bea320
76 changed files with 3836 additions and 1961 deletions
+19 -39
View File
@@ -3,11 +3,11 @@ import { FunctionComponent, useMemo, useState } from 'react';
import { Stack, StackProps } from '@mui/material';
import { IS_DEBUG } from '@/config';
import { AppIconButton, ErrorBoundary } from '@/components';
import { useAppStore } from '@/store';
import { LinkToPage } from '@/utils';
import { useEventSwitchDarkMode, useIsMobile } from '@/hooks';
import { useIsMobile } from '@/hooks';
import { TopBar } from './components';
import SideBar, { SideBarProps } from './components/SideBar';
import { DarkModeToggleButton } from './components/DarkModeButton';
import {
SIDE_BAR_DESKTOP_ANCHOR,
SIDE_BAR_MOBILE_ANCHOR,
@@ -23,14 +23,13 @@ interface Props extends StackProps {
}
/**
* Renders "TopBar and SideBar" composition
* Renders "TopBar and SideBar" composition.
* Does NOT subscribe to the color scheme — only <DarkModeToggleButton> does.
* @layout TopBarAndSideBarLayout
*/
const TopBarAndSideBarLayout: FunctionComponent<Props> = ({ children, sidebarItems, title, variant }) => {
const [state] = useAppStore();
const [sidebarVisible, setSidebarVisible] = useState(false); // TODO: Verify is default value is correct
const [sidebarVisible, setSidebarVisible] = useState(false);
const onMobile = useIsMobile();
const onSwitchDarkMode = useEventSwitchDarkMode();
const sidebarProps = useMemo((): Partial<SideBarProps> => {
const anchor = onMobile ? SIDE_BAR_MOBILE_ANCHOR : SIDE_BAR_DESKTOP_ANCHOR;
@@ -53,7 +52,7 @@ const TopBarAndSideBarLayout: FunctionComponent<Props> = ({ children, sidebarIte
const stackStyles = useMemo(
() => ({
minHeight: '100vh', // Full screen height
minHeight: '100vh',
paddingTop: onMobile ? TOP_BAR_MOBILE_HEIGHT : TOP_BAR_DESKTOP_HEIGHT,
paddingLeft:
sidebarProps.variant === 'persistent' && sidebarProps.open && sidebarProps?.anchor?.includes('left')
@@ -67,43 +66,28 @@ const TopBarAndSideBarLayout: FunctionComponent<Props> = ({ children, sidebarIte
[onMobile, sidebarProps]
);
const onSideBarOpen = () => {
if (!sidebarVisible) setSidebarVisible(true); // Don't re-render Layout when SideBar is already open
};
const onSideBarClose = () => {
if (sidebarVisible) setSidebarVisible(false); // Don't re-render Layout when SideBar is already closed
};
const onSideBarOpen = () => { if (!sidebarVisible) setSidebarVisible(true); };
const onSideBarClose = () => { if (sidebarVisible) setSidebarVisible(false); };
const LogoButton = (
<AppIconButton
icon="logo"
title={sidebarProps.open ? undefined : 'Open Sidebar'}
to={sidebarProps.open ? '/' : undefined} // Navigate to Home only when SideBar is closed
onClick={sidebarProps.open ? undefined : onSideBarOpen} // Open SideBar only when it's closed
to={sidebarProps.open ? '/' : undefined}
onClick={sidebarProps.open ? undefined : onSideBarOpen}
/>
);
const DarkModeButton = (
<AppIconButton
icon={state.darkMode ? 'day' : 'night'} // Variant 1
// icon="daynight" // Variant 2
title={state.darkMode ? 'Switch to Light mode' : 'Switch to Dark mode'}
onClick={onSwitchDarkMode}
/>
);
// Note: useMemo() is not needed for startNode, endNode. We need respect store.darkMode and so on.
/*
* DarkModeToggleButton is a self-contained component that subscribes to
* useColorScheme() on its own. This layout component never reads the color
* scheme and therefore never re-renders when the theme switches.
*/
const { startNode, endNode } = sidebarProps?.anchor?.includes('left')
? { startNode: LogoButton, endNode: DarkModeButton }
: { startNode: DarkModeButton, endNode: LogoButton };
? { startNode: LogoButton, endNode: <DarkModeToggleButton /> }
: { startNode: <DarkModeToggleButton />, endNode: LogoButton };
IS_DEBUG &&
console.log('Render <TopbarAndSidebarLayout/>', {
onMobile,
darkMode: state.darkMode,
sidebarProps,
});
IS_DEBUG && console.log('Render <TopbarAndSidebarLayout/>', { onMobile, sidebarProps });
return (
<Stack sx={stackStyles}>
@@ -114,11 +98,7 @@ const TopBarAndSideBarLayout: FunctionComponent<Props> = ({ children, sidebarIte
<Stack
component="main"
flexGrow={1} // Takes all possible space
justifyContent="space-between" // Push children content (Footer, StatusBar, etc.) to the bottom
paddingLeft={1}
paddingRight={1}
paddingTop={1}
sx={{ flexGrow: 1, justifyContent: 'space-between', paddingLeft: 1, paddingRight: 1, paddingTop: 1 }}
>
<ErrorBoundary name="Content">{children}</ErrorBoundary>
</Stack>