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