@abi-software/map-utilities 1.8.2 → 1.8.3

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.8.2",
3
+ "version": "1.8.3",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -135,9 +135,13 @@ async function competencyQuery(options) {
135
135
  // Neuron populations that share at least one edge with another neuron population [query id => 23]
136
136
  async function queryAllConnectedPaths(flatmapAPI, knowledgeSource, featureId) {
137
137
  const featureIds = Array.isArray(featureId) ? featureId : [featureId];
138
- // Split into paths (ilxtr:) and features (non-ilxtr:)
139
- const pathIds = featureIds.filter(id => id.startsWith('ilxtr:'));
140
- const locationIds = featureIds.filter(id => !id.startsWith('ilxtr:'));
138
+ // Split IDs by prefix: ilx and ilxtr are pathIds, everything else is locationIds.
139
+ const isPathId = (id) => id.startsWith('ilxtr:') || id.startsWith('ilx:');
140
+
141
+ const { pathIds, locationIds } = featureIds.reduce((acc, id) => {
142
+ acc[isPathId(id) ? 'pathIds' : 'locationIds'].push(id);
143
+ return acc;
144
+ }, { pathIds: [], locationIds: [] });
141
145
 
142
146
  const promises = [];
143
147
 
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <div v-if="entry" class="main" v-loading="loading">
3
+ <!-- Navigation -->
3
4
  <div v-if="tooltipEntry.length > 1" class="toggle-button">
4
5
  <el-popover width="auto" trigger="hover" :teleported="false">
5
6
  <template #reference>
@@ -26,76 +27,91 @@
26
27
  <span>{{ nextLabel }}</span>
27
28
  </el-popover>
28
29
  </div>
29
- <div class="block" v-if="entry.title">
30
- <div class="title">{{ capitalise(entry.title) }}</div>
30
+
31
+ <!-- Title -->
32
+ <div>
33
+ <div class="block" v-if="entry.title">
34
+ <div class="title">{{ capitalise(entry.title) }}</div>
35
+ <div
36
+ v-if="
37
+ entry.provenanceTaxonomyLabel &&
38
+ entry.provenanceTaxonomyLabel.length > 0
39
+ "
40
+ class="subtitle"
41
+ >
42
+ {{ provSpeciesDescription }}
43
+ </div>
44
+ </div>
45
+ <div class="block" v-else>
46
+ <div class="title">{{ entry.featureId }}</div>
47
+ </div>
48
+ </div>
49
+
50
+ <!-- Alert notes -->
51
+ <div v-if="entry.featuresAlert?.length">
31
52
  <div
32
- v-if="
33
- entry.provenanceTaxonomyLabel &&
34
- entry.provenanceTaxonomyLabel.length > 0
35
- "
36
- class="subtitle"
53
+ class="collapse-toggle"
54
+ id="toggle-notes"
55
+ @click="showNotes = !showNotes"
37
56
  >
38
- {{ provSpeciesDescription }}
57
+ Notes
58
+ <el-icon>
59
+ <el-icon-arrow-up v-if="showNotes" />
60
+ <el-icon-arrow-down v-else />
61
+ </el-icon>
39
62
  </div>
63
+ <transition name="slide-fade">
64
+ <div v-show="showNotes" class="block collapse-block">
65
+ <div class="alert-block"
66
+ v-for="alert in entry.featuresAlert"
67
+ v-html="formatAlertText(alert)"
68
+ ></div>
69
+ </div>
70
+ </transition>
40
71
  </div>
41
- <div class="block" v-else>
42
- <div class="title">{{ entry.featureId }}</div>
43
- </div>
44
- <div v-if="entry.featuresAlert" class="attribute-title-container">
45
- <span class="attribute-title">Alert</span>
46
- <el-popover
47
- width="250"
48
- trigger="hover"
49
- :teleported="false"
50
- popper-class="popover-origin-help"
72
+
73
+ <!-- Connectiity info -->
74
+ <div>
75
+ <div
76
+ v-show="showDetails"
77
+ class="collapse-toggle"
78
+ id="hide-path-info"
79
+ @click="showDetails = false"
51
80
  >
52
- <template #reference>
53
- <el-icon class="info"><el-icon-warning /></el-icon>
54
- </template>
55
- <span style="word-break: keep-all">
56
- {{ entry.featuresAlert }}
57
- </span>
58
- </el-popover>
59
- </div>
60
- <div
61
- v-show="showDetails"
62
- class="hide"
63
- id="hide-path-info"
64
- @click="showDetails = false"
65
- >
66
- Hide path information
67
- <el-icon><el-icon-arrow-up /></el-icon>
68
- </div>
69
- <div
70
- v-show="!showDetails"
71
- class="hide"
72
- id="show-path-info"
73
- @click="showDetails = true"
74
- >
75
- Show path information
76
- <el-icon><el-icon-arrow-down /></el-icon>
77
- </div>
78
- <transition name="slide-fade">
79
- <div v-show="showDetails" class="content-container scrollbar">
80
- <connectivity-list
81
- :key="entry.featureId[0]"
82
- :entry="entry"
83
- :origins="origins"
84
- :components="components"
85
- :destinations="destinations"
86
- :originsWithDatasets="originsWithDatasets"
87
- :componentsWithDatasets="componentsWithDatasets"
88
- :destinationsWithDatasets="destinationsWithDatasets"
89
- :availableAnatomyFacets="availableAnatomyFacets"
90
- :connectivityError="connectivityError"
91
- @connectivity-action-click="onConnectivityActionClick"
92
- />
93
- <external-resource-card
94
- v-if="resources.length"
95
- :resources="resources"
96
- />
81
+ Hide path information
82
+ <el-icon><el-icon-arrow-up /></el-icon>
83
+ </div>
84
+ <div
85
+ v-show="!showDetails"
86
+ class="collapse-toggle"
87
+ id="show-path-info"
88
+ @click="showDetails = true"
89
+ >
90
+ Show path information
91
+ <el-icon><el-icon-arrow-down /></el-icon>
97
92
  </div>
98
- </transition>
93
+ <transition name="slide-fade">
94
+ <div v-show="showDetails" class="content-container scrollbar collapse-block">
95
+ <connectivity-list
96
+ :key="entry.featureId[0]"
97
+ :entry="entry"
98
+ :origins="origins"
99
+ :components="components"
100
+ :destinations="destinations"
101
+ :originsWithDatasets="originsWithDatasets"
102
+ :componentsWithDatasets="componentsWithDatasets"
103
+ :destinationsWithDatasets="destinationsWithDatasets"
104
+ :availableAnatomyFacets="availableAnatomyFacets"
105
+ :connectivityError="connectivityError"
106
+ @connectivity-action-click="onConnectivityActionClick"
107
+ />
108
+ <external-resource-card
109
+ v-if="resources.length"
110
+ :resources="resources"
111
+ />
112
+ </div>
113
+ </transition>
114
+ </div>
99
115
  </div>
100
116
  </template>
101
117
 
@@ -129,6 +145,7 @@ export default {
129
145
  return {
130
146
  loading: false,
131
147
  showDetails: false,
148
+ showNotes: false,
132
149
  originDescriptions: {
133
150
  motor: "is the location of the initial cell body of the circuit",
134
151
  sensory: "is the location of the initial cell body in the PNS circuit",
@@ -192,6 +209,7 @@ export default {
192
209
  handler: function (newVal, oldVal) {
193
210
  if (newVal !== oldVal) {
194
211
  this.entryIndex = 0;
212
+ this.showNotes = false;
195
213
  }
196
214
  },
197
215
  },
@@ -229,6 +247,38 @@ export default {
229
247
  this.availableAnatomyFacets = JSON.parse(availableAnatomyFacets);
230
248
  }
231
249
  },
250
+ formatAlertText: function (text) {
251
+ if (!text) return '';
252
+ const escaped = text
253
+ .replace(/&/g, '&amp;')
254
+ .replace(/</g, '&lt;')
255
+ .replace(/>/g, '&gt;');
256
+ const linkified = escaped.replace(
257
+ /(https?:\/\/[^\s"<>\[]+)/g,
258
+ (url) => {
259
+ const parts = url.match(/^(.*?)([\].,;:!?]*)$/);
260
+ const cleanUrl = parts ? parts[1] : url;
261
+ const suffix = parts ? parts[2] : '';
262
+ return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer">${cleanUrl}</a>${suffix}`;
263
+ }
264
+ );
265
+
266
+ const normalised = linkified
267
+ .replace(/\\n/g, '\n')
268
+ .replace(/\r\n/g, '\n')
269
+ .replace(/\r/g, '\n');
270
+
271
+ return normalised
272
+ .split('\n')
273
+ .map((line) => {
274
+ const withBoldLabel = line.replace(
275
+ /^\s*([A-Za-z][^:<]{0,120}:)/,
276
+ '<strong>$1</strong>'
277
+ );
278
+ return `<div class="alert-line">${withBoldLabel}</div>`;
279
+ })
280
+ .join('\n');
281
+ },
232
282
  },
233
283
  };
234
284
  </script>
@@ -237,6 +287,7 @@ export default {
237
287
  .toggle-button {
238
288
  display: flex;
239
289
  justify-content: space-between;
290
+ padding-right: 0.75rem; // for close button
240
291
 
241
292
  .is-disabled {
242
293
  color: #fff !important;
@@ -252,13 +303,15 @@ export default {
252
303
  font-size: 18px;
253
304
  font-family: Helvetica;
254
305
  font-weight: bold;
255
- padding-bottom: 8px;
306
+ padding-right: 0.75rem; // for close button
256
307
  color: $app-primary-color;
257
308
  }
258
309
 
259
- .block {
260
- margin-bottom: 0.5em;
310
+ .subtitle {
311
+ margin-top: 8px;
312
+ }
261
313
 
314
+ .block {
262
315
  .main > &:first-of-type {
263
316
  margin-right: 1em;
264
317
  }
@@ -270,11 +323,14 @@ export default {
270
323
  margin-left: 8px;
271
324
  }
272
325
 
273
- .hide {
326
+ .collapse-toggle {
274
327
  color: $app-primary-color;
275
328
  cursor: pointer;
276
- margin-right: 6px;
277
- margin-top: 3px;
329
+ margin-right: 0.75rem;
330
+ }
331
+
332
+ .collapse-block {
333
+ margin-top: 0.5rem;
278
334
  }
279
335
 
280
336
  .main {
@@ -287,6 +343,9 @@ export default {
287
343
  padding: 1em !important;
288
344
  overflow: hidden;
289
345
  min-width: 16rem;
346
+ display: flex;
347
+ flex-direction: column;
348
+ gap: 1rem;
290
349
  }
291
350
 
292
351
  .attribute-title-container {
@@ -376,6 +435,26 @@ export default {
376
435
  }
377
436
  }
378
437
 
438
+ .alert-block {
439
+ background-color: var(--el-color-warning-light-9);
440
+ border: 1px dashed var(--el-color-warning);
441
+ padding: 0.75rem;
442
+ border-radius: 4px;
443
+
444
+ :deep(.alert-line + .alert-line) {
445
+ margin-top: 0.5rem;
446
+ }
447
+
448
+ :deep(a) {
449
+ color: $app-primary-color;
450
+ word-break: break-all;
451
+
452
+ &:hover {
453
+ opacity: 0.8;
454
+ }
455
+ }
456
+ }
457
+
379
458
  .content-container {
380
459
  overflow-y: scroll;
381
460
  scrollbar-width: thin !important;
@@ -384,10 +463,6 @@ export default {
384
463
  .block {
385
464
  padding-top: 0.5em;
386
465
  }
387
-
388
- .connectivity-list {
389
- padding-top: 1rem;
390
- }
391
466
  }
392
467
 
393
468
  .scrollbar::-webkit-scrollbar-track {