@abi-software/map-side-bar 2.10.0-beta.0 → 2.10.0-beta.2

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.10.0-beta.0",
3
+ "version": "2.10.0-beta.2",
4
4
  "files": [
5
5
  "dist/*",
6
6
  "src/*",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@abi-software/gallery": "^1.1.2",
42
- "@abi-software/map-utilities": "^1.6.0",
42
+ "@abi-software/map-utilities": "1.6.1-beta.6",
43
43
  "@abi-software/svg-sprite": "^1.0.1",
44
44
  "@element-plus/icons-vue": "^2.3.1",
45
45
  "algoliasearch": "^4.10.5",
@@ -6,7 +6,18 @@
6
6
  <div class="card-title">{{ capitalise(entry.label) }}</div>
7
7
  <template v-for="field in displayFields" :key="field">
8
8
  <div class="card-details" v-if="entry[field]">
9
- <strong>{{ field }}:</strong> {{ entry[field] }}
9
+ <strong>{{ field }}:</strong>
10
+ <div v-if="field === 'nerve-label'" class="card-tags">
11
+ <div v-for="nerve in entry[field]" :key="nerve.nerve">
12
+ <el-tag type="primary" size="small">
13
+ {{ nerve.nerve }}
14
+ </el-tag>
15
+ <el-tag type="primary" size="small" v-for="subNerve in nerve.subNerves">
16
+ {{ subNerve }}
17
+ </el-tag>
18
+ </div>
19
+ </div>
20
+ <span v-else>{{ entry[field] }}</span>
10
21
  </div>
11
22
  </template>
12
23
  </div>
@@ -19,7 +30,7 @@ export default {
19
30
  name: "ConnectivityCard",
20
31
  data() {
21
32
  return {
22
- displayFields: ["id"],
33
+ displayFields: ["id", "nerve-label"],
23
34
  };
24
35
  },
25
36
  props: {
@@ -90,4 +101,24 @@ export default {
90
101
  line-height: 1.5;
91
102
  letter-spacing: 1.05px;
92
103
  }
104
+
105
+ .el-tag {
106
+ border-radius: 4px!important;
107
+ font-size: 0.75rem!important;
108
+ margin: 2px!important;
109
+ background: #f9f2fc!important;
110
+ border: 1px solid $app-primary-color!important;
111
+ color: $app-primary-color!important;
112
+ }
113
+
114
+ .card-tags {
115
+ overflow-y: scroll;
116
+ max-height: 4.1rem;
117
+
118
+ &::-webkit-scrollbar {
119
+ display: none; /* Hide scrollbar for Chrome, Safari and Opera */
120
+ }
121
+ -ms-overflow-style: none; /* Hide scrollbar for IE and Edge */
122
+ scrollbar-width: none; /* Hide scrollbar for Firefox */
123
+ }
93
124
  </style>
@@ -22,7 +22,7 @@
22
22
  <el-button
23
23
  link
24
24
  class="el-button-link"
25
- @click="openSearch([], '')"
25
+ @click="onResetClick"
26
26
  size="large"
27
27
  >
28
28
  Reset
@@ -133,7 +133,9 @@ import {
133
133
  ElIcon as Icon,
134
134
  ElInput as Input,
135
135
  ElPagination as Pagination,
136
+ ElMessage as Message,
136
137
  } from "element-plus";
138
+ import 'element-plus/es/components/message/style/css';
137
139
  import EventBus from './EventBus.js'
138
140
  import SearchFilters from "./SearchFilters.vue";
139
141
  import SearchHistory from "./SearchHistory.vue";
@@ -244,9 +246,6 @@ export default {
244
246
  this.initLoading = false;
245
247
  this.numberOfHits = this.results.length;
246
248
  // knowledge is from the neuron click if there is 'ready' property
247
- if (this.numberOfHits === 1 && !('ready' in this.results[0])) {
248
- this.onConnectivityCollapseChange(this.results[0]);
249
- }
250
249
  if (this.numberOfHits > 0 && ('ready' in this.results[0])) {
251
250
  this.$refs.filtersRef.checkShowAllBoxes();
252
251
  this.searchInput = '';
@@ -261,9 +260,12 @@ export default {
261
260
  JSON.stringify(newVal) !== JSON.stringify(oldVal) &&
262
261
  newVal.length === 1 && newVal[0].ready
263
262
  ) {
264
- // if the changed property is connectivity source,
265
- // or two different maps in split view, do not collapse
263
+ const hasValidFacet = this.filter.some(f => f.facet !== "Show all");
266
264
  if (
265
+ // card should not be expanded if only one entry and from neuron click
266
+ (this.numberOfHits === 1 && !this.searchInput && !hasValidFacet)||
267
+ // if the changed property is connectivity source,
268
+ // or two different maps in split view, do not collapse
267
269
  (
268
270
  newVal[0].connectivitySource !== oldVal[0].connectivitySource ||
269
271
  newVal[0].mapId !== oldVal[0].mapId
@@ -303,7 +305,6 @@ export default {
303
305
  },
304
306
  onConnectivityClicked: function (data) {
305
307
  this.searchInput = data.query;
306
- this.filter = data.filter;
307
308
  this.searchAndFilterUpdate();
308
309
  },
309
310
  collapseChange:function (data) {
@@ -342,13 +343,13 @@ export default {
342
343
  // disable hover changes when show connectivity is clicked
343
344
  if (!this.freezed) {
344
345
  let payload = { tabType: "connectivity" };
345
-
346
+
346
347
  if (data) {
347
348
  payload = {...payload, ...data};
348
349
  } else if (this.expandedData) {
349
350
  payload = {...payload, ...this.expandedData};
350
351
  }
351
-
352
+
352
353
  this.$emit("hover-changed", payload);
353
354
  }
354
355
  },
@@ -359,17 +360,50 @@ export default {
359
360
  },
360
361
  resetSearchIfNoActiveSearch: function() {
361
362
  const hasValidFacet = this.filter.some(f => f.facet !== "Show all");
362
- if ((!this.searchInput && !hasValidFacet) || this.numberOfHits === 0) {
363
+ if (!this.searchInput && !hasValidFacet) {
363
364
  this.openSearch([], '');
364
365
  }
365
366
  },
367
+ onResetClick: function () {
368
+ this.openSearch([], '');
369
+ this.$emit('connectivity-explorer-reset', []);
370
+ },
366
371
  openSearch: function (filter, search = "") {
367
372
  this.searchInput = search;
368
373
  this.resetPageNavigation();
369
374
  //Proceed normally if cascader is ready
370
375
  if (this.cascaderIsReady) {
371
- this.filter =
372
- this.$refs.filtersRef.getHierarchicalValidatedFilters(filter);
376
+ const validatedFilters = this.$refs.filtersRef.getHierarchicalValidatedFilters(filter);
377
+ const notFoundItems = validatedFilters.notFound || [];
378
+ this.filter = validatedFilters.result;
379
+
380
+ // Show not found filter items warning message
381
+ notFoundItems.forEach((notFoundItem) => {
382
+ const itemLabel = notFoundItem.tagLabel || notFoundItem.facet;
383
+ const itemLabelLowerCase = itemLabel.charAt(0).toLowerCase() + itemLabel.slice(1);
384
+ let message = '';
385
+ if (notFoundItem.term.toLowerCase() === 'origin') {
386
+ message = `There are no neuron populations beginning at <strong>${itemLabelLowerCase}</strong>.`;
387
+ } else if (notFoundItem.term.toLowerCase() === 'via') {
388
+ message = `There are no neuron populations that run through <strong>${itemLabelLowerCase}</strong>.`;
389
+ } else if (notFoundItem.term.toLowerCase() === 'destination') {
390
+ message = `There are no neuron populations terminating at <strong>${itemLabelLowerCase}</strong>.`;
391
+ } else {
392
+ message = `There are no neuron populations beginning, terminating, or running through <strong>${itemLabelLowerCase}</strong>.`
393
+ }
394
+ Message({
395
+ dangerouslyUseHTMLString: true,
396
+ message: message,
397
+ appendTo: this.$el,
398
+ showClose: true,
399
+ offset: 113,
400
+ });
401
+ });
402
+
403
+ if (notFoundItems.length) {
404
+ this.$emit('connectivity-explorer-reset', notFoundItems);
405
+ }
406
+
373
407
  //Facets provided but cannot find at least one valid
374
408
  //facet. Tell the users the search is invalid and reset
375
409
  //facets check boxes.
@@ -437,6 +471,7 @@ export default {
437
471
  searchAndFilterUpdate: function () {
438
472
  this.resetPageNavigation();
439
473
  this.searchKnowledge(this.filter, this.searchInput);
474
+ this.$refs.filtersRef.setCascader(this.filter);
440
475
  this.searchHistoryUpdate(this.filter, this.searchInput);
441
476
  },
442
477
  searchHistoryUpdate: function (filters, search) {
@@ -495,6 +530,12 @@ export default {
495
530
  }
496
531
  });
497
532
  },
533
+ getSearch: function () {
534
+ return this.searchInput;
535
+ },
536
+ getFilters: function () {
537
+ return this.filter;
538
+ },
498
539
  },
499
540
  mounted: function () {
500
541
  localStorage.removeItem('connectivity-active-view');
@@ -554,7 +595,7 @@ export default {
554
595
  transition: all 0.3s ease;
555
596
 
556
597
  .connectivity-card {
557
- max-height: 200px;
598
+ max-height: 215px;
558
599
  }
559
600
  .connectivity-info {
560
601
  background-color: #f7faff;
@@ -567,6 +608,7 @@ export default {
567
608
  width: 298px !important;
568
609
  height: 40px;
569
610
  padding-right: 14px;
611
+ font-family: inherit;
570
612
 
571
613
  :deep(.el-input__inner) {
572
614
  font-family: inherit;
@@ -607,6 +649,24 @@ export default {
607
649
  padding: 1rem;
608
650
  }
609
651
 
652
+ .content-card :deep(.el-message) {
653
+ position: absolute !important;
654
+ width: 80%;
655
+ font-size: 12px;
656
+ border-radius: var(--el-border-radius-base);
657
+ --el-message-bg-color: var(--el-color-error-light-9);
658
+ --el-message-border-color: var(--el-color-error);
659
+ --el-message-text-color: var(--el-text-color-primary);
660
+
661
+ .el-icon.el-message__icon {
662
+ display: none;
663
+ }
664
+
665
+ .el-message__closeBtn {
666
+ margin-left: auto;
667
+ }
668
+ }
669
+
610
670
  .content {
611
671
  // width: 515px;
612
672
  flex: 1 1 auto;
@@ -668,7 +728,6 @@ export default {
668
728
  background-color: transparent !important;
669
729
  padding: 2px !important;
670
730
  height: auto !important;
671
- margin-left: 4px!important;
672
731
 
673
732
  &:hover {
674
733
  text-decoration-color: transparent;
@@ -92,7 +92,7 @@
92
92
  <div class="block buttons-row">
93
93
  <span>Connectivity from:</span>
94
94
  <el-radio-group v-model="connectivitySource" @change="onConnectivitySourceChange">
95
- <el-radio value="map">Map</el-radio>
95
+ <el-radio value="map" :disabled="noMapConnectivity">Map</el-radio>
96
96
  <el-radio value="sckan">SCKAN</el-radio>
97
97
  </el-radio-group>
98
98
  <el-button
@@ -110,6 +110,45 @@
110
110
  </div>
111
111
  </div>
112
112
 
113
+ <div v-if="entry['nerve-label']" class="block">
114
+ <div class="attribute-title-container">
115
+ <span class="attribute-title">Nerves</span>
116
+ </div>
117
+ <div v-for="(nerve, i) in entry['nerve-label']">
118
+ <div
119
+ class="attribute-content"
120
+ :origin-item-label="nerve.nerve"
121
+ :key="nerve.nerve"
122
+ >
123
+ <span>{{ capitalise(nerve.nerve) }}</span>
124
+ </div>
125
+ <div
126
+ v-for="(subNerve, i) in nerve.subNerves"
127
+ class="attribute-content"
128
+ style="margin-left: 1rem"
129
+ :origin-item-label="subNerve"
130
+ :key="subNerve"
131
+ @mouseenter="onConnectivityHovered(subNerve)"
132
+ @mouseleave="onConnectivityHovered()"
133
+ >
134
+ <el-popover
135
+ width="150"
136
+ trigger="hover"
137
+ :teleported="false"
138
+ popper-class="popover-origin-help"
139
+ >
140
+ <template #reference>
141
+ <el-icon class="magnify-glass" @click="onConnectivityClicked(subNerve)">
142
+ <el-icon-search />
143
+ </el-icon>
144
+ </template>
145
+ <span>Search sub nerve</span>
146
+ </el-popover>
147
+ <span>{{ capitalise(subNerve) }}</span>
148
+ </div>
149
+ </div>
150
+ </div>
151
+
113
152
  <div class="content-container content-container-connectivity" v-show="activeView === 'listView'">
114
153
  <connectivity-list
115
154
  v-loading="connectivityLoading"
@@ -228,7 +267,8 @@ export default {
228
267
  updatedCopyContent: '',
229
268
  activeView: 'listView',
230
269
  connectivityLoading: false,
231
- connectivitySource: 'sckan',
270
+ connectivitySource: 'map', // sckan
271
+ noMapConnectivity: false,
232
272
  connectivityError: {},
233
273
  graphViewLoaded: false,
234
274
  connectivityFromMap: null,
@@ -277,7 +317,7 @@ export default {
277
317
  return this.entry.destinationsWithDatasets;
278
318
  },
279
319
  resources: function () {
280
- return this.entry.hyperlinks;
320
+ return this.entry.hyperlinks || [];
281
321
  },
282
322
  sckanVersion: function () {
283
323
  return this.entry.knowledgeSource;
@@ -300,6 +340,7 @@ export default {
300
340
  this.graphViewLoaded = true;
301
341
  }
302
342
  this.connectivitySource = this.entry.connectivitySource;
343
+ this.noMapConnectivity = this.entry.noMapConnectivity;
303
344
  this.updateGraphConnectivity();
304
345
  this.connectivityLoading = false;
305
346
  // only emit to scroll when entire entry content changes
@@ -407,6 +448,15 @@ export default {
407
448
  return contentString;
408
449
  }
409
450
 
451
+ // Nerves
452
+ if (this.entry['nerve-label']?.length) {
453
+ const title = 'Nerves';
454
+ const nerves = this.entry['nerve-label'];
455
+ const nerveLabels = nerves.map(nerve => Object.values(nerve)).flat(Infinity);
456
+ const transformedNerves = transformData(title, nerveLabels);
457
+ contentArray.push(transformedNerves);
458
+ }
459
+
410
460
  // Origins
411
461
  if (this.origins?.length) {
412
462
  const title = 'Origin';
@@ -471,6 +521,7 @@ export default {
471
521
  onConnectivityHovered: function (label) {
472
522
  const payload = {
473
523
  connectivityInfo: this.entry,
524
+ label: label,
474
525
  data: label ? this.getConnectivityDatasets(label) : [],
475
526
  };
476
527
  // type: to show error only for click event
@@ -528,19 +579,23 @@ export default {
528
579
  }
529
580
  },
530
581
  getConnectionsFromMap: async function () {
531
- if (this.entry.mapuuid) {
582
+ if (this.entry.mapuuid) {
532
583
  const url =
533
584
  this.flatmapApi +
534
585
  `flatmap/${this.entry.mapuuid}/connectivity/${this.entry.featureId[0]}`;
535
-
586
+
536
587
  try {
537
588
  const response = await fetch(url);
538
589
  if (!response.ok) {
539
590
  throw new Error(`Response status: ${response.status}`);
540
591
  }
541
-
592
+
542
593
  return await response.json();
543
594
  } catch (error) {
595
+ EventBus.emit('connectivity-source-change', {
596
+ entry: this.entry,
597
+ connectivitySource: "sckan",
598
+ });
544
599
  throw new Error(error);
545
600
  }
546
601
  }
@@ -915,4 +970,46 @@ export default {
915
970
  margin-top: -1.25rem;
916
971
  }
917
972
  }
973
+
974
+ .attribute-content {
975
+ font-size: 14px;
976
+ font-weight: 500;
977
+ transition: color 0.25s ease;
978
+ position: relative;
979
+ cursor: default;
980
+ padding-left: 16px;
981
+
982
+ .magnify-glass {
983
+ display: none;
984
+ position: absolute;
985
+ top: 0;
986
+ left: 0;
987
+ }
988
+
989
+ &:hover {
990
+ color: $app-primary-color;
991
+
992
+ .magnify-glass {
993
+ display: block;
994
+ padding-top: 4px;
995
+ cursor: pointer;
996
+ }
997
+ }
998
+
999
+ + .attribute-content {
1000
+ &::before {
1001
+ content: "";
1002
+ width: 90%;
1003
+ height: 1px;
1004
+ background-color: var(--el-border-color);
1005
+ position: absolute;
1006
+ top: 0;
1007
+ left: 0;
1008
+ }
1009
+ }
1010
+
1011
+ &:last-of-type {
1012
+ margin-bottom: 0.5em;
1013
+ }
1014
+ }
918
1015
  </style>
@@ -79,7 +79,9 @@ import {
79
79
  ElIcon as Icon,
80
80
  ElInput as Input,
81
81
  ElPagination as Pagination,
82
+ ElMessage as Message,
82
83
  } from 'element-plus'
84
+ import 'element-plus/es/components/message/style/css';
83
85
  import SearchFilters from './SearchFilters.vue'
84
86
  import SearchHistory from './SearchHistory.vue'
85
87
  import DatasetCard from './DatasetCard.vue'
@@ -192,8 +194,20 @@ export default {
192
194
  this.resetPageNavigation()
193
195
  //Proceed normally if cascader is ready
194
196
  if (this.cascaderIsReady) {
195
- this.filter =
196
- this.$refs.filtersRef.getHierarchicalValidatedFilters(filter)
197
+ const validatedFilters = this.$refs.filtersRef.getHierarchicalValidatedFilters(filter);
198
+ const notFoundItems = validatedFilters.notFound || [];
199
+ this.filter = validatedFilters.result;
200
+
201
+ // Show not found filter items warning message
202
+ notFoundItems.forEach((notFoundItem) => {
203
+ Message({
204
+ message: `${notFoundItem.facet} cannot be found in ${notFoundItem.term}!`,
205
+ appendTo: this.$el,
206
+ showClose: true,
207
+ offset: 113,
208
+ });
209
+ });
210
+
197
211
  //Facets provided but cannot find at least one valid
198
212
  //facet. Tell the users the search is invalid and reset
199
213
  //facets check boxes.
@@ -416,7 +430,6 @@ export default {
416
430
  ? element['abi-contextual-information']
417
431
  : undefined,
418
432
  segmentation: element['mbf-segmentation'],
419
- //omex format will be the preferred mimetype
420
433
  simulation: element['abi-simulation-omex-file'] ? element['abi-simulation-omex-file'] : element['abi-simulation-file'],
421
434
  additionalLinks: element.additionalLinks,
422
435
  detailsReady: true,
@@ -464,6 +477,12 @@ export default {
464
477
  this.filter = item.filters
465
478
  this.openSearch([...item.filters], item.search);
466
479
  },
480
+ getSearch: function () {
481
+ return this.searchInput
482
+ },
483
+ getFilters: function () {
484
+ return this.filter;
485
+ },
467
486
  },
468
487
  mounted: function () {
469
488
  // initialise algolia
@@ -564,6 +583,20 @@ export default {
564
583
  padding: 1rem;
565
584
  }
566
585
 
586
+ .content-card :deep(.el-message) {
587
+ position: absolute !important;
588
+ width: 80%;
589
+ font-size: 12px;
590
+ border-radius: var(--el-border-radius-base);
591
+ --el-message-bg-color: var(--el-color-error-light-9);
592
+ --el-message-border-color: var(--el-color-error);
593
+ --el-message-text-color: var(--el-text-color-primary);
594
+
595
+ .el-icon.el-message__icon {
596
+ display: none;
597
+ }
598
+ }
599
+
567
600
  .content {
568
601
  // width: 515px;
569
602
  flex: 1 1 auto;
@@ -348,8 +348,6 @@ export default {
348
348
  } else {
349
349
  const filePath = simulation.dataset.path
350
350
  const id = simulation.identifier
351
- //Despite of the name, this method can be used to retreive
352
- //the thumbnail information for any none scaffold type thumbnail
353
351
  const thumbnail = this.getThumbnailForPlot(
354
352
  simulation,
355
353
  this.entry.thumbnails