@abi-software/map-side-bar 2.17.1-demo.1 → 2.19.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.
Files changed (41) hide show
  1. package/.postcssrc.json +5 -5
  2. package/README.md +8 -9
  3. package/cypress.config.js +6 -7
  4. package/dist/map-side-bar.js +5906 -7001
  5. package/dist/map-side-bar.umd.cjs +62 -65
  6. package/dist/style.css +1 -1
  7. package/eslint.config.js +44 -9
  8. package/package.json +12 -5
  9. package/reporter-config.json +1 -1
  10. package/src/App.vue +80 -86
  11. package/src/algolia/algolia.js +173 -167
  12. package/src/algolia/utils.js +34 -38
  13. package/src/assets/_variables.scss +12 -10
  14. package/src/assets/connectivity-explorer.scss +1 -1
  15. package/src/assets/fonts.scss +0 -1
  16. package/src/assets/searchPopover.scss +1 -2
  17. package/src/components/AnnotationTool.vue +9 -32
  18. package/src/components/BadgesGroup.vue +29 -32
  19. package/src/components/ConnectivityCard.vue +22 -19
  20. package/src/components/ConnectivityExplorer.vue +107 -128
  21. package/src/components/ConnectivityInfo.vue +169 -128
  22. package/src/components/ContributorItem.vue +15 -11
  23. package/src/components/DatasetCard.vue +166 -201
  24. package/src/components/DatasetExplorer.vue +160 -201
  25. package/src/components/ImageGallery.vue +35 -36
  26. package/src/components/SearchFilters.vue +381 -408
  27. package/src/components/SearchHistory.vue +75 -91
  28. package/src/components/SideBar.vue +86 -193
  29. package/src/components/Tabs.vue +16 -30
  30. package/src/components/index.js +3 -5
  31. package/src/components/species-map.js +7 -7
  32. package/src/components.d.ts +0 -3
  33. package/src/exampleConnectivityInput.js +310 -318
  34. package/src/main.js +6 -6
  35. package/src/mixins/S3Bucket.vue +10 -13
  36. package/vite.config.js +62 -61
  37. package/vuese-generator.js +47 -55
  38. package/src/components/CellCard.vue +0 -994
  39. package/src/components/CellCardExplorer.vue +0 -882
  40. package/src/components/icons/IconOpenExternal.vue +0 -28
  41. package/src/utils/common.js +0 -71
@@ -48,12 +48,7 @@
48
48
  </div>
49
49
  </div>
50
50
  <div class="title-buttons">
51
- <el-popover
52
- width="auto"
53
- trigger="hover"
54
- :teleported="false"
55
- popper-class="popover-map-pin"
56
- >
51
+ <el-popover width="auto" trigger="hover" :teleported="false" popper-class="popover-map-pin">
57
52
  <template #reference>
58
53
  <el-button class="button-circle secondary" circle @click="showConnectivity">
59
54
  <el-icon color="#8300bf">
@@ -61,9 +56,7 @@
61
56
  </el-icon>
62
57
  </el-button>
63
58
  </template>
64
- <span>
65
- Show connectivity on map
66
- </span>
59
+ <span>Show connectivity on map</span>
67
60
  </el-popover>
68
61
  <CopyToClipboard @copied="onCopied" :content="updatedCopyContent" />
69
62
  <template v-if="withCloseButton">
@@ -86,7 +79,10 @@
86
79
  </div>
87
80
  </div>
88
81
 
89
- <div class="content-container population-details" :class="{'flex-row': hasSingleConnectivityList}">
82
+ <div
83
+ class="content-container population-details"
84
+ :class="{ 'flex-row': hasSingleConnectivityList }"
85
+ >
90
86
  <div class="block attribute-title-container">
91
87
  <span class="attribute-title">Population Details</span>
92
88
  <el-popover
@@ -100,12 +96,11 @@
100
96
  <el-icon class="info"><el-icon-warning /></el-icon>
101
97
  </template>
102
98
  <span v-if="hasSingleConnectivityList" style="word-break: keep-all">
103
- This list is ordered alphabetically. Switch to graph view for path details,
104
- and use the legend below for reconciliation status.
99
+ This list is ordered alphabetically. Switch to graph view for path details, and use the
100
+ legend below for reconciliation status.
105
101
  </span>
106
102
  <span v-else style="word-break: keep-all">
