@abi-software/map-utilities 1.4.3-isan.1 → 1.5.0-beta.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.
@@ -1,31 +1,5 @@
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>
29
3
  <div class="block">
30
4
  <el-row class="info-field">
31
5
  <div class="title">Feature Annotations</div>
@@ -33,16 +7,16 @@
33
7
  <copy-to-clipboard :content="updatedCopyContent" />
34
8
  </div>
35
9
  </el-row>
36
- <template v-if="entry">
10
+ <template v-if="annotationEntry">
37
11
  <el-row
38
12
  v-for="(key, label) in displayPair"
39
- v-show="entry[key]"
13
+ v-show="annotationEntry[key]"
40
14
  class="dialog-text"
41
15
  :key="key"
42
16
  >
43
17
  <strong>{{ label }}: </strong>&nbsp;
44
- <span v-if="label !== 'Ontology'">{{ entry[key] }}</span>
45
- <a v-else :href="ontologyLink" target="_blank">{{ entry[key] }}</a>
18
+ <span v-if="label !== 'Ontology'">{{ annotationEntry[key] }}</span>
19
+ <a v-else :href="ontologyLink" target="_blank">{{ annotationEntry[key] }}</a>
46
20
  </el-row>
47
21
  <template v-if="prevSubs.length > 0">
48
22
  <div
@@ -67,7 +41,7 @@
67
41
  <strong class="sub-title">Previous submissions:</strong>
68
42
  </el-row>
69
43
  <div class="entry" v-for="(sub, index) in prevSubs" :key="index">
70
- <el-row class="dialog-text">
44
+ <el-row class="dialog-text" v-if="sub.creator">
71
45
  <strong>{{ formatTime(sub.created) }}</strong>
72
46
  {{ sub.creator.name }}
73
47
  </el-row>
@@ -91,7 +65,7 @@
91
65
  </div>
92
66
  </template>
93
67
  </template>
94
- <template v-if="authenticated">
68
+ <template v-if="authenticated || offlineAnnotationEnabled">
95
69
  <template v-if="isEditable">
96
70
  <el-row class="dialog-spacer"></el-row>
97
71
  <el-row v-if="!editing">
@@ -184,7 +158,7 @@ export default {
184
158
  name: "AnnotationPopup",
185
159
  props: {
186
160
  annotationEntry: {
187
- type: Array,
161
+ type: Object,
188
162
  },
189
163
  },
190
164
  inject: ["$annotator", "userApiKey"],
@@ -213,64 +187,41 @@ export default {
213
187
  errorMessage: "",
214
188
  creator: undefined,
215
189
  copyContent: '',
216
- entryIndex: 0,
217
190
  };
218
191
  },
219
192
  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
- },
235
193
  isEditable: function () {
236
194
  return (
237
- this.entry["resourceId"] && this.entry["featureId"]
195
+ this.annotationEntry["resourceId"] && this.annotationEntry["featureId"]
238
196
  );
239
197
  },
240
198
  isPositionUpdated: function () {
241
199
  return (
242
- this.entry["resourceId"] &&
243
- this.entry["type"] === "updated" &&
244
- this.entry["positionUpdated"]
200
+ this.annotationEntry["resourceId"] &&
201
+ this.annotationEntry["type"] === "updated" &&
202
+ this.annotationEntry["positionUpdated"]
245
203
  );
246
204
  },
247
205
  isDeleted: function () {
248
206
  return (
249
- this.entry["resourceId"] &&
250
- this.entry["type"] === "deleted"
207
+ this.annotationEntry["resourceId"] &&
208
+ this.annotationEntry["type"] === "deleted"
251
209
  );
252
210
  },
253
211
  ontologyLink: function () {
254
- const models = this.entry['models'];
212
+ const models = this.annotationEntry['models'];
255
213
  if (models && models.startsWith("UBERON")) {
256
- return `http://purl.obolibrary.org/obo/${this.entry.models.replace(":", "_")}`;
214
+ return `http://purl.obolibrary.org/obo/${this.annotationEntry.models.replace(":", "_")}`;
257
215
  }
258
216
  },
259
217
  updatedCopyContent: function () {
260
218
  return this.getUpdateCopyContent();
261
219
  },
220
+ offlineAnnotationEnabled: function () {
221
+ return this.annotationEntry["offline"];
222
+ },
262
223
  },
263
224
  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
