From 2eea71c3e534918ed82e00e391dd5c594b087f02 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Sat, 28 May 2022 12:48:18 +0800 Subject: [PATCH] client: update tocs --- .../src/components/document/reader/index.tsx | 4 +- .../document/reader/public/index.module.scss | 2 + .../document/reader/public/index.tsx | 4 +- .../table-of-contents/index.module.scss | 7 +- .../core/wrappers/table-of-contents/index.tsx | 90 +++++-------------- .../collaboration/collaboration/editor.tsx | 4 +- .../collaboration/index.module.scss | 1 + .../src/tiptap/editor/tocs/index.module.scss | 25 +++++- .../client/src/tiptap/editor/tocs/index.tsx | 75 +++++++++++++--- 9 files changed, 124 insertions(+), 88 deletions(-) diff --git a/packages/client/src/components/document/reader/index.tsx b/packages/client/src/components/document/reader/index.tsx index bcd4200e..71e61750 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 }) => { > -
+
-
+
= ({ documentId, hideLogo = /> - +
{content} - +
); }; diff --git a/packages/client/src/tiptap/core/wrappers/table-of-contents/index.module.scss b/packages/client/src/tiptap/core/wrappers/table-of-contents/index.module.scss index 161dff60..77569bf3 100644 --- a/packages/client/src/tiptap/core/wrappers/table-of-contents/index.module.scss +++ b/packages/client/src/tiptap/core/wrappers/table-of-contents/index.module.scss @@ -1,12 +1,15 @@ .toc { width: max-content; max-width: 100%; - padding: 0.75rem; - margin: 0.75em 0; background: var(--semi-color-fill-1); border-radius: 0.5rem; opacity: 0.75; + &.visible { + padding: 0.75rem; + margin: 0.75em 0; + } + .list { padding: 0; margin: 0 0 12px; diff --git a/packages/client/src/tiptap/core/wrappers/table-of-contents/index.tsx b/packages/client/src/tiptap/core/wrappers/table-of-contents/index.tsx index deea91e1..e1908def 100644 --- a/packages/client/src/tiptap/core/wrappers/table-of-contents/index.tsx +++ b/packages/client/src/tiptap/core/wrappers/table-of-contents/index.tsx @@ -1,66 +1,23 @@ -import { Button, Collapsible } from '@douyinfe/semi-ui'; import { NodeViewWrapper } from '@tiptap/react'; -import { useToggle } from 'hooks/use-toggle'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import cls from 'classnames'; +import { useCallback, useEffect, useState } from 'react'; import styles from './index.module.scss'; const arrToTree = (tocs) => { - 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); + const levels = [{ children: [] }]; + tocs.forEach(function (o) { + levels.length = o.level; + levels[o.level - 1].children = levels[o.level - 1].children || []; + levels[o.level - 1].children.push(o); + levels[o.level] = o; + }); + return levels[0].children; }; export const TableOfContentsWrapper = ({ editor }) => { + const isEditable = editor.isEditable; const [items, setItems] = useState([]); - const [visible, toggleVisible] = useToggle(true); - - const maskStyle = useMemo( - () => - visible - ? {} - : { - WebkitMaskImage: - 'linear-gradient(to bottom, black 0%, rgba(0, 0, 0, 1) 60%, rgba(0, 0, 0, 0.2) 80%, transparent 100%)', - }, - [visible] - ); const handleUpdate = useCallback(() => { const headings = []; @@ -87,12 +44,9 @@ export const TableOfContentsWrapper = ({ editor }) => { transaction.setMeta('addToHistory', false); transaction.setMeta('preventUpdate', true); - editor.view.dispatch(transaction); - setItems(headings); - - return headings; + editor.eventEmitter.emit('TableOfContents', arrToTree(headings)); }, [editor]); useEffect(() => { @@ -101,7 +55,7 @@ export const TableOfContentsWrapper = ({ editor }) => { } if (!editor.options.editable) { - editor.eventEmitter.emit('TableOfContents', arrToTree(handleUpdate())); + handleUpdate(); return; } @@ -112,10 +66,15 @@ export const TableOfContentsWrapper = ({ editor }) => { }; }, [editor, handleUpdate]); + useEffect(() => { + handleUpdate(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return ( - -
- + + {isEditable ? ( +
    {items.map((item, index) => (
  • @@ -123,11 +82,8 @@ export const TableOfContentsWrapper = ({ editor }) => {
  • ))}
- - -
+
+ ) : null}
); }; diff --git a/packages/client/src/tiptap/editor/collaboration/collaboration/editor.tsx b/packages/client/src/tiptap/editor/collaboration/collaboration/editor.tsx index 3a097b97..6ee1cea2 100644 --- a/packages/client/src/tiptap/editor/collaboration/collaboration/editor.tsx +++ b/packages/client/src/tiptap/editor/collaboration/collaboration/editor.tsx @@ -174,9 +174,9 @@ export const EditorInstance = forwardRef((props: IProps, ref) => { )} -
+
- {editor && } + {!isMobile && editor ? : null} {protals}
diff --git a/packages/client/src/tiptap/editor/collaboration/collaboration/index.module.scss b/packages/client/src/tiptap/editor/collaboration/collaboration/index.module.scss index 43fdb25c..a1f461d1 100644 --- a/packages/client/src/tiptap/editor/collaboration/collaboration/index.module.scss +++ b/packages/client/src/tiptap/editor/collaboration/collaboration/index.module.scss @@ -43,6 +43,7 @@ } > main { + display: flex; flex: 1; overflow: auto; } diff --git a/packages/client/src/tiptap/editor/tocs/index.module.scss b/packages/client/src/tiptap/editor/tocs/index.module.scss index fe0c9519..27153392 100644 --- a/packages/client/src/tiptap/editor/tocs/index.module.scss +++ b/packages/client/src/tiptap/editor/tocs/index.module.scss @@ -1,13 +1,32 @@ .wrapper { position: fixed; - right: 16px; - z-index: 4; + right: 0; background-color: var(--semi-color-nav-bg); > header { margin-bottom: 12px; - font-weight: 600; line-height: 22px; color: var(--main-text-color); + opacity: 0; + } + + &:hover { + > header { + opacity: 1; + } + } + + .collapsedItem { + position: relative; + height: 2px; + background-color: #d8d8d8; + } + + :global { + .semi-anchor-link-title-active { + .collapsedItem { + background-color: var(--semi-color-primary); + } + } } } diff --git a/packages/client/src/tiptap/editor/tocs/index.tsx b/packages/client/src/tiptap/editor/tocs/index.tsx index 9b5c75ae..250e21c8 100644 --- a/packages/client/src/tiptap/editor/tocs/index.tsx +++ b/packages/client/src/tiptap/editor/tocs/index.tsx @@ -1,8 +1,13 @@ -import { Anchor } from '@douyinfe/semi-ui'; -import React, { useCallback } from 'react'; +import { IconDoubleChevronLeft, IconDoubleChevronRight } from '@douyinfe/semi-icons'; +import { Anchor, Button } from '@douyinfe/semi-ui'; +import { useDocumentStyle, Width } from 'hooks/use-document-style'; +import { useToggle } from 'hooks/use-toggle'; +import React, { useCallback, useEffect } from 'react'; +import { TableOfContents } from 'tiptap/core/extensions/table-of-contents'; +import { findNode } from 'tiptap/prose-utils'; import { Editor } from '../react'; -import style from './index.module.scss'; +import styles from './index.module.scss'; interface IToc { level: number; @@ -10,24 +15,74 @@ interface IToc { text: string; } -const renderToc = (toc) => { +const MAX_LEVEL = 6; + +const Toc = ({ toc, collapsed }) => { return ( - - {toc.children && toc.children.length && toc.children.map(renderToc)} +
+ ) : ( + toc.text + ) + } + > + {toc.children && toc.children.length + ? toc.children.map((toc) => ) + : null} ); }; export const Tocs: React.FC<{ tocs: Array; editor: Editor }> = ({ tocs = [], editor }) => { + const [hasToc, toggleHasToc] = useToggle(false); + const [collapsed, toggleCollapsed] = useToggle(true); + const { width } = useDocumentStyle(); + const getContainer = useCallback(() => { - return document.querySelector(`#js-reader-container`); + return document.querySelector(`#js-tocs-container`); }, []); + useEffect(() => { + if (width === Width.fullWidth) { + toggleCollapsed(true); + } else { + toggleCollapsed(false); + } + }, [width, toggleCollapsed]); + + useEffect(() => { + const listener = () => { + const nodes = findNode(editor, TableOfContents.name); + const hasTocNow = !!(nodes && nodes.length); + + if (hasTocNow !== hasToc) { + toggleHasToc(hasTocNow); + } + }; + + editor.on('transaction', listener); + + return () => { + editor.off('transaction', listener); + }; + }, [editor, hasToc, toggleHasToc]); + return ( -
+
+
+ +
- - {tocs.length && tocs.map(renderToc)} + + {tocs.length && tocs.map((toc) => )}