diff --git a/packages/client/src/components/document/reader/index.tsx b/packages/client/src/components/document/reader/index.tsx index 3baa305f..bcd4200e 100644 --- a/packages/client/src/components/document/reader/index.tsx +++ b/packages/client/src/components/document/reader/index.tsx @@ -132,9 +132,9 @@ export const DocumentReader: React.FC = ({ documentId }) => { > -
+
-
+
{ + const data = [...tocs, { level: Infinity }]; + const res = []; + + const makeChildren = (item, flattenChildren) => { + if (!flattenChildren.length) return; + + const stopAt = flattenChildren.findIndex((d) => d.level !== item.level + 1); + + if (stopAt > -1) { + const children = flattenChildren.slice(0, stopAt); + item.children = children; + + const remain = flattenChildren.slice(stopAt + 1); + + if (remain.length) { + makeChildren(children[children.length - 1], remain); + } + } else { + item.children = flattenChildren; + } + }; + + let i = 0; + + while (i < data.length) { + const item = data[i]; + const stopAt = data.slice(i + 1).findIndex((d) => d.level !== item.level + 1); + + if (stopAt > -1) { + makeChildren(item, data.slice(i + 1).slice(0, stopAt)); + i += 1 + stopAt; + } else { + i += 1; + } + + res.push(item); + } + + return res.slice(0, -1); +}; + export const TableOfContentsWrapper = ({ editor }) => { const [items, setItems] = useState([]); const [visible, toggleVisible] = useToggle(true); @@ -49,11 +91,18 @@ export const TableOfContentsWrapper = ({ editor }) => { editor.view.dispatch(transaction); setItems(headings); + + return headings; }, [editor]); useEffect(() => { if (!editor) { - return null; + return; + } + + if (!editor.options.editable) { + editor.eventEmitter.emit('TableOfContents', arrToTree(handleUpdate())); + return; } editor.on('update', handleUpdate); diff --git a/packages/client/src/tiptap/editor/collaboration/collaboration/editor.tsx b/packages/client/src/tiptap/editor/collaboration/collaboration/editor.tsx index 48fa0bcf..3a097b97 100644 --- a/packages/client/src/tiptap/editor/collaboration/collaboration/editor.tsx +++ b/packages/client/src/tiptap/editor/collaboration/collaboration/editor.tsx @@ -8,9 +8,10 @@ import { isAndroid, isIOS } from 'helpers/env'; import { useNetwork } from 'hooks/use-network'; import { IsOnMobile } from 'hooks/use-on-mobile'; import { useToggle } from 'hooks/use-toggle'; -import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from 'react'; +import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; import { Collaboration } from 'tiptap/core/extensions/collaboration'; import { CollaborationCursor } from 'tiptap/core/extensions/collaboration-cursor'; +import { Tocs } from 'tiptap/editor/tocs'; import { EditorContent, useEditor } from '../../react'; import { CollaborationKit } from '../kit'; @@ -70,6 +71,7 @@ export const EditorInstance = forwardRef((props: IProps, ref) => { }, [editable, user, onTitleUpdate, hocuspocusProvider] ); + const [headings, setHeadings] = useState([]); useImperativeHandle(ref, () => editor); @@ -137,6 +139,21 @@ export const EditorInstance = forwardRef((props: IProps, ref) => { }; }, [isMobile]); + useEffect(() => { + if (!editor) return; + + const collectHeadings = (headings) => { + console.log({ headings }); + setHeadings(headings); + }; + + editor.eventEmitter.on('TableOfContents', collectHeadings); + + return () => { + editor.eventEmitter.off('TableOfContents', collectHeadings); + }; + }, [editor]); + return ( <> {(!online || status === 'disconnected') && ( @@ -159,6 +176,7 @@ export const EditorInstance = forwardRef((props: IProps, ref) => {
+ {editor && } {protals}
diff --git a/packages/client/src/tiptap/editor/tocs/index.module.scss b/packages/client/src/tiptap/editor/tocs/index.module.scss new file mode 100644 index 00000000..fe0c9519 --- /dev/null +++ b/packages/client/src/tiptap/editor/tocs/index.module.scss @@ -0,0 +1,13 @@ +.wrapper { + position: fixed; + right: 16px; + z-index: 4; + background-color: var(--semi-color-nav-bg); + + > header { + margin-bottom: 12px; + font-weight: 600; + line-height: 22px; + color: var(--main-text-color); + } +} diff --git a/packages/client/src/tiptap/editor/tocs/index.tsx b/packages/client/src/tiptap/editor/tocs/index.tsx new file mode 100644 index 00000000..9b5c75ae --- /dev/null +++ b/packages/client/src/tiptap/editor/tocs/index.tsx @@ -0,0 +1,35 @@ +import { Anchor } from '@douyinfe/semi-ui'; +import React, { useCallback } from 'react'; + +import { Editor } from '../react'; +import style from './index.module.scss'; + +interface IToc { + level: number; + id: string; + text: string; +} + +const renderToc = (toc) => { + return ( + + {toc.children && toc.children.length && toc.children.map(renderToc)} + + ); +}; + +export const Tocs: React.FC<{ tocs: Array; editor: Editor }> = ({ tocs = [], editor }) => { + const getContainer = useCallback(() => { + return document.querySelector(`#js-reader-container`); + }, []); + + return ( +
+
+ + {tocs.length && tocs.map(renderToc)} + +
+
+ ); +};