@abi-software/map-side-bar 2.14.7 → 2.14.8-demo.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.
- package/dist/map-side-bar.js +9768 -8732
- package/dist/map-side-bar.umd.cjs +77 -72
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/App.vue +11 -4
- package/src/components/CellCard.vue +946 -0
- package/src/components/CellCardExplorer.vue +754 -0
- package/src/components/ConnectivityCard.vue +2 -2
- package/src/components/ConnectivityInfo.vue +3 -46
- package/src/components/SearchFilters.vue +10 -9
- package/src/components/SearchHistory.vue +1 -14
- package/src/components/SideBar.vue +82 -1
- package/src/components/Tabs.vue +15 -6
- package/src/components/icons/IconOpenExternal.vue +28 -0
- package/src/components.d.ts +3 -0
- package/src/utils/common.js +71 -0
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
<script>
|
|
30
30
|
import EventBus from './EventBus';
|
|
31
|
+
import { capitalise as capitaliseText } from '../utils/common.js';
|
|
31
32
|
|
|
32
33
|
export default {
|
|
33
34
|
name: "ConnectivityCard",
|
|
@@ -78,8 +79,7 @@ export default {
|
|
|
78
79
|
},
|
|
79
80
|
methods: {
|
|
80
81
|
capitalise: function (text) {
|
|
81
|
-
|
|
82
|
-
return "";
|
|
82
|
+
return capitaliseText(text);
|
|
83
83
|
},
|
|
84
84
|
cardClicked: function (data) {
|
|
85
85
|
if (!this.loading) {
|
|
@@ -330,6 +330,7 @@ import {
|
|
|
330
330
|
ExternalResourceCard,
|
|
331
331
|
} from '@abi-software/map-utilities';
|
|
332
332
|
import '@abi-software/map-utilities/dist/style.css';
|
|
333
|
+
import { capitalise, formatAlertText as formatAlertTextUtil, scrollToRef } from '../utils/common.js'
|
|
333
334
|
|
|
334
335
|
const titleCase = (str) => {
|
|
335
336
|
return str.replace(/\w\S*/g, (t) => {
|
|
@@ -337,11 +338,6 @@ const titleCase = (str) => {
|
|
|
337
338
|
})
|
|
338
339
|
}
|
|
339
340
|
|
|
340
|
-
const capitalise = function (str) {
|
|
341
|
-
if (str) return str.charAt(0).toUpperCase() + str.slice(1)
|
|
342
|
-
return ''
|
|
343
|
-
}
|
|
344
|
-
|
|
345
341
|
export default {
|
|
346
342
|
name: 'ConnectivityInfo',
|
|
347
343
|
components: {
|
|
@@ -933,49 +929,10 @@ export default {
|
|
|
933
929
|
EventBus.emit('trackEvent', data);
|
|
934
930
|
},
|
|
935
931
|
showAlertMessage: function () {
|
|
936
|
-
|
|
937
|
-
this.$nextTick(() => {
|
|
938
|
-
const alertElement = this.$refs.alertElement;
|
|
939
|
-
if (alertElement) {
|
|
940
|
-
alertElement.scrollIntoView({
|
|
941
|
-
behavior: 'smooth',
|
|
942
|
-
block: 'start',
|
|
943
|
-
inline: 'nearest',
|
|
944
|
-
});
|
|
945
|
-
}
|
|
946
|
-
});
|
|
932
|
+
scrollToRef(this, 'alertElement');
|
|
947
933
|
},
|
|
948
934
|
formatAlertText: function (text) {
|
|
949
|
-
|
|
950
|
-
const escaped = text
|
|
951
|
-
.replace(/&/g, '&')
|
|
952
|
-
.replace(/</g, '<')
|
|
953
|
-
.replace(/>/g, '>');
|
|
954
|
-
const linkified = escaped.replace(
|
|
955
|
-
/(https?:\/\/[^\s"<>\[]+)/g,
|
|
956
|
-
(url) => {
|
|
957
|
-
const parts = url.match(/^(.*?)([\].,;:!?]*)$/);
|
|
958
|
-
const cleanUrl = parts ? parts[1] : url;
|
|
959
|
-
const suffix = parts ? parts[2] : '';
|
|
960
|
-
return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer">${cleanUrl}</a>${suffix}`;
|
|
961
|
-
}
|
|
962
|
-
);
|
|
963
|
-
|
|
964
|
-
const normalised = linkified
|
|
965
|
-
.replace(/\\n/g, '\n')
|
|
966
|
-
.replace(/\r\n/g, '\n')
|
|
967
|
-
.replace(/\r/g, '\n');
|
|
968
|
-
|
|
969
|
-
return normalised
|
|
970
|
-
.split('\n')
|
|
971
|
-
.map((line) => {
|
|
972
|
-
const withBoldLabel = line.replace(
|
|
973
|
-
/^\s*([A-Za-z][^:<]{0,120}:)/,
|
|
974
|
-
'<strong>$1</strong>'
|
|
975
|
-
);
|
|
976
|
-
return `<div class="alert-line">${withBoldLabel}</div>`;
|
|
977
|
-
})
|
|
978
|
-
.join('\n');
|
|
935
|
+
return formatAlertTextUtil(text, { formatLines: true });
|
|
979
936
|
},
|
|
980
937
|
},
|
|
981
938
|
mounted: function () {
|
|
@@ -157,10 +157,7 @@ import '@abi-software/svg-sprite/dist/style.css'
|
|
|
157
157
|
import { AlgoliaClient } from '../algolia/algolia.js'
|
|
158
158
|
import { facetPropPathMapping } from '../algolia/utils.js'
|
|
159
159
|
import EventBus from './EventBus.js'
|
|
160
|
-
|
|
161
|
-
const capitalise = function (txt) {
|
|
162
|
-
return txt.charAt(0).toUpperCase() + txt.slice(1)
|
|
163
|
-
}
|
|
160
|
+
import { capitalise } from '../utils/common.js'
|
|
164
161
|
|
|
165
162
|
const convertReadableLabel = function (original) {
|
|
166
163
|
const name = original.toLowerCase()
|
|
@@ -288,18 +285,22 @@ export default {
|
|
|
288
285
|
return value;
|
|
289
286
|
},
|
|
290
287
|
createChildrenCascaderValue: function(children, facet, facets) {
|
|
288
|
+
const parentKey = facet.key;
|
|
289
|
+
|
|
291
290
|
if (children?.length) {
|
|
292
291
|
for (let i = 0; i < children.length; i++) {
|
|
293
292
|
const facetItem = children[i];
|
|
294
293
|
//copy the facets into
|
|
295
294
|
if (children[i].facetPropPath !== 'supportingAwards.consortium.name') {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
295
|
+
// `sourceNomenclatureLabel` is from cell card explorer filters
|
|
296
|
+
if (parentKey === 'sourceNomenclatureLabel') {
|
|
297
|
+
children[i].label = facetItem.label;
|
|
298
|
+
} else {
|
|
299
|
+
children[i].label = convertReadableLabel(facetItem.label);
|
|
300
|
+
}
|
|
299
301
|
}
|
|
300
|
-
if (facetItem.key &&
|
|
302
|
+
if (facetItem.key && parentKey.includes('flatmap.connectivity.source.')) {
|
|
301
303
|
const childKey = facetItem.key;
|
|
302
|
-
const parentKey = facet.key;
|
|
303
304
|
const key = childKey.replace(`${parentKey}.`, '');
|
|
304
305
|
children[i].value = this.createCascaderItemValue([facet.label, key]);
|
|
305
306
|
} else {
|
|
@@ -147,23 +147,10 @@ import {
|
|
|
147
147
|
} from 'element-plus'
|
|
148
148
|
|
|
149
149
|
import EventBus from './EventBus.js'
|
|
150
|
+
import { generateUUID } from '../utils/common.js';
|
|
150
151
|
|
|
151
152
|
const MAX_SEARCH_HISTORY = 12;
|
|
152
153
|
|
|
153
|
-
function generateUUID() {
|
|
154
|
-
const arr = new Uint8Array(16);
|
|
155
|
-
window.crypto.getRandomValues(arr);
|
|
156
|
-
|
|
157
|
-
arr[6] = (arr[6] & 0x0f) | 0x40;
|
|
158
|
-
arr[8] = (arr[8] & 0x3f) | 0x80;
|
|
159
|
-
|
|
160
|
-
const hex = Array.from(arr)
|
|
161
|
-
.map(byte => byte.toString(16).padStart(2, '0'))
|
|
162
|
-
.join('');
|
|
163
|
-
|
|
164
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
154
|
export default {
|
|
168
155
|
name: 'SearchHistory',
|
|
169
156
|
components: {
|
|
@@ -65,6 +65,19 @@
|
|
|
65
65
|
@connectivity-item-close="onConnectivityItemClose"
|
|
66
66
|
/>
|
|
67
67
|
</template>
|
|
68
|
+
<template v-else-if="tab.type === 'cellCardExplorer' && showCellCards">
|
|
69
|
+
<CellCardExplorer
|
|
70
|
+
:ref="'cellCardExplorerTab_' + tab.id"
|
|
71
|
+
class="sidebar-content-container"
|
|
72
|
+
v-show="tab.id === activeTabId"
|
|
73
|
+
:envVars="envVars"
|
|
74
|
+
:activeSpecies="activeSpeciesForEntries"
|
|
75
|
+
@dataset-search="openDatasetSearchFromCellCard($event)"
|
|
76
|
+
@connectivity-search="openConnectivitySearch($event.facets, $event.query)"
|
|
77
|
+
@soma-location-hovered="showSomaLocation"
|
|
78
|
+
@soma-locations-ready="onSomaLocationsReady"
|
|
79
|
+
/>
|
|
80
|
+
</template>
|
|
68
81
|
<template v-else>
|
|
69
82
|
<DatasetExplorer
|
|
70
83
|
class="sidebar-content-container"
|
|
@@ -95,6 +108,7 @@ import EventBus from './EventBus.js'
|
|
|
95
108
|
import Tabs from './Tabs.vue'
|
|
96
109
|
import AnnotationTool from './AnnotationTool.vue'
|
|
97
110
|
import ConnectivityExplorer from './ConnectivityExplorer.vue'
|
|
111
|
+
import CellCardExplorer from './CellCardExplorer.vue'
|
|
98
112
|
import { removeShowAllFacets } from '../algolia/utils.js'
|
|
99
113
|
|
|
100
114
|
/**
|
|
@@ -110,6 +124,7 @@ export default {
|
|
|
110
124
|
Icon,
|
|
111
125
|
AnnotationTool,
|
|
112
126
|
ConnectivityExplorer,
|
|
127
|
+
CellCardExplorer,
|
|
113
128
|
},
|
|
114
129
|
name: 'SideBar',
|
|
115
130
|
props: {
|
|
@@ -119,6 +134,7 @@ export default {
|
|
|
119
134
|
{ title: 'Dataset Explorer', id: 1, type: 'datasetExplorer', closable: false },
|
|
120
135
|
{ title: 'Connectivity Explorer', id: 2, type: 'connectivityExplorer', closable: false },
|
|
121
136
|
{ title: 'Annotation', id: 3, type: 'annotation', closable: true },
|
|
137
|
+
{ title: 'Cell Card Explorer', id: 4, type: 'cellCardExplorer', closable: false },
|
|
122
138
|
],
|
|
123
139
|
},
|
|
124
140
|
/**
|
|
@@ -184,6 +200,10 @@ export default {
|
|
|
184
200
|
type: Boolean,
|
|
185
201
|
default: false,
|
|
186
202
|
},
|
|
203
|
+
showCellCards: {
|
|
204
|
+
type: Boolean,
|
|
205
|
+
default: false,
|
|
206
|
+
},
|
|
187
207
|
},
|
|
188
208
|
data: function () {
|
|
189
209
|
return {
|
|
@@ -192,6 +212,7 @@ export default {
|
|
|
192
212
|
activeTabId: 1,
|
|
193
213
|
activeAnnotationData: { tabType: "annotation" },
|
|
194
214
|
activeConnectivityData: { tabType: "connectivity" },
|
|
215
|
+
activeSpeciesForEntries: [],
|
|
195
216
|
state: {
|
|
196
217
|
dataset: {
|
|
197
218
|
search: '',
|
|
@@ -235,6 +256,16 @@ export default {
|
|
|
235
256
|
this.activeAnnotationData = data;
|
|
236
257
|
}
|
|
237
258
|
},
|
|
259
|
+
/**
|
|
260
|
+
* This event is emitted when the mouse hover on or off a soma location in cell card explorer.
|
|
261
|
+
* @param name Soma location
|
|
262
|
+
*/
|
|
263
|
+
showSomaLocation: function (name) {
|
|
264
|
+
this.$emit('soma-location-hovered', name);
|
|
265
|
+
},
|
|
266
|
+
onSomaLocationsReady: function (somaLocations) {
|
|
267
|
+
this.$emit('soma-locations-ready', somaLocations);
|
|
268
|
+
},
|
|
238
269
|
/**
|
|
239
270
|
* This event is emitted when the show connectivity button is clicked.
|
|
240
271
|
* @arg featureIds
|
|
@@ -299,6 +330,42 @@ export default {
|
|
|
299
330
|
datasetExplorerTabRef.openSearch(facets, query);
|
|
300
331
|
})
|
|
301
332
|
},
|
|
333
|
+
openCellCardExplorerSearch: function (filters, query) {
|
|
334
|
+
this.drawerOpen = true
|
|
335
|
+
// Because refs are in v-for, nextTick is needed here
|
|
336
|
+
this.$nextTick(() => {
|
|
337
|
+
const cellCardExplorerTabRef = this.getTabRef(undefined, 'cellCardExplorer', true);
|
|
338
|
+
if (cellCardExplorerTabRef && typeof cellCardExplorerTabRef.openSearch === 'function') {
|
|
339
|
+
cellCardExplorerTabRef.openSearch(filters, query);
|
|
340
|
+
}
|
|
341
|
+
})
|
|
342
|
+
},
|
|
343
|
+
openDatasetSearchFromCellCard: function (payload) {
|
|
344
|
+
if (!payload || typeof payload !== 'object') {
|
|
345
|
+
this.openSearch([], payload);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const facets = [];
|
|
350
|
+
if (payload.species) {
|
|
351
|
+
facets.push({
|
|
352
|
+
facet: payload.species,
|
|
353
|
+
term: 'Species',
|
|
354
|
+
facetPropPath: 'organisms.primary.species.name',
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (payload.location) {
|
|
359
|
+
facets.push({
|
|
360
|
+
facet: payload.location,
|
|
361
|
+
term: 'Anatomical structure',
|
|
362
|
+
facetPropPath: 'anatomy.organ.category.name',
|
|
363
|
+
AND: true,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
this.openSearch(facets, '');
|
|
368
|
+
},
|
|
302
369
|
/**
|
|
303
370
|
* Get the ref id of the tab by id and type.
|
|
304
371
|
*/
|
|
@@ -501,7 +568,17 @@ export default {
|
|
|
501
568
|
...data,
|
|
502
569
|
};
|
|
503
570
|
this.$emit('trackEvent', taggingData);
|
|
504
|
-
}
|
|
571
|
+
},
|
|
572
|
+
/**
|
|
573
|
+
* @public
|
|
574
|
+
* Update the active species to use in filters.
|
|
575
|
+
* Used by SplitFlow component.
|
|
576
|
+
* @param activeSpecies
|
|
577
|
+
*/
|
|
578
|
+
updateActiveSpeciesForEntries: function (activeSpecies) {
|
|
579
|
+
const speciesEntries = Array.isArray(activeSpecies) ? activeSpecies : [activeSpecies];
|
|
580
|
+
this.activeSpeciesForEntries = [...new Set(speciesEntries.filter(Boolean))];
|
|
581
|
+
},
|
|
505
582
|
},
|
|
506
583
|
computed: {
|
|
507
584
|
// This should respect the information provided by the property
|
|
@@ -513,6 +590,10 @@ export default {
|
|
|
513
590
|
tab.type === "annotation" &&
|
|
514
591
|
this.annotationEntry &&
|
|
515
592
|
this.annotationEntry.length > 0
|
|
593
|
+
) ||
|
|
594
|
+
(
|
|
595
|
+
tab.type === "cellCardExplorer" &&
|
|
596
|
+
this.showCellCards
|
|
516
597
|
)
|
|
517
598
|
);
|
|
518
599
|
},
|
package/src/components/Tabs.vue
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
class="tab"
|
|
5
5
|
v-for="tab in tabs"
|
|
6
6
|
:key="tab.id"
|
|
7
|
-
:class="{ 'active-tab': tab.id == activeId }"
|
|
7
|
+
:class="{ 'active-tab': tab.id == activeId, 'closable-tab': tab.closable }"
|
|
8
8
|
@click="tabClicked(tab)"
|
|
9
9
|
>
|
|
10
10
|
<span class="tab-title">{{ tab.title }} </span>
|
|
@@ -103,14 +103,23 @@ export default {
|
|
|
103
103
|
.tab-title {
|
|
104
104
|
text-align: center;
|
|
105
105
|
font-size: 14px;
|
|
106
|
-
padding: 0
|
|
106
|
+
padding: 0 0.75rem;
|
|
107
|
+
|
|
108
|
+
.closable-tab & {
|
|
109
|
+
padding-right: 0.25rem;
|
|
110
|
+
}
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
.tab-close-icon {
|
|
110
|
-
width:
|
|
111
|
-
height:
|
|
112
|
-
font-size:
|
|
113
|
-
|
|
114
|
+
width: 18px !important;
|
|
115
|
+
height: 18px !important;
|
|
116
|
+
font-size: 18px !important;
|
|
117
|
+
margin-right: 4px !important;
|
|
114
118
|
color: $app-primary-color !important;
|
|
119
|
+
border-radius: 2px;
|
|
120
|
+
|
|
121
|
+
&:hover {
|
|
122
|
+
background-color: rgba($app-primary-color, 0.15) !important;
|
|
123
|
+
}
|
|
115
124
|
}
|
|
116
125
|
</style>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg
|
|
3
|
+
viewBox="0 0 24 24"
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
aria-hidden="true"
|
|
6
|
+
focusable="false"
|
|
7
|
+
>
|
|
8
|
+
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
9
|
+
<polygon
|
|
10
|
+
fill="currentColor"
|
|
11
|
+
fill-rule="nonzero"
|
|
12
|
+
transform="translate(15.500000, 8.878680) rotate(225.000000) translate(-15.500000, -8.878680)"
|
|
13
|
+
points="16.25 4.12867966 16.25 13.6286797 14.75 13.6286797 14.75 4.12867966"
|
|
14
|
+
/>
|
|
15
|
+
<polygon
|
|
16
|
+
fill="currentColor"
|
|
17
|
+
fill-rule="nonzero"
|
|
18
|
+
transform="translate(17.500000, 6.918996) rotate(225.000000) translate(-17.500000, -6.918996)"
|
|
19
|
+
points="20.9294459 3.82036868 22.058311 4.80812559 17.5 10.0176239 12.941689 4.80812559 14.0705541 3.82036868 17.5 7.739"
|
|
20
|
+
/>
|
|
21
|
+
<polygon
|
|
22
|
+
fill="currentColor"
|
|
23
|
+
fill-rule="nonzero"
|
|
24
|
+
points="11.6570511 7.25 11.6570511 8.75 5.75 8.75 5.75 16.25 15.25 16.25 15.25 11.2267933 16.75 11.2267933 16.75 17.75 4.25 17.75 4.25 7.25"
|
|
25
|
+
/>
|
|
26
|
+
</g>
|
|
27
|
+
</svg>
|
|
28
|
+
</template>
|
package/src/components.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ declare module 'vue' {
|
|
|
9
9
|
export interface GlobalComponents {
|
|
10
10
|
AnnotationTool: typeof import('./components/AnnotationTool.vue')['default']
|
|
11
11
|
BadgesGroup: typeof import('./components/BadgesGroup.vue')['default']
|
|
12
|
+
CellCard: typeof import('./components/CellCard.vue')['default']
|
|
13
|
+
CellCardExplorer: typeof import('./components/CellCardExplorer.vue')['default']
|
|
12
14
|
ConnectivityCard: typeof import('./components/ConnectivityCard.vue')['default']
|
|
13
15
|
ConnectivityExplorer: typeof import('./components/ConnectivityExplorer.vue')['default']
|
|
14
16
|
ConnectivityInfo: typeof import('./components/ConnectivityInfo.vue')['default']
|
|
@@ -43,6 +45,7 @@ declare module 'vue' {
|
|
|
43
45
|
ElRow: typeof import('element-plus/es')['ElRow']
|
|
44
46
|
ElSelect: typeof import('element-plus/es')['ElSelect']
|
|
45
47
|
ElTag: typeof import('element-plus/es')['ElTag']
|
|
48
|
+
IconOpenExternal: typeof import('./components/icons/IconOpenExternal.vue')['default']
|
|
46
49
|
ImageGallery: typeof import('./components/ImageGallery.vue')['default']
|
|
47
50
|
SearchFilters: typeof import('./components/SearchFilters.vue')['default']
|
|
48
51
|
SearchHistory: typeof import('./components/SearchHistory.vue')['default']
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export function generateUUID() {
|
|
2
|
+
const arr = new Uint8Array(16);
|
|
3
|
+
window.crypto.getRandomValues(arr);
|
|
4
|
+
|
|
5
|
+
arr[6] = (arr[6] & 0x0f) | 0x40;
|
|
6
|
+
arr[8] = (arr[8] & 0x3f) | 0x80;
|
|
7
|
+
|
|
8
|
+
const hex = Array.from(arr)
|
|
9
|
+
.map(byte => byte.toString(16).padStart(2, '0'))
|
|
10
|
+
.join('');
|
|
11
|
+
|
|
12
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function capitalise(text) {
|
|
16
|
+
if (!text) return '';
|
|
17
|
+
const value = String(text);
|
|
18
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function formatAlertText(text, { formatLines = false } = {}) {
|
|
22
|
+
if (!text) return '';
|
|
23
|
+
|
|
24
|
+
const escaped = String(text)
|
|
25
|
+
.replace(/&/g, '&')
|
|
26
|
+
.replace(/</g, '<')
|
|
27
|
+
.replace(/>/g, '>');
|
|
28
|
+
|
|
29
|
+
const linkified = escaped.replace(
|
|
30
|
+
/(https?:\/\/[^\s"<>\[]+)/g,
|
|
31
|
+
(url) => {
|
|
32
|
+
const parts = url.match(/^(.*?)([\].,;:!?]*)$/);
|
|
33
|
+
const cleanUrl = parts ? parts[1] : url;
|
|
34
|
+
const suffix = parts ? parts[2] : '';
|
|
35
|
+
return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer">${cleanUrl}</a>${suffix}`;
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const normalised = linkified
|
|
40
|
+
.replace(/\\n/g, '\n')
|
|
41
|
+
.replace(/\r\n/g, '\n')
|
|
42
|
+
.replace(/\r/g, '\n');
|
|
43
|
+
|
|
44
|
+
if (!formatLines) {
|
|
45
|
+
return normalised;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return normalised
|
|
49
|
+
.split('\n')
|
|
50
|
+
.map((line) => {
|
|
51
|
+
const withBoldLabel = line.replace(
|
|
52
|
+
/^\s*([A-Za-z][^:<]{0,120}:)/,
|
|
53
|
+
'<strong>$1</strong>'
|
|
54
|
+
);
|
|
55
|
+
return `<div class="alert-line">${withBoldLabel}</div>`;
|
|
56
|
+
})
|
|
57
|
+
.join('\n');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function scrollToRef(vm, refName) {
|
|
61
|
+
vm?.$nextTick(() => {
|
|
62
|
+
const element = vm?.$refs?.[refName];
|
|
63
|
+
if (element) {
|
|
64
|
+
element.scrollIntoView({
|
|
65
|
+
behavior: 'smooth',
|
|
66
|
+
block: 'start',
|
|
67
|
+
inline: 'nearest',
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|