@abi-software/flatmapvuer 1.1.0-beta.2 → 1.1.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.
@@ -0,0 +1,360 @@
1
+ <template>
2
+ <div class="help-mode-dialog" :class="{'finish': lastItem}">
3
+ <h4>Help Mode</h4>
4
+
5
+ <template v-if="lastItem">
6
+ <p>
7
+ All caught up! <br>
8
+ Click 'Help' to restart.
9
+ </p>
10
+ <div>
11
+ <el-button class="button" @click="finishHelpMode">
12
+ Finish
13
+ </el-button>
14
+ </div>
15
+ </template>
16
+ <template v-else>
17
+ <p>Click "Next" to see the next item.</p>
18
+ <div>
19
+ <el-button class="button" @click="showNext">
20
+ Next
21
+ </el-button>
22
+ <el-button class="button secondary" @click="finishHelpMode">
23
+ Exit Help Mode
24
+ </el-button>
25
+ </div>
26
+ </template>
27
+ </div>
28
+ </template>
29
+
30
+ <script>
31
+ import {
32
+ ElButton as Button,
33
+ } from 'element-plus';
34
+
35
+ export default {
36
+ name: 'HelpModeDialog',
37
+ components: {
38
+ Button,
39
+ },
40
+ props: {
41
+ multiflatmapRef: {
42
+ type: Object,
43
+ default: null,
44
+ },
45
+ flatmapRef: {
46
+ type: Object,
47
+ default: null,
48
+ },
49
+ scaffoldRef: {
50
+ type: Object,
51
+ default: null,
52
+ },
53
+ lastItem: {
54
+ type: Boolean,
55
+ default: false,
56
+ required: false,
57
+ }
58
+ },
59
+ mounted: function () {
60
+ this.toggleHelpModeHighlight(true);
61
+ // For tooltips outside of Flatmap
62
+ this.toggleTooltipHighlight();
63
+ },
64
+ unmounted: function () {
65
+ this.toggleHelpModeHighlight(false);
66
+ },
67
+ watch: {
68
+ lastItem: function (isLastItem) {
69
+ if (isLastItem) {
70
+ this.toggleTooltipHighlight();
71
+ }
72
+ }
73
+ },
74
+ methods: {
75
+ showNext: function () {
76
+ this.$emit('show-next');
77
+ },
78
+ finishHelpMode: function () {
79
+ this.$emit('finish-help-mode');
80
+ },
81
+ /**
82
+ * This function must be called on 'shown-map-tooltip' event.
83
+ */
84
+ toggleTooltipPinHighlight: function () {
85
+ const currentFlatmapEl = this.getCurrentFlatmap();
86
+ this.resetHighlightedItems();
87
+
88
+ this.$nextTick(() => {
89
+ // Temporary solution to find the position of map marker from popover
90
+ const mapPins = currentFlatmapEl.querySelectorAll('.maplibregl-marker');
91
+ const mapPinPopover = currentFlatmapEl.querySelector('.flatmap-popup-popper');
92
+ const styleVal = mapPinPopover?.style?.transform || '';
93
+ const mapPopoverPosition = this.extractMarkerPosition(styleVal);
94
+
95
+ mapPins.forEach((mapPin) => {
96
+ const mapPinStyleVal = mapPin.style.transform;
97
+ const mapPinPosition = this.extractMarkerPosition(mapPinStyleVal);
98
+
99
+ if (mapPinPosition === mapPopoverPosition) {
100
+ mapPin.classList.add('in-help-highlight');
101
+ }
102
+ });
103
+ });
104
+ },
105
+ /**
106
+ * This function must be called on 'shown-tooltip' event.
107
+ */
108
+ toggleTooltipHighlight: function () {
109
+ this.resetHighlightedItems();
110
+
111
+ this.$nextTick(() => {
112
+ const activePoppers = document.querySelectorAll('.el-popper:not([style*="none"])');
113
+
114
+ activePoppers.forEach((activePopper) => {
115
+ const multiFlatmapTooltip = activePopper.classList.contains('flatmap-popper');
116
+ const flatmapTooltip = activePopper.classList.contains('el-fade-in-linear-enter-active');
117
+
118
+ if (multiFlatmapTooltip || flatmapTooltip) {
119
+ this.toggleHighlight(activePopper);
120
+ }
121
+ });
122
+ });
123
+ },
124
+ toggleHighlight: function (activePopper) {
125
+ const popperId = activePopper?.id || '';
126
+ const popperTrigger = document.querySelector(`[aria-describedby="${popperId}"]`);
127
+
128
+ if (popperTrigger) {
129
+ popperTrigger.classList.add('in-help-highlight');
130
+ }
131
+ },
132
+ resetHighlightedItems: function () {
133
+ const allHighlightedItems = document.querySelectorAll('.in-help-highlight');
134
+ allHighlightedItems.forEach((el) => {
135
+ el.classList.remove('in-help-highlight');
136
+ });
137
+ },
138
+ getCurrentScaffold: function () {
139
+ const scaffoldEl = this.scaffoldRef?.$el || null;
140
+ return scaffoldEl;
141
+ },
142
+ getCurrentMultiflatmap: function () {
143
+ const multiflatmapEl = this.multiflatmapRef?.$el || null;
144
+ return multiflatmapEl;
145
+ },
146
+ getCurrentFlatmap: function () {
147
+ const flatmap = this.flatmapRef || this.multiflatmapRef?.getCurrentFlatmap();
148
+ const flatmapEl = flatmap?.$el || null;
149
+ return flatmapEl;
150
+ },
151
+ toggleHelpModeHighlight: function (option) {
152
+ const currentMultiflatmapEl = this.getCurrentMultiflatmap();
153
+ const currentFlatmapEl = this.getCurrentFlatmap();
154
+ const currentScaffoldEl = this.getCurrentScaffold();
155
+ const allHighlightedItems = document.querySelectorAll('.in-help-highlight');
156
+
157
+ if (currentMultiflatmapEl) {
158
+ if (option) {
159
+ currentMultiflatmapEl.classList.add('in-help');
160
+ } else {
161
+ currentMultiflatmapEl.classList.remove('in-help');
162
+ }
163
+ }
164
+
165
+ if (currentFlatmapEl) {
166
+ if (option) {
167
+ currentFlatmapEl.classList.add('in-help');
168
+ } else {
169
+ currentFlatmapEl.classList.remove('in-help');
170
+ }
171
+ }
172
+
173
+ if (currentScaffoldEl) {
174
+ if (option) {
175
+ currentScaffoldEl.classList.add('in-help');
176
+ } else {
177
+ currentScaffoldEl.classList.remove('in-help');
178
+ }
179
+ }
180
+
181
+ if (!option) {
182
+ allHighlightedItems.forEach((el) => {
183
+ el.classList.remove('in-help-highlight');
184
+ });
185
+ }
186
+ },
187
+ /**
188
+ * Temporary solution to find the position of map marker from popover
189
+ */
190
+ extractMarkerPosition: function (str) {
191
+ const translateRegex = /translate\((.*?)\)/g;
192
+ const matches = str.match(translateRegex);
193
+ if (!matches) {
194
+ return '';
195
+ }
196
+ const lastMatch = matches[matches.length - 1];
197
+ const values = lastMatch.slice(10, -1);
198
+ return values;
199
+ },
200
+ }
201
+ }
202
+ </script>
203
+
204
+ <style lang="scss" scoped>
205
+ .help-mode-dialog {
206
+ display: flex;
207
+ flex-direction: column;
208
+ align-items: center;
209
+ justify-content: center;
210
+ gap: 1rem;
211
+ width: 300px;
212
+ padding: 1rem;
213
+ font-family: inherit;
214
+ font-size: 14px;
215
+ background: white;
216
+ border-radius: 4px 4px;
217
+ border: 1px solid $app-primary-color;
218
+ box-shadow: 0px 0px 160px 80px rgba(0,0,0,0.5);
219
+ position: absolute;
220
+ top: 0;
221
+ left: 50%;
222
+ transform: translateX(-50%);
223
+ z-index: 2;
224
+
225
+ &.finish {
226
+ animation: shake 0.35s;
227
+ animation-delay: 0.7s;
228
+ }
229
+
230
+ h4 {
231
+ color: $app-primary-color;
232
+ }
233
+
234
+ h4, p {
235
+ margin: 0;
236
+ }
237
+
238
+ p {
239
+ line-height: 1.5;
240
+ }
241
+
242
+ .button {
243
+ color: #fff;
244
+ background-color: $app-primary-color;
245
+ transform: scale(1);
246
+ transform-origin: 50% 50%;
247
+ transition: all var(--el-transition-duration);
248
+
249
+ &:hover {
250
+ color: #fff !important;
251
+ background: #ac76c5 !important;
252
+ border: 1px solid #ac76c5 !important;
253
+ }
254
+
255
+ &:active {
256
+ transform: scale(0.95);
257
+ }
258
+
259
+ &.secondary {
260
+ background: #f3e6f9;
261
+ border-color: $app-primary-color;
262
+ color: $app-primary-color;
263
+
264
+ &:hover {
265
+ color: $app-primary-color !important;
266
+ background: #fff !important;
267
+ }
268
+ }
269
+ }
270
+ }
271
+
272
+ @keyframes shake {
273
+ 0% {
274
+ transform: translateX(-50%) rotate(2deg);
275
+ }
276
+ 25% {
277
+ transform: translateX(-50%) rotate(-2deg);
278
+ }
279
+ 50% {
280
+ transform: translateX(-50%) rotate(2deg);
281
+ }
282
+ 75% {
283
+ transform: translateX(-50%) rotate(-2deg);
284
+ }
285
+ 100% {
286
+ transform: translateX(-50%) rotate(2deg);
287
+ }
288
+ }
289
+
290
+ // Just for App.vue with options popover on top
291
+ .help-mode-dialog {
292
+ .options-popover + .multi-container + & {
293
+ margin-top: 40px;
294
+ }
295
+ .options-popover:not([style*="display: none"]) + .multi-container + & {
296
+ margin-top: 175px;
297
+ }
298
+ }
299
+ </style>
300
+
301
+ <style lang="scss">
302
+ .scaffold-container.in-help,
303
+ .flatmap-container.in-help {
304
+ background: rgba(0,0,0,0.3);
305
+ }
306
+
307
+ .scaffold-container.in-help {
308
+ canvas {
309
+ opacity: 0.3;
310
+ }
311
+ }
312
+
313
+ .scaffold-container.in-help,
314
+ .flatmap-container.in-help {
315
+ .el-tooltip__trigger,
316
+ .el-popover {
317
+ opacity: 0.3;
318
+ }
319
+
320
+ .pathway-location:has(.in-help-highlight) {
321
+ opacity: 1;
322
+
323
+ .pathway-container {
324
+ background: transparent;
325
+ }
326
+
327
+ .container,
328
+ .legends-container,
329
+ .selections-container {
330
+ opacity: 0.3;
331
+ }
332
+ }
333
+
334
+ .maplibregl-canvas,
335
+ .maplibregl-ctrl-minimap {
336
+ opacity: 0.3;
337
+ }
338
+
339
+ .maplibregl-map,
340
+ .maplibregl-canvas {
341
+ pointer-events: none;
342
+ }
343
+ }
344
+
345
+ .in-help .el-popper:not([style*="none"]) {
346
+ opacity: 1 !important;
347
+ }
348
+
349
+ .in-help-highlight {
350
+ opacity: 1 !important;
351
+ background: white !important;
352
+ box-shadow: 0px 0px 128px 128px white !important;
353
+ animation: highlight-area 0.1s;
354
+
355
+ &.maplibregl-marker {
356
+ background: none !important;
357
+ box-shadow: 0px 0px 128px 128px rgba(255,255,255,0.5) !important;
358
+ }
359
+ }
360
+ </style>
@@ -6,8 +6,9 @@
6
6
  content="Select a species"
