♻️(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:
@@ -13,8 +13,9 @@ import { useAuthStore } from '@/core/auth';
|
||||
import { Doc } from '@/features/docs/doc-management';
|
||||
|
||||
import { useUploadFile } from '../hook';
|
||||
import { useHeadings } from '../hook/useHeadings';
|
||||
import useSaveDoc from '../hook/useSaveDoc';
|
||||
import { useEditorStore, useHeadingStore } from '../stores';
|
||||
import { useEditorStore } from '../stores';
|
||||
import { randomColor } from '../utils';
|
||||
|
||||
import { BlockNoteToolbar } from './BlockNoteToolbar';
|
||||
@@ -75,7 +76,6 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
||||
|
||||
const readOnly = !doc.abilities.partial_update;
|
||||
useSaveDoc(doc.id, provider.document, !readOnly);
|
||||
const { setHeadings, resetHeadings } = useHeadingStore();
|
||||
const { i18n } = useTranslation();
|
||||
const lang = i18n.language;
|
||||
|
||||
@@ -126,6 +126,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
||||
},
|
||||
[collabName, lang, provider, uploadFile],
|
||||
);
|
||||
useHeadings(editor);
|
||||
|
||||
useEffect(() => {
|
||||
setEditor(editor);
|
||||
@@ -135,18 +136,6 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
||||
};
|
||||
}, [setEditor, editor]);
|
||||
|
||||
useEffect(() => {
|
||||
setHeadings(editor);
|
||||
|
||||
editor?.onEditorContentChange(() => {
|
||||
setHeadings(editor);
|
||||
});
|
||||
|
||||
return () => {
|
||||
resetHeadings();
|
||||
};
|
||||
}, [editor, resetHeadings, setHeadings]);
|
||||
|
||||
return (
|
||||
<Box $css={cssEditor(readOnly)}>
|
||||
{errorAttachment && (
|
||||
@@ -179,8 +168,7 @@ export const BlockNoteEditorVersion = ({
|
||||
initialContent,
|
||||
}: BlockNoteEditorVersionProps) => {
|
||||
const readOnly = true;
|
||||
const { setHeadings, resetHeadings } = useHeadingStore();
|
||||
|
||||
const { setEditor } = useEditorStore();
|
||||
const editor = useCreateBlockNote(
|
||||
{
|
||||
collaboration: {
|
||||
@@ -194,18 +182,15 @@ export const BlockNoteEditorVersion = ({
|
||||
},
|
||||
[initialContent],
|
||||
);
|
||||
useHeadings(editor);
|
||||
|
||||
useEffect(() => {
|
||||
setHeadings(editor);
|
||||
|
||||
editor?.onEditorContentChange(() => {
|
||||
setHeadings(editor);
|
||||
});
|
||||
setEditor(editor);
|
||||
|
||||
return () => {
|
||||
resetHeadings();
|
||||
setEditor(undefined);
|
||||
};
|
||||
}, [editor, resetHeadings, setHeadings]);
|
||||
}, [setEditor, editor]);
|
||||
|
||||
return (
|
||||
<Box $css={cssEditor(readOnly)}>
|
||||
|
||||
@@ -15,8 +15,6 @@ import {
|
||||
import { Versions, useDocVersion } from '@/features/docs/doc-versioning/';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import { useHeadingStore } from '../stores';
|
||||
|
||||
import { BlockNoteEditor, BlockNoteEditorVersion } from './BlockNoteEditor';
|
||||
import { IconOpenPanelEditor, PanelEditor } from './PanelEditor';
|
||||
|
||||
@@ -29,7 +27,6 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
|
||||
query: { versionId },
|
||||
} = useRouter();
|
||||
const { t } = useTranslation();
|
||||
const { headings } = useHeadingStore();
|
||||
const { isMobile } = useResponsiveStore();
|
||||
|
||||
const isVersion = versionId && typeof versionId === 'string';
|
||||
@@ -79,9 +76,9 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
|
||||
) : (
|
||||
<BlockNoteEditor doc={doc} provider={provider} />
|
||||
)}
|
||||
{!isMobile && <IconOpenPanelEditor headings={headings} />}
|
||||
{!isMobile && <IconOpenPanelEditor />}
|
||||
</Card>
|
||||
<PanelEditor doc={doc} headings={headings} />
|
||||
<PanelEditor doc={doc} />
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -8,18 +8,13 @@ import { TableContent } from '@/features/docs/doc-table-content';
|
||||
import { VersionList } from '@/features/docs/doc-versioning';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import { usePanelEditorStore } from '../stores';
|
||||
import { HeadingBlock } from '../types';
|
||||
import { useHeadingStore, usePanelEditorStore } from '../stores';
|
||||
|
||||
interface PanelProps {
|
||||
doc: Doc;
|
||||
headings: HeadingBlock[];
|
||||
}
|
||||
|
||||
export const PanelEditor = ({
|
||||
doc,
|
||||
headings,
|
||||
}: PropsWithChildren<PanelProps>) => {
|
||||
export const PanelEditor = ({ doc }: PropsWithChildren<PanelProps>) => {
|
||||
const { t } = useTranslation();
|
||||
const { colorsTokens } = useCunninghamTheme();
|
||||
const { isMobile } = useResponsiveStore();
|
||||
@@ -63,7 +58,7 @@ export const PanelEditor = ({
|
||||
`}
|
||||
$maxHeight="99vh"
|
||||
>
|
||||
{isMobile && <IconOpenPanelEditor headings={headings} />}
|
||||
{isMobile && <IconOpenPanelEditor />}
|
||||
<Box
|
||||
$direction="row"
|
||||
$justify="space-between"
|
||||
@@ -127,7 +122,7 @@ export const PanelEditor = ({
|
||||
</BoxButton>
|
||||
)}
|
||||
</Box>
|
||||
{isPanelTableContentOpen && <TableContent headings={headings} />}
|
||||
{isPanelTableContentOpen && <TableContent />}
|
||||
{!isPanelTableContentOpen && doc.abilities.versions_list && (
|
||||
<VersionList doc={doc} />
|
||||
)}
|
||||
@@ -136,11 +131,8 @@ export const PanelEditor = ({
|
||||
);
|
||||
};
|
||||
|
||||
interface IconOpenPanelEditorProps {
|
||||
headings: HeadingBlock[];
|
||||
}
|
||||
|
||||
export const IconOpenPanelEditor = ({ headings }: IconOpenPanelEditorProps) => {
|
||||
export const IconOpenPanelEditor = () => {
|
||||
const { headings } = useHeadingStore();
|
||||
const { t } = useTranslation();
|
||||
const { setIsPanelOpen, isPanelOpen, setIsPanelTableContentOpen } =
|
||||
usePanelEditorStore();
|
||||
|
||||
@@ -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]);
|
||||
};
|
||||
@@ -2,16 +2,13 @@ import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
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 { Heading } from './Heading';
|
||||
|
||||
interface TableContentProps {
|
||||
headings: HeadingBlock[];
|
||||
}
|
||||
|
||||
export const TableContent = ({ headings }: TableContentProps) => {
|
||||
export const TableContent = () => {
|
||||
const { headings } = useHeadingStore();
|
||||
const { editor } = useEditorStore();
|
||||
const { isMobile } = useResponsiveStore();
|
||||
const { t } = useTranslation();
|
||||
|
||||
Reference in New Issue
Block a user