♻️(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 { 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)}>
|
||||||
|
|||||||
@@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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 { 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();
|
||||||
|
|||||||
Reference in New Issue
Block a user