mirror of https://github.com/fantasticit/think.git
tiptap: refactor utils
parent
6d0a596496
commit
a066cea4a7
|
@ -18,7 +18,7 @@ import {
|
|||
getIndexdbProvider,
|
||||
destoryIndexdbProvider,
|
||||
} from 'tiptap';
|
||||
import { findMentions } from 'tiptap/utils/find-mention';
|
||||
import { findMentions } from 'tiptap/prose-utils';
|
||||
import { useCollaborationDocument } from 'data/document';
|
||||
import { DataRender } from 'components/data-render';
|
||||
import { Banner } from 'components/banner';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Node, mergeAttributes } from '@tiptap/core';
|
||||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
import { AttachmentWrapper } from '../wrappers/attachment';
|
||||
import { getDatasetAttribute } from '../utils/dataset';
|
||||
import { getDatasetAttribute } from 'tiptap/prose-utils';
|
||||
import { AttachmentWrapper } from 'tiptap/wrappers/attachment';
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
|
|
|
@ -1,28 +1,24 @@
|
|||
import { Blockquote as BuiltInBlockquote } from '@tiptap/extension-blockquote';
|
||||
import { wrappingInputRule } from '@tiptap/core';
|
||||
import { getParents } from '../utils/dom';
|
||||
import { getMarkdownSource } from '../markdown/markdown-to-prosemirror';
|
||||
import { getParents } from 'tiptap/prose-utils';
|
||||
import { getMarkdownSource } from 'tiptap/markdown/markdown-to-prosemirror';
|
||||
|
||||
export const Blockquote = BuiltInBlockquote.extend({
|
||||
addAttributes() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
|
||||
multiline: {
|
||||
default: false,
|
||||
parseHTML: (element) => {
|
||||
const source = getMarkdownSource(element);
|
||||
const parentsIncludeBlockquote = getParents(element).some((p) => p.nodeName.toLowerCase() === 'blockquote');
|
||||
|
||||
return source && !source.startsWith('>') && !parentsIncludeBlockquote;
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
addInputRules() {
|
||||
const multilineInputRegex = /^\s*>>>\s$/gm;
|
||||
|
||||
return [
|
||||
...this.parent?.(),
|
||||
wrappingInputRule({
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
import { BulletList as BuiltInBulletList } from '@tiptap/extension-bullet-list';
|
||||
import { getMarkdownSource } from '../markdown/markdown-to-prosemirror';
|
||||
import { getMarkdownSource } from 'tiptap/markdown/markdown-to-prosemirror';
|
||||
|
||||
export const BulletList = BuiltInBulletList.extend({
|
||||
addAttributes() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
|
||||
bullet: {
|
||||
default: '*',
|
||||
parseHTML(element) {
|
||||
const bullet = getMarkdownSource(element)?.charAt(0);
|
||||
|
||||
return '*+-'.includes(bullet) ? bullet : '*';
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Node, mergeAttributes, wrappingInputRule } from '@tiptap/core';
|
||||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
import { CalloutWrapper } from '../wrappers/callout';
|
||||
import { getDatasetAttribute } from '../utils/dataset';
|
||||
import { CalloutWrapper } from 'tiptap/wrappers/callout';
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
|
@ -73,7 +72,7 @@ export const Callout = Node.create({
|
|||
addInputRules() {
|
||||
return [
|
||||
wrappingInputRule({
|
||||
find: /^:::([\dA-Za-z]*) $/,
|
||||
find: /^:::callout $/,
|
||||
type: this.type,
|
||||
getAttributes: (match) => {
|
||||
return { type: match[1] };
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { lowlight } from 'lowlight/lib/all';
|
||||
import { Node, textblockTypeInputRule, mergeAttributes } from '@tiptap/core';
|
||||
import { Plugin, PluginKey, TextSelection } from 'prosemirror-state';
|
||||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
import { LowlightPlugin } from '../utils/lowlight-plugin';
|
||||
import { CodeBlockWrapper } from '../wrappers/code-block';
|
||||
import { lowlight } from 'lowlight/lib/all';
|
||||
import { LowlightPlugin } from 'tiptap/prose-utils';
|
||||
import { CodeBlockWrapper } from 'tiptap/wrappers/code-block';
|
||||
|
||||
export interface CodeBlockOptions {
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import BuiltInCode from '@tiptap/extension-code';
|
||||
import { EXTENSION_PRIORITY_LOWER } from '../constants';
|
||||
import { EXTENSION_PRIORITY_LOWER } from 'tiptap/constants';
|
||||
|
||||
export const Code = BuiltInCode.extend({
|
||||
excludes: null,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Extension } from '@tiptap/core';
|
||||
import { Plugin } from 'prosemirror-state';
|
||||
import { findColors } from '../utils/color';
|
||||
import { findColors } from 'tiptap/prose-utils';
|
||||
|
||||
export const ColorHighlighter = Extension.create({
|
||||
name: 'colorHighlighter',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Node, mergeAttributes } from '@tiptap/core';
|
||||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
import { CountdownWrapper } from '../wrappers/countdown';
|
||||
import { getDatasetAttribute } from '../utils/dataset';
|
||||
import { CountdownWrapper } from 'tiptap/wrappers/countdown';
|
||||
import { getDatasetAttribute } from 'tiptap/prose-utils';
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Node, mergeAttributes, wrappingInputRule } from '@tiptap/core';
|
||||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
import { DocumentChildrenWrapper } from '../wrappers/document-children';
|
||||
import { getDatasetAttribute } from '../utils/dataset';
|
||||
import { DocumentChildrenWrapper } from 'tiptap/wrappers/document-children';
|
||||
import { getDatasetAttribute } from 'tiptap/prose-utils';
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Node, mergeAttributes, wrappingInputRule } from '@tiptap/core';
|
||||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
import { DocumentReferenceWrapper } from '../wrappers/document-reference';
|
||||
import { getDatasetAttribute } from '../utils/dataset';
|
||||
import { DocumentReferenceWrapper } from 'tiptap/wrappers/document-reference';
|
||||
import { getDatasetAttribute } from 'tiptap/prose-utils';
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { Node, findParentNode } from '@tiptap/core';
|
||||
import { Node } from '@tiptap/core';
|
||||
import { ReactRenderer } from '@tiptap/react';
|
||||
import { Plugin, PluginKey } from 'prosemirror-state';
|
||||
import { Decoration, DecorationSet } from 'prosemirror-view';
|
||||
import Suggestion from '@tiptap/suggestion';
|
||||
import tippy from 'tippy.js';
|
||||
import { EXTENSION_PRIORITY_HIGHEST } from 'tiptap/constants';
|
||||
import { EmojiList } from '../wrappers/emoji-list';
|
||||
import { emojiSearch, emojisToName } from '../wrappers/emoji-list/emojis';
|
||||
import { EmojiList } from 'tiptap/wrappers/emoji-list';
|
||||
import { emojiSearch, emojisToName } from 'tiptap/wrappers/emoji-list/emojis';
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Mark, mergeAttributes, markInputRule } from '@tiptap/core';
|
||||
import { PARSE_HTML_PRIORITY_LOWEST } from '../constants';
|
||||
import { markInputRegex, extractMarkAttributesFromMatch } from '../utils/mark-utils';
|
||||
import { PARSE_HTML_PRIORITY_LOWEST } from 'tiptap/constants';
|
||||
import { markInputRegex, extractMarkAttributesFromMatch } from 'tiptap/prose-utils';
|
||||
|
||||
export const marks = [{ name: 'underline', tag: 'u' }];
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Node, mergeAttributes } from '@tiptap/core';
|
||||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
import { IframeWrapper } from '../wrappers/iframe';
|
||||
import { getDatasetAttribute } from '../utils/dataset';
|
||||
import { IframeWrapper } from 'tiptap/wrappers/iframe';
|
||||
import { getDatasetAttribute } from 'tiptap/prose-utils';
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Image as BuiltInImage } from '@tiptap/extension-image';
|
||||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
import { ImageWrapper } from '../wrappers/image';
|
||||
import { ImageWrapper } from 'tiptap/wrappers/image';
|
||||
|
||||
const resolveImageEl = (element) => (element.nodeName === 'IMG' ? element : element.querySelector('img'));
|
||||
|
||||
|
|
|
@ -1,16 +1,7 @@
|
|||
import { Command, Extension } from '@tiptap/core';
|
||||
import { sinkListItem, liftListItem } from 'prosemirror-schema-list';
|
||||
import { TextSelection, AllSelection, Transaction } from 'prosemirror-state';
|
||||
import { isListActive } from '../utils/is-active';
|
||||
import { clamp } from '../utils/clamp';
|
||||
import { getNodeType } from '../utils/type';
|
||||
import { isListNode } from '../utils/node';
|
||||
|
||||
type IndentOptions = {
|
||||
types: string[];
|
||||
indentLevels: number[];
|
||||
defaultIndentLevel: number;
|
||||
};
|
||||
import { isListActive, getNodeType } from 'tiptap/prose-utils';
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands {
|
||||
|
@ -21,35 +12,12 @@ declare module '@tiptap/core' {
|
|||
}
|
||||
}
|
||||
|
||||
export enum IndentProps {
|
||||
min = 0,
|
||||
max = 210,
|
||||
more = 30,
|
||||
less = -30,
|
||||
}
|
||||
export type Options = {
|
||||
type: 'space' | 'tab';
|
||||
size: number;
|
||||
};
|
||||
|
||||
function setNodeIndentMarkup(tr: Transaction, pos: number, delta: number): Transaction {
|
||||
if (!tr.doc) return tr;
|
||||
|
||||
const node = tr.doc.nodeAt(pos);
|
||||
if (!node) return tr;
|
||||
|
||||
const minIndent = IndentProps.min;
|
||||
const maxIndent = IndentProps.max;
|
||||
|
||||
const indent = clamp((node.attrs.indent || 0) + delta, minIndent, maxIndent);
|
||||
|
||||
if (indent === node.attrs.indent) return tr;
|
||||
|
||||
const nodeAttrs = {
|
||||
...node.attrs,
|
||||
indent,
|
||||
};
|
||||
|
||||
return tr.setNodeMarkup(pos, node.type, nodeAttrs, node.marks);
|
||||
}
|
||||
|
||||
function updateIndentLevel(tr: Transaction, delta: number): Transaction {
|
||||
const updateIndent = (tr: Transaction, options: Options): Transaction => {
|
||||
const { doc, selection } = tr;
|
||||
|
||||
if (!doc || !selection) return tr;
|
||||
|
@ -58,50 +26,23 @@ function updateIndentLevel(tr: Transaction, delta: number): Transaction {
|
|||
return tr;
|
||||
}
|
||||
|
||||
const { from, to } = selection;
|
||||
const { to } = selection;
|
||||
|
||||
doc.nodesBetween(from, to, (node, pos) => {
|
||||
const nodeType = node.type;
|
||||
const text = options.type === 'space' ? Array(options.size).fill(' ').join('') : '\t';
|
||||
|
||||
if (nodeType.name === 'paragraph' || nodeType.name === 'heading') {
|
||||
tr = setNodeIndentMarkup(tr, pos, delta);
|
||||
return false;
|
||||
}
|
||||
if (isListNode(node)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return tr.insertText(text, to);
|
||||
};
|
||||
|
||||
return tr;
|
||||
}
|
||||
|
||||
export const Indent = Extension.create<IndentOptions>({
|
||||
export const Indent = Extension.create<Options>({
|
||||
name: 'indent',
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
types: ['heading', 'paragraph'],
|
||||
indentLevels: [0, 30, 60, 90, 120, 150, 180, 210],
|
||||
defaultIndentLevel: 0,
|
||||
const options: Options = {
|
||||
type: 'space',
|
||||
size: 2,
|
||||
};
|
||||
},
|
||||
|
||||
addGlobalAttributes() {
|
||||
return [
|
||||
{
|
||||
types: this.options.types,
|
||||
attributes: {
|
||||
indent: {
|
||||
default: this.options.defaultIndentLevel,
|
||||
renderHTML: (attributes) => ({
|
||||
style: `margin-left: ${attributes.indent}px!important;`,
|
||||
}),
|
||||
parseHTML: (element) => parseInt(element.style.marginLeft) || this.options.defaultIndentLevel,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
return options;
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
|
@ -115,15 +56,11 @@ export const Indent = Extension.create<IndentOptions>({
|
|||
return sinkListItem(type)(state, dispatch);
|
||||
}
|
||||
|
||||
const { selection } = state;
|
||||
tr = tr.setSelection(selection);
|
||||
tr = updateIndentLevel(tr, IndentProps.more);
|
||||
|
||||
if (tr.docChanged) {
|
||||
dispatch && dispatch(tr);
|
||||
const _tr = updateIndent(tr, this.options);
|
||||
if (_tr.docChanged) {
|
||||
dispatch?.(_tr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
outdent:
|
||||
|
@ -135,15 +72,11 @@ export const Indent = Extension.create<IndentOptions>({
|
|||
return liftListItem(type)(state, dispatch);
|
||||
}
|
||||
|
||||
const { selection } = state;
|
||||
tr = tr.setSelection(selection);
|
||||
tr = updateIndentLevel(tr, IndentProps.less);
|
||||
|
||||
if (tr.docChanged) {
|
||||
dispatch && dispatch(tr);
|
||||
const _tr = updateIndent(tr, this.options);
|
||||
if (_tr.docChanged) {
|
||||
dispatch?.(_tr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Node, mergeAttributes, nodeInputRule } from '@tiptap/core';
|
||||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
import { KatexWrapper } from '../wrappers/katex';
|
||||
import { KatexWrapper } from 'tiptap/wrappers/katex';
|
||||
|
||||
type IKatexAttrs = {
|
||||
text?: string;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Node } from '@tiptap/core';
|
||||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
import { LoadingWrapper } from '../wrappers/loading';
|
||||
import { LoadingWrapper } from 'tiptap/wrappers/loading';
|
||||
|
||||
export const Loading = Node.create({
|
||||
name: 'loading',
|
||||
|
|
|
@ -2,8 +2,8 @@ import BulitInMention from '@tiptap/extension-mention';
|
|||
import { ReactRenderer } from '@tiptap/react';
|
||||
import tippy from 'tippy.js';
|
||||
import { getUsers } from 'services/user';
|
||||
import { getDatasetAttribute } from '../utils/dataset';
|
||||
import { MentionList } from '../wrappers/mention-list';
|
||||
import { getDatasetAttribute } from 'tiptap/prose-utils';
|
||||
import { MentionList } from 'tiptap/wrappers/mention-list';
|
||||
|
||||
const suggestion = {
|
||||
items: async ({ query }) => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Node, mergeAttributes, nodeInputRule } from '@tiptap/core';
|
||||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
import { MindWrapper } from '../wrappers/mind';
|
||||
import { getDatasetAttribute } from '../utils/dataset';
|
||||
import { MindWrapper } from 'tiptap/wrappers/mind';
|
||||
import { getDatasetAttribute } from 'tiptap/prose-utils';
|
||||
|
||||
const DEFAULT_MIND_DATA = {
|
||||
root: { data: { text: '中心节点' }, children: [] },
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { OrderedList as BuiltInOrderedList } from '@tiptap/extension-ordered-list';
|
||||
import { getMarkdownSource } from '../markdown/markdown-to-prosemirror';
|
||||
import { getMarkdownSource } from 'tiptap/markdown/markdown-to-prosemirror';
|
||||
|
||||
export const OrderedList = BuiltInOrderedList.extend({
|
||||
addAttributes() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
|
||||
parens: {
|
||||
default: false,
|
||||
parseHTML: (element) => /^[0-9]+\)/.test(getMarkdownSource(element)),
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
import { Extension } from '@tiptap/core';
|
||||
import { Plugin, PluginKey } from 'prosemirror-state';
|
||||
import { EXTENSION_PRIORITY_HIGHEST } from '../constants';
|
||||
import { handleFileEvent } from '../utils/upload';
|
||||
import { isInCode, LANGUAGES } from '../utils/code';
|
||||
import { EXTENSION_PRIORITY_HIGHEST } from 'tiptap/constants';
|
||||
import { handleFileEvent, isInCode, LANGUAGES, isTitleNode } from 'tiptap/prose-utils';
|
||||
import {
|
||||
isMarkdown,
|
||||
normalizePastedMarkdown,
|
||||
markdownToProsemirror,
|
||||
prosemirrorToMarkdown,
|
||||
} from '../markdown/markdown-to-prosemirror';
|
||||
import { isTitleNode } from '../utils/node';
|
||||
} from 'tiptap/markdown/markdown-to-prosemirror';
|
||||
|
||||
const isPureText = (content): boolean => {
|
||||
if (!content) return false;
|
||||
|
||||
if (Array.isArray(content)) {
|
||||
if (content.length > 1) return false;
|
||||
return isPureText(content[0]);
|
||||
}
|
||||
|
||||
const child = content['content'];
|
||||
if (child) {
|
||||
return isPureText(child);
|
||||
}
|
||||
|
||||
return content['type'] === 'text';
|
||||
};
|
||||
|
||||
export const Paste = Extension.create({
|
||||
name: 'paste',
|
||||
|
@ -25,6 +39,7 @@ export const Paste = Extension.create({
|
|||
if (view.props.editable && !view.props.editable(view.state)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!event.clipboardData) return false;
|
||||
|
||||
const files = Array.from(event.clipboardData.files);
|
||||
|
@ -116,16 +131,18 @@ export const Paste = Extension.create({
|
|||
return false;
|
||||
},
|
||||
clipboardTextSerializer: (slice) => {
|
||||
const doc = slice.content;
|
||||
const isText = isPureText(slice.content.toJSON());
|
||||
if (isText) {
|
||||
return slice.content.textBetween(0, slice.content.size, '\n\n');
|
||||
}
|
||||
|
||||
const doc = slice.content;
|
||||
if (!doc) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const content = prosemirrorToMarkdown({
|
||||
content: doc,
|
||||
});
|
||||
|
||||
return content;
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { Node, findParentNode } from '@tiptap/core';
|
||||
import tippy from 'tippy.js';
|
||||
import { Node } from '@tiptap/core';
|
||||
import { ReactRenderer } from '@tiptap/react';
|
||||
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 '../wrappers/menu-list';
|
||||
import { QUICK_INSERT_ITEMS } from '../menus/quick-insert';
|
||||
import { MenuList } from 'tiptap/wrappers/menu-list';
|
||||
import { QUICK_INSERT_ITEMS } from 'tiptap/menus/quick-insert';
|
||||
import { EXTENSION_PRIORITY_HIGHEST } from 'tiptap/constants';
|
||||
|
||||
export const QuickInsertPluginKey = new PluginKey('quickInsert');
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Extension } from '@tiptap/core';
|
||||
import { Plugin, PluginKey, NodeSelection, TextSelection, Selection, AllSelection } from 'prosemirror-state';
|
||||
import { Decoration, DecorationSet } from 'prosemirror-view';
|
||||
import { getCurrentNode, isInCodeBlock, isInCallout } from '../utils/node';
|
||||
import { EXTENSION_PRIORITY_HIGHEST } from '../constants';
|
||||
import { getCurrentNode, isInCodeBlock, isInCallout } from 'tiptap/prose-utils';
|
||||
import { EXTENSION_PRIORITY_HIGHEST } from 'tiptap/constants';
|
||||
|
||||
export const selectionPluginKey = new PluginKey('selection');
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Node, mergeAttributes } from '@tiptap/core';
|
||||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
import { StatusWrapper } from '../wrappers/status';
|
||||
import { getDatasetAttribute } from '../utils/dataset';
|
||||
import { StatusWrapper } from 'tiptap/wrappers/status';
|
||||
import { getDatasetAttribute } from 'tiptap/prose-utils';
|
||||
|
||||
type IStatusAttrs = {
|
||||
color?: string;
|
||||
|
|
|
@ -13,8 +13,8 @@ import {
|
|||
isTableSelected,
|
||||
selectRow,
|
||||
selectTable,
|
||||
} from '../utils/table';
|
||||
import { FloatMenuView } from '../views/float-menu';
|
||||
} from 'tiptap/prose-utils';
|
||||
import { FloatMenuView } from 'tiptap/views/float-menu';
|
||||
|
||||
export const TableCell = BuiltInTableCell.extend({
|
||||
addAttributes() {
|
||||
|
|
|
@ -6,7 +6,7 @@ import { IconDelete, IconPlus } from '@douyinfe/semi-icons';
|
|||
import { Tooltip } from 'components/tooltip';
|
||||
import { Plugin, PluginKey } from 'prosemirror-state';
|
||||
import { Decoration, DecorationSet } from 'prosemirror-view';
|
||||
import { getCellsInRow, isColumnSelected, isTableSelected, selectColumn } from '../utils/table';
|
||||
import { getCellsInRow, isColumnSelected, isTableSelected, selectColumn } from 'tiptap/prose-utils';
|
||||
import { FloatMenuView } from '../views/float-menu';
|
||||
|
||||
export const TableHeader = BuiltInTableHeader.extend({
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import { wrappingInputRule } from '@tiptap/core';
|
||||
import { ReactNodeViewRenderer } from '@tiptap/react';
|
||||
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 '../wrappers/task-item';
|
||||
import { PARSE_HTML_PRIORITY_HIGHEST } from 'tiptap/constants';
|
||||
|
||||
const CustomTaskItem = BuiltInTaskItem.extend({
|
||||
parseHTML() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { TaskList as BuiltInTaskList } from '@tiptap/extension-task-list';
|
||||
import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants';
|
||||
import { PARSE_HTML_PRIORITY_HIGHEST } from 'tiptap/constants';
|
||||
|
||||
export const TaskList = BuiltInTaskList.extend({
|
||||
parseHTML() {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Node, mergeAttributes } from '@tiptap/core';
|
||||
import { Plugin, PluginKey } from 'prosemirror-state';
|
||||
import { isInTitle } from '../utils/node';
|
||||
import { TextSelection } from 'prosemirror-state';
|
||||
import { Plugin, PluginKey, TextSelection } from 'prosemirror-state';
|
||||
import { isInTitle } from 'tiptap/prose-utils';
|
||||
|
||||
export interface TitleOptions {
|
||||
HTMLAttributes: Record<string, any>;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { HocuspocusProvider } from '@hocuspocus/provider';
|
||||
import { Collaboration } from './collaboration';
|
||||
import { CollaborationCursor } from './collaboration-cursor';
|
||||
import { Collaboration } from './extensions/collaboration';
|
||||
import { CollaborationCursor } from './extensions/collaboration-cursor';
|
||||
import History from '@tiptap/extension-history';
|
||||
import { getRandomColor } from 'helpers/color';
|
||||
import { Document } from './extensions/document';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import container from 'markdown-it-container';
|
||||
import { strToJSON, jsonToDOMDataset } from '../../../utils/dataset';
|
||||
import { strToJSON, jsonToDOMDataset } from 'tiptap/prose-utils';
|
||||
|
||||
export const createMarkdownContainer = (types: string | Array<string>) => (md) => {
|
||||
if (!Array.isArray(types)) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { jsonToStr } from '../../utils/dataset';
|
||||
import { jsonToStr } from 'tiptap/prose-utils';
|
||||
|
||||
const uniq = (arr: string[]) => [...new Set(arr)];
|
||||
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
import { MarkdownSerializer as ProseMirrorMarkdownSerializer, defaultMarkdownSerializer } from 'prosemirror-markdown';
|
||||
import { Attachment } from '../../extensions/attachment';
|
||||
import { Bold } from '../../extensions/bold';
|
||||
import { BulletList } from '../../extensions/bullet-list';
|
||||
import { Callout } from '../../extensions/callout';
|
||||
import { Code } from '../../extensions/code';
|
||||
import { CodeBlock } from '../../extensions/code-block';
|
||||
import { Countdown } from '../../extensions/countdown';
|
||||
import { DocumentChildren } from '../../extensions/document-children';
|
||||
import { DocumentReference } from '../../extensions/document-reference';
|
||||
import { HardBreak } from '../../extensions/hard-break';
|
||||
import { Heading } from '../../extensions/heading';
|
||||
import { HorizontalRule } from '../../extensions/horizontal-rule';
|
||||
import { marks } from '../../extensions/html-marks';
|
||||
import { Mention } from '../../extensions/mention';
|
||||
import { Iframe } from '../../extensions/iframe';
|
||||
import { Image } from '../../extensions/image';
|
||||
import { Italic } from '../../extensions/italic';
|
||||
import { Katex } from '../../extensions/katex';
|
||||
import { Link } from '../../extensions/link';
|
||||
import { ListItem } from '../../extensions/listItem';
|
||||
import { Mind } from '../../extensions/mind';
|
||||
import { OrderedList } from '../../extensions/ordered-list';
|
||||
import { Paragraph } from '../../extensions/paragraph';
|
||||
import { Status } from '../../extensions/status';
|
||||
import { Strike } from '../../extensions/strike';
|
||||
import { Subscript } from '../../extensions/subscript';
|
||||
import { Superscript } from '../../extensions/superscript';
|
||||
import { Table } from '../../extensions/table';
|
||||
import { TableCell } from '../../extensions/table-cell';
|
||||
import { TableHeader } from '../../extensions/table-header';
|
||||
import { TableRow } from '../../extensions/table-row';
|
||||
import { Text } from '../../extensions/text';
|
||||
import { TaskItem } from '../../extensions/task-item';
|
||||
import { TaskList } from '../../extensions/task-list';
|
||||
import { TextStyle } from '../../extensions/text-style';
|
||||
import { Title } from '../../extensions/title';
|
||||
import { Attachment } from 'tiptap/extensions/attachment';
|
||||
import { Bold } from 'tiptap/extensions/bold';
|
||||
import { BulletList } from 'tiptap/extensions/bullet-list';
|
||||
import { Callout } from 'tiptap/extensions/callout';
|
||||
import { Code } from 'tiptap/extensions/code';
|
||||
import { CodeBlock } from 'tiptap/extensions/code-block';
|
||||
import { Countdown } from 'tiptap/extensions/countdown';
|
||||
import { DocumentChildren } from 'tiptap/extensions/document-children';
|
||||
import { DocumentReference } from 'tiptap/extensions/document-reference';
|
||||
import { HardBreak } from 'tiptap/extensions/hard-break';
|
||||
import { Heading } from 'tiptap/extensions/heading';
|
||||
import { HorizontalRule } from 'tiptap/extensions/horizontal-rule';
|
||||
import { marks } from 'tiptap/extensions/html-marks';
|
||||
import { Mention } from 'tiptap/extensions/mention';
|
||||
import { Iframe } from 'tiptap/extensions/iframe';
|
||||
import { Image } from 'tiptap/extensions/image';
|
||||
import { Italic } from 'tiptap/extensions/italic';
|
||||
import { Katex } from 'tiptap/extensions/katex';
|
||||
import { Link } from 'tiptap/extensions/link';
|
||||
import { ListItem } from 'tiptap/extensions/listItem';
|
||||
import { Mind } from 'tiptap/extensions/mind';
|
||||
import { OrderedList } from 'tiptap/extensions/ordered-list';
|
||||
import { Paragraph } from 'tiptap/extensions/paragraph';
|
||||
import { Status } from 'tiptap/extensions/status';
|
||||
import { Strike } from 'tiptap/extensions/strike';
|
||||
import { Subscript } from 'tiptap/extensions/subscript';
|
||||
import { Superscript } from 'tiptap/extensions/superscript';
|
||||
import { Table } from 'tiptap/extensions/table';
|
||||
import { TableCell } from 'tiptap/extensions/table-cell';
|
||||
import { TableHeader } from 'tiptap/extensions/table-header';
|
||||
import { TableRow } from 'tiptap/extensions/table-row';
|
||||
import { Text } from 'tiptap/extensions/text';
|
||||
import { TaskItem } from 'tiptap/extensions/task-item';
|
||||
import { TaskList } from 'tiptap/extensions/task-list';
|
||||
import { TextStyle } from 'tiptap/extensions/text-style';
|
||||
import { Title } from 'tiptap/extensions/title';
|
||||
import {
|
||||
isPlainURL,
|
||||
renderHardBreak,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { useCallback, useRef } from 'react';
|
||||
import { Button, Form, Dropdown } from '@douyinfe/semi-ui';
|
||||
import { FormApi } from '@douyinfe/semi-ui/lib/es/form';
|
||||
import { number } from 'lib0';
|
||||
|
||||
type ISize = { width: number; height: number };
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { Editor } from '@tiptap/core';
|
||||
import { Button, Dropdown, Tooltip } from '@douyinfe/semi-ui';
|
||||
import { IconAlignLeft, IconAlignCenter, IconAlignRight, IconAlignJustify } from '@douyinfe/semi-icons';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Align: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
const current = (() => {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Editor } from '@tiptap/core';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { IconMark } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
import { ColorPicker } from '../_components/color-picker';
|
||||
|
||||
export const BackgroundColor: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Editor } from '@tiptap/core';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { IconQuote } from 'components/icons';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Blockquote: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Editor } from '@tiptap/core';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { IconBold } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Bold: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Editor } from '@tiptap/core';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { IconList } from 'components/icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const BulletList: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { useCallback } from 'react';
|
||||
import { Editor } from '@tiptap/core';
|
||||
import { Space, Button, Popover, Typography } from '@douyinfe/semi-ui';
|
||||
import { IconDelete } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { IconDrawBoard } from 'components/icons';
|
||||
import { BubbleMenu } from '../../views/bubble-menu';
|
||||
import { Divider } from '../../divider';
|
||||
import { Callout } from '../../extensions/callout';
|
||||
import { deleteNode } from '../../utils/delete-node';
|
||||
import { BubbleMenu } from 'tiptap/views/bubble-menu';
|
||||
import { Divider } from 'tiptap/divider';
|
||||
import { Callout } from 'tiptap/extensions/callout';
|
||||
import { deleteNode } from 'tiptap/prose-utils';
|
||||
import styles from './bubble.module.scss';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Editor } from '@tiptap/core';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { IconCode } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Code: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
|
|
|
@ -2,9 +2,9 @@ import { useCallback } from 'react';
|
|||
import { Space, Button } from '@douyinfe/semi-ui';
|
||||
import { IconEdit, IconDelete } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { BubbleMenu } from '../../views/bubble-menu';
|
||||
import { Countdown } from '../../extensions/countdown';
|
||||
import { Divider } from '../../divider';
|
||||
import { BubbleMenu } from 'tiptap/views/bubble-menu';
|
||||
import { Countdown } from 'tiptap/extensions/countdown';
|
||||
import { Divider } from 'tiptap/divider';
|
||||
import { triggerOpenCountSettingModal } from '../_event';
|
||||
|
||||
export const CountdownBubbleMenu = ({ editor }) => {
|
||||
|
|
|
@ -2,8 +2,8 @@ import { useCallback } from 'react';
|
|||
import { Space, Button } from '@douyinfe/semi-ui';
|
||||
import { IconDelete } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { BubbleMenu } from '../../views/bubble-menu';
|
||||
import { DocumentChildren } from '../../extensions/document-children';
|
||||
import { BubbleMenu } from 'tiptap/views/bubble-menu';
|
||||
import { DocumentChildren } from 'tiptap/extensions/document-children';
|
||||
|
||||
export const DocumentChildrenBubbleMenu = ({ editor }) => {
|
||||
const deleteNode = useCallback(() => editor.chain().deleteSelection().run(), [editor]);
|
||||
|
|
|
@ -6,9 +6,9 @@ import { Tooltip } from 'components/tooltip';
|
|||
import { DataRender } from 'components/data-render';
|
||||
import { IconDocument } from 'components/icons';
|
||||
import { useWikiTocs } from 'data/wiki';
|
||||
import { BubbleMenu } from '../../views/bubble-menu';
|
||||
import { DocumentReference } from '../../extensions/document-reference';
|
||||
import { Divider } from '../../divider';
|
||||
import { BubbleMenu } from 'tiptap/views/bubble-menu';
|
||||
import { DocumentReference } from 'tiptap/extensions/document-reference';
|
||||
import { Divider } from 'tiptap/divider';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { Select } from '@douyinfe/semi-ui';
|
||||
import { Editor } from '@tiptap/core';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const FONT_SIZES = [12, 13, 14, 15, 16, 19, 22, 24, 29, 32, 40, 48];
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { Editor } from '@tiptap/core';
|
||||
import { Select } from '@douyinfe/semi-ui';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
const getCurrentCaretTitle = (editor) => {
|
||||
if (editor.isActive('heading', { level: 1 })) return 1;
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Editor } from '@tiptap/core';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { IconHorizontalRule } from 'components/icons';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const HorizontalRule: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Editor } from '@tiptap/core';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { IconIndentLeft, IconIndentRight } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Ident: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
|
|
|
@ -4,9 +4,9 @@ import { FormApi } from '@douyinfe/semi-ui/lib/es/form';
|
|||
import { IconEdit, IconExternalOpen, IconLineHeight, IconDelete } from '@douyinfe/semi-icons';
|
||||
import { useToggle } from 'hooks/use-toggle';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { BubbleMenu } from '../../views/bubble-menu';
|
||||
import { Iframe } from '../../extensions/iframe';
|
||||
import { Divider } from '../../divider';
|
||||
import { BubbleMenu } from 'tiptap/views/bubble-menu';
|
||||
import { Iframe } from 'tiptap/extensions/iframe';
|
||||
import { Divider } from 'tiptap/divider';
|
||||
import { Size } from '../_components/size';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
|
|
@ -2,11 +2,11 @@ import React, { useEffect, useState } from 'react';
|
|||
import { Space, Button } from '@douyinfe/semi-ui';
|
||||
import { IconAlignLeft, IconAlignCenter, IconAlignRight, IconLineHeight, IconDelete } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { BubbleMenu } from '../../views/bubble-menu';
|
||||
import { Divider } from '../../divider';
|
||||
import { Image } from '../../extensions/image';
|
||||
import { BubbleMenu } from 'tiptap/views/bubble-menu';
|
||||
import { Divider } from 'tiptap/divider';
|
||||
import { Image } from 'tiptap/extensions/image';
|
||||
import { getEditorContainerDOMSize } from 'tiptap/prose-utils';
|
||||
import { Size } from '../_components/size';
|
||||
import { getEditorContainerDOMSize } from '../../utils/editor';
|
||||
|
||||
export const ImageBubbleMenu = ({ editor }) => {
|
||||
const attrs = editor.getAttributes(Image.name);
|
||||
|
|
|
@ -20,8 +20,7 @@ import { GridSelect } from 'components/grid-select';
|
|||
import { useToggle } from 'hooks/use-toggle';
|
||||
import { useUser } from 'data/user';
|
||||
import { createKeysLocalStorageLRUCache } from 'helpers/lru-cache';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { getEditorContainerDOMSize } from '../../utils/editor';
|
||||
import { isTitleActive, getEditorContainerDOMSize } from 'tiptap/prose-utils';
|
||||
import { createCountdown } from '../countdown/service';
|
||||
|
||||
const insertMenuLRUCache = createKeysLocalStorageLRUCache('TIPTAP_INSERT_MENU', 3);
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Editor } from '@tiptap/core';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { IconItalic } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Italic: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
|
|
|
@ -2,11 +2,10 @@ import { useEffect, useState, useRef, useCallback } from 'react';
|
|||
import { Space, Button } from '@douyinfe/semi-ui';
|
||||
import { IconExternalOpen, IconUnlink, IconEdit } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { Divider } from '../../divider';
|
||||
import { BubbleMenu } from '../../views/bubble-menu';
|
||||
import { Link } from '../../extensions/link';
|
||||
import { isMarkActive } from '../../utils/is-active';
|
||||
import { findMarkPosition } from '../../utils/find-position';
|
||||
import { Divider } from 'tiptap/divider';
|
||||
import { BubbleMenu } from 'tiptap/views/bubble-menu';
|
||||
import { Link } from 'tiptap/extensions/link';
|
||||
import { isMarkActive, findMarkPosition } from 'tiptap/prose-utils';
|
||||
import { triggerOpenLinkSettingModal } from '../_event';
|
||||
|
||||
export const LinkBubbleMenu = ({ editor }) => {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Editor } from '@tiptap/core';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { IconLink } from 'components/icons';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
import { createOrToggleLink } from './service';
|
||||
import { LinkBubbleMenu } from './bubble';
|
||||
import { LinkSettingModal } from './modal';
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Form, Modal } from '@douyinfe/semi-ui';
|
|||
import { FormApi } from '@douyinfe/semi-ui/lib/es/form';
|
||||
import { Editor } from '@tiptap/core';
|
||||
import { useToggle } from 'hooks/use-toggle';
|
||||
import { isValidURL } from '../../utils/valid-url';
|
||||
import { isValidURL } from 'tiptap/prose-utils';
|
||||
import { event, OPEN_LINK_SETTING_MODAL } from '../_event';
|
||||
|
||||
type IProps = { editor: Editor };
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Editor } from '@tiptap/core';
|
||||
import { isMarkActive } from '../../utils/is-active';
|
||||
import { isMarkActive } from 'tiptap/prose-utils';
|
||||
import { triggerOpenLinkSettingModal } from '../_event';
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,11 +2,11 @@ import { useCallback } from 'react';
|
|||
import { Space, Button } from '@douyinfe/semi-ui';
|
||||
import { IconLineHeight, IconDelete } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { BubbleMenu } from '../../views/bubble-menu';
|
||||
import { Mind } from '../../extensions/mind';
|
||||
import { BubbleMenu } from 'tiptap/views/bubble-menu';
|
||||
import { Mind } from 'tiptap/extensions/mind';
|
||||
import { Divider } from 'tiptap/divider';
|
||||
import { getEditorContainerDOMSize } from 'tiptap/prose-utils';
|
||||
import { Size } from '../_components/size';
|
||||
import { Divider } from '../../divider';
|
||||
import { getEditorContainerDOMSize } from '../../utils/editor';
|
||||
|
||||
export const MindBubbleMenu = ({ editor }) => {
|
||||
const attrs = editor.getAttributes(Mind.name);
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Editor } from '@tiptap/core';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { IconOrderedList } from 'components/icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const OrderedList: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Popover, Button, Typography, Input, Space } from '@douyinfe/semi-ui';
|
|||
import { Editor } from '@tiptap/core';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { IconSearchReplace } from 'components/icons';
|
||||
import { SearchNReplace } from '../../extensions/search';
|
||||
import { SearchNReplace } from 'tiptap/extensions/search';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Editor } from '@tiptap/core';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { IconStrikeThrough } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Strike: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { IconSub } from 'components/icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Subscript: React.FC<{ editor: any }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { IconSup } from 'components/icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Superscript: React.FC<{ editor: any }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
|
|
|
@ -14,9 +14,9 @@ import {
|
|||
IconTableHeaderCell,
|
||||
} from 'components/icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { Divider } from '../../divider';
|
||||
import { BubbleMenu } from '../../views/bubble-menu';
|
||||
import { Table } from '../../extensions/table';
|
||||
import { Divider } from 'tiptap/divider';
|
||||
import { BubbleMenu } from 'tiptap/views/bubble-menu';
|
||||
import { Table } from 'tiptap/extensions/table';
|
||||
|
||||
export const TableBubbleMenu = ({ editor }) => {
|
||||
return (
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Editor } from '@tiptap/core';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { IconTask } from 'components/icons';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const TaskList: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Editor } from '@tiptap/core';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { IconFont } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
import { ColorPicker } from '../_components/color-picker';
|
||||
|
||||
export const TextColor: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Editor } from '@tiptap/core';
|
|||
import { Button } from '@douyinfe/semi-ui';
|
||||
import { IconUnderline } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { isTitleActive } from '../../utils/is-active';
|
||||
import { isTitleActive } from 'tiptap/prose-utils';
|
||||
|
||||
export const Underline: React.FC<{ editor: Editor }> = ({ editor }) => {
|
||||
if (!editor) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { EditorState } from 'prosemirror-state';
|
||||
// @ts-ignore
|
||||
import { lowlight } from 'lowlight';
|
||||
import { isMarkActive } from './is-active';
|
||||
import { isMarkActive } from './active';
|
||||
|
||||
export const LANGUAGES = lowlight.listLanguages().reduce((a, language) => {
|
||||
a[language] = language;
|
|
@ -53,6 +53,23 @@ export const normalizeFileType = (fileType): FileType => {
|
|||
return 'file';
|
||||
};
|
||||
|
||||
export const readImageAsBase64 = (file: File): Promise<{ alt: string; src: string }> => {
|
||||
return new Promise((resolve) => {
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener(
|
||||
'load',
|
||||
() => {
|
||||
resolve({
|
||||
alt: file.name,
|
||||
src: reader.result as string,
|
||||
});
|
||||
},
|
||||
false
|
||||
);
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
};
|
||||
|
||||
export const getImageWidthHeight = (url: string): Promise<{ width: number | string; height: number | string }> => {
|
||||
return new Promise((resolve) => {
|
||||
const img = document.createElement('img');
|
|
@ -0,0 +1,20 @@
|
|||
export * from './active';
|
||||
export * from './clamp';
|
||||
export * from './code';
|
||||
export * from './color';
|
||||
export * from './delete-node';
|
||||
export * from './dom-dataset';
|
||||
export * from './dom';
|
||||
export * from './download';
|
||||
export * from './editor-container-size';
|
||||
export * from './file';
|
||||
export * from './lowlight-plugin';
|
||||
export * from './mark';
|
||||
export * from './mention';
|
||||
export * from './node';
|
||||
export * from './position';
|
||||
export * from './table';
|
||||
export * from './text';
|
||||
export * from './type';
|
||||
export * from './upload';
|
||||
export * from './url';
|
|
@ -1,5 +1,22 @@
|
|||
import { EditorState } from 'prosemirror-state';
|
||||
|
||||
export const markInputRegex = (tag) => new RegExp(`(<(${tag})((?: \\w+=".+?")+)?>([^<]+)</${tag}>)$`, 'gm');
|
||||
|
||||
export const extractMarkAttributesFromMatch = ([, , , attrsString]) => {
|
||||
const attrRegex = /(\w+)="(.+?)"/g;
|
||||
const attrs = {};
|
||||
|
||||
let key;
|
||||
let value;
|
||||
|
||||
do {
|
||||
[, key, value] = attrRegex.exec(attrsString) || [];
|
||||
if (key) attrs[key] = value;
|
||||
} while (key);
|
||||
|
||||
return attrs;
|
||||
};
|
||||
|
||||
export function findMarkPosition(state: EditorState, mark, from, to) {
|
||||
let markPos = { start: -1, end: -1 };
|
||||
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
@ -0,0 +1,61 @@
|
|||
/* Copyright 2021, Milkdown by Mirone. */
|
||||
import type { EditorView } from 'prosemirror-view';
|
||||
|
||||
type Point = [top: number, left: number];
|
||||
|
||||
export const calculateNodePosition = (
|
||||
view: EditorView,
|
||||
target: HTMLElement,
|
||||
handler: (selectedRect: DOMRect, targetRect: DOMRect, parentRect: DOMRect) => Point
|
||||
) => {
|
||||
const state = view.state;
|
||||
const { from } = state.selection;
|
||||
|
||||
const { node } = view.domAtPos(from);
|
||||
const element = node instanceof Text ? node.parentElement : node;
|
||||
if (!(element instanceof HTMLElement)) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const selectedNodeRect = element.getBoundingClientRect();
|
||||
const targetNodeRect = target.getBoundingClientRect();
|
||||
const parentNodeRect = target.parentElement?.getBoundingClientRect();
|
||||
if (!parentNodeRect) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const [top, left] = handler(selectedNodeRect, targetNodeRect, parentNodeRect);
|
||||
|
||||
target.style.top = top + 'px';
|
||||
target.style.left = left + 'px';
|
||||
};
|
||||
|
||||
type Rect = {
|
||||
left: number;
|
||||
right: number;
|
||||
top: number;
|
||||
bottom: number;
|
||||
};
|
||||
|
||||
export const calculateTextPosition = (
|
||||
view: EditorView,
|
||||
target: HTMLElement,
|
||||
handler: (start: Rect, end: Rect, targetRect: DOMRect, parentRect: DOMRect) => Point
|
||||
) => {
|
||||
const state = view.state;
|
||||
const { from, to } = state.selection;
|
||||
const start = view.coordsAtPos(from);
|
||||
const end = view.coordsAtPos(to);
|
||||
|
||||
const targetNodeRect = target.getBoundingClientRect();
|
||||
const parent = target.parentElement;
|
||||
if (!parent) {
|
||||
throw new Error();
|
||||
}
|
||||
const parentNodeRect = parent.getBoundingClientRect();
|
||||
|
||||
const [top, left] = handler(start, end, targetNodeRect, parentNodeRect);
|
||||
|
||||
target.style.top = top + 'px';
|
||||
target.style.left = left + 'px';
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
type R = Record<string, unknown>;
|
||||
|
||||
export const isPureText = (content: R | R[] | undefined | null): boolean => {
|
||||
if (!content) return false;
|
||||
if (Array.isArray(content)) {
|
||||
if (content.length > 1) return false;
|
||||
return isPureText(content[0]);
|
||||
}
|
||||
|
||||
const child = content['content'];
|
||||
if (child) {
|
||||
return isPureText(child as R[]);
|
||||
}
|
||||
|
||||
return content['type'] === 'text';
|
||||
};
|
|
@ -1,21 +0,0 @@
|
|||
import { InputRule, wrappingInputRule } from '@tiptap/core';
|
||||
|
||||
/**
|
||||
* Wrapping input handler that will append the content of the last match
|
||||
*
|
||||
* @param {RegExp} find find param for the wrapping input rule
|
||||
* @param {object} type Node Type object
|
||||
* @param {*} getAttributes handler to get the attributes
|
||||
*/
|
||||
export function listInputRule(find, type, getAttributes = null) {
|
||||
const handler = ({ state, range, match }) => {
|
||||
const wrap = wrappingInputRule({ find, type, getAttributes });
|
||||
// @ts-ignore
|
||||
wrap.handler({ state, range, match });
|
||||
// Insert the first character after bullet if there is one
|
||||
if (match.length >= 3) {
|
||||
state.tr.insertText(match[2]);
|
||||
}
|
||||
};
|
||||
return new InputRule({ find, handler });
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
export const markInputRegex = (tag) => new RegExp(`(<(${tag})((?: \\w+=".+?")+)?>([^<]+)</${tag}>)$`, 'gm');
|
||||
|
||||
export const extractMarkAttributesFromMatch = ([, , , attrsString]) => {
|
||||
const attrRegex = /(\w+)="(.+?)"/g;
|
||||
const attrs = {};
|
||||
|
||||
let key;
|
||||
let value;
|
||||
|
||||
do {
|
||||
[, key, value] = attrRegex.exec(attrsString) || [];
|
||||
if (key) attrs[key] = value;
|
||||
} while (key);
|
||||
|
||||
return attrs;
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
import { IconFile, IconSong, IconVideo, IconImage } from '@douyinfe/semi-icons';
|
||||
import { normalizeFileType } from '../../utils/file';
|
||||
import { normalizeFileType } from 'tiptap/prose-utils';
|
||||
|
||||
export const getFileTypeIcon = (fileType: string) => {
|
||||
const type = normalizeFileType(fileType);
|
||||
|
|
|
@ -5,9 +5,8 @@ import { Button, Typography, Spin, Collapsible, Space } from '@douyinfe/semi-ui'
|
|||
import { IconDownload, IconPlayCircle, IconClose } from '@douyinfe/semi-icons';
|
||||
import { Tooltip } from 'components/tooltip';
|
||||
import { useToggle } from 'hooks/use-toggle';
|
||||
import { download } from '../../utils/download';
|
||||
import { download, normalizeFileSize, extractFileExtension, extractFilename } from 'tiptap/prose-utils';
|
||||
import { uploadFile } from 'services/file';
|
||||
import { normalizeFileSize, extractFileExtension, extractFilename } from '../../utils/file';
|
||||
import { Player } from './player';
|
||||
import { getFileTypeIcon } from './file-icon';
|
||||
import styles from './index.module.scss';
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
extractFilename,
|
||||
normalizeFileType,
|
||||
FileType,
|
||||
} from '../../../utils/file';
|
||||
} from 'tiptap/prose-utils';
|
||||
import { PDFPlayer } from './pdf-player';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { useMemo } from 'react';
|
||||
import { NodeViewWrapper } from '@tiptap/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import Link from 'next/link';
|
||||
import cls from 'classnames';
|
||||
import { IconDocument } from 'components/icons';
|
||||
import styles from './index.module.scss';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const DocumentReferenceWrapper = ({ editor, node, updateAttributes }) => {
|
||||
const { pathname } = useRouter();
|
||||
|
|
|
@ -3,7 +3,7 @@ import cls from 'classnames';
|
|||
import { NodeViewWrapper } from '@tiptap/react';
|
||||
import { Typography } from '@douyinfe/semi-ui';
|
||||
import { Resizeable } from 'components/resizeable';
|
||||
import { getEditorContainerDOMSize } from '../../utils/editor';
|
||||
import { getEditorContainerDOMSize } from 'tiptap/prose-utils';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue