From 5146b0f5a2e5b427821d7de8d2202642abe46d2f Mon Sep 17 00:00:00 2001 From: fantasticit Date: Fri, 19 Aug 2022 11:03:09 +0800 Subject: [PATCH 01/11] tiptap: dynamic placeholder --- .../src/tiptap/core/extensions/placeholder.ts | 96 ++++++++++++++++++- .../src/tiptap/editor/collaboration/kit.ts | 11 ++- 2 files changed, 103 insertions(+), 4 deletions(-) diff --git a/packages/client/src/tiptap/core/extensions/placeholder.ts b/packages/client/src/tiptap/core/extensions/placeholder.ts index a60a49f..d4c8b56 100644 --- a/packages/client/src/tiptap/core/extensions/placeholder.ts +++ b/packages/client/src/tiptap/core/extensions/placeholder.ts @@ -1,3 +1,95 @@ -import BuiltInPlaceholder from '@tiptap/extension-placeholder'; +import { Editor, Extension } from '@tiptap/core'; +import { Node as ProsemirrorNode } from 'prosemirror-model'; +import { Plugin } from 'prosemirror-state'; +import { Decoration, DecorationSet } from 'prosemirror-view'; -export const Placeholder = BuiltInPlaceholder; +export interface PlaceholderOptions { + emptyEditorClass: string; + emptyNodeClass: string; + placeholder: ((PlaceholderProps: { editor: Editor; node: ProsemirrorNode; pos: number }) => string) | string; + showOnlyWhenEditable: boolean; + showOnlyCurrent: boolean; + includeChildren: boolean; +} + +export const Placeholder = Extension.create({ + name: 'placeholder', + + addOptions() { + return { + emptyEditorClass: 'is-editor-empty', + emptyNodeClass: 'is-empty', + placeholder: 'Write something …', + showOnlyWhenEditable: true, + showOnlyCurrent: true, + includeChildren: false, + }; + }, + + addStorage() { + return new Map(); + }, + + addProseMirrorPlugins() { + return [ + new Plugin({ + props: { + decorations: ({ doc, selection }) => { + const active = this.editor.isEditable || !this.options.showOnlyWhenEditable; + const { anchor } = selection; + const decorations: Decoration[] = []; + + if (!active) { + return; + } + + doc.descendants((node, pos) => { + const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize; + const isEmpty = !node.isLeaf && !node.childCount; + + if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) { + const classes = [this.options.emptyNodeClass]; + + if (this.editor.isEmpty) { + classes.push(this.options.emptyEditorClass); + } + + const start = pos; + const end = pos + node.nodeSize; + const key = `${start}-${end}`; + + if (!this.editor.storage[this.name].has(key)) { + this.editor.storage[this.name].set( + key, + typeof this.options.placeholder === 'function' + ? this.options.placeholder({ + editor: this.editor, + node, + pos, + }) + : this.options.placeholder + ); + } + + const decoration = Decoration.node(start, end, { + 'class': classes.join(' '), + 'data-placeholder': this.editor.storage[this.name].get(key), + }); + + setTimeout(() => { + this.editor.storage[this.name].delete(key); + }, 500); + + decorations.push(decoration); + } + + return this.options.includeChildren; + }); + + return DecorationSet.create(doc, decorations); + }, + }, + }), + ]; + }, +}); diff --git a/packages/client/src/tiptap/editor/collaboration/kit.ts b/packages/client/src/tiptap/editor/collaboration/kit.ts index 1c23684..48f495e 100644 --- a/packages/client/src/tiptap/editor/collaboration/kit.ts +++ b/packages/client/src/tiptap/editor/collaboration/kit.ts @@ -1,5 +1,4 @@ import { Toast } from '@douyinfe/semi-ui'; -import scrollIntoView from 'scroll-into-view-if-needed'; // 自定义节点扩展 import { Attachment } from 'tiptap/core/extensions/attachment'; import { BackgroundColor } from 'tiptap/core/extensions/background-color'; @@ -78,6 +77,14 @@ const DocumentWithTitle = Document.extend({ export { Document }; +const placeholders = [ + '输入 / 唤起更多', + '使用 markdown 语法进行输入', + '输入 @ 来提及他人', + '输入 : 来插入表情', + '你知道吗?输入 $katex 然后按一下空格就可以快速插入数学公式,其他节点操作类似哦', +]; + export const CollaborationKit = [ Paragraph, Placeholder.configure({ @@ -88,7 +95,7 @@ export const CollaborationKit = [ if (!editor.isEditable) return; - return '输入 / 唤起更多'; + return placeholders[~~(Math.random() * placeholders.length)]; }, showOnlyCurrent: false, showOnlyWhenEditable: false, From c502474e57b0e5adebefe3a3f75ba418a0c2c32d Mon Sep 17 00:00:00 2001 From: fantasticit Date: Fri, 19 Aug 2022 11:03:33 +0800 Subject: [PATCH 02/11] client: format code --- packages/client/src/tiptap/core/extensions/quick-insert.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/client/src/tiptap/core/extensions/quick-insert.ts b/packages/client/src/tiptap/core/extensions/quick-insert.ts index 5df5eb7..95692f8 100644 --- a/packages/client/src/tiptap/core/extensions/quick-insert.ts +++ b/packages/client/src/tiptap/core/extensions/quick-insert.ts @@ -8,7 +8,7 @@ import { insertMenuLRUCache, QUICK_INSERT_COMMANDS, transformToCommands } from ' import { MenuList } from 'tiptap/core/wrappers/menu-list'; export const QuickInsertPluginKey = new PluginKey('quickInsert'); -const extensionName = 'quickInsert' +const extensionName = 'quickInsert'; export const QuickInsert = Node.create({ name: extensionName, @@ -63,8 +63,8 @@ export const QuickInsert = Node.create({ top: 0, right: 0, bottom: 0, - } - } + }, + }; }, }).configure({ suggestion: { From cb811ab21f5e2ca08d98445bfbf3858ed3dad1ef Mon Sep 17 00:00:00 2001 From: fantasticit Date: Fri, 19 Aug 2022 14:38:57 +0800 Subject: [PATCH 03/11] tiptap: fix status input blur --- .../src/tiptap/core/wrappers/status/index.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/client/src/tiptap/core/wrappers/status/index.tsx b/packages/client/src/tiptap/core/wrappers/status/index.tsx index a79081b..9885777 100644 --- a/packages/client/src/tiptap/core/wrappers/status/index.tsx +++ b/packages/client/src/tiptap/core/wrappers/status/index.tsx @@ -4,7 +4,7 @@ import { NodeViewWrapper } from '@tiptap/react'; import cls from 'classnames'; import { useUser } from 'data/user'; import { useToggle } from 'hooks/use-toggle'; -import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import styles from './index.module.scss'; @@ -24,14 +24,15 @@ export const StatusWrapper = ({ editor, node, updateAttributes }) => { const { user } = useUser(); const ref = useRef(); const [visible, toggleVisible] = useToggle(false); + const [currentText, setCurrentText] = useState(text); const content = useMemo( () => ( - {text || '点击设置状态'} + {currentText || '点击设置状态'} ), - [bgcolor, borderColor, currentTextColor, text] + [bgcolor, borderColor, currentTextColor, currentText] ); const onVisibleChange = useCallback( @@ -64,8 +65,10 @@ export const StatusWrapper = ({ editor, node, updateAttributes }) => { useEffect(() => { if (visible) { ref.current?.focus(); + } else { + updateAttributes({ text: currentText }); } - }, [visible]); + }, [visible, updateAttributes, currentText]); return ( @@ -78,7 +81,7 @@ export const StatusWrapper = ({ editor, node, updateAttributes }) => { content={
- updateAttributes({ text: v })} /> +
{STATUS_COLORS.map((color) => { From 2f97380e63581999ef1fddc872d0de0f3e669775 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Fri, 19 Aug 2022 16:14:00 +0800 Subject: [PATCH 04/11] tiptap: only use dynamic placeholder in edit --- .../src/tiptap/core/extensions/placeholder.ts | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/packages/client/src/tiptap/core/extensions/placeholder.ts b/packages/client/src/tiptap/core/extensions/placeholder.ts index d4c8b56..794b5f3 100644 --- a/packages/client/src/tiptap/core/extensions/placeholder.ts +++ b/packages/client/src/tiptap/core/extensions/placeholder.ts @@ -56,30 +56,44 @@ export const Placeholder = Extension.create({ const start = pos; const end = pos + node.nodeSize; - const key = `${start}-${end}`; + let placeholder = ''; - if (!this.editor.storage[this.name].has(key)) { - this.editor.storage[this.name].set( - key, + if (this.editor.isEditable) { + const key = `${start}-${end}`; + + if (!this.editor.storage[this.name].has(key)) { + this.editor.storage[this.name].set( + key, + typeof this.options.placeholder === 'function' + ? this.options.placeholder({ + editor: this.editor, + node, + pos, + }) + : this.options.placeholder + ); + } + placeholder = this.editor.storage[this.name].get(key); + + setTimeout(() => { + this.editor.storage[this.name].delete(key); + }, 500); + } else { + placeholder = typeof this.options.placeholder === 'function' ? this.options.placeholder({ editor: this.editor, node, pos, }) - : this.options.placeholder - ); + : this.options.placeholder; } const decoration = Decoration.node(start, end, { 'class': classes.join(' '), - 'data-placeholder': this.editor.storage[this.name].get(key), + 'data-placeholder': placeholder, }); - setTimeout(() => { - this.editor.storage[this.name].delete(key); - }, 500); - decorations.push(decoration); } From 3b68238249b072c2ee2029c0ed56f9a33d9d6624 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Fri, 19 Aug 2022 17:38:51 +0800 Subject: [PATCH 05/11] tiptap: improve mind toolbar --- .../kityminder/kity-core/module/priority.js | 2 +- .../src/tiptap/core/menus/mind/constant.ts | 33 ++ .../src/tiptap/core/menus/mind/modal.tsx | 7 + .../core/menus/mind/toolbar/bgcolor.tsx | 39 ++ .../core/menus/mind/toolbar/font-color.tsx | 39 ++ .../tiptap/core/menus/mind/toolbar/help.tsx | 33 ++ .../tiptap/core/menus/mind/toolbar/image.tsx | 4 +- .../tiptap/core/menus/mind/toolbar/index.tsx | 387 +++++++++--------- .../core/menus/mind/toolbar/priority.tsx | 29 ++ .../core/menus/mind/toolbar/progress.tsx | 29 ++ .../core/menus/mind/toolbar/template.tsx | 41 ++ .../tiptap/core/menus/mind/toolbar/theme.tsx | 42 ++ 12 files changed, 481 insertions(+), 204 deletions(-) create mode 100644 packages/client/src/tiptap/core/menus/mind/toolbar/bgcolor.tsx create mode 100644 packages/client/src/tiptap/core/menus/mind/toolbar/font-color.tsx create mode 100644 packages/client/src/tiptap/core/menus/mind/toolbar/help.tsx create mode 100644 packages/client/src/tiptap/core/menus/mind/toolbar/priority.tsx create mode 100644 packages/client/src/tiptap/core/menus/mind/toolbar/progress.tsx create mode 100644 packages/client/src/tiptap/core/menus/mind/toolbar/template.tsx create mode 100644 packages/client/src/tiptap/core/menus/mind/toolbar/theme.tsx diff --git a/packages/client/src/thirtypart/kityminder/kity-core/module/priority.js b/packages/client/src/thirtypart/kityminder/kity-core/module/priority.js index 7facd62..21e44fe 100644 --- a/packages/client/src/thirtypart/kityminder/kity-core/module/priority.js +++ b/packages/client/src/thirtypart/kityminder/kity-core/module/priority.js @@ -82,7 +82,7 @@ define(function (require, exports, module) { mask.fill(color[0]); } - number.setContent(value); + number.setContent('P' + value); }, }); diff --git a/packages/client/src/tiptap/core/menus/mind/constant.ts b/packages/client/src/tiptap/core/menus/mind/constant.ts index 9d8fbda..25e3367 100644 --- a/packages/client/src/tiptap/core/menus/mind/constant.ts +++ b/packages/client/src/tiptap/core/menus/mind/constant.ts @@ -1,3 +1,36 @@ +const getProgressTitle = (index) => { + switch (index) { + case 0: + return '移除进度'; + case 1: + return '未开始'; + case 9: + return '全部完成'; + default: + return '完成' + (index - 1) + '/8'; + } +}; + +export const PROGRESSES = Array.from({ length: 10 }, (_, i) => { + return { + text: getProgressTitle(i), + value: i, + }; +}); + +export const PRIORITIES = [ + { + text: '移除优先级', + value: 0, + }, + ...Array.from({ length: 9 }, (_, i) => { + return { + text: `P${i + 1}`, + value: i + 1, + }; + }), +]; + export const TEMPLATES = [ { label: '经典', diff --git a/packages/client/src/tiptap/core/menus/mind/modal.tsx b/packages/client/src/tiptap/core/menus/mind/modal.tsx index d8ecf19..f9518dc 100644 --- a/packages/client/src/tiptap/core/menus/mind/modal.tsx +++ b/packages/client/src/tiptap/core/menus/mind/modal.tsx @@ -65,6 +65,12 @@ export const MindSettingModal: React.FC = ({ editor }) => { }; }, [editor, toggleVisible]); + useEffect(() => { + if (!visible && mind) { + mind.destroy(); + } + }, [visible, mind]); + return ( = ({ editor }) => { onOk={save} okText="保存" cancelText="退出" + motion={false} >
{ + return ( + { + setBackgroundColor(color); + }} + > + +
+ } + /> + + + ); +}; diff --git a/packages/client/src/tiptap/core/menus/mind/toolbar/font-color.tsx b/packages/client/src/tiptap/core/menus/mind/toolbar/font-color.tsx new file mode 100644 index 0000000..f3b3894 --- /dev/null +++ b/packages/client/src/tiptap/core/menus/mind/toolbar/font-color.tsx @@ -0,0 +1,39 @@ +import { IconFont } from '@douyinfe/semi-icons'; +import { Button, Tooltip } from '@douyinfe/semi-ui'; +import { ColorPicker } from 'components/color-picker'; + +export const FontColor = ({ selectedNode, setFontColor, textColor }) => { + return ( + { + setFontColor(color); + }} + > + +
+ } + /> + + + ); +}; diff --git a/packages/client/src/tiptap/core/menus/mind/toolbar/help.tsx b/packages/client/src/tiptap/core/menus/mind/toolbar/help.tsx new file mode 100644 index 0000000..cc28a9c --- /dev/null +++ b/packages/client/src/tiptap/core/menus/mind/toolbar/help.tsx @@ -0,0 +1,33 @@ +import { IconHelpCircle } from '@douyinfe/semi-icons'; +import { Button, Descriptions, Popover } from '@douyinfe/semi-ui'; + +import styles from './index.module.scss'; + +const HELP_MESSAGE = [ + { key: '新增同级节点', value: 'Enter 键' }, + { key: '新增子节点', value: 'Tab 键' }, + { key: '编辑节点文字', value: '双击节点' }, + { key: '编辑节点菜单', value: '在节点右键' }, +]; + +const HELP_MESSAGE_STYLE = { + width: '200px', +}; + +export const Help = () => { + return ( + + + + } + > +