From cd22f259fab5b8cf827e7c7b7abf783207f53ce9 Mon Sep 17 00:00:00 2001 From: fantasticit Date: Thu, 24 Mar 2022 21:37:32 +0800 Subject: [PATCH] feat: improve tiptap editor --- .../client/src/components/tiptap/basekit.tsx | 2 + .../tiptap/extensions/attachment.ts | 4 +- .../components/tiptap/extensions/banner.ts | 3 +- .../components/tiptap/extensions/codeBlock.ts | 2 +- .../tiptap/extensions/documentChildren.ts | 23 +++--- .../tiptap/extensions/documentReference.ts | 7 +- .../src/components/tiptap/extensions/emoji.ts | 4 +- .../tiptap/extensions/evokeMenu.tsx | 2 +- .../tiptap/extensions/horizontalRule.ts | 1 + .../components/tiptap/extensions/iframe.ts | 5 +- .../src/components/tiptap/extensions/image.ts | 8 +- .../src/components/tiptap/extensions/katex.ts | 5 +- .../components/tiptap/extensions/loading.ts | 2 +- .../src/components/tiptap/extensions/mind.ts | 6 +- .../components/tiptap/extensions/selection.ts | 60 ++++++++++++++ .../components/tiptap/extensions/status.ts | 9 +-- .../src/components/tiptap/extensions/table.ts | 5 +- .../components/tiptap/extensions/taskItem.ts | 2 +- .../client/src/components/tiptap/menubar.tsx | 6 +- .../src/components/tiptap/menus/align.tsx | 3 + .../src/components/tiptap/menus/banner.tsx | 4 +- .../components/tiptap/menus/baseInsert.tsx | 2 +- .../src/components/tiptap/menus/color.tsx | 11 +-- .../color => colorPicker}/index.tsx | 2 +- .../color => colorPicker}/style.module.scss | 0 .../menus/{components => }/emoji/constants.ts | 0 .../{components => }/emoji/index.module.scss | 0 .../menus/{components => }/emoji/index.tsx | 0 .../src/components/tiptap/menus/evokeMenu.tsx | 19 +++++ .../menus/{components => }/fontSize.tsx | 2 +- .../src/components/tiptap/menus/image.tsx | 34 +++----- .../src/components/tiptap/menus/link.tsx | 8 +- .../components/tiptap/menus/mediaInsert.tsx | 8 +- .../menus/{components => }/paragraph.tsx | 2 +- .../components/tiptap/menus/search/index.tsx | 3 + .../src/components/tiptap/menus/table.tsx | 4 +- .../tiptap/services/lowlightPlugin.ts | 4 +- .../bubbleMenu/bubbleMenuPlugin.tsx | 0 .../components => views}/bubbleMenu/index.tsx | 0 .../attachment/index.module.scss | 0 .../attachment/index.tsx | 10 +-- .../banner/index.module.scss | 0 .../{components => wrappers}/banner/index.tsx | 3 +- .../codeBlock/index.module.scss | 0 .../codeBlock/index.tsx | 3 +- .../{components => wrappers}/divider.tsx | 0 .../documentChildren/index.module.scss | 17 +++- .../documentChildren/index.tsx | 5 +- .../documentReference/index.module.scss | 9 ++- .../documentReference/index.tsx | 2 +- .../emojiList/emojis.ts | 0 .../emojiList/index.module.scss | 0 .../emojiList/index.tsx | 0 .../iframe/index.module.scss | 0 .../{components => wrappers}/iframe/index.tsx | 5 +- .../image/index.module.scss | 0 .../{components => wrappers}/image/index.tsx | 8 +- .../katex/index.module.scss | 0 .../{components => wrappers}/katex/index.tsx | 5 +- .../{components => wrappers}/loading.tsx | 0 .../menuList/index.module.scss | 0 .../menuList/index.tsx | 0 .../mind/index.module.scss | 5 +- .../{components => wrappers}/mind/index.tsx | 3 +- .../{components => wrappers}/mind/jsmind.jsx | 0 .../status/index.module.scss | 2 +- .../{components => wrappers}/status/index.tsx | 12 ++- .../taskItem/index.module.scss | 0 .../taskItem/index.tsx | 0 packages/client/src/styles/prosemirror.scss | 80 ++++++++++++++++++- 70 files changed, 303 insertions(+), 128 deletions(-) create mode 100644 packages/client/src/components/tiptap/extensions/selection.ts rename packages/client/src/components/tiptap/menus/{components/color => colorPicker}/index.tsx (97%) rename packages/client/src/components/tiptap/menus/{components/color => colorPicker}/style.module.scss (100%) rename packages/client/src/components/tiptap/menus/{components => }/emoji/constants.ts (100%) rename packages/client/src/components/tiptap/menus/{components => }/emoji/index.module.scss (100%) rename packages/client/src/components/tiptap/menus/{components => }/emoji/index.tsx (100%) rename packages/client/src/components/tiptap/menus/{components => }/fontSize.tsx (93%) rename packages/client/src/components/tiptap/menus/{components => }/paragraph.tsx (96%) rename packages/client/src/components/tiptap/{menus/components => views}/bubbleMenu/bubbleMenuPlugin.tsx (100%) rename packages/client/src/components/tiptap/{menus/components => views}/bubbleMenu/index.tsx (100%) rename packages/client/src/components/tiptap/{components => wrappers}/attachment/index.module.scss (100%) rename packages/client/src/components/tiptap/{components => wrappers}/attachment/index.tsx (92%) rename packages/client/src/components/tiptap/{components => wrappers}/banner/index.module.scss (100%) rename packages/client/src/components/tiptap/{components => wrappers}/banner/index.tsx (74%) rename packages/client/src/components/tiptap/{components => wrappers}/codeBlock/index.module.scss (100%) rename packages/client/src/components/tiptap/{components => wrappers}/codeBlock/index.tsx (93%) rename packages/client/src/components/tiptap/{components => wrappers}/divider.tsx (100%) rename packages/client/src/components/tiptap/{components => wrappers}/documentChildren/index.module.scss (61%) rename packages/client/src/components/tiptap/{components => wrappers}/documentChildren/index.tsx (94%) rename packages/client/src/components/tiptap/{components => wrappers}/documentReference/index.module.scss (83%) rename packages/client/src/components/tiptap/{components => wrappers}/documentReference/index.tsx (95%) rename packages/client/src/components/tiptap/{components => wrappers}/emojiList/emojis.ts (100%) rename packages/client/src/components/tiptap/{components => wrappers}/emojiList/index.module.scss (100%) rename packages/client/src/components/tiptap/{components => wrappers}/emojiList/index.tsx (100%) rename packages/client/src/components/tiptap/{components => wrappers}/iframe/index.module.scss (100%) rename packages/client/src/components/tiptap/{components => wrappers}/iframe/index.tsx (92%) rename packages/client/src/components/tiptap/{components => wrappers}/image/index.module.scss (100%) rename packages/client/src/components/tiptap/{components => wrappers}/image/index.tsx (92%) rename packages/client/src/components/tiptap/{components => wrappers}/katex/index.module.scss (100%) rename packages/client/src/components/tiptap/{components => wrappers}/katex/index.tsx (90%) rename packages/client/src/components/tiptap/{components => wrappers}/loading.tsx (100%) rename packages/client/src/components/tiptap/{components => wrappers}/menuList/index.module.scss (100%) rename packages/client/src/components/tiptap/{components => wrappers}/menuList/index.tsx (100%) rename packages/client/src/components/tiptap/{components => wrappers}/mind/index.module.scss (96%) rename packages/client/src/components/tiptap/{components => wrappers}/mind/index.tsx (97%) rename packages/client/src/components/tiptap/{components => wrappers}/mind/jsmind.jsx (100%) rename packages/client/src/components/tiptap/{components => wrappers}/status/index.module.scss (79%) rename packages/client/src/components/tiptap/{components => wrappers}/status/index.tsx (82%) rename packages/client/src/components/tiptap/{components => wrappers}/taskItem/index.module.scss (100%) rename packages/client/src/components/tiptap/{components => wrappers}/taskItem/index.tsx (100%) diff --git a/packages/client/src/components/tiptap/basekit.tsx b/packages/client/src/components/tiptap/basekit.tsx index 78bae278..e4fd19e2 100644 --- a/packages/client/src/components/tiptap/basekit.tsx +++ b/packages/client/src/components/tiptap/basekit.tsx @@ -33,6 +33,7 @@ import { OrderedList } from './extensions/orderedList'; import { Paragraph } from './extensions/paragraph'; import { Placeholder } from './extensions/placeholder'; import { SearchNReplace } from './extensions/search'; +import { SelectionExtension } from './extensions/selection'; import { Status } from './extensions/status'; import { Strike } from './extensions/strike'; import { Table } from './extensions/table'; @@ -85,6 +86,7 @@ export const BaseKit = [ Paragraph, Placeholder, SearchNReplace, + SelectionExtension, Status, Strike, Table, diff --git a/packages/client/src/components/tiptap/extensions/attachment.ts b/packages/client/src/components/tiptap/extensions/attachment.ts index 9cabf05c..9dbc357a 100644 --- a/packages/client/src/components/tiptap/extensions/attachment.ts +++ b/packages/client/src/components/tiptap/extensions/attachment.ts @@ -1,6 +1,6 @@ import { Node, mergeAttributes } from '@tiptap/core'; import { ReactNodeViewRenderer } from '@tiptap/react'; -import { AttachmentWrapper } from '../components/attachment'; +import { AttachmentWrapper } from '../wrappers/attachment'; import { getDatasetAttribute } from '../services/dataset'; declare module '@tiptap/core' { @@ -68,7 +68,6 @@ export const Attachment = Node.create({ }; }, - // @ts-ignore addCommands() { return { setAttachment: @@ -78,6 +77,7 @@ export const Attachment = Node.create({ }, }; }, + addNodeView() { return ReactNodeViewRenderer(AttachmentWrapper); }, diff --git a/packages/client/src/components/tiptap/extensions/banner.ts b/packages/client/src/components/tiptap/extensions/banner.ts index c0e80480..53135f0b 100644 --- a/packages/client/src/components/tiptap/extensions/banner.ts +++ b/packages/client/src/components/tiptap/extensions/banner.ts @@ -1,6 +1,6 @@ import { Node, Command, mergeAttributes, wrappingInputRule } from '@tiptap/core'; import { ReactNodeViewRenderer } from '@tiptap/react'; -import { BannerWrapper } from '../components/banner'; +import { BannerWrapper } from '../wrappers/banner'; import { typesAvailable } from '../services/markdown/markdownToHTML/markdownBanner'; import { getDatasetAttribute } from '../services/dataset'; @@ -54,7 +54,6 @@ export const Banner = Node.create({ return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]; }, - // @ts-ignore addCommands() { return { setBanner: diff --git a/packages/client/src/components/tiptap/extensions/codeBlock.ts b/packages/client/src/components/tiptap/extensions/codeBlock.ts index 962a0547..55004ec6 100644 --- a/packages/client/src/components/tiptap/extensions/codeBlock.ts +++ b/packages/client/src/components/tiptap/extensions/codeBlock.ts @@ -3,7 +3,7 @@ import { Node, textblockTypeInputRule, mergeAttributes } from '@tiptap/core'; import { Plugin, PluginKey, TextSelection } from 'prosemirror-state'; import { ReactNodeViewRenderer } from '@tiptap/react'; import { LowlightPlugin } from '../services/lowlightPlugin'; -import { CodeBlockWrapper } from '../components/codeBlock'; +import { CodeBlockWrapper } from '../wrappers/codeBlock'; export interface CodeBlockOptions { /** diff --git a/packages/client/src/components/tiptap/extensions/documentChildren.ts b/packages/client/src/components/tiptap/extensions/documentChildren.ts index cfae46de..cfe79646 100644 --- a/packages/client/src/components/tiptap/extensions/documentChildren.ts +++ b/packages/client/src/components/tiptap/extensions/documentChildren.ts @@ -1,6 +1,6 @@ import { Node, mergeAttributes, wrappingInputRule } from '@tiptap/core'; import { ReactNodeViewRenderer } from '@tiptap/react'; -import { DocumentChildrenWrapper } from '../components/documentChildren'; +import { DocumentChildrenWrapper } from '../wrappers/documentChildren'; import { getDatasetAttribute } from '../services/dataset'; declare module '@tiptap/core' { @@ -16,10 +16,16 @@ export const DocumentChildrenInputRegex = /^documentChildren\$$/; export const DocumentChildren = Node.create({ name: 'documentChildren', group: 'block', - draggable: true, - selectable: true, atom: true, + addOptions() { + return { + HTMLAttributes: { + class: 'documentChildren', + }, + }; + }, + addAttributes() { return { wikiId: { @@ -32,18 +38,11 @@ export const DocumentChildren = Node.create({ }, }; }, - addOptions() { - return { - HTMLAttributes: { - class: 'documentChildren', - }, - }; - }, parseHTML() { return [ { - tag: 'div', + tag: 'div.documentChildren', }, ]; }, @@ -51,7 +50,7 @@ export const DocumentChildren = Node.create({ renderHTML({ HTMLAttributes }) { return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]; }, - // @ts-ignore + addCommands() { return { setDocumentChildren: diff --git a/packages/client/src/components/tiptap/extensions/documentReference.ts b/packages/client/src/components/tiptap/extensions/documentReference.ts index 179afb47..c17be2dc 100644 --- a/packages/client/src/components/tiptap/extensions/documentReference.ts +++ b/packages/client/src/components/tiptap/extensions/documentReference.ts @@ -1,6 +1,6 @@ import { Node, mergeAttributes, wrappingInputRule } from '@tiptap/core'; import { ReactNodeViewRenderer } from '@tiptap/react'; -import { DocumentReferenceWrapper } from '../components/documentReference'; +import { DocumentReferenceWrapper } from '../wrappers/documentReference'; import { getDatasetAttribute } from '../services/dataset'; declare module '@tiptap/core' { @@ -16,9 +16,7 @@ export const DocumentReferenceInputRegex = /^documentReference\$$/; export const DocumentReference = Node.create({ name: 'documentReference', group: 'block', - draggable: true, atom: true, - selectable: true, addAttributes() { return { @@ -48,7 +46,7 @@ export const DocumentReference = Node.create({ parseHTML() { return [ { - tag: 'div', + tag: 'div.documentReference', }, ]; }, @@ -57,7 +55,6 @@ export const DocumentReference = Node.create({ return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]; }, - // @ts-ignore addCommands() { return { setDocumentReference: diff --git a/packages/client/src/components/tiptap/extensions/emoji.ts b/packages/client/src/components/tiptap/extensions/emoji.ts index 1fff0746..59c09133 100644 --- a/packages/client/src/components/tiptap/extensions/emoji.ts +++ b/packages/client/src/components/tiptap/extensions/emoji.ts @@ -4,8 +4,8 @@ import { Plugin, PluginKey } from 'prosemirror-state'; import { Decoration, DecorationSet } from 'prosemirror-view'; import Suggestion from '@tiptap/suggestion'; import tippy from 'tippy.js'; -import { EmojiList } from '../components/emojiList'; -import { emojiSearch, emojisToName } from '../components/emojiList/emojis'; +import { EmojiList } from '../wrappers/emojiList'; +import { emojiSearch, emojisToName } from '../wrappers/emojiList/emojis'; declare module '@tiptap/core' { interface Commands { diff --git a/packages/client/src/components/tiptap/extensions/evokeMenu.tsx b/packages/client/src/components/tiptap/extensions/evokeMenu.tsx index 80716eb7..d9e397e3 100644 --- a/packages/client/src/components/tiptap/extensions/evokeMenu.tsx +++ b/packages/client/src/components/tiptap/extensions/evokeMenu.tsx @@ -4,7 +4,7 @@ import { Plugin, PluginKey } from 'prosemirror-state'; import { Decoration, DecorationSet } from 'prosemirror-view'; import Suggestion from '@tiptap/suggestion'; import tippy from 'tippy.js'; -import { MenuList } from '../components/menuList'; +import { MenuList } from '../wrappers/menuList'; import { EVOKE_MENU_ITEMS } from '../menus/evokeMenu'; export const EvokeMenuPluginKey = new PluginKey('evokeMenu'); diff --git a/packages/client/src/components/tiptap/extensions/horizontalRule.ts b/packages/client/src/components/tiptap/extensions/horizontalRule.ts index f9c37438..e588b1c1 100644 --- a/packages/client/src/components/tiptap/extensions/horizontalRule.ts +++ b/packages/client/src/components/tiptap/extensions/horizontalRule.ts @@ -16,6 +16,7 @@ declare module '@tiptap/core' { export const HorizontalRule = Node.create({ name: 'horizontalRule', group: 'block', + selectable: true, addOptions() { return { diff --git a/packages/client/src/components/tiptap/extensions/iframe.ts b/packages/client/src/components/tiptap/extensions/iframe.ts index 109d400d..4fc18502 100644 --- a/packages/client/src/components/tiptap/extensions/iframe.ts +++ b/packages/client/src/components/tiptap/extensions/iframe.ts @@ -1,6 +1,6 @@ import { Node, mergeAttributes } from '@tiptap/core'; import { ReactNodeViewRenderer } from '@tiptap/react'; -import { IframeWrapper } from '../components/iframe'; +import { IframeWrapper } from '../wrappers/iframe'; import { getDatasetAttribute } from '../services/dataset'; declare module '@tiptap/core' { @@ -16,7 +16,7 @@ export const Iframe = Node.create({ content: '', marks: '', group: 'block', - draggable: true, + selectable: true, atom: true, addOptions() { @@ -56,7 +56,6 @@ export const Iframe = Node.create({ return ['iframe', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]; }, - // @ts-ignore addCommands() { return { setIframe: diff --git a/packages/client/src/components/tiptap/extensions/image.ts b/packages/client/src/components/tiptap/extensions/image.ts index 4a308859..84dffad3 100644 --- a/packages/client/src/components/tiptap/extensions/image.ts +++ b/packages/client/src/components/tiptap/extensions/image.ts @@ -1,6 +1,6 @@ import { Image as BuiltInImage } from '@tiptap/extension-image'; import { ReactNodeViewRenderer } from '@tiptap/react'; -import { ImageWrapper } from '../components/image'; +import { ImageWrapper } from '../wrappers/image'; const resolveImageEl = (element) => (element.nodeName === 'IMG' ? element : element.querySelector('img')); @@ -20,10 +20,12 @@ export const Image = BuiltInImage.extend({ content: '', marks: '', group: 'block', - draggable: true, + draggable: false, + selectable: true, atom: true, }; }, + addAttributes() { return { ...this.parent?.(), @@ -59,6 +61,7 @@ export const Image = BuiltInImage.extend({ }, }; }, + addCommands() { return { ...this.parent?.(), @@ -69,6 +72,7 @@ export const Image = BuiltInImage.extend({ }, }; }, + addNodeView() { return ReactNodeViewRenderer(ImageWrapper); }, diff --git a/packages/client/src/components/tiptap/extensions/katex.ts b/packages/client/src/components/tiptap/extensions/katex.ts index 91b992ce..20fc7b03 100644 --- a/packages/client/src/components/tiptap/extensions/katex.ts +++ b/packages/client/src/components/tiptap/extensions/katex.ts @@ -1,6 +1,6 @@ import { Node, mergeAttributes, nodeInputRule } from '@tiptap/core'; import { ReactNodeViewRenderer } from '@tiptap/react'; -import { KatexWrapper } from '../components/katex'; +import { KatexWrapper } from '../wrappers/katex'; declare module '@tiptap/core' { interface Commands { @@ -46,11 +46,10 @@ export const Katex = Node.create({ return ['span', mergeAttributes((this.options && this.options.HTMLAttributes) || {}, HTMLAttributes)]; }, - // @ts-ignore addCommands() { return { setKatex: - (options) => + (options = {}) => ({ commands }) => { return commands.insertContent({ type: this.name, diff --git a/packages/client/src/components/tiptap/extensions/loading.ts b/packages/client/src/components/tiptap/extensions/loading.ts index 20d81a72..4707fe64 100644 --- a/packages/client/src/components/tiptap/extensions/loading.ts +++ b/packages/client/src/components/tiptap/extensions/loading.ts @@ -1,6 +1,6 @@ import { Node } from '@tiptap/core'; import { ReactNodeViewRenderer } from '@tiptap/react'; -import { LoadingWrapper } from '../components/loading'; +import { LoadingWrapper } from '../wrappers/loading'; export const Loading = Node.create({ name: 'loading', diff --git a/packages/client/src/components/tiptap/extensions/mind.ts b/packages/client/src/components/tiptap/extensions/mind.ts index 4831927b..14da112b 100644 --- a/packages/client/src/components/tiptap/extensions/mind.ts +++ b/packages/client/src/components/tiptap/extensions/mind.ts @@ -1,7 +1,6 @@ import { Node, mergeAttributes, nodeInputRule } from '@tiptap/core'; import { ReactNodeViewRenderer } from '@tiptap/react'; -import { safeJSONParse } from 'helpers/json'; -import { MindWrapper } from '../components/mind'; +import { MindWrapper } from '../wrappers/mind'; import { getDatasetAttribute } from '../services/dataset'; const DEFAULT_MIND_DATA = { @@ -27,7 +26,7 @@ export const Mind = Node.create({ content: '', marks: '', group: 'block', - draggable: true, + selectable: true, atom: true, addAttributes() { @@ -67,7 +66,6 @@ export const Mind = Node.create({ return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]; }, - // @ts-ignore addCommands() { return { setMind: diff --git a/packages/client/src/components/tiptap/extensions/selection.ts b/packages/client/src/components/tiptap/extensions/selection.ts new file mode 100644 index 00000000..19a2ef2d --- /dev/null +++ b/packages/client/src/components/tiptap/extensions/selection.ts @@ -0,0 +1,60 @@ +import { Extension } from '@tiptap/core'; +import { Plugin, PluginKey, NodeSelection, TextSelection, Selection, AllSelection } from 'prosemirror-state'; +import { Decoration, DecorationSet } from 'prosemirror-view'; +import { EXTENSION_PRIORITY_HIGHEST } from '../constants'; + +export const selectionPluginKey = new PluginKey('selection'); + +export const getTopLevelNodesFromSelection = (selection: Selection, doc) => { + const nodes: { node; pos: number }[] = []; + if (selection.from !== selection.to) { + const { from, to } = selection; + doc.nodesBetween(from, to, (node, pos) => { + const withinSelection = from <= pos && pos + node.nodeSize <= to; + if (node && node.type.name !== 'paragraph' && !node.isText && withinSelection) { + nodes.push({ node, pos }); + return false; + } + return true; + }); + } + return nodes; +}; + +export const getDecorations = (doc, selection: Selection): DecorationSet => { + if (selection instanceof NodeSelection) { + return DecorationSet.create(doc, [ + Decoration.node(selection.from, selection.to, { + class: 'selected-node', + }), + ]); + } + if (selection instanceof TextSelection || selection instanceof AllSelection) { + const decorations = getTopLevelNodesFromSelection(selection, doc).map(({ node, pos }) => { + return Decoration.node(pos, pos + node.nodeSize, { + class: 'selected-node', + }); + }); + return DecorationSet.create(doc, decorations); + } + return DecorationSet.empty; +}; + +export const SelectionExtension = Extension.create({ + name: 'selection', + priority: EXTENSION_PRIORITY_HIGHEST, + addProseMirrorPlugins() { + return [ + new Plugin({ + key: selectionPluginKey, + props: { + decorations(state) { + const { doc, selection } = state; + const decorationSet = getDecorations(doc, selection); + return decorationSet; + }, + }, + }), + ]; + }, +}); diff --git a/packages/client/src/components/tiptap/extensions/status.ts b/packages/client/src/components/tiptap/extensions/status.ts index 00107d97..f027d413 100644 --- a/packages/client/src/components/tiptap/extensions/status.ts +++ b/packages/client/src/components/tiptap/extensions/status.ts @@ -1,6 +1,6 @@ import { Node, mergeAttributes } from '@tiptap/core'; import { ReactNodeViewRenderer } from '@tiptap/react'; -import { StatusWrapper } from '../components/status'; +import { StatusWrapper } from '../wrappers/status'; import { getDatasetAttribute } from '../services/dataset'; declare module '@tiptap/core' { @@ -41,20 +41,19 @@ export const Status = Node.create({ parseHTML() { return [ { - tag: 'div', + tag: 'span.status', }, ]; }, renderHTML({ HTMLAttributes }) { - return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]; + return ['span', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]; }, - // @ts-ignore addCommands() { return { setStatus: - (options) => + (options = {}) => ({ commands }) => { return commands.insertContent({ type: this.name, diff --git a/packages/client/src/components/tiptap/extensions/table.ts b/packages/client/src/components/tiptap/extensions/table.ts index 519fc6ba..01d2c08c 100644 --- a/packages/client/src/components/tiptap/extensions/table.ts +++ b/packages/client/src/components/tiptap/extensions/table.ts @@ -40,17 +40,18 @@ export const Table = BuiltInTable.extend({ if (fixedWidth && totalWidth > 0) { HTMLAttributes.style = `width: ${totalWidth}px;`; } else if (totalWidth && totalWidth > 0) { - HTMLAttributes.style = `min-width: 100%`; + HTMLAttributes.style = `min-width: ${totalWidth}px`; } else { HTMLAttributes.style = null; } return [ 'div', - { class: 'tableWrapper' }, + { class: 'tableWrapper adas' }, ['table', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), ['tbody', 0]], ]; }, }).configure({ resizable: true, + cellMinWidth: 50, }); diff --git a/packages/client/src/components/tiptap/extensions/taskItem.ts b/packages/client/src/components/tiptap/extensions/taskItem.ts index 36bd5403..d3749935 100644 --- a/packages/client/src/components/tiptap/extensions/taskItem.ts +++ b/packages/client/src/components/tiptap/extensions/taskItem.ts @@ -4,7 +4,7 @@ import { TaskItem as BuiltInTaskItem } from '@tiptap/extension-task-item'; import { Plugin } from 'prosemirror-state'; import { findParentNodeClosestToPos } from 'prosemirror-utils'; import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants'; -import { TaskItemWrapper } from '../components/taskItem'; +import { TaskItemWrapper } from '../wrappers/taskItem'; const CustomTaskItem = BuiltInTaskItem.extend({ parseHTML() { diff --git a/packages/client/src/components/tiptap/menubar.tsx b/packages/client/src/components/tiptap/menubar.tsx index 38695471..cce3d6e7 100644 --- a/packages/client/src/components/tiptap/menubar.tsx +++ b/packages/client/src/components/tiptap/menubar.tsx @@ -3,10 +3,10 @@ import { Space, Button } from '@douyinfe/semi-ui'; import { IconUndo, IconRedo } from '@douyinfe/semi-icons'; import { Tooltip } from 'components/tooltip'; import { IconClear } from 'components/icons'; -import { Divider } from './components/divider'; +import { Divider } from './wrappers/divider'; import { MediaInsertMenu } from './menus/mediaInsert'; -import { Paragraph } from './menus/components/paragraph'; -import { FontSize } from './menus/components/fontSize'; +import { Paragraph } from './menus/paragraph'; +import { FontSize } from './menus/fontSize'; import { BaseMenu } from './menus/baseMenu'; import { AlignMenu } from './menus/align'; import { ListMenu } from './menus/list'; diff --git a/packages/client/src/components/tiptap/menus/align.tsx b/packages/client/src/components/tiptap/menus/align.tsx index 8c02cfe4..ae683877 100644 --- a/packages/client/src/components/tiptap/menus/align.tsx +++ b/packages/client/src/components/tiptap/menus/align.tsx @@ -29,12 +29,15 @@ export const AlignMenu = ({ editor }) => { + + + diff --git a/packages/client/src/components/tiptap/menus/table.tsx b/packages/client/src/components/tiptap/menus/table.tsx index a5d1e2c6..b6580f23 100644 --- a/packages/client/src/components/tiptap/menus/table.tsx +++ b/packages/client/src/components/tiptap/menus/table.tsx @@ -11,8 +11,8 @@ import { IconDeleteTable, } from 'components/icons'; import { Tooltip } from 'components/tooltip'; -import { Divider } from '../components/divider'; -import { BubbleMenu } from './components/bubbleMenu'; +import { Divider } from '../wrappers/divider'; +import { BubbleMenu } from '../views/bubbleMenu'; import { Table } from '../extensions/table'; export const TableBubbleMenu = ({ editor }) => { diff --git a/packages/client/src/components/tiptap/services/lowlightPlugin.ts b/packages/client/src/components/tiptap/services/lowlightPlugin.ts index b16d28c6..2c384e32 100644 --- a/packages/client/src/components/tiptap/services/lowlightPlugin.ts +++ b/packages/client/src/components/tiptap/services/lowlightPlugin.ts @@ -102,14 +102,14 @@ export function LowlightPlugin({ // (for example, a transaction that affects the entire document). // Such transactions can happen during collab syncing via y-prosemirror, for example. transaction.steps.some((step) => { - // @ts-ignore return ( + // @ts-ignore step.from !== undefined && // @ts-ignore step.to !== undefined && oldNodes.some((node) => { - // @ts-ignore return ( + // @ts-ignore node.pos >= step.from && // @ts-ignore node.pos + node.node.nodeSize <= step.to diff --git a/packages/client/src/components/tiptap/menus/components/bubbleMenu/bubbleMenuPlugin.tsx b/packages/client/src/components/tiptap/views/bubbleMenu/bubbleMenuPlugin.tsx similarity index 100% rename from packages/client/src/components/tiptap/menus/components/bubbleMenu/bubbleMenuPlugin.tsx rename to packages/client/src/components/tiptap/views/bubbleMenu/bubbleMenuPlugin.tsx diff --git a/packages/client/src/components/tiptap/menus/components/bubbleMenu/index.tsx b/packages/client/src/components/tiptap/views/bubbleMenu/index.tsx similarity index 100% rename from packages/client/src/components/tiptap/menus/components/bubbleMenu/index.tsx rename to packages/client/src/components/tiptap/views/bubbleMenu/index.tsx diff --git a/packages/client/src/components/tiptap/components/attachment/index.module.scss b/packages/client/src/components/tiptap/wrappers/attachment/index.module.scss similarity index 100% rename from packages/client/src/components/tiptap/components/attachment/index.module.scss rename to packages/client/src/components/tiptap/wrappers/attachment/index.module.scss diff --git a/packages/client/src/components/tiptap/components/attachment/index.tsx b/packages/client/src/components/tiptap/wrappers/attachment/index.tsx similarity index 92% rename from packages/client/src/components/tiptap/components/attachment/index.tsx rename to packages/client/src/components/tiptap/wrappers/attachment/index.tsx index 3f1de1d7..d6c4dbe1 100644 --- a/packages/client/src/components/tiptap/components/attachment/index.tsx +++ b/packages/client/src/components/tiptap/wrappers/attachment/index.tsx @@ -1,4 +1,5 @@ import { useEffect, useRef } from 'react'; +import cls from 'classnames'; import { NodeViewWrapper, NodeViewContent } from '@tiptap/react'; import { Button, Typography, Spin, Collapsible, Space } from '@douyinfe/semi-ui'; import { @@ -47,7 +48,7 @@ const getFileTypeIcon = (type: FileType) => { }; export const AttachmentWrapper = ({ editor, node, updateAttributes }) => { - const $upload = useRef(); + const $upload = useRef(); const isEditable = editor.isEditable; const { hasTrigger, fileName, fileSize, fileExt, fileType, url, error } = node.attrs; const [loading, toggleLoading] = useToggle(false); @@ -55,7 +56,6 @@ export const AttachmentWrapper = ({ editor, node, updateAttributes }) => { const selectFile = () => { if (!isEditable || error || url) return; - // @ts-ignore isEditable && $upload.current.click(); }; @@ -90,7 +90,7 @@ export const AttachmentWrapper = ({ editor, node, updateAttributes }) => { const content = (() => { if (error) { return ( -
+
{error}
); @@ -99,7 +99,7 @@ export const AttachmentWrapper = ({ editor, node, updateAttributes }) => { if (url) { return ( <> -
+
{getFileTypeIcon(type)} {fileName}.{fileExt} @@ -139,7 +139,7 @@ export const AttachmentWrapper = ({ editor, node, updateAttributes }) => { if (isEditable && !url) { return ( -
+
{loading ? '正在上传中' : '请选择文件'} diff --git a/packages/client/src/components/tiptap/components/banner/index.module.scss b/packages/client/src/components/tiptap/wrappers/banner/index.module.scss similarity index 100% rename from packages/client/src/components/tiptap/components/banner/index.module.scss rename to packages/client/src/components/tiptap/wrappers/banner/index.module.scss diff --git a/packages/client/src/components/tiptap/components/banner/index.tsx b/packages/client/src/components/tiptap/wrappers/banner/index.tsx similarity index 74% rename from packages/client/src/components/tiptap/components/banner/index.tsx rename to packages/client/src/components/tiptap/wrappers/banner/index.tsx index ec3b5022..49d073aa 100644 --- a/packages/client/src/components/tiptap/components/banner/index.tsx +++ b/packages/client/src/components/tiptap/wrappers/banner/index.tsx @@ -1,10 +1,11 @@ import { NodeViewWrapper, NodeViewContent } from '@tiptap/react'; import { Banner as SemiBanner } from '@douyinfe/semi-ui'; +import cls from 'classnames'; import styles from './index.module.scss'; export const BannerWrapper = ({ node }) => { return ( - + } closeIcon={null} fullMode={false} /> ); diff --git a/packages/client/src/components/tiptap/components/codeBlock/index.module.scss b/packages/client/src/components/tiptap/wrappers/codeBlock/index.module.scss similarity index 100% rename from packages/client/src/components/tiptap/components/codeBlock/index.module.scss rename to packages/client/src/components/tiptap/wrappers/codeBlock/index.module.scss diff --git a/packages/client/src/components/tiptap/components/codeBlock/index.tsx b/packages/client/src/components/tiptap/wrappers/codeBlock/index.tsx similarity index 93% rename from packages/client/src/components/tiptap/components/codeBlock/index.tsx rename to packages/client/src/components/tiptap/wrappers/codeBlock/index.tsx index 9acce0db..b0b4b245 100644 --- a/packages/client/src/components/tiptap/components/codeBlock/index.tsx +++ b/packages/client/src/components/tiptap/wrappers/codeBlock/index.tsx @@ -1,4 +1,5 @@ import React, { useRef } from 'react'; +import cls from 'classnames'; import { NodeViewWrapper, NodeViewContent } from '@tiptap/react'; import { Button, Select, Tooltip } from '@douyinfe/semi-ui'; import { IconCopy } from '@douyinfe/semi-icons'; @@ -17,7 +18,7 @@ export const CodeBlockWrapper = ({ const $container = useRef(); return ( - +
{isEditable && ( updateAttributes({ url })}> diff --git a/packages/client/src/components/tiptap/components/image/index.module.scss b/packages/client/src/components/tiptap/wrappers/image/index.module.scss similarity index 100% rename from packages/client/src/components/tiptap/components/image/index.module.scss rename to packages/client/src/components/tiptap/wrappers/image/index.module.scss diff --git a/packages/client/src/components/tiptap/components/image/index.tsx b/packages/client/src/components/tiptap/wrappers/image/index.tsx similarity index 92% rename from packages/client/src/components/tiptap/components/image/index.tsx rename to packages/client/src/components/tiptap/wrappers/image/index.tsx index e519847e..f0e189b7 100644 --- a/packages/client/src/components/tiptap/components/image/index.tsx +++ b/packages/client/src/components/tiptap/wrappers/image/index.tsx @@ -1,6 +1,7 @@ import { NodeViewWrapper, NodeViewContent } from '@tiptap/react'; import { Resizeable } from 'components/resizeable'; import { useEffect, useRef } from 'react'; +import cls from 'classnames'; import { Typography, Spin } from '@douyinfe/semi-ui'; import { useToggle } from 'hooks/useToggle'; import { uploadFile } from 'services/file'; @@ -12,7 +13,7 @@ const { Text } = Typography; export const ImageWrapper = ({ editor, node, updateAttributes }) => { const isEditable = editor.isEditable; const { hasTrigger, error, src, alt, title, width, height, textAlign } = node.attrs; - const $upload = useRef(); + const $upload = useRef(); const [loading, toggleLoading] = useToggle(false); const onResize = (size) => { @@ -21,7 +22,6 @@ export const ImageWrapper = ({ editor, node, updateAttributes }) => { const selectFile = () => { if (!isEditable || error || src) return; - // @ts-ignore isEditable && $upload.current.click(); }; @@ -54,7 +54,7 @@ export const ImageWrapper = ({ editor, node, updateAttributes }) => { const content = (() => { if (error) { return ( -
+
{error}
); @@ -62,7 +62,7 @@ export const ImageWrapper = ({ editor, node, updateAttributes }) => { if (!src) { return ( -
+
{loading ? '正在上传中' : '请选择图片'} diff --git a/packages/client/src/components/tiptap/components/katex/index.module.scss b/packages/client/src/components/tiptap/wrappers/katex/index.module.scss similarity index 100% rename from packages/client/src/components/tiptap/components/katex/index.module.scss rename to packages/client/src/components/tiptap/wrappers/katex/index.module.scss diff --git a/packages/client/src/components/tiptap/components/katex/index.tsx b/packages/client/src/components/tiptap/wrappers/katex/index.tsx similarity index 90% rename from packages/client/src/components/tiptap/components/katex/index.tsx rename to packages/client/src/components/tiptap/wrappers/katex/index.tsx index 76c41ae7..331bc0fb 100644 --- a/packages/client/src/components/tiptap/components/katex/index.tsx +++ b/packages/client/src/components/tiptap/wrappers/katex/index.tsx @@ -1,5 +1,6 @@ import { NodeViewWrapper, NodeViewContent } from '@tiptap/react'; -import { useEffect, useMemo } from 'react'; +import { useMemo } from 'react'; +import cls from 'classnames'; import { Popover, TextArea, Typography, Space } from '@douyinfe/semi-ui'; import { IconHelpCircle } from '@douyinfe/semi-icons'; import katex from 'katex'; @@ -26,7 +27,7 @@ export const KatexWrapper = ({ editor, node, updateAttributes }) => { ); return ( - + {isEditable ? ( input { diff --git a/packages/client/src/components/tiptap/components/mind/index.tsx b/packages/client/src/components/tiptap/wrappers/mind/index.tsx similarity index 97% rename from packages/client/src/components/tiptap/components/mind/index.tsx rename to packages/client/src/components/tiptap/wrappers/mind/index.tsx index f92769ad..cb589298 100644 --- a/packages/client/src/components/tiptap/components/mind/index.tsx +++ b/packages/client/src/components/tiptap/wrappers/mind/index.tsx @@ -1,4 +1,5 @@ import { NodeViewWrapper, NodeViewContent } from '@tiptap/react'; +import cls from 'classnames'; import { useCallback, useEffect, useRef } from 'react'; import { Button } from '@douyinfe/semi-ui'; import { IconMinus, IconPlus } from '@douyinfe/semi-icons'; @@ -120,7 +121,7 @@ export const MindWrapper = ({ editor, node, updateAttributes }) => { ); return ( - + {isEditable ? ( diff --git a/packages/client/src/components/tiptap/components/mind/jsmind.jsx b/packages/client/src/components/tiptap/wrappers/mind/jsmind.jsx similarity index 100% rename from packages/client/src/components/tiptap/components/mind/jsmind.jsx rename to packages/client/src/components/tiptap/wrappers/mind/jsmind.jsx diff --git a/packages/client/src/components/tiptap/components/status/index.module.scss b/packages/client/src/components/tiptap/wrappers/status/index.module.scss similarity index 79% rename from packages/client/src/components/tiptap/components/status/index.module.scss rename to packages/client/src/components/tiptap/wrappers/status/index.module.scss index ba5bcd52..833ac536 100644 --- a/packages/client/src/components/tiptap/components/status/index.module.scss +++ b/packages/client/src/components/tiptap/wrappers/status/index.module.scss @@ -1,6 +1,6 @@ .wrap { display: inline-block; vertical-align: middle; - margin-right: 4px; + font-size: 0; cursor: pointer; } diff --git a/packages/client/src/components/tiptap/components/status/index.tsx b/packages/client/src/components/tiptap/wrappers/status/index.tsx similarity index 82% rename from packages/client/src/components/tiptap/components/status/index.tsx rename to packages/client/src/components/tiptap/wrappers/status/index.tsx index c2d1bb95..ff00f361 100644 --- a/packages/client/src/components/tiptap/components/status/index.tsx +++ b/packages/client/src/components/tiptap/wrappers/status/index.tsx @@ -1,14 +1,19 @@ import { NodeViewWrapper, NodeViewContent } from '@tiptap/react'; import { Space, Popover, Tag, Input } from '@douyinfe/semi-ui'; +import cls from 'classnames'; import styles from './index.module.scss'; export const StatusWrapper = ({ editor, node, updateAttributes }) => { const isEditable = editor.isEditable; const { color, text } = node.attrs; - const content = {text || '点击设置状态'}; + const content = ( + + {text || '点击设置状态'} + + ); return ( - + {isEditable ? ( { key={color} style={{ width: 24, height: 24, cursor: 'pointer' }} type="solid" - // @ts-ignore - color={color} + color={color as unknown as any} onClick={() => updateAttributes({ color })} > ); diff --git a/packages/client/src/components/tiptap/components/taskItem/index.module.scss b/packages/client/src/components/tiptap/wrappers/taskItem/index.module.scss similarity index 100% rename from packages/client/src/components/tiptap/components/taskItem/index.module.scss rename to packages/client/src/components/tiptap/wrappers/taskItem/index.module.scss diff --git a/packages/client/src/components/tiptap/components/taskItem/index.tsx b/packages/client/src/components/tiptap/wrappers/taskItem/index.tsx similarity index 100% rename from packages/client/src/components/tiptap/components/taskItem/index.tsx rename to packages/client/src/components/tiptap/wrappers/taskItem/index.tsx diff --git a/packages/client/src/styles/prosemirror.scss b/packages/client/src/styles/prosemirror.scss index 61de7c8b..da9d6fa2 100644 --- a/packages/client/src/styles/prosemirror.scss +++ b/packages/client/src/styles/prosemirror.scss @@ -255,8 +255,7 @@ pre { position: relative; - border-radius: var(--border-radius); - margin: 0.75rem 0px; + margin: 0; counter-reset: line 0; display: flex; min-width: 48px; @@ -264,7 +263,6 @@ line-height: 1.3; background-color: #0d0d0d; background-color: var(--semi-color-fill-0); - border: 1px solid var(--semi-color-border); code { color: inherit; @@ -397,6 +395,82 @@ .search-result-current { background: rgb(255, 0, 0); } + + /******* 选中样式 *******/ + hr.selected-node { + background-color: rgb(0, 101, 255); + } + + .node-status { + .semi-tag-default { + border: 1px solid var(--semi-color-border); + } + + &.selected-node { + .semi-tag-default { + border: 1px solid rgb(0 101 255); + } + } + } + + .node-codeBlock, + .node-documentChildren, + .node-documentReference, + .node-katex { + .render-wrapper { + border: 1px solid transparent; + } + } + + .node-katex { + .render-wrapper { + border-radius: var(--border-radius); + } + } + + .node-attachment, + .node-banner, + .node-iframe, + .node-image, + .node-katex, + .node-mind, + .node-codeBlock, + .node-documentChildren, + .node-documentReference { + &:not(.has-focus) { + ::selection { + background-color: transparent; + } + } + // #e0ebfa + + .render-wrapper { + position: relative; + user-select: text; + } + + &.selected-node { + .render-wrapper { + border: 1px solid rgb(0 101 255); + background-color: var(--semi-color-info-light-hover); + } + } + } + + .tableWrapper { + ::selection { + background-color: transparent; + } + + &.selected-node { + td, + th { + border-color: rgb(0 101 255); + background-color: var(--semi-color-info-light-hover); + } + } + } + /******* 选中样式 *******/ } .resize-cursor {