@abi-software/mapintegratedvuer 1.23.0 → 1.24.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/{ConnectivityGraph-Cao0C1-G.js → ConnectivityGraph-CCxk8vcE.js} +6 -6
- package/dist/{ContentMixin-4t04VWoF.js → ContentMixin-Db8nqZax.js} +295 -240
- package/dist/{DynamicMarkerMixin-tATj70ym.js → DynamicMarkerMixin-LB2RYdSY.js} +1 -1
- package/dist/{Flatmap-DF4FAwLY.js → Flatmap-CqUzOkdl.js} +5 -5
- package/dist/{Iframe-DbQ22y53.js → Iframe-I-xdZ2FP.js} +2 -2
- package/dist/{MultiFlatmap-Dwy9gbWC.js → MultiFlatmap-11L97ycJ.js} +6 -6
- package/dist/{Plot-COXE_H78.js → Plot-Bd9RqyVD.js} +2 -2
- package/dist/{Scaffold-9ubjNzcD.js → Scaffold-CySWXQxI.js} +46 -36
- package/dist/{Simulation-CR-yKjxu.js → Simulation-ChUbGudZ.js} +2 -2
- package/dist/{connectivities-DhBMlBSj.js → connectivities-LtlFwLOp.js} +6399 -5790
- package/dist/es-D-YkSGXX.js +436 -0
- package/dist/mapintegratedvuer.js +12994 -11652
- package/dist/mapintegratedvuer.umd.cjs +191 -190
- package/dist/style.css +1 -1
- package/package.json +4 -4
- package/src/assets/header-icon.scss +9 -3
- package/src/components/ContentBar.vue +172 -92
- package/src/components/ContentVuer.vue +41 -1
- package/src/components/DialogToolbarContent.vue +45 -4
- package/src/components/EventBus.js +8 -0
- package/src/components/MapContent.vue +77 -31
- package/src/components/ResizeSensor.vue +7 -9
- package/src/components/ScreenshotPopover.vue +526 -0
- package/src/components/SplitDialog.vue +94 -93
- package/src/components/SplitFlow.vue +158 -96
- package/src/components/scripts/utilities.js +56 -1
- package/src/components/viewers/Flatmap.vue +1 -0
- package/src/components/viewers/MultiFlatmap.vue +3 -0
- package/src/components/viewers/Scaffold.vue +25 -13
- package/src/components.d.ts +2 -0
- package/src/mixins/ContentMixin.js +23 -10
- package/src/services/screenshot.js +1186 -0
- package/src/stores/connectivities.js +38 -5
- package/src/stores/settings.js +18 -0
|
@@ -22,12 +22,13 @@
|
|
|
22
22
|
<script>
|
|
23
23
|
import ContentVuer from './ContentVuer.vue';
|
|
24
24
|
import CustomSplitter from './CustomSplitter.vue';
|
|
25
|
-
import EventBus from './EventBus';
|
|
25
|
+
import EventBus, { onEvents } from './EventBus';
|
|
26
26
|
import { mapStores } from 'pinia';
|
|
27
27
|
import { useSplitFlowStore } from '../stores/splitFlow';
|
|
28
28
|
import { useSettingsStore } from '../stores/settings';
|
|
29
29
|
import { useConnectivitiesStore } from '../stores/connectivities';
|
|
30
30
|
import { queryPathsByRoute, queryAllConnectedPaths } from '@abi-software/map-utilities';
|
|
31
|
+
import { getLatestFlatmap } from './scripts/utilities.js';
|
|
31
32
|
|
|
32
33
|
export default {
|
|
33
34
|
name: 'SplitDialog',
|
|
@@ -314,13 +315,8 @@ export default {
|
|
|
314
315
|
return ids;
|
|
315
316
|
},
|
|
316
317
|
getLatestFlatmapUUID: async function (flatmapAPI, id) {
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
const latestFlatmap = flatmapJson
|
|
320
|
-
.filter((f) => f.id === id)
|
|
321
|
-
.sort((a, b) => b.created.localeCompare(a.created))[0];
|
|
322
|
-
const flatmapUUID = latestFlatmap.uuid;
|
|
323
|
-
return flatmapUUID;
|
|
318
|
+
const latestFlatmap = await getLatestFlatmap(flatmapAPI, id);
|
|
319
|
+
return latestFlatmap.uuid;
|
|
324
320
|
},
|
|
325
321
|
parseSearchTerms: function (query) {
|
|
326
322
|
return query
|
|
@@ -587,93 +583,98 @@ export default {
|
|
|
587
583
|
},
|
|
588
584
|
},
|
|
589
585
|
mounted: function () {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
586
|
+
this._offBus = onEvents({
|
|
587
|
+
PaneResize: (payload) => {
|
|
588
|
+
this.setStyles(payload.refName, payload.rect);
|
|
589
|
+
this.updateFlatmapMinimap();
|
|
590
|
+
},
|
|
591
|
+
PaneUnmounted: (payload) => {
|
|
592
|
+
this.hidePane(payload.refName);
|
|
593
|
+
},
|
|
594
|
+
'species-layout-connectivity-update': () => {
|
|
595
|
+
this.onSpeciesLayoutConnectivityUpdate();
|
|
596
|
+
},
|
|
597
|
+
'connectivity-query-filter': (payload) => {
|
|
598
|
+
this.connectivityQueryFilter(payload);
|
|
599
|
+
},
|
|
600
|
+
//The followings are migrated from ContentVuer and its child components to here
|
|
601
|
+
hoverUpdate: (payload) => {
|
|
602
|
+
const contents = this.getActiveContents();
|
|
603
|
+
contents.forEach((content) => {
|
|
604
|
+
content.onHoverUpdate(payload);
|
|
605
|
+
});
|
|
606
|
+
},
|
|
607
|
+
startHelp: () => {
|
|
608
|
+
const contents = this.getActiveContents();
|
|
609
|
+
contents.forEach((content) => {
|
|
610
|
+
content.onStartHelp();
|
|
611
|
+
});
|
|
612
|
+
},
|
|
613
|
+
'connectivity-item-close': () => {
|
|
614
|
+
const contents = this.getActiveContents();
|
|
615
|
+
contents.forEach((content) => {
|
|
616
|
+
content.onConnectivityItemClose();
|
|
617
|
+
});
|
|
618
|
+
},
|
|
619
|
+
'sidebar-annotation-close': () => {
|
|
620
|
+
const contents = this.getActiveContents();
|
|
621
|
+
contents.forEach((content) => {
|
|
622
|
+
content.onSidebarAnnotationClose();
|
|
623
|
+
});
|
|
624
|
+
},
|
|
625
|
+
globalViewerSettingsUpdate: () => {
|
|
626
|
+
const contents = this.$refs['content'];
|
|
627
|
+
contents.forEach((content) => {
|
|
628
|
+
content.onGlobalViewerSettingsUpdate();
|
|
629
|
+
});
|
|
630
|
+
},
|
|
631
|
+
markerUpdate: () => {
|
|
632
|
+
const contents = this.$refs['content'];
|
|
633
|
+
contents.forEach((content) => {
|
|
634
|
+
content.onFlatmapMarkerUpdate();
|
|
635
|
+
});
|
|
636
|
+
},
|
|
637
|
+
'connectivity-hovered': (payload) => {
|
|
638
|
+
const contents = this.getActiveContents();
|
|
639
|
+
contents.forEach((content) => {
|
|
640
|
+
content.onShowConnectivityTooltips(payload);
|
|
641
|
+
});
|
|
642
|
+
},
|
|
643
|
+
'connectivity-source-change': (payload) => {
|
|
644
|
+
const contents = this.getActiveContents();
|
|
645
|
+
//Use ongoingSource array to make sure, the knowledge
|
|
646
|
+
//from each source is only loaded once.
|
|
647
|
+
//TODO: This can be improved with using just one viewer to give us
|
|
648
|
+
//the new knowlegdge with the species/sources specified which
|
|
649
|
+
//will require some UI changes on the sidebar
|
|
650
|
+
const ongoingSources = [];
|
|
651
|
+
contents.forEach((content) => {
|
|
652
|
+
content.onConnectivitySourceChange(payload, ongoingSources);
|
|
653
|
+
});
|
|
654
|
+
},
|
|
655
|
+
'show-connectivity': (payload) => {
|
|
656
|
+
const contents = this.getActiveContents();
|
|
657
|
+
contents.forEach((content) => {
|
|
658
|
+
content.onShowConnectivity(payload);
|
|
659
|
+
});
|
|
660
|
+
},
|
|
661
|
+
'show-reference-connectivities': (payload) => {
|
|
662
|
+
const contents = this.getActiveContents();
|
|
663
|
+
contents.forEach((content) => {
|
|
664
|
+
content.onShowReferenceConnectivity(payload);
|
|
665
|
+
});
|
|
666
|
+
},
|
|
667
|
+
'filter-visibility': (payload) => {
|
|
668
|
+
const contents = this.getActiveContents();
|
|
669
|
+
contents.forEach((content) => {
|
|
670
|
+
content.onFilterVisibility(payload);
|
|
671
|
+
});
|
|
672
|
+
},
|
|
675
673
|
});
|
|
676
674
|
},
|
|
675
|
+
beforeUnmount: function () {
|
|
676
|
+
this._offBus?.();
|
|
677
|
+
},
|
|
677
678
|
};
|
|
678
679
|
</script>
|
|
679
680
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
@@ -11,11 +11,16 @@
|
|
|
11
11
|
@onFullscreen="onFullscreen"
|
|
12
12
|
@local-search="onDisplaySearch"
|
|
13
13
|
@fetch-suggestions="fetchSuggestions"
|
|
14
|
+
:captureScreenshot="captureScreenshot"
|
|
15
|
+
:getScreenshotTarget="getScreenshotElement"
|
|
14
16
|
ref="dialogToolbar"
|
|
15
17
|
/>
|
|
16
18
|
</el-header>
|
|
17
19
|
<el-main class="dialog-main">
|
|
18
|
-
<div
|
|
20
|
+
<div
|
|
21
|
+
ref="captureArea"
|
|
22
|
+
style="width: 100%; height: 100%; position: relative; overflow: hidden"
|
|
23
|
+
>
|
|
19
24
|
<SideBar
|
|
20
25
|
ref="sideBar"
|
|
21
26
|
:envVars="envVars"
|
|
@@ -67,7 +72,8 @@
|
|
|
67
72
|
import { provide, markRaw, computed } from 'vue';
|
|
68
73
|
import Tagging from '../services/tagging.js';
|
|
69
74
|
import DialogToolbarContent from './DialogToolbarContent.vue';
|
|
70
|
-
import
|
|
75
|
+
import { captureElement, ScreenshotError } from '../services/screenshot';
|
|
76
|
+
import EventBus, { onEvents } from './EventBus';
|
|
71
77
|
import SplitDialog from './SplitDialog.vue';
|
|
72
78
|
// import contextCards from './context-cards'
|
|
73
79
|
import { SideBar } from '@abi-software/map-side-bar';
|
|
@@ -887,6 +893,56 @@ export default {
|
|
|
887
893
|
onFullscreen: function (val) {
|
|
888
894
|
this.$emit('onFullscreen', val);
|
|
889
895
|
},
|
|
896
|
+
/**
|
|
897
|
+
* Capture a screenshot and download it.
|
|
898
|
+
* Captures the pane with `paneId` if provided, otherwise all
|
|
899
|
+
* visible panes including the sidebar.
|
|
900
|
+
* With `legend` set to `only`, the legend of each pane is downloaded as a separate image.
|
|
901
|
+
* @param {Object} options - { paneId, scale, legend, toolbar }
|
|
902
|
+
*/
|
|
903
|
+
captureScreenshot: async function (options = {}) {
|
|
904
|
+
const { paneId } = options;
|
|
905
|
+
const contents = this.$refs.splitdialog?.getActiveContents() || [];
|
|
906
|
+
const scale = options.scale || this.settingsStore.screenshot.scale;
|
|
907
|
+
const legend = options.legend || this.settingsStore.screenshot.legend;
|
|
908
|
+
const toolbar = options.toolbar ?? this.settingsStore.screenshot.toolbar;
|
|
909
|
+
|
|
910
|
+
if (paneId !== undefined) {
|
|
911
|
+
const content = this.$refs.splitdialog?.getContentsWithId(paneId);
|
|
912
|
+
|
|
913
|
+
if (!content) {
|
|
914
|
+
return Promise.reject(new Error(`Pane ${paneId} not found`));
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
return content.captureScreenshot(options);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
if (legend === 'only') {
|
|
921
|
+
const withLegend = contents.filter((content) => content.hasLegend?.());
|
|
922
|
+
const results = [];
|
|
923
|
+
|
|
924
|
+
if (!withLegend.length) {
|
|
925
|
+
throw new ScreenshotError('no-legend', 'No legend to capture');
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
for (const content of withLegend) {
|
|
929
|
+
results.push(...(await content.captureScreenshot({ ...options, scale, legend })));
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
return results;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
return captureElement(this.$refs.captureArea, {
|
|
936
|
+
scale,
|
|
937
|
+
legend,
|
|
938
|
+
toolbar,
|
|
939
|
+
title: 'maps-view',
|
|
940
|
+
viewers: contents,
|
|
941
|
+
});
|
|
942
|
+
},
|
|
943
|
+
getScreenshotElement: function () {
|
|
944
|
+
return this.$refs.captureArea;
|
|
945
|
+
},
|
|
890
946
|
resetApp: function () {
|
|
891
947
|
this.setState(initialDefaultState());
|
|
892
948
|
},
|
|
@@ -1166,117 +1222,123 @@ export default {
|
|
|
1166
1222
|
this._externalStateSet = false;
|
|
1167
1223
|
},
|
|
1168
1224
|
mounted: function () {
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1225
|
+
this._offBus = onEvents({
|
|
1226
|
+
CreateNewEntry: (newView) => {
|
|
1227
|
+
this.createNewEntry(newView);
|
|
1228
|
+
},
|
|
1229
|
+
RemoveEntryRequest: (id) => {
|
|
1230
|
+
this.removeEntry(id);
|
|
1231
|
+
},
|
|
1232
|
+
PopoverActionClick: (payload) => {
|
|
1233
|
+
this.actionClick(payload);
|
|
1234
|
+
},
|
|
1235
|
+
'annotation-open': (payload) => {
|
|
1236
|
+
this.openAnnotation(payload);
|
|
1237
|
+
},
|
|
1238
|
+
'sidebar-annotation-close': () => {
|
|
1239
|
+
const globalSettings = { ...this.settingsStore.globalSettings };
|
|
1240
|
+
const { interactiveMode, viewingMode } = globalSettings;
|
|
1184
1241
|
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1242
|
+
// Sidebar annotation close event emits whenever viewing mode is changed.
|
|
1243
|
+
// if annotation state is being restored on first load for annotation viewing mode,
|
|
1244
|
+
// open the anootation tab and return.
|
|
1245
|
+
if (this.sidebarAnnotationState && viewingMode === 'Annotation') {
|
|
1246
|
+
this.sidebarAnnotationState = false;
|
|
1247
|
+
this.$refs.sideBar.tabClicked({ id: 3, type: 'annotation' });
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1193
1250
|
|
|
1194
|
-
|
|
1195
|
-
|
|
1251
|
+
this.annotationEntry = [];
|
|
1252
|
+
this.createData = {};
|
|
1196
1253
|
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1254
|
+
if (this.$refs.sideBar) {
|
|
1255
|
+
if (interactiveMode === 'dataset') {
|
|
1256
|
+
this.$refs.sideBar.tabClicked({ id: 1, type: 'datasetExplorer' });
|
|
1257
|
+
} else if (interactiveMode === 'connectivity') {
|
|
1258
|
+
this.$refs.sideBar.tabClicked({ id: 2, type: 'connectivityExplorer' });
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
this.$refs.sideBar.closeConnectivity();
|
|
1262
|
+
EventBus.emit('connectivity-item-close');
|
|
1263
|
+
}
|
|
1264
|
+
},
|
|
1265
|
+
'update-offline-annotation-enabled': (payload) => {
|
|
1266
|
+
this.settingsStore.updateOfflineAnnotationEnabled(payload);
|
|
1267
|
+
},
|
|
1268
|
+
'connectivity-info-open': (payload) => {
|
|
1269
|
+
this.openConnectivityInfo(payload);
|
|
1270
|
+
},
|
|
1271
|
+
'connectivity-info-close': (_payload) => {
|
|
1272
|
+
if (this.$refs.sideBar) {
|
|
1273
|
+
this.connectivityProcessed = false;
|
|
1274
|
+
this.$refs.sideBar.resetConnectivitySearch();
|
|
1275
|
+
}
|
|
1276
|
+
},
|
|
1277
|
+
'connectivity-error': (payload) => {
|
|
1278
|
+
if (this.$refs.sideBar) {
|
|
1279
|
+
this.$refs.sideBar.updateConnectivityError(payload.data);
|
|
1280
|
+
}
|
|
1281
|
+
},
|
|
1282
|
+
'neuron-connection-feature-click': (payload) => {
|
|
1283
|
+
if (this.$refs.sideBar) {
|
|
1284
|
+
const { filters, search } = payload;
|
|
1285
|
+
this.$refs.sideBar.openConnectivitySearch(filters, search);
|
|
1201
1286
|
this.$refs.sideBar.tabClicked({ id: 2, type: 'connectivityExplorer' });
|
|
1287
|
+
this.$refs.sideBar.setDrawerOpen(true);
|
|
1288
|
+
}
|
|
1289
|
+
},
|
|
1290
|
+
OpenNewMap: (type) => {
|
|
1291
|
+
this.updateFlatmapMinimap();
|
|
1292
|
+
this.openNewMap(type);
|
|
1293
|
+
},
|
|
1294
|
+
startHelp: () => {
|
|
1295
|
+
if (this.$refs.sideBar) {
|
|
1296
|
+
this.$refs.sideBar.close();
|
|
1297
|
+
}
|
|
1298
|
+
},
|
|
1299
|
+
'connectivity-knowledge': (payload) => {
|
|
1300
|
+
// Ignore the default/global connectivity dump (emitted when no active
|
|
1301
|
+
// viewer's map/species keys are resolved yet) while restoring a
|
|
1302
|
+
// permalink's sidebar connectivityEntries, otherwise the sidebar would
|
|
1303
|
+
// briefly flash the entire, unfiltered connectivity list.
|
|
1304
|
+
if (!(payload.isDefaultKnowledge && this.restoringConnectivityState)) {
|
|
1305
|
+
this.connectivityKnowledge = payload.data;
|
|
1306
|
+
this.connectivityHighlight = payload.highlight;
|
|
1307
|
+
this.connectivityProcessed = payload.processed;
|
|
1202
1308
|
}
|
|
1203
1309
|
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
EventBus.on('connectivity-error', (payload) => {
|
|
1221
|
-
if (this.$refs.sideBar) {
|
|
1222
|
-
this.$refs.sideBar.updateConnectivityError(payload.data);
|
|
1223
|
-
}
|
|
1224
|
-
});
|
|
1225
|
-
EventBus.on('neuron-connection-feature-click', (payload) => {
|
|
1226
|
-
if (this.$refs.sideBar) {
|
|
1227
|
-
const { filters, search } = payload;
|
|
1228
|
-
this.$refs.sideBar.openConnectivitySearch(filters, search);
|
|
1229
|
-
this.$refs.sideBar.tabClicked({ id: 2, type: 'connectivityExplorer' });
|
|
1230
|
-
this.$refs.sideBar.setDrawerOpen(true);
|
|
1231
|
-
}
|
|
1232
|
-
});
|
|
1233
|
-
EventBus.on('OpenNewMap', (type) => {
|
|
1234
|
-
this.updateFlatmapMinimap();
|
|
1235
|
-
this.openNewMap(type);
|
|
1236
|
-
});
|
|
1237
|
-
EventBus.on('startHelp', () => {
|
|
1238
|
-
if (this.$refs.sideBar) {
|
|
1239
|
-
this.$refs.sideBar.close();
|
|
1240
|
-
}
|
|
1241
|
-
});
|
|
1242
|
-
EventBus.on('connectivity-knowledge', (payload) => {
|
|
1243
|
-
// Ignore the default/global connectivity dump (emitted when no active
|
|
1244
|
-
// viewer's map/species keys are resolved yet) while restoring a
|
|
1245
|
-
// permalink's sidebar connectivityEntries, otherwise the sidebar would
|
|
1246
|
-
// briefly flash the entire, unfiltered connectivity list.
|
|
1247
|
-
if (!(payload.isDefaultKnowledge && this.restoringConnectivityState)) {
|
|
1248
|
-
this.connectivityKnowledge = payload.data;
|
|
1249
|
-
this.connectivityHighlight = payload.highlight;
|
|
1250
|
-
this.connectivityProcessed = payload.processed;
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
// Restore sidebar state if it exists and not restored yet
|
|
1254
|
-
// after loading connectivity knowledge
|
|
1255
|
-
if (!this.sidebarStateRestored) {
|
|
1256
|
-
this.restoreSidebarState(this.state);
|
|
1257
|
-
}
|
|
1258
|
-
});
|
|
1259
|
-
EventBus.on('modeUpdate', (payload) => {
|
|
1260
|
-
if (payload === 'dataset') {
|
|
1261
|
-
this.$refs.sideBar.tabClicked({ id: 1, type: 'datasetExplorer' });
|
|
1262
|
-
} else if (payload === 'connectivity') {
|
|
1263
|
-
this.$refs.sideBar.tabClicked({ id: 2, type: 'connectivityExplorer' });
|
|
1264
|
-
}
|
|
1265
|
-
});
|
|
1266
|
-
EventBus.on('connectivity-filter-options', (payload) => {
|
|
1267
|
-
this.filterOptions = payload;
|
|
1310
|
+
// Restore sidebar state if it exists and not restored yet
|
|
1311
|
+
// after loading connectivity knowledge
|
|
1312
|
+
if (!this.sidebarStateRestored) {
|
|
1313
|
+
this.restoreSidebarState(this.state);
|
|
1314
|
+
}
|
|
1315
|
+
},
|
|
1316
|
+
modeUpdate: (payload) => {
|
|
1317
|
+
if (payload === 'dataset') {
|
|
1318
|
+
this.$refs.sideBar.tabClicked({ id: 1, type: 'datasetExplorer' });
|
|
1319
|
+
} else if (payload === 'connectivity') {
|
|
1320
|
+
this.$refs.sideBar.tabClicked({ id: 2, type: 'connectivityExplorer' });
|
|
1321
|
+
}
|
|
1322
|
+
},
|
|
1323
|
+
'connectivity-filter-options': (payload) => {
|
|
1324
|
+
this.filterOptions = payload;
|
|
1325
|
+
},
|
|
1268
1326
|
});
|
|
1269
1327
|
this.$nextTick(() => {
|
|
1270
1328
|
if (this.search === '' && this._facets.length === 0) {
|
|
1271
1329
|
if (this.$refs.sideBar) {
|
|
1272
1330
|
this.$refs.sideBar.close();
|
|
1273
1331
|
}
|
|
1274
|
-
setTimeout(() => {
|
|
1332
|
+
this._startUpTimer = setTimeout(() => {
|
|
1275
1333
|
this.startUp = false;
|
|
1276
1334
|
}, 2000);
|
|
1277
1335
|
} else this.openSearch(this._facets, this.search);
|
|
1278
1336
|
});
|
|
1279
1337
|
},
|
|
1338
|
+
beforeUnmount: function () {
|
|
1339
|
+
this._offBus?.();
|
|
1340
|
+
clearTimeout(this._startUpTimer);
|
|
1341
|
+
},
|
|
1280
1342
|
computed: {
|
|
1281
1343
|
...mapStores(useEntriesStore, useSettingsStore, useSplitFlowStore, useConnectivitiesStore),
|
|
1282
1344
|
envVars: function () {
|
|
@@ -1,5 +1,46 @@
|
|
|
1
|
+
import { markRaw } from 'vue';
|
|
2
|
+
|
|
1
3
|
const defaultSpecies = 'Human Male';
|
|
2
4
|
|
|
5
|
+
// Session-level caches, so viewers and searches share one request instead of
|
|
6
|
+
// re-fetching the same data.
|
|
7
|
+
const flatmapListCache = new Map();
|
|
8
|
+
const flatmapPathwaysCache = new Map();
|
|
9
|
+
const bodyScaffoldInfoCache = new Map();
|
|
10
|
+
|
|
11
|
+
// Cache the promise so concurrent callers share a single request.
|
|
12
|
+
// Failed requests are dropped so a later call can retry.
|
|
13
|
+
const cachedRequest = (cache, key, load) => {
|
|
14
|
+
if (!cache.has(key)) {
|
|
15
|
+
const promise = load().catch((error) => {
|
|
16
|
+
cache.delete(key);
|
|
17
|
+
throw error;
|
|
18
|
+
});
|
|
19
|
+
cache.set(key, promise);
|
|
20
|
+
}
|
|
21
|
+
return cache.get(key);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const fetchJson = async (url) => {
|
|
25
|
+
const response = await fetch(url);
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw new Error(`Request failed (${response.status}): ${url}`);
|
|
28
|
+
}
|
|
29
|
+
return response.json();
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Latest version of the flatmap with the given id, e.g. 'human-flatmap_male'
|
|
33
|
+
const getLatestFlatmap = async (flatmapAPI, id) => {
|
|
34
|
+
const flatmaps = await cachedRequest(flatmapListCache, flatmapAPI, () => fetchJson(flatmapAPI));
|
|
35
|
+
return flatmaps.filter((f) => f.id === id).sort((a, b) => b.created.localeCompare(a.created))[0];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Pathways are large and read-only, so they are shared and kept non-reactive
|
|
39
|
+
const getFlatmapPathways = (flatmapAPI, uuid) => {
|
|
40
|
+
const url = `${flatmapAPI}/flatmap/${uuid}/pathways`;
|
|
41
|
+
return cachedRequest(flatmapPathwaysCache, url, () => fetchJson(url).then(markRaw));
|
|
42
|
+
};
|
|
43
|
+
|
|
3
44
|
const initialDefaultState = () => {
|
|
4
45
|
return {
|
|
5
46
|
entries: [
|
|
@@ -174,7 +215,19 @@ const extractS3BucketNameAndPrefix = (uri) => {
|
|
|
174
215
|
return undefined;
|
|
175
216
|
};
|
|
176
217
|
|
|
177
|
-
const getBodyScaffoldInfo =
|
|
218
|
+
const getBodyScaffoldInfo = (sparcApi, species) => {
|
|
219
|
+
const key = `${sparcApi}|${species}`;
|
|
220
|
+
return cachedRequest(bodyScaffoldInfoCache, key, async () => {
|
|
221
|
+
const info = await fetchBodyScaffoldInfo(sparcApi, species);
|
|
222
|
+
// Do not keep the fallback url, so a later call can retry the API
|
|
223
|
+
if (!info.datasetInfo) {
|
|
224
|
+
bodyScaffoldInfoCache.delete(key);
|
|
225
|
+
}
|
|
226
|
+
return info;
|
|
227
|
+
});
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const fetchBodyScaffoldInfo = async (sparcApi, species) => {
|
|
178
231
|
//Get body scaffold information
|
|
179
232
|
let url = '';
|
|
180
233
|
let datasetInfo = undefined;
|
|
@@ -243,6 +296,8 @@ export {
|
|
|
243
296
|
initialState,
|
|
244
297
|
initialDefaultState,
|
|
245
298
|
getBodyScaffoldInfo,
|
|
299
|
+
getLatestFlatmap,
|
|
300
|
+
getFlatmapPathways,
|
|
246
301
|
getNewMapEntry,
|
|
247
302
|
intersectArrays,
|
|
248
303
|
transformObjToString,
|
|
@@ -100,6 +100,7 @@ export default {
|
|
|
100
100
|
},
|
|
101
101
|
flatmapReadyCall: function (flatmap) {
|
|
102
102
|
this.flatmapReady = true;
|
|
103
|
+
this.$emit('viewer-ready', true);
|
|
103
104
|
const mapImp = this.getFlatmapImp();
|
|
104
105
|
if (mapImp?.mapMetadata?.name) {
|
|
105
106
|
this.updateEntryLabel(mapImp?.mapMetadata?.name);
|
|
@@ -228,6 +228,8 @@ export default {
|
|
|
228
228
|
flatmapChanged: async function (activeSpecies) {
|
|
229
229
|
this.activeSpecies = activeSpecies;
|
|
230
230
|
this.openMapOptions = getOpenMapOptions(activeSpecies);
|
|
231
|
+
// The selected species' map may still be loading, multiFlatmapReady will set it ready
|
|
232
|
+
this.$emit('viewer-ready', !!this.$refs.multiflatmap?.getCurrentFlatmap()?.mapImp);
|
|
231
233
|
const flatmapImp = this.getFlatmapImp();
|
|
232
234
|
this.updateProvCard();
|
|
233
235
|
//If the viewer is loading a new map, flatmapImp is not defined here yet.
|
|
@@ -254,6 +256,7 @@ export default {
|
|
|
254
256
|
if (flatmap) {
|
|
255
257
|
flatmap.enablePanZoomEvents(true); // Use zoom events for dynamic markers
|
|
256
258
|
this.flatmapReady = true;
|
|
259
|
+
this.$emit('viewer-ready', true);
|
|
257
260
|
const flatmapImp = flatmap.mapImp;
|
|
258
261
|
this.flatmapMarkerUpdate(flatmapImp);
|
|
259
262
|
this.updateProvCard();
|