Skip to content

Commit a1b55b2

Browse files
authored
Merge pull request #1197 from monarch-initiative/issue-1141-publications
Display Latest Publications First on Publications Page for monarchinitiative.org
2 parents f63ab53 + 7542c92 commit a1b55b2

File tree

1 file changed

+200
-80
lines changed

1 file changed

+200
-80
lines changed

frontend/src/pages/aboutV2/PagePublications.vue

Lines changed: 200 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,19 @@
55
title="Monarch Publications"
66
class="publications"
77
/>
8-
<AppSection>
8+
9+
<!-- Top metadata section: short description + key metrics -->
10+
<AppSection width="big">
911
<p class="metadata">
10-
This list includes papers by the Monarch Team that were foundational to
11-
the current Monarch work. The graph below shows the number of citations to
12-
Monarch papers over time.
12+
The Monarch Initiative maintains a rich and foundational body of
13+
publications that trace back to its early days, underscoring how these
14+
works have been pivotal in shaping the fields of variant interpretation,
15+
knowledge graphs, and disease gene discovery. Committed to open science,
16+
Monarch shares much of this work as preprints or in open-access venues,
17+
ensuring broad, transparent, and lasting accessibility. Use this page to
18+
explore Monarch’s full body of publications.
1319
</p>
1420

15-
<Apex
16-
class="chart"
17-
type="bar"
18-
:options="options"
19-
:series="[citesPerYear]"
20-
height="300px"
21-
/>
22-
2321
<AppGallery>
2422
<p v-for="(item, index) in metadata" :key="index" class="metadata">
2523
<strong>{{ item.name }}</strong>
@@ -29,60 +27,94 @@
2927
</AppGallery>
3028
</AppSection>
3129

32-
<AppSection>
33-
<AppHeading>Years</AppHeading>
34-
<!-- row of links to year sections -->
35-
<p>
36-
<template
37-
v-for="(group, index) in publications.publications"
38-
:key="index"
39-
>
40-
<AppLink :to="'#year-' + group.year" :replace="true">
41-
{{ group.year }}</AppLink
30+
<!-- Publications grouped by year with a simple tab UI -->
31+
<AppSection width="big">
32+
<AppHeading>Publications by Year </AppHeading>
33+
34+
<div class="tab-container">
35+
<div class="tabs" role="tablist" aria-label="Publication years">
36+
<div
37+
v-for="year in years"
38+
:key="year"
39+
class="tab-item"
40+
:class="{ active: activeYear === year }"
4241
>
43-
<span v-if="index !== publications.publications.length - 1"> · </span>
44-
</template>
45-
</p>
42+
<AppButton
43+
:text="year"
44+
color="none"
45+
:aria-selected="activeYear === year"
46+
role="tab"
47+
:class="{ active: activeYear === year }"
48+
@click="setActiveYear(year)"
49+
/>
50+
</div>
51+
</div>
52+
53+
<!-- Panel shows items for the currently active year -->
54+
<div
55+
class="citations"
56+
role="tabpanel"
57+
:aria-labelledby="`tab-${activeYear}`"
58+
>
59+
<AppGallery>
60+
<AppCitation
61+
v-for="(publication, idx) in currentGroup.items"
62+
:key="idx"
63+
:link="publication.link"
64+
:title="publication.title"
65+
:authors="publication.authors"
66+
:details="[publication.journal, publication.issue]"
67+
/>
68+
</AppGallery>
69+
</div>
70+
</div>
4671
</AppSection>
4772

