diff --git a/public/copy_icon.png b/public/copy_icon.png new file mode 100644 index 0000000..6cfdbd5 Binary files /dev/null and b/public/copy_icon.png differ diff --git a/src/App.css b/src/App.css index 087cca4..b73bbed 100644 --- a/src/App.css +++ b/src/App.css @@ -34,4 +34,7 @@ tbody tr:nth-child(odd) { max-width: 20px; max-height: 20px; margin: 2px; -} \ No newline at end of file +} + +@-webkit-keyframes seesaw { from { transform: rotate(-0.05turn) } to { transform: rotate(0.05turn); } } +@keyframes seesaw { from { transform: rotate(-0.05turn) } to { transform: rotate(0.05turn); } } diff --git a/src/CopyButton.jsx b/src/CopyButton.jsx new file mode 100644 index 0000000..fe8c1be --- /dev/null +++ b/src/CopyButton.jsx @@ -0,0 +1,53 @@ +import React from "react"; + +export default function CopyButton({ source }) { + + let [shaking, setShaking] = React.useState(false); + + function copyTextToClipboard() { + var textArea = document.createElement("textarea"); + // Place in the top-left corner of screen regardless of scroll position. + textArea.style.position = "fixed"; + + textArea.value = source; + + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + + var successful; + try { + successful = document.execCommand("copy"); + } catch (err) { + console.log("Oops, unable to copy"); + } + document.body.removeChild(textArea); + + if (successful) { + // show user that copying happened - update text on element (e.g. button) + setShaking(true); + setTimeout(() => { + // reset after 1 second + setShaking(false); + }, 1000); + } else { + console.log("Copying failed"); + } + } + + let buttonStyle = { + background: "transparent", + border: "none", + padding: "0 2px" + }; + if (shaking) { + // NB: seesaw is defined in global App.css + buttonStyle["animation"] = "0.1s linear 0s infinite alternate seesaw"; + } + + return ( + + ); +} diff --git a/src/ImageItem.jsx b/src/ImageItem.jsx index 9d6e127..e62c87c 100644 --- a/src/ImageItem.jsx +++ b/src/ImageItem.jsx @@ -2,6 +2,7 @@ import React from "react"; import Viewer from "./Viewer"; import OpenWith from "./OpenWith"; +import CopyButton from "./CopyButton"; import { loadOmeroMultiscales, open, getNgffAxes } from "./util"; // DeckGL react component @@ -103,6 +104,7 @@ export default function ImageItem({ source }) { {imgInfo.version} {source} + {sizes} diff --git a/src/OpenWith.jsx b/src/OpenWith.jsx index c6afbdf..54a3829 100644 --- a/src/OpenWith.jsx +++ b/src/OpenWith.jsx @@ -3,17 +3,15 @@ import React from "react"; import openwithJson from "../public/openwith.json"; export default function OpenWith({ source }) { - let viewers = openwithJson.viewers; - - return (
- { - viewers.map(viewer => ( + return ( + + {viewers.map((viewer) => ( - + - )) - } -
); + ))} + + ); }