From d4586357284f10848b205581b66f2c375d8e4d99 Mon Sep 17 00:00:00 2001 From: MasanobuRyuman <22011376mr@tama.ac.jp> Date: Tue, 13 Jun 2023 19:27:45 +0900 Subject: [PATCH] feat: node copy with button --- packages/core/src/helpers/index.ts | 2 +- packages/core/src/helpers/node/index.ts | 19 ++++++++++- packages/core/src/toolmenu/index.tsx | 44 ++++++++++++++++++++----- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/packages/core/src/helpers/index.ts b/packages/core/src/helpers/index.ts index df1d2b26..144ad8b8 100644 --- a/packages/core/src/helpers/index.ts +++ b/packages/core/src/helpers/index.ts @@ -2,7 +2,7 @@ import { style } from '@vanilla-extract/css'; import Element, * as elements from './element'; import * as logger from './logger'; import * as error from './error'; -import Node from './node'; +import * as Node from './node'; import Path, * as pathHelpers from './path'; import ReactEditor from './reactEditor'; import * as transforms from './transform'; diff --git a/packages/core/src/helpers/node/index.ts b/packages/core/src/helpers/node/index.ts index 82f33658..3e5901f7 100644 --- a/packages/core/src/helpers/node/index.ts +++ b/packages/core/src/helpers/node/index.ts @@ -1,3 +1,20 @@ -import { Node as SlateNode } from 'slate'; +import { Editor, Node as SlateNode } from 'slate'; export default SlateNode; + +export const copyNode = async (editor: Editor) => { + const dataTransfer = new DataTransfer(); + editor.setFragmentData(dataTransfer, 'copy'); + const clipboardBlobList = dataTransfer.types.map((type) => { + return new Blob([dataTransfer.getData(type)], { type: type }); + }); + + const clipBoardItem: Record = {}; + const removeType = 'application/x-slate-fragment'; + clipboardBlobList + .filter((value) => value.type !== removeType) + .forEach((blob) => { + clipBoardItem[blob.type] = blob; + }); + await navigator.clipboard.write([new ClipboardItem(clipBoardItem)]); +}; diff --git a/packages/core/src/toolmenu/index.tsx b/packages/core/src/toolmenu/index.tsx index 383526a3..172b8d0b 100644 --- a/packages/core/src/toolmenu/index.tsx +++ b/packages/core/src/toolmenu/index.tsx @@ -1,9 +1,11 @@ import { useCallback } from 'react'; -import { Node, Path } from 'slate'; +import { Element, Node, Path } from 'slate'; import { useSlate } from 'slate-react'; import { CopyIcon } from '../components/icons/copy'; import { DustboxIcon } from '../components/icons/dustbox'; import { styles } from './index.css'; +import { Text } from 'slate'; +import { helpers } from '../helpers'; export type ToolmenuProps = { path: Path; @@ -19,13 +21,39 @@ export const Toolmenu: React.FC = ({ path, onDone }) => { onDone(); }, [editor, path, onDone]); - const handleCopyClick = useCallback(() => { - const node = Node.get(editor, path); - // TODO: There seems to be no API for copying. Replace this workaround if any found. - const copiedNode = JSON.parse(JSON.stringify(node)); - editor.insertNodes(copiedNode, { - at: Path.next(path), - }); + const handleCopyClick = useCallback(async () => { + const selection = editor.selection; + if (!selection || selection.anchor.offset === selection.focus.offset) { + const node = Node.get(editor, path); + if (!Element.isElement(node)) return; + const lastChildIndex = node.children.length - 1; + const lastNode = node.children[lastChildIndex]; + if (!Text.isText(lastNode)) return; + const lastOffset = lastNode.text.length; + editor.selection = { + anchor: { + path: [...path, 0], + offset: 0, + }, + focus: { + path: [...path, lastChildIndex], + offset: lastOffset, + }, + }; + await helpers.Node.copyNode(editor); + editor.selection = { + anchor: { + path: [...path, 0], + offset: 0, + }, + focus: { + path: [...path, 0], + offset: 0, + }, + }; + } else { + helpers.Node.copyNode(editor); + } onDone(); }, [editor, path, onDone]);