refactor: refactor clip board
This commit is contained in:
@@ -1,18 +1,6 @@
|
|||||||
import { HooksRunner } from '../types';
|
import { HooksRunner } from '../types';
|
||||||
import {
|
|
||||||
OFFICE_CLIPBOARD_MIMETYPE,
|
|
||||||
InnerClipInfo,
|
|
||||||
ClipBlockInfo,
|
|
||||||
} from './types';
|
|
||||||
import { Editor } from '../editor';
|
import { Editor } from '../editor';
|
||||||
import { AsyncBlock } from '../block';
|
|
||||||
import ClipboardParse from './clipboard-parse';
|
import ClipboardParse from './clipboard-parse';
|
||||||
import { SelectInfo } from '../selection';
|
|
||||||
import {
|
|
||||||
Protocol,
|
|
||||||
BlockFlavorKeys,
|
|
||||||
services,
|
|
||||||
} from '@toeverything/datasource/db-service';
|
|
||||||
import { MarkdownParser } from './markdown-parse';
|
import { MarkdownParser } from './markdown-parse';
|
||||||
import { shouldHandlerContinue } from './utils';
|
import { shouldHandlerContinue } from './utils';
|
||||||
import { Paste } from './paste';
|
import { Paste } from './paste';
|
||||||
|
|||||||
@@ -1,23 +1,16 @@
|
|||||||
import { Editor } from '../editor';
|
import { Editor } from '../editor';
|
||||||
import { SelectionManager, SelectInfo, SelectBlock } from '../selection';
|
import { SelectionManager } from '../selection';
|
||||||
import { HookType, PluginHooks } from '../types';
|
import { HookType, PluginHooks } from '../types';
|
||||||
import {
|
|
||||||
ClipBlockInfo,
|
|
||||||
OFFICE_CLIPBOARD_MIMETYPE,
|
|
||||||
InnerClipInfo,
|
|
||||||
} from './types';
|
|
||||||
import { Clip } from './clip';
|
|
||||||
import assert from 'assert';
|
|
||||||
import ClipboardParse from './clipboard-parse';
|
import ClipboardParse from './clipboard-parse';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
import { Copy } from './copy';
|
||||||
class ClipboardPopulator {
|
class ClipboardPopulator {
|
||||||
private _editor: Editor;
|
private _editor: Editor;
|
||||||
private _hooks: PluginHooks;
|
private _hooks: PluginHooks;
|
||||||
private _selectionManager: SelectionManager;
|
private _selectionManager: SelectionManager;
|
||||||
private _clipboardParse: ClipboardParse;
|
private _clipboardParse: ClipboardParse;
|
||||||
private _sub = new Subscription();
|
private _sub = new Subscription();
|
||||||
|
private _copy: Copy;
|
||||||
constructor(
|
constructor(
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
hooks: PluginHooks,
|
hooks: PluginHooks,
|
||||||
@@ -27,128 +20,15 @@ class ClipboardPopulator {
|
|||||||
this._hooks = hooks;
|
this._hooks = hooks;
|
||||||
this._selectionManager = selectionManager;
|
this._selectionManager = selectionManager;
|
||||||
this._clipboardParse = new ClipboardParse(editor);
|
this._clipboardParse = new ClipboardParse(editor);
|
||||||
|
this._copy = new Copy(editor);
|
||||||
this._sub.add(
|
this._sub.add(
|
||||||
hooks
|
hooks.get(HookType.BEFORE_COPY).subscribe(this._copy.handleCopy)
|
||||||
.get(HookType.BEFORE_COPY)
|
|
||||||
.subscribe(this._populateAppClipboard)
|
|
||||||
);
|
);
|
||||||
this._sub.add(
|
this._sub.add(
|
||||||
hooks.get(HookType.BEFORE_CUT).subscribe(this._populateAppClipboard)
|
hooks.get(HookType.BEFORE_CUT).subscribe(this._copy.handleCopy)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _populateAppClipboard = async (e: ClipboardEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
const clips = await this.getClips();
|
|
||||||
if (!clips.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: is not compatible with safari
|
|
||||||
const success = this._copyToClipboardFromPc(clips);
|
|
||||||
if (!success) {
|
|
||||||
// This way, not compatible with firefox
|
|
||||||
const clipboardData = e.clipboardData;
|
|
||||||
if (clipboardData) {
|
|
||||||
try {
|
|
||||||
clips.forEach(clip => {
|
|
||||||
clipboardData.setData(
|
|
||||||
clip.getMimeType(),
|
|
||||||
clip.getData()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
// TODO handle exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private _copyToClipboardFromPc(clips: any[]) {
|
|
||||||
let success = false;
|
|
||||||
const tempElem = document.createElement('textarea');
|
|
||||||
tempElem.value = 'temp';
|
|
||||||
document.body.appendChild(tempElem);
|
|
||||||
tempElem.select();
|
|
||||||
tempElem.setSelectionRange(0, tempElem.value.length);
|
|
||||||
|
|
||||||
const listener = function (e: any) {
|
|
||||||
const clipboardData = e.clipboardData;
|
|
||||||
if (clipboardData) {
|
|
||||||
clips.forEach(clip => {
|
|
||||||
clipboardData.setData(clip.getMimeType(), clip.getData());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
tempElem.removeEventListener('copy', listener);
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
tempElem.addEventListener('copy', listener);
|
|
||||||
try {
|
|
||||||
success = document.execCommand('copy');
|
|
||||||
} finally {
|
|
||||||
tempElem.removeEventListener('copy', listener);
|
|
||||||
document.body.removeChild(tempElem);
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _getClipBlockInfo(selBlock: SelectBlock) {
|
|
||||||
const block = await this._editor.getBlockById(selBlock.blockId);
|
|
||||||
const blockView = this._editor.getView(block.type);
|
|
||||||
assert(blockView);
|
|
||||||
const blockInfo: ClipBlockInfo = {
|
|
||||||
type: block.type,
|
|
||||||
properties: blockView.getSelProperties(block, selBlock),
|
|
||||||
children: [] as any[],
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let i = 0; i < selBlock.children.length; i++) {
|
|
||||||
const childInfo = await this._getClipBlockInfo(
|
|
||||||
selBlock.children[i]
|
|
||||||
);
|
|
||||||
blockInfo.children.push(childInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
return blockInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _getInnerClip(): Promise<InnerClipInfo> {
|
|
||||||
const clips: ClipBlockInfo[] = [];
|
|
||||||
const selectInfo: SelectInfo =
|
|
||||||
await this._selectionManager.getSelectInfo();
|
|
||||||
for (let i = 0; i < selectInfo.blocks.length; i++) {
|
|
||||||
const selBlock = selectInfo.blocks[i];
|
|
||||||
const clipBlockInfo = await this._getClipBlockInfo(selBlock);
|
|
||||||
clips.push(clipBlockInfo);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
select: selectInfo,
|
|
||||||
data: clips,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async getClips() {
|
|
||||||
const clips: any[] = [];
|
|
||||||
|
|
||||||
const innerClip = await this._getInnerClip();
|
|
||||||
clips.push(
|
|
||||||
new Clip(
|
|
||||||
OFFICE_CLIPBOARD_MIMETYPE.DOCS_DOCUMENT_SLICE_CLIP_WRAPPED,
|
|
||||||
JSON.stringify(innerClip)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
const htmlClip = await this._clipboardParse.generateHtml();
|
|
||||||
htmlClip &&
|
|
||||||
clips.push(new Clip(OFFICE_CLIPBOARD_MIMETYPE.HTML, htmlClip));
|
|
||||||
|
|
||||||
return clips;
|
|
||||||
}
|
|
||||||
|
|
||||||
disposeInternal() {
|
disposeInternal() {
|
||||||
this._sub.unsubscribe();
|
this._sub.unsubscribe();
|
||||||
this._hooks = null;
|
this._hooks = null;
|
||||||
|
|||||||
136
libs/components/editor-core/src/editor/clipboard/copy.ts
Normal file
136
libs/components/editor-core/src/editor/clipboard/copy.ts
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import { Editor } from '../editor';
|
||||||
|
import { SelectInfo, SelectBlock } from '../selection';
|
||||||
|
import {
|
||||||
|
ClipBlockInfo,
|
||||||
|
OFFICE_CLIPBOARD_MIMETYPE,
|
||||||
|
InnerClipInfo,
|
||||||
|
} from './types';
|
||||||
|
import { Clip } from './clip';
|
||||||
|
import assert from 'assert';
|
||||||
|
import ClipboardParse from './clipboard-parse';
|
||||||
|
|
||||||
|
class Copy {
|
||||||
|
private _editor: Editor;
|
||||||
|
private _clipboardParse: ClipboardParse;
|
||||||
|
|
||||||
|
constructor(editor: Editor) {
|
||||||
|
this._editor = editor;
|
||||||
|
this._clipboardParse = new ClipboardParse(editor);
|
||||||
|
|
||||||
|
this.handleCopy = this.handleCopy.bind(this);
|
||||||
|
}
|
||||||
|
public async handleCopy(e: ClipboardEvent) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
const clips = await this.getClips();
|
||||||
|
if (!clips.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: is not compatible with safari
|
||||||
|
const success = this._copyToClipboardFromPc(clips);
|
||||||
|
if (!success) {
|
||||||
|
// This way, not compatible with firefox
|
||||||
|
const clipboardData = e.clipboardData;
|
||||||
|
if (clipboardData) {
|
||||||
|
try {
|
||||||
|
clips.forEach(clip => {
|
||||||
|
clipboardData.setData(
|
||||||
|
clip.getMimeType(),
|
||||||
|
clip.getData()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// TODO handle exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getClips() {
|
||||||
|
const clips: any[] = [];
|
||||||
|
|
||||||
|
// get custom clip
|
||||||
|
const affineClip = await this._getAffineClip();
|
||||||
|
clips.push(
|
||||||
|
new Clip(
|
||||||
|
OFFICE_CLIPBOARD_MIMETYPE.DOCS_DOCUMENT_SLICE_CLIP_WRAPPED,
|
||||||
|
JSON.stringify(affineClip)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// get common html clip
|
||||||
|
const htmlClip = await this._clipboardParse.generateHtml();
|
||||||
|
htmlClip &&
|
||||||
|
clips.push(new Clip(OFFICE_CLIPBOARD_MIMETYPE.HTML, htmlClip));
|
||||||
|
|
||||||
|
return clips;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _getAffineClip(): Promise<InnerClipInfo> {
|
||||||
|
const clips: ClipBlockInfo[] = [];
|
||||||
|
const selectInfo: SelectInfo =
|
||||||
|
await this._editor.selectionManager.getSelectInfo();
|
||||||
|
for (let i = 0; i < selectInfo.blocks.length; i++) {
|
||||||
|
const selBlock = selectInfo.blocks[i];
|
||||||
|
const clipBlockInfo = await this._getClipInfoOfBlock(selBlock);
|
||||||
|
clips.push(clipBlockInfo);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
select: selectInfo,
|
||||||
|
data: clips,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _getClipInfoOfBlock(selBlock: SelectBlock) {
|
||||||
|
const block = await this._editor.getBlockById(selBlock.blockId);
|
||||||
|
const blockView = this._editor.getView(block.type);
|
||||||
|
assert(blockView);
|
||||||
|
const blockInfo: ClipBlockInfo = {
|
||||||
|
type: block.type,
|
||||||
|
properties: blockView.getSelProperties(block, selBlock),
|
||||||
|
children: [] as any[],
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0; i < selBlock.children.length; i++) {
|
||||||
|
const childInfo = await this._getClipInfoOfBlock(
|
||||||
|
selBlock.children[i]
|
||||||
|
);
|
||||||
|
blockInfo.children.push(childInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _copyToClipboardFromPc(clips: any[]) {
|
||||||
|
let success = false;
|
||||||
|
const tempElem = document.createElement('textarea');
|
||||||
|
tempElem.value = 'temp';
|
||||||
|
document.body.appendChild(tempElem);
|
||||||
|
tempElem.select();
|
||||||
|
tempElem.setSelectionRange(0, tempElem.value.length);
|
||||||
|
|
||||||
|
const listener = function (e: any) {
|
||||||
|
const clipboardData = e.clipboardData;
|
||||||
|
if (clipboardData) {
|
||||||
|
clips.forEach(clip => {
|
||||||
|
clipboardData.setData(clip.getMimeType(), clip.getData());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
tempElem.removeEventListener('copy', listener);
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
tempElem.addEventListener('copy', listener);
|
||||||
|
try {
|
||||||
|
success = document.execCommand('copy');
|
||||||
|
} finally {
|
||||||
|
tempElem.removeEventListener('copy', listener);
|
||||||
|
document.body.removeChild(tempElem);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Copy };
|
||||||
Reference in New Issue
Block a user