48-
<!-- by year -->
49-
<AppSection
50-
v-for="(group, index) in publications.publications"
51-
:key="index"
52-
width="big"
53-
>
54-
<AppHeading :id="'year-' + group.year">{{ group.year }}</AppHeading>
55-
<AppGallery>
56-
<AppCitation
57-
v-for="(publication, item) in group.items"
58-
:key="item"
59-
:link="publication.link"
60-
:title="publication.title"
61-
:authors="publication.authors"
62-
:details="[publication.journal, publication.issue]"
63-
/>
64-
</AppGallery>
73+
<!-- Citations bar chart (ApexCharts) -->
74+
<AppSection>
75+
<AppHeading>Yearly Citation Trend </AppHeading>
76+
<Apex
77+
class="chart"
78+
type="bar"
79+
:options="options"
80+
:series="[citesPerYear]"
81+
height="300px"
82+
/>
6583
</AppSection>
6684
</template>
6785

6886
<script setup lang="ts">
69-
/** https://apexcharts.com/docs/vue-charts/ */
87+
import { computed, nextTick, onMounted, ref } from "vue";
7088
import Apex from "vue3-apexcharts";
89+
import ApexCharts from "apexcharts";
7190
import type { ApexOptions } from "apexcharts";
7291
import AppBreadcrumb from "@/components/AppBreadcrumb.vue";
92+
import AppButton from "@/components/AppButton.vue";
7393
import AppCitation from "@/components/AppCitation.vue";
94+
import AppSection from "@/components/AppSection.vue";
7495
import PageTitle from "@/components/ThePageTitle.vue";
7596
import publications from "@/data/publications.json";
7697
77-
/** data for chart */
98+
// Types inferred directly from JSON for safety and IntelliSense
99+
type PublicationsData = typeof publications;
100+
type PublicationGroup = PublicationsData["publications"][number];
101+
const groups = publications.publications as PublicationGroup[];
102+
const years = computed(() => groups.map((g) => String(g.year)));
103+
const activeYear = ref<string>(years.value[0] ?? "");
104+
105+
const currentGroup = computed<PublicationGroup>(() => {
106+
return groups.find((g) => String(g.year) === activeYear.value) || groups[0];
107+
});
108+
109+
// Convert the cites_per_year object into [{ x: year, y: count }, ...]
78110
const citesPerYear = {
79111
name: "citations",
80112
data: Object.entries(publications.metadata.cites_per_year).map(
81113
([year, count]) => ({ x: year, y: count }),
82114
),
83115
};
84116
85-
/** extra metadata fields */
117+
// Cards shown above the tabs (keys reference publications.metadata fields)
86118
const metadata: {
87119
name: string;
88120
key: keyof (typeof publications)["metadata"];
@@ -92,17 +124,17 @@ const metadata: {
92124
{ name: "Citations in last 5 years", key: "last_5_yrs" },
93125
];
94126
95-
/** chart options */
127+
/** ----- Base chart options ----- */
96128
const options: ApexOptions = {
97129
chart: {
98130
id: "citations",
99131
type: "bar",
100-
redrawOnParentResize: true,
132+
redrawOnParentResize: true, // keep chart responsive inside containers
101133
},
102134
title: {
103135
text: `Monarch Citations`,
104136
},
105-
colors: ["#00acc1"],
137+
colors: ["#00acc1"], // single series color
106138
plotOptions: {
107139
bar: {
108140
horizontal: false,
@@ -111,53 +143,141 @@ const options: ApexOptions = {
111143
},
112144
},
113145
},
146+
states: {
147+
active: {
148+
allowMultipleDataPointsSelection: false,
149+
filter: { type: "lighten", value: 0.15 },
150+
},
151+
},
114152
tooltip: {
115-
enabled: false,
153+
enabled: false, // declutter; rely on selection highlight
116154
},
117155
xaxis: {
118-
title: {
119-
text: "Year",
120-
},
121-
axisBorder: {
122-
color: "#000000",
123-
},
124-
axisTicks: {
125-
show: true,
126-
color: "#000000",
127-
},
156+
title: { text: "Year" },
157+
axisBorder: { color: "#000000" },
158+
axisTicks: { show: true, color: "#000000" },
128159
},
129160
yaxis: {
130-
title: {
131-
text: "# of Citations",
132-
},
133-
axisBorder: {
134-
color: "#000000",
135-
},
136-
axisTicks: {
137-
show: true,
138-
color: "#000000",
139-
},
161+
title: { text: "# of Citations" },
162+
axisBorder: { color: "#000000" },
163+
axisTicks: { show: true, color: "#000000" },
140164
},
141165
grid: {
142-
xaxis: {
143-
lines: {
144-
show: false,
145-
},
146-
},
147-
yaxis: {
148-
lines: {
149-
show: false,
150-
},
151-
},
166+
xaxis: { lines: { show: false } },
167+
yaxis: { lines: { show: false } },
152168
},
153169
};
170+
171+
/**
172+
* Update activeYear and mirror the selection on the bar chart for context. We
173+
* clear any previous selection and then select the data point at index
174+
* `dataPointIndex`.
175+
*/
176+
function setActiveYear(year: string) {
177+
activeYear.value = year;
178+
nextTick(() => {
179+
const dataPointIndex = citesPerYear.data.findIndex(
180+
(item) => String(item.x) === year,
181+
);
182+
if (dataPointIndex >= 0) {
183+
ApexCharts.exec("citations", "clearSelectedDataPoints");
184+
ApexCharts.exec(
185+
"citations",
186+
"toggleDataPointSelection",
187+
0,
188+
dataPointIndex,
189+
);
190+
}
191+
});
192+
}
193+
194+
// Initialize selection on mount so the default tab and the chart match
195+
onMounted(() => setActiveYear(activeYear.value));
154196
</script>
155197

156198
<style scoped lang="scss">
199+
.section.center {
200+
padding-bottom: 10px;
201+
}
202+
157203
.metadata {
158-
text-align: center;
204+
text-align: left;
159205
}
160206
.publications {
161207
background-color: #ffffff;
162208
}
209+
210+
.tab-container {
211+
display: flex;
212+
flex-direction: column;
213+
align-items: center;
214+
width: 100%;
215+
margin: 0 auto;
216+
padding: 1rem;
217+
gap: 1.5em;
218+
}
219+
220+
.tabs {
221+
display: flex;
222+
width: 100%;
223+
margin-bottom: 1rem;
224+
overflow-x: auto;
225+
gap: 0.3rem;
226+
border-bottom: 3px solid $theme;
227+
background-color: #fff;
228+
}
229+
230+
.tab-item {
231+
display: flex;
232+
flex-direction: column;
233+
align-items: center;
234+
text-align: center;
235+
}
236+
237+
.tab-item.active {
238+
border: 1px solid #ccc;
239+
border-bottom: 0;
240+
background-color: #008080;
241+
color: #fff;
242+
}
243+
244+
/* Ensure AppButton text remains readable in active tab */
245+
.tab-item.active :deep(.button) {
246+
color: #fff !important;
247+
}
248+
249+
.tab-item:not(.active) {
250+
border: 1px solid transparent;
251+
border-bottom: 0;
252+
background-color: #f5f5f5;
253+
}
254+
255+
:deep(.button) {
256+
width: 100%;
257+
border: none;
258+
background-color: transparent;
259+
font-weight: bold;
260+
cursor: pointer;
261+
&:hover,
262+
&:focus {
263+
outline: none !important;
264+
box-shadow: none !important;
265+
}
266+
}
267+
268+
.citations {
269+
max-height: 32rem;
270+
overflow-x: hidden;
271+
overflow-y: auto;
272+
scrollbar-color: $theme;
273+
scrollbar-gutter: stable both-edges;
274+
scrollbar-width: thin;
275+
}
276+
277+
/* Subtle dark-mode contrast for the custom scrollbar colors */
278+
@media (prefers-color-scheme: dark) {
279+
.citations {
280+
scrollbar-color: rgba(0, 200, 200, 0.7) rgba(255, 255, 255, 0.08);
281+
}
282+
}
163283
</style>

0 commit comments

Comments
 (0)