pull/115/head
fantasticit 2022-07-02 10:26:41 +08:00
parent b068b4ef06
commit 27d4261c1d
9 changed files with 66 additions and 42 deletions

View File

@ -43,15 +43,11 @@ export const Callout = Node.create({
}, },
parseHTML() { parseHTML() {
return [ return [{ tag: 'div[class=callout]' }];
{
tag: 'div',
},
];
}, },
renderHTML({ HTMLAttributes }) { renderHTML({ HTMLAttributes }) {
return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]; return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
}, },
addCommands() { addCommands() {

View File

@ -63,41 +63,41 @@ export const Clipboard = Extension.create<IClipboardOptions>({
return false; return false;
}, },
clipboardTextSerializer: (slice) => { // clipboardTextSerializer: (slice) => {
const json = slice.content.toJSON(); // const json = slice.content.toJSON();
const isSelectAll = slice.openStart === slice.openEnd && slice.openEnd === 0; // const isSelectAll = slice.openStart === slice.openEnd && slice.openEnd === 0;
if (Array.isArray(json) && !isSelectAll) { // if (Array.isArray(json) && !isSelectAll) {
const type = json[0].type; // const type = json[0].type;
// 列表项返回文字内容 // // 列表项返回文字内容
if (['bulletList', 'orderedList', 'taskList'].includes(type)) { // if (['bulletList', 'orderedList', 'taskList'].includes(type)) {
return slice.content.textBetween(0, slice.content.size, '\n\n'); // return slice.content.textBetween(0, slice.content.size, '\n\n');
} // }
} // }
if (typeof json === 'object' || isSelectAll) { // if (typeof json === 'object' || isSelectAll) {
return extensionThis.options.prosemirrorToMarkdown({ // return extensionThis.options.prosemirrorToMarkdown({
content: slice.content, // content: slice.content,
}); // });
} // }
const isText = isPureText(json) && !isSelectAll; // const isText = isPureText(json) && !isSelectAll;
if (isText) { // if (isText) {
return slice.content.textBetween(0, slice.content.size, '\n\n'); // return slice.content.textBetween(0, slice.content.size, '\n\n');
} // }
const doc = slice.content; // const doc = slice.content;
if (!doc) { // if (!doc) {
return ''; // return '';
} // }
const content = extensionThis.options.prosemirrorToMarkdown({ // const content = extensionThis.options.prosemirrorToMarkdown({
content: doc, // content: doc,
}); // });
return content; // return content;
}, // },
}, },
}), }),
]; ];

View File

@ -43,7 +43,7 @@ export const Countdown = Node.create({
parseHTML() { parseHTML() {
return [ return [
{ {
tag: 'div', tag: 'div[class=countdown]',
}, },
]; ];
}, },

View File

@ -60,7 +60,7 @@ export const Flow = Node.create({
parseHTML() { parseHTML() {
return [ return [
{ {
tag: 'div', tag: 'div[class=flow]',
}, },
]; ];
}, },

View File

@ -2,6 +2,7 @@ import { IUser } from '@think/domains';
import { mergeAttributes, Node, nodeInputRule } from '@tiptap/core'; import { mergeAttributes, Node, nodeInputRule } from '@tiptap/core';
import { ReactNodeViewRenderer } from '@tiptap/react'; import { ReactNodeViewRenderer } from '@tiptap/react';
import { KatexWrapper } from 'tiptap/core/wrappers/katex'; import { KatexWrapper } from 'tiptap/core/wrappers/katex';
import { getDatasetAttribute } from 'tiptap/prose-utils';
export type IKatexAttrs = { export type IKatexAttrs = {
text?: string; text?: string;
@ -35,9 +36,7 @@ export const Katex = Node.create({
return { return {
text: { text: {
default: '', default: '',
parseHTML: (element) => { parseHTML: getDatasetAttribute('text'),
return element.getAttribute('data-text');
},
}, },
defaultShowPicker: { defaultShowPicker: {
default: false, default: false,

View File

@ -71,7 +71,7 @@ export const Mind = Node.create({
parseHTML() { parseHTML() {
return [ return [
{ {
tag: 'div', tag: 'div[class=mind]',
}, },
]; ];
}, },

View File

@ -144,6 +144,13 @@ export const Paste = Extension.create<IPasteOptions>({
return true; return true;
} }
// If the HTML on the clipboard is from Prosemirror then the best
// compatability is to just use the HTML parser, regardless of
// whether it "looks" like Markdown, see: outline/outline#2416
if (html?.includes('data-pm-slice')) {
return false;
}
// 处理 markdown // 处理 markdown
if (markdownText || isMarkdown(text) || html.length === 0 || pasteCodeLanguage === 'markdown') { if (markdownText || isMarkdown(text) || html.length === 0 || pasteCodeLanguage === 'markdown') {
event.preventDefault(); event.preventDefault();

View File

@ -68,7 +68,7 @@ export const TitleWrapper = ({ editor, node }) => {
<NodeViewWrapper className={cls(styles.wrap, 'title')}> <NodeViewWrapper className={cls(styles.wrap, 'title')}>
{cover ? ( {cover ? (
<div className={styles.coverWrap} contentEditable={false}> <div className={styles.coverWrap} contentEditable={false}>
<LazyLoadImage src={cover} alt="cover" /> <LazyLoadImage src={cover} alt="请选择或移除封面" />
{isEditable ? ( {isEditable ? (
<div className={styles.toolbar}> <div className={styles.toolbar}>
<ImageUploader images={images} selectImage={setCover}> <ImageUploader images={images} selectImage={setCover}>

View File

@ -50,7 +50,29 @@ export const getDatasetAttribute =
(attribute: string, transformToJSON = false) => (attribute: string, transformToJSON = false) =>
(element: HTMLElement) => { (element: HTMLElement) => {
const dataKey = attribute.startsWith('data-') ? attribute : `data-${attribute}`; const dataKey = attribute.startsWith('data-') ? attribute : `data-${attribute}`;
const value = decodeURIComponent(element.getAttribute(dataKey)); let value = decodeURIComponent(element.getAttribute(dataKey));
if (value == null || (typeof value === 'string' && value === 'null')) {
try {
const html = element.outerHTML;
// eslint-disable-next-line no-useless-escape
const texts = html.match(/(.|\s)+?\="(.|\s)+?"/gi);
if (texts && texts.length) {
const params = texts
.map((str) => str.trim())
.reduce((accu, item) => {
const i = item.indexOf('=');
const arr = [item.slice(0, i), item.slice(i + 1).slice(1, -1)];
accu[arr[0]] = arr[1];
return accu;
}, {});
value = (params[attribute.toLowerCase()] || '').replaceAll('&quot;', '"');
}
} catch (e) {
console.error('解析 element 失败!', e.message, element);
}
}
if (transformToJSON) { if (transformToJSON) {
try { try {