♻️(frontend) create useHeadings hook

- We create the useHeadings hook to manage the
headings of the document and staty DRY.
- We use the headings store in IconOpenPanelEditor
and TableContent, to avoid prop drilling.
- We add a debounce on the onEditorContentChange
to improve a bit the performance.
This commit is contained in:
Anthony LC
2024-12-06 14:47:54 +01:00
committed by Anthony LC
parent 69186e9a26
commit a9def8cb18
5 changed files with 42 additions and 48 deletions

View File

@@ -13,8 +13,9 @@ import { useAuthStore } from '@/core/auth';
import { Doc } from '@/features/docs/doc-management'; import { Doc } from '@/features/docs/doc-management';
import { useUploadFile } from '../hook'; import { useUploadFile } from '../hook';
import { useHeadings } from '../hook/useHeadings';
import useSaveDoc from '../hook/useSaveDoc'; import useSaveDoc from '../hook/useSaveDoc';
import { useEditorStore, useHeadingStore } from '../stores'; import { useEditorStore } from '../stores';
import { randomColor } from '../utils'; import { randomColor } from '../utils';
import { BlockNoteToolbar } from './BlockNoteToolbar'; import { BlockNoteToolbar } from './BlockNoteToolbar';
@@ -75,7 +76,6 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
const readOnly = !doc.abilities.partial_update; const readOnly = !doc.abilities.partial_update;
useSaveDoc(doc.id, provider.document, !readOnly); useSaveDoc(doc.id, provider.document, !readOnly);
const { setHeadings, resetHeadings } = useHeadingStore();
const { i18n } = useTranslation(); const { i18n } = useTranslation();
const lang = i18n.language; const lang = i18n.language;
@@ -126,6 +126,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
}, },
[collabName, lang, provider, uploadFile], [collabName, lang, provider, uploadFile],
); );
useHeadings(editor);
useEffect(() => { useEffect(() => {
setEditor(editor); setEditor(editor);
@@ -135,18 +136,6 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
}; };
}, [setEditor, editor]); }, [setEditor, editor]);
useEffect(() => {
setHeadings(editor);
editor?.onEditorContentChange(() => {
setHeadings(editor);
});
return () => {
resetHeadings();
};
}, [editor, resetHeadings, setHeadings]);
return ( return (
<Box $css={cssEditor(readOnly)}> <Box $css={cssEditor(readOnly)}>
{errorAttachment && ( {errorAttachment && (
@@ -179,8 +168,7 @@ export const BlockNoteEditorVersion = ({
initialContent, initialContent,
}: BlockNoteEditorVersionProps) => { }: BlockNoteEditorVersionProps) => {
const readOnly = true; const readOnly = true;
const { setHeadings, resetHeadings } = useHeadingStore(); const { setEditor } = useEditorStore();
const editor = useCreateBlockNote( const editor = useCreateBlockNote(
{ {
collaboration: { collaboration: {
@@ -194,18 +182,15 @@ export const BlockNoteEditorVersion = ({
}, },
[initialContent], [initialContent],
); );
useHeadings(editor);
useEffect(() => { useEffect(() => {
setHeadings(editor); setEditor(editor);
editor?.onEditorContentChange(() => {
setHeadings(editor);
});
return () => { return () => {
resetHeadings(); setEditor(undefined);
}; };
}, [editor, resetHeadings, setHeadings]); }, [setEditor, editor]);
return ( return (
<Box $css={cssEditor(readOnly)}> <Box $css={cssEditor(readOnly)}>

View File

@@ -15,8 +15,6 @@ import {
import { Versions, useDocVersion } from '@/features/docs/doc-versioning/'; import { Versions, useDocVersion } from '@/features/docs/doc-versioning/';
import { useResponsiveStore } from '@/stores'; import { useResponsiveStore } from '@/stores';
import { useHeadingStore } from '../stores';
import { BlockNoteEditor, BlockNoteEditorVersion } from './BlockNoteEditor'; import { BlockNoteEditor, BlockNoteEditorVersion } from './BlockNoteEditor';
import { IconOpenPanelEditor, PanelEditor } from './PanelEditor'; import { IconOpenPanelEditor, PanelEditor } from './PanelEditor';
@@ -29,7 +27,6 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
query: { versionId }, query: { versionId },
} = useRouter(); } = useRouter();
const { t } = useTranslation(); const { t } = useTranslation();
const { headings } = useHeadingStore();
const { isMobile } = useResponsiveStore(); const { isMobile } = useResponsiveStore();
const isVersion = versionId && typeof versionId === 'string'; const isVersion = versionId && typeof versionId === 'string';
@@ -79,9 +76,9 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
) : ( ) : (
<BlockNoteEditor doc={doc} provider={provider} /> <BlockNoteEditor doc={doc} provider={provider} />
)} )}
{!isMobile && <IconOpenPanelEditor headings={headings} />} {!isMobile && <IconOpenPanelEditor />}
</Card> </Card>
<PanelEditor doc={doc} headings={headings} /> <PanelEditor doc={doc} />
</Box> </Box>
</> </>
); );

View File

@@ -8,18 +8,13 @@ import { TableContent } from '@/features/docs/doc-table-content';
import { VersionList } from '@/features/docs/doc-versioning'; import { VersionList } from '@/features/docs/doc-versioning';
import { useResponsiveStore } from '@/stores'; import { useResponsiveStore } from '@/stores';
import { usePanelEditorStore } from '../stores'; import { useHeadingStore, usePanelEditorStore } from '../stores';
import { HeadingBlock } from '../types';
interface PanelProps { interface PanelProps {
doc: Doc; doc: Doc;
headings: HeadingBlock[];
} }
export const PanelEditor = ({ export const PanelEditor = ({ doc }: PropsWithChildren<PanelProps>) => {
doc,
headings,
}: PropsWithChildren<PanelProps>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { colorsTokens } = useCunninghamTheme(); const { colorsTokens } = useCunninghamTheme();
const { isMobile } = useResponsiveStore(); const { isMobile } = useResponsiveStore();
@@ -63,7 +58,7 @@ export const PanelEditor = ({
`} `}
$maxHeight="99vh" $maxHeight="99vh"
> >
{isMobile && <IconOpenPanelEditor headings={headings} />} {isMobile && <IconOpenPanelEditor />}
<Box <Box
$direction="row" $direction="row"
$justify="space-between" $justify="space-between"
@@ -127,7 +122,7 @@ export const PanelEditor = ({
</BoxButton> </BoxButton>
)} )}
</Box> </Box>
{isPanelTableContentOpen && <TableContent headings={headings} />} {isPanelTableContentOpen && <TableContent />}
{!isPanelTableContentOpen && doc.abilities.versions_list && ( {!isPanelTableContentOpen && doc.abilities.versions_list && (
<VersionList doc={doc} /> <VersionList doc={doc} />
)} )}
@@ -136,11 +131,8 @@ export const PanelEditor = ({
); );
}; };
interface IconOpenPanelEditorProps { export const IconOpenPanelEditor = () => {
headings: HeadingBlock[]; const { headings } = useHeadingStore();
}
export const IconOpenPanelEditor = ({ headings }: IconOpenPanelEditorProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { setIsPanelOpen, isPanelOpen, setIsPanelTableContentOpen } = const { setIsPanelOpen, isPanelOpen, setIsPanelTableContentOpen } =
usePanelEditorStore(); usePanelEditorStore();

View File

@@ -0,0 +1,23 @@
import { BlockNoteEditor } from '@blocknote/core';
import { useEffect } from 'react';
import { useHeadingStore } from '../stores';
export const useHeadings = (editor: BlockNoteEditor) => {
const { setHeadings, resetHeadings } = useHeadingStore();
useEffect(() => {
setHeadings(editor);
let timeout: NodeJS.Timeout;
editor?.onEditorContentChange(() => {
clearTimeout(timeout);
timeout = setTimeout(() => setHeadings(editor), 200);
});
return () => {
clearTimeout(timeout);
resetHeadings();
};
}, [editor, resetHeadings, setHeadings]);
};

View File

@@ -2,16 +2,13 @@ import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Box, BoxButton, Text } from '@/components'; import { Box, BoxButton, Text } from '@/components';
import { HeadingBlock, useEditorStore } from '@/features/docs/doc-editor'; import { useEditorStore, useHeadingStore } from '@/features/docs/doc-editor';
import { useResponsiveStore } from '@/stores'; import { useResponsiveStore } from '@/stores';
import { Heading } from './Heading'; import { Heading } from './Heading';
interface TableContentProps { export const TableContent = () => {
headings: HeadingBlock[]; const { headings } = useHeadingStore();
}
export const TableContent = ({ headings }: TableContentProps) => {
const { editor } = useEditorStore(); const { editor } = useEditorStore();
const { isMobile } = useResponsiveStore(); const { isMobile } = useResponsiveStore();
const { t } = useTranslation(); const { t } = useTranslation();