@abi-software/map-side-bar 2.7.2-beta.4 → 2.7.2-beta.6

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,5 +1,27 @@
1
1
  <template>
2
2
  <div v-if="entry" class="main" v-loading="loading">
3
+ <div v-if="connectivityEntry.length > 1" class="button-container">
4
+ <el-popover
5
+ width="auto"
6
+ trigger="hover"
7
+ :teleported="false"
8
+ >
9
+ <template #reference>
10
+ <el-button class="button" @click="previous">Previous</el-button>
11
+ </template>
12
+ <span>{{ previousLabel }}</span>
13
+ </el-popover>
14
+ <el-popover
15
+ width="auto"
16
+ trigger="hover"
17
+ :teleported="false"
18
+ >
19
+ <template #reference>
20
+ <el-button class="button" @click="next">Next</el-button>
21
+ </template>
22
+ <span>{{ nextLabel }}</span>
23
+ </el-popover>
24
+ </div>
3
25
  <!-- Connectivity Info Title -->
4
26
  <div class="connectivity-info-title">
5
27
  <div class="title-content">
@@ -44,7 +66,7 @@
44
66
  popper-class="popover-map-pin"
45
67
  >
46
68
  <template #reference>
47
- <el-button class="button-circle" circle @click="showConnectivity(entry)">
69
+ <el-button class="button-circle" circle @click="showConnectivity">
48
70
  <el-icon color="white">
49
71
  <el-icon-location />
50
72
  </el-icon>
@@ -58,53 +80,152 @@
58
80
  </div>
59
81
  </div>
60
82
 
61
- <div
62
- class="content-container population-display"
63
- :class="dualConnectionSource ? 'population-display-toolbar' : ''"
64
- >
83
+ <div class="content-container population-display">
65
84
  <div class="block attribute-title-container">
66
85
  <span class="attribute-title">Population Display</span>
67
86
  </div>
68
87
  <div class="block buttons-row">
69
- <div v-if="dualConnectionSource">
70
- <span>Connectivity from:</span>
71
- <el-radio-group v-model="connectivitySource" @change="onConnectivitySourceChange">
72
- <el-radio value="map">Map</el-radio>
73
- <el-radio value="sckan">SCKAN</el-radio>
74
- </el-radio-group>
75
- </div>
76
- <div>
77
- <el-button
78
- :class="activeView === 'listView' ? 'button' : 'el-button-secondary'"
79
- @click="switchConnectivityView('listView')"
88
+ <el-button
89
+ :class="activeView === 'listView' ? 'button' : 'el-button-secondary'"
90
+ @click="switchConnectivityView('listView')"
91
+ >
92
+ List view
93
+ </el-button>
94
+ <el-button
95
+ :class="activeView === 'graphView' ? 'button' : 'el-button-secondary'"
96
+ @click="switchConnectivityView('graphView')"
97
+ >
98
+ Graph view
99
+ </el-button>
100
+ </div>
101
+ </div>
102
+
103
+ <div class="content-container content-container-connectivity" v-show="activeView === 'listView'">
104
+ {{ entry.paths }}
105
+ <div v-if="entry.origins && entry.origins.length > 0" class="block">
106
+ <div class="attribute-title-container">
107
+ <span class="attribute-title">Origin</span>
108
+ <el-popover
109
+ width="250"
110
+ trigger="hover"
111
+ :teleported="false"
112
+ popper-class="popover-origin-help"
80
113
  >
