mirror of
https://github.com/ajnart/homarr.git
synced 2026-05-05 18:27:52 +02:00
123 lines
4.1 KiB
TypeScript
123 lines
4.1 KiB
TypeScript
import { Box, Group, LoadingOverlay, Stack } from '@mantine/core';
|
|
import { useLocalStorage } from '@mantine/hooks';
|
|
import { useCallback, useEffect, useRef } from 'react';
|
|
import {
|
|
CategorySection,
|
|
EmptySection,
|
|
SidebarSection,
|
|
useRequiredBoard,
|
|
} from '~/components/Board/context';
|
|
import { useResize } from '~/hooks/use-resize';
|
|
import { useScreenLargerThan } from '~/hooks/useScreenLargerThan';
|
|
|
|
import { BoardCategorySection } from '../../Board/Sections/CategorySection';
|
|
import { BoardEmptySection } from '../../Board/Sections/EmptySection';
|
|
import { DashboardSidebar } from '../Wrappers/Sidebar/Sidebar';
|
|
import { useGridstackStore } from '../Wrappers/gridstack/store';
|
|
|
|
export const BoardView = () => {
|
|
const boardName = useRequiredBoard().name;
|
|
const stackedSections = useStackedSections();
|
|
const sidebarsVisible = useSidebarVisibility();
|
|
const { isReady, mainAreaRef } = usePrepareGridstack();
|
|
const leftSidebarSection = useSidebarSection('left');
|
|
const rightSidebarSection = useSidebarSection('right');
|
|
const [toggledCategories, setToggledCategories] = useLocalStorage({
|
|
key: `${boardName}-category-section-toggled`,
|
|
// This is a bit of a hack to toggle the categories on the first load, return a string[] of the categories
|
|
defaultValue: stackedSections.filter((s) => s.type === 'category').map((s) => s.id),
|
|
});
|
|
const toggleCategory = useCallback((categoryId: string) => {
|
|
setToggledCategories((current) => {
|
|
console.log('toggle', current, categoryId);
|
|
if (current.includes(categoryId)) {
|
|
return current.filter((x) => x !== categoryId);
|
|
}
|
|
return [...current, categoryId];
|
|
});
|
|
}, []);
|
|
|
|
return (
|
|
<Box h="100%" pos="relative">
|
|
<LoadingOverlay
|
|
visible={!isReady}
|
|
transitionDuration={500}
|
|
loaderProps={{ size: 'lg', variant: 'bars' }}
|
|
/>
|
|
<Group
|
|
align="top"
|
|
h="100%"
|
|
spacing="xs"
|
|
style={{ visibility: isReady ? 'visible' : 'hidden' }}
|
|
>
|
|
{sidebarsVisible.left && leftSidebarSection ? (
|
|
<DashboardSidebar section={leftSidebarSection} isGridstackReady={isReady} />
|
|
) : null}
|
|
|
|
<Stack ref={mainAreaRef} mx={-10} style={{ flexGrow: 1 }}>
|
|
{stackedSections.map((item) =>
|
|
item.type === 'category' ? (
|
|
<BoardCategorySection
|
|
key={item.id}
|
|
section={item}
|
|
isOpened={toggledCategories.includes(item.id)}
|
|
toggle={toggleCategory}
|
|
/>
|
|
) : (
|
|
<BoardEmptySection key={item.id} section={item} />
|
|
)
|
|
)}
|
|
</Stack>
|
|
|
|
{sidebarsVisible.right && rightSidebarSection ? (
|
|
<DashboardSidebar section={rightSidebarSection} isGridstackReady={isReady} />
|
|
) : null}
|
|
</Group>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
const usePrepareGridstack = () => {
|
|
const mainAreaRef = useRef<HTMLDivElement>(null);
|
|
const { width } = useResize(mainAreaRef, []);
|
|
const setMainAreaWidth = useGridstackStore((x) => x.setMainAreaWidth);
|
|
const mainAreaWidth = useGridstackStore((x) => x.mainAreaWidth);
|
|
|
|
useEffect(() => {
|
|
if (width === 0) return;
|
|
setMainAreaWidth(width);
|
|
}, [width]);
|
|
|
|
return {
|
|
isReady: !!mainAreaWidth,
|
|
mainAreaRef,
|
|
};
|
|
};
|
|
|
|
const useSidebarVisibility = () => {
|
|
const board = useRequiredBoard();
|
|
const screenLargerThanMd = useScreenLargerThan('md'); // For smaller screens mobile ribbons are displayed with drawers
|
|
|
|
const isScreenSizeUnknown = typeof screenLargerThanMd === 'undefined';
|
|
|
|
return {
|
|
right: board.isRightSidebarVisible && screenLargerThanMd,
|
|
left: board.isLeftSidebarVisible && screenLargerThanMd,
|
|
isLoading: isScreenSizeUnknown,
|
|
};
|
|
};
|
|
|
|
const useStackedSections = () => {
|
|
const board = useRequiredBoard();
|
|
return board.sections
|
|
.filter((s): s is CategorySection | EmptySection => s.type === 'category' || s.type === 'empty')
|
|
.sort((a, b) => a.position - b.position);
|
|
};
|
|
|
|
const useSidebarSection = (position: 'left' | 'right') => {
|
|
const board = useRequiredBoard();
|
|
return board.sections.find(
|
|
(s): s is SidebarSection => s.type === 'sidebar' && s.position === position
|
|
);
|
|
};
|