@abi-software/mapintegratedvuer 1.22.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.
Files changed (39) hide show
  1. package/dist/{ConnectivityGraph-BBZ3Dgat.js → ConnectivityGraph-CCxk8vcE.js} +6 -6
  2. package/dist/{ContentMixin-B-O_IwPg.js → ContentMixin-Db8nqZax.js} +303 -247
  3. package/dist/{DynamicMarkerMixin-B9iqtP_f.js → DynamicMarkerMixin-LB2RYdSY.js} +1 -1
  4. package/dist/{Flatmap-BoEFI50b.js → Flatmap-CqUzOkdl.js} +5 -5
  5. package/dist/{Iframe-Slj-LQTF.js → Iframe-I-xdZ2FP.js} +2 -2
  6. package/dist/{MultiFlatmap-l6aBn6x3.js → MultiFlatmap-11L97ycJ.js} +6 -6
  7. package/dist/{Plot-CIhEip5b.js → Plot-Bd9RqyVD.js} +2 -2
  8. package/dist/{Scaffold-D5ZGCDvt.js → Scaffold-CySWXQxI.js} +46 -36
  9. package/dist/Simulation-ChUbGudZ.js +32 -0
  10. package/dist/{connectivities-Br3MpoAv.js → connectivities-LtlFwLOp.js} +6399 -5790
  11. package/dist/es-D-YkSGXX.js +436 -0
  12. package/dist/mapintegratedvuer.js +13061 -11664
  13. package/dist/mapintegratedvuer.umd.cjs +191 -190
  14. package/dist/style.css +1 -1
  15. package/package.json +7 -7
  16. package/src/assets/header-icon.scss +9 -3
  17. package/src/components/ContentBar.vue +172 -92
  18. package/src/components/ContentVuer.vue +41 -1
  19. package/src/components/ContextCard.vue +15 -3
  20. package/src/components/DatasetHeader.vue +4 -1
  21. package/src/components/DialogToolbarContent.vue +45 -4
  22. package/src/components/EventBus.js +8 -0
  23. package/src/components/MapContent.vue +85 -31
  24. package/src/components/ResizeSensor.vue +7 -9
  25. package/src/components/ScreenshotPopover.vue +526 -0
  26. package/src/components/SplitDialog.vue +94 -93
  27. package/src/components/SplitFlow.vue +158 -96
  28. package/src/components/scripts/utilities.js +56 -1
  29. package/src/components/viewers/Flatmap.vue +1 -0
  30. package/src/components/viewers/MultiFlatmap.vue +3 -0
  31. package/src/components/viewers/Scaffold.vue +25 -13
  32. package/src/components/viewers/Simulation.vue +23 -1
  33. package/src/components.d.ts +2 -0
  34. package/src/mixins/ContentMixin.js +23 -10
  35. package/src/services/screenshot.js +1186 -0
  36. package/src/stores/connectivities.js +38 -5
  37. package/src/stores/settings.js +18 -0
  38. package/vite.config.js +27 -4
  39. package/dist/Simulation-C5o5DXRb.js +0 -25
