@abi-software/map-side-bar 2.4.0-isan-1 → 2.4.0

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abi-software/map-side-bar",
3
- "version": "2.4.0-isan-1",
3
+ "version": "2.4.0",
4
4
  "files": [
5
5
  "dist/*",
6
6
  "src/*",
@@ -39,6 +39,7 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@abi-software/gallery": "^1.1.1",
42
+ "@abi-software/map-utilities": "^1.1.0",
42
43
  "@abi-software/svg-sprite": "^1.0.0",
43
44
  "@element-plus/icons-vue": "^2.3.1",
44
45
  "algoliasearch": "^4.10.5",
package/src/App.vue CHANGED
@@ -112,7 +112,6 @@ export default {
112
112
  BL_SERVER_URL: import.meta.env.VITE_APP_BL_SERVER_URL,
113
113
  NL_LINK_PREFIX: import.meta.env.VITE_APP_NL_LINK_PREFIX,
114
114
  ROOT_URL: import.meta.env.VITE_APP_ROOT_URL,
115
- FLATMAP_API_LOCATION: import.meta.env.VITE_APP_FLATMAP_API_LOCATION,
116
115
  },
117
116
  connectivityInput: exampleConnectivityInput,
118
117
  activeId: 1,
@@ -120,7 +119,7 @@ export default {
120
119
  },
121
120
  methods: {
122
121
  hoverChanged: function (data) {
123
- // console.log('hoverChanged', data)
122
+ console.log('hoverChanged', data)
124
123
  },
125
124
  searchChanged: function (data) {
126
125
  console.log(data)
@@ -132,18 +131,16 @@ export default {
132
131
  action: function (action) {
133
132
  console.log('action fired: ', action)
134
133
  let facets = [];
135
- if (action.labels) {
136
- facets.push(
137
- ...action.labels.map(val => ({
138
- facet: capitalise(val),
139
- term: "Anatomical structure",
140
- facetPropPath: "anatomy.organ.category.name",
141
- }))
142
- );
143
- if (this.$refs.sideBar) {
144
- console.log('openSearch', facets)
145
- this.$refs.sideBar.openSearch(facets, "");
146
- }
134
+ facets.push(
135
+ ...action.labels.map(val => ({
136
+ facet: capitalise(val),
137
+ term: "Anatomical structure",
138
+ facetPropPath: "anatomy.organ.category.name",
139
+ }))
140
+ );
141
+ if (this.$refs.sideBar) {
142
+ console.log('openSearch', facets)
143
+ this.$refs.sideBar.openSearch(facets, "");
147
144
  }
148
145
  },
149
146
  openSearch: function () {
@@ -100,7 +100,6 @@ export class AlgoliaClient {
100
100
  for (let res of results) {
101
101
  newResult = { ...res }
102
102
  newResult = {
103
- dataSource: 'SPARC',
104
103
  anatomy: res.anatomy ? res.anatomy.organ.map((organ => organ.curie)) : undefined,
105
104
  doi: res.item.curie.split(':')[1],
106
105
  name: res.item.name,
@@ -153,48 +152,35 @@ export class AlgoliaClient {
153
152
  * Get Search results
154
153
  * This is using fetch from the Algolia API
155
154
  */
156
- search(filter, query = '', offset = 0, length = 8) {
157
- console.log('searching', filter, query, offset, length)
158
- // If the length is 0, return an empty result
159
- if (length === 0) {
160
- return new Promise(resolve => {
161
- resolve({
162
- items: [],
163
- total: 0,
164
- discoverIds: [],
165
- dois: []
155
+ search(filter, query = '', hitsperPage = 10, page = 1) {
156
+ return new Promise(resolve => {
157
+ this.index
158
+ .search(query, {
159
+ facets: ['*'],
160
+ hitsPerPage: hitsperPage,
161
+ page: page - 1,
162
+ filters: filter,
163
+ attributesToHighlight: [],
164
+ attributesToRetrieve: [
165
+ 'pennsieve.publishDate',
166
+ 'pennsieve.updatedAt',
167
+ 'item.curie',
168
+ 'item.name',
169
+ 'item.description',
170
+ 'objectID',
171
+ 'anatomy.organ.curie'
172
+ ],
166
173
  })
167
- })
168
- } else {
169
- return new Promise(resolve => {
170
- this.index
171
- .search(query, {
172
- facets: ['*'],
173
- offset: offset,
174
- length: length,
175
- filters: filter,
176
- attributesToHighlight: [],
177
- attributesToRetrieve: [
178
- 'pennsieve.publishDate',
179
- 'pennsieve.updatedAt',
180
- 'item.curie',
181
- 'item.name',
182
- 'item.description',
183
- 'objectID',
184
- 'anatomy.organ.curie'
185
- ],
186
- })
187
- .then(response => {
188
- let searchData = {
189
- items: this._processResultsForCards(response.hits),
190
- total: response.nbHits,
191
- discoverIds: response.hits.map(r => r.pennsieve ? r.pennsieve.identifier : r.objectID),
192
- dois: response.hits.map(r => r.item.curie.split(':')[1])
193
- }
194
- resolve(searchData)
195
- })
196
- })
197
- }
174
+ .then(response => {
175
+ let searchData = {
176
+ items: this._processResultsForCards(response.hits),
177
+ total: response.nbHits,
178
+ discoverIds: response.hits.map(r => r.pennsieve ? r.pennsieve.identifier : r.objectID),
179
+ dois: response.hits.map(r => r.item.curie.split(':')[1])
180
+ }
181
+ resolve(searchData)
182
+ })
183
+ })
198
184
  }
199
185
 
200
186
  /**
@@ -60,13 +60,8 @@ export function getFilters(selectedFacetArray=undefined) {
60
60
  return 'NOT item.published.status:embargo'
61
61
  }
62
62
 
63
- // Switch the 'term' attribute to 'label' if 'label' does not exist. Use facet2 if available
64
- selectedFacetArray.forEach(f=>{
65
- f.label=f.facet
66
- if (f.facet2) {
67
- f.label = f.facet2
68
- }
69
- })
63
+ // Switch the 'term' attribute to 'label' if 'label' does not exist
64
+ selectedFacetArray.forEach(f=>f.label=f.facet)
70
65
 
71
66
 
72
67
  let facets = removeShowAllFacets(selectedFacetArray)
@@ -20,12 +20,12 @@
20
20
  </div>
21
21
  <external-resource-card :resources="resources"></external-resource-card>
22
22
  </div>
23
- <div>
23
+ <div class="title-buttons">
24
24
  <el-popover
25
- width="200"
25
+ width="auto"
26
26
  trigger="hover"
27
27
  :teleported="false"
28
- popper-class="popover-origin-help"
28
+ popper-class="popover-map-pin"
29
29
  >
30
30
  <template #reference>
31
31
  <el-button class="button-circle" circle @click="showConnectivity(entry)">
@@ -38,6 +38,7 @@
38
38
  Show connectivity on map
39
39
  </span>
40
40
  </el-popover>
41
+ <CopyToClipboard :content="updatedCopyContent" />
41
42
  </div>
42
43
  </div>
43
44
  <div v-if="featuresAlert" class="attribute-title-container">
@@ -191,6 +192,8 @@ import {
191
192
  } from 'element-plus'
192
193
  import ExternalResourceCard from './ExternalResourceCard.vue'
193
194
  import EventBus from './EventBus.js'
195
+ import { CopyToClipboard } from '@abi-software/map-utilities';
196
+ import '@abi-software/map-utilities/dist/style.css';
194
197
 
195
198
  const titleCase = (str) => {
196
199
  return str.replace(/\w\S*/g, (t) => {
@@ -213,6 +216,7 @@ export default {
213
216
  ElIconArrowDown,
214
217
  ElIconWarning,
215
218
  ExternalResourceCard,
219
+ CopyToClipboard,
216
220
  },
217
221
  props: {
218
222
  entry: {
@@ -260,6 +264,9 @@ export default {
260
264
  },
261
265
  },
262
266
  computed: {
267
+ updatedCopyContent: function () {
268
+ return this.getUpdateCopyContent();
269
+ },
263
270
  resources: function () {
264
271
  let resources = [];
265
272
  if (this.entry && this.entry.hyperlinks) {
@@ -347,6 +354,100 @@ export default {
347
354
  // connected to flatmapvuer > moveMap(featureIds) function
348
355
  this.$emit('show-connectivity', featureIds);
349
356
  },
357
+ getUpdateCopyContent: function () {
358
+ if (!this.entry) {
359
+ return '';
360
+ }
361
+
362
+ const contentArray = [];
363
+
364
+ // Use <div> instead of <h1>..<h6> or <p>
365
+ // to avoid default formatting on font size and margin
366
+
367
+ // Title
368
+ if (this.entry.title) {
369
+ contentArray.push(`<div><strong>${capitalise(this.entry.title)}</strong></div>`);
370
+ } else {
371
+ contentArray.push(`<div><strong>${this.entry.featureId}</strong></div>`);
372
+ }
373
+
374
+ // Description
375
+ if (this.entry.provenanceTaxonomyLabel?.length) {
376
+ contentArray.push(`<div>${this.provSpeciesDescription}</div>`);
377
+ }
378
+
379
+ // PubMed URL
380
+ if (this.resources?.length) {
381
+ const pubmedContents = [];
382
+ this.resources.forEach((resource) => {
383
+ let pubmedContent = '';
384
+ if (resource.id === 'pubmed') {
385
+ pubmedContent += `<div><strong>PubMed URL:</strong></div>`;
386
+ pubmedContent += '\n';
387
+ pubmedContent += `<div><a href="${resource.url}">${resource.url}</a></div>`;
388
+ }
389
+ pubmedContents.push(pubmedContent);
390
+ });
391
+ contentArray.push(pubmedContents.join('\n\n<br>'));
392
+ }
393
+
394
+ // entry.paths
395
+ if (this.entry.paths) {
396
+ contentArray.push(`<div>${this.entry.paths}</div>`);
397
+ }
398
+
399
+ function transformData(title, items, itemsWithDatasets = []) {
400
+ let contentString = `<div><strong>${title}</strong></div>`;
401
+ const transformedItems = [];
402
+ items.forEach((item) => {
403
+ let itemNames = [];
404
+ item.split(',').forEach((name) => {
405
+ const match = itemsWithDatasets.find((a) => a.name === name.trim());
406
+ if (match) {
407
+ itemNames.push(`${capitalise(name)} (${match.id})`);
408
+ } else {
409
+ itemNames.push(`${capitalise(name)}`);
410
+ }
411
+ });
412
+ transformedItems.push(itemNames.join(','));
413
+ });
414
+ const contentList = transformedItems
415
+ .map((item) => `<li>${item}</li>`)
416
+ .join('\n');
417
+ contentString += '\n';
418
+ contentString += `<ul>${contentList}</ul>`;
419
+ return contentString;
420
+ }
421
+
422
+ // Origins
423
+ if (this.entry.origins?.length) {
424
+ const title = 'Origin';
425
+ const origins = this.entry.origins;
426
+ const originsWithDatasets = this.entry.originsWithDatasets;
427
+ const transformedOrigins = transformData(title, origins, originsWithDatasets);
428
+ contentArray.push(transformedOrigins);
429
+ }
430
+
431
+ // Components
432
+ if (this.entry.components?.length) {
433
+ const title = 'Components';
434
+ const components = this.entry.components;
435
+ const componentsWithDatasets = this.entry.componentsWithDatasets;
436
+ const transformedComponents = transformData(title, components, componentsWithDatasets);
437
+ contentArray.push(transformedComponents);
438
+ }
439
+
440
+ // Destination
441
+ if (this.entry.destinations?.length) {
442
+ const title = 'Destination';
443
+ const destinations = this.entry.destinations;
444
+ const destinationsWithDatasets = this.entry.destinationsWithDatasets;
445
+ const transformedDestinations = transformData(title, destinations, destinationsWithDatasets);
446
+ contentArray.push(transformedDestinations);
447
+ }
448
+
449
+ return contentArray.join('\n\n<br>');
450
+ },
350
451
  },
351
452
  }
352
453
  </script>
@@ -390,6 +491,7 @@ export default {
390
491
  }
391
492
 
392
493
  .button-circle {
494
+ margin: 0;
393
495
  width: 24px !important;
394
496
  height: 24px !important;
395
497
 
@@ -616,4 +718,36 @@ export default {
616
718
  .tooltip-container::after {
617
719
  top: 99.4%;
618
720
  }
721
+
722
+ .title-buttons {
723
+ display: flex;
724
+ flex-direction: row;
725
+ gap: 0.5rem;
726
+
727
+ :deep(.copy-clipboard-button) {
728
+ &,
729
+ &:hover,
730
+ &:focus {
731
+ border-color: $app-primary-color !important;
732
+ border-radius: 50%;
733
+ }
734
+ }
735
+ }
736
+
737
+ :deep(.el-popper.popover-map-pin) {
738
+ padding: 4px 10px;
739
+ min-width: max-content;
740
+ font-family: Asap;
741
+ font-size: 12px;
742
+ line-height: inherit;
743
+ color: inherit;
744
+ background: #f3ecf6 !important;
745
+ border: 1px solid $app-primary-color;
746
+
747
+ & .el-popper__arrow::before {
748
+ border: 1px solid;
749
+ border-color: $app-primary-color;
750
+ background: #f3ecf6;
751
+ }
752
+ }
619
753
  </style>
@@ -19,7 +19,6 @@
19
19
  />
20
20
  </span>
21
21
  <div class="card-right">
22
- <el-tag type="primary" class="source-tag">SPARC Dataset</el-tag>
23
22
  <div class="title" @click="cardClicked">{{ entry.name }}</div>
24
23
  <div class="details">
25
24
  {{ contributors }} {{ entry.publishDate ? `(${publishYear})` : '' }}
@@ -47,6 +46,11 @@
47
46
  @categoryChanged="categoryChanged"
48
47
  />
49
48
  </div>
49
+
50
+ <!-- Copy to clipboard button container -->
51
+ <div class="float-button-container">
52
+ <CopyToClipboard :content="copyContent" />
53
+ </div>
50
54
  </div>
51
55
  </div>
52
56
  </div>
@@ -65,6 +69,8 @@ import EventBus from './EventBus.js'
65
69
  import speciesMap from './species-map.js'
66
70
  import ImageGallery from './ImageGallery.vue'
67
71
  import MissingImage from '@/../assets/missing-image.svg'
72
+ import { CopyToClipboard } from '@abi-software/map-utilities';
73
+ import '@abi-software/map-utilities/dist/style.css';
68
74
 
69
75
  export default {
70
76
  data() {
@@ -77,7 +83,8 @@ export default {
77
83
  BadgesGroup,
78
84
  ImageGallery,
79
85
  Button,
80
- Icon
86
+ Icon,
87
+ CopyToClipboard,
81
88
  },
82
89
  props: {
83
90
  /**
@@ -103,6 +110,7 @@ export default {
103
110
  lastDoi: undefined,
104
111
  biolucidaData: undefined,
105
112
  currentCategory: 'All',
113
+ copyContent: '',
106
114
  }
107
115
  },
108
116
  computed: {
@@ -155,6 +163,9 @@ export default {
155
163
  return this.entry.publishDate.split('-')[0]
156
164
  },
157
165
  },
166
+ mounted: function () {
167
+ this.updateCopyContent();
168
+ },
158
169
  methods: {
159
170
  cardClicked: function () {
160
171
  this.openDataset()
@@ -227,6 +238,7 @@ export default {
227
238
  this.dataLocation = `https://sparc.science/datasets/${data.id}?type=dataset`
228
239
  this.getBiolucidaInfo(this.discoverId)
229
240
  this.loading = false
241
+ this.updateCopyContent();
230
242
  })
231
243
  .catch(() => {
232
244
  //set defaults if we hit an error
@@ -249,6 +261,66 @@ export default {
249
261
  if (data.status == 'success') this.biolucidaData = data
250
262
  })
251
263
  },
264
+ updateCopyContent: function () {
265
+ const contentArray = [];
266
+
267
+ // Use <div> instead of <h1>..<h6> or <p>
268
+ // to avoid default formatting on font size and margin
269
+
270
+ // Title
271
+ if (this.entry.name) {
272
+ contentArray.push(`<div><strong>${this.entry.name}</strong></div>`);
273
+ }
274
+
275
+ // Contributors and Publish Date
276
+ if (this.contributors) {
277
+ let details = this.contributors;
278
+
279
+ if (this.entry.publishDate) {
280
+ details += ` (${this.publishYear})`;
281
+ }
282
+ contentArray.push(`<div>${details}</div>`);
283
+ }
284
+
285
+ // samples
286
+ if (this.samples) {
287
+ contentArray.push(`<div>${this.samples}</div>`);
288
+ }
289
+
290
+ // DOI
291
+ if (this.entry.doi) {
292
+ let doiContent = `<div><strong>DOI:</strong></div>`;
293
+ doiContent += `\n`;
294
+ doiContent += `<a href="${this.entry.doi}">${this.entry.doi}</a>`;
295
+ contentArray.push(`<div>${doiContent}</div>`);
296
+ }
297
+
298
+ // Dataset ID
299
+ if (this.entry.datasetId) {
300
+ let datasetIdContent = `<div><strong>Dataset ID:</strong></div>`;
301
+ datasetIdContent += `\n`;
302
+ datasetIdContent += `${this.entry.datasetId}`;
303
+ contentArray.push(`<div>${datasetIdContent}</div>`);
304
+ }
305
+
306
+ // Dataset URL
307
+ if (this.dataLocation) {
308
+ let dataLocationContent = `<div><strong>Dataset URL:</strong></div>`;
309
+ dataLocationContent += `\n`;
310
+ dataLocationContent += `<a href="${this.dataLocation}">${this.dataLocation}</a>`;
311
+ contentArray.push(`<div>${dataLocationContent}</div>`);
312
+ }
313
+
314
+ // Dataset version
315
+ if (this.version) {
316
+ let versionContent = `<div><strong>Dataset version:</strong></div>`;
317
+ versionContent += `\n`;
318
+ versionContent += `${this.version}`;
319
+ contentArray.push(`<div>${versionContent}</div>`);
320
+ }
321
+
322
+ this.copyContent = contentArray.join('\n\n<br>');
323
+ },
252
324
  },
253
325
  created: function () {
254
326
  this.getBanner()
@@ -283,15 +355,6 @@ export default {
283
355
  color: #484848;
284
356
  cursor: pointer;
285
357
  }
286
-
287
- .source-tag {
288
- margin-bottom: 0.75rem;
289
- margin-right: 2rem;
290
- position: absolute;
291
- bottom: 0;
292
- right: 0;
293
- }
294
-
295
358
  .card {
296
359
  padding-top: 18px;
297
360
  position: relative;
@@ -364,4 +427,17 @@ export default {
364
427
  .loading-icon :deep(.el-loading-spinner .path) {
365
428
  stroke: $app-primary-color;
366
429
  }
430
+
431
+ .float-button-container {
432
+ position: absolute;
433
+ bottom: 8px;
434
+ right: 16px;
435
+ opacity: 0;
436
+ visibility: hidden;
437
+
438
+ .card:hover & {
439
+ opacity: 1;
440
+ visibility: visible;
441
+ }
442
+ }
367
443
  </style>
@@ -156,6 +156,7 @@ export default {
156
156
  },
157
157
  data: function () {
158
158
  return {
159
+ algoliaClient: undefined,
159
160
  cascaderIsReady: false,
160
161
  previousShowAllChecked: {
161
162
  species: false,
@@ -261,29 +262,12 @@ export default {
261
262
  }
262
263
  })
263
264
  })
264
-
265
- this.populatePMRinCascader();
266
265
  })
267
266
  .finally(() => {
268
267
  resolve()
269
268
  })
270
269
  })
271
270
  },
272
- /**
273
- * Add PMR checkbox in filters (cascader)
274
- */
275
- populatePMRinCascader: function () {
276
- for (let i = 0; i < this.options.length; i += 1) {
277
- const option = this.options[i];
278
- // match with "Data type"'s' key
279
- if (option.key === 'item.types.name') {
280
- option.children.push({
281
- label: 'PMR',
282
- value: this.createCascaderItemValue("Data type", "PMR"),
283
- });
284
- }
285
- }
286
- },
287
271
  /**
288
272
  * Create manual events when cascader tag is closed
289
273
  */
@@ -485,7 +469,7 @@ export default {
485
469
  facetSubPropPath: facetSubPropPath, // will be used for filters if we are at the third level of the cascader
486
470
  }
487
471
  })
488
-
472
+
489
473
  this.$emit('loading', true) // let sidebarcontent wait for the requests
490
474
  this.$emit('filterResults', filters) // emit filters for apps above sidebar
491
475
  this.setCascader(filterKeys) //update our cascader v-model if we modified the event
@@ -647,7 +631,7 @@ export default {
647
631
  let filters = createFilter(e)
648
632
  return filters
649
633
  })
650
-
634
+
651
635
  // Unforttunately the cascader is very particular about it's v-model
652
636
  // to get around this we create a clone of it and use this clone for adding our boolean information
653
637
  this.cascadeSelectedWithBoolean = filterFacets.map((e) => {
@@ -793,11 +777,11 @@ export default {
793
777
  },
794
778
  },
795
779
  mounted: function () {
796
- this.algoliaClient = new AlgoliaClient(
780
+ this.algoliaClient = markRaw(new AlgoliaClient(
797
781
  this.envVars.ALGOLIA_ID,
798
782
  this.envVars.ALGOLIA_KEY,
799
783
  this.envVars.PENNSIEVE_API_LOCATION
800
- )
784
+ ))
801
785
  this.algoliaClient.initIndex(this.envVars.ALGOLIA_INDEX)
802
786
  this.populateCascader().then(() => {
803
787
  this.cascaderIsReady = true
@@ -46,7 +46,6 @@
46
46
  :contextCardEntry="tab.contextCard"
47
47
  :envVars="envVars"
48
48
  :ref="'searchTab_' + tab.id"
49
- @pmr-action-click="onPmrActionClick"
50
49
  @search-changed="searchChanged(tab.id, $event)"
51
50
  @hover-changed="hoverChanged($event)"
52
51
  />
@@ -247,9 +246,6 @@ export default {
247
246
  setDrawerOpen: function (value = true) {
248
247
  this.drawerOpen = value
249
248
  },
250
- onPmrActionClick: function (payload) {
251
- this.$emit('actionClick', payload);
252
- },
253
249
  /**
254
250
  * @vuese
255
251
  * The function to emit 'tabClicked' event with tab's `id` and tab's `type`