7
7
  placement="right"
8
8
  trigger="manual"
9
- popper-class="flatmap-popper right-popper"
10
- :visible="helpMode"
9
+ popper-class="flatmap-popper flatmap-teleport-popper right-popper"
10
+ width="max-content"
11
+ :visible="activateTooltipByIndex(0)"
11
12
  :teleported="false"
12
13
  ref="selectPopover"
13
14
  >
@@ -62,8 +63,15 @@
62
63
  * @arg $event
63
64
  */
64
65
  $emit('open-map', $event)"
66
+ @pathway-selection-changed="onSelectionsDataChanged"
65
67
  :minZoom="minZoom"
66
- :helpMode="helpMode"
68
+ :helpMode="activeSpecies == key && helpMode"
69
+ :helpModeActiveItem="helpModeActiveItem"
70
+ :helpModeDialog="helpModeDialog"
71
+ :helpModeInitialIndex="-2"
72
+ @help-mode-last-item="onHelpModeLastItem"
73
+ @shown-tooltip="onTooltipShown"
74
+ @shown-map-tooltip="onMapTooltipShown"
67
75
  :renderAtMounted="renderAtMounted"
68
76
  :displayMinimap="displayMinimap"
69
77
  :showStarInLegend="showStarInLegend"
@@ -120,6 +128,15 @@ export default {
120
128
  EventBus.on('onActionClick', (action) => {
121
129
  this.resourceSelected(action)
122
130
  })
131
+ EventBus.on('open-pubmed-url', (url) => {
132
+ /**
133
+ * This event is emitted when the user clicks
134
+ * on "Open publications in pubmed" button
135
+ * from provenance popup.
136
+ * @arg url
137
+ */
138
+ this.$emit('open-pubmed-url', url);
139
+ });
123
140
  },
