fix: 优化粘贴的word格式文本
This commit is contained in:
parent
07157acacf
commit
4259d75920
|
@ -415,6 +415,21 @@ export default class Paste {
|
|||
nodeParent &&
|
||||
nodeApi.isMark(nodeParent, this.schema) &&
|
||||
nodeApi.isMark(node, this.schema)
|
||||
) {
|
||||
if (markApi.compare(nodeParent.clone(), node, true)) {
|
||||
nodeApi.unwrap(node);
|
||||
return;
|
||||
} else {
|
||||
nodeParent = nodeParent.parent();
|
||||
}
|
||||
}
|
||||
// mark 按级别排序
|
||||
nodeParent = parent;
|
||||
if (
|
||||
node.length > 0 &&
|
||||
nodeParent &&
|
||||
nodeApi.isMark(nodeParent, this.schema) &&
|
||||
nodeApi.isMark(node, this.schema)
|
||||
) {
|
||||
const pMarkPlugin = markApi.findPlugin(nodeParent);
|
||||
const cMarkPlugin = markApi.findPlugin(node);
|
||||
|
@ -454,12 +469,6 @@ export default class Paste {
|
|||
return node;
|
||||
}
|
||||
}
|
||||
if (markApi.compare(nodeParent.clone(), node, true)) {
|
||||
nodeApi.unwrap(node);
|
||||
break;
|
||||
} else {
|
||||
nodeParent = nodeParent.parent();
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
|
|
@ -120,6 +120,7 @@ class Parser implements ParserInterface {
|
|||
let value = conversion.transform(node);
|
||||
const oldRules: Array<ConversionRule> = [];
|
||||
const nodeApi = this.editor.node;
|
||||
let convertAfterNode: NodeInterface | null = null;
|
||||
while (value) {
|
||||
const { rule } = value;
|
||||
oldRules.push(rule);
|
||||
|
@ -139,13 +140,22 @@ class Parser implements ParserInterface {
|
|||
if (!nodeApi.isBlock(newNode, schema)) {
|
||||
if (value.replace) {
|
||||
node.replaceWith(newNode);
|
||||
node = newNode;
|
||||
} else {
|
||||
//把包含旧子节点的新节点追加到旧节点下
|
||||
node.append(newNode);
|
||||
}
|
||||
if (!convertAfterNode || convertAfterNode.length === 0)
|
||||
convertAfterNode = newNode;
|
||||
} else {
|
||||
// 替换
|
||||
node.replaceWith(newNode);
|
||||
if (value.replace) {
|
||||
node.replaceWith(newNode);
|
||||
node = newNode;
|
||||
} else {
|
||||
node.append(newNode);
|
||||
}
|
||||
if (!convertAfterNode || convertAfterNode.length === 0)
|
||||
convertAfterNode = newNode;
|
||||
//排除之前的过滤规则后再次过滤
|
||||
value = conversion.transform(
|
||||
newNode,
|
||||
|
@ -157,7 +167,7 @@ class Parser implements ParserInterface {
|
|||
//排除之前的过滤规则后再次过滤
|
||||
value = conversion.transform(node, (r) => oldRules.indexOf(r) < 0);
|
||||
}
|
||||
return;
|
||||
return convertAfterNode;
|
||||
}
|
||||
normalize(
|
||||
root: NodeInterface,
|
||||
|
@ -199,10 +209,11 @@ class Parser implements ParserInterface {
|
|||
return;
|
||||
const attrCount = Object.keys(attributes).length;
|
||||
const styleCount = Object.keys(style).length;
|
||||
//过滤不符合当前节点规则的属性样式
|
||||
schema.filter(node, attributes, style);
|
||||
|
||||
//复制一个节点
|
||||
const newNode = node.clone();
|
||||
//过滤不符合当前节点规则的属性样式
|
||||
schema.filter(node, attributes, style, true);
|
||||
//移除 data-id,以免在下次判断类型的时候使用缓存
|
||||
newNode.removeAttributes(DATA_ID);
|
||||
//移除符合当前节点的属性样式,剩余的属性样式组成新的节点
|
||||
|
@ -257,8 +268,7 @@ class Parser implements ParserInterface {
|
|||
if (conversion && (!schema.getType(node) || isCard)) {
|
||||
const newNode = this.convert(conversion, node, schema);
|
||||
if (newNode) {
|
||||
this.normalize(newNode, schema, conversion);
|
||||
return;
|
||||
return newNode;
|
||||
}
|
||||
}
|
||||
if (isCard) return;
|
||||
|
@ -281,16 +291,23 @@ class Parser implements ParserInterface {
|
|||
);
|
||||
if (!type) {
|
||||
if (conversion) {
|
||||
this.convert(conversion, newNode, schema);
|
||||
const newChildren = newNode.children();
|
||||
if (newChildren.length > 0) {
|
||||
const newChildren = this.convert(
|
||||
conversion,
|
||||
newNode,
|
||||
schema,
|
||||
);
|
||||
if (newChildren && newChildren.length > 0) {
|
||||
const children = node.children();
|
||||
newChildren.append(
|
||||
children.length > 0
|
||||
? children
|
||||
: $('\u200b', null),
|
||||
);
|
||||
node.append(newChildren);
|
||||
node.append(
|
||||
newNode.length === 0
|
||||
? newChildren
|
||||
: newNode.children(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -300,7 +317,16 @@ class Parser implements ParserInterface {
|
|||
let tempNode = node;
|
||||
while (type === 'mark') {
|
||||
const children = tempNode.children();
|
||||
newNode.append(
|
||||
let appendTarget = newNode;
|
||||
while (true) {
|
||||
const children = appendTarget.children();
|
||||
if (children.length > 0) {
|
||||
appendTarget = children;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
appendTarget.append(
|
||||
children.length > 0
|
||||
? children
|
||||
: $('\u200b', null),
|
||||
|
@ -319,10 +345,16 @@ class Parser implements ParserInterface {
|
|||
);
|
||||
if (!type) {
|
||||
if (conversion) {
|
||||
this.convert(conversion, newNode, schema);
|
||||
const newChildren = newNode.children();
|
||||
if (newChildren.length > 0) {
|
||||
newNode = newChildren;
|
||||
const newChildren = this.convert(
|
||||
conversion,
|
||||
newNode,
|
||||
schema,
|
||||
);
|
||||
if (newChildren && newChildren.length > 0) {
|
||||
newNode =
|
||||
newNode.length > 0
|
||||
? newNode.children()
|
||||
: newChildren;
|
||||
type = 'mark';
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -320,7 +320,11 @@ class Schema implements SchemaInterface {
|
|||
* @param styles 样式
|
||||
* @param rule 规则
|
||||
*/
|
||||
filterStyles(styles: { [k: string]: string }, rule: SchemaRule) {
|
||||
filterStyles(
|
||||
styles: { [k: string]: string },
|
||||
rule: SchemaRule,
|
||||
callback?: (name: string, value: string) => void,
|
||||
) {
|
||||
Object.keys(styles).forEach((styleName) => {
|
||||
if (
|
||||
!rule.attributes?.style ||
|
||||
|
@ -330,8 +334,10 @@ class Schema implements SchemaInterface {
|
|||
styles[styleName],
|
||||
true,
|
||||
)
|
||||
)
|
||||
) {
|
||||
if (callback) callback(styleName, styles[styleName]);
|
||||
delete styles[styleName];
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
|
@ -339,7 +345,11 @@ class Schema implements SchemaInterface {
|
|||
* @param attributes 属性
|
||||
* @param rule 规则
|
||||
*/
|
||||
filterAttributes(attributes: { [k: string]: string }, rule: SchemaRule) {
|
||||
filterAttributes(
|
||||
attributes: { [k: string]: string },
|
||||
rule: SchemaRule,
|
||||
callback?: (name: string, value: string) => void,
|
||||
) {
|
||||
Object.keys(attributes).forEach((attributesName) => {
|
||||
if (
|
||||
!rule.attributes ||
|
||||
|
@ -349,8 +359,11 @@ class Schema implements SchemaInterface {
|
|||
attributes[attributesName],
|
||||
true,
|
||||
)
|
||||
)
|
||||
) {
|
||||
if (callback)
|
||||
callback(attributesName, attributes[attributesName]);
|
||||
delete attributes[attributesName];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -359,12 +372,14 @@ class Schema implements SchemaInterface {
|
|||
* @param node 节点,用于获取规则
|
||||
* @param attributes 属性
|
||||
* @param styles 样式
|
||||
* @param apply 是否把过滤的属性和样式应用到节点上
|
||||
* @returns
|
||||
*/
|
||||
filter(
|
||||
node: NodeInterface,
|
||||
attributes: { [k: string]: string },
|
||||
styles: { [k: string]: string },
|
||||
apply: boolean = false,
|
||||
) {
|
||||
const rule = this.getRule(node);
|
||||
if (!rule) return;
|
||||
|
@ -377,8 +392,16 @@ class Schema implements SchemaInterface {
|
|||
globalRule ? globals[globalRule] : {},
|
||||
),
|
||||
});
|
||||
this.filterAttributes(attributes, allRule);
|
||||
this.filterStyles(styles, allRule);
|
||||
this.filterAttributes(
|
||||
attributes,
|
||||
allRule,
|
||||
apply ? (name) => node.removeAttributes(name) : undefined,
|
||||
);
|
||||
this.filterStyles(
|
||||
styles,
|
||||
allRule,
|
||||
apply ? (name) => node.css(name, '') : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -147,16 +147,23 @@ export interface SchemaInterface {
|
|||
* 过滤节点样式
|
||||
* @param styles 样式
|
||||
* @param rule 规则
|
||||
* @param callback 回调
|
||||
*/
|
||||
filterStyles(styles: { [k: string]: string }, rule: SchemaRule): void;
|
||||
filterStyles(
|
||||
styles: { [k: string]: string },
|
||||
rule: SchemaRule,
|
||||
callback?: (name: string, value: string) => void,
|
||||
): void;
|
||||
/**
|
||||
* 过滤节点属性
|
||||
* @param attributes 属性
|
||||
* @param rule 规则
|
||||
* @param callback 回调
|
||||
*/
|
||||
filterAttributes(
|
||||
attributes: { [k: string]: string },
|
||||
rule: SchemaRule,
|
||||
callback?: (name: string, value: string) => void,
|
||||
): void;
|
||||
/**
|
||||
* 过滤满足node节点规则的属性和样式
|
||||
|
@ -169,6 +176,7 @@ export interface SchemaInterface {
|
|||
node: NodeInterface,
|
||||
attributes: { [k: string]: string },
|
||||
styles: { [k: string]: string },
|
||||
apply?: boolean,
|
||||
): void;
|
||||
/**
|
||||
* 克隆当前schema对象
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import { MarkPlugin, PluginOptions } from '@aomao/engine';
|
||||
import {
|
||||
$,
|
||||
ConversionFromValue,
|
||||
ConversionToValue,
|
||||
MarkPlugin,
|
||||
PluginOptions,
|
||||
} from '@aomao/engine';
|
||||
|
||||
export interface StrikethroughOptions extends PluginOptions {
|
||||
hotkey?: string | Array<string>;
|
||||
|
@ -20,17 +26,28 @@ export default class<
|
|||
markdown =
|
||||
this.options.markdown === undefined ? '~~' : this.options.markdown;
|
||||
|
||||
conversion() {
|
||||
conversion(): { from: ConversionFromValue; to: ConversionToValue }[] {
|
||||
return [
|
||||
{
|
||||
from: {
|
||||
span: {
|
||||
style: {
|
||||
'text-decoration': 'line-through',
|
||||
},
|
||||
},
|
||||
from: (name, style) => {
|
||||
return (
|
||||
name === 'span' &&
|
||||
(style['text-decoration'] || '').includes(
|
||||
'line-through',
|
||||
)
|
||||
);
|
||||
},
|
||||
to: (_, style, attrs) => {
|
||||
const newNode = $(`<${this.tagName} />`);
|
||||
style['text-decoration'] = style['text-decoration']
|
||||
.split(/\s+/)
|
||||
.filter((value) => value !== 'line-through')
|
||||
.join(' ')
|
||||
.trim();
|
||||
newNode.css(style);
|
||||
newNode.attributes(attrs);
|
||||
return newNode;
|
||||
},
|
||||
to: this.tagName,
|
||||
},
|
||||
{
|
||||
from: 's',
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
position: absolute;
|
||||
border: 1px solid #e8e8e8;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
|
||||
z-index: 124;
|
||||
z-index: 130;
|
||||
text-indent: 0;
|
||||
top:0;
|
||||
padding: 8px 0;
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
import { MarkPlugin, PluginOptions } from '@aomao/engine';
|
||||
import { $, MarkPlugin } from '@aomao/engine';
|
||||
import type {
|
||||
ConversionFromValue,
|
||||
ConversionToValue,
|
||||
PluginOptions,
|
||||
} from '@aomao/engine';
|
||||
|
||||
export interface UnderlineOptions extends PluginOptions {
|
||||
hotkey?: string | Array<string>;
|
||||
|
@ -16,17 +21,26 @@ export default class<
|
|||
return this.options.hotkey || 'mod+u';
|
||||
}
|
||||
|
||||
conversion() {
|
||||
conversion(): { from: ConversionFromValue; to: ConversionToValue }[] {
|
||||
return [
|
||||
{
|
||||
from: {
|
||||
span: {
|
||||
style: {
|
||||
'text-decoration': 'underline',
|
||||
},
|
||||
},
|
||||
from: (name, style) => {
|
||||
return (
|
||||
name === 'span' &&
|
||||
(style['text-decoration'] || '').includes('underline')
|
||||
);
|
||||
},
|
||||
to: (_, style, attrs) => {
|
||||
const newNode = $(`<${this.tagName} />`);
|
||||
style['text-decoration'] = style['text-decoration']
|
||||
.split(/\s+/)
|
||||
.filter((value) => value !== 'underline')
|
||||
.join(' ')
|
||||
.trim();
|
||||
newNode.css(style);
|
||||
newNode.attributes(attrs);
|
||||
return newNode;
|
||||
},
|
||||
to: this.tagName,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue