diff --git a/taxonium_component/package.json b/taxonium_component/package.json
index 7e4da8b8..8476199c 100644
--- a/taxonium_component/package.json
+++ b/taxonium_component/package.json
@@ -26,6 +26,7 @@
},
"dependencies": {
"@fontsource/roboto": "^5.0.1",
+ "@headlessui/react": "^1.7.17",
"@jbrowse/core": "^2.5.0",
"@jbrowse/plugin-data-management": "^2.5.0",
"@jbrowse/react-linear-genome-view": "^2.5.0",
diff --git a/taxonium_component/src/Deck.jsx b/taxonium_component/src/Deck.jsx
index b1dc18b1..a28c83f3 100644
--- a/taxonium_component/src/Deck.jsx
+++ b/taxonium_component/src/Deck.jsx
@@ -178,7 +178,7 @@ function Deck({
[hoverDetails]
);
- const { layers, layerFilter, keyStuff } = useLayers({
+ const { layers, layerFilter, keyStuff,triggerSVGdownload } = useLayers({
data,
search,
viewState,
@@ -314,6 +314,7 @@ function Deck({
// we want this to intercept all mouse events
// so that we can prevent the default behavior
// of the browser
+ triggerSVGdownload={triggerSVGdownload}
zoomReset={zoomReset}
zoomIncrement={zoomIncrement}
diff --git a/taxonium_component/src/components/DeckButtons.jsx b/taxonium_component/src/components/DeckButtons.jsx
index b1fa96cb..ff39bcf2 100644
--- a/taxonium_component/src/components/DeckButtons.jsx
+++ b/taxonium_component/src/components/DeckButtons.jsx
@@ -11,22 +11,8 @@ import { TiZoom, TiCog } from "react-icons/ti";
import { MdOutlineZoomOutMap } from "react-icons/md";
import { ClipLoader } from "react-spinners";
-
-const TaxButton = ({ children, onClick, title }) => {
- return (
-
- );
-};
+import TaxButton from "./TaxButton";
+import SnapshotButton from "./SnapshotButton";
export const DeckButtons = ({
loading,
@@ -37,6 +23,7 @@ export const DeckButtons = ({
requestOpenSettings,
zoomReset,
settings,
+ triggerSVGdownload
}) => {
return (
-
{
- snapshot();
- }}
- title="Take screenshot"
- >
-
-
+
+
{
+ const [isOpen, setIsOpen] = useState(false);
+
+ function snapshot(option) {
+ if (option === 'pixels') {
+ pixelFunction();
+ } else if (option === 'SVG') {
+ svgFunction();
+ }
+ setIsOpen(false);
+ }
+
+ return (
+ <>
+ setIsOpen(true)}
+ title="Take screenshot"
+ >
+
+
+
+
+
+
+ >
+ );
+}
+
+export default SnapshotButton;
\ No newline at end of file
diff --git a/taxonium_component/src/components/TaxButton.jsx b/taxonium_component/src/components/TaxButton.jsx
new file mode 100644
index 00000000..13dffc48
--- /dev/null
+++ b/taxonium_component/src/components/TaxButton.jsx
@@ -0,0 +1,18 @@
+
+const TaxButton = ({ children, onClick, title }) => {
+ return (
+
+ );
+ };
+
+ export default TaxButton;
\ No newline at end of file
diff --git a/taxonium_component/src/hooks/useLayers.jsx b/taxonium_component/src/hooks/useLayers.jsx
index 658c3f3e..971f623f 100644
--- a/taxonium_component/src/hooks/useLayers.jsx
+++ b/taxonium_component/src/hooks/useLayers.jsx
@@ -8,6 +8,7 @@ import {
import { useMemo, useCallback } from "react";
import useTreenomeLayers from "./useTreenomeLayers";
+import getSVGfunction from "../utils/deckglToSvg";
const getKeyStuff = (getNodeColorField, colorByField, dataset, toRGB) => {
const counts = {};
@@ -570,10 +571,12 @@ const useLayers = ({
console.log("could not map layer spec for ", layer);
});
- console.log("processedLayers", processedLayers)
+ const {renderSVG, triggerSVGdownload} = getSVGfunction(layers,viewState);
+ window.renderSVG = renderSVG;
+
- return { layers: processedLayers, layerFilter, keyStuff };
+ return { layers: processedLayers, layerFilter, keyStuff, renderSVG, triggerSVGdownload };
};
export default useLayers;
diff --git a/taxonium_component/src/utils/deckglToSvg.js b/taxonium_component/src/utils/deckglToSvg.js
new file mode 100644
index 00000000..165b09eb
--- /dev/null
+++ b/taxonium_component/src/utils/deckglToSvg.js
@@ -0,0 +1,179 @@
+
+
+const getSVGfunction = (layers, viewState) => {
+
+const accessOrConstant = (accessor, node) => {
+ if (typeof accessor === "function") {
+ return accessor(node);
+ } else {
+ return accessor;
+ }
+ };
+ const normalise = (value,min,max)=>{
+
+ return (value-min)/(max-min)
+ }
+
+
+ const svgHeight = 600
+ const svgWidth = 600
+
+
+ const applyBounds = ( point) => {
+
+
+ const minY = viewState.min_y
+ const maxY = viewState.max_y
+ const minX = viewState.min_x
+ const maxX = viewState.max_x
+ const initial = point
+ const x = normalise(initial[0],minX,maxX)
+ const y = normalise(initial[1],minY,maxY)
+ return [x*svgWidth,y*svgHeight]
+
+ };
+
+
+ const getSVG = (layers) => {
+ if(!viewState.min_x){
+ window.alert("Please zoom in and out a little before SVG export")
+ return
+ }
+ let svgContent = "";
+ return svgContent;
+ };
+ console.log(viewState)
+ function renderSVG() {
+ const svgContent = getSVG(layers, viewState);
+ // Create a new SVG container element
+ const svgContainer = document.createElement("div");
+ svgContainer.id = "mySVG";
+ svgContainer.innerHTML = svgContent;
+
+ // Optional: set styles for the SVG container
+ svgContainer.style.width = '100%'; // or any other desired width
+ svgContainer.style.height = 'auto'; // maintains aspect ratio
+
+ // if there's already an SVG container, remove it
+ const existingSVG = document.getElementById("mySVG");
+ if (existingSVG) {
+ existingSVG.remove();
+ }
+ // Append the container to the bottom of the body
+ document.body.appendChild(svgContainer);
+ }
+
+ function triggerSVGdownload() {
+ const svgContent = getSVG(layers, viewState);
+ // Create a new blob object
+ const blob = new Blob([svgContent], { type: "image/svg+xml" });
+ // Create a link element, hide it, direct it towards the blob, and then 'click' it programatically
+ const a = document.createElement("a");
+ a.style.display = "none";
+ document.body.appendChild(a);
+ // Create a DOMString representing the blob and point the link element towards it
+ const url = window.URL.createObjectURL(blob);
+ a.href = url;
+ const date_and_time = new Date().toISOString().replace(/:/g, "-");
+ a.download = `taxonium-${date_and_time}.svg`;
+ //programatically click the link to trigger the download
+ a.click();
+ //release the reference to the file by revoking the Object URL
+ window.URL.revokeObjectURL(url);
+ }
+
+
+ return {renderSVG, triggerSVGdownload}
+}
+
+export default getSVGfunction
\ No newline at end of file
diff --git a/taxonium_component/yarn.lock b/taxonium_component/yarn.lock
index f3e712d0..7ca541ad 100644
--- a/taxonium_component/yarn.lock
+++ b/taxonium_component/yarn.lock
@@ -1529,6 +1529,13 @@
resolved "https://registry.yarnpkg.com/@gmod/vcf/-/vcf-5.0.10.tgz#6c2d7952b15f61642454be90119ea89fd3c227de"
integrity sha512-o7QuPcOeXlJpzwQaFmgojhNvJE4yB9fhrfVEDKpkDjV27pAqwMy89367vtXu4JfBFE9t4zZ6sQRkqYaJ+cIheg==
+"@headlessui/react@^1.7.17":
+ version "1.7.17"
+ resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.17.tgz#a0ec23af21b527c030967245fd99776aa7352bc6"
+ integrity sha512-4am+tzvkqDSSgiwrsEpGWqgGo9dz8qU5M3znCkC4PgkpY4HcCZzEDEvozltGGGHIKl9jbXbZPSH5TWn4sWJdow==
+ dependencies:
+ client-only "^0.0.1"
+
"@humanwhocodes/config-array@^0.11.8":
version "0.11.8"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9"
@@ -4659,6 +4666,11 @@ cli-width@^4.0.0:
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.0.0.tgz#a5622f6a3b0a9e3e711a25f099bf2399f608caf6"
integrity sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw==
+client-only@^0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
+ integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
+
cliui@^7.0.2:
version "7.0.4"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"