diff --git a/components/content/RefseqDb.vue b/components/content/RefseqDb.vue index df52203508334192b04f2dc5d4b9d94be15fcf87..da1cea371a9f7c0713775b46ae109a45ef43a5ee 100644 --- a/components/content/RefseqDb.vue +++ b/components/content/RefseqDb.vue @@ -5,6 +5,10 @@ import { useDisplay } from "vuetify"; import type { SortItem } from "@/components/ServerDbTable.vue" import { ServerDbTable } from "#components" import { useSerialize } from "@/composables/useSerialize"; +import { useRasterize } from "@/composables/useRasterize"; +import { useDownloadBlob } from '@/composables/useDownloadBlob'; + + import type { ComponentPublicInstance } from 'vue' const sortBy: Ref<SortItem[]> = ref([{ key: 'type', order: "asc" }]) @@ -14,6 +18,8 @@ const scaleTransform: Ref<string[]> = ref([]) const { serialize } = useSerialize() +const { rasterize } = useRasterize() +const { download } = useDownloadBlob() const facets = ref([ "replicon", @@ -259,8 +265,19 @@ const systemsDistributionPlot = ref<ComponentPublicInstance | null>(null) const taxonomicDistributionPlot = ref<ComponentPublicInstance | null>(null) const heatmapPlot = ref<ComponentPublicInstance | null>(null) function downloadSvg(component: ComponentPublicInstance | null, filename: string) { - serialize(toValue(component), filename) + const blob = toValue(serialize(toValue(component))) + if (blob !== undefined) { + download(blob, filename) + } } + +async function downloadPng(component: ComponentPublicInstance | null, filename: string) { + const blob = await rasterize(toValue(component), filename)?.then((blob) => { + download(blob, filename) + }) + +} + </script> <template> @@ -289,9 +306,27 @@ function downloadSvg(component: ComponentPublicInstance | null, filename: string <v-card variant="flat"> <v-toolbar density="compact" color="transparent"> <v-spacer></v-spacer> - <v-btn variant="text" size="x-small" - @click="downloadSvg(systemsDistributionPlot, 'systems-distribution.svg')">export - to svg</v-btn> + + <v-menu> + <template v-slot:activator="{ props }"> + <v-btn color="primary" prepend-icon="md:download" v-bind="props"> + export + </v-btn> + </template> + <v-list> + <v-list-item value="svg"> + <v-list-item-title + @click="downloadSvg(systemsDistributionPlot, 'df-systems-distribution.svg')">to + svg</v-list-item-title> + + </v-list-item> + <v-list-item value="png"> + <v-list-item-title + @click="downloadPng(systemsDistributionPlot, 'df-systems-distribution.png')">to + png</v-list-item-title> + </v-list-item> + </v-list> + </v-menu> </v-toolbar> <PlotFigure ref="systemsDistributionPlot" @@ -302,10 +337,26 @@ function downloadSvg(component: ComponentPublicInstance | null, filename: string <v-card variant="flat"> <v-toolbar density="compact" color="transparent"> <v-spacer></v-spacer> - <v-btn variant="text" size="x-small" - @click="downloadSvg(taxonomicDistributionPlot, 'taxonomic-distribution.svg')">export - to - svg</v-btn> + <v-menu> + <template v-slot:activator="{ props }"> + <v-btn color="primary" prepend-icon="md:download" v-bind="props"> + export + </v-btn> + </template> + <v-list> + <v-list-item value="svg"> + <v-list-item-title + @click="downloadSvg(taxonomicDistributionPlot, 'df-taxonomic-distribution.svg')">to + svg</v-list-item-title> + + </v-list-item> + <v-list-item value="png"> + <v-list-item-title + @click="downloadPng(taxonomicDistributionPlot, 'df-taxonomic-distribution.png')">to + png</v-list-item-title> + </v-list-item> + </v-list> + </v-menu> </v-toolbar> <PlotFigure ref="taxonomicDistributionPlot" defer :options="unref(computedDistriTaxoOptions)"></PlotFigure> @@ -333,15 +384,31 @@ function downloadSvg(component: ComponentPublicInstance | null, filename: string label="Select taxonomic rank" hide-details="auto" class="mx-2"></v-select> </v-toolbar> - <v-card variant="flat"> + <v-card v-if="toValue(binPlotDataOptions) !== null" variant="flat"> <v-toolbar density="compact" color="transparent"> <v-spacer></v-spacer> - <v-btn variant="text" size="x-small" - @click="downloadSvg(heatmapPlot, 'heatmap-systems-taxonomic.svg')">export to - svg</v-btn> + <v-menu> + <template v-slot:activator="{ props }"> + <v-btn color="primary" prepend-icon="md:download" v-bind="props"> + export + </v-btn> + </template> + <v-list> + <v-list-item value="svg"> + <v-list-item-title + @click="downloadSvg(heatmapPlot, 'df-heatmap-systems-taxonomy.svg')">to + svg</v-list-item-title> + + </v-list-item> + <!-- <v-list-item value="png"> + <v-list-item-title + @click="downloadPng(heatmapPlot, 'df-heatmap-systems-taxonomy.png')">to + png</v-list-item-title> + </v-list-item> --> + </v-list> + </v-menu> </v-toolbar> - <PlotFigure ref="heatmapPlot" v-if="toValue(binPlotDataOptions) !== null" - :options="unref(binPlotDataOptions)" defer> + <PlotFigure ref="heatmapPlot" :options="unref(binPlotDataOptions)" defer> </PlotFigure> </v-card> </v-card> diff --git a/composables/useRasterize.ts b/composables/useRasterize.ts new file mode 100644 index 0000000000000000000000000000000000000000..8fa51ebb5b8c693c30bd491b13f4bc0ea58ac5df --- /dev/null +++ b/composables/useRasterize.ts @@ -0,0 +1,40 @@ +import { useDownloadBlob } from './useDownloadBlob'; +import { useSerialize } from './useSerialize'; +const { serialize } = useSerialize() + +export function useRasterize() { + + function rasterize(component: MaybeRef<ComponentPublicInstance | null>, filename: MaybeRef<string>) { + const toValueCompo = toValue(component) + if (toValueCompo !== null) { + let resolve, reject; + const promise: Promise<Blob> = new Promise((y, n) => (resolve = y, reject = n)); + const image = new Image; + image.onerror = reject; + + const svg = toValueCompo.$el.firstChild + console.log(svg) + image.onload = () => { + const rect = svg.getBoundingClientRect(); + const canvas = document.createElement("canvas"); + canvas.width = rect.width + canvas.height = rect.height + const ctx = canvas.getContext("2d") + + + if (ctx !== null) { + ctx.drawImage(image, 0, 0, rect.width, rect.height); + ctx.canvas.toBlob(resolve); + } + } + const blob = toValue(serialize(component)) + if (blob !== undefined) { + image.src = URL.createObjectURL(blob); + } + return promise; + } + + } + return { rasterize } + +} \ No newline at end of file diff --git a/composables/useSerialize.ts b/composables/useSerialize.ts index b24ce7a0db1139429b8d2d4545bed9ed384d750a..0f0520e15d695b725185e7606893ea65cea59e51 100644 --- a/composables/useSerialize.ts +++ b/composables/useSerialize.ts @@ -1,19 +1,15 @@ -import { useDownloadBlob } from './useDownloadBlob'; -const { download } = useDownloadBlob() export function useSerialize() { const xmlns = ref("http://www.w3.org/2000/xmlns/"); const xlinkns = ref("http://www.w3.org/1999/xlink"); const svgns = ref("http://www.w3.org/2000/svg"); - const blob = ref() + const blob = ref<Blob>() - function serialize(compo: MaybeRef<ComponentPublicInstance | null>, filename: MaybeRef<string>) { - console.log(compo) + function serialize(compo: MaybeRef<ComponentPublicInstance | null>) { const toValueCompo = toValue(compo) if (toValueCompo !== null) { const svg = toValueCompo.$el.firstChild - console.log(svg) const clonedSvg = svg.cloneNode(true); const fragment = window.location.href + "#"; const walker = document.createTreeWalker(svg, NodeFilter.SHOW_ELEMENT); @@ -29,7 +25,7 @@ export function useSerialize() { const serializer = new window.XMLSerializer; const string = serializer.serializeToString(clonedSvg); blob.value = new Blob([string], { type: "image/svg+xml" }); - download(blob, filename) + return blob } } return { serialize }