81
- List view
82
- </el-button>
83
- <el-button
84
- :class="activeView === 'graphView' ? 'button' : 'el-button-secondary'"
85
- @click="switchConnectivityView('graphView')"
114
+ <template #reference>
115
+ <el-icon class="info"><el-icon-warning /></el-icon>
116
+ </template>
117
+ <span style="word-break: keep-all">
118
+ <i>Origin</i> {{ originDescription }}
119
+ </span>
120
+
121
+ </el-popover>
122
+ </div>
123
+ <div
124
+ v-for="(origin, i) in entry.origins"
125
+ class="attribute-content"
126
+ :origin-item-label="origin"
127
+ :key="origin"
128
+ @mouseenter="toggleConnectivityTooltip(origin, {show: true})"
129
+ @mouseleave="toggleConnectivityTooltip(origin, {show: false})"
130
+ >
131
+ {{ capitalise(origin) }}
132
+ </div>
133
+ <el-button
134
+ v-show="
135
+ entry.originsWithDatasets && entry.originsWithDatasets.length > 0 &&
136
+ shouldShowExploreButton(entry.originsWithDatasets)
137
+ "
138
+ class="button"
139
+ id="open-dendrites-button"
140
+ @click="openDendrites"
141
+ >
142
+ Explore origin data
143
+ </el-button>
144
+ </div>
145
+ <div
146
+ v-if="entry.components && entry.components.length > 0"
147
+ class="block"
148
+ >
149
+ <div class="attribute-title-container">
150
+ <div class="attribute-title">Components</div>
151
+ </div>
152
+ <div
153
+ v-for="(component, i) in entry.components"
154
+ class="attribute-content"
155
+ :component-item-label="component"
156
+ :key="component"
157
+ @mouseenter="toggleConnectivityTooltip(component, {show: true})"
158
+ @mouseleave="toggleConnectivityTooltip(component, {show: false})"
159
+ >
160
+ {{ capitalise(component) }}
161
+ </div>
162
+ </div>
163
+ <div
164
+ v-if="entry.destinations && entry.destinations.length > 0"
165
+ class="block"
166
+ >
167
+ <div class="attribute-title-container">
168
+ <span class="attribute-title">Destination</span>
169
+ <el-popover
170
+ width="250"
171
+ trigger="hover"
172
+ :teleported="false"
173
+ popper-class="popover-origin-help"
86
174
  >
87
- Graph view
88
- </el-button>
175
+ <template #reference>
176
+ <el-icon class="info"><el-icon-warning /></el-icon>
177
+ </template>
178
+ <span style="word-break: keep-all">
179
+ <i>Destination</i> is where the axons terminate
180
+ </span>
181
+ </el-popover>
182
+ </div>
183
+ <div
184
+ v-for="(destination, i) in entry.destinations"
185
+ class="attribute-content"
186
+ :destination-item-label="destination"
187
+ :key="destination"
188
+ @mouseenter="toggleConnectivityTooltip(destination, {show: true})"
189
+ @mouseleave="toggleConnectivityTooltip(destination, {show: false})"
190
+ >
191
+ {{ capitalise(destination) }}
89
192
  </div>
193
+ <el-button
194
+ v-show="
195
+ entry.destinationsWithDatasets &&
196
+ entry.destinationsWithDatasets.length > 0 &&
197
+ shouldShowExploreButton(entry.destinationsWithDatasets)
198
+ "
199
+ class="button"
200
+ @click="openAxons"
201
+ >
202
+ Explore destination data
203
+ </el-button>
204
+ </div>
205
+ <div
206
+ v-show="
207
+ entry.componentsWithDatasets &&
208
+ entry.componentsWithDatasets.length > 0 &&
209
+ shouldShowExploreButton(entry.componentsWithDatasets)
210
+ "
211
+ class="block"
212
+ >
213
+ <el-button
214
+ class="button"
215
+ @click="openAll"
216
+ >
217
+ Search for data on components
218
+ </el-button>
90
219
  </div>
91
- </div>
92
220
 
93
- <div class="content-container content-container-connectivity" v-show="activeView === 'listView'">
94
- <connectivity-list
95
- :key="entry.featureId[0]"
96
- :entry="entry"
97
- :origins="origins"
98
- :components="components"
99
- :destinations="destinations"
100
- :originsWithDatasets="originsWithDatasets"
101
- :componentsWithDatasets="componentsWithDatasets"
102
- :destinationsWithDatasets="destinationsWithDatasets"
103
- :availableAnatomyFacets="availableAnatomyFacets"
104
- :connectivityError="connectivityError"
105
- @toggle-connectivity-tooltip="onToggleConnectivityTooltip"
106
- @connectivity-action-click="onConnectivityActionClick"
107
- ></connectivity-list>
221
+ <div class="connectivity-error-container">
222
+ <div class="connectivity-error" v-if="connectivityError">
223
+ <strong v-if="connectivityError.errorConnectivities">
224
+ {{ connectivityError.errorConnectivities }}
225
+ </strong>
226
+ {{ connectivityError.errorMessage }}
227
+ </div>
228
+ </div>
108
229
  </div>
