diff --git a/packages/client/src/features/copy-paste/copy-paste-context-menu.ts b/packages/client/src/features/copy-paste/copy-paste-context-menu.ts index 9b3afd78..15822375 100644 --- a/packages/client/src/features/copy-paste/copy-paste-context-menu.ts +++ b/packages/client/src/features/copy-paste/copy-paste-context-menu.ts @@ -49,21 +49,31 @@ export class InvokeCopyPasteActionHandler implements IActionHandler { handle(action: Action): void { switch (action.kind) { case InvokeCopyAction.KIND: - document.execCommand('copy'); + if (supportsCopy()) { + document.execCommand('copy'); + } else { + this.notifyUserToUseShortcut('copy'); + } break; case InvokePasteAction.KIND: - // in a browser without additional permission we can't invoke the paste command - // the user needs to invoke it from the browser, so notify the user about it - this.notifyUserToUseShortcut(); + if (supportsPaste()) { + document.execCommand('paste'); + } else { + this.notifyUserToUseShortcut('paste'); + } break; case InvokeCutAction.KIND: - document.execCommand('cut'); + if (supportsCut()) { + document.execCommand('cut'); + } else { + this.notifyUserToUseShortcut('cut'); + } break; } } - protected notifyUserToUseShortcut(): void { - const message = 'Please use the browser\'s paste command or shortcut.'; + protected notifyUserToUseShortcut(operation: string): void { + const message = `Please use the browser's ${operation} command or shortcut.`; const timeout = 10000; const severity = 'WARNING'; this.dispatcher.dispatchAll([ @@ -105,3 +115,24 @@ export class CopyPasteContextMenuItemProvider implements IContextMenuItemProvide }; } } + +export function supportsCopy(): boolean { + return isNative() || document.queryCommandSupported('copy'); +} + +export function supportsCut(): boolean { + return isNative() || document.queryCommandSupported('cut'); +} + +export function supportsPaste(): boolean { + const isChrome = (userAgent().indexOf('Chrome') >= 0); + return isNative() || (!isChrome && document.queryCommandSupported('paste')); +} + +export function isNative(): boolean { + return typeof (window as any).process !== 'undefined'; +} + +function userAgent(): string { + return typeof navigator !== 'undefined' ? navigator.userAgent : ''; +} diff --git a/packages/client/src/features/copy-paste/copy-paste-handler.ts b/packages/client/src/features/copy-paste/copy-paste-handler.ts index b244259b..bc455559 100644 --- a/packages/client/src/features/copy-paste/copy-paste-handler.ts +++ b/packages/client/src/features/copy-paste/copy-paste-handler.ts @@ -91,7 +91,7 @@ function getClipboardIdFromDataTransfer(dataTransfer: DataTransfer): string | un return isClipboardId(jsonObject) ? jsonObject.clipboardId : undefined; } -const CLIPBOARD_DATA_FORMAT = 'application/json'; +const CLIPBOARD_DATA_FORMAT = 'text/plain'; @injectable() export class ServerCopyPasteHandler implements ICopyPasteHandler { @@ -126,7 +126,7 @@ export class ServerCopyPasteHandler implements ICopyPasteHandler { } handlePaste(event: ClipboardEvent): void { - if (event.clipboardData) { + if (event.clipboardData && this.shouldPaste(event)) { const clipboardId = getClipboardIdFromDataTransfer(event.clipboardData); const clipboardData = this.clipboadService.get(clipboardId); if (clipboardData) { @@ -137,8 +137,16 @@ export class ServerCopyPasteHandler implements ICopyPasteHandler { } protected shouldCopy(_event: ClipboardEvent): boolean | null { - return this.editorContext.get().selectedElementIds.length > 0 && document.activeElement instanceof SVGElement - && document.activeElement.parentElement && document.activeElement.parentElement.id === this.viewerOptions.baseDiv; + return this.editorContext.get().selectedElementIds.length > 0 && this.isDiagramActive(); } + protected shouldPaste(_event: ClipboardEvent): boolean | null { + return this.isDiagramActive(); + } + + private isDiagramActive(): boolean | null { + return document.activeElement instanceof SVGElement + && document.activeElement.parentElement + && document.activeElement.parentElement.id === this.viewerOptions.baseDiv; + } }