fix
- 表格中不能导出未渲染卡片的html - 优化标记插件协同交互 - 协同交互影响了当前编辑者的光标 - 浏览器会响应默认快捷键命令与当前绑定的快捷键会重复执行命令
This commit is contained in:
parent
f299f6a704
commit
8da00f1ba9
|
@ -126,7 +126,7 @@ export const pluginConfig: { [key: string]: PluginOptions } = {
|
|||
},
|
||||
[Image.pluginName]: {
|
||||
onBeforeRender: (status: string, url: string) => {
|
||||
if (!url) return url;
|
||||
if (!url || url.indexOf('http') === 0) return url;
|
||||
return url + `?token=12323`;
|
||||
},
|
||||
},
|
||||
|
@ -138,7 +138,8 @@ export const pluginConfig: { [key: string]: PluginOptions } = {
|
|||
remote: {
|
||||
action: `${DOMAIN}/upload/image`,
|
||||
},
|
||||
isRemote: (src: string) => src.indexOf(DOMAIN) < 0,
|
||||
isRemote: (src: string) =>
|
||||
src.indexOf(DOMAIN) < 0 && src.indexOf('192.168') < 0,
|
||||
},
|
||||
[FileUploader.pluginName]: {
|
||||
action: `${DOMAIN}/upload/file`,
|
||||
|
|
|
@ -627,16 +627,20 @@ class CardModel implements CardModelInterface {
|
|||
/**
|
||||
* 渲染
|
||||
* @param container 需要重新渲染包含卡片的节点,如果不传,则渲染全部待创建的卡片节点
|
||||
* @param options 是否异步渲染, 全部异步渲染完成后触发
|
||||
* @param callback 渲染完成后回调
|
||||
* @param lazyRender 是否懒渲染,默认取决于editor的lazyRender属性
|
||||
*/
|
||||
render(container?: NodeInterface, callback?: (count: number) => void) {
|
||||
render(
|
||||
container?: NodeInterface,
|
||||
callback?: (count: number) => void,
|
||||
lazyRender = this.lazyRender,
|
||||
) {
|
||||
const cards = container
|
||||
? container.isCard()
|
||||
? container
|
||||
: container.find(`${READY_CARD_SELECTOR}`)
|
||||
: this.editor.container.find(READY_CARD_SELECTOR);
|
||||
this.gc();
|
||||
let setp = 0;
|
||||
|
||||
const asyncRenderCards: Array<CardInterface> = [];
|
||||
cards.each((node) => {
|
||||
|
@ -686,8 +690,8 @@ class CardModel implements CardModelInterface {
|
|||
}
|
||||
});
|
||||
|
||||
asyncRenderCards.forEach(async (card) => {
|
||||
if (this.lazyRender && (card.constructor as CardEntry).lazyRender) {
|
||||
asyncRenderCards.forEach((card) => {
|
||||
if (lazyRender && (card.constructor as CardEntry).lazyRender) {
|
||||
if (card.beforeRender) {
|
||||
const result = card.beforeRender();
|
||||
const center = card.getCenter();
|
||||
|
@ -702,15 +706,9 @@ class CardModel implements CardModelInterface {
|
|||
} else {
|
||||
this.renderComponent(card);
|
||||
}
|
||||
setp++;
|
||||
if (setp === asyncRenderCards.length) {
|
||||
if (callback) callback(asyncRenderCards.length);
|
||||
}
|
||||
});
|
||||
if (asyncRenderCards.length === 0) {
|
||||
if (callback) callback(0);
|
||||
}
|
||||
if (asyncRenderCards.length > 0) {
|
||||
if (callback) callback(asyncRenderCards.length);
|
||||
if (this.asyncComponents.length > 0) {
|
||||
// 触发当前在视图内的卡片渲染
|
||||
this.renderAsnycComponents();
|
||||
}
|
||||
|
@ -718,7 +716,7 @@ class CardModel implements CardModelInterface {
|
|||
|
||||
renderComponent(card: CardInterface, ...args: any) {
|
||||
const center = card.getCenter();
|
||||
const result = card.render();
|
||||
const result = card.render(...args);
|
||||
if (result !== undefined) {
|
||||
center.append(typeof result === 'string' ? $(result) : result);
|
||||
}
|
||||
|
|
|
@ -159,7 +159,13 @@ class ChangeEvent implements ChangeEventInterface {
|
|||
const commandName = inputType
|
||||
.substring(type.length)
|
||||
.toLowerCase();
|
||||
this.engine.command.execute(commandName);
|
||||
if (this.engine.command.queryEnabled(commandName)) {
|
||||
this.engine.hotkey.disable();
|
||||
this.engine.command.execute(commandName);
|
||||
setTimeout(() => {
|
||||
this.engine.hotkey.enable();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -133,7 +133,7 @@ class ChangeRange implements ChangeRangeInterface {
|
|||
return range;
|
||||
}
|
||||
|
||||
select(range: RangeInterface) {
|
||||
select(range: RangeInterface, triggerSelect: boolean = true) {
|
||||
const { container, inline, node, change } = this.engine;
|
||||
const { window } = container;
|
||||
const selection = window?.getSelection();
|
||||
|
@ -321,7 +321,7 @@ class ChangeRange implements ChangeRangeInterface {
|
|||
selection.addRange(range.toRange());
|
||||
}
|
||||
const { onSelect } = this.#otpions;
|
||||
if (onSelect) onSelect(range);
|
||||
if (onSelect && triggerSelect) onSelect(range);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,7 +26,7 @@ import Request, {
|
|||
} from './request';
|
||||
import Scrollbar from './scrollbar';
|
||||
import Position from './position';
|
||||
import { $, getHashId } from './node';
|
||||
import { $, getHashId, uuid } from './node';
|
||||
|
||||
export * from './types';
|
||||
export * from './utils';
|
||||
|
@ -39,6 +39,7 @@ export default Engine;
|
|||
|
||||
export {
|
||||
$,
|
||||
uuid,
|
||||
getHashId,
|
||||
Selection,
|
||||
Range,
|
||||
|
|
|
@ -23,7 +23,7 @@ import {
|
|||
} from '../constants';
|
||||
import { getDocument, getStyleMap, isEngine } from '../utils';
|
||||
import $ from './query';
|
||||
import getHashId from './hash';
|
||||
import getHashId, { uuid } from './hash';
|
||||
import { isNode, isNodeEntry } from './utils';
|
||||
|
||||
class NodeModel implements NodeModelInterface {
|
||||
|
@ -1027,4 +1027,4 @@ class NodeModel implements NodeModelInterface {
|
|||
|
||||
export default NodeModel;
|
||||
|
||||
export { Entry as NodeEntry, Event, $, getHashId };
|
||||
export { Entry as NodeEntry, Event, $, getHashId, uuid };
|
||||
|
|
|
@ -453,7 +453,7 @@ class Consumer implements ConsumerInterface {
|
|||
if (endInfo && endInfo.container) {
|
||||
range.setEnd(endInfo.container, endInfo.offset);
|
||||
}
|
||||
this.engine.change.range.select(range);
|
||||
this.engine.change.range.select(range, false);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
|
|
@ -9,12 +9,7 @@ import {
|
|||
ConversionRule,
|
||||
SchemaRule,
|
||||
} from '../types';
|
||||
import {
|
||||
CARD_ELEMENT_KEY,
|
||||
CARD_KEY,
|
||||
CARD_SELECTOR,
|
||||
READY_CARD_KEY,
|
||||
} from '../constants';
|
||||
import { CARD_ELEMENT_KEY, CARD_SELECTOR } from '../constants';
|
||||
import {
|
||||
escape,
|
||||
unescape,
|
||||
|
|
|
@ -530,9 +530,14 @@ export interface CardModelInterface {
|
|||
/**
|
||||
* 渲染
|
||||
* @param container 需要重新渲染包含卡片的节点,如果不传,则渲染全部待创建的卡片节点
|
||||
* @param callback 全部异步渲染完成后触发
|
||||
* @param callback 渲染完成后回调
|
||||
* @param lazyRender 是否懒渲染,默认取决于editor的lazyRender属性
|
||||
*/
|
||||
render(container?: NodeInterface, callback?: (count: number) => void): void;
|
||||
render(
|
||||
container?: NodeInterface,
|
||||
callback?: (count: number) => void,
|
||||
lazyRender?: boolean,
|
||||
): void;
|
||||
/**
|
||||
* 重新渲染卡片
|
||||
* @param cards 卡片集合
|
||||
|
|
|
@ -137,8 +137,9 @@ export interface ChangeRangeInterface {
|
|||
/**
|
||||
* 选中指定的范围
|
||||
* @param range 光标
|
||||
* @param triggerSelect 时候触发onSelect事件
|
||||
*/
|
||||
select(range: RangeInterface): void;
|
||||
select(range: RangeInterface, triggerSelect?: boolean): void;
|
||||
/**
|
||||
* 聚焦编辑器
|
||||
* @param toStart true:开始位置,false:结束位置,默认为之前操作位置
|
||||
|
|
|
@ -23,7 +23,7 @@ import { BlockModelInterface } from './block';
|
|||
import { RequestInterface } from './request';
|
||||
import { RangeInterface } from './range';
|
||||
import { Op } from 'sharedb';
|
||||
import { NodeIdInterface } from './';
|
||||
import { HotkeyInterface, NodeIdInterface } from './';
|
||||
|
||||
/**
|
||||
* 编辑器容器接口
|
||||
|
@ -508,6 +508,10 @@ export interface EngineInterface extends EditorInterface {
|
|||
* 历史记录
|
||||
*/
|
||||
history: HistoryInterface;
|
||||
/**
|
||||
* 快捷键
|
||||
*/
|
||||
hotkey: HotkeyInterface;
|
||||
/**
|
||||
* 聚焦到编辑器
|
||||
*/
|
||||
|
|
|
@ -12,6 +12,8 @@ import {
|
|||
unescape,
|
||||
CARD_TYPE_KEY,
|
||||
PluginOptions,
|
||||
READY_CARD_KEY,
|
||||
decodeCardValue,
|
||||
} from '@aomao/engine';
|
||||
import CodeBlockComponent, { CodeBlockEditor } from './component';
|
||||
|
||||
|
@ -298,62 +300,60 @@ export default class extends Plugin<Options> {
|
|||
parseHtml(root: NodeInterface) {
|
||||
if (isServer) return;
|
||||
|
||||
root.find(`[${CARD_KEY}=${CodeBlockComponent.cardName}`).each(
|
||||
(cardNode) => {
|
||||
const node = $(cardNode);
|
||||
const card = this.editor.card.find(node) as CodeBlockComponent;
|
||||
const value = card?.getValue();
|
||||
if (value && value.code) {
|
||||
node.empty();
|
||||
const synatxMap: { [key: string]: string } = {};
|
||||
CodeBlockComponent.getModes().forEach((item) => {
|
||||
synatxMap[item.value] = item.syntax;
|
||||
});
|
||||
const codeEditor = new CodeBlockEditor(this.editor, {
|
||||
synatxMap,
|
||||
});
|
||||
root.find(
|
||||
`[${CARD_KEY}="${CodeBlockComponent.cardName}"],[${READY_CARD_KEY}="${CodeBlockComponent.cardName}]"`,
|
||||
).each((cardNode) => {
|
||||
const node = $(cardNode);
|
||||
const card = this.editor.card.find(node) as CodeBlockComponent;
|
||||
const value =
|
||||
card?.getValue() ||
|
||||
decodeCardValue(node.attributes(CARD_VALUE_KEY));
|
||||
if (value && value.code) {
|
||||
node.empty();
|
||||
const synatxMap: { [key: string]: string } = {};
|
||||
CodeBlockComponent.getModes().forEach((item) => {
|
||||
synatxMap[item.value] = item.syntax;
|
||||
});
|
||||
const codeEditor = new CodeBlockEditor(this.editor, {
|
||||
synatxMap,
|
||||
});
|
||||
|
||||
const content = codeEditor.container.find(
|
||||
'.data-codeblock-content',
|
||||
);
|
||||
content.css({
|
||||
border: '1px solid #e8e8e8',
|
||||
'max-width': '750px',
|
||||
});
|
||||
codeEditor.render(value.mode || 'plain', value.code || '');
|
||||
content.addClass('am-engine-view');
|
||||
content.hide();
|
||||
document.body.appendChild(content[0]);
|
||||
content.traverse((node) => {
|
||||
if (
|
||||
node.type === Node.ELEMENT_NODE &&
|
||||
(node.get<HTMLElement>()?.classList?.length || 0) >
|
||||
0
|
||||
) {
|
||||
const element = node.get<HTMLElement>()!;
|
||||
const style = window.getComputedStyle(element);
|
||||
[
|
||||
'color',
|
||||
'margin',
|
||||
'padding',
|
||||
'background',
|
||||
].forEach((attr) => {
|
||||
const content = codeEditor.container.find(
|
||||
'.data-codeblock-content',
|
||||
);
|
||||
content.css({
|
||||
border: '1px solid #e8e8e8',
|
||||
'max-width': '750px',
|
||||
});
|
||||
codeEditor.render(value.mode || 'plain', value.code || '');
|
||||
content.addClass('am-engine-view');
|
||||
content.hide();
|
||||
document.body.appendChild(content[0]);
|
||||
content.traverse((node) => {
|
||||
if (
|
||||
node.type === Node.ELEMENT_NODE &&
|
||||
(node.get<HTMLElement>()?.classList?.length || 0) > 0
|
||||
) {
|
||||
const element = node.get<HTMLElement>()!;
|
||||
const style = window.getComputedStyle(element);
|
||||
['color', 'margin', 'padding', 'background'].forEach(
|
||||
(attr) => {
|
||||
(element.style as any)[attr] =
|
||||
style.getPropertyValue(attr);
|
||||
});
|
||||
}
|
||||
});
|
||||
content.show();
|
||||
content.css('background', '#f9f9f9');
|
||||
node.append(content);
|
||||
node.removeAttributes(CARD_KEY);
|
||||
node.removeAttributes(CARD_TYPE_KEY);
|
||||
node.removeAttributes(CARD_VALUE_KEY);
|
||||
node.attributes('data-syntax', value.mode || 'plain');
|
||||
content.removeClass('am-engine-view');
|
||||
} else node.remove();
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
content.show();
|
||||
content.css('background', '#f9f9f9');
|
||||
node.append(content);
|
||||
node.removeAttributes(CARD_KEY);
|
||||
node.removeAttributes(CARD_TYPE_KEY);
|
||||
node.removeAttributes(CARD_VALUE_KEY);
|
||||
node.attributes('data-syntax', value.mode || 'plain');
|
||||
content.removeClass('am-engine-view');
|
||||
} else node.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
export { CodeBlockComponent };
|
||||
|
|
|
@ -12,6 +12,8 @@ import {
|
|||
unescape,
|
||||
CARD_TYPE_KEY,
|
||||
PluginOptions,
|
||||
READY_CARD_KEY,
|
||||
decodeCardValue,
|
||||
} from '@aomao/engine';
|
||||
import CodeBlockComponent, { CodeBlockEditor } from './component';
|
||||
|
||||
|
@ -299,62 +301,60 @@ export default class extends Plugin<Options> {
|
|||
parseHtml(root: NodeInterface) {
|
||||
if (isServer) return;
|
||||
|
||||
root.find(`[${CARD_KEY}=${CodeBlockComponent.cardName}`).each(
|
||||
(cardNode) => {
|
||||
const node = $(cardNode);
|
||||
const card = this.editor.card.find(node) as CodeBlockComponent;
|
||||
const value = card?.getValue();
|
||||
if (value) {
|
||||
node.empty();
|
||||
const synatxMap = {};
|
||||
CodeBlockComponent.getModes().forEach((item) => {
|
||||
synatxMap[item.value] = item.syntax;
|
||||
});
|
||||
const codeEditor = new CodeBlockEditor(this.editor, {
|
||||
synatxMap,
|
||||
});
|
||||
root.find(
|
||||
`[${CARD_KEY}="${CodeBlockComponent.cardName}"],[${READY_CARD_KEY}="${CodeBlockComponent.cardName}"]`,
|
||||
).each((cardNode) => {
|
||||
const node = $(cardNode);
|
||||
const card = this.editor.card.find(node) as CodeBlockComponent;
|
||||
const value =
|
||||
card?.getValue() ||
|
||||
decodeCardValue(node.attributes(CARD_VALUE_KEY));
|
||||
if (value) {
|
||||
node.empty();
|
||||
const synatxMap = {};
|
||||
CodeBlockComponent.getModes().forEach((item) => {
|
||||
synatxMap[item.value] = item.syntax;
|
||||
});
|
||||
const codeEditor = new CodeBlockEditor(this.editor, {
|
||||
synatxMap,
|
||||
});
|
||||
|
||||
const content = codeEditor.container.find(
|
||||
'.data-codeblock-content',
|
||||
);
|
||||
content.css({
|
||||
border: '1px solid #e8e8e8',
|
||||
'max-width': '750px',
|
||||
});
|
||||
codeEditor.render(value.mode || 'plain', value.code || '');
|
||||
content.addClass('am-engine-view');
|
||||
content.hide();
|
||||
document.body.appendChild(content[0]);
|
||||
content.traverse((node) => {
|
||||
if (
|
||||
node.type === Node.ELEMENT_NODE &&
|
||||
(node.get<HTMLElement>()?.classList?.length || 0) >
|
||||
0
|
||||
) {
|
||||
const element = node.get<HTMLElement>()!;
|
||||
const style = window.getComputedStyle(element);
|
||||
[
|
||||
'color',
|
||||
'margin',
|
||||
'padding',
|
||||
'background',
|
||||
].forEach((attr) => {
|
||||
const content = codeEditor.container.find(
|
||||
'.data-codeblock-content',
|
||||
);
|
||||
content.css({
|
||||
border: '1px solid #e8e8e8',
|
||||
'max-width': '750px',
|
||||
});
|
||||
codeEditor.render(value.mode || 'plain', value.code || '');
|
||||
content.addClass('am-engine-view');
|
||||
content.hide();
|
||||
document.body.appendChild(content[0]);
|
||||
content.traverse((node) => {
|
||||
if (
|
||||
node.type === Node.ELEMENT_NODE &&
|
||||
(node.get<HTMLElement>()?.classList?.length || 0) > 0
|
||||
) {
|
||||
const element = node.get<HTMLElement>()!;
|
||||
const style = window.getComputedStyle(element);
|
||||
['color', 'margin', 'padding', 'background'].forEach(
|
||||
(attr) => {
|
||||
element.style[attr] =
|
||||
style.getPropertyValue(attr);
|
||||
});
|
||||
}
|
||||
});
|
||||
content.show();
|
||||
content.css('background', '#f9f9f9');
|
||||
node.append(content);
|
||||
node.removeAttributes(CARD_KEY);
|
||||
node.removeAttributes(CARD_TYPE_KEY);
|
||||
node.removeAttributes(CARD_VALUE_KEY);
|
||||
node.attributes('data-syntax', value.mode || 'plain');
|
||||
content.removeClass('am-engine-view');
|
||||
} else node.remove();
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
content.show();
|
||||
content.css('background', '#f9f9f9');
|
||||
node.append(content);
|
||||
node.removeAttributes(CARD_KEY);
|
||||
node.removeAttributes(CARD_TYPE_KEY);
|
||||
node.removeAttributes(CARD_VALUE_KEY);
|
||||
node.attributes('data-syntax', value.mode || 'plain');
|
||||
content.removeClass('am-engine-view');
|
||||
} else node.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
export { CodeBlockComponent };
|
||||
|
|
|
@ -3,12 +3,14 @@ import {
|
|||
CardEntry,
|
||||
CardInterface,
|
||||
CARD_KEY,
|
||||
CARD_VALUE_KEY,
|
||||
decodeCardValue,
|
||||
encodeCardValue,
|
||||
isEngine,
|
||||
NodeInterface,
|
||||
Plugin,
|
||||
PluginEntry,
|
||||
READY_CARD_KEY,
|
||||
SchemaInterface,
|
||||
} from '@aomao/engine';
|
||||
import FileComponent, { FileValue } from './component';
|
||||
|
@ -154,10 +156,14 @@ export default class extends Plugin {
|
|||
}
|
||||
|
||||
parseHtml(root: NodeInterface) {
|
||||
root.find(`[${CARD_KEY}=${FileComponent.cardName}`).each((cardNode) => {
|
||||
root.find(
|
||||
`[${CARD_KEY}="${FileComponent.cardName}"],[${READY_CARD_KEY}="${FileComponent.cardName}"`,
|
||||
).each((cardNode) => {
|
||||
const node = $(cardNode);
|
||||
const card = this.editor.card.find(node) as FileComponent;
|
||||
const value = card?.getValue();
|
||||
const value =
|
||||
card?.getValue() ||
|
||||
decodeCardValue(node.attributes(CARD_VALUE_KEY));
|
||||
if (value?.url && value.status === 'done') {
|
||||
const html = `<a data-type="${
|
||||
FileComponent.cardName
|
||||
|
|
|
@ -544,6 +544,11 @@ class Image {
|
|||
width: width + 'px',
|
||||
height: height + 'px',
|
||||
});
|
||||
const { onChange } = this.options;
|
||||
if (width > 0 && height > 0) {
|
||||
this.size = { ...this.size, width, height };
|
||||
if (onChange) onChange(this.size);
|
||||
}
|
||||
}
|
||||
this.bg.css({
|
||||
width: width + 'px',
|
||||
|
|
|
@ -4,9 +4,13 @@ import {
|
|||
CardInterface,
|
||||
CardType,
|
||||
CARD_KEY,
|
||||
CARD_TYPE_KEY,
|
||||
CARD_VALUE_KEY,
|
||||
decodeCardValue,
|
||||
NodeInterface,
|
||||
Plugin,
|
||||
PluginEntry,
|
||||
READY_CARD_KEY,
|
||||
} from '@aomao/engine';
|
||||
import ImageComponent, { ImageValue } from './component';
|
||||
import ImageUploader from './uploader';
|
||||
|
@ -108,40 +112,41 @@ export default class extends Plugin<{
|
|||
}
|
||||
|
||||
parseHtml(root: NodeInterface) {
|
||||
root.find(`[${CARD_KEY}=${ImageComponent.cardName}`).each(
|
||||
(cardNode) => {
|
||||
const node = $(cardNode);
|
||||
const card = this.editor.card.find(node) as ImageComponent;
|
||||
const value = card?.getValue();
|
||||
if (value?.src && value.status === 'done') {
|
||||
const img = node.find('.data-image-meta > img');
|
||||
node.empty();
|
||||
let src = value.src;
|
||||
const { onBeforeRender } = this.options;
|
||||
if (onBeforeRender) {
|
||||
src = onBeforeRender(value.status, value.src);
|
||||
root.find(
|
||||
`[${CARD_KEY}="${ImageComponent.cardName}"],[${READY_CARD_KEY}="${ImageComponent.cardName}"]`,
|
||||
).each((cardNode) => {
|
||||
const node = $(cardNode);
|
||||
const card = this.editor.card.find(node) as ImageComponent;
|
||||
const value =
|
||||
card?.getValue() ||
|
||||
decodeCardValue(node.attributes(CARD_VALUE_KEY));
|
||||
if (value?.src && value.status === 'done') {
|
||||
let img = $('<img />');
|
||||
node.empty();
|
||||
let src = value.src;
|
||||
const { onBeforeRender } = this.options;
|
||||
if (onBeforeRender) {
|
||||
src = onBeforeRender(value.status, value.src);
|
||||
}
|
||||
const type = node.attributes(CARD_TYPE_KEY);
|
||||
img.attributes('src', src);
|
||||
img.css('visibility', 'visible');
|
||||
const size = value.size;
|
||||
if (size.width) img.css('width', `${size.width}px`);
|
||||
if (size.height) img.css('height', `${size.height}px`);
|
||||
img.removeAttributes('class');
|
||||
img.attributes('data-type', type);
|
||||
if (img.length > 0) {
|
||||
if (type === CardType.BLOCK) {
|
||||
img = this.editor.node.wrap(
|
||||
img,
|
||||
$(`<p style="text-align:center;"></p>`),
|
||||
);
|
||||
}
|
||||
img.attributes('src', src);
|
||||
img.css('visibility', 'visible');
|
||||
img.css('background', '');
|
||||
img.css('background-color', '');
|
||||
img.css('background-repeat', '');
|
||||
img.css('background-position', '');
|
||||
img.css('background-image', '');
|
||||
img.removeAttributes('class');
|
||||
|
||||
if (img.length > 0) {
|
||||
node.replaceWith(img);
|
||||
if (card.type === CardType.BLOCK) {
|
||||
this.editor.node.wrap(
|
||||
img,
|
||||
$(`<p style="text-align:center;"></p>`),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else node.remove();
|
||||
},
|
||||
);
|
||||
node.replaceWith(img);
|
||||
}
|
||||
} else node.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
decodeCardValue,
|
||||
encodeCardValue,
|
||||
removeUnit,
|
||||
CardType,
|
||||
} from '@aomao/engine';
|
||||
import ImageComponent, { ImageValue } from './component';
|
||||
|
||||
|
@ -476,15 +477,28 @@ export default class extends Plugin<Options> {
|
|||
} */
|
||||
//图片
|
||||
if (node.name === 'img') {
|
||||
const src = node.attributes('src') || node.attributes('data-src');
|
||||
const alt = node.attributes('alt');
|
||||
const attributes = node.attributes();
|
||||
const src = attributes['src'] || attributes['data-src'];
|
||||
const alt = attributes['alt'];
|
||||
if (!src) {
|
||||
node.remove();
|
||||
return;
|
||||
}
|
||||
const width = node.css('width');
|
||||
const height = node.css('height');
|
||||
const dataTypeValue = attributes['data-type'];
|
||||
let type = CardType.INLINE;
|
||||
if (dataTypeValue === 'block') {
|
||||
const parent = node.parent();
|
||||
// 移除转换为html的时候加载的额外p标签
|
||||
if (parent && parent.name === 'p') {
|
||||
this.editor.node.unwrap(node);
|
||||
}
|
||||
type = CardType.BLOCK;
|
||||
}
|
||||
|
||||
this.editor.card.replaceNode(node, 'image', {
|
||||
type,
|
||||
src,
|
||||
status:
|
||||
(isRemote && isRemote(src)) || /^data:image\//i.test(src)
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
SchemaMark,
|
||||
Selection,
|
||||
PluginOptions,
|
||||
uuid,
|
||||
} from '@aomao/engine';
|
||||
import { Path } from 'sharedb';
|
||||
|
||||
|
@ -26,7 +27,9 @@ export default class extends MarkPlugin<Options> {
|
|||
private range?: RangeInterface;
|
||||
private executeBySelf: boolean = false;
|
||||
private MARK_KEY = `data-mark-key`;
|
||||
private MARK_UUID = `data-mark-uuid`;
|
||||
private ids: { [key: string]: Array<string> } = {};
|
||||
private m_uuid = uuid(18, 24);
|
||||
|
||||
readonly followStyle: boolean = false;
|
||||
|
||||
|
@ -84,8 +87,8 @@ export default class extends MarkPlugin<Options> {
|
|||
|
||||
if (isEngine(this.editor)) {
|
||||
const { change } = this.editor;
|
||||
this.editor.on('change', () => {
|
||||
this.triggerChange();
|
||||
this.editor.on('change', (_, trigger) => {
|
||||
this.triggerChange(trigger !== 'local');
|
||||
});
|
||||
this.editor.on('select', () => this.onSelectionChange());
|
||||
this.editor.on('parse:value', (node, atts) => {
|
||||
|
@ -141,6 +144,7 @@ export default class extends MarkPlugin<Options> {
|
|||
required: true,
|
||||
value: key,
|
||||
},
|
||||
[this.MARK_UUID]: '*',
|
||||
[this.getIdName(key)]: '*',
|
||||
},
|
||||
};
|
||||
|
@ -277,7 +281,9 @@ export default class extends MarkPlugin<Options> {
|
|||
this.MARK_KEY
|
||||
}="${key}" ${DATA_TRANSIENT_ATTRIBUTES}="${this.getPreviewName(
|
||||
key,
|
||||
)}" ${this.getPreviewName(key)}="true" />`,
|
||||
)}" ${this.MARK_UUID}="${this.m_uuid}" ${this.getPreviewName(
|
||||
key,
|
||||
)}="true" />`,
|
||||
range,
|
||||
);
|
||||
//遍历当前光标选择节点,拼接选择的文本
|
||||
|
@ -478,7 +484,6 @@ export default class extends MarkPlugin<Options> {
|
|||
execute() {}
|
||||
|
||||
action(key: string, action: string, ...args: any): any {
|
||||
const history = isEngine(this.editor) ? this.editor.history : undefined;
|
||||
const id = args[0];
|
||||
switch (action) {
|
||||
case 'preview':
|
||||
|
@ -552,10 +557,11 @@ export default class extends MarkPlugin<Options> {
|
|||
this.range = range;
|
||||
}
|
||||
|
||||
triggerChange() {
|
||||
triggerChange(remote: boolean = false) {
|
||||
const addIds: { [key: string]: Array<string> } = {};
|
||||
const removeIds: { [key: string]: Array<string> } = {};
|
||||
const ids = this.getIds();
|
||||
|
||||
this.options.keys.forEach((key) => {
|
||||
const prevIds = this.ids[key] || [];
|
||||
const curIds = ids[key] || [];
|
||||
|
@ -572,6 +578,25 @@ export default class extends MarkPlugin<Options> {
|
|||
}
|
||||
});
|
||||
});
|
||||
if (remote) {
|
||||
const currentElements = this.editor.container.find(
|
||||
`[${this.MARK_UUID}="${this.m_uuid}"]`,
|
||||
);
|
||||
currentElements.each((_, index) => {
|
||||
const child = currentElements.eq(index);
|
||||
const attributes = child?.attributes() || {};
|
||||
const key = attributes[this.MARK_KEY];
|
||||
// 如果这个元素没有被标记,并且没有创建、没有预览选项就增加预览样式
|
||||
const previewName = this.getPreviewName(key);
|
||||
if (
|
||||
key &&
|
||||
!attributes[this.getIdName(key)] &&
|
||||
!attributes[previewName]
|
||||
) {
|
||||
child!.attributes(previewName, 'true');
|
||||
}
|
||||
});
|
||||
}
|
||||
this.ids = ids;
|
||||
this.editor.trigger(`${PLUGIN_NAME}:change`, addIds, removeIds, ids);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ import {
|
|||
decodeCardValue,
|
||||
encodeCardValue,
|
||||
AjaxInterface,
|
||||
READY_CARD_KEY,
|
||||
CARD_VALUE_KEY,
|
||||
} from '@aomao/engine';
|
||||
import MathComponent from './component';
|
||||
import locales from './locales';
|
||||
|
@ -241,9 +243,10 @@ export default class Math extends Plugin<Options> {
|
|||
pasteHtml(node: NodeInterface) {
|
||||
if (!isEngine(this.editor)) return;
|
||||
if (node.isElement()) {
|
||||
const type = node.attributes('data-type');
|
||||
const attributes = node.attributes();
|
||||
const type = attributes['data-type'];
|
||||
if (type === MathComponent.cardName) {
|
||||
const value = node.attributes('data-value');
|
||||
const value = attributes['data-value'];
|
||||
const cardValue = decodeCardValue(value);
|
||||
if (!cardValue.url) return;
|
||||
this.editor.card.replaceNode(
|
||||
|
@ -259,10 +262,14 @@ export default class Math extends Plugin<Options> {
|
|||
}
|
||||
|
||||
parseHtml(root: NodeInterface) {
|
||||
root.find(`[${CARD_KEY}=${MathComponent.cardName}`).each((cardNode) => {
|
||||
root.find(
|
||||
`[${CARD_KEY}="${MathComponent.cardName}"],[${READY_CARD_KEY}="${MathComponent.cardName}"]`,
|
||||
).each((cardNode) => {
|
||||
const node = $(cardNode);
|
||||
const card = this.editor.card.find(node) as MathComponent;
|
||||
const value = card?.getValue();
|
||||
const value =
|
||||
card?.getValue() ||
|
||||
decodeCardValue(node.attributes(CARD_VALUE_KEY));
|
||||
if (value) {
|
||||
const img = node.find('img');
|
||||
node.empty();
|
||||
|
|
|
@ -14,6 +14,8 @@ import {
|
|||
encodeCardValue,
|
||||
CardInterface,
|
||||
AjaxInterface,
|
||||
READY_CARD_KEY,
|
||||
CARD_VALUE_KEY,
|
||||
} from '@aomao/engine';
|
||||
import MentionComponent from './component';
|
||||
import locales from './locales';
|
||||
|
@ -229,9 +231,10 @@ class MentionPlugin extends Plugin<Options> {
|
|||
pasteHtml(node: NodeInterface) {
|
||||
if (!isEngine(this.editor)) return;
|
||||
if (node.isElement()) {
|
||||
const type = node.attributes('data-type');
|
||||
const attributes = node.attributes();
|
||||
const type = attributes['data-type'];
|
||||
if (type === MentionComponent.cardName) {
|
||||
const value = node.attributes('data-value');
|
||||
const value = attributes['data-value'];
|
||||
const cardValue = decodeCardValue(value);
|
||||
if (!cardValue.name) return;
|
||||
this.editor.card.replaceNode(
|
||||
|
@ -247,22 +250,24 @@ class MentionPlugin extends Plugin<Options> {
|
|||
}
|
||||
|
||||
parseHtml(root: NodeInterface) {
|
||||
root.find(`[${CARD_KEY}=${MentionComponent.cardName}`).each(
|
||||
(cardNode) => {
|
||||
const node = $(cardNode);
|
||||
const card = this.editor.card.find(node) as MentionComponent;
|
||||
const value = card?.getValue();
|
||||
if (value?.id && value.name) {
|
||||
const html = `<span data-type="${
|
||||
MentionComponent.cardName
|
||||
}" data-value="${encodeCardValue(
|
||||
value,
|
||||
)}" style="color:#1890ff">@${value.name}</span>`;
|
||||
node.empty();
|
||||
node.replaceWith($(html));
|
||||
} else node.remove();
|
||||
},
|
||||
);
|
||||
root.find(
|
||||
`[${CARD_KEY}="${MentionComponent.cardName}"],[${READY_CARD_KEY}="${MentionComponent.cardName}"]`,
|
||||
).each((cardNode) => {
|
||||
const node = $(cardNode);
|
||||
const card = this.editor.card.find(node) as MentionComponent;
|
||||
const value =
|
||||
card?.getValue() ||
|
||||
decodeCardValue(node.attributes(CARD_VALUE_KEY));
|
||||
if (value?.id && value.name) {
|
||||
const html = `<span data-type="${
|
||||
MentionComponent.cardName
|
||||
}" data-value="${encodeCardValue(
|
||||
value,
|
||||
)}" style="color:#1890ff">@${value.name}</span>`;
|
||||
node.empty();
|
||||
node.replaceWith($(html));
|
||||
} else node.remove();
|
||||
});
|
||||
}
|
||||
|
||||
execute() {}
|
||||
|
|
|
@ -9,6 +9,10 @@ import {
|
|||
PluginOptions,
|
||||
SchemaInterface,
|
||||
getDocument,
|
||||
Parser,
|
||||
READY_CARD_KEY,
|
||||
decodeCardValue,
|
||||
CARD_VALUE_KEY,
|
||||
} from '@aomao/engine';
|
||||
import TableComponent, { Template } from './component';
|
||||
import locales from './locale';
|
||||
|
@ -355,55 +359,57 @@ class Table extends Plugin<Options> {
|
|||
}
|
||||
|
||||
parseHtml(root: NodeInterface) {
|
||||
root.find(`[${CARD_KEY}=${TableComponent.cardName}`).each(
|
||||
(tableNode) => {
|
||||
const node = $(tableNode);
|
||||
const card = this.editor.card.find(node) as TableComponent;
|
||||
const value = card?.getValue();
|
||||
if (value && value.html) {
|
||||
let table = node.find('table');
|
||||
root.find(
|
||||
`[${CARD_KEY}="${TableComponent.cardName}"],[${READY_CARD_KEY}="${TableComponent.cardName}"]`,
|
||||
).each((tableNode) => {
|
||||
const node = $(tableNode);
|
||||
const card = this.editor.card.find(node) as TableComponent;
|
||||
const value =
|
||||
card?.getValue() ||
|
||||
decodeCardValue(node.attributes(CARD_VALUE_KEY));
|
||||
if (value && value.html) {
|
||||
let table = node.find('table');
|
||||
if (table.length === 0) {
|
||||
// 表格值里面的卡片都是没有被转换过的,所以需要先把卡片转换过来
|
||||
table = $(value.html);
|
||||
if (table.length === 0) {
|
||||
table = $(value.html);
|
||||
if (table.length === 0) {
|
||||
node.remove();
|
||||
return;
|
||||
}
|
||||
node.remove();
|
||||
return;
|
||||
} else {
|
||||
table = $(new Parser(table, this.editor).toHTML());
|
||||
}
|
||||
const width =
|
||||
table.attributes('width') || table.css('width');
|
||||
table.css({
|
||||
outline: 'none',
|
||||
'border-collapse': 'collapse',
|
||||
width: '100%',
|
||||
});
|
||||
table.attributes('data-width', width);
|
||||
const tds = table.find('td');
|
||||
tds.each((_, index) => {
|
||||
const tdElement = tds.eq(index);
|
||||
tdElement?.css({
|
||||
'min-width': 'auto',
|
||||
'white-space': 'flat',
|
||||
'word-wrap': 'break-word',
|
||||
margin: '4px 8px',
|
||||
border: !!table.attributes('data-table-no-border')
|
||||
? '0 none'
|
||||
: '1px solid #d9d9d9',
|
||||
padding: '4px 8px',
|
||||
cursor: 'default',
|
||||
'vertical-align':
|
||||
tdElement.css('vertical-align') || 'top',
|
||||
});
|
||||
});
|
||||
table.find(Template.TABLE_TD_BG_CLASS).remove();
|
||||
table
|
||||
.find(Template.TABLE_TD_CONTENT_CLASS)
|
||||
.each((content) => {
|
||||
this.editor.node.unwrap($(content));
|
||||
});
|
||||
node.replaceWith(table);
|
||||
}
|
||||
},
|
||||
);
|
||||
const width = table.attributes('width') || table.css('width');
|
||||
table.css({
|
||||
outline: 'none',
|
||||
'border-collapse': 'collapse',
|
||||
width: '100%',
|
||||
});
|
||||
table.attributes('data-width', width);
|
||||
const tds = table.find('td');
|
||||
tds.each((_, index) => {
|
||||
const tdElement = tds.eq(index);
|
||||
tdElement?.css({
|
||||
'min-width': 'auto',
|
||||
'white-space': 'flat',
|
||||
'word-wrap': 'break-word',
|
||||
margin: '4px 8px',
|
||||
border: !!table.attributes('data-table-no-border')
|
||||
? '0 none'
|
||||
: '1px solid #d9d9d9',
|
||||
padding: '4px 8px',
|
||||
cursor: 'default',
|
||||
'vertical-align':
|
||||
tdElement.css('vertical-align') || 'top',
|
||||
});
|
||||
});
|
||||
table.find(Template.TABLE_TD_BG_CLASS).remove();
|
||||
table.find(Template.TABLE_TD_CONTENT_CLASS).each((content) => {
|
||||
this.editor.node.unwrap($(content));
|
||||
});
|
||||
node.replaceWith(table);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getMarkdownCell(match: RegExpExecArray, count?: number) {
|
||||
|
|
|
@ -3,12 +3,14 @@ import {
|
|||
CardEntry,
|
||||
CardInterface,
|
||||
CARD_KEY,
|
||||
CARD_VALUE_KEY,
|
||||
decodeCardValue,
|
||||
encodeCardValue,
|
||||
isEngine,
|
||||
NodeInterface,
|
||||
Plugin,
|
||||
PluginEntry,
|
||||
READY_CARD_KEY,
|
||||
sanitizeUrl,
|
||||
SchemaInterface,
|
||||
} from '@aomao/engine';
|
||||
|
@ -162,34 +164,36 @@ export default class VideoPlugin extends Plugin<{
|
|||
}
|
||||
|
||||
parseHtml(root: NodeInterface) {
|
||||
root.find(`[${CARD_KEY}=${VideoComponent.cardName}`).each(
|
||||
(cardNode) => {
|
||||
const node = $(cardNode);
|
||||
const card = this.editor.card.find(node) as VideoComponent;
|
||||
const value = card?.getValue();
|
||||
if (value?.url && value.status === 'done') {
|
||||
const { onBeforeRender } = this.options;
|
||||
const { cover, url } = value;
|
||||
const html = `<div data-type="${
|
||||
VideoComponent.cardName
|
||||
}" data-value="${encodeCardValue(
|
||||
value,
|
||||
)}"><video controls src="${sanitizeUrl(
|
||||
onBeforeRender ? onBeforeRender('query', url) : url,
|
||||
)}" poster="${
|
||||
!cover
|
||||
? 'none'
|
||||
: sanitizeUrl(
|
||||
onBeforeRender
|
||||
? onBeforeRender('cover', cover)
|
||||
: cover,
|
||||
)
|
||||
}" webkit-playsinline="webkit-playsinline" playsinline="playsinline" style="outline:none;" /></div>`;
|
||||
node.empty();
|
||||
node.replaceWith($(html));
|
||||
} else node.remove();
|
||||
},
|
||||
);
|
||||
root.find(
|
||||
`[${CARD_KEY}="${VideoComponent.cardName}"],[${READY_CARD_KEY}="${VideoComponent.cardName}"]`,
|
||||
).each((cardNode) => {
|
||||
const node = $(cardNode);
|
||||
const card = this.editor.card.find(node) as VideoComponent;
|
||||
const value =
|
||||
card?.getValue() ||
|
||||
decodeCardValue(node.attributes(CARD_VALUE_KEY));
|
||||
if (value?.url && value.status === 'done') {
|
||||
const { onBeforeRender } = this.options;
|
||||
const { cover, url } = value;
|
||||
const html = `<div data-type="${
|
||||
VideoComponent.cardName
|
||||
}" data-value="${encodeCardValue(
|
||||
value,
|
||||
)}"><video controls src="${sanitizeUrl(
|
||||
onBeforeRender ? onBeforeRender('query', url) : url,
|
||||
)}" poster="${
|
||||
!cover
|
||||
? 'none'
|
||||
: sanitizeUrl(
|
||||
onBeforeRender
|
||||
? onBeforeRender('cover', cover)
|
||||
: cover,
|
||||
)
|
||||
}" webkit-playsinline="webkit-playsinline" playsinline="playsinline" style="outline:none;" /></div>`;
|
||||
node.empty();
|
||||
node.replaceWith($(html));
|
||||
} else node.remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue