@@ -3,29 +3,85 @@ aside: false
3
3
---
4
4
5
5
<script setup >
6
- import { ref , onMounted } from ' vue'
6
+ import { ref , onMounted , computed , watch } from ' vue'
7
+
8
+ const SORT_OPTIONS = {
9
+ sort: [" year" , " author" , " title" , " conference" ],
10
+ ascending: false // boolean for ascending/descending order, true = ascending
11
+ }
7
12
8
13
const publications = ref ([])
9
14
10
- onMounted (async () => {
11
- const response = await fetch (' /assets/publications.json' )
12
- const pubs = await response .json ()
15
+ const sorting = ref ({
16
+ sort: " author" ,
17
+ ascending: false
18
+ })
19
+
20
+ const previousSort = ref (" author" )
21
+
22
+ const search = ref (" " )
23
+ const searchOpen = ref (false )
24
+
25
+ // helper
26
+ function sortByAuthor (a , b , order ) {
27
+ const aFirstAuthor = a .authors .split (' ,' )[0 ]
28
+ const bFirstAuthor = b .authors .split (' ,' )[0 ]
29
+ const aLastName = aFirstAuthor .split (' ' ).pop ()
30
+ const bLastName = bFirstAuthor .split (' ' ).pop ()
31
+ return aLastName .localeCompare (bLastName) * order
32
+ }
33
+
34
+ const sortedPublications = computed (() => {
35
+ // filter search string first
36
+ let filteredPubs = [... publications .value ]
37
+ if (search .value !== " " ) {
38
+ filteredPubs = filteredPubs .filter ((p ) => p .title .toLowerCase ().includes (search .value .toLowerCase ()))
39
+ }
40
+
41
+ return filteredPubs .sort ((a , b ) => {
42
+ const sortKey = sorting .value .sort
43
+ const order = sorting .value .ascending ? 1 : - 1
44
+
45
+ if (sortKey === " year" ) {
46
+ if (a .year !== b .year ) {
47
+ return (b .year - a .year ) * order
48
+ }
49
+ return sortByAuthor (a, b, 1 )
50
+ }
51
+
52
+ if (sortKey === " author" ) {
53
+ return sortByAuthor (a, b, order)
54
+ }
55
+
56
+ if (sortKey === " title" ) {
57
+ return a .title .localeCompare (b .title ) * order
58
+ }
13
59
14
- // TODO: update with better sorting when functionality added to UI
15
- // sort by year, then by first author (alphabetical)
16
- publications .value = pubs .sort ((a , b ) => {
17
- if (a .year !== b .year ) {
18
- return b .year - a .year ; // Sort by year in descending order
60
+ if (sortKey === " conference" ) {
61
+ return a .conference .localeCompare (b .conference ) * order
19
62
}
20
- // Sort by first author alphabetically (last name)
21
- const aFirstAuthor = a .authors .split (' ,' )[0 ]
22
- const bFirstAuthor = b .authors .split (' ,' )[0 ]
23
- const aLastName = aFirstAuthor .split (' ' ).pop ()
24
- const bLastName = bFirstAuthor .split (' ' ).pop ()
25
63
26
- return aLastName . localeCompare (bLastName);
64
+ return 0
27
65
})
28
66
})
67
+
68
+ function handleSortToggle (v ) {
69
+ if (previousSort .value === v) {
70
+ sorting .value .ascending = ! sorting .value .ascending
71
+ } else {
72
+ previousSort .value = v
73
+ sorting .value .ascending = false
74
+ }
75
+ }
76
+
77
+ function openSearchBar () {
78
+ searchOpen .value = true
79
+ }
80
+
81
+ onMounted (async () => {
82
+ const response = await fetch (' /assets/publications.json' )
83
+ publications .value = await response .json ()
84
+ })
29
85
</script >
30
86
31
87
<style >
@@ -36,9 +92,9 @@ onMounted(async () => {
36
92
}
37
93
38
94
.publication img {
39
- max-width : 200 px ;
95
+ max-width : 150 px ;
40
96
height : auto ; /* Maintain aspect ratio */
41
- margin-left : 20px ;
97
+ margin-right : 20px ;
42
98
object-fit : contain ; /* Ensure the image fits within the container while maintaining aspect ratio */
43
99
}
44
100
@@ -52,21 +108,81 @@ onMounted(async () => {
52
108
}
53
109
54
110
.publication img {
55
- margin-left : 0 ;
111
+ margin-right : 0 ;
56
112
margin-bottom : 10px ;
57
113
}
58
114
}
115
+
116
+ .v-icon-placeholder {
117
+ width : 16px ; /* Width of the icon */
118
+ height : 16px ; /* Height of the icon */
119
+ display : inline-block ;
120
+ }
59
121
</style >
60
122
123
+ <v-row
124
+ justify="space-between"
125
+ class="mb-4"
126
+ >
127
+
61
128
# Publications
62
129
130
+ <v-btn-toggle
131
+ v-model="sorting.sort"
132
+ mandatory
133
+ group
134
+ >
135
+ <v-btn value="year" @click="() => sorting.sort === 'year' && handleSortToggle('year')">
136
+ Year
137
+ <v-icon v-if="sorting.sort === 'year'" class="opacity-60">
138
+ {{ (sorting.ascending) ? "mdi-arrow-up" : "mdi-arrow-down" }}
139
+ </v-icon>
140
+ <span v-else class="v-icon-placeholder"></span>
141
+ </v-btn>
142
+ <v-btn value="author" @click="handleSortToggle('author')">
143
+ Author
144
+ <v-icon v-if="sorting.sort === 'author'" class="opacity-60">
145
+ {{ (sorting.ascending) ? "mdi-arrow-up" : "mdi-arrow-down" }}
146
+ </v-icon>
147
+ <span v-else class="v-icon-placeholder"></span>
148
+ </v-btn>
149
+ <v-btn value="title" @click="handleSortToggle('title')">
150
+ Title
151
+ <v-icon v-if="sorting.sort === 'title'" class="opacity-60">
152
+ {{ (sorting.ascending) ? "mdi-arrow-up" : "mdi-arrow-down" }}
153
+ </v-icon>
154
+ <span v-else class="v-icon-placeholder"></span>
155
+ </v-btn>
156
+ <v-menu
157
+ :close-on-content-click="false"
158
+ location="bottom"
159
+ >
160
+ <template v-slot:activator="{ props }">
161
+ <v-btn v-bind="props" icon="mdi-magnify" @click="() => {
162
+ sorting.sort = previousSort // reset sorting to previous to ignore the click action
163
+ openSearchBar()
164
+ }">
165
+ </v-btn>
166
+ </template>
167
+ <v-card min-width="300">
168
+ <v-text-field
169
+ v-model="search"
170
+ hide-details
171
+ label="Search"
172
+ >
173
+ </v-text-field>
174
+ </v-card>
175
+ </v-menu>
176
+ </v-btn-toggle >
177
+ </v-row >
178
+
63
179
<div class =" container " >
64
- <div v-for =" publication in publications " :key =" publication.title " class =" publication " >
180
+ <div v-for =" publication in sortedPublications " :key =" publication.title " class =" publication " >
181
+ <img v-if="publication.image" :src="`../assets/images/publications/${publication.image.src}`" :alt="publication.image.alt">
65
182
<div class="publication-info">
66
- <a :href="publication.link" target="_blank">{{ publication.title }}</a>
67
183
<p>{{ publication.authors }}</p>
184
+ <a :href="publication.link" target="_blank">{{ publication.title }}</a>
68
185
<p>{{ publication.conference }} ({{ publication.year }})</p>
69
186
</div>
70
- <img v-if="publication.image" :src="`../assets/images/publications/${publication.image.src}`" :alt="publication.image.alt">
71
187
</div >
72
- </div >
188
+ </div >
0 commit comments