Files
Homarr/src/components/Dashboard/Views/DashboardView.tsx
2023-10-09 06:30:43 +02:00

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
);
};