💄(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:
@@ -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.')}
|
||||
>
|
||||
|
||||
+2
-2
@@ -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);
|
||||
|
||||
-40
@@ -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 });
|
||||
|
||||
Reference in New Issue
Block a user