@abi-software/map-side-bar 1.3.0 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,476 +1,482 @@
1
- <template>
2
- <el-card :body-style="bodyStyle" class="content-card">
3
- <div slot="header" class="header">
4
- <context-card v-if="contextCardEntry && contextCardEnabled" :entry="contextCardEntry" />
5
- <el-input
6
- class="search-input"
7
- placeholder="Search"
8
- v-model="searchInput"
9
- @keyup.native="searchEvent"
10
- clearable
11
- @clear="clearSearchClicked"
12
- ></el-input>
13
- <el-button class="button" @click="searchEvent">Search</el-button>
14
- </div>
15
- <SearchFilters
16
- class="filters"
17
- ref="filtersRef"
18
- :entry="filterEntry"
19
- :envVars="envVars"
20
- @filterResults="filterUpdate"
21
- @numberPerPage="numberPerPageUpdate"
22
- @loading="filtersLoading"
23
- ></SearchFilters>
24
- <div class="content scrollbar" v-loading="loadingCards" ref="content">
25
- <div
26
- class="error-feedback"
27
- v-if="results.length === 0 && !loadingCards"
28
- >No results found - Please change your search / filter criteria.</div>
29
- <div v-for="result in results" :key="result.doi" class="step-item">
30
- <DatasetCard :entry="result" :envVars="envVars" @contextUpdate="contextCardUpdate"></DatasetCard>
31
- </div>
32
- <el-pagination
33
- class="pagination"
34
- :current-page.sync="page"
35
- hide-on-single-page
36
- large
37
- layout="prev, pager, next"
38
- :page-size="numberPerPage"
39
- :total="numberOfHits"
40
- @current-change="pageChange"
41
- ></el-pagination>
42
- </div>
43
- </el-card>
44
- </template>
45
-
46
-
47
- <script>
48
- /* eslint-disable no-alert, no-console */
49
- import Vue from "vue";
50
- import {
51
- Button,
52
- Card,
53
- Drawer,
54
- Icon,
55
- Input,
56
- Loading,
57
- Pagination
58
- } from "element-ui";
59
- import lang from "element-ui/lib/locale/lang/en";
60
- import locale from "element-ui/lib/locale";
61
- import SearchFilters from "./SearchFilters";
62
- import DatasetCard from "./DatasetCard";
63
- import ContextCard from "./ContextCard.vue";
64
-
65
- import {AlgoliaClient} from "../algolia/algolia.js";
66
- import {getFilters} from "../algolia/utils.js"
67
-
68
- locale.use(lang);
69
- Vue.use(Button);
70
- Vue.use(Card);
71
- Vue.use(Drawer);
72
- Vue.use(Icon);
73
- Vue.use(Input);
74
- Vue.use(Loading);
75
- Vue.use(Pagination);
76
-
77
- // handleErrors: A custom fetch error handler to recieve messages from the server
78
- // even when an error is found
79
- var handleErrors = async function(response) {
80
- if (!response.ok) {
81
- let parse = await response.json();
82
- if (parse) {
83
- throw new Error(parse.message);
84
- } else {
85
- throw new Error(response);
86
- }
87
- }
88
- return response;
89
- };
90
-
91
- var initial_state = {
92
- searchInput: "",
93
- lastSearch: "",
94
- results: [],
95
- numberOfHits: 0,
96
- filter: [],
97
- loadingCards: false,
98
- numberPerPage: 10,
99
- page: 1,
100
- pageModel: 1,
101
- start: 0,
102
- hasSearched: false,
103
- contextCardEntry: undefined,
104
- contextCardEnabled: true,
105
- };
106
-
107
- export default {
108
- components: { SearchFilters, DatasetCard, ContextCard },
109
- name: "SideBarContent",
110
- props: {
111
- visible: {
112
- type: Boolean,
113
- default: false
114
- },
115
- isDrawer: {
116
- type: Boolean,
117
- default: true
118
- },
119
- entry: {
120
- type: Object,
121
- default: () => initial_state
122
- },
123
- envVars: {
124
- type: Object,
125
- default: () => {}
126
- },
127
- firstSearch: {
128
- type: String,
129
- default: ""
130
- }
131
- },
132
- data: function() {
133
- return {
134
- ...this.entry,
135
- bodyStyle: {
136
- flex: "1 1 auto",
137
- "flex-flow": "column",
138
- display: "flex"
139
- }
140
- };
141
- },
142
- computed: {
143
- // This computed property populates filter data's entry object with $data from this sidebar
144
- filterEntry: function() {
145
- return {
146
- numberOfHits: this.numberOfHits,
147
- filterFacets: this.filter
148
- };
149
- }
150
- },
151
- methods: {
152
- contextCardUpdate: function(val){
153
- this.contextCardEntry = val
154
- },
155
- openSearch: function(filter, search='') {
156
- this.searchInput = search;
157
- this.resetPageNavigation();
158
- this.searchAlgolia(filter, search);
159
- if (filter) {
160
- this.filter = [...filter];
161
- this.$refs.filtersRef.setCascader(this.filter);
162
- }
163
- },
164
- addFilter: function(filter) {
165
- this.resetPageNavigation();
166
- if (filter) {
167
- this.$refs.filtersRef.addFilter(filter);
168
- this.$refs.filtersRef.initiateSearch()
169
- }
170
- },
171
- clearSearchClicked: function() {
172
- this.searchInput = "";
173
- this.resetPageNavigation();
174
- this.searchAlgolia(this.filters, this.searchInput);
175
- },
176
- searchEvent: function(event = false) {
177
- if (event.keyCode === 13 || event instanceof MouseEvent) {
178
- this.resetPageNavigation();
179
- this.searchAlgolia(this.filters, this.searchInput);
180
- }
181
- },
182
- filterUpdate: function(filters) {
183
- this.filters = [...filters]
184
- this.resetPageNavigation()
185
- this.searchAlgolia(filters, this.searchInput)
186
- this.$emit("search-changed", {
187
- value: filters,
188
- type: "filter-update"
189
- });
190
- },
191
- searchAlgolia(filters, query=''){
192
- // Algolia search
193
- this.loadingCards = true
194
- this.algoliaClient.search(getFilters(filters), query, this.numberPerPage, this.page).then(searchData => {
195
- this.numberOfHits = searchData.total
196
- this.discoverIds = searchData.discoverIds
197
- this._dois = searchData.dois
198
- this.results = searchData.items
199
- this.loadingCards = false
200
- this.scrollToTop()
201
- this.$emit("search-changed", { value: this.searchInput, type: "query-update" })
202
- if (this._abortController)
203
- this._abortController.abort()
204
- this._abortController = new AbortController()
205
- const signal = this._abortController.signal
206
- //Search ongoing, let the current flow progress
207
- this.perItemSearch(signal, { count: 0 })
208
- })
209
- },
210
- filtersLoading: function (val) {
211
- this.loadingCards = val;
212
- },
213
- numberPerPageUpdate: function(val) {
214
- this.numberPerPage = val;
215
- this.pageChange(1);
216
- },
217
- pageChange: function(page) {
218
- this.start = (page - 1) * this.numberPerPage;
219
- this.page = page
220
- this.searchAlgolia(this.filters, this.searchInput, this.numberPerPage, this.page)
221
- },
222
- handleMissingData: function(doi) {
223
- let i = this.results.findIndex(res=> res.doi === doi)
224
- if (this.results[i])
225
- this.results[i].detailsReady = true;
226
- },
227
- perItemSearch: function(signal, data) {
228
- //Maximum 10 downloads at once to prevent long waiting time
229
- //between unfinished search and new search
230
- const maxDownloads = 10;
231
- if (maxDownloads > data.count) {
232
- const doi = this._dois.shift();
233
- if (doi) {
234
- data.count++;
235
- this.callSciCrunch(this.envVars.API_LOCATION, {'dois': [doi]}, signal)
236
- .then(result => {
237
- if (result.numberOfHits === 0)
238
- this.handleMissingData(doi);
239
- else
240
- this.resultsProcessing(result);
241
- this.$refs.content.style["overflow-y"] = "scroll";
242
- data.count--;
243
- //Async::Download finished, get the next one
244
- this.perItemSearch(signal, data);
245
- })
246
- .catch(result => {
247
- if (result.name !== 'AbortError') {
248
- this.handleMissingData(doi);
249
- data.count--;
250
- //Async::Download not aborted, get the next one
251
- this.perItemSearch(signal, data);
252
- }
253
- });
254
- //Check and make another request until it gets to max downloads
255
- this.perItemSearch(signal, data);
256
- }
257
- }
258
- },
259
- scrollToTop: function() {
260
- if (this.$refs.content) {
261
- this.$refs.content.scroll({ top: 0, behavior: "smooth" });
262
- }
263
- },
264
- resetPageNavigation: function() {
265
- this.start = 0;
266
- this.page = 1;
267
- },
268
- resultsProcessing: function(data) {
269
- this.lastSearch = this.searchInput;
270
-
271
- if (data.results.length === 0) {
272
- return;
273
- }
274
- data.results.forEach(element => {
275
- // match the scicrunch result with algolia result
276
- let i = this.results.findIndex(res=> res.name === element.name)
277
- // Assign scicrunch results to the object
278
- Object.assign(this.results[i], element)
279
- // Assign the attributes that need some processing
280
- Object.assign(this.results[i],{
281
- numberSamples: element.sampleSize
282
- ? parseInt(element.sampleSize)
283
- : 0,
284
- numberSubjects: element.subjectSize
285
- ? parseInt(element.subjectSize)
286
- : 0,
287
- updated: element.updated[0].timestamp.split("T")[0],
288
- url: element.uri[0],
289
- datasetId: element.dataset_identifier,
290
- datasetRevision: element.dataset_revision,
291
- datasetVersion: element.dataset_version,
292
- organs: (element.organs && element.organs.length > 0)
293
- ? [...new Set(element.organs.map(v => v.name))]
294
- : undefined,
295
- species: element.organisms
296
- ? element.organisms[0].species
297
- ? [...new Set(element.organisms.map((v) =>v.species ? v.species.name : null))]
298
- : undefined
299
- : undefined, // This processing only includes each gender once into 'sexes'
300
- scaffolds: element['abi-scaffold-metadata-file'],
301
- thumbnails: element['abi-thumbnail'] ? element['abi-thumbnail']: element['abi-scaffold-thumbnail'],
302
- scaffoldViews: element['abi-scaffold-view-file'],
303
- videos: element.video,
304
- plots: element['abi-plot'],
305
- images: element['common-images'],
306
- contextualInformation: element['abi-contextual-information'].length > 0 ? element['abi-contextual-information'] : undefined,
307
- segmentation: element['mbf-segmentation'],
308
- simulation: element['abi-simulation-file'],
309
- additionalLinks: element.additionalLinks,
310
- detailsReady: true,
311
- })
312
- Vue.set(this.results, i, this.results[i])
313
- });
314
- },
315
- createfilterParams: function(params) {
316
- let p = new URLSearchParams();
317
- //Check if field is array or value
318
- for (const key in params) {
319
- if (Array.isArray(params[key])) {
320
- params[key].forEach(e => {
321
- p.append(key, e);
322
- });
323
- } else {
324
- p.append(key, params[key]);
325
- }
326
- }
327
- return p.toString();
328
- },
329
- callSciCrunch: function(apiLocation, params = {}, signal) {
330
- return new Promise((resolve, reject) => {
331
- // Add parameters if we are sent them
332
- let fullEndpoint = this.envVars.API_LOCATION + this.searchEndpoint + "?" + this.createfilterParams(params);
333
- fetch(fullEndpoint, {signal})
334
- .then(handleErrors)
335
- .then(response => response.json())
336
- .then(data => resolve(data))
337
- .catch(data => reject(data));
338
- });
339
- },
340
- },
341
- mounted: function() {
342
- // initialise algolia
343
- this.algoliaClient = new AlgoliaClient(this.envVars.ALGOLIA_ID, this.envVars.ALGOLIA_KEY, this.envVars.PENNSIEVE_API_LOCATION);
344
- this.algoliaClient.initIndex(this.envVars.ALGOLIA_INDEX);
345
-
346
- // temporarily disable flatmap search since there are no datasets
347
- if (this.firstSearch === "Flatmap" || this.firstSearch === "flatmap") {
348
- this.openSearch(undefined, '')
349
- } else {
350
- this.openSearch(undefined, '');
351
- }
352
- },
353
- created: function() {
354
- //Create non-reactive local variables
355
- this.searchEndpoint = "dataset_info/using_multiple_dois/";
356
- }
357
- };
358
- </script>
359
-
360
- <!-- Add "scoped" attribute to limit CSS to this component only -->
361
- <style scoped>
362
- .content-card {
363
- height: 100%;
364
- flex-flow: column;
365
- display: flex;
366
- }
367
-
368
- .button {
369
- background-color: #8300bf;
370
- border: #8300bf;
371
- color: white;
372
- }
373
-
374
- .step-item {
375
- font-size: 14px;
376
- margin-bottom: 18px;
377
- text-align: left;
378
- }
379
-
380
- .search-input {
381
- width: 298px !important;
382
- height: 40px;
383
- padding-right: 14px;
384
- align-items: left;
385
- }
386
-
387
- .header {
388
- border: solid 1px #292b66;
389
- background-color: #292b66;
390
- text-align: left;
391
- }
392
-
393
- .pagination {
394
- padding-bottom: 16px;
395
- background-color: white;
396
- text-align: center;
397
- }
398
-
399
- .pagination >>> button {
400
- background-color: white !important;
401
- }
402
- .pagination >>> li {
403
- background-color: white !important;
404
- }
405
- .pagination >>> li.active {
406
- color: #8300bf;
407
- }
408
-
409
- .error-feedback {
410
- font-family: Asap;
411
- font-size: 14px;
412
- font-style: italic;
413
- padding-top: 15px;
414
- }
415
-
416
- .content-card >>> .el-card__header {
417
- background-color: #292b66;
418
- border: solid 1px #292b66;
419
- }
420
-
421
- .content-card >>> .el-card__body {
422
- background-color: #f7faff;
423
- overflow-y: hidden;
424
- }
425
-
426
- .content {
427
- width: 518px;
428
- flex: 1 1 auto;
429
- box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
430
- border: solid 1px #e4e7ed;
431
- background-color: #ffffff;
432
- overflow-y: scroll;
433
- scrollbar-width: thin;
434
- }
435
-
436
- .content >>> .el-loading-spinner .path {
437
- stroke: #8300bf;
438
- }
439
-
440
- .content >>> .step-item:first-child .seperator-path{
441
- display: none;
442
- }
443
-
444
- .content >>> .step-item:not(:first-child) .seperator-path{
445
- width: 486px;
446
- height: 0px;
447
- border: solid 1px #e4e7ed;
448
- background-color: #e4e7ed;
449
- }
450
-
451
- .scrollbar::-webkit-scrollbar-track {
452
- border-radius: 10px;
453
- background-color: #f5f5f5;
454
- }
455
-
456
- .scrollbar::-webkit-scrollbar {
457
- width: 12px;
458
- right: -12px;
459
- background-color: #f5f5f5;
460
- }
461
-
462
- .scrollbar::-webkit-scrollbar-thumb {
463
- border-radius: 4px;
464
- box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.06);
465
- background-color: #979797;
466
- }
467
-
468
- >>> .el-input__suffix {
469
- padding-right: 10px;
470
- }
471
-
472
- >>> .my-drawer {
473
- background: rgba(0, 0, 0, 0);
474
- box-shadow: none;
475
- }
476
- </style>
1
+ <template>
2
+ <el-card :body-style="bodyStyle" class="content-card">
3
+ <div slot="header" class="header">
4
+ <context-card v-if="contextCardEntry && contextCardEnabled" :entry="contextCardEntry" />
5
+ <el-input
6
+ class="search-input"
7
+ placeholder="Search"
8
+ v-model="searchInput"
9
+ @keyup.native="searchEvent"
10
+ clearable
11
+ @clear="clearSearchClicked"
12
+ ></el-input>
13
+ <el-button class="button" @click="searchEvent">Search</el-button>
14
+ </div>
15
+ <SearchFilters
16
+ class="filters"
17
+ ref="filtersRef"
18
+ :entry="filterEntry"
19
+ :envVars="envVars"
20
+ @filterResults="filterUpdate"
21
+ @numberPerPage="numberPerPageUpdate"
22
+ @loading="filtersLoading"
23
+ ></SearchFilters>
24
+ <div class="content scrollbar" v-loading="loadingCards" ref="content">
25
+ <div
26
+ class="error-feedback"
27
+ v-if="results.length === 0 && !loadingCards"
28
+ >No results found - Please change your search / filter criteria.</div>
29
+ <div v-for="result in results" :key="result.doi" class="step-item">
30
+ <DatasetCard :entry="result" :envVars="envVars" @contextUpdate="contextCardUpdate"></DatasetCard>
31
+ </div>
32
+ <el-pagination
33
+ class="pagination"
34
+ :current-page.sync="page"
35
+ hide-on-single-page
36
+ large
37
+ layout="prev, pager, next"
38
+ :page-size="numberPerPage"
39
+ :total="numberOfHits"
40
+ @current-change="pageChange"
41
+ ></el-pagination>
42
+ </div>
43
+ </el-card>
44
+ </template>
45
+
46
+
47
+ <script>
48
+ /* eslint-disable no-alert, no-console */
49
+ import Vue from "vue";
50
+ import {
51
+ Button,
52
+ Card,
53
+ Drawer,
54
+ Icon,
55
+ Input,
56
+ Loading,
57
+ Pagination
58
+ } from "element-ui";
59
+ import lang from "element-ui/lib/locale/lang/en";
60
+ import locale from "element-ui/lib/locale";
61
+ import SearchFilters from "./SearchFilters";
62
+ import DatasetCard from "./DatasetCard";
63
+ import ContextCard from "./ContextCard.vue";
64
+ import EventBus from "./EventBus"
65
+
66
+ import {AlgoliaClient} from "../algolia/algolia.js";
67
+ import {getFilters} from "../algolia/utils.js"
68
+
69
+ locale.use(lang);
70
+ Vue.use(Button);
71
+ Vue.use(Card);
72
+ Vue.use(Drawer);
73
+ Vue.use(Icon);
74
+ Vue.use(Input);
75
+ Vue.use(Loading);
76
+ Vue.use(Pagination);
77
+
78
+ // handleErrors: A custom fetch error handler to recieve messages from the server
79
+ // even when an error is found
80
+ var handleErrors = async function(response) {
81
+ if (!response.ok) {
82
+ let parse = await response.json();
83
+ if (parse) {
84
+ throw new Error(parse.message);
85
+ } else {
86
+ throw new Error(response);
87
+ }
88
+ }
89
+ return response;
90
+ };
91
+
92
+ var initial_state = {
93
+ searchInput: "",
94
+ lastSearch: "",
95
+ results: [],
96
+ numberOfHits: 0,
97
+ filter: [],
98
+ loadingCards: false,
99
+ numberPerPage: 10,
100
+ page: 1,
101
+ pageModel: 1,
102
+ start: 0,
103
+ hasSearched: false,
104
+ contextCardEntry: undefined,
105
+ contextCardEnabled: true,
106
+ };
107
+
108
+ export default {
109
+ components: { SearchFilters, DatasetCard, ContextCard },
110
+ name: "SideBarContent",
111
+ props: {
112
+ visible: {
113
+ type: Boolean,
114
+ default: false
115
+ },
116
+ isDrawer: {
117
+ type: Boolean,
118
+ default: true
119
+ },
120
+ entry: {
121
+ type: Object,
122
+ default: () => initial_state
123
+ },
124
+ envVars: {
125
+ type: Object,
126
+ default: () => {}
127
+ },
128
+ firstSearch: {
129
+ type: String,
130
+ default: ""
131
+ }
132
+ },
133
+ data: function() {
134
+ return {
135
+ ...this.entry,
136
+ bodyStyle: {
137
+ flex: "1 1 auto",
138
+ "flex-flow": "column",
139
+ display: "flex"
140
+ }
141
+ };
142
+ },
143
+ computed: {
144
+ // This computed property populates filter data's entry object with $data from this sidebar
145
+ filterEntry: function() {
146
+ return {
147
+ numberOfHits: this.numberOfHits,
148
+ filterFacets: this.filter
149
+ };
150
+ }
151
+ },
152
+ methods: {
153
+ contextCardUpdate: function(val){
154
+ this.contextCardEntry = val
155
+ },
156
+ openSearch: function(filter, search='') {
157
+ this.searchInput = search;
158
+ this.resetPageNavigation();
159
+ this.searchAlgolia(filter, search);
160
+ if (filter) {
161
+ this.filter = [...filter];
162
+ this.$refs.filtersRef.setCascader(this.filter);
163
+ }
164
+ },
165
+ addFilter: function(filter) {
166
+ this.resetPageNavigation();
167
+ if (filter) {
168
+ this.$refs.filtersRef.addFilter(filter);
169
+ this.$refs.filtersRef.initiateSearch()
170
+ }
171
+ },
172
+ clearSearchClicked: function() {
173
+ this.searchInput = "";
174
+ this.resetPageNavigation();
175
+ this.searchAlgolia(this.filters, this.searchInput);
176
+ },
177
+ searchEvent: function(event = false) {
178
+ if (event.keyCode === 13 || event instanceof MouseEvent) {
179
+ this.resetPageNavigation();
180
+ this.searchAlgolia(this.filters, this.searchInput);
181
+ }
182
+ },
183
+ filterUpdate: function(filters) {
184
+ this.filters = [...filters]
185
+ this.resetPageNavigation()
186
+ this.searchAlgolia(filters, this.searchInput)
187
+ this.$emit("search-changed", {
188
+ value: filters,
189
+ type: "filter-update"
190
+ });
191
+ },
192
+ searchAlgolia(filters, query=''){
193
+ // Algolia search
194
+ this.loadingCards = true
195
+ this.algoliaClient.keywordsInSearch(getFilters(filters), query).then(keywords => {
196
+ console.log('key search complete!')
197
+ console.log(keywords)
198
+ EventBus.$emit("kewordsFound", keywords)
199
+ })
200
+ this.algoliaClient.search(getFilters(filters), query, this.numberPerPage, this.page).then(searchData => {
201
+ this.numberOfHits = searchData.total
202
+ this.discoverIds = searchData.discoverIds
203
+ this._dois = searchData.dois
204
+ this.results = searchData.items
205
+ this.loadingCards = false
206
+ this.scrollToTop()
207
+ this.$emit("search-changed", { value: this.searchInput, type: "query-update" })
208
+ if (this._abortController)
209
+ this._abortController.abort()
210
+ this._abortController = new AbortController()
211
+ const signal = this._abortController.signal
212
+ //Search ongoing, let the current flow progress
213
+ this.perItemSearch(signal, { count: 0 })
214
+ })
215
+ },
216
+ filtersLoading: function (val) {
217
+ this.loadingCards = val;
218
+ },
219
+ numberPerPageUpdate: function(val) {
220
+ this.numberPerPage = val;
221
+ this.pageChange(1);
222
+ },
223
+ pageChange: function(page) {
224
+ this.start = (page - 1) * this.numberPerPage;
225
+ this.page = page
226
+ this.searchAlgolia(this.filters, this.searchInput, this.numberPerPage, this.page)
227
+ },
228
+ handleMissingData: function(doi) {
229
+ let i = this.results.findIndex(res=> res.doi === doi)
230
+ if (this.results[i])
231
+ this.results[i].detailsReady = true;
232
+ },
233
+ perItemSearch: function(signal, data) {
234
+ //Maximum 10 downloads at once to prevent long waiting time
235
+ //between unfinished search and new search
236
+ const maxDownloads = 10;
237
+ if (maxDownloads > data.count) {
238
+ const doi = this._dois.shift();
239
+ if (doi) {
240
+ data.count++;
241
+ this.callSciCrunch(this.envVars.API_LOCATION, {'dois': [doi]}, signal)
242
+ .then(result => {
243
+ if (result.numberOfHits === 0)
244
+ this.handleMissingData(doi);
245
+ else
246
+ this.resultsProcessing(result);
247
+ this.$refs.content.style["overflow-y"] = "scroll";
248
+ data.count--;
249
+ //Async::Download finished, get the next one
250
+ this.perItemSearch(signal, data);
251
+ })
252
+ .catch(result => {
253
+ if (result.name !== 'AbortError') {
254
+ this.handleMissingData(doi);
255
+ data.count--;
256
+ //Async::Download not aborted, get the next one
257
+ this.perItemSearch(signal, data);
258
+ }
259
+ });
260
+ //Check and make another request until it gets to max downloads
261
+ this.perItemSearch(signal, data);
262
+ }
263
+ }
264
+ },
265
+ scrollToTop: function() {
266
+ if (this.$refs.content) {
267
+ this.$refs.content.scroll({ top: 0, behavior: "smooth" });
268
+ }
269
+ },
270
+ resetPageNavigation: function() {
271
+ this.start = 0;
272
+ this.page = 1;
273
+ },
274
+ resultsProcessing: function(data) {
275
+ this.lastSearch = this.searchInput;
276
+
277
+ if (data.results.length === 0) {
278
+ return;
279
+ }
280
+ data.results.forEach(element => {
281
+ // match the scicrunch result with algolia result
282
+ let i = this.results.findIndex(res=> res.name === element.name)
283
+ // Assign scicrunch results to the object
284
+ Object.assign(this.results[i], element)
285
+ // Assign the attributes that need some processing
286
+ Object.assign(this.results[i],{
287
+ numberSamples: element.sampleSize
288
+ ? parseInt(element.sampleSize)
289
+ : 0,
290
+ numberSubjects: element.subjectSize
291
+ ? parseInt(element.subjectSize)
292
+ : 0,
293
+ updated: element.updated[0].timestamp.split("T")[0],
294
+ url: element.uri[0],
295
+ datasetId: element.dataset_identifier,
296
+ datasetRevision: element.dataset_revision,
297
+ datasetVersion: element.dataset_version,
298
+ organs: (element.organs && element.organs.length > 0)
299
+ ? [...new Set(element.organs.map(v => v.name))]
300
+ : undefined,
301
+ species: element.organisms
302
+ ? element.organisms[0].species
303
+ ? [...new Set(element.organisms.map((v) =>v.species ? v.species.name : null))]
304
+ : undefined
305
+ : undefined, // This processing only includes each gender once into 'sexes'
306
+ scaffolds: element['abi-scaffold-metadata-file'],
307
+ thumbnails: element['abi-thumbnail'] ? element['abi-thumbnail']: element['abi-scaffold-thumbnail'],
308
+ scaffoldViews: element['abi-scaffold-view-file'],
309
+ videos: element.video,
310
+ plots: element['abi-plot'],
311
+ images: element['common-images'],
312
+ contextualInformation: element['abi-contextual-information'].length > 0 ? element['abi-contextual-information'] : undefined,
313
+ segmentation: element['mbf-segmentation'],
314
+ simulation: element['abi-simulation-file'],
315
+ additionalLinks: element.additionalLinks,
316
+ detailsReady: true,
317
+ })
318
+ Vue.set(this.results, i, this.results[i])
319
+ });
320
+ },
321
+ createfilterParams: function(params) {
322
+ let p = new URLSearchParams();
323
+ //Check if field is array or value
324
+ for (const key in params) {
325
+ if (Array.isArray(params[key])) {
326
+ params[key].forEach(e => {
327
+ p.append(key, e);
328
+ });
329
+ } else {
330
+ p.append(key, params[key]);
331
+ }
332
+ }
333
+ return p.toString();
334
+ },
335
+ callSciCrunch: function(apiLocation, params = {}, signal) {
336
+ return new Promise((resolve, reject) => {
337
+ // Add parameters if we are sent them
338
+ let fullEndpoint = this.envVars.API_LOCATION + this.searchEndpoint + "?" + this.createfilterParams(params);
339
+ fetch(fullEndpoint, {signal})
340
+ .then(handleErrors)
341
+ .then(response => response.json())
342
+ .then(data => resolve(data))
343
+ .catch(data => reject(data));
344
+ });
345
+ },
346
+ },
347
+ mounted: function() {
348
+ // initialise algolia
349
+ this.algoliaClient = new AlgoliaClient(this.envVars.ALGOLIA_ID, this.envVars.ALGOLIA_KEY, this.envVars.PENNSIEVE_API_LOCATION);
350
+ this.algoliaClient.initIndex(this.envVars.ALGOLIA_INDEX);
351
+
352
+ // temporarily disable flatmap search since there are no datasets
353
+ if (this.firstSearch === "Flatmap" || this.firstSearch === "flatmap") {
354
+ this.openSearch(undefined, '')
355
+ } else {
356
+ this.openSearch(undefined, '');
357
+ }
358
+ },
359
+ created: function() {
360
+ //Create non-reactive local variables
361
+ this.searchEndpoint = "dataset_info/using_multiple_dois/";
362
+ }
363
+ };
364
+ </script>
365
+
366
+ <!-- Add "scoped" attribute to limit CSS to this component only -->
367
+ <style scoped>
368
+ .content-card {
369
+ height: 100%;
370
+ flex-flow: column;
371
+ display: flex;
372
+ }
373
+
374
+ .button {
375
+ background-color: #8300bf;
376
+ border: #8300bf;
377
+ color: white;
378
+ }
379
+
380
+ .step-item {
381
+ font-size: 14px;
382
+ margin-bottom: 18px;
383
+ text-align: left;
384
+ }
385
+
386
+ .search-input {
387
+ width: 298px !important;
388
+ height: 40px;
389
+ padding-right: 14px;
390
+ align-items: left;
391
+ }
392
+
393
+ .header {
394
+ border: solid 1px #292b66;
395
+ background-color: #292b66;
396
+ text-align: left;
397
+ }
398
+
399
+ .pagination {
400
+ padding-bottom: 16px;
401
+ background-color: white;
402
+ text-align: center;
403
+ }
404
+
405
+ .pagination >>> button {
406
+ background-color: white !important;
407
+ }
408
+ .pagination >>> li {
409
+ background-color: white !important;
410
+ }
411
+ .pagination >>> li.active {
412
+ color: #8300bf;
413
+ }
414
+
415
+ .error-feedback {
416
+ font-family: Asap;
417
+ font-size: 14px;
418
+ font-style: italic;
419
+ padding-top: 15px;
420
+ }
421
+
422
+ .content-card >>> .el-card__header {
423
+ background-color: #292b66;
424
+ border: solid 1px #292b66;
425
+ }
426
+
427
+ .content-card >>> .el-card__body {
428
+ background-color: #f7faff;
429
+ overflow-y: hidden;
430
+ }
431
+
432
+ .content {
433
+ width: 518px;
434
+ flex: 1 1 auto;
435
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
436
+ border: solid 1px #e4e7ed;
437
+ background-color: #ffffff;
438
+ overflow-y: scroll;
439
+ scrollbar-width: thin;
440
+ }
441
+
442
+ .content >>> .el-loading-spinner .path {
443
+ stroke: #8300bf;
444
+ }
445
+
446
+ .content >>> .step-item:first-child .seperator-path{
447
+ display: none;
448
+ }
449
+
450
+ .content >>> .step-item:not(:first-child) .seperator-path{
451
+ width: 486px;
452
+ height: 0px;
453
+ border: solid 1px #e4e7ed;
454
+ background-color: #e4e7ed;
455
+ }
456
+
457
+ .scrollbar::-webkit-scrollbar-track {
458
+ border-radius: 10px;
459
+ background-color: #f5f5f5;
460
+ }
461
+
462
+ .scrollbar::-webkit-scrollbar {
463
+ width: 12px;
464
+ right: -12px;
465
+ background-color: #f5f5f5;
466
+ }
467
+
468
+ .scrollbar::-webkit-scrollbar-thumb {
469
+ border-radius: 4px;
470
+ box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.06);
471
+ background-color: #979797;
472
+ }
473
+
474
+ >>> .el-input__suffix {
475
+ padding-right: 10px;
476
+ }
477
+
478
+ >>> .my-drawer {
479
+ background: rgba(0, 0, 0, 0);
480
+ box-shadow: none;
481
+ }
482
+ </style>