diff --git a/components/content/RefseqDb.vue b/components/content/RefseqDb.vue index dc286eec6c145bcd51f0707393f3a2f721e4acce..df52203508334192b04f2dc5d4b9d94be15fcf87 100644 --- a/components/content/RefseqDb.vue +++ b/components/content/RefseqDb.vue @@ -4,7 +4,8 @@ import PlotFigure from "~/components/PlotFigure"; import { useDisplay } from "vuetify"; import type { SortItem } from "@/components/ServerDbTable.vue" import { ServerDbTable } from "#components" - +import { useSerialize } from "@/composables/useSerialize"; +import type { ComponentPublicInstance } from 'vue' const sortBy: Ref<SortItem[]> = ref([{ key: 'type', order: "asc" }]) const itemValue = ref("id"); @@ -12,6 +13,7 @@ const { width } = useDisplay(); const scaleTransform: Ref<string[]> = ref([]) +const { serialize } = useSerialize() const facets = ref([ "replicon", @@ -32,6 +34,8 @@ const availableTaxo: Ref<string[]> = ref([ "phylum", "Superkingdom" ]); + +const scaleTypes = ref<string[]>(['linear', 'sqrt', 'log', 'symlog']) const selectedTaxoRank = ref("phylum"); const headers = ref([ @@ -121,22 +125,25 @@ const dataTableServerProps = computed(() => { const defaultBarPlotOptions = computed(() => { return { - x: { label: null, tickRotate: 45, ticks: 10 }, - y: { grid: true, type: scaleType.value }, - color: { legend: true }, - width: computedWidth.value, + x: { label: null, tickRotate: 45 }, + y: { grid: true, clamp: true }, + // height: plotHeight.value + 100, } }) + +// system distri + const computedSystemDistribution = computed(() => { if (toValue(msResult)?.facetDistribution?.type) { - return Object.entries(toValue(msResult).facetDistribution.type).map(([key, value]) => { - return { - type: key, - count: value - } - }).sort() + return Object.entries(toValue(msResult).facetDistribution.type) + .map(([key, value]) => { + return { + type: key, + count: value + } + }).sort() } else { return [] } }) @@ -144,13 +151,15 @@ const computedDistriSystemOptions = computed(() => { return { ...defaultBarPlotOptions.value, marginBottom: 100, + y: { ...defaultBarPlotOptions.value.y, type: toValue(scaleType), label: "Count" }, + x: { ...defaultBarPlotOptions.value.x, label: "Systems" }, + width: computedWidth.value, marks: [ // Plot.frame(), Plot.barY( toValue(computedSystemDistribution), { y: "count", x: 'type', tip: true, - // fill: "#6750a4", sort: { x: "-y" }, }, @@ -158,6 +167,9 @@ const computedDistriSystemOptions = computed(() => { ], }; }); + + +// Taxo distri const computedTaxonomyDistribution = computed(() => { if (toValue(msResult)?.facetDistribution?.[selectedTaxoRank.value]) { return Object.entries(toValue(msResult).facetDistribution[selectedTaxoRank.value]).map(([key, value]) => { @@ -174,6 +186,9 @@ const computedDistriTaxoOptions = computed(() => { return { ...defaultBarPlotOptions.value, marginBottom: 100, + x: { ...defaultBarPlotOptions.value.x, label: selectedTaxoRank.value }, + y: { ...defaultBarPlotOptions.value.y, type: toValue(scaleType), label: "Count" }, + width: computedWidth.value, marks: [ Plot.barY( toValue(computedTaxonomyDistribution), @@ -212,8 +227,8 @@ const binPlotOptions = ref({ marginBottom: 200, padding: 0, grid: true, - x: { tickRotate: 90, tip: true, }, - + x: { tickRotate: 90, tip: true, label: "Systems" }, + // y: { tickFormat: 's' }, color: { scheme: "turbo", legend: true }, }) @@ -225,7 +240,11 @@ const binPlotDataOptions = computed(() => { color: { ...binPlotOptions.value.color, - type: scaleType.value + type: scaleType.value, + tickFormat: '~s', + ticks: scaleType.value === 'symlog' ? 3 : 5, + // width: 350 + }, // fy: { domain: groupSortDomain.value }, marks: [ @@ -236,6 +255,12 @@ const binPlotDataOptions = computed(() => { }) const scaleType = ref("linear") +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) +} </script> <template> @@ -253,21 +278,38 @@ const scaleType = ref("linear") <v-btn icon="md:grid_view" value="grid"></v-btn> <v-btn icon="md:view_agenda" value="fullwidth"></v-btn> </v-btn-toggle> - <v-select v-model="scaleType" class="mx-2" density="compact" - :items="['linear', 'sqrt', 'symlog']" label="Scale Type" hide-details="auto"></v-select> + <v-select v-model="scaleType" class="mx-2" density="compact" :items="scaleTypes" + label="Scale Type" hide-details="auto"></v-select> <v-select v-model="selectedTaxoRank" :items="availableTaxo" density="compact" label="Select taxonomic rank" hide-details="auto" class="mx-2"></v-select> </v-toolbar> <v-row align="start"> <v-col :cols="fullWidth ? 12 : 6"> - <PlotFigure :options="unref(computedDistriSystemOptions)" defer></PlotFigure> - + <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-toolbar> + + <PlotFigure ref="systemsDistributionPlot" + :options="unref(computedDistriSystemOptions)" defer></PlotFigure> + </v-card> </v-col> <v-col :cols="fullWidth ? 12 : 6"> - - <PlotFigure defer :options="unref(computedDistriTaxoOptions)"></PlotFigure> - + <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-toolbar> + <PlotFigure ref="taxonomicDistributionPlot" defer + :options="unref(computedDistriTaxoOptions)"></PlotFigure> + </v-card> </v-col> </v-row> </v-card> @@ -285,19 +327,26 @@ const scaleType = ref("linear") <v-expansion-panel-text> <v-card v-if="pendingAllHits === false" flat color="transparent"> <v-toolbar flat color="transparent" density="compact"> - <v-select v-model="scaleType" class="mx-2" density="compact" - :items="['linear', 'sqrt', 'symlog']" label="Scale Type" hide-details="auto"></v-select> + <v-select v-model="scaleType" class="mx-2" density="compact" :items="scaleTypes" + label="Scale Type" hide-details="auto"></v-select> <v-select v-model="selectedTaxoRank" :items="availableTaxo" density="compact" label="Select taxonomic rank" hide-details="auto" class="mx-2"></v-select> </v-toolbar> - <PlotFigure v-if="toValue(binPlotDataOptions) !== null" :options="unref(binPlotDataOptions)" - defer> - </PlotFigure> + <v-card 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-toolbar> + <PlotFigure ref="heatmapPlot" v-if="toValue(binPlotDataOptions) !== null" + :options="unref(binPlotDataOptions)" defer> + </PlotFigure> + </v-card> </v-card> <v-card v-else flat color="transparent"> - <v-skeleton-loader type="card" :loading="pendingAllHits" - height="400"></v-skeleton-loader> + <v-skeleton-loader type="card" :loading="pendingAllHits" height="400"></v-skeleton-loader> </v-card> </v-expansion-panel-text> </v-expansion-panel> diff --git a/composables/useCsvDownload.ts b/composables/useCsvDownload.ts index a84e6ad90db80aaf3a27d3f9c0acef621fc25b26..ffbf6e7b517664010b224b8202b135286a9d072c 100644 --- a/composables/useCsvDownload.ts +++ b/composables/useCsvDownload.ts @@ -1,5 +1,7 @@ import Papa from 'papaparse'; // import { saveAs } from "file-saver"; +import { useDownloadBlob } from './useDownloadBlob'; +const { download } = useDownloadBlob() export function useCsvDownload( rawData: MaybeRef<Record<string, any>>, @@ -29,12 +31,7 @@ export function useCsvDownload( }) const csvContent = Papa.unparse(toValue(data), { columns: toValue(columns) }); blob.value = new Blob([csvContent], { type: "text/csv" }); - var a = document.createElement("a"); - a.href = URL.createObjectURL(blob.value); - a.download = filename.value; - a.click(); - URL.revokeObjectURL(a.href); - + download(blob, filename) } return { data, filename } } diff --git a/composables/useDownloadBlob.ts b/composables/useDownloadBlob.ts new file mode 100644 index 0000000000000000000000000000000000000000..7ae7b07beab9547b5e2e9d6ae992271f03a01a1e --- /dev/null +++ b/composables/useDownloadBlob.ts @@ -0,0 +1,14 @@ +export function useDownloadBlob() { + function download(blob: MaybeRef<Blob>, filename: MaybeRef<string>) { + const toValueBlob = toValue(blob) + const toValueFilename = toValue(filename) + var a = document.createElement("a"); + a.href = URL.createObjectURL(toValueBlob); + a.download = toValueFilename; + a.click(); + URL.revokeObjectURL(a.href); + } + + return { download } + +} \ No newline at end of file diff --git a/composables/useSerialize.ts b/composables/useSerialize.ts new file mode 100644 index 0000000000000000000000000000000000000000..b24ce7a0db1139429b8d2d4545bed9ed384d750a --- /dev/null +++ b/composables/useSerialize.ts @@ -0,0 +1,36 @@ +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() + + function serialize(compo: MaybeRef<ComponentPublicInstance | null>, filename: MaybeRef<string>) { + console.log(compo) + 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); + while (walker.nextNode()) { + for (const attr of walker.currentNode.attributes) { + if (attr.value.includes(fragment)) { + attr.value = attr.value.replace(fragment, "#"); + } + } + } + clonedSvg.setAttributeNS(xmlns.value, "xmlns", svgns.value); + clonedSvg.setAttributeNS(xmlns.value, "xmlns:xlink", xlinkns.value); + const serializer = new window.XMLSerializer; + const string = serializer.serializeToString(clonedSvg); + blob.value = new Blob([string], { type: "image/svg+xml" }); + download(blob, filename) + } + } + return { serialize } +} \ No newline at end of file