💄(frontend) add left panel

In the new interface there is a new left panel. We implement it and add it
to the MainLayout
This commit is contained in:
Nathan Panchout
2024-11-13 17:33:25 +01:00
parent d523493fa9
commit 7d14309032
15 changed files with 199 additions and 76 deletions
@@ -1,6 +1,14 @@
import { Text, TextType } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
type Props = {
iconName: string;
className?: string;
};
export const Icon = ({ iconName, className }: Props) => {
return <span className={`material-icons ${className}`}>{iconName}</span>;
};
interface IconBGProps extends TextType {
iconName: string;
}
@@ -0,0 +1,29 @@
import { PropsWithChildren } from 'react';
import { useCunninghamTheme } from '@/cunningham';
import { Box } from '../Box';
type Props = {
showSeparator?: boolean;
};
export const SeparatedSection = ({
showSeparator = true,
children,
}: PropsWithChildren<Props>) => {
const theme = useCunninghamTheme();
const colors = theme.colorsTokens();
return (
<Box
className="toto"
$padding={{ vertical: '100V' }}
$css={`
padding: 12px 0;
${showSeparator ? `border-bottom: 1px solid ${(colors?.['greyscale-200'] as string) ?? '#E5E5E5'};` : ''}
`}
>
{children}
</Box>
);
};
@@ -58,10 +58,10 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
)}
<Box
$background={colorsTokens()['primary-bg']}
$height="100%"
$direction="row"
$width="100%"
$margin={{ all: isMobile ? 'tiny' : 'small', top: 'none' }}
$css="overflow-x: clip;"
$css="overflow-x: clip; flex: 1;"
$position="relative"
>
<Card
@@ -1,4 +1,4 @@
import React, { Fragment } from 'react';
import { Fragment } from 'react';
import { useTranslation } from 'react-i18next';
import { Box, Card, StyledLink, Text } from '@/components';
@@ -32,6 +32,7 @@ export const DocHeader = ({ doc, versionId }: DocHeaderProps) => {
return (
<>
<Card
$width="100%"
$margin={isMobile ? 'tiny' : 'small'}
aria-label={t('It is the card information about the document.')}
>
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Box, BoxButton, Text } from '@/components';
@@ -49,7 +49,7 @@ export const TableContent = ({ doc, headings }: TableContentProps) => {
}
};
window.addEventListener('scroll', () => {
document.getElementById('mainContent')?.addEventListener('scroll', () => {
setTimeout(() => {
handleScroll();
}, 300);
@@ -1,40 +0,0 @@
import { Button } from '@openfun/cunningham-react';
import { useRouter } from 'next/navigation';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Box } from '@/components';
import { useCreateDoc, useTrans } from '@/features/docs/doc-management/';
import { useResponsiveStore } from '@/stores';
import { DocsGrid } from './DocsGrid';
export const DocsGridContainer = () => {
const { t } = useTranslation();
const { untitledDocument } = useTrans();
const router = useRouter();
const { isMobile } = useResponsiveStore();
const { mutate: createDoc } = useCreateDoc({
onSuccess: (doc) => {
router.push(`/docs/${doc.id}`);
},
});
const handleCreateDoc = () => {
createDoc({ title: untitledDocument });
};
return (
<Box $overflow="auto">
<Box
$align="flex-end"
$justify="center"
$margin={isMobile ? 'small' : 'big'}
>
<Button onClick={handleCreateDoc}>{t('Create a new document')}</Button>
</Box>
<DocsGrid />
</Box>
);
};
@@ -1 +0,0 @@
export * from './DocsGridContainer';
@@ -1 +0,0 @@
export * from './components';
@@ -1,5 +1,4 @@
import Image from 'next/image';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Box, StyledLink, Text } from '@/components/';
@@ -23,7 +22,7 @@ export const Header = () => {
$width="100%"
$zIndex="100"
$padding={{ vertical: 'xtiny' }}
$css="box-shadow: 0 1px 4px #00000040;"
$css="border-bottom: 1px solid #EDEDED;"
>
<Box
$margin={{
@@ -0,0 +1 @@
export const HEADER_HEIGHT = 52;
@@ -0,0 +1,77 @@
import { Button } from '@openfun/cunningham-react';
import { useRouter } from 'next/navigation';
import { PropsWithChildren, ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import { Box, Icon } from '@/components';
import { SeparatedSection } from '@/components/separators/SeparatedSection';
import { useCunninghamTheme } from '@/cunningham';
import { useCreateDoc } from '@/features/docs';
import { HEADER_HEIGHT } from '@/features/header/conf';
import { useResponsiveStore } from '@/stores';
export const LeftPanel = ({ children }: PropsWithChildren) => {
const { t } = useTranslation();
const router = useRouter();
const { isResponsive } = useResponsiveStore();
const theme = useCunninghamTheme();
const colors = theme.colorsTokens();
const { mutate: createDoc } = useCreateDoc({
onSuccess: (doc) => {
router.push(`/docs/${doc.id}`);
},
});
const goToHome = () => {
router.push('/');
};
const createNewDoc = () => {
createDoc({ title: t('Untitled document') });
};
const getContent = (): ReactNode => {
return (
<div>
<SeparatedSection>
<Box
$padding={{ horizontal: '300V' }}
$direction="row"
$justify="space-between"
$align="center"
>
<Box $direction="row" $gap="2px">
<Button
onClick={goToHome}
size="medium"
color="primary-text"
icon={<Icon iconName="house" />}
/>
</Box>
<Button onClick={createNewDoc}>{t('New doc')}</Button>
</Box>
</SeparatedSection>
{children}
</div>
);
};
return (
<>
{!isResponsive && (
<Box
data-testid="left-panel-desktop"
$css={`
height: calc(100vh - ${HEADER_HEIGHT}px);
width: 300px;
min-width: 300px;
border-right: 1px solid ${(colors?.['greyscale-200'] as string) ?? '#E5E5E5'};
`}
>
{getContent()}
</Box>
)}
</>
);
};
@@ -2,35 +2,50 @@ import { PropsWithChildren } from 'react';
import { Box } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { Footer } from '@/features/footer';
import { Header } from '@/features/header';
import { HEADER_HEIGHT } from '@/features/header/conf';
import { LeftPanel } from '@/features/left-pannel/components/LeftPanel';
interface MainLayoutProps {
withoutFooter?: boolean;
export enum MainLayoutBackgroundColor {
WHITE = 'white',
GREY = 'grey',
}
type MainLayoutProps = {
backgroundColor?: MainLayoutBackgroundColor;
};
export function MainLayout({
children,
withoutFooter,
backgroundColor = MainLayoutBackgroundColor.WHITE,
}: PropsWithChildren<MainLayoutProps>) {
const { colorsTokens } = useCunninghamTheme();
const { themeTokens, colorsTokens } = useCunninghamTheme();
const tokens = themeTokens();
const colors = colorsTokens();
return (
<Box>
<Box $minHeight="100vh">
<Header />
<Box $css="flex: 1;" $direction="row">
<Box
as="main"
$minHeight="100vh"
$width="100%"
$background={colorsTokens()['primary-bg']}
>
{children}
</Box>
<div>
<Header />
<Box $direction="row" $width="100%">
<LeftPanel />
<Box
as="main"
id="mainContent"
$css={`
display: flex;
width: 100%;
flex-direction: column;
align-items: center;
flex: 1;
padding: ${tokens.spacings?.['200W'] ?? '1.12rem'} ${tokens.spacings?.['1200W'] ?? '5.62rem'};
height: calc(100dvh - ${HEADER_HEIGHT}px);
overflow-y: scroll;
background-color: ${backgroundColor === MainLayoutBackgroundColor.WHITE ? colors['greyscale-000'] : colors['greyscale-050']};
`}
>
{children}
</Box>
</Box>
{!withoutFooter && <Footer />}
</Box>
</div>
);
}
@@ -28,7 +28,7 @@ export function DocLayout() {
<Head>
<meta name="robots" content="noindex" />
</Head>
<MainLayout withoutFooter>
<MainLayout>
<DocPage id={id} />
</MainLayout>
</>
@@ -1,15 +1,24 @@
import type { ReactElement } from 'react';
import { DocsGridContainer } from '@/features/docs/docs-grid';
import { MainLayout } from '@/layouts';
import { Box } from '@/components';
import { DocsGrid } from '@/features/docs/docs-grid/components/DocsGrid';
import { MainLayout, MainLayoutBackgroundColor } from '@/layouts';
import { NextPageWithLayout } from '@/types/next';
const Page: NextPageWithLayout = () => {
return <DocsGridContainer />;
return (
<Box $width="100%">
<DocsGrid />
</Box>
);
};
Page.getLayout = function getLayout(page: ReactElement) {
return <MainLayout>{page}</MainLayout>;
return (
<MainLayout backgroundColor={MainLayoutBackgroundColor.GREY}>
{page}
</MainLayout>
);
};
export default Page;
@@ -4,41 +4,67 @@ export type ScreenSize = 'small-mobile' | 'mobile' | 'tablet' | 'desktop';
export interface UseResponsiveStore {
isMobile: boolean;
isTablet: boolean;
isSmallMobile: boolean;
screenSize: ScreenSize;
screenWidth: number;
setScreenSize: (size: ScreenSize) => void;
isResponsive: boolean;
initializeResizeListener: () => () => void;
}
const initialState = {
isMobile: false,
isSmallMobile: false,
isTablet: false,
isResponsive: false,
screenSize: 'desktop' as ScreenSize,
screenWidth: 0,
};
export const useResponsiveStore = create<UseResponsiveStore>((set) => ({
isMobile: initialState.isMobile,
isTablet: initialState.isTablet,
isSmallMobile: initialState.isSmallMobile,
screenSize: initialState.screenSize,
screenWidth: initialState.screenWidth,
setScreenSize: (size: ScreenSize) => set(() => ({ screenSize: size })),
isResponsive: initialState.isResponsive,
initializeResizeListener: () => {
const resizeHandler = () => {
const width = window.innerWidth;
if (width < 560) {
set({
isResponsive: true,
screenSize: 'small-mobile',
isMobile: true,
isTablet: false,
isSmallMobile: true,
});
} else if (width < 768) {
set({ screenSize: 'mobile', isMobile: true, isSmallMobile: false });
set({
isResponsive: true,
screenSize: 'mobile',
isTablet: false,
isMobile: true,
isSmallMobile: false,
});
} else if (width >= 768 && width < 1024) {
set({ screenSize: 'tablet', isMobile: false, isSmallMobile: false });
set({
isResponsive: true,
screenSize: 'tablet',
isTablet: true,
isMobile: false,
isSmallMobile: false,
});
} else {
set({ screenSize: 'desktop', isMobile: false, isSmallMobile: false });
set({
isResponsive: false,
screenSize: 'desktop',
isTablet: false,
isMobile: false,
isSmallMobile: false,
});
}
set({ screenWidth: width });