mirror of
https://github.com/suitenumerique/docs.git
synced 2026-09-29 21:15:14 +02:00
✨(frontend) add right panel
Add a right panel, it will in a first time be used to display the comment side panel, but it will be used in the future for other features as well.
This commit is contained in:
@@ -1,3 +1,2 @@
|
||||
export * from './useEditorStore';
|
||||
export * from './useHeadingStore';
|
||||
export * from './usePanelEditorStore';
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
export interface UsePanelEditorStore {
|
||||
isPanelOpen: boolean;
|
||||
setIsPanelOpen: (isOpen: boolean) => void;
|
||||
isPanelTableContentOpen: boolean;
|
||||
setIsPanelTableContentOpen: (isOpen: boolean) => void;
|
||||
}
|
||||
|
||||
export const usePanelEditorStore = create<UsePanelEditorStore>((set) => ({
|
||||
isPanelOpen: false,
|
||||
isPanelTableContentOpen: true,
|
||||
setIsPanelTableContentOpen: (isPanelTableContentOpen) => {
|
||||
set(() => ({ isPanelTableContentOpen }));
|
||||
},
|
||||
setIsPanelOpen: (isPanelOpen) => {
|
||||
set(() => ({ isPanelOpen }));
|
||||
},
|
||||
}));
|
||||
@@ -24,6 +24,7 @@ const PRINT_ONLY_CONTENT_CSS = `
|
||||
.--docs--table-content,
|
||||
.--docs--doc-footer,
|
||||
.--docs--footer,
|
||||
.--docs--right-panel,
|
||||
footer,
|
||||
[role="contentinfo"],
|
||||
div[data-is-empty-and-focused="true"],
|
||||
|
||||
@@ -4,6 +4,7 @@ import { css } from 'styled-components';
|
||||
import { Box } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham/useCunninghamTheme';
|
||||
import { LeftPanelCollapseButton } from '@/features/left-panel';
|
||||
import { RightPanelCollapseButton } from '@/features/right-panel/components/RightPanelCollapseButton';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
/**
|
||||
@@ -22,19 +23,15 @@ export const FloatingBar = () => {
|
||||
const sm = spacingsTokens['sm'];
|
||||
return css`
|
||||
position: sticky;
|
||||
top: calc(-${base});
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: calc(100% + ${base} + ${base});
|
||||
width: 100%;
|
||||
min-height: 64px;
|
||||
padding: ${sm};
|
||||
margin-left: calc(-${base});
|
||||
margin-right: calc(-${base});
|
||||
margin-top: calc(-${base});
|
||||
z-index: 21; // Under editor select box but above other elements (e.g., doc title, suggestion menu)
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
isolation: isolate;
|
||||
|
||||
&::before {
|
||||
@@ -64,17 +61,16 @@ export const FloatingBar = () => {
|
||||
`;
|
||||
}, [spacingsTokens]);
|
||||
|
||||
if (!isDesktop) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="--docs--floating-bar"
|
||||
data-testid="floating-bar"
|
||||
$css={FLOATING_STYLES}
|
||||
$direction="row"
|
||||
$justify="space-between"
|
||||
>
|
||||
<LeftPanelCollapseButton />
|
||||
{isDesktop ? <LeftPanelCollapseButton /> : <Box />}
|
||||
<RightPanelCollapseButton />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { css } from 'styled-components';
|
||||
|
||||
import { Box } from '@/components';
|
||||
import { useDocStore, useProviderStore } from '@/features/docs/doc-management';
|
||||
import { HEADER_HEIGHT } from '@/features/header';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import { useRightPanelStore } from './useRightPanelStore';
|
||||
|
||||
export const RightPanel = () => {
|
||||
const { t } = useTranslation();
|
||||
const { currentDoc: doc } = useDocStore();
|
||||
const { _, isPanelOpen } = useRightPanelStore();
|
||||
const { isMobile } = useResponsiveStore();
|
||||
const { provider, isReady } = useProviderStore();
|
||||
const isProviderReady =
|
||||
isReady && provider && provider?.configuration.name === doc?.id;
|
||||
|
||||
if (!doc || !isProviderReady) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="--docs--right-panel"
|
||||
aria-label={t('Right panel')}
|
||||
$width="300px"
|
||||
$height={`calc(100dvh - ${HEADER_HEIGHT}px)`}
|
||||
$position={isMobile ? 'absolute' : 'sticky'}
|
||||
$zIndex={25}
|
||||
$hasTransition
|
||||
$background="var(--c--contextuals--background--surface--secondary)"
|
||||
$css={css`
|
||||
flex-shrink: 0;
|
||||
border-left: 1px solid var(--c--contextuals--border--surface--primary);
|
||||
transform: translateX(0%);
|
||||
top: 0;
|
||||
right: 0;
|
||||
align-self: flex-start;
|
||||
opacity: 1;
|
||||
${!isPanelOpen &&
|
||||
css`
|
||||
transform: translateX(200%);
|
||||
opacity: 0;
|
||||
margin-left: 0rem;
|
||||
width: 0;
|
||||
`}
|
||||
`}
|
||||
></Box>
|
||||
);
|
||||
};
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { Button } from '@gouvfr-lasuite/cunningham-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { css } from 'styled-components';
|
||||
|
||||
import CommentsIcon from '@/assets/icons/ui-kit/bubble-text.svg';
|
||||
import { Card } from '@/components';
|
||||
|
||||
import { useRightPanelStore } from './useRightPanelStore';
|
||||
|
||||
export const RightPanelCollapseButton = () => {
|
||||
const { t } = useTranslation();
|
||||
const { isPanelOpen, togglePanel } = useRightPanelStore();
|
||||
|
||||
const ariaLabel = isPanelOpen
|
||||
? t('Hide the right side panel')
|
||||
: t('Show the right side panel');
|
||||
|
||||
return (
|
||||
<Card
|
||||
className="--docs--right-panel-collapse-button"
|
||||
$direction="row"
|
||||
$css={css`
|
||||
padding: var(--c--globals--spacings--xxxs);
|
||||
align-items: center;
|
||||
gap: var(--c--globals--spacings--xxxs);
|
||||
border-radius: var(--c--globals--spacings--xs);
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.05);
|
||||
`}
|
||||
>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={togglePanel}
|
||||
aria-label={ariaLabel}
|
||||
aria-expanded={isPanelOpen}
|
||||
color="neutral"
|
||||
variant={isPanelOpen ? 'secondary' : 'tertiary'}
|
||||
icon={<CommentsIcon width={24} height={24} aria-hidden="true" />}
|
||||
data-testid="floating-bar-toggle-right-panel"
|
||||
></Button>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
export interface UseRightPanelStore {
|
||||
isPanelOpen: boolean;
|
||||
setIsPanelOpen: (isOpen: boolean) => void;
|
||||
togglePanel: () => void;
|
||||
}
|
||||
|
||||
export const useRightPanelStore = create<UseRightPanelStore>((set) => ({
|
||||
isPanelOpen: false,
|
||||
setIsPanelOpen: (isPanelOpen) => {
|
||||
set(() => ({ isPanelOpen }));
|
||||
},
|
||||
togglePanel: () => {
|
||||
set((state) => ({ isPanelOpen: !state.isPanelOpen }));
|
||||
},
|
||||
}));
|
||||
@@ -2,10 +2,11 @@ import { PropsWithChildren } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { css } from 'styled-components';
|
||||
|
||||
import { Box } from '@/components';
|
||||
import { Box, BoxProps } from '@/components';
|
||||
import { Header } from '@/features/header';
|
||||
import { HEADER_HEIGHT } from '@/features/header/conf';
|
||||
import { LeftPanel, ResizableLeftPanel } from '@/features/left-panel';
|
||||
import { RightPanel } from '@/features/right-panel/components/RightPanel';
|
||||
import { DocEditorSkeleton, Skeleton } from '@/features/skeletons';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
@@ -43,20 +44,29 @@ export function MainLayout({
|
||||
|
||||
export interface MainLayoutContentProps {
|
||||
backgroundColor: 'white' | 'grey';
|
||||
enableResizablePanel?: boolean;
|
||||
enableResizablePanel: boolean;
|
||||
}
|
||||
|
||||
export function MainLayoutContent({
|
||||
children,
|
||||
backgroundColor,
|
||||
enableResizablePanel = false,
|
||||
enableResizablePanel,
|
||||
}: PropsWithChildren<MainLayoutContentProps>) {
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
|
||||
if (enableResizablePanel) {
|
||||
return (
|
||||
<ResizableLeftPanel leftPanel={<LeftPanel />}>
|
||||
<MainContent backgroundColor={backgroundColor}>{children}</MainContent>
|
||||
<Box $direction="row" $width="100%" $position="relative">
|
||||
<MainContent
|
||||
backgroundColor={backgroundColor}
|
||||
$flex="auto"
|
||||
$padding="0"
|
||||
>
|
||||
{children}
|
||||
</MainContent>
|
||||
<RightPanel />
|
||||
</Box>
|
||||
</ResizableLeftPanel>
|
||||
);
|
||||
}
|
||||
@@ -86,10 +96,15 @@ export function MainLayoutContent({
|
||||
);
|
||||
}
|
||||
|
||||
type MainContentProps = BoxProps & {
|
||||
backgroundColor: 'white' | 'grey';
|
||||
};
|
||||
|
||||
const MainContent = ({
|
||||
children,
|
||||
backgroundColor,
|
||||
}: PropsWithChildren<MainLayoutContentProps>) => {
|
||||
...props
|
||||
}: PropsWithChildren<MainContentProps>) => {
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
|
||||
const { t } = useTranslation();
|
||||
@@ -106,9 +121,7 @@ const MainContent = ({
|
||||
$width="100%"
|
||||
$height={`calc(100dvh - ${HEADER_HEIGHT}px)`}
|
||||
$position="relative"
|
||||
$padding={{
|
||||
all: isDesktop ? 'base' : '0',
|
||||
}}
|
||||
$padding={isDesktop ? 'base' : '0'}
|
||||
$background={
|
||||
currentBackgroundColor === 'white'
|
||||
? 'var(--c--contextuals--background--surface--primary)'
|
||||
@@ -118,6 +131,7 @@ const MainContent = ({
|
||||
overflow-y: auto;
|
||||
overflow-x: clip;
|
||||
`}
|
||||
{...props}
|
||||
>
|
||||
<Skeleton>
|
||||
<DocEditorSkeleton />
|
||||
|
||||
Reference in New Issue
Block a user