107
- This list is ordered alphabetically,
108
- switch to graph view for path details.
103
+ This list is ordered alphabetically, switch to graph view for path details.
109
104
  </span>
110
105
  <div v-if="hasSingleConnectivityList" class="connectivity-legends">
111
106
  <div class="legend-title">Legend</div>
@@ -138,9 +133,11 @@
138
133
  <el-icon class="info"><el-icon-warning /></el-icon>
139
134
  </template>
140
135
  <span style="word-break: keep-all">
141
- <strong>Map</strong> - connectivity as defined in active map.
142
- <br>
143
- <strong>SCKAN</strong> - connectivity as defined in SCKAN.
136
+ <strong>Map</strong>
137
+ - connectivity as defined in active map.
138
+ <br />
139
+ <strong>SCKAN</strong>
140
+ - connectivity as defined in SCKAN.
144
141
  </span>
145
142
  </el-popover>
146
143
  </span>
@@ -149,7 +146,7 @@
149
146
  <el-radio value="sckan">SCKAN</el-radio>
150
147
  </el-radio-group>
151
148
  </div>
152
- <div class="population-details-view" :class="{'align-right': hasSingleConnectivityList}">
149
+ <div class="population-details-view" :class="{ 'align-right': hasSingleConnectivityList }">
153
150
  <el-button
154
151
  :class="activeView === 'listView' ? 'button' : 'el-button-secondary'"
155
152
  @click="switchConnectivityView('listView')"
@@ -170,16 +167,12 @@
170
167
  <div class="attribute-title-container">
171
168
  <span class="attribute-title">Nerves</span>
172
169
  </div>
173
- <div v-for="(nerve, i) in entry['nerve-label']">
174
- <div
175
- class="attribute-content"
176
- :origin-item-label="nerve.nerve"
177
- :key="nerve.nerve"
178
- >
170
+ <div v-for="(nerve, i) in entry['nerve-label']" :key="i">
171
+ <div class="attribute-content" :origin-item-label="nerve.nerve" :key="nerve.nerve">
179
172
  <span>{{ capitalise(nerve.nerve) }}</span>
180
173
  </div>
181
174
  <div
182
- v-for="(subNerve, i) in nerve.subNerves"
175
+ v-for="subNerve in nerve.subNerves"
183
176
  class="attribute-content"
184
177
  style="margin-left: 1rem"
185
178
  :origin-item-label="subNerve"
@@ -205,7 +198,10 @@
205
198
  </div>
206
199
  </div>
207
200
 
208
- <div class="content-container content-container-connectivity" v-show="activeView === 'listView'">
201
+ <div
202
+ class="content-container content-container-connectivity"
203
+ v-show="activeView === 'listView'"
204
+ >
209
205
  <!-- TODO: To use only one component when the data is ready -->
210
206
  <template v-if="hasSingleConnectivityList">
211
207
  <connectivity-reconciliation-list
@@ -248,14 +244,12 @@
248
244
  </template>
249
245
  </div>
250
246
 
251
- <div class="content-container content-container-connectivity" v-show="activeView === 'graphView'">
247
+ <div
248
+ class="content-container content-container-connectivity"
249
+ v-show="activeView === 'graphView'"
250
+ >
252
251
  <template v-if="graphViewLoaded">
253
- <el-button
254
- class="button"
255
- @click="openGraphInViewer"
256
- >
257
- Open in viewer
258
- </el-button>
252
+ <el-button class="button" @click="openGraphInViewer">Open in viewer</el-button>
259
253
  <connectivity-graph
260
254
  v-loading="connectivityLoading"
261
255
  :key="`${connectivityKey}graph`"
@@ -297,8 +291,10 @@
297
291
  <span class="attribute-title">Notes</span>
298
292
  </div>
299
293
  <div class="block">
300
- <div class="alert-block"
301
- v-for="alert in entry.featuresAlert"
294
+ <div
295
+ class="alert-block"
296
+ v-for="(alert, alertIndex) in entry.featuresAlert"
297
+ :key="alertIndex"
302
298
  v-html="formatAlertText(alert)"
303
299
  ></div>
304
300
  </div>
@@ -320,9 +316,7 @@
320
316
  <template v-if="consultant.refreshable">
321
317
  <br />
322
318
  Please try again.
323
- <span class="reload-button" @click="reloadConsultant(consultant)">
324
- Reload
325
- </span>
319
+ <span class="reload-button" @click="reloadConsultant(consultant)">Reload</span>
326
320
  </template>
