mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-23 00:11:33 +03:00
Merge pull request #283 from toeverything/fix/experience
Fix/experience
This commit is contained in:
commit
fdafde8ad1
@ -5,7 +5,10 @@ import { getSession } from '@toeverything/components/board-sessions';
|
||||
import { deepCopy, TldrawApp } from '@toeverything/components/board-state';
|
||||
import { tools } from '@toeverything/components/board-tools';
|
||||
import { TDShapeType } from '@toeverything/components/board-types';
|
||||
import { RecastBlockProvider } from '@toeverything/components/editor-core';
|
||||
import {
|
||||
RecastBlockProvider,
|
||||
getClipDataOfBlocksById,
|
||||
} from '@toeverything/components/editor-core';
|
||||
import { services } from '@toeverything/datasource/db-service';
|
||||
import { AsyncBlock, BlockEditor } from '@toeverything/framework/virgo';
|
||||
import { useEffect, useState } from 'react';
|
||||
@ -16,7 +19,11 @@ interface AffineBoardProps {
|
||||
rootBlockId: string;
|
||||
}
|
||||
|
||||
const AffineBoard = ({ workspace, rootBlockId }: AffineBoardProps) => {
|
||||
const AffineBoard = ({
|
||||
workspace,
|
||||
rootBlockId,
|
||||
editor,
|
||||
}: AffineBoardProps & { editor: BlockEditor }) => {
|
||||
const [app, set_app] = useState<TldrawApp>();
|
||||
|
||||
const [document] = useState(() => {
|
||||
@ -62,6 +69,17 @@ const AffineBoard = ({ workspace, rootBlockId }: AffineBoardProps) => {
|
||||
onMount(app) {
|
||||
set_app(app);
|
||||
},
|
||||
async onCopy(e, groupIds) {
|
||||
const clip = await getClipDataOfBlocksById(
|
||||
editor,
|
||||
groupIds
|
||||
);
|
||||
|
||||
e.clipboardData?.setData(
|
||||
clip.getMimeType(),
|
||||
clip.getData()
|
||||
);
|
||||
},
|
||||
onChangePage(app, shapes, bindings, assets) {
|
||||
Promise.all(
|
||||
Object.entries(shapes).map(async ([id, shape]) => {
|
||||
@ -130,7 +148,11 @@ export const AffineBoardWitchContext = ({
|
||||
}, [editor, rootBlockId]);
|
||||
return page ? (
|
||||
<RecastBlockProvider block={page}>
|
||||
<AffineBoard workspace={workspace} rootBlockId={rootBlockId} />
|
||||
<AffineBoard
|
||||
workspace={workspace}
|
||||
rootBlockId={rootBlockId}
|
||||
editor={editor}
|
||||
/>
|
||||
</RecastBlockProvider>
|
||||
) : null;
|
||||
};
|
||||
|
@ -171,6 +171,10 @@ interface TDCallbacks {
|
||||
* (optional) A callback to run when the user exports their page or selection.
|
||||
*/
|
||||
onExport?: (app: TldrawApp, info: TDExport) => Promise<void>;
|
||||
/**
|
||||
* (optional) A callback to run when the shape is copied.
|
||||
*/
|
||||
onCopy?: (e: ClipboardEvent, ids: string[]) => void;
|
||||
}
|
||||
|
||||
export interface TldrawAppCtorProps {
|
||||
@ -1898,12 +1902,14 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
||||
/**
|
||||
* Copy one or more shapes to the clipboard.
|
||||
* @param ids The ids of the shapes to copy.
|
||||
* @param pageId
|
||||
* @param e
|
||||
*/
|
||||
copy = (
|
||||
copy = async (
|
||||
ids = this.selectedIds,
|
||||
pageId = this.currentPageId,
|
||||
e?: ClipboardEvent
|
||||
): this => {
|
||||
) => {
|
||||
e?.preventDefault();
|
||||
|
||||
this.clipboard = this.get_clipboard(ids, pageId);
|
||||
@ -1919,17 +1925,24 @@ export class TldrawApp extends StateManager<TDSnapshot> {
|
||||
|
||||
if (e) {
|
||||
e.clipboardData?.setData('text/html', tldrawString);
|
||||
await this.callbacks.onCopy?.(e, this.selectedIds);
|
||||
}
|
||||
|
||||
if (navigator.clipboard && window.ClipboardItem) {
|
||||
navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
'text/html': new Blob([tldrawString], {
|
||||
type: 'text/html',
|
||||
}),
|
||||
}),
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Reasons for not using Clipboard API for now:
|
||||
* 1. The `clipboardData.setData` method temporarily satisfies the need for replication functionality
|
||||
* 2. Clipboard API requires the user to agree to access(https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API)
|
||||
*
|
||||
* **/
|
||||
// if (navigator.clipboard && window.ClipboardItem) {
|
||||
// navigator.clipboard.write([
|
||||
// new ClipboardItem({
|
||||
// 'text/html': new Blob([tldrawString], {
|
||||
// type: 'text/html',
|
||||
// }),
|
||||
// }),
|
||||
// ]);
|
||||
// }
|
||||
|
||||
this.pasteInfo.offset = [0, 0];
|
||||
this.pasteInfo.center = [0, 0];
|
||||
|
@ -1,18 +1,6 @@
|
||||
import { HooksRunner } from '../types';
|
||||
import {
|
||||
OFFICE_CLIPBOARD_MIMETYPE,
|
||||
InnerClipInfo,
|
||||
ClipBlockInfo,
|
||||
} from './types';
|
||||
import { Editor } from '../editor';
|
||||
import { AsyncBlock } from '../block';
|
||||
import ClipboardParse from './clipboard-parse';
|
||||
import { SelectInfo } from '../selection';
|
||||
import {
|
||||
Protocol,
|
||||
BlockFlavorKeys,
|
||||
services,
|
||||
} from '@toeverything/datasource/db-service';
|
||||
import { MarkdownParser } from './markdown-parse';
|
||||
import { shouldHandlerContinue } from './utils';
|
||||
import { Paste } from './paste';
|
||||
|
@ -1,23 +1,16 @@
|
||||
import { Editor } from '../editor';
|
||||
import { SelectionManager, SelectInfo, SelectBlock } from '../selection';
|
||||
import { SelectionManager } from '../selection';
|
||||
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 { Subscription } from 'rxjs';
|
||||
|
||||
import { Copy } from './copy';
|
||||
class ClipboardPopulator {
|
||||
private _editor: Editor;
|
||||
private _hooks: PluginHooks;
|
||||
private _selectionManager: SelectionManager;
|
||||
private _clipboardParse: ClipboardParse;
|
||||
private _sub = new Subscription();
|
||||
|
||||
private _copy: Copy;
|
||||
constructor(
|
||||
editor: Editor,
|
||||
hooks: PluginHooks,
|
||||
@ -27,128 +20,15 @@ class ClipboardPopulator {
|
||||
this._hooks = hooks;
|
||||
this._selectionManager = selectionManager;
|
||||
this._clipboardParse = new ClipboardParse(editor);
|
||||
this._copy = new Copy(editor);
|
||||
this._sub.add(
|
||||
hooks
|
||||
.get(HookType.BEFORE_COPY)
|
||||
.subscribe(this._populateAppClipboard)
|
||||
hooks.get(HookType.BEFORE_COPY).subscribe(this._copy.handleCopy)
|
||||
);
|
||||
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() {
|
||||
this._sub.unsubscribe();
|
||||
this._hooks = null;
|
||||
|
102
libs/components/editor-core/src/editor/clipboard/copy.ts
Normal file
102
libs/components/editor-core/src/editor/clipboard/copy.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import { Editor } from '../editor';
|
||||
import { SelectInfo } from '../selection';
|
||||
import { OFFICE_CLIPBOARD_MIMETYPE } from './types';
|
||||
import { Clip } from './clip';
|
||||
import ClipboardParse from './clipboard-parse';
|
||||
import { getClipDataOfBlocksById } from './utils';
|
||||
import { copyToClipboard } from '@toeverything/utils';
|
||||
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: Clip[] = [];
|
||||
|
||||
// get custom clip
|
||||
const affineClip = await this._getAffineClip();
|
||||
clips.push(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<Clip> {
|
||||
const selectInfo: SelectInfo =
|
||||
await this._editor.selectionManager.getSelectInfo();
|
||||
|
||||
return getClipDataOfBlocksById(
|
||||
this._editor,
|
||||
selectInfo.blocks.map(block => block.blockId)
|
||||
);
|
||||
}
|
||||
|
||||
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 };
|
@ -241,15 +241,12 @@ export class Paste {
|
||||
[]
|
||||
);
|
||||
|
||||
selectedBlock.setProperties({
|
||||
await selectedBlock.setProperties({
|
||||
text: {
|
||||
value: newTextValue,
|
||||
},
|
||||
});
|
||||
const pasteBlocks = await this._createBlocks(blocks);
|
||||
await Promise.all(
|
||||
pasteBlocks.map(block => selectedBlock.after(block))
|
||||
);
|
||||
const pastedBlocks = await this._createBlocks(blocks);
|
||||
|
||||
const nextBlock = await this._editor.createBlock(
|
||||
selectedBlock?.type
|
||||
@ -259,11 +256,12 @@ export class Paste {
|
||||
value: nextTextValue,
|
||||
},
|
||||
});
|
||||
pasteBlocks[pasteBlocks.length - 1].after(nextBlock);
|
||||
|
||||
this._setEndSelectToBlock(
|
||||
pasteBlocks[pasteBlocks.length - 1].id
|
||||
);
|
||||
await this._insertBlocksAfterBlock(selectedBlock, [
|
||||
...pastedBlocks,
|
||||
nextBlock,
|
||||
]);
|
||||
await this._setEndSelectToBlock(nextBlock.id);
|
||||
} else {
|
||||
this._editor.blockHelper.insertNodes(
|
||||
selectedBlock.id,
|
||||
@ -327,10 +325,7 @@ export class Paste {
|
||||
value: newTextValue,
|
||||
},
|
||||
});
|
||||
const pasteBlocks = await this._createBlocks(blocks);
|
||||
pasteBlocks.forEach((block: AsyncBlock) => {
|
||||
selectedBlock.after(block);
|
||||
});
|
||||
const pastedBlocks = await this._createBlocks(blocks);
|
||||
const nextBlock = await this._editor.createBlock(
|
||||
selectedBlock?.type
|
||||
);
|
||||
@ -339,11 +334,12 @@ export class Paste {
|
||||
value: nextTextValue,
|
||||
},
|
||||
});
|
||||
pasteBlocks[pasteBlocks.length - 1].after(nextBlock);
|
||||
await this._insertBlocksAfterBlock(selectedBlock, [
|
||||
...pastedBlocks,
|
||||
nextBlock,
|
||||
]);
|
||||
|
||||
this._setEndSelectToBlock(
|
||||
pasteBlocks[pasteBlocks.length - 1].id
|
||||
);
|
||||
await this._setEndSelectToBlock(nextBlock.id);
|
||||
} else {
|
||||
this._editor.blockHelper.insertNodes(
|
||||
selectedBlock.id,
|
||||
@ -353,10 +349,10 @@ export class Paste {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const pasteBlocks = await this._createBlocks(blocks);
|
||||
const pastedBlocks = await this._createBlocks(blocks);
|
||||
|
||||
await Promise.all(
|
||||
pasteBlocks.map(block => selectedBlock.after(block))
|
||||
pastedBlocks.map(block => selectedBlock.after(block))
|
||||
);
|
||||
|
||||
if (isSelectedBlockEmpty) {
|
||||
@ -364,7 +360,7 @@ export class Paste {
|
||||
}
|
||||
|
||||
this._setEndSelectToBlock(
|
||||
pasteBlocks[pasteBlocks.length - 1].id
|
||||
pastedBlocks[pastedBlocks.length - 1].id
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -374,27 +370,39 @@ export class Paste {
|
||||
currentSelectInfo.blocks[currentSelectInfo.blocks.length - 1]
|
||||
.blockId
|
||||
);
|
||||
const pasteBlocks = await this._createBlocks(blocks);
|
||||
const pastedBlocks = await this._createBlocks(blocks);
|
||||
|
||||
let groupBlock: AsyncBlock;
|
||||
if (
|
||||
selectedBlock?.type === 'group' ||
|
||||
selectedBlock?.type === 'page'
|
||||
) {
|
||||
if (selectedBlock?.type === 'page') {
|
||||
groupBlock = await this._editor.createBlock('group');
|
||||
await Promise.all(
|
||||
pasteBlocks.map(block => groupBlock.append(block))
|
||||
pastedBlocks.map(block => groupBlock.append(block))
|
||||
);
|
||||
await selectedBlock.after(groupBlock);
|
||||
} else if (selectedBlock?.type === 'group') {
|
||||
await Promise.all(
|
||||
pastedBlocks.map(block => selectedBlock.append(block))
|
||||
);
|
||||
} else {
|
||||
await Promise.all(
|
||||
pasteBlocks.map(block => selectedBlock.after(block))
|
||||
pastedBlocks.map(block => selectedBlock.after(block))
|
||||
);
|
||||
}
|
||||
this._setEndSelectToBlock(pasteBlocks[pasteBlocks.length - 1].id);
|
||||
this._setEndSelectToBlock(pastedBlocks[pastedBlocks.length - 1].id);
|
||||
}
|
||||
}
|
||||
|
||||
private async _insertBlocksAfterBlock(
|
||||
targetBlock: AsyncBlock,
|
||||
blocks: AsyncBlock[]
|
||||
) {
|
||||
if (blocks.length === 0) {
|
||||
return;
|
||||
}
|
||||
const [firstBlock, ...otherBlock] = blocks;
|
||||
await targetBlock.after(firstBlock);
|
||||
await this._insertBlocksAfterBlock(blocks[0], otherBlock);
|
||||
}
|
||||
private async _setEndSelectToBlock(blockId: string) {
|
||||
const block = await this._editor.getBlockById(blockId);
|
||||
const isBlockCanEdit = Paste._isTextEditBlock(block.type);
|
||||
@ -406,14 +414,37 @@ export class Paste {
|
||||
}, 100);
|
||||
}
|
||||
|
||||
private async _createBlocks(blocks: ClipBlockInfo[], parentId?: string) {
|
||||
private _flatGroupBlocks(blocks: ClipBlockInfo[]) {
|
||||
return blocks.reduce(
|
||||
(blockList: ClipBlockInfo[], block: ClipBlockInfo) => {
|
||||
if (block.type === 'group') {
|
||||
block?.children?.forEach(childBlock => {
|
||||
childBlock.children = this._flatGroupBlocks(
|
||||
childBlock.children
|
||||
);
|
||||
});
|
||||
block?.children?.length &&
|
||||
blockList.push(...block.children);
|
||||
} else {
|
||||
blockList.push(block);
|
||||
block.children = this._flatGroupBlocks(block.children);
|
||||
}
|
||||
return blockList;
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
private async _createBlocks(blocks: ClipBlockInfo[]) {
|
||||
return Promise.all(
|
||||
blocks.map(async clipBlockInfo => {
|
||||
this._flatGroupBlocks(blocks).map(async clipBlockInfo => {
|
||||
const block = await this._editor.createBlock(
|
||||
clipBlockInfo.type
|
||||
);
|
||||
block?.setProperties(clipBlockInfo.properties);
|
||||
await this._createBlocks(clipBlockInfo.children, block?.id);
|
||||
const children = await this._createBlocks(
|
||||
clipBlockInfo.children
|
||||
);
|
||||
await Promise.all(children.map(child => block?.append(child)));
|
||||
return block;
|
||||
})
|
||||
);
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { Editor } from '../editor';
|
||||
import { ClipBlockInfo, OFFICE_CLIPBOARD_MIMETYPE } from './types';
|
||||
import { Clip } from './clip';
|
||||
|
||||
export const shouldHandlerContinue = (event: Event, editor: Editor) => {
|
||||
const filterNodes = ['INPUT', 'SELECT', 'TEXTAREA'];
|
||||
@ -12,3 +14,39 @@ export const shouldHandlerContinue = (event: Event, editor: Editor) => {
|
||||
|
||||
return editor.selectionManager.currentSelectInfo.type !== 'None';
|
||||
};
|
||||
|
||||
export const getClipInfoOfBlockById = async (
|
||||
editor: Editor,
|
||||
blockId: string
|
||||
) => {
|
||||
const block = await editor.getBlockById(blockId);
|
||||
const blockView = editor.getView(block.type);
|
||||
const blockInfo: ClipBlockInfo = {
|
||||
type: block.type,
|
||||
properties: blockView.getSelProperties(block, {}),
|
||||
children: [] as ClipBlockInfo[],
|
||||
};
|
||||
const children = (await block?.children()) ?? [];
|
||||
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const childInfo = await getClipInfoOfBlockById(editor, children[i].id);
|
||||
blockInfo.children.push(childInfo);
|
||||
}
|
||||
return blockInfo;
|
||||
};
|
||||
|
||||
export const getClipDataOfBlocksById = async (
|
||||
editor: Editor,
|
||||
blockIds: string[]
|
||||
) => {
|
||||
const clipInfos = await Promise.all(
|
||||
blockIds.map(blockId => getClipInfoOfBlockById(editor, blockId))
|
||||
);
|
||||
|
||||
return new Clip(
|
||||
OFFICE_CLIPBOARD_MIMETYPE.DOCS_DOCUMENT_SLICE_CLIP_WRAPPED,
|
||||
JSON.stringify({
|
||||
data: clipInfos,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
@ -10,3 +10,4 @@ export { BlockDropPlacement, HookType, GroupDirection } from './types';
|
||||
export type { Plugin, PluginCreator, PluginHooks, Virgo } from './types';
|
||||
export { BaseView, getTextHtml, getTextProperties } from './views/base-view';
|
||||
export type { ChildrenView, CreateView } from './views/base-view';
|
||||
export { getClipDataOfBlocksById } from './clipboard/utils';
|
||||
|
Loading…
Reference in New Issue
Block a user