- },
274
225
  processEvidences: function(sub) {
275
226
  const evidences = [];
276
227
  if (sub?.body?.evidence) {
@@ -314,16 +265,24 @@ export default {
314
265
  return new Date(dateString).toLocaleDateString(undefined, options);
315
266
  },
316
267
  updatePrevSubmissions: function () {
317
- if (this.$annotator && this.authenticated) {
268
+ if (this.offlineAnnotationEnabled) {
269
+ const offlineAnnotations = JSON.parse(sessionStorage.getItem('anonymous-annotation')) || [];
270
+ this.prevSubs = offlineAnnotations.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) {
318
277
  if (
319
- this.entry["resourceId"] &&
320
- this.entry["featureId"]
278
+ this.annotationEntry["resourceId"] &&
279
+ this.annotationEntry["featureId"]
321
280
  ) {
322
281
  this.$annotator
323
282
  ?.itemAnnotations(
324
283
  this.userApiKey,
325
- this.entry["resourceId"],
326
- this.entry["featureId"]
284
+ this.annotationEntry["resourceId"],
285
+ this.annotationEntry["featureId"]
327
286
  )
328
287
  .then((value) => {
329
288
  this.prevSubs = value;
@@ -338,13 +297,13 @@ export default {
338
297
  // User can either update/delete annotation directly
339
298
  // or provide extra comments for update/delete action
340
299
  if (
341
- this.entry["type"] === "updated" &&
342
- this.entry["positionUpdated"]
300
+ this.annotationEntry["type"] === "updated" &&
301
+ this.annotationEntry["positionUpdated"]
343
302
  ) {
344
303
  this.comment = this.comment
345
304
  ? `Position Updated: ${this.comment}`
346
305
  : "Position Updated";
347
- } else if (this.entry["type"] === "deleted") {
306
+ } else if (this.annotationEntry["type"] === "deleted") {
348
307
  this.comment = this.comment
349
308
  ? `Feature Deleted: ${this.comment}`
350
309
  : "Feature Deleted";
@@ -352,8 +311,8 @@ export default {
352
311
 
353
312
  if (this.evidence.length > 0 || this.comment) {
354
313
  if (
355
- this.entry["resourceId"] &&
356
- this.entry["featureId"]
314
+ this.annotationEntry["resourceId"] &&
315
+ this.annotationEntry["featureId"]
357
316
  ) {
358
317
  const evidenceURLs = [];
359
318
  this.evidence.forEach((evidence) => {
@@ -371,11 +330,11 @@ export default {
371
330
  }
372
331
  });
373
332
  const userAnnotation = {
374
- resource: this.entry["resourceId"],
333
+ resource: this.annotationEntry["resourceId"],
375
334
  item: Object.assign(
376
- { id: this.entry["featureId"] },
335
+ { id: this.annotationEntry["featureId"] },
377
336
  Object.fromEntries(
378
- Object.entries(this.entry).filter(([key]) =>
337
+ Object.entries(this.annotationEntry).filter(([key]) =>
379
338
  ["label", "models"].includes(key)
380
339
  )
381
340
  )
@@ -384,17 +343,16 @@ export default {
384
343
  evidence: evidenceURLs,
385
344
  comment: this.comment,
386
345
  },
387
- feature: this.entry["feature"],
346
+ feature: this.annotationEntry["feature"],
388
347
  };
389
- Object.assign(userAnnotation.body, this.entry["body"]);
390
- if (this.entry["type"] === "deleted") {
348
+ Object.assign(userAnnotation.body, this.annotationEntry["body"]);
349
+ if (this.annotationEntry["type"] === "deleted") {
391
350
  userAnnotation.feature = undefined;
392
351
  }
393
352
  if (this.creator) userAnnotation.creator = this.creator;
394
353
  this.$annotator
395
354
  ?.addAnnotation(this.userApiKey, userAnnotation)
396
355
  .then(() => {
397
- this.$emit("annotation", userAnnotation);
398
356
  this.errorMessage = "";
399
357
  this.resetSubmission();
400
358
  this.updatePrevSubmissions();
@@ -403,6 +361,7 @@ export default {
403
361
  this.errorMessage =
404
362
  "There is a problem with the submission, please try again later";
405
363
  });
364
+ this.$emit("annotation", userAnnotation);
406
365
  }
407
366
  }
408
367
  },
@@ -416,41 +375,43 @@ export default {
416
375
  this.comment = "";
417
376
  },
418
377
  getUpdateCopyContent: function () {
419
- if (!this.entry) {
378
+ if (!this.annotationEntry) {
420
379
  return '';
421
380
  }
422
381
 
423
382
  const contentArray = [];
424
383
 
425
384
  // featureId
426
- if (this.entry.featureId) {
427
- contentArray.push(`<div><strong>Feature ID:</strong>${this.entry.featureId}</div>`);
385
+ if (this.annotationEntry.featureId) {
386
+ contentArray.push(`<div><strong>Feature ID:</strong>${this.annotationEntry.featureId}</div>`);
428
387
  }
429
388
 
430
389
  // label
431
- if (this.entry.label) {
432
- contentArray.push(`<div><strong>Label:</strong>${this.entry.label}</div>`);
390
+ if (this.annotationEntry.label) {
391
+ contentArray.push(`<div><strong>Label:</strong>${this.annotationEntry.label}</div>`);
433
392
  }
434
393
 
435
394
  // models
436
- if (this.entry.models) {
437
- contentArray.push(`<div><strong>Ontology:</strong>${this.entry.models}</div>`);
395
+ if (this.annotationEntry.models) {
396
+ contentArray.push(`<div><strong>Ontology:</strong>${this.annotationEntry.models}</div>`);
438
397
  if (this.ontologyLink) {
439
398
  contentArray.push(`<div><strong>Ontology Link:</strong>${this.ontologyLink}</div>`);
440
399
  }
441
400
  }
442
401
 
443
402
  // resourceId
444
- if (this.entry.resourceId) {
445
- contentArray.push(`<div><strong>Resource:</strong>${this.entry.resourceId}</div>`);
403
+ if (this.annotationEntry.resourceId) {
404
+ contentArray.push(`<div><strong>Resource:</strong>${this.annotationEntry.resourceId}</div>`);
446
405
  }
447
406
 
448
407
  if (this.prevSubs.length) {
449
408
  let annotationContent = '<div><strong>Annotations:</strong></div>\n<br>';
450
409
  this.prevSubs.map((sub, index) => {
451
- annotationContent += `<div><strong>Created:</strong>${this.formatTime(sub.created)}</div>\n<br>`;
452
- annotationContent += `<div><strong>Creator:</strong>${sub.creator.name}</div>\n<br>`;
453
- annotationContent += `<div><strong>Email:</strong>${sub.creator.email}</div>\n<br>`;
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
+ }
454
415
  if (sub.body.evidence.length) {
455
416
  let evidenceContent = '';
456
417
  sub.body.evidence.forEach((evi, index) => {
@@ -468,15 +429,15 @@ export default {
468
429
  },
469
430
  },
470
431
  watch: {
471
- entry: {
472
- deep: true,
473
- immediate: true,
432
+ annotationEntry: {
474
433
  handler: function (newVal, oldVal) {
475
434
  if (newVal !== oldVal) {
476
435
  this.resetSubmission();
477
436
  this.updatePrevSubmissions();
478
437
  }
479
438
  },
439
+ immediate: false,
440
+ deep: false,
480
441
  },
481
442
  },
482
443
  mounted: function () {
@@ -485,46 +446,16 @@ export default {
485
446
  this.creator = userData;
486
447
  if (!userData.orcid) this.creator.orcid = "0000-0000-0000-0000";
487
448
  this.authenticated = true;
488
- this.updatePrevSubmissions();
489
449
  } else {
490
450
  this.errorMessage = "";
491
451
  }
452
+ this.updatePrevSubmissions();
492
453
  });
493
454
  },
494
455
  };
495
456
  </script>
496
457
 
497
458
  <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
-
528
459
  .info-field {
529
460
  padding: 0;
530
461
  display: flex;
@@ -22,8 +22,8 @@
22
22
  v-for="reference of pubMedReferences"
23
23
  :key="reference.id"
24
24
  :class="{
25
- 'loading': reference.citation && !reference.citation.error && reference.citation[citationType] === '',
26
- 'error': reference.citation && reference.citation.error
25
+ 'loading': isCitationLoading(reference.citation),
26
+ 'error': isCitationError(reference.citation),
27
27
  }"
28
28
  >
29
29
  <template v-if="reference.citation">
@@ -50,49 +50,34 @@
50
50
  <template v-else>
51
51
  <span v-html="reference.citation[citationType]"></span>
52
52
 
53
- <div class="reference-button-container">
54
- <el-button
55
- class="reference-icon-button"
56
- size="small"
57
- @click="showRelatedConnectivities(reference.resource)"
58
- >
59
- Show related connectivities
60
- </el-button>
61
- </div>
53
+ <RelatedConnectivitiesButton
54
+ :resource="reference.resource"
55
+ @show-related-connectivities="showRelatedConnectivities"
56
+ />
62
57
 
63
58
  <CopyToClipboard :content="reference.citation[citationType]" />
64
59
  </template>
65
60
  </template>
66
61
  </li>
67
62
 
68
- <li v-for="reference of openLibReferences">
63
+ <li v-for="reference of openLibReferences" :key="reference.id">
69
64
  <div v-html="formatCopyReference(reference)"></div>
70
65
 
71
- <div class="reference-button-container">
72
- <el-button
73
- class="reference-icon-button"
74
- size="small"
75
- @click="showRelatedConnectivities(reference.resource)"
76
- >
77
- Show related connectivities
78
- </el-button>
79
- </div>
66
+ <RelatedConnectivitiesButton
67
+ :resource="reference.resource"
68
+ @show-related-connectivities="showRelatedConnectivities"
69
+ />
80
70
 
81
71
  <CopyToClipboard :content="formatCopyReference(reference)" />
82
72
  </li>
83
73
 
84
- <li v-for="reference of isbnDBReferences">
74
+ <li v-for="reference of isbnDBReferences" :key="reference.id">
85
75
  <a :href="reference.url" target="_blank">{{ reference.url }}</a>
86
76
 
87
- <div class="reference-button-container">
88
- <el-button
89
- class="reference-icon-button"
90
- size="small"
91
- @click="showRelatedConnectivities(reference.resource)"
92
- >
93
- Show related connectivities
94
- </el-button>
95
- </div>
77
+ <RelatedConnectivitiesButton
78
+ :resource="reference.resource"
79
+ @show-related-connectivities="showRelatedConnectivities"
80
+ />
96
81
 
97
82
  <CopyToClipboard :content="reference.url" />
98
83
  </li>
@@ -103,6 +88,7 @@
103
88
  <script>
104
89
  import CopyToClipboard from '../CopyToClipboard/CopyToClipboard.vue';
105
90
  import { delay } from '../utilities';
91
+ import RelatedConnectivitiesButton from './RelatedConnectivitiesButton.vue';
106
92
 
107
93
  const CROSSCITE_API_HOST = 'https://citation.doi.org';
108
94
  const CITATION_OPTIONS = [
@@ -128,6 +114,10 @@ const LOADING_DELAY = 600;
128
114
 
129
115
  export default {
130
116
  name: "ExternalResourceCard",
117
+ components: {
118
+ CopyToClipboard,
119
+ RelatedConnectivitiesButton,
120
+ },
131
121
  props: {
132
122
  resources: {
133
123
  type: Array,
@@ -411,6 +401,12 @@ export default {
411
401
  reloadCitation: function (reference) {
412
402
  this.generateCitationText(reference, this.citationType);
413
403
  },
404
+ isCitationLoading: function (citation) {
405
+ return citation && !citation[this.citationType] && !citation.error;
406
+ },
407
+ isCitationError: function (citation) {
408
+ return citation && citation.error;
409
+ },
414
410
  updateCopyContents: function () {
415
411
  const citationTypeObj = this.citationOptions.find((item) => item.value === this.citationType);
416
412
  let citationFormatStyle = '';
@@ -611,7 +607,7 @@ export default {
611
607
  &.loading {
612
608
  padding: 1rem;
613
609
 
614
- &::before {
610
+ &::after {
615
611
  content: "";
616
612
  display: block;
617
613
  width: 100%;
@@ -691,20 +687,6 @@ export default {
691
687
  cursor: pointer;
692
688
  }
693
689
 
694
- .reference-button-container {
695
- margin-top: 0.5rem;
696
- }
697
-
698
- .reference-icon-button {
699
- color: $app-primary-color !important;
700
- background-color: #f9f2fc !important;
701
- border-color: $app-primary-color !important;
702
-
703
- &:hover {
704
- background-color: transparent !important;
705
- }
706
- }
707
-
708
690
  @keyframes loadingAnimation {
709
691
  0% {
710
692
  background-position: -30vw 0;