<script setup lang="ts"> import * as Plot from "@observablehq/plot"; import PlotFigure from "~/components/PlotFigure"; import { useDisplay } from "vuetify"; import type { SortItem } from "@/components/ServerDbTable.vue" import { ServerDbTable } from "#components" const sortBy: Ref<SortItem[]> = ref([{ key: 'type', order: "asc" }]) const itemValue = ref("id"); const { width } = useDisplay(); const scaleTransform: Ref<string[]> = ref([]) const facets = ref([ "replicon", "type", "subtype", "Superkingdom", "phylum", "order", "family", "genus", "species", ]) const availableTaxo: Ref<string[]> = ref([ "species", "genus", "family", "order", "phylum", "Superkingdom" ]); const selectedTaxoRank = ref("phylum"); const headers = ref([ { title: "Replicon", key: "replicon" }, { title: "Type", key: "type", }, { title: "Subtype", key: "subtype", }, { title: "Accessions", key: "accession_in_sys", sortable: false } ]) const fullWidth = computed(() => { return layoutPlot.value === 'fullwidth' }) const computedHeaders = computed(() => { return [...headers.value, ...availableTaxo.value.map(taxo => { return { title: capitalize(taxo), key: taxo } })] }) const { result: msResult } = useMeiliSearch('refseq') const computedWidth = computed(() => { const currentWidth = fullWidth.value ? width.value : width.value / 2 return Math.max(currentWidth, 550); }); const allHits = ref([]) onMounted(async () => { const params = { facets: ["*"], filter: [], sort: ["type:asc"], limit: 500000 } await getAllHits({ index: "refseq", params, query: "" }) }) async function getAllHits(params) { if (params.index === 'refseq') { console.log(params.index) const { data, error } = await useAsyncMeiliSearch(params) console.log(error.value) console.log(data.value) allHits.value = data.value } } const plotHeight = computed(() => { return computedWidth.value / 3; // return 500 }); const defaultDataTableServerProps = ref({ showExpand: false }) const dataTableServerProps = computed(() => { return { ...defaultDataTableServerProps.value, headers: computedHeaders.value, itemValue: itemValue.value } }) const defaultBarPlotOptions = computed(() => { return { x: { label: null, tickRotate: 45, ticks: 10 }, y: { grid: true, type: scaleType.value }, color: { legend: true }, width: computedWidth.value, height: plotHeight.value + 100, } }) const computedSystemDistribution = computed(() => { if (toValue(msResult)?.facetDistribution?.type) { return Object.entries(toValue(msResult).facetDistribution.type).map(([key, value]) => { return { type: key, count: value } }).sort() } else { return [] } }) const computedDistriSystemOptions = computed(() => { return { ...defaultBarPlotOptions.value, marginBottom: 100, marks: [ // Plot.frame(), Plot.barY( toValue(computedSystemDistribution), { y: "count", x: 'type', tip: true, fill: "#6750a4", sort: { x: "-y" }, }, ), ], }; }); const computedTaxonomyDistribution = computed(() => { if (toValue(msResult)?.facetDistribution?.[selectedTaxoRank.value]) { return Object.entries(toValue(msResult).facetDistribution[selectedTaxoRank.value]).map(([key, value]) => { return { [selectedTaxoRank.value]: key, count: value } }).sort() } else { return [] } }) const computedDistriTaxoOptions = computed(() => { return { ...defaultBarPlotOptions.value, marginBottom: 100, marks: [ Plot.barY( toValue(computedTaxonomyDistribution), { y: "count", x: selectedTaxoRank.value, tip: true, fill: "#6750a4", sort: { x: "-y" }, } ), ], }; }); function capitalize(name: string) { const [first, ...rest] = name return first.toUpperCase() + rest.join('').toLowerCase(); } function namesToCollapsibleChips(names: string[]) { return names.filter((it) => it !== "").map(it => ({ title: it })) } function namesToAccessionChips(names: string[]) { return namesToCollapsibleChips(names).map(it => { return { ...it, // href: new URL(it.title, "http://toto.pasteur.cloud").toString() } }) } const systemPanel: Ref<number> = ref(["table"]) const layoutPlot: Ref<string[]> = ref("grid") const binPlotOptions = ref({ marginLeft: 150, marginBottom: 200, padding: 0, width: 1920, grid: true, x: { tickRotate: 90, tip: true, }, color: { scheme: "turbo", legend: true }, }) const binPlotDataOptions = computed(() => { return allHits.value?.hits?.length > 0 ? { ...binPlotOptions.value, color: { ...binPlotOptions.value.color, type: scaleType.value }, // fy: { domain: groupSortDomain.value }, marks: [ Plot.cell(allHits.value?.hits ?? [], Plot.group({ fill: "count" }, { x: "type", y: selectedTaxoRank.value, tip: true, inset: 0.5, sort: { y: "fill" } })), ] } : null }) const scaleType = ref("linear") </script> <template> <v-card flat class="mb-2" color="transparent"> <v-card color="transparent" flat> <v-expansion-panels v-model="systemPanel" class="my-2" density="compact" multiple> <v-expansion-panel elevation="3" value="barplot"> <v-expansion-panel-title color="grey-lighten-4"><v-icon color="primary" class="mr-2">mdi-chart-bar</v-icon>Systems - Taxonomic</v-expansion-panel-title> <v-expansion-panel-text> <v-card flat color="transparent"> <v-toolbar flat color="transparent" density="compact"> <v-btn-toggle v-model="layoutPlot" density="compact" rounded="false" variant="text" color="primary" mandatory class="mx-2"> <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="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-col> <v-col :cols="fullWidth ? 12 : 6"> <PlotFigure defer :options="unref(computedDistriTaxoOptions)"></PlotFigure> </v-col> </v-row> </v-card> </v-expansion-panel-text> </v-expansion-panel> <v-expansion-panel elevation="3" value="matrix"> <v-expansion-panel-title color="grey-lighten-4"><v-icon color="primary" class="mr-2">mdi-data-matrix</v-icon>Heatmap</v-expansion-panel-title> <v-expansion-panel-text> <v-card 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="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> </v-expansion-panel-text> </v-expansion-panel> </v-expansion-panels> <ServerDbTable title="RefSeq" db="refseq" :sortBy="sortBy" :facets="facets" :data-table-server-props="dataTableServerProps" @refresh:search="getAllHits"> <template #top> <v-toolbar><v-toolbar-title class="text-capitalize"> RefSeq </v-toolbar-title><v-spacer></v-spacer> </v-toolbar> </template> <template #[`item.accession_in_sys`]="{ item }"> <CollapsibleChips :items="namesToAccessionChips(item.accession_in_sys)"> </CollapsibleChips> </template> </ServerDbTable> </v-card> </v-card> </template>