124
141
  methods: {
125
142
  /**
@@ -244,6 +261,9 @@ export default {
244
261
  */
245
262
  this.$emit('pan-zoom-callback', payload)
246
263
  },
264
+ onSelectionsDataChanged: function (data) {
265
+ this.$emit('pathway-selection-changed', data);
266
+ },
247
267
  /**
248
268
  * @vuese
249
269
  * Function to show popup on map.
@@ -449,6 +469,45 @@ export default {
449
469
  })
450
470
  }
451
471
  },
472
+ /**
473
+ * @vuese
474
+ * Function to activate help mode tooltip by item index number
475
+ */
476
+ activateTooltipByIndex: function (index) {
477
+ return (
478
+ index === this.helpModeActiveItem
479
+ && this.helpMode
480
+ );
481
+ },
482
+ /**
483
+ * @vuese
484
+ * Function to check the last item of help mode
485
+ */
486
+ onHelpModeLastItem: function (isLastItem) {
487
+ if (isLastItem) {
488
+ this.$emit('help-mode-last-item', true);
489
+ }
490
+ },
491
+ /**
492
+ * @vuese
493
+ * Function to emit event after a tooltip is shown.
494
+ */
495
+ onTooltipShown: function () {
496
+ /**
497
+ * This event is emitted after a tooltip in Flatmap is shown.
498
+ */
499
+ this.$emit('shown-tooltip');
500
+ },
501
+ /**
502
+ * @vuese
503
+ * Function to emit event after a tooltip on the map is shown.
504
+ */
505
+ onMapTooltipShown: function () {
506
+ /**
507
+ * This event is emitted after a tooltip on Flatmap's map is shown.
508
+ */
509
+ this.$emit('shown-map-tooltip');
510
+ },
452
511
  },