@@ -0,0 +1,526 @@
1
+ <template>
2
+ <div class="screenshot-popover-container" data-screenshot-ignore>
3
+ <el-popover
4
+ v-if="triggerRef"
5
+ ref="popover"
6
+ :virtual-ref="triggerRef"
7
+ :placement="placement"
8
+ width="280"
9
+ :teleported="false"
10
+ trigger="click"
11
+ popper-class="screenshot-popover"
12
+ virtual-triggering
13
+ :disabled="disabled || capturing"
14
+ @show="onShow"
15
+ >
16
+ <div class="screenshot-popover-inner">
17
+ <h4>{{ title }}</h4>
18
+ <div class="screenshot-popover-block">
19
+ <h5>Resolution</h5>
20
+ <el-radio-group v-model="scale" size="small">
21
+ <el-radio-button v-for="item in scales" :key="item" :value="item">
22
+ {{ item }}x
23
+ </el-radio-button>
24
+ </el-radio-group>
25
+ </div>
26
+ <div v-if="hasLegend" class="screenshot-popover-block">
27
+ <h5>Legend</h5>
28
+ <el-radio-group v-model="legend" size="small">
29
+ <el-radio-button v-for="item in legendModes" :key="item.value" :value="item.value">
30
+ {{ item.label }}
31
+ </el-radio-button>
32
+ </el-radio-group>
33
+ </div>
34
+ <div v-if="getLegendMode() !== 'only'" class="screenshot-popover-block">
35
+ <h5>Header</h5>
36
+ <el-radio-group v-model="toolbar" size="small">
37
+ <el-radio-button :value="false">Hide</el-radio-button>
38
+ <el-radio-button :value="true">Include</el-radio-button>
39
+ </el-radio-group>
40
+ </div>
41
+ <div v-if="shortcut" class="screenshot-popover-block is-stacked">
42
+ <h5>Shortcut ({{ shortcutLabel }})</h5>
43
+ <small class="screenshot-description">
44
+ To capture a tooltip on Flatmap, choose your settings here, then hover over the feature
45
+ or connection in the Flatmap and press the shortcut.
46
+ </small>
47
+ </div>
48
+ <div class="screenshot-popover-footer">
49
+ <div class="screenshot-info">
50
+ <template v-if="outputSize.width">
51
+ <template v-if="outputSize.count > 1">
52
+ {{ outputSize.count }} PNG files, up to
53
+ </template>
54
+ <template v-else>PNG,</template>
55
+ {{ outputSize.width }} × {{ outputSize.height }} px
56
+ </template>
57
+ <div v-if="outputSize.clamped" class="screenshot-warning">
58
+ Reduced to fit browser limits.
59
+ </div>
60
+ <div v-if="errorMessage" class="screenshot-warning">
61
+ {{ errorMessage }}
62
+ </div>
63
+ </div>
64
+ <el-button
65
+ type="primary"
66
+ size="small"
67
+ class="screenshot-download"
68
+ :icon="ElIconDownload"
69
+ :loading="capturing"
70
+ @click="onCapture()"
71
+ >
72
+ Download
73
+ </el-button>
74
+ </div>
75
+ </div>
76
+ </el-popover>
77
+ <el-popover
78
+ class="tooltip"
79
+ :content="tooltipText"
80
+ :placement="placement"
81
+ :show-after="helpDelay"
82
+ :teleported="false"
83
+ trigger="hover"
84
+ popper-class="header-popper"
85
+ :disabled="disabled"
86
+ >
87
+ <template #reference>
88
+ <el-icon
89
+ ref="triggerRef"
90
+ class="header-icon screenshot-icon"
91
+ :class="{ disabled: disabled, 'is-capturing': capturing }"
92
+ >
93
+ <el-icon-camera />
94
+ </el-icon>
95
+ </template>
96
+ </el-popover>
97
+ </div>
98
+ </template>
99
+
100
+ <script>
101
+ import { shallowRef } from 'vue';
102
+ import { mapStores } from 'pinia';
103
+ import { Camera as ElIconCamera, Download as ElIconDownload } from '@element-plus/icons-vue';
104
+ import { ElMessage, ElMessageBox } from 'element-plus';
105
+ import 'element-plus/es/components/message/style/css';
106
+ import 'element-plus/es/components/message-box/style/css';
107
+ import { useSettingsStore } from '../stores/settings';
108
+ import {
109
+ SCREENSHOT_SCALES,
110
+ SCREENSHOT_LEGEND_MODES,
111
+ findLegends,
112
+ getOutputSize,
113
+ } from '../services/screenshot';
114
+
115
+ // Safari and every iOS browser use WebKit, which has lower canvas memory limits.
116
+ const isWebKit = () => navigator.vendor === 'Apple Computer, Inc.';
117
+
118
+ /**
119
+ * Explain a failed capture and what the user can do about it.
120
+ * @param {Error} error - usually a `ScreenshotError` from the screenshot service.
121
+ * @param {Number} scale - the requested resolution.
122
+ * @returns {Object} { title, message, inline } where `inline` is the short
123
+ * version shown in the popover.
124
+ */
125
+ const describeError = (error, scale) => {
126
+ if (error?.code === 'no-legend') {
127
+ return { inline: 'There is no legend to capture.' };
128
+ }
129
+ const otherBrowser = isWebKit()
130
+ ? ' Safari has lower limits for large images, so another browser such as Chrome or Firefox may work better.'
131
+ : '';
132
+ if (error?.code === 'context-lost') {
133
+ return {
134
+ title: 'Screenshot failed',
135
+ message:
136
+ 'The viewer ran out of graphics memory while creating the screenshot ' +
137
+ 'and cannot be drawn again until the page is reloaded. ' +
138
+ 'Please reload the page, then try a lower resolution.' +
139
+ otherBrowser,
140
+ inline: 'Screenshot failed. Please reload the page and try a lower resolution.',
141
+ };
142
+ }
143
+ const lower =
144
+ scale > 1
145
+ ? `This view is too large or complex to capture at ${scale}x in this browser. ` +
146
+ 'Please choose a lower resolution and download again.'
147
+ : 'This view is too large or complex to capture in this browser. ' +
148
+ 'Please make the window or view smaller, or reload the page and try again.';
149
+ return {
150
+ title: 'Screenshot failed',
151
+ message: lower + otherBrowser,
152
+ inline: 'Screenshot failed. Please try a lower resolution.',
153
+ };
154
+ };
155
+
156
+ const LEGEND_LABELS = {
157
+ exclude: 'Hide',
158
+ include: 'Include',
159
+ only: 'Only',
160
+ };
161
+
162
+ /**
163
+ * Camera button with a popover to choose the resolution
164
+ * before capturing a screenshot.
165
+ */
166
+ export default {
167
+ name: 'ScreenshotPopover',
168
+ components: {
169
+ ElIconCamera,
170
+ },
171
+ props: {
172
+ /**
173
+ * Async function called with `{ scale, legend, toolbar, source }` to perform the capture.
174
+ */
175
+ capture: {
176
+ type: Function,
177
+ required: true,
178
+ },
179
+ /**
180
+ * Function returning the element to be captured, used for size preview
181
+ * and to check whether it has a legend.
182
+ * Called with `{ legend, toolbar }` as the element may depend on them.
183
+ */
184
+ getTarget: {
185
+ type: Function,
186
+ default: undefined,
187
+ },
188
+ title: {
189
+ type: String,
190
+ default: 'Download screenshot',
191
+ },
192
+ tooltip: {
193
+ type: String,
194
+ default: 'Download screenshot',
195
+ },
196
+ placement: {
197
+ type: String,
198
+ default: 'bottom-end',
199
+ },
200
+ disabled: {
201
+ type: Boolean,
202
+ default: false,
203
+ },
204
+ /**
205
+ * Enable the Alt/Option + Shift + S keyboard shortcut,
206
+ * which captures with the last used options.
207
+ */
208
+ shortcut: {
209
+ type: Boolean,
210
+ default: false,
211
+ },
212
+ },
213
+ data: function () {
214
+ return {
215
+ triggerRef: undefined,
216
+ ElIconDownload: shallowRef(ElIconDownload),
217
+ scales: SCREENSHOT_SCALES,
218
+ scale: 1,
219
+ legendModes: SCREENSHOT_LEGEND_MODES.map((value) => ({ value, label: LEGEND_LABELS[value] })),
220
+ legend: SCREENSHOT_LEGEND_MODES[0],
221
+ toolbar: false,
222
+ hasLegend: false,
223
+ capturing: false,
224
+ errorMessage: '',
225
+ outputSize: { width: 0, height: 0, clamped: false, count: 0 },
226
+ };
227
+ },
228
+ computed: {
229
+ ...mapStores(useSettingsStore),
230
+ helpDelay() {
231
+ return this.settingsStore.helpDelay;
232
+ },
233
+ shortcutLabel() {
234
+ const platform = navigator.userAgentData?.platform || navigator.platform || '';
235
+ return /mac/i.test(platform) ? '⌥⇧S' : 'Alt+Shift+S';
236
+ },
237
+ tooltipText() {
238
+ if (this.capturing) {
239
+ return 'Capturing screenshot…';
240
+ }
241
+ return this.shortcut ? `${this.tooltip} (${this.shortcutLabel})` : this.tooltip;
242
+ },
243
+ },
244
+ watch: {
245
+ scale: function (value) {
246
+ // Remember the selection so reopening and the shortcut reuse it
247
+ this.settingsStore.updateScreenshotOptions({ scale: value });
248
+ this.updateOutputSize();
249
+ },
250
+ legend: function (value) {
251
+ this.settingsStore.updateScreenshotOptions({ legend: value });
252
+ this.updateOutputSize();
253
+ },
254
+ toolbar: function (value) {
255
+ this.settingsStore.updateScreenshotOptions({ toolbar: value });
256
+ this.updateOutputSize();
257
+ },
258
+ },
259
+ methods: {
260
+ loadSavedOptions: function () {
261
+ const { scale, legend, toolbar } = this.settingsStore.screenshot;
262
+ this.scale = SCREENSHOT_SCALES.includes(scale) ? scale : SCREENSHOT_SCALES[0];
263
+ this.legend = SCREENSHOT_LEGEND_MODES.includes(legend) ? legend : SCREENSHOT_LEGEND_MODES[0];
264
+ this.toolbar = toolbar === true;
265
+ this.hasLegend = findLegends(this.getTarget?.({ legend: 'only' })).length > 0;
266
+ },
267
+ /**
268
+ * The legend option only applies when the target has a legend.
269
+ */
270
+ getLegendMode: function () {
271
+ return this.hasLegend ? this.legend : 'exclude';
272
+ },
273
+ getOptions: function () {
274
+ return { scale: this.scale, legend: this.getLegendMode(), toolbar: this.toolbar };
275
+ },
276
+ onShow: function () {
277
+ this.loadSavedOptions();
278
+ this.updateOutputSize();
279
+ },
280
+ onKeydown: function (event) {
281
+ // Match on `code`, as Option changes `key` on macOS
282
+ const isShortcut =
283
+ event.altKey && event.shiftKey && !event.ctrlKey && !event.metaKey && event.code === 'KeyS';
284
+ if (!isShortcut || event.repeat || this.disabled || this.capturing) {
285
+ return;
286
+ }
287
+ if (event.target?.closest?.('input, textarea, select, [contenteditable]')) {
288
+ return;
289
+ }
290
+ event.preventDefault();
291
+ this.loadSavedOptions();
292
+ this.onCapture('shortcut');
293
+ },
294
+ updateOutputSize: function () {
295
+ const { scale, legend, toolbar } = this.getOptions();
296
+ const target = this.getTarget?.({ legend, toolbar });
297
+ this.outputSize = getOutputSize(target, scale, legend, toolbar);
298
+ },
299
+ onCapture: async function (source = 'button') {
300
+ const options = this.getOptions();
301
+ let failure;
302
+ this.capturing = true;
303
+ this.errorMessage = '';
304
+ try {
305
+ this.$refs.popover?.hide();
306
+ const result = await this.capture({ ...options, source });
307
+ this.notifyReduced(result);
308
+ } catch (error) {
309
+ console.error('Screenshot failed', error);
310
+ failure = describeError(error, options.scale);
311
+ this.errorMessage = failure.inline;
312
+ } finally {
313
+ this.capturing = false;
314
+ }
315
+ if (failure?.message) {
316
+ await ElMessageBox.alert(failure.message, failure.title, {
317
+ type: 'error',
318
+ confirmButtonText: 'OK',
319
+ customClass: 'screenshot-failure-message-box',
320
+ }).catch(() => {});
321
+ }
322
+ if (failure) {
323
+ // Reopen after `capturing` resets, as the popover is disabled while capturing
324
+ await this.$nextTick();
325
+ this.$refs.popover?.show?.();
326
+ }
327
+ },
328
+ /**
329
+ * Tell the user when the image was saved smaller than the preview,
330
+ * because the browser could not create it at the requested size.
331
+ */
332
+ notifyReduced: function (result) {
333
+ const results = [result].flat().filter(Boolean);
334
+ const reduced = results.find((item) => item.reduced);
335
+ if (!reduced) return;
336
+ ElMessage({
337
+ type: 'warning',
338
+ duration: 8000,
339
+ showClose: true,
340
+ message:
341
+ `The screenshot was too large for this browser and was saved at ` +
342
+ `${reduced.width} × ${reduced.height} px instead. ` +
343
+ `Choose a lower resolution to avoid the wait next time.`,
344
+ });
345
+ },
346
+ },
347
+ mounted: function () {
348
+ this.triggerRef = shallowRef(this.$refs.triggerRef);
349
+ if (this.shortcut) {
350
+ document.addEventListener('keydown', this.onKeydown);
351
+ }
352
+ },
353
+ beforeUnmount: function () {
354
+ document.removeEventListener('keydown', this.onKeydown);
355
+ },
356
+ };
357
+ </script>
358
+
359
+ <style scoped lang="scss">
360
+ @use '../assets/header-icon.scss';
361
+
362
+ .screenshot-popover-container {
363
+ display: flex;
364
+ align-items: center;
365
+ }
366
+
367
+ .screenshot-icon {
368
+ padding: 3px;
369
+ box-sizing: border-box;
370
+
371
+ &.is-capturing {
372
+ position: relative;
373
+ overflow: visible;
374
+ cursor: progress;
375
+
376
+ &::after {
377
+ content: '';
378
+ position: absolute;
379
+ inset: -3px;
380
+ border: 2px solid rgba($app-primary-color, 0.2);
381
+ border-top-color: $app-primary-color;
382
+ border-radius: 50%;
383
+ animation: screenshot-spin 0.8s linear infinite;
384
+ pointer-events: none;
385
+ }
386
+ }
387
+ }
388
+
389
+ @keyframes screenshot-spin {
390
+ to {
391
+ transform: rotate(360deg);
392
+ }
393
+ }
394
+
395
+ .screenshot-popover-inner {
396
+ padding: 0.5rem 0.75rem;
397
+ max-height: calc(100vh - 135px);
398
+ overflow-y: auto;
399
+ border-radius: var(--el-popover-border-radius);
400
+ scrollbar-width: thin;
401
+ display: flex;
402
+ flex-direction: column;
403
+ gap: 0.5rem;
404
+
405
+ > h4 {
406
+ margin: 0;
407
+ padding: 0;
408
+ font-size: 16px;
409
+ color: $app-primary-color;
410
+ text-align: center;
411
+ }
412
+ }
413
+
414
+ .screenshot-popover-block {
415
+ background-color: rgba(0, 0, 0, 0.05);
416
+ padding: 0.5rem 0.75rem;
417
+ border-radius: 4px;
418
+ width: 100%;
419
+ box-sizing: border-box;
420
+ display: flex;
421
+ align-items: center;
422
+ justify-content: space-between;
423
+ gap: 0.5rem;
424
+
425
+ h5 {
426
+ margin: 0;
427
+ padding: 0;
428
+ font-size: 14px;
429
+ font-weight: 500;
430
+ line-height: 32px;
431
+ color: #303133;
432
+ }
433
+
434
+ &.is-stacked {
435
+ flex-direction: column;
436
+ align-items: flex-start;
437
+ gap: 0;
438
+ }
439
+ }
440
+
441
+ .screenshot-description {
442
+ display: inline-block;
443
+ font-size: 12px;
444
+ white-space: normal;
445
+ line-height: 1.2;
446
+ font-weight: normal;
447
+ font-style: italic;
448
+ color: gray;
449
+ }
450
+
451
+ .screenshot-popover-footer {
452
+ display: flex;
453
+ align-items: flex-end;
454
+ justify-content: space-between;
455
+ gap: 0.5rem;
456
+ }
457
+
458
+ .screenshot-info {
459
+ font-size: 12px;
460
+ line-height: 1.2;
461
+ font-style: italic;
462
+ color: gray;
463
+ }
464
+
465
+ .screenshot-warning {
466
+ color: #e6a23c;
467
+ }
468
+
469
+ .screenshot-download.el-button {
470
+ flex-shrink: 0;
471
+ margin-left: auto;
472
+ font-family: inherit;
473
+
474
+ &:hover {
475
+ background-color: $app-primary-color;
476
+ border-color: $app-primary-color;
477
+ }
478
+ }
479
+
480
+ :deep(.screenshot-popover.el-popper) {
481
+ border: 1px solid $app-primary-color;
482
+ box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.06);
483
+ padding: 1px !important;
484
+ background-color: #f3ecf6;
485
+ cursor: default;
486
+
487
+ .el-popper__arrow:before {
488
+ border-color: $app-primary-color;
489
+ background-color: #f3ecf6;
490
+ }
491
+ }
492
+
493
+ :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) {
494
+ background-color: $app-primary-color;
495
+ border-color: $app-primary-color;
496
+ box-shadow: -1px 0 0 0 $app-primary-color;
497
+ }
498
+
499
+ :global(.screenshot-failure-message-box) {
500
+ --el-color-primary: #{$app-primary-color};
501
+ font-family: 'Asap', sans-serif;
502
+ border: 1px solid $app-primary-color;
503
+ border-radius: 8px;
504
+ }
505
+
506
+ :global(.screenshot-failure-message-box .el-message-box__title) {
507
+ color: $app-primary-color;
508
+ font-family: 'Asap', sans-serif;
509
+ }
510
+
511
+ :global(.screenshot-failure-message-box .el-message-box__message) {
512
+ color: #303133;
513
+ font-family: 'Asap', sans-serif;
514
+ }
515
+
516
+ :global(.screenshot-failure-message-box .el-button) {
517
+ font-family: 'Asap', sans-serif;
518
+ }
519
+
520
+ :global(.screenshot-failure-message-box .el-button--primary),
521
+ :global(.screenshot-failure-message-box .el-button--primary:hover),
522
+ :global(.screenshot-failure-message-box .el-button--primary:focus) {
523
+ background-color: $app-primary-color;
524
+ border-color: $app-primary-color;
525
+ }
526
+ </style>