109
230
 
110
231
  <div class="content-container" v-show="activeView === 'graphView'">
@@ -112,9 +233,8 @@
112
233
  <connectivity-graph
113
234
  :key="entry.featureId[0]"
114
235
  :entry="entry.featureId[0]"
115
- :mapServer="flatmapApi"
236
+ :mapServer="envVars.FLATMAPAPI_LOCATION"
116
237
  :sckanVersion="sckanVersion"
117
- :connectivityFromMap="connectivityFromMap"
118
238
  @tap-node="onTapNode"
119
239
  ref="connectivityGraphRef"
120
240
  />
@@ -132,6 +252,11 @@
132
252
  </template>
133
253
 
134
254
  <script>
255
+ import {
256
+ ArrowUp as ElIconArrowUp,
257
+ ArrowDown as ElIconArrowDown,
258
+ Warning as ElIconWarning,
259
+ } from '@element-plus/icons-vue'
135
260
  /* eslint-disable no-alert, no-console */
136
261
  import {
137
262
  ElButton as Button,
@@ -143,11 +268,9 @@ import EventBus from './EventBus.js'
143
268
  import {
144
269
  CopyToClipboard,
145
270
  ConnectivityGraph,
146
- ConnectivityList,
147
271
  ExternalResourceCard,
148
272
  } from '@abi-software/map-utilities';
149
273
  import '@abi-software/map-utilities/dist/style.css';
150
- import { processConnectivity } from './flatmapQueries.js';
151
274
 
152
275
  const titleCase = (str) => {
153
276
  return str.replace(/\w\S*/g, (t) => {
@@ -168,24 +291,17 @@ export default {
168
291
  Button,
169
292
  Container,
170
293
  Icon,
294
+ ElIconArrowUp,
295
+ ElIconArrowDown,
296
+ ElIconWarning,
171
297
  ExternalResourceCard,
172
298
  CopyToClipboard,
173
299
  ConnectivityGraph,
174
- ConnectivityList,
175
300
  },
176
301
  props: {
177
- entry: {
178
- type: Object,
179
- default: () => ({
180
- destinations: [],
181
- origins: [],
182
- components: [],
183
- destinationsWithDatasets: [],
184
- originsWithDatasets: [],
185
- componentsWithDatasets: [],
186
- resource: undefined,
187
- featuresAlert: undefined,
188
- }),
302
+ connectivityEntry: {
303
+ type: Array,
304
+ default: [],
189
305
  },
190
306
  envVars: {
191
307
  type: Object,
@@ -195,44 +311,43 @@ export default {
195
311
  type: Array,
196
312
  default: () => [],
197
313
  },
198
- useDOIFormatter: {
199
- type: Boolean,
200
- default: true,
201
- },
202
314
  },
203
315
  data: function () {
204
316
  return {
205
317
  controller: undefined,
206
318
  activeSpecies: undefined,
319
+ pubmedSearchUrl: '',
207
320
  loading: false,
208
321
  activeView: 'listView',
322
+ facetList: [],
209
323
  showToolip: false,
210
324
  showDetails: false,
211
325
  originDescriptions: {
212
326
  motor: 'is the location of the initial cell body of the circuit',
213
327
  sensory: 'is the location of the initial cell body in the PNS circuit',
214
328
  },
215
- origins: [],
216
- originsWithDatasets: [],
217
- components: [],
218
329
  componentsWithDatasets: [],
219
- destinations: [],
220
- destinationsWithDatasets: [],
221
- connectivityFromMap: null,
222
330
  uberons: [{ id: undefined, name: undefined }],
223
331
  connectivityError: null,
224
332
  timeoutID: undefined,
225
333
  graphViewLoaded: false,
226
334
  updatedCopyContent: '',
227
- sckanVersion: '',
228
- connectivitySource: 'sckan',
229
- mapuuid: '',
230
- mapId: '',
231
- dualConnectionSource: false,
232
- flatmapApi: '',
335
+ entryIndex: 0
233
336
  }
234
337
  },
338
+ watch: {
339
+ availableAnatomyFacets: {
340
+ handler: function (val) {
341
+ this.convertFacetsToList(val)
342
+ },
343
+ immediate: true,
344
+ deep: true,
345
+ },
346
+ },
235
347
  computed: {
348
+ entry: function () {
349
+ return this.connectivityEntry[this.entryIndex];
350
+ },
236
351
  resources: function () {
237
352
  let resources = [];
238
353
  if (this.entry && this.entry.hyperlinks) {
@@ -260,17 +375,85 @@ export default {
260
375
  text += ' species'
261
376
  return text
262
377
  },
378
+ sckanVersion: function () {
379
+ return this.entry.knowledgeSource
380
+ },
381
+ previousLabel: function () {
382
+ if (this.entryIndex === 0) {
383
+ return "This is the first item"
384
+ }
385
+ return this.connectivityEntry[this.entryIndex - 1].title
386
+ },
387
+ nextLabel: function () {
388
+ if (this.entryIndex === this.connectivityEntry.length - 1) {
389
+ return "This is the last item"
390
+ }
391
+ return this.connectivityEntry[this.entryIndex + 1].title
392
+ }
263
393
  },
264
394
  methods: {
395
+ previous: function () {
396
+ if (this.entryIndex !== 0) {
397
+ this.entryIndex = this.entryIndex - 1;
398
+ }
399
+ },
400
+ next: function () {
401
+ if (this.entryIndex !== this.connectivityEntry.length - 1) {
402
+ this.entryIndex = this.entryIndex + 1;
403
+ }
404
+ },
265
405
  titleCase: function (title) {
266
406
  return titleCase(title)
267
407
  },
268
408
  capitalise: function (text) {
269
409
  return capitalise(text)
270
410
  },
271
- showConnectivity: function (entry) {
411
+ openUrl: function (url) {
412
+ window.open(url, '_blank')
413
+ },
414
+ openAll: function () {
415
+ EventBus.emit('onConnectivityActionClick', {
416
+ type: 'Facets',
417
+ labels: this.entry.componentsWithDatasets.map((a) => a.name.toLowerCase()),
418
+ })
419
+ },
420
+ openAxons: function () {
421
+ EventBus.emit('onConnectivityActionClick', {
422
+ type: 'Facets',
423
+ labels: this.entry.destinationsWithDatasets.map((a) => a.name.toLowerCase()),
424
+ })
425
+ },
426
+ // shouldShowExploreButton: Checks if the feature is in the list of available anatomy facets
427
+ shouldShowExploreButton: function (features) {
428
+ for (let i = 0; i < features.length; i++) {
429
+ if (this.facetList.includes(features[i].name.toLowerCase())) {
430
+ return true
431
+ }
432
+ }
433
+ return false
434
+ },
435
+ // convertFacetsToList: Converts the available anatomy facets to a list for easy searching
436
+ convertFacetsToList: function (facets) {
437
+ facets.forEach((facet) => {
438
+ if(facet.children) {
439
+ this.convertFacetsToList(facet.children)
440
+ } else {
441
+ this.facetList.push(facet.label.toLowerCase())
442
+ }
443
+ })
444
+ },
445
+ openDendrites: function () {
446
+ EventBus.emit('onConnectivityActionClick', {
447
+ type: 'Facets',
448
+ labels: this.entry.originsWithDatasets.map((a) => a.name.toLowerCase()),
449
+ })
450
+ },
451
+ pubmedSearchUrlUpdate: function (val) {
452
+ this.pubmedSearchUrl = val
453
+ },
454
+ showConnectivity: function () {
272
455
  // move the map center to highlighted area
273
- const featureIds = entry.featureId || [];
456
+ const featureIds = this.entry.featureId || [];
274
457
  // connected to flatmapvuer > moveMap(featureIds) function
275
458
  this.$emit('show-connectivity', featureIds);
276
459
  },
@@ -358,28 +541,28 @@ export default {
358
541
  }
359
542
 
360
543
  // Origins
361
- if (this.origins?.length) {
544
+ if (this.entry.origins?.length) {
362
545
  const title = 'Origin';
363
- const origins = this.origins;
364
- const originsWithDatasets = this.originsWithDatasets;
546
+ const origins = this.entry.origins;
547
+ const originsWithDatasets = this.entry.originsWithDatasets;
365
548
  const transformedOrigins = transformData(title, origins, originsWithDatasets);
366
549
  contentArray.push(transformedOrigins);
367
550
  }
368
551
 
369
552
  // Components
370
- if (this.components?.length) {
553
+ if (this.entry.components?.length) {
371
554
  const title = 'Components';
372
- const components = this.components;
373
- const componentsWithDatasets = this.componentsWithDatasets;
555
+ const components = this.entry.components;
556
+ const componentsWithDatasets = this.entry.componentsWithDatasets;
374
557
  const transformedComponents = transformData(title, components, componentsWithDatasets);
375
558
  contentArray.push(transformedComponents);
376
559
  }
377
560
 
378
561
  // Destination
379
- if (this.destinations?.length) {
562
+ if (this.entry.destinations?.length) {
380
563
  const title = 'Destination';
381
- const destinations = this.destinations;
382
- const destinationsWithDatasets = this.destinationsWithDatasets;
564
+ const destinations = this.entry.destinations;
565
+ const destinationsWithDatasets = this.entry.destinationsWithDatasets;
383
566
  const transformedDestinations = transformData(title, destinations, destinationsWithDatasets);
384
567
  contentArray.push(transformedDestinations);
385
568
  }
@@ -399,9 +582,9 @@ export default {
399
582
  },
400
583
  toggleConnectivityTooltip: function (name, option) {
401
584
  const allWithDatasets = [
402
- ...this.componentsWithDatasets,
403
- ...this.destinationsWithDatasets,
404
- ...this.originsWithDatasets,
585
+ ...this.entry.componentsWithDatasets,
586
+ ...this.entry.destinationsWithDatasets,
587
+ ...this.entry.originsWithDatasets,
405
588
  ];
406
589
  const names = name.split(','); // some features have more than one value
407
590
  const data = [];
@@ -477,73 +660,9 @@ export default {
477
660
  this.connectivityError = null;
478
661
  }, ERROR_TIMEOUT);
479
662
  },
480
- updateConnectionsData: function (source) {
481
- this.origins = source.origins;
482
- this.components = source.components;
483
- this.destinations = source.destinations;
484
- this.originsWithDatasets = source.originsWithDatasets;
485
- this.componentsWithDatasets = source.componentsWithDatasets;
486
- this.destinationsWithDatasets = source.destinationsWithDatasets;
487
-
488
- this.updatedCopyContent = this.getUpdateCopyContent();
489
- },
490
- onConnectivitySourceChange: function (val) {
491
- if (this.connectivitySource === 'map') {
492
- this.getConnectionsFromMap(this.mapuuid, this.entry.featureId[0])
493
- .then((response) => {
494
- this.connectivityFromMap = response;
495
- processConnectivity(this.flatmapApi, this.sckanVersion, response)
496
- .then((result) => {
497
- const mapSource = {
498
- origins: result.labels.origins,
499
- components: result.labels.components,
500
- destinations: result.labels.destinations,
501
- originsWithDatasets: result.withDatasets.originsWithDatasets,
502
- componentsWithDatasets: result.withDatasets.componentsWithDatasets,
503
- destinationsWithDatasets: result.withDatasets.destinationsWithDatasets,
504
- }
505
- this.updateConnectionsData(mapSource);
506
- })
507
- });
508
- } else {
509
- this.connectivityFromMap = null;
510
- this.updateConnectionsData(this.entry);
511
- }
512
- },
513
- getConnectionsFromMap: async function (mapuuid, pathId) {
514
- const url = this.flatmapApi + `flatmap/${mapuuid}/connectivity/${pathId}`;
515
-
516
- try {
517
- const response = await fetch(url);
518
- if (!response.ok) {
519
- throw new Error(`Response status: ${response.status}`);
520
- }
521
-
522
- return await response.json();
523
- } catch (error) {
524
- throw new Error(error);
525
- }
526
- },
527
- onToggleConnectivityTooltip: function (data) {
528
- const {name, option} = data;
529
- this.toggleConnectivityTooltip(name, option);
530
- },
531
- onConnectivityActionClick: function (data) {
532
- EventBus.emit('onConnectivityActionClick', data);
533
- },
534
663
  },
535
664
  mounted: function () {
536
- this.sckanVersion = this.entry['knowledge-source'];
537
- this.mapuuid = this.entry['mapuuid'];
538
- this.mapId = this.entry['mapId'];
539
- this.flatmapApi = this.envVars.FLATMAPAPI_LOCATION;
540
- this.updateConnectionsData(this.entry);
541
-
542
- // TODO: only rat flatmap has dual connections now
543
- if (this.mapId === 'rat-flatmap') {
544
- this.dualConnectionSource = true;
545
- }
546
-
665
+ this.updatedCopyContent = this.getUpdateCopyContent();
547
666
  EventBus.on('connectivity-graph-error', (errorInfo) => {
548
667
  this.pushConnectivityError(errorInfo);
549
668
  });
@@ -571,6 +690,11 @@ export default {
571
690
  }
572
691
  }
573
692
 
693
+ .button-container {
694
+ display: flex;
695
+ justify-content: space-between;
696
+ }
697
+
574
698
  .title {
575
699
  text-align: left;
576
700
  // width: 16em;
@@ -624,8 +748,17 @@ export default {
624
748
  }
625
749
  }
626
750
 
751
+ .info,
627
752
  .alert {
628
753
  color: #8300bf;
754
+ }
755
+
756
+ .info {
757
+ transform: rotate(180deg);
758
+ margin-left: 8px;
759
+ }
760
+
761
+ .alert {
629
762
  margin-left: 5px;
630
763
  vertical-align: text-bottom;
631
764
 
@@ -685,6 +818,39 @@ export default {
685
818
  text-transform: uppercase;
686
819
  }
687
820
 
821
+ .attribute-content {
822
+ font-size: 14px;
823
+ font-weight: 500;
824
+ transition: color 0.25s ease;
825
+ position: relative;
826
+ cursor: default;
827
+
828
+ &:hover {
829
+ color: $app-primary-color;
830
+ }
831
+
832
+ + .attribute-content {
833
+ &::before {
834
+ content: "";
835
+ width: 90%;
836
+ height: 1px;
837
+ background-color: var(--el-border-color);
838
+ position: absolute;
839
+ top: 0;
840
+ left: 0;
841
+ }
842
+ }
843
+
844
+ &:last-of-type {
845
+ margin-bottom: 0.5em;
846
+ }
847
+ }
848
+
849
+ .popover-container {
850
+ height: 100%;
851
+ width: 100%;
852
+ }
853
+
688
854
  .main {
689
855
  .el-button.is-round {
690
856
  border-radius: 4px;
@@ -741,12 +907,6 @@ export default {
741
907
  margin-top: 0 !important;
742
908
  margin-left: 10px !important;
743
909
  }
744
-
745
- > div:first-child {
746
- display: flex;
747
- align-items: center;
748
- gap: 0.5rem;
749
- }
750
910
  }
751
911
 
752
912
  .population-display {
@@ -757,23 +917,6 @@ export default {
757
917
  justify-content: space-between;
758
918
  border-bottom: 1px solid $app-primary-color;
759
919
  padding-bottom: 0.5rem !important;
760
-
761
- &.population-display-toolbar {
762
- flex-direction: column !important;
763
- align-items: start;
764
-
765
- .buttons-row {
766
- display: flex;
767
- flex-direction: row;
768
- align-items: center;
769
- justify-content: space-between;
770
- width: 100%;
771
- }
772
- }
773
-
774
- .el-radio {
775
- margin-right: 1rem;
776
- }
777
920
  }
778
921
 
779
922
  .tooltip-container {
@@ -895,4 +1038,25 @@ export default {
895
1038
  margin-top: -1.25rem;
896
1039
  }
897
1040
  }
1041
+
1042
+ .connectivity-error-container {
1043
+ position: sticky;
1044
+ bottom: 0.5rem;
1045
+ width: 100%;
1046
+ min-height: 31px; // placeholder
1047
+ margin-top: -10px !important;
1048
+ display: flex;
1049
+ flex-direction: row;
1050
+ align-items: center;
1051
+ justify-content: center;
1052
+ }
1053
+
1054
+ .connectivity-error {
1055
+ width: fit-content;
1056
+ font-size: 12px;
1057
+ padding: 0.25rem 0.5rem;
1058
+ background-color: var(--el-color-error-light-9);
1059
+ border-radius: var(--el-border-radius-small);
1060
+ border: 1px solid var(--el-color-error);
1061
+ }
898
1062
  </style>