@abi-software/map-utilities 1.4.1-beta.0 → 1.4.1-beta.1

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-utilities",
3
- "version": "1.4.1-beta.0",
3
+ "version": "1.4.1-beta.1",
4
4
  "files": [
5
5
  "dist/*",
6
6
  "src/*",
@@ -30,6 +30,11 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@abi-software/svg-sprite": "^1.0.1",
33
+ "@citation-js/core": "^0.7.14",
34
+ "@citation-js/plugin-bibtex": "^0.7.17",
35
+ "@citation-js/plugin-csl": "^0.7.14",
36
+ "@citation-js/plugin-doi": "^0.7.16",
37
+ "@citation-js/plugin-pubmed": "^0.3.0",
33
38
  "@element-plus/icons-vue": "^2.3.1",
34
39
  "cytoscape": "^3.30.2",
35
40
  "element-plus": "2.8.4",
@@ -160,6 +160,10 @@ export default {
160
160
  type: Array,
161
161
  default: [],
162
162
  },
163
+ connectivityFromMap: {
164
+ type: Object,
165
+ default: () => null,
166
+ },
163
167
  },
164
168
  data: function () {
165
169
  return {
@@ -184,26 +188,19 @@ export default {
184
188
  connectivityGraphContainer: null,
185
189
  };
186
190
  },
191
+ watch: {
192
+ connectivityFromMap: function (oldVal, newVal) {
193
+ if (oldVal != newVal) {
194
+ this.start();
195
+ }
196
+ }
197
+ },
187
198
  mounted() {
188
199
  this.showSpinner();
189
200
  this.updateTooltipContainer();
190
201
  this.refreshCache();
191
202
  this.loadCacheData();
192
- this.run()
193
- .then((res) => {
194
- if (res?.success) {
195
- this.showGraph(this.entry);
196
- } else if (res?.error) {
197
- this.loadingError = res.error;
198
- } else {
199
- this.loadingError = 'Loading error!';
200
- }
201
- this.hideSpinner();
202
- })
203
- .catch((error) => {
204
- this.loadingError = 'Loading error!';
205
- this.hideSpinner();
206
- });
203
+ this.start();
207
204
  },
208
205
  methods: {
209
206
  updateTooltipContainer: function () {
@@ -275,6 +272,23 @@ export default {
275
272
 
276
273
  sessionStorage.setItem('connectivity-graph-expiry', expiry);
277
274
  },
275
+ start: function () {
276
+ this.run()
277
+ .then((res) => {
278
+ if (res?.success) {
279
+ this.showGraph(this.entry);
280
+ } else if (res?.error) {
281
+ this.loadingError = res.error;
282
+ } else {
283
+ this.loadingError = 'Loading error!';
284
+ }
285
+ this.hideSpinner();
286
+ })
287
+ .catch((error) => {
288
+ this.loadingError = 'Loading error!';
289
+ this.hideSpinner();
290
+ });
291
+ },
278
292
  run: async function () {
279
293
  if (!this.schemaVersion) {
280
294
  this.schemaVersion = await this.getSchemaVersion();
@@ -305,8 +319,24 @@ export default {
305
319
  showGraph: async function (neuronPath) {
306
320
  const graphCanvas = this.$refs.graphCanvas;
307
321
 
322
+ // Update label data
323
+ if (this.connectivityFromMap) {
324
+ this.cacheLabels(this.connectivityFromMap);
325
+ await this.getCachedTermLabels();
326
+ }
327
+
308
328
  this.connectivityGraph = new ConnectivityGraph(this.labelCache, graphCanvas);
309
- await this.connectivityGraph.addConnectivity(this.knowledgeByPath.get(neuronPath));
329
+ const connectivityInfo = this.knowledgeByPath.get(neuronPath);
330
+
331
+ // Update connectivity
332
+ if (this.connectivityFromMap) {
333
+ connectivityInfo.axons = this.connectivityFromMap.axons;
334
+ connectivityInfo.connectivity = this.connectivityFromMap.connectivity;
335
+ connectivityInfo.dendrites = this.connectivityFromMap.dendrites;
336
+ connectivityInfo.somas = this.connectivityFromMap.somas;
337
+ }
338
+
339
+ await this.connectivityGraph.addConnectivity(connectivityInfo);
310
340
 
311
341
  this.connectivityGraph.showConnectivity(graphCanvas);
312
342
 
@@ -0,0 +1,395 @@
1
+ <template>
2
+ <div class="connectivity-list">
3
+ {{ entry.paths }}
4
+ <div v-if="origins && origins.length > 0" class="block">
5
+ <div class="attribute-title-container">
6
+ <span class="attribute-title">Origin</span>
7
+ <el-popover
8
+ width="250"
9
+ trigger="hover"
10
+ :teleported="false"
11
+ popper-class="popover-origin-help"
12
+ >
13
+ <template #reference>
14
+ <el-icon class="info"><el-icon-warning /></el-icon>
15
+ </template>
16
+ <span style="word-break: keep-all">
17
+ <i>Origin</i> {{ originDescription }}
18
+ </span>
19
+
20
+ </el-popover>
21
+ </div>
22
+ <div
23
+ v-for="(origin, i) in origins"
24
+ class="attribute-content"
25
+ :origin-item-label="origin"
26
+ :key="origin"
27
+ @mouseenter="toggleConnectivityTooltip(origin, {show: true})"
28
+ @mouseleave="toggleConnectivityTooltip(origin, {show: false})"
29
+ >
30
+ {{ capitalise(origin) }}
31
+ </div>
32
+ <el-button
33
+ v-show="
34
+ originsWithDatasets && originsWithDatasets.length > 0 &&
35
+ shouldShowExploreButton(originsWithDatasets)
36
+ "
37
+ class="button"
38
+ id="open-dendrites-button"
39
+ @click="openDendrites"
40
+ >
41
+ Explore origin data
42
+ </el-button>
43
+ </div>
44
+ <div
45
+ v-if="components && components.length > 0"
46
+ class="block"
47
+ >
48
+ <div class="attribute-title-container">
49
+ <div class="attribute-title">Components</div>
50
+ </div>
51
+ <div
52
+ v-for="(component, i) in components"
53
+ class="attribute-content"
54
+ :component-item-label="component"
55
+ :key="component"
56
+ @mouseenter="toggleConnectivityTooltip(component, {show: true})"
57
+ @mouseleave="toggleConnectivityTooltip(component, {show: false})"
58
+ >
59
+ {{ capitalise(component) }}
60
+ </div>
61
+ </div>
62
+ <div
63
+ v-if="destinations && destinations.length > 0"
64
+ class="block"
65
+ >
66
+ <div class="attribute-title-container">
67
+ <span class="attribute-title">Destination</span>
68
+ <el-popover
69
+ width="250"
70
+ trigger="hover"
71
+ :teleported="false"
72
+ popper-class="popover-origin-help"
73
+ >
74
+ <template #reference>
75
+ <el-icon class="info"><el-icon-warning /></el-icon>
76
+ </template>
77
+ <span style="word-break: keep-all">
78
+ <i>Destination</i> is where the axons terminate
79
+ </span>
80
+ </el-popover>
81
+ </div>
82
+ <div
83
+ v-for="(destination, i) in destinations"
84
+ class="attribute-content"
85
+ :destination-item-label="destination"
86
+ :key="destination"
87
+ @mouseenter="toggleConnectivityTooltip(destination, {show: true})"
88
+ @mouseleave="toggleConnectivityTooltip(destination, {show: false})"
89
+ >
90
+ {{ capitalise(destination) }}
91
+ </div>
92
+ <el-button
93
+ v-show="
94
+ destinationsWithDatasets &&
95
+ destinationsWithDatasets.length > 0 &&
96
+ shouldShowExploreButton(destinationsWithDatasets)
97
+ "
98
+ class="button"
99
+ @click="openAxons"
100
+ >
101
+ Explore destination data
102
+ </el-button>
103
+ </div>
104
+ <div
105
+ v-show="
106
+ componentsWithDatasets &&
107
+ componentsWithDatasets.length > 0 &&
108
+ shouldShowExploreButton(componentsWithDatasets)
109
+ "
110
+ class="block"
111
+ >
112
+ <el-button
113
+ class="button"
114
+ @click="openAll"
115
+ >
116
+ Search for data on components
117
+ </el-button>
118
+ </div>
119
+
120
+ <div class="connectivity-error-container">
121
+ <div class="connectivity-error" v-if="connectivityError">
122
+ <strong v-if="connectivityError.errorConnectivities">
123
+ {{ connectivityError.errorConnectivities }}
124
+ </strong>
125
+ {{ connectivityError.errorMessage }}
126
+ </div>
127
+ </div>
128
+ </div>
129
+ </template>
130
+
131
+ <script>
132
+ import {
133
+ Warning as ElIconWarning,
134
+ } from '@element-plus/icons-vue'
135
+ import {
136
+ ElButton as Button,
137
+ ElContainer as Container,
138
+ ElIcon as Icon,
139
+ } from 'element-plus'
140
+ import { capitalise } from '../utilities'
141
+
142
+ export default {
143
+ name: 'ConnectivityList',
144
+ components: {
145
+ Button,
146
+ Container,
147
+ Icon,
148
+ ElIconWarning,
149
+ },
150
+ props: {
151
+ entry: {
152
+ type: Object,
153
+ default: () => ({
154
+ destinations: [],
155
+ origins: [],
156
+ components: [],
157
+ destinationsWithDatasets: [],
158
+ originsWithDatasets: [],
159
+ componentsWithDatasets: [],
160
+ resource: undefined,
161
+ featuresAlert: undefined,
162
+ }),
163
+ },
164
+ origins: {
165
+ type: Array,
166
+ default: () => []
167
+ },
168
+ components: {
169
+ type: Array,
170
+ default: () => []
171
+ },
172
+ destinations: {
173
+ type: Array,
174
+ default: () => []
175
+ },
176
+ originsWithDatasets: {
177
+ type: Array,
178
+ default: () => []
179
+ },
180
+ componentsWithDatasets: {
181
+ type: Array,
182
+ default: () => []
183
+ },
184
+ destinationsWithDatasets: {
185
+ type: Array,
186
+ default: () => []
187
+ },
188
+ availableAnatomyFacets: {
189
+ type: Array,
190
+ default: () => [],
191
+ },
192
+ connectivityError: {
193
+ type: Object,
194
+ default: () => null,
195
+ }
196
+ },
197
+ data: function () {
198
+ return {
199
+ originDescriptions: {
200
+ motor: 'is the location of the initial cell body of the circuit',
201
+ sensory: 'is the location of the initial cell body in the PNS circuit',
202
+ },
203
+ facetList: [],
204
+ sckanVersion: '',
205
+ }
206
+ },
207
+ watch: {
208
+ availableAnatomyFacets: {
209
+ handler: function (val) {
210
+ this.convertFacetsToList(val)
211
+ },
212
+ immediate: true,
213
+ deep: true,
214
+ },
215
+ },
216
+ computed: {
217
+ originDescription: function () {
218
+ if (
219
+ this.entry &&
220
+ this.entry.title &&
221
+ this.entry.title.toLowerCase().includes('motor')
222
+ ) {
223
+ return this.originDescriptions.motor
224
+ } else {
225
+ return this.originDescriptions.sensory
226
+ }
227
+ },
228
+ },
229
+ methods: {
230
+ capitalise: function (text) {
231
+ return capitalise(text)
232
+ },
233
+ toggleConnectivityTooltip: function (name, option) {
234
+ this.$emit('toggle-connectivity-tooltip', {
235
+ name,
236
+ option
237
+ });
238
+ },
239
+ // shouldShowExploreButton: Checks if the feature is in the list of available anatomy facets
240
+ shouldShowExploreButton: function (features) {
241
+ for (let i = 0; i < features.length; i++) {
242
+ if (this.facetList.includes(features[i].name.toLowerCase())) {
243
+ return true
244
+ }
245
+ }
246
+ return false
247
+ },
248
+ // convertFacetsToList: Converts the available anatomy facets to a list for easy searching
249
+ convertFacetsToList: function (facets) {
250
+ facets.forEach((facet) => {
251
+ if(facet.children) {
252
+ this.convertFacetsToList(facet.children)
253
+ } else {
254
+ this.facetList.push(facet.label.toLowerCase())
255
+ }
256
+ })
257
+ },
258
+ openAll: function () {
259
+ this.$emit('connectivity-action-click', {
260
+ type: 'Facets',
261
+ labels: this.componentsWithDatasets.map((a) => a.name.toLowerCase()),
262
+ })
263
+ },
264
+ openAxons: function () {
265
+ this.$emit('connectivity-action-click', {
266
+ type: 'Facets',
267
+ labels: this.destinationsWithDatasets.map((a) => a.name.toLowerCase()),
268
+ })
269
+ },
270
+ openDendrites: function () {
271
+ this.$emit('connectivity-action-click', {
272
+ type: 'Facets',
273
+ labels: this.originsWithDatasets.map((a) => a.name.toLowerCase()),
274
+ })
275
+ },
276
+ }
277
+ }
278
+ </script>
279
+
280
+ <style lang="scss" scoped>
281
+ .connectivity-list {
282
+ display: flex;
283
+ flex-direction: column;
284
+ gap: 1rem;
285
+ position: relative;
286
+ }
287
+
288
+ .button {
289
+ margin-left: 0px !important;
290
+ margin-top: 0px !important;
291
+ font-size: 14px !important;
292
+ background-color: $app-primary-color;
293
+ color: #fff;
294
+
295
+ &:hover {
296
+ color: #fff !important;
297
+ background-color: #ac76c5 !important;
298
+ border: 1px solid #ac76c5 !important;
299
+ }
300
+
301
+ & + .button {
302
+ margin-top: 10px !important;
303
+ }
304
+ }
305
+
306
+ .icon {
307
+ right: 0px;
308
+ position: absolute;
309
+ top: 10px;
310
+ }
311
+
312
+ .icon:hover {
313
+ cursor: pointer;
314
+ }
315
+
316
+ :deep(.popover-origin-help.el-popover) {
317
+ text-transform: none !important; // need to overide the tooltip text transform
318
+ border: 1px solid $app-primary-color;
319
+ font-weight: 400;
320
+ font-family: Asap, sans-serif, Helvetica;
321
+
322
+ .el-popper__arrow {
323
+ &:before {
324
+ border-color: $app-primary-color;
325
+ background-color: #ffffff;
326
+ }
327
+ }
328
+ }
329
+
330
+ .info {
331
+ color: #8300bf;
332
+ transform: rotate(180deg);
333
+ margin-left: 8px;
334
+ }
335
+
336
+ .attribute-title-container {
337
+ margin-bottom: 0.5em;
338
+ }
339
+
340
+ .attribute-title {
341
+ font-size: 16px;
342
+ font-weight: 600;
343
+ /* font-weight: bold; */
344
+ text-transform: uppercase;
345
+ }
346
+
347
+ .attribute-content {
348
+ font-size: 14px;
349
+ font-weight: 500;
350
+ transition: color 0.25s ease;
351
+ position: relative;
352
+ cursor: default;
353
+
354
+ &:hover {
355
+ color: $app-primary-color;
356
+ }
357
+
358
+ + .attribute-content {
359
+ &::before {
360
+ content: "";
361
+ width: 90%;
362
+ height: 1px;
363
+ background-color: var(--el-border-color);
364
+ position: absolute;
365
+ top: 0;
366
+ left: 0;
367
+ }
368
+ }
369
+
370
+ &:last-of-type {
371
+ margin-bottom: 0.5em;
372
+ }
373
+ }
374
+
375
+ .connectivity-error-container {
376
+ position: sticky;
377
+ bottom: 0.5rem;
378
+ width: 100%;
379
+ min-height: 31px; // placeholder
380
+ margin-top: -10px !important;
381
+ display: flex;
382
+ flex-direction: row;
383
+ align-items: center;
384
+ justify-content: center;
385
+ }
386
+
387
+ .connectivity-error {
388
+ width: fit-content;
389
+ font-size: 12px;
390
+ padding: 0.25rem 0.5rem;
391
+ background-color: var(--el-color-error-light-9);
392
+ border-radius: var(--el-border-radius-small);
393
+ border: 1px solid var(--el-color-error);
394
+ }
395
+ </style>
@@ -41,7 +41,7 @@
41
41
  <strong class="sub-title">Previous submissions:</strong>
42
42
  </el-row>
43
43
  <div class="entry" v-for="(sub, index) in prevSubs" :key="index">
44
- <el-row class="dialog-text" v-if="sub.creator">
44
+ <el-row class="dialog-text">
45
45
  <strong>{{ formatTime(sub.created) }}</strong>
46
46
  {{ sub.creator.name }}
47
47
  </el-row>
@@ -65,7 +65,7 @@
65
65
  </div>
66
66
  </template>
67
67
  </template>
68
- <template v-if="authenticated || offlineAnnotate">
68
+ <template v-if="authenticated">
69
69
  <template v-if="isEditable">
70
70
  <el-row class="dialog-spacer"></el-row>
71
71
  <el-row v-if="!editing">
@@ -217,9 +217,6 @@ export default {
217
217
  updatedCopyContent: function () {
218
218
  return this.getUpdateCopyContent();
219
219
  },
220
- offlineAnnotate: function () {
221
- return this.annotationEntry["offline"];
222
- },
223
220
  },
224
221
  methods: {
225
222
  processEvidences: function(sub) {
@@ -265,15 +262,7 @@ export default {
265
262
  return new Date(dateString).toLocaleDateString(undefined, options);
266
263
  },
267
264
  updatePrevSubmissions: function () {
268
- if (this.offlineAnnotate) {
269
- const offlineAnnotation = JSON.parse(sessionStorage.getItem('offline-annotation')) || [];
270
- this.prevSubs = offlineAnnotation.filter((offline) => {
271
- return (
272
- offline.resource === this.annotationEntry.resourceId &&
273
- offline.item.id === this.annotationEntry.featureId
274
- )
275
- });
276
- } else if (this.$annotator && this.authenticated) {
265
+ if (this.$annotator && this.authenticated) {
277
266
  if (
278
267
  this.annotationEntry["resourceId"] &&
279
268
  this.annotationEntry["featureId"]
@@ -353,6 +342,7 @@ export default {
353
342
  this.$annotator
354
343
  ?.addAnnotation(this.userApiKey, userAnnotation)
355
344
  .then(() => {
345
+ this.$emit("annotation", userAnnotation);
356
346
  this.errorMessage = "";
357
347
  this.resetSubmission();
358
348
  this.updatePrevSubmissions();
@@ -361,7 +351,6 @@ export default {
361
351
  this.errorMessage =
362
352
  "There is a problem with the submission, please try again later";
363
353
  });
364
- this.$emit("annotation", userAnnotation);
365
354
  }
366
355
  }
367
356
  },
@@ -407,11 +396,9 @@ export default {
407
396
  if (this.prevSubs.length) {
408
397
  let annotationContent = '<div><strong>Annotations:</strong></div>\n<br>';
409
398
  this.prevSubs.map((sub, index) => {
410
- if (sub.creator) {
411
- annotationContent += `<div><strong>Created:</strong>${this.formatTime(sub.created)}</div>\n<br>`;
412
- annotationContent += `<div><strong>Creator:</strong>${sub.creator.name}</div>\n<br>`;
413
- annotationContent += `<div><strong>Email:</strong>${sub.creator.email}</div>\n<br>`;
414
- }
399
+ annotationContent += `<div><strong>Created:</strong>${this.formatTime(sub.created)}</div>\n<br>`;
400
+ annotationContent += `<div><strong>Creator:</strong>${sub.creator.name}</div>\n<br>`;
401
+ annotationContent += `<div><strong>Email:</strong>${sub.creator.email}</div>\n<br>`;
415
402
  if (sub.body.evidence.length) {
416
403
  let evidenceContent = '';
417
404
  sub.body.evidence.forEach((evi, index) => {
@@ -446,10 +433,10 @@ export default {
446
433
  this.creator = userData;
447
434
  if (!userData.orcid) this.creator.orcid = "0000-0000-0000-0000";
448
435
  this.authenticated = true;
436
+ this.updatePrevSubmissions();
449
437
  } else {
450
438
  this.errorMessage = "";
451
439
  }
452
- this.updatePrevSubmissions();
453
440
  });
454
441
  },
455
442
  };