327
321
  </div>
328
322
  </li>
@@ -332,21 +326,15 @@
332
326
  </template>
333
327
 
334
328
  <script>
335
- /* eslint-disable no-alert, no-console */
336
329
  import {
337
330
  Warning as ElIconWarning,
338
331
  Location as ElIconLocation,
339
332
  Search as ElIconSearch,
340
333
  ArrowDown as ElIconArrowDown,
341
334
  ArrowUp as ElIconArrowUp,
342
- } from '@element-plus/icons-vue'
343
- import {
344
- ElButton as Button,
345
- ElContainer as Container,
346
- ElIcon as Icon,
347
- } from 'element-plus'
348
- import ContributorItem from './ContributorItem.vue'
349
- import EventBus from './EventBus.js'
335
+ } from '@element-plus/icons-vue';
336
+ import ContributorItem from './ContributorItem.vue';
337
+ import EventBus from './EventBus.js';
350
338
  import {
351
339
  CopyToClipboard,
352
340
  ConnectivityGraph,
@@ -355,20 +343,21 @@ import {
355
343
  ExternalResourceCard,
356
344
  } from '@abi-software/map-utilities';
357
345
  import '@abi-software/map-utilities/dist/style.css';
358
- import { capitalise, formatAlertText as formatAlertTextUtil, scrollToRef } from '../utils/common.js'
359
346
 
360
347
  const titleCase = (str) => {
361
348
  return str.replace(/\w\S*/g, (t) => {
362
- return t.charAt(0).toUpperCase() + t.substr(1).toLowerCase()
363
- })
364
- }
349
+ return t.charAt(0).toUpperCase() + t.substr(1).toLowerCase();
350
+ });
351
+ };
352
+
353
+ const capitalise = function (str) {
354
+ if (str) return str.charAt(0).toUpperCase() + str.slice(1);
355
+ return '';
356
+ };
365
357
 