453
512
  props: {
454
513
  /**
@@ -480,6 +539,29 @@ export default {
480
539
  type: Boolean,
481
540
  default: false,
482
541
  },
542
+ /**
543
+ * The active item index of help mode.
544
+ */
545
+ helpModeActiveItem: {
546
+ type: Number,
547
+ default: 0,
548
+ },
549
+ /**
550
+ * The option to use helpModeDialog.
551
+ * On default, `false`, clicking help will show all tooltips.
552
+ * If `true`, clicking help will show the help-mode-dialog.
553
+ */
554
+ helpModeDialog: {
555
+ type: Boolean,
556
+ default: false,
557
+ },
558
+ /**
559
+ * The last item of help mode.
560
+ */
561
+ helpModeLastItem: {
562
+ type: Boolean,
563
+ default: false,
564
+ },
483
565
  /**
484
566
  * The option to display minimap at the top-right corner of the map.
485
567
  */
@@ -729,4 +811,4 @@ export default {
729
811
  --el-color-primary: #8300BF;
730
812
  }
731
813
 
732
- </style>
814
+ </style>
@@ -251,7 +251,7 @@ export default {
251
251
  }
252
252
  },
253
253
  provSpeciesDescription: function () {
254
- let text = 'Observed in'
254
+ let text = 'Studied in'
255
255
  this.entry.provenanceTaxonomyLabel.forEach((label) => {
256
256
  text += ` ${label},`
257
257
  })
@@ -341,7 +341,7 @@ export default {
341
341
  .el-popper__arrow {
342
342
  &:before {
343
343
  border-color: $app-primary-color;
344
- background-color: #f3ecf6;
344
+ background-color: #ffffff;
345
345
  }
346
346
  }
347
347
  }
@@ -2,7 +2,21 @@
2
2
  <div class="selections-container">
3
3
  <el-row>
4
4
  <el-col :span="12">
5
- <div class="checkall-display-text">{{ title }}</div>
5
+ <span class="checkall-display-text">{{ title }}</span>
6
+ <el-popover
7
+ width="250"
8
+ trigger="hover"
9
+ :teleported="false"
10
+ popper-class="popover-origin-help"
11
+ v-if="helpMessage"
12
+ >
13
+ <template v-if="helpMessage" #reference>
14
+ <el-icon class="info"><el-icon-warning /></el-icon>
15
+ </template>
16
+ <span style="word-break: keep-all">
17
+ {{ helpMessage }}
18
+ </span>
19
+ </el-popover>
6
20
  </el-col>
7
21
  <el-col :span="12">
8
22
  <el-checkbox
@@ -11,6 +25,7 @@
11
25
  :indeterminate="isIndeterminate"
12
26
  v-model="checkAll"
13
27
  @change="handleCheckAllChange"
28
+ @click="onAllCheckboxNativeChange"
14
29
  >Display all</el-checkbox
15
30
  >
16
31
  </el-col>
@@ -35,6 +50,7 @@
35
50
  class="my-checkbox"
36
51
  :label="item[identifierKey]"
37
52
  @change="visibilityToggle(item[identifierKey], $event)"
53
+ @click="onCheckboxNativeChange"
38
54
  :checked="!('enabled' in item) || item.enabled === true"
39
55
  >
40
56
  <el-row class="checkbox-row">
@@ -57,9 +73,13 @@
57
73
 
58
74
  <script>
59
75
  /* eslint-disable no-alert, no-console */
76
+ import {
77
+ Warning as ElIconWarning,
78
+ } from '@element-plus/icons-vue'
60
79
  import {
61
80
  ElCheckbox as Checkbox,
62
81
  ElCheckboxGroup as CheckboxGroup,
82
+ ElIcon as Icon,
63
83
  ElCol as Col,
64
84
  ElRow as Row,
65
85
  } from 'element-plus'
@@ -70,7 +90,9 @@ export default {
70
90
  Checkbox,
71
91
  CheckboxGroup,
72
92
  Col,
93
+ Icon,
73
94
  Row,
95
+ ElIconWarning,
74
96
  },
75
97
  methods: {
76
98
  /**
@@ -88,8 +110,52 @@ export default {
88
110
  }
89
111
  })
90
112
  },
113
+ setCheckboxActionData: function (containerEl, option) {
114
+ // option = 'individual' or 'all'
115
+ if (containerEl) {
116
+ const checkboxEl = containerEl.querySelector('input[type="checkbox"]');
117
+ const checkboxLabelEl = containerEl.querySelector('.el-checkbox__label');
118
+ const selectionsContainerEl = containerEl.closest('.selections-container');
119
+ const selectionsTitleEl = selectionsContainerEl.querySelector('.checkall-display-text');
120
+
121
+ // change true/false to checked/unchecked for readability
122
+ let checkedLabel = '';
123
+ if (checkboxEl) {
124
+ checkedLabel = checkboxEl.checked ? 'checked' : 'unchecked';
125
+ }
126
+
127
+ this.checkboxActionData = {
128
+ selectionsTitle: selectionsTitleEl ? selectionsTitleEl.innerText : '',
129
+ property: (checkboxEl && option !== 'all') ? checkboxEl.value : '',
130
+ label: checkboxLabelEl ? checkboxLabelEl.innerText : '',
131
+ checked: checkedLabel
132
+ };
133
+ } else {
134
+ // reset if no checkbox container found
135
+ this.checkboxActionData = {
136
+ selectionsTitle: '',
137
+ property: '',
138
+ label: '',
139
+ checked: '',
140
+ };
141
+ }
142
+ },
143
+ onCheckboxNativeChange: function (event) {
144
+ const checkboxContainerEl = event.target.closest('.checkbox-container');
145
+ this.setCheckboxActionData(checkboxContainerEl, 'individual');
146
+ },
147
+ onAllCheckboxNativeChange: function (event) {
148
+ const checkboxContainerEl = event.target.closest('.all-checkbox');
149
+ this.setCheckboxActionData(checkboxContainerEl, 'all');
150
+ },
91
151
  visibilityToggle: function (key, value) {
92
152
  this.$emit('changed', { key, value })
153
+ // emit event with checkbox data for tracking
154
+ if (key === this.checkboxActionData.property) {
155
+ // change true/false to checked/unchecked for readability
156
+ this.checkboxActionData.checked = value ? 'checked' : 'unchecked';
157
+ }
158
+ this.$emit('selections-data-changed', this.checkboxActionData);
93
159
  },
94
160
  checkboxMouseEnterEmit: function (key, value) {
95
161
  // Update the stated to send to the emit
@@ -109,6 +175,11 @@ export default {
109
175
  keys: this.selections.map((a) => a[this.identifierKey]),
110
176
  value: val,
111
177
  })
178
+ // emit event with checkbox data for tracking
179
+ this.checkboxActionData.property = this.identifierKey;
180
+ // change true/false to checked/unchecked for readability
181
+ this.checkboxActionData.checked = val ? 'checked' : 'unchecked';
182
+ this.$emit('selections-data-changed', this.checkboxActionData);
112
183
  },
113
184
  getBackgroundStyles: function (item) {
114
185
  if ('colour' in item && this.colourStyle === 'background') {
@@ -136,6 +207,10 @@ export default {
136
207
  type: String,
137
208
  default: 'line',
138
209
  },
210
+ helpMessage: {
211
+ type: String,
212
+ default: '',
213
+ },
139
214
  identifierKey: {
140
215
  type: String,
141
216
  default: 'id',
@@ -168,6 +243,12 @@ export default {
168
243
  return {
169
244
  checkedItems: [],
170
245
  checkAll: true,
246
+ checkboxActionData: {
247
+ selectionsTitle: '',
248
+ property: '',
249
+ label: '',
250
+ checked: '',
251
+ },
171
252
  }
172
253
  },
173
254
  mounted: function () {
@@ -261,4 +342,22 @@ export default {
261
342
  width: 100%;
262
343
  top: 2px;
263
344
  }
345
+
346
+ .info {
347
+ transform: rotate(180deg);
348
+ color: #8300bf;
349
+ margin-left: 8px;
350
+ }
351
+
352
+
353
+ :deep(.popover-origin-help.el-popover) {
354
+ text-transform: none !important; // need to overide the tooltip text transform
355
+ border: 1px solid $app-primary-color;
356
+ .el-popper__arrow {
357
+ &:before {
358
+ border-color: $app-primary-color;
359
+ background-color: #ffffff;
360
+ }
361
+ }
362
+ }
264
363
  </style>
@@ -3,5 +3,6 @@
3
3
  import FlatmapVuer from './FlatmapVuer.vue'
4
4
  import MultiFlatmapVuer from './MultiFlatmapVuer.vue'
5
5
  import Tooltip from './Tooltip.vue'
6
+ import HelpModeDialog from './HelpModeDialog.vue'
6
7
 
7
- export { FlatmapVuer, MultiFlatmapVuer, Tooltip}
8
+ export { FlatmapVuer, MultiFlatmapVuer, Tooltip, HelpModeDialog }