fix: Add copy to clipboard option for capture (#5720)
* Add copy to clipboard option for capture * PR comments * Added toast feedback and updated labels --------- Co-authored-by: Dan Rukas <dan.rukas@gmail.com>
This commit is contained in:
parent
c85ca0e177
commit
53c67f3612
@ -1,5 +1,5 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { ImageModal, FooterAction } from '@ohif/ui-next';
|
import { ImageModal, FooterAction, toast } from '@ohif/ui-next';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
const MAX_TEXTURE_SIZE = 10000;
|
const MAX_TEXTURE_SIZE = 10000;
|
||||||
const DEFAULT_FILENAME = 'image';
|
const DEFAULT_FILENAME = 'image';
|
||||||
@ -16,6 +16,7 @@ interface ViewportDownloadFormNewProps {
|
|||||||
onEnableViewport: (element: HTMLElement) => void;
|
onEnableViewport: (element: HTMLElement) => void;
|
||||||
onDisableViewport: () => void;
|
onDisableViewport: () => void;
|
||||||
onDownload: (filename: string, fileType: string) => void;
|
onDownload: (filename: string, fileType: string) => void;
|
||||||
|
onCopyToClipboard: () => void;
|
||||||
warningState: { enabled: boolean; value: string };
|
warningState: { enabled: boolean; value: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ function ViewportDownloadFormNew({
|
|||||||
onEnableViewport,
|
onEnableViewport,
|
||||||
onDisableViewport,
|
onDisableViewport,
|
||||||
onDownload,
|
onDownload,
|
||||||
|
onCopyToClipboard,
|
||||||
}: ViewportDownloadFormNewProps) {
|
}: ViewportDownloadFormNewProps) {
|
||||||
const [viewportElement, setViewportElement] = useState<HTMLElement | null>(null);
|
const [viewportElement, setViewportElement] = useState<HTMLElement | null>(null);
|
||||||
const [showWarningMessage, setShowWarningMessage] = useState(true);
|
const [showWarningMessage, setShowWarningMessage] = useState(true);
|
||||||
@ -138,13 +140,27 @@ function ViewportDownloadFormNew({
|
|||||||
<FooterAction.Secondary onClick={onClose}>
|
<FooterAction.Secondary onClick={onClose}>
|
||||||
{t('Common:Cancel')}
|
{t('Common:Cancel')}
|
||||||
</FooterAction.Secondary>
|
</FooterAction.Secondary>
|
||||||
|
<FooterAction.Secondary
|
||||||
|
onClick={async () => {
|
||||||
|
try {
|
||||||
|
await onCopyToClipboard();
|
||||||
|
toast.success(t('Image copied to clipboard'));
|
||||||
|
onClose();
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(t('Failed to copy image to clipboard'));
|
||||||
|
console.error('Failed to copy to clipboard:', error);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Copy to Clipboard')}
|
||||||
|
</FooterAction.Secondary>
|
||||||
<FooterAction.Primary
|
<FooterAction.Primary
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onDownload(filename || DEFAULT_FILENAME, fileType);
|
onDownload(filename || DEFAULT_FILENAME, fileType);
|
||||||
onClose();
|
onClose();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('Common:Save')}
|
{t('Save Image')}
|
||||||
</FooterAction.Primary>
|
</FooterAction.Primary>
|
||||||
</FooterAction.Right>
|
</FooterAction.Right>
|
||||||
</FooterAction>
|
</FooterAction>
|
||||||
|
|||||||
@ -118,16 +118,20 @@ const CornerstoneViewportDownloadForm = ({
|
|||||||
const downloadViewport = renderingEngine.getViewport(VIEWPORT_ID);
|
const downloadViewport = renderingEngine.getViewport(VIEWPORT_ID);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const properties = viewport.getProperties();
|
||||||
if (downloadViewport instanceof StackViewport) {
|
if (downloadViewport instanceof StackViewport) {
|
||||||
const imageId = viewport.getCurrentImageId();
|
const imageId = viewport.getCurrentImageId();
|
||||||
const properties = viewport.getProperties();
|
|
||||||
|
|
||||||
await downloadViewport.setStack([imageId]);
|
await downloadViewport.setStack([imageId]);
|
||||||
downloadViewport.setProperties(properties);
|
downloadViewport.setProperties(properties);
|
||||||
} else if (downloadViewport instanceof BaseVolumeViewport) {
|
} else if (downloadViewport instanceof BaseVolumeViewport) {
|
||||||
const volumeIds = viewport.getAllVolumeIds();
|
const volumeIds = viewport.getAllVolumeIds();
|
||||||
downloadViewport.setVolumes([{ volumeId: volumeIds[0] }]);
|
await downloadViewport.setVolumes([{ volumeId: volumeIds[0] }]);
|
||||||
}
|
}
|
||||||
|
downloadViewport.setProperties(properties);
|
||||||
|
const viewRef = viewport.getViewReference();
|
||||||
|
downloadViewport.setViewReference(viewRef);
|
||||||
|
downloadViewport.render();
|
||||||
|
|
||||||
if (segmentationRepresentations?.length) {
|
if (segmentationRepresentations?.length) {
|
||||||
segmentationRepresentations.forEach(segRepresentation => {
|
segmentationRepresentations.forEach(segRepresentation => {
|
||||||
@ -230,6 +234,48 @@ const CornerstoneViewportDownloadForm = ({
|
|||||||
downloadUrl(canvas.toDataURL(`image/${fileType}`, 1.0), { filename });
|
downloadUrl(canvas.toDataURL(`image/${fileType}`, 1.0), { filename });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCopyToClipboard = async () => {
|
||||||
|
const divForDownloadViewport = document.querySelector(
|
||||||
|
`div[data-viewport-uid="${VIEWPORT_ID}"]`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!divForDownloadViewport) {
|
||||||
|
console.debug('No viewport found for copy');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const canvas = await html2canvas(divForDownloadViewport as HTMLElement);
|
||||||
|
|
||||||
|
// Clipboard API only supports PNG format in most browsers
|
||||||
|
const blob = await new Promise<Blob>((resolve, reject) => {
|
||||||
|
canvas.toBlob(
|
||||||
|
blob => {
|
||||||
|
if (blob) {
|
||||||
|
resolve(blob);
|
||||||
|
} else {
|
||||||
|
reject(new Error('Failed to create blob from canvas'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'image/png',
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Copy to clipboard using the Clipboard API
|
||||||
|
await navigator.clipboard.write([
|
||||||
|
new ClipboardItem({
|
||||||
|
'image/png': blob,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
console.log('Image copied to clipboard successfully');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to copy image to clipboard:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const ViewportDownloadFormNew = customizationService.getCustomization(
|
const ViewportDownloadFormNew = customizationService.getCustomization(
|
||||||
'ohif.captureViewportModal'
|
'ohif.captureViewportModal'
|
||||||
);
|
);
|
||||||
@ -247,6 +293,7 @@ const CornerstoneViewportDownloadForm = ({
|
|||||||
onEnableViewport={handleEnableViewport}
|
onEnableViewport={handleEnableViewport}
|
||||||
onDisableViewport={handleDisableViewport}
|
onDisableViewport={handleDisableViewport}
|
||||||
onDownload={handleDownload}
|
onDownload={handleDownload}
|
||||||
|
onCopyToClipboard={handleCopyToClipboard}
|
||||||
warningState={warningState}
|
warningState={warningState}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
{
|
{
|
||||||
|
"Copy to Clipboard": "Copy Image",
|
||||||
"File name": "File name",
|
"File name": "File name",
|
||||||
"Image size": "Image size",
|
"Image size": "Image size",
|
||||||
"Image size in pixels": "Image size in pixels",
|
"Image size in pixels": "Image size in pixels",
|
||||||
"Include annotations": "Include annotations",
|
"Include annotations": "Include annotations",
|
||||||
"Include warning message": "Include warning message",
|
"Include warning message": "Include warning message",
|
||||||
"Width": "Width",
|
"Width": "Width",
|
||||||
"Height": "Height"
|
"Height": "Height",
|
||||||
|
"Save Image": "Save Image",
|
||||||
|
"Image copied to clipboard": "Image copied to clipboard",
|
||||||
|
"Failed to copy image to clipboard": "Failed to copy image to clipboard"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"Capture": "Capture",
|
"Capture": "Capture",
|
||||||
|
"Copy to Clipboard": "Copier dans le presse-papiers",
|
||||||
"Cancel": "$t(Common:Cancel)",
|
"Cancel": "$t(Common:Cancel)",
|
||||||
"Download": "$t(Common:Download)",
|
"Download": "$t(Common:Download)",
|
||||||
"Image size": "Taille de l'image",
|
"Image size": "Taille de l'image",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user