366
358
  export default {
367
359
  name: 'ConnectivityInfo',
368
360
  components: {
369
- Button,
370
- Container,
371
- Icon,
372
361
  ElIconWarning,
373
362
  ElIconLocation,
374
363
  ElIconSearch,
@@ -384,7 +373,7 @@ export default {
384
373
  props: {
385
374
  connectivityEntry: {
386
375
  type: Array,
387
- default: [],
376
+ default: () => [],
388
377
  },
389
378
  entryData: {
390
379
  type: Object,
@@ -392,7 +381,7 @@ export default {
392
381
  },
393
382
  entryId: {
394
383
  type: String,
395
- default: "",
384
+ default: '',
396
385
  },
397
386
  envVars: {
398
387
  type: Object,
@@ -440,18 +429,15 @@ export default {
440
429
  return this.entry?.title || '';
441
430
  },
442
431
  hasProvenanceTaxonomyLabel: function () {
443
- return (
444
- this.entry.provenanceTaxonomyLabel &&
445
- this.entry.provenanceTaxonomyLabel.length > 0
446
- );
432
+ return this.entry.provenanceTaxonomyLabel && this.entry.provenanceTaxonomyLabel.length > 0;
447
433
  },
448
434
  provSpeciesDescription: function () {
449
- let text = "Studied in";
435
+ let text = 'Studied in';
450
436
  this.entry.provenanceTaxonomyLabel.forEach((label) => {
451
437
  text += ` ${label},`;
452
438
  });
453
439
  text = text.slice(0, -1); // remove last comma
454
- text += " species";
440
+ text += ' species';
455
441
  return text;
456
442
  },
457
443
  connectivityKey: function () {
@@ -513,9 +499,7 @@ export default {
513
499
  handler: function (newVal, oldVal) {
514
500
  if (newVal && newVal !== oldVal) {
515
501
  this.connectivityLoading = true;
516
- this.activeView =
517
- localStorage.getItem('connectivity-active-view') ||
518
- this.activeView;
502
+ this.activeView = localStorage.getItem('connectivity-active-view') || this.activeView;
519
503
  if (this.activeView === 'graphView') {
520
504
  this.graphViewLoaded = true;
521
505
  }
@@ -540,10 +524,10 @@ export default {
540
524
  },
541
525
  methods: {
542
526
  titleCase: function (title) {
543
- return titleCase(title)
527
+ return titleCase(title);
544
528
  },
545
529
  capitalise: function (text) {
546
- return capitalise(text)
530
+ return capitalise(text);
547
531
  },
548
532
  toggleTitleExpansion: function () {
549
533
  this.isTitleExpanded = !this.isTitleExpanded;
@@ -585,16 +569,16 @@ export default {
585
569
  this.$emit('show-connectivity', featureIds);
586
570
 
587
571
  EventBus.emit('trackEvent', {
588
- 'event_name': `portal_maps_show_connectivity_on_map`,
589
- 'category': this.entry.id || '',
590
- 'location': 'map_sidebar_connectivity',
572
+ event_name: `portal_maps_show_connectivity_on_map`,
573
+ category: this.entry.id || '',
574
+ location: 'map_sidebar_connectivity',
591
575
  });
592
576
  },
593
577
  onCopied: function () {
594
578
  EventBus.emit('trackEvent', {
595
- 'event_name': `portal_maps_connectivity_copy`,
596
- 'category': this.entry.id || '',
597
- 'location': 'map_sidebar_connectivity',
579
+ event_name: `portal_maps_connectivity_copy`,
580
+ category: this.entry.id || '',
581
+ location: 'map_sidebar_connectivity',
598
582
  });
599
583
  },
600
584
  switchConnectivityView: function (val) {
@@ -609,9 +593,9 @@ export default {
609
593
  }
610
594
 
611
595
  EventBus.emit('trackEvent', {
612
- 'event_name': `portal_maps_connectivity_switch_view`,
613
- 'category': val,
614
- 'location': 'map_sidebar_connectivity',
596
+ event_name: `portal_maps_connectivity_switch_view`,
597
+ category: val,
598
+ location: 'map_sidebar_connectivity',
615
599
  });
616
600
  },
617
601
  openGraphInViewer: function () {
@@ -643,7 +627,7 @@ export default {
643
627
  },
644
628
  onTapNode: function (data) {
645
629
  // save selected state for list view
646
- const name = data.map(t => t.label).join(', ');
630
+ const name = data.map((t) => t.label).join(', ');
647
631
  this.onConnectivityHovered(name);
648
632
  },
649
633
  onShowReferenceConnectivities: function (refSource) {
@@ -712,9 +696,7 @@ export default {
712
696
  });
713
697
  transformedItems.push(itemNames.join(','));
714
698
  });
715
- const contentList = transformedItems
716
- .map((item) => `<li>${item}</li>`)
717
- .join('\n');
699
+ const contentList = transformedItems.map((item) => `<li>${item}</li>`).join('\n');
718
700
  contentString += '\n';
719
701
  contentString += `<ul>${contentList}</ul>`;
720
702
  return contentString;
@@ -730,7 +712,7 @@ export default {
730
712
  const getFirstId = (idArr) => {
731
713
  if (!idArr?.length) return null;
732
714
  const first = idArr[0];
733
- return typeof first === 'string' ? first : (first?.[0] || null);
715
+ return typeof first === 'string' ? first : first?.[0] || null;
734
716
  };
735
717
  const transformedItems = sortedCombinations.map((item) => {
736
718
  const isDirectMatch =
@@ -760,9 +742,7 @@ export default {
760
742
  hasDifferReference = true;
761
743
  return `<s>${sckanLabelWithId}</s> (<strong>Map:</strong> ${mapLabelWithId}) **`;
762
744
  });
763
- const contentList = transformedItems
764
- .map((item) => `<li>${item}</li>`)
765
- .join('\n');
745
+ const contentList = transformedItems.map((item) => `<li>${item}</li>`).join('\n');
766
746
  contentString += '\n';
767
747
  contentString += `<ul>${contentList}</ul>`;
768
748
  return contentString;
@@ -772,7 +752,7 @@ export default {
772
752
  if (this.entry['nerve-label']?.length) {
773
753
  const title = 'Nerves';
774
754
  const nerves = this.entry['nerve-label'];
775
- const nerveLabels = nerves.map(nerve => Object.values(nerve)).flat(Infinity);
755
+ const nerveLabels = nerves.map((nerve) => Object.values(nerve)).flat(Infinity);
776
756
  const transformedNerves = transformData(title, nerveLabels);
777
757
  contentArray.push(transformedNerves);
778
758
  }
@@ -780,17 +760,26 @@ export default {
780
760
  // Origins / Components / Destination
781
761
  if (this.hasSingleConnectivityList) {
782
762
  if (this.originsCombinations?.length) {
783
- const transformedOrigins = transformReconciliationData('Origin', this.originsCombinations);
763
+ const transformedOrigins = transformReconciliationData(
764
+ 'Origin',
765
+ this.originsCombinations,
766
+ );
784
767
  contentArray.push(transformedOrigins);
785
768
  }
786
769
 
787
770
  if (this.componentsCombinations?.length) {
788
- const transformedComponents = transformReconciliationData('Components', this.componentsCombinations);
771
+ const transformedComponents = transformReconciliationData(
772
+ 'Components',
773
+ this.componentsCombinations,
774
+ );
789
775
  contentArray.push(transformedComponents);
790
776
  }
791
777
 
792
778
  if (this.destinationsCombinations?.length) {
793
- const transformedDestinations = transformReconciliationData('Destination', this.destinationsCombinations);
779
+ const transformedDestinations = transformReconciliationData(
780
+ 'Destination',
781
+ this.destinationsCombinations,
782
+ );
794
783
  contentArray.push(transformedDestinations);
795
784
  }
796
785
 
@@ -806,17 +795,29 @@ export default {
806
795
  }
807
796
  } else {
808
797
  if (this.origins?.length) {
809
- const transformedOrigins = transformData('Origin', this.origins, this.originsWithDatasets);
798
+ const transformedOrigins = transformData(
799
+ 'Origin',
800
+ this.origins,
801
+ this.originsWithDatasets,
802
+ );
810
803
  contentArray.push(transformedOrigins);
811
804
  }
812
805
 
813
806
  if (this.components?.length) {
814
- const transformedComponents = transformData('Components', this.components, this.componentsWithDatasets);
807
+ const transformedComponents = transformData(
808
+ 'Components',
809
+ this.components,
810
+ this.componentsWithDatasets,
811
+ );
815
812
  contentArray.push(transformedComponents);
816
813
  }
817
814
 
818
815
  if (this.destinations?.length) {
819
- const transformedDestinations = transformData('Destination', this.destinations, this.destinationsWithDatasets);
816
+ const transformedDestinations = transformData(
817
+ 'Destination',
818
+ this.destinations,
819
+ this.destinationsWithDatasets,
820
+ );
820
821
  contentArray.push(transformedDestinations);
821
822
  }
822
823
  }
@@ -825,9 +826,7 @@ export default {
825
826
  if (references) {
826
827
  let contentString = `<div><strong>References</strong></div>`;
827
828
  contentString += '\n';
828
- const contentList = references.list
829
- .map((item) => `<li>${item}</li>`)
830
- .join('\n');
829
+ const contentList = references.list.map((item) => `<li>${item}</li>`).join('\n');
831
830
  contentString += `<ul>${contentList}</ul>`;
832
831
  contentArray.push(contentString);
833
832
  }
@@ -842,19 +841,24 @@ export default {
842
841
  const fields = [`<div><strong>${consultant.name}</strong></div>`];
843
842
 
844
843
  if (consultant.orcidId) {
845
- fields.push(`<div>`)
846
- fields.push(`<strong>ORCID iD</strong>: <span>${consultant.orcidId}</span> `)
847
- fields.push(`(<a href="${consultant.url}" target="_blank" rel="noopener noreferrer">${consultant.url}</a>)`)
844
+ fields.push(`<div>`);
845
+ fields.push(`<strong>ORCID iD</strong>: <span>${consultant.orcidId}</span> `);
846
+ fields.push(
847
+ `(<a href="${consultant.url}" target="_blank" rel="noopener noreferrer">${consultant.url}</a>)`,
848
+ );
848
849
  fields.push(`</div>`);
849
850
  }
850
851
 
851
- const rrid = consultant.url.indexOf('RRID:') > -1
852
- ? 'RRID:' + consultant.url.split('RRID:')[1]
853
- : '';
852
+ const rrid =
853
+ consultant.url.indexOf('RRID:') > -1
854
+ ? 'RRID:' + consultant.url.split('RRID:')[1]
855
+ : '';
854
856
  if (rrid) {
855
- fields.push(`<div>`)
856
- fields.push(`<strong>RRID</strong>: <span>${rrid}</span> `)
857
- fields.push(`(<a href="${consultant.url}" target="_blank" rel="noopener noreferrer">${consultant.url}</a>)`)
857
+ fields.push(`<div>`);
858
+ fields.push(`<strong>RRID</strong>: <span>${rrid}</span> `);
859
+ fields.push(
860
+ `(<a href="${consultant.url}" target="_blank" rel="noopener noreferrer">${consultant.url}</a>)`,
861
+ );
858
862
  fields.push(`</div>`);
859
863
  }
860
864
 
@@ -892,17 +896,17 @@ export default {
892
896
  const names = label.split(','); // some features have more than one value
893
897
  let data = [];
894
898
  names.forEach((n) => {
895
- const foundData = allWithDatasets.find((a) =>
896
- a.name.toLowerCase().trim() === n.toLowerCase().trim()
899
+ const foundData = allWithDatasets.find(
900
+ (a) => a.name.toLowerCase().trim() === n.toLowerCase().trim(),
897
901
  );
898
902
  if (foundData) {
899
903
  data.push({
900
904
  id: foundData.id,
901
- label: foundData.name
905
+ label: foundData.name,
902
906
  });
903
907
  }
904
908
  });
905
- return data
909
+ return data;
906
910
  },
907
911
  fetchExpertConsultants: async function () {
908
912
  this.expertConsultants = this.expertConsultantURLs.map((url) => ({
@@ -1008,7 +1012,8 @@ export default {
1008
1012
  }
1009
1013
 
1010
1014
  const orcidId = data?.['orcid-identifier']?.path || '';
1011
- const employmentSummary = data?.['activities-summary']?.['employments']?.['employment-summary'] || [];
1015
+ const employmentSummary =
1016
+ data?.['activities-summary']?.['employments']?.['employment-summary'] || [];
1012
1017
  const latestEmployment = employmentSummary[0] || {};
1013
1018
 
1014
1019
  this.updateExpertConsultantByURL(url, {
@@ -1035,7 +1040,9 @@ export default {
1035
1040
  }
1036
1041
  },
1037
1042
  updateExpertConsultantByURL: function (url, fields = {}) {
1038
- const consultantIndex = this.expertConsultants.findIndex((consultant) => consultant.url === url);
1043
+ const consultantIndex = this.expertConsultants.findIndex(
1044
+ (consultant) => consultant.url === url,
1045
+ );
1039
1046
  if (consultantIndex === -1) {
1040
1047
  return;
1041
1048
  }
@@ -1095,13 +1102,13 @@ export default {
1095
1102
  });
1096
1103
 
1097
1104
  EventBus.emit('trackEvent', {
1098
- 'event_name': `portal_maps_connectivity_source_change`,
1099
- 'category': connectivitySource,
1100
- 'location': 'map_sidebar_connectivity',
1105
+ event_name: `portal_maps_connectivity_source_change`,
1106
+ category: connectivitySource,
1107
+ location: 'map_sidebar_connectivity',
1101
1108
  });
1102
1109
  },
1103
1110
  updateGraphConnectivity: function () {
1104
- if (this.connectivitySource === "map") {
1111
+ if (this.connectivitySource === 'map') {
1105
1112
  this.getConnectionsFromMap().then((response) => {
1106
1113
  // show sckan source graph if map source not exist
1107
1114
  this.connectivityFromMap = null;
@@ -1118,8 +1125,7 @@ export default {
1118
1125
  getConnectionsFromMap: async function () {
1119
1126
  if (this.entry.mapuuid) {
1120
1127
  const url =
1121
- this.flatmapApi +
1122
- `flatmap/${this.entry.mapuuid}/connectivity/${this.entry.featureId[0]}`;
1128
+ this.flatmapApi + `flatmap/${this.entry.mapuuid}/connectivity/${this.entry.featureId[0]}`;
1123
1129
 
1124
1130
  try {
1125
1131
  const response = await fetch(url);
@@ -1131,9 +1137,9 @@ export default {
1131
1137
  } catch (error) {
1132
1138
  EventBus.emit('connectivity-source-change', {
1133
1139
  entry: this.entry,
1134
- connectivitySource: "sckan",
1140
+ connectivitySource: 'sckan',
1135
1141
  });
1136
- throw new Error(error);
1142
+ throw error;
1137
1143
  }
1138
1144
  }
1139
1145
  },
@@ -1144,19 +1150,49 @@ export default {
1144
1150
  this.$emit('close-connectivity');
1145
1151
 
1146
1152
  EventBus.emit('trackEvent', {
1147
- 'event_name': `portal_maps_connectivity_close`,
1148
- 'category': this.entry.id || '',
1149
- 'location': 'map_sidebar_connectivity',
1153
+ event_name: `portal_maps_connectivity_close`,
1154
+ category: this.entry.id || '',
1155
+ location: 'map_sidebar_connectivity',
1150
1156
  });
1151
1157
  },
1152
1158
  onTrackEvent: function (data) {
1153
1159
  EventBus.emit('trackEvent', data);
1154
1160
  },
1155
1161
  showAlertMessage: function () {
1156
- scrollToRef(this, 'alertElement');
1162
+ // scroll to alert message
1163
+ this.$nextTick(() => {
1164
+ const alertElement = this.$refs.alertElement;
1165
+ if (alertElement) {
1166
+ alertElement.scrollIntoView({
1167
+ behavior: 'smooth',
1168
+ block: 'start',
1169
+ inline: 'nearest',
1170
+ });
1171
+ }
1172
+ });
1157
1173
  },
1158
1174
  formatAlertText: function (text) {
1159
- return formatAlertTextUtil(text, { formatLines: true });
1175
+ if (!text) return '';
1176
+ const escaped = text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1177
+ const linkified = escaped.replace(/(https?:\/\/[^\s"<>[]+)/g, (url) => {
1178
+ const parts = url.match(/^(.*?)([\].,;:!?]*)$/);
1179
+ const cleanUrl = parts ? parts[1] : url;
1180
+ const suffix = parts ? parts[2] : '';
1181
+ return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer">${cleanUrl}</a>${suffix}`;
1182
+ });
1183
+
1184
+ const normalised = linkified
1185
+ .replace(/\\n/g, '\n')
1186
+ .replace(/\r\n/g, '\n')
1187
+ .replace(/\r/g, '\n');
1188
+
1189
+ return normalised
1190
+ .split('\n')
1191
+ .map((line) => {
1192
+ const withBoldLabel = line.replace(/^\s*([A-Za-z][^:<]{0,120}:)/, '<strong>$1</strong>');
1193
+ return `<div class="alert-line">${withBoldLabel}</div>`;
1194
+ })
1195
+ .join('\n');
1160
1196
  },
1161
1197
  },
1162
1198
  mounted: function () {
@@ -1169,7 +1205,7 @@ export default {
1169
1205
  this.connectivityError = { ...connectivityError };
1170
1206
  });
1171
1207
  },
1172
- }
1208
+ };
1173
1209
  </script>
1174
1210
 
1175
1211
  <style lang="scss" scoped>
@@ -1292,10 +1328,14 @@ export default {
1292
1328
  }
1293
1329
 
1294
1330
  .slide-fade-enter-active {
1295
- transition: opacity 0.5s, transform 0.5s;
1331
+ transition:
1332
+ opacity 0.5s,
1333
+ transform 0.5s;
1296
1334
  }
1297
1335
  .slide-fade-leave-active {
1298
- transition: opacity 0.2s, transform 0.2s;
1336
+ transition:
1337
+ opacity 0.2s,
1338
+ transform 0.2s;
1299
1339
  }
1300
1340
  .slide-fade-enter, .slide-fade-leave-to /* .slide-fade-leave-active in <2.1.8 */ {
1301
1341
  opacity: 0;
@@ -1694,7 +1734,7 @@ export default {
1694
1734
 
1695
1735
  + .attribute-content {
1696
1736
  &::before {
1697
- content: "";
1737
+ content: '';
1698
1738
  width: 90%;
1699
1739
  height: 1px;
1700
1740
  background-color: var(--el-border-color);
@@ -1752,7 +1792,7 @@ export default {
1752
1792
  }
1753
1793
 
1754
1794
  &::after {
1755
- content: "";
1795
+ content: '';
1756
1796
  display: block;
1757
1797
  width: 100%;
1758
1798
  height: 100%;
@@ -1764,7 +1804,8 @@ export default {
1764
1804
  animation-iteration-count: infinite;
1765
1805
  animation-name: loadingAnimation;
1766
1806
  animation-timing-function: linear;
1767
- background: linear-gradient(to right,
1807
+ background: linear-gradient(
1808
+ to right,
1768
1809
  var(--el-bg-color-page) 5%,
1769
1810
  var(--el-color-info-light-8) 15%,
1770
1811
  var(--el-bg-color-page) 30%