@abi-software/map-utilities 1.4.3-beta.2 → 1.4.3-isan.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.3-beta.2",
3
+ "version": "1.4.3-isan.1",
4
4
  "files": [
5
5
  "dist/*",
6
6
  "src/*",
@@ -30,11 +30,6 @@
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",
38
33
  "@element-plus/icons-vue": "^2.3.1",
39
34
  "cytoscape": "^3.30.2",
40
35
  "element-plus": "2.8.4",
package/src/App.vue CHANGED
@@ -130,7 +130,7 @@ function onActionClick(value) {
130
130
  * Tooltip
131
131
  */
132
132
  const tooltipDisplay = ref(false);
133
- const tooltipEntry = ref({});
133
+ const tooltipEntry = ref([]);
134
134
  const featuresAlert = ref(undefined);
135
135
  const annotationDisplay = ref(false);
136
136
  const annotationEntry = ref({});
@@ -141,33 +141,90 @@ provide(/* key */ "userApiKey", /* value */ undefined);
141
141
 
142
142
  function addTooltipEntry() {
143
143
  tooltipDisplay.value = true;
144
- tooltipEntry.value = {
145
- destinations: [null],
146
- origins: [null],
147
- components: ["pudendal nerve"],
148
- destinationsWithDatasets: [
149
- { id: "UBERON:0004917", name: "urethral sphincter" },
150
- ],
151
- originsWithDatasets: [
152
- { id: "UBERON:0022278", name: "nucleus of pudendal nerve" },
153
- ],
154
- componentsWithDatasets: [{ id: "UBERON:0011390", name: "pudendal nerve" }],
155
- title:
156
- "Nucleus of the pudendal nerve to urethral sphincter via pudendal nerve",
157
- featureId: ["ilxtr:sparc-nlp/mmset1/1"],
158
- hyperlinks: [
159
- {
160
- url: "https://pubmed.ncbi.nlm.nih.gov/?term=%2F%2Fdoi.org%2F10.1155%252F2012%252F816274",
161
- id: "pubmed",
162
- },
163
- ],
164
- provenanceTaxonomy: ["NCBITaxon:9606"],
165
- provenanceTaxonomyLabel: ["Homo sapiens"],
166
- };
144
+ tooltipEntry.value = [
145
+ {
146
+ destinations: ["eccrine sweat gland of the trunk"],
147
+ origins: [
148
+ "Sixth thoracic ganglion",
149
+ "Twelfth thoracic ganglion",
150
+ "Fifth thoracic ganglion",
151
+ "Ninth thoracic ganglion",
152
+ "Seventh thoracic ganglion",
153
+ "Eighth thoracic ganglion",
154
+ "Fourth thoracic ganglion",
155
+ "Tenth thoracic ganglion",
156
+ "Eleventh thoracic ganglion",
157
+ ],
158
+ components: ["nerve"],
159
+ destinationsWithDatasets: [
160
+ { id: "ILX:0795061", name: "eccrine sweat gland of the trunk" },
161
+ ],
162
+ originsWithDatasets: [
163
+ { id: "ILX:0784378", name: "Ninth thoracic ganglion" },
164
+ { id: "ILX:0784569", name: "Tenth thoracic ganglion" },
165
+ { id: "ILX:0784721", name: "Eighth thoracic ganglion" },
166
+ { id: "ILX:0786141", name: "Fifth thoracic ganglion" },
167
+ { id: "ILX:0786272", name: "Fourth thoracic ganglion" },
168
+ { id: "ILX:0787009", name: "Twelfth thoracic ganglion" },
169
+ { id: "ILX:0787015", name: "Eleventh thoracic ganglion" },
170
+ { id: "ILX:0789947", name: "Sixth thoracic ganglion" },
171
+ { id: "ILX:0790482", name: "Seventh thoracic ganglion" },
172
+ ],
173
+ componentsWithDatasets: [{ id: "UBERON:0001021", name: "nerve" }],
174
+ title: "neuron type swglnd 161",
175
+ featureId: ["ilxtr:sparc-nlp/swglnd/161"],
176
+ hyperlinks: [
177
+ "https://doi.org/10.1007/s10286-015-0282-1",
178
+ "https://doi.org/10.1111/bjd.15808",
179
+ "https://doi.org/10.1159/000060678",
180
+ ],
181
+ provenanceTaxonomy: ["NCBITaxon:9606"],
182
+ provenanceTaxonomyLabel: ["Homo sapiens"],
183
+ knowledgeSource: "sckan-2024-09-21-npo",
184
+ mapId: "rat-flatmap",
185
+ mapuuid: "b4ae1699-5690-5640-97b7-d711ae02dcb9",
186
+ },
187
+ {
188
+ destinations: ["intramural ganglion of the kidney"],
189
+ origins: ["dorsal motor nucleus of vagus nerve"],
190
+ components: [
191
+ "renal nerve plexus",
192
+ "aortic plexus",
193
+ "esophageal vagus trunk",
194
+ "vagus X nerve trunk",
195
+ "vagus nerve",
196
+ ],
197
+ destinationsWithDatasets: [
198
+ { id: "ILX:0795056", name: "intramural ganglion of the kidney" },
199
+ ],
200
+ originsWithDatasets: [
201
+ { id: "UBERON:0002870", name: "dorsal motor nucleus of vagus nerve" },
202
+ ],
203
+ componentsWithDatasets: [
204
+ { id: "ILX:0794853", name: "esophageal vagus trunk" },
205
+ { id: "UBERON:0001759", name: "vagus nerve" },
206
+ { id: "UBERON:0003535", name: "vagus X nerve trunk" },
207
+ { id: "UBERON:0018676", name: "renal nerve plexus" },
208
+ { id: "UBERON:0035772", name: "aortic plexus" },
209
+ ],
210
+ title:
211
+ "dorsal motor nucleus of vagus nerve to intramural ganglia of the kidney via vagus nerve via esophageal vagus trunk via vagal trunks via aortic plexus via renal plexus",
212
+ featureId: ["ilxtr:sparc-nlp/kidney/135"],
213
+ hyperlinks: [
214
+ "https://uilx.org/tgbugs/u/r/isbn-13/978-0323680424",
215
+ "https://doi.org/10.1016/j.aanat.2015.11.004",
216
+ ],
217
+ provenanceTaxonomy: ["NCBITaxon:9606"],
218
+ provenanceTaxonomyLabel: ["Homo sapiens"],
219
+ knowledgeSource: "sckan-2024-09-21-npo",
220
+ mapId: "rat-flatmap",
221
+ mapuuid: "b4ae1699-5690-5640-97b7-d711ae02dcb9",
222
+ },
223
+ ];
167
224
  }
168
225
  function removeTooltipEntry() {
169
226
  tooltipDisplay.value = false;
170
- tooltipEntry.value = {};
227
+ tooltipEntry.value = [];
171
228
  }
172
229
  function addAnnotationEntry() {
173
230
  tooltipDisplay.value = true;
@@ -234,7 +291,7 @@ function setColourField(treeData, nodeData, activeColour) {
234
291
  function setColour(nodeData, value) {
235
292
  if (nodeData && nodeData.isPrimitives) {
236
293
  const activeColour = value ? value : nodeData.defaultColour;
237
- setColourField(treeDataEntry.value, nodeData, activeColour)
294
+ setColourField(treeDataEntry.value, nodeData, activeColour);
238
295
  }
239
296
  }
240
297
  function checkAll(value) {
@@ -263,7 +320,7 @@ const createData = ref({
263
320
  editingIndex: -1,
264
321
  faceIndex: -1,
265
322
  toBeDeleted: false,
266
- })
323
+ });
267
324
  function cancelCreate() {
268
325
  console.log("🚀 ~ CreateTooltipContent : cancelCreate");
269
326
  }
@@ -422,14 +479,14 @@ function confirmCreate(value) {
422
479
  Add Tooltip Entry
423
480
  </el-button>
424
481
  <el-button
425
- v-show="Object.keys(tooltipEntry).length > 0"
482
+ v-show="tooltipEntry.length > 0"
426
483
  @click="removeTooltipEntry"
427
484
  size="small"
428
485
  >
429
486
  Remove Tooltip Entry
430
487
  </el-button>
431
488
  <el-button
432
- v-show="!Object.keys(tooltipEntry).length > 0"
489
+ v-show="tooltipEntry.length === 0"
433
490
  @click="addAnnotationEntry"
434
491
  size="small"
435
492
  >
@@ -449,10 +506,18 @@ function confirmCreate(value) {
449
506
  <h3>TreeControls - {{ mapType }}</h3>
450
507
  </el-col>
451
508
  <el-col>
452
- <el-button v-show="mapType==='scaffold'" @click="switchTreeEntry('flatmap')" size="small">
509
+ <el-button
510
+ v-show="mapType === 'scaffold'"
511
+ @click="switchTreeEntry('flatmap')"
512
+ size="small"
513
+ >
453
514
  Display Flatmap Tree
454
515
  </el-button>
455
- <el-button v-show="mapType==='flatmap'" @click="switchTreeEntry('scaffold')" size="small">
516
+ <el-button
517
+ v-show="mapType === 'flatmap'"
518
+ @click="switchTreeEntry('scaffold')"
519
+ size="small"
520
+ >
456
521
  Display Scaffold Tree
457
522
  </el-button>
458
523
  </el-col>
@@ -462,16 +527,10 @@ function confirmCreate(value) {
462
527
  <h3>Connectivity Graph</h3>
463
528
  </el-col>
464
529
  <el-col>
465
- <el-button
466
- @click="showConnectivityGraph = true"
467
- size="small"
468
- >
530
+ <el-button @click="showConnectivityGraph = true" size="small">
469
531
  Show connectivity graph
470
532
  </el-button>
471
- <el-button
472
- @click="showConnectivityGraph = false"
473
- size="small"
474
- >
533
+ <el-button @click="showConnectivityGraph = false" size="small">
475
534
  Hide connectivity graph
476
535
  </el-button>
477
536
  </el-col>
@@ -568,9 +627,9 @@ function confirmCreate(value) {
568
627
  top: calc(50% - 100px);
569
628
  left: calc(50% - 200px);
570
629
  }
571
- .annotation-popup{
630
+ .annotation-popup {
572
631
  margin-top: 8px;
573
- width:400px;
632
+ width: 400px;
574
633
  border-style: solid;
575
634
  border-width: 1px;
576
635
  border-color: black;
@@ -1,5 +1,31 @@
1
1
  <template>
2
2
  <el-main class="main">
3
+ <div v-if="annotationEntry.length > 1" class="toggle-button">
4
+ <el-popover width="auto" trigger="hover" :teleported="false">
5
+ <template #reference>
6
+ <el-button
7
+ class="button"
8
+ @click="previous"
9
+ :disabled="this.entryIndex === 0"
10
+ >
11
+ Previous
12
+ </el-button>
13
+ </template>
14
+ <span>{{ previousLabel }}</span>
15
+ </el-popover>
16
+ <el-popover width="auto" trigger="hover" :teleported="false">
17
+ <template #reference>
18
+ <el-button
19
+ class="button"
20
+ @click="next"
21
+ :disabled="this.entryIndex === this.annotationEntry.length - 1"
22
+ >
23
+ Next
24
+ </el-button>
25
+ </template>
26
+ <span>{{ nextLabel }}</span>
27
+ </el-popover>
28
+ </div>
3
29
  <div class="block">
4
30
  <el-row class="info-field">
5
31
  <div class="title">Feature Annotations</div>
@@ -7,16 +33,16 @@
7
33
  <copy-to-clipboard :content="updatedCopyContent" />
8
34
  </div>
9
35
  </el-row>
10
- <template v-if="annotationEntry">
36
+ <template v-if="entry">
11
37
  <el-row
12
38
  v-for="(key, label) in displayPair"
13
- v-show="annotationEntry[key]"
39
+ v-show="entry[key]"
14
40
  class="dialog-text"
15
41
  :key="key"
16
42
  >
17
43
  <strong>{{ label }}: </strong>&nbsp;
18
- <span v-if="label !== 'Ontology'">{{ annotationEntry[key] }}</span>
19
- <a v-else :href="ontologyLink" target="_blank">{{ annotationEntry[key] }}</a>
44
+ <span v-if="label !== 'Ontology'">{{ entry[key] }}</span>
45
+ <a v-else :href="ontologyLink" target="_blank">{{ entry[key] }}</a>
20
46
  </el-row>
21
47
  <template v-if="prevSubs.length > 0">
22
48
  <div
@@ -158,7 +184,7 @@ export default {
158
184
  name: "AnnotationPopup",
159
185
  props: {
160
186
  annotationEntry: {
161
- type: Object,
187
+ type: Array,
162
188
  },
163
189
  },
164
190
  inject: ["$annotator", "userApiKey"],
@@ -187,31 +213,47 @@ export default {
187
213
  errorMessage: "",
188
214
  creator: undefined,
189
215
  copyContent: '',
216
+ entryIndex: 0,
190
217
  };
191
218
  },
192
219
  computed: {
220
+ entry: function () {
221
+ return this.annotationEntry[this.entryIndex];
222
+ },
223
+ previousLabel: function () {
224
+ if (this.entryIndex === 0) {
225
+ return "This is the first item. Click 'Next' to see more information.";
226
+ }
227
+ return this.annotationEntry[this.entryIndex - 1]?.label;
228
+ },
229
+ nextLabel: function () {
230
+ if (this.entryIndex === this.annotationEntry.length - 1) {
231
+ return "This is the last item. Click 'Previous' to see more information.";
232
+ }
233
+ return this.annotationEntry[this.entryIndex + 1]?.label;
234
+ },
193
235
  isEditable: function () {
194
236
  return (
195
- this.annotationEntry["resourceId"] && this.annotationEntry["featureId"]
237
+ this.entry["resourceId"] && this.entry["featureId"]
196
238
  );
197
239
  },
198
240
  isPositionUpdated: function () {
199
241
  return (
200
- this.annotationEntry["resourceId"] &&
201
- this.annotationEntry["type"] === "updated" &&
202
- this.annotationEntry["positionUpdated"]
242
+ this.entry["resourceId"] &&
243
+ this.entry["type"] === "updated" &&
244
+ this.entry["positionUpdated"]
203
245
  );
204
246
  },
205
247
  isDeleted: function () {
206
248
  return (
207
- this.annotationEntry["resourceId"] &&
208
- this.annotationEntry["type"] === "deleted"
249
+ this.entry["resourceId"] &&
250
+ this.entry["type"] === "deleted"
209
251
  );
210
252
  },
211
253
  ontologyLink: function () {
212
- const models = this.annotationEntry['models'];
254
+ const models = this.entry['models'];
213
255
  if (models && models.startsWith("UBERON")) {
214
- return `http://purl.obolibrary.org/obo/${this.annotationEntry.models.replace(":", "_")}`;
256
+ return `http://purl.obolibrary.org/obo/${this.entry.models.replace(":", "_")}`;
215
257
  }
216
258
  },
217
259
  updatedCopyContent: function () {
@@ -219,6 +261,16 @@ export default {
219
261
  },
220
262
  },
221
263
  methods: {
264
+ previous: function () {
265
+ if (this.entryIndex !== 0) {
266
+ this.entryIndex = this.entryIndex - 1;
267
+ }
268
+ },
269
+ next: function () {
270
+ if (this.entryIndex !== this.annotationEntry.length - 1) {
271
+ this.entryIndex = this.entryIndex + 1;
272
+ }
273
+ },
222
274
  processEvidences: function(sub) {
223
275
  const evidences = [];
224
276
  if (sub?.body?.evidence) {
@@ -264,14 +316,14 @@ export default {
264
316
  updatePrevSubmissions: function () {
265
317
  if (this.$annotator && this.authenticated) {
266
318
  if (
267
- this.annotationEntry["resourceId"] &&
268
- this.annotationEntry["featureId"]
319
+ this.entry["resourceId"] &&
320
+ this.entry["featureId"]
269
321
  ) {
270
322
  this.$annotator
271
323
  ?.itemAnnotations(
272
324
  this.userApiKey,
273
- this.annotationEntry["resourceId"],
274
- this.annotationEntry["featureId"]
325
+ this.entry["resourceId"],
326
+ this.entry["featureId"]
275
327
  )
276
328
  .then((value) => {
277
329
  this.prevSubs = value;
@@ -286,13 +338,13 @@ export default {
286
338
  // User can either update/delete annotation directly
287
339
  // or provide extra comments for update/delete action
288
340
  if (
289
- this.annotationEntry["type"] === "updated" &&
290
- this.annotationEntry["positionUpdated"]
341
+ this.entry["type"] === "updated" &&
342
+ this.entry["positionUpdated"]
291
343
  ) {
292
344
  this.comment = this.comment
293
345
  ? `Position Updated: ${this.comment}`
294
346
  : "Position Updated";
295
- } else if (this.annotationEntry["type"] === "deleted") {
347
+ } else if (this.entry["type"] === "deleted") {
296
348
  this.comment = this.comment
297
349
  ? `Feature Deleted: ${this.comment}`
298
350
  : "Feature Deleted";
@@ -300,8 +352,8 @@ export default {
300
352
 
301
353
  if (this.evidence.length > 0 || this.comment) {
302
354
  if (
303
- this.annotationEntry["resourceId"] &&
304
- this.annotationEntry["featureId"]
355
+ this.entry["resourceId"] &&
356
+ this.entry["featureId"]
305
357
  ) {
306
358
  const evidenceURLs = [];
307
359
  this.evidence.forEach((evidence) => {
@@ -319,11 +371,11 @@ export default {
319
371
  }
320
372
  });
321
373
  const userAnnotation = {
322
- resource: this.annotationEntry["resourceId"],
374
+ resource: this.entry["resourceId"],
323
375
  item: Object.assign(
324
- { id: this.annotationEntry["featureId"] },
376
+ { id: this.entry["featureId"] },
325
377
  Object.fromEntries(
326
- Object.entries(this.annotationEntry).filter(([key]) =>
378
+ Object.entries(this.entry).filter(([key]) =>
327
379
  ["label", "models"].includes(key)
328
380
  )
329
381
  )
@@ -332,10 +384,10 @@ export default {
332
384
  evidence: evidenceURLs,
333
385
  comment: this.comment,
334
386
  },
335
- feature: this.annotationEntry["feature"],
387
+ feature: this.entry["feature"],
336
388
  };
337
- Object.assign(userAnnotation.body, this.annotationEntry["body"]);
338
- if (this.annotationEntry["type"] === "deleted") {
389
+ Object.assign(userAnnotation.body, this.entry["body"]);
390
+ if (this.entry["type"] === "deleted") {
339
391
  userAnnotation.feature = undefined;
340
392
  }
341
393
  if (this.creator) userAnnotation.creator = this.creator;
@@ -364,33 +416,33 @@ export default {
364
416
  this.comment = "";
365
417
  },
366
418
  getUpdateCopyContent: function () {
367
- if (!this.annotationEntry) {
419
+ if (!this.entry) {
368
420
  return '';
369
421
  }
370
422
 
371
423
  const contentArray = [];
372
424
 
373
425
  // featureId
374
- if (this.annotationEntry.featureId) {
375
- contentArray.push(`<div><strong>Feature ID:</strong>${this.annotationEntry.featureId}</div>`);
426
+ if (this.entry.featureId) {
427
+ contentArray.push(`<div><strong>Feature ID:</strong>${this.entry.featureId}</div>`);
376
428
  }
377
429
 
378
430
  // label
379
- if (this.annotationEntry.label) {
380
- contentArray.push(`<div><strong>Label:</strong>${this.annotationEntry.label}</div>`);
431
+ if (this.entry.label) {
432
+ contentArray.push(`<div><strong>Label:</strong>${this.entry.label}</div>`);
381
433
  }
382
434
 
383
435
  // models
384
- if (this.annotationEntry.models) {
385
- contentArray.push(`<div><strong>Ontology:</strong>${this.annotationEntry.models}</div>`);
436
+ if (this.entry.models) {
437
+ contentArray.push(`<div><strong>Ontology:</strong>${this.entry.models}</div>`);
386
438
  if (this.ontologyLink) {
387
439
  contentArray.push(`<div><strong>Ontology Link:</strong>${this.ontologyLink}</div>`);
388
440
  }
389
441
  }
390
442
 
391
443
  // resourceId
392
- if (this.annotationEntry.resourceId) {
393
- contentArray.push(`<div><strong>Resource:</strong>${this.annotationEntry.resourceId}</div>`);
444
+ if (this.entry.resourceId) {
445
+ contentArray.push(`<div><strong>Resource:</strong>${this.entry.resourceId}</div>`);
394
446
  }
395
447
 
396
448
  if (this.prevSubs.length) {
@@ -416,15 +468,15 @@ export default {
416
468
  },
417
469
  },
418
470
  watch: {
419
- annotationEntry: {
471
+ entry: {
472
+ deep: true,
473
+ immediate: true,
420
474
  handler: function (newVal, oldVal) {
421
475
  if (newVal !== oldVal) {
422
476
  this.resetSubmission();
423
477
  this.updatePrevSubmissions();
424
478
  }
425
479
  },
426
- immediate: false,
427
- deep: false,
428
480
  },
429
481
  },
430
482
  mounted: function () {
@@ -443,6 +495,36 @@ export default {
443
495
  </script>
444
496
 
445
497
  <style lang="scss" scoped>
498
+ .toggle-button {
499
+ display: flex;
500
+ justify-content: space-between;
501
+ margin-bottom: 30px;
502
+
503
+ .is-disabled {
504
+ color: #fff !important;
505
+ background-color: #ac76c5 !important;
506
+ border: 1px solid #ac76c5 !important;
507
+ }
508
+
509
+ .button {
510
+ margin-left: 0px !important;
511
+ margin-top: 0px !important;
512
+ font-size: 14px !important;
513
+ background-color: $app-primary-color;
514
+ color: #fff;
515
+
516
+ & + .button {
517
+ margin-top: 10px !important;
518
+ }
519
+
520
+ &:hover {
521
+ color: #fff !important;
522
+ background: #ac76c5 !important;
523
+ border: 1px solid #ac76c5 !important;
524
+ }
525
+ }
526
+ }
527
+
446
528
  .info-field {
447
529
  padding: 0;
448
530
  display: flex;