@abi-software/map-utilities 1.0.1-beta.3 → 1.1.1-beta.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abi-software/map-utilities",
3
- "version": "1.0.1-beta.3",
3
+ "version": "1.1.1-beta.0",
4
4
  "files": [
5
5
  "dist/*",
6
6
  "src/*",
@@ -29,6 +29,7 @@
29
29
  "./src/*": "./src/*"
30
30
  },
31
31
  "dependencies": {
32
+ "@abi-software/gallery": "^1.1.0",
32
33
  "@abi-software/svg-sprite": "^1.0.0",
33
34
  "@element-plus/icons-vue": "^2.3.1",
34
35
  "element-plus": "^2.7.3",
@@ -0,0 +1,70 @@
1
+ <template>
2
+ <div class="image-dialog" v-if="imageIframeOpen">
3
+ <div class="map-iframe-container">
4
+ <iframe
5
+ class="my-iframe"
6
+ :src="imageIframeURL">
7
+ </iframe>
8
+ </div>
9
+ <button
10
+ class="image-dialog-close"
11
+ @click="closeImageIframe"
12
+ >
13
+ Close
14
+ </button>
15
+ </div>
16
+ </template>
17
+
18
+ <script>
19
+ export default {
20
+ name: 'IframeImageDialog',
21
+ props: {
22
+ imageIframeOpen: false,
23
+ imageIframeURL: ''
24
+ },
25
+ methods: {
26
+ closeImageIframe: function () {
27
+ this.$emit('closeImageIframe')
28
+ },
29
+ }
30
+ }
31
+ </script>
32
+
33
+ <style lang="scss" scoped>
34
+ .image-dialog {
35
+ position: fixed;
36
+ top: 0;
37
+ left: 0;
38
+ width: 100%;
39
+ height: 100%;
40
+ background-color: rgba(#000, 0.9);
41
+ z-index: 1000;
42
+ display: flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+
46
+ .image-dialog-close {
47
+ position: absolute;
48
+ top: 1rem;
49
+ right: 1rem;
50
+ padding: 4px;
51
+ border: 2px solid;
52
+ border-radius: 4px;
53
+ }
54
+
55
+ .map-iframe-container {
56
+ height: 80%;
57
+ width: 80%;
58
+ background: #fff;
59
+ border: 1px solid black;
60
+ overflow: hidden;
61
+ }
62
+
63
+ .my-iframe {
64
+ height:100%;
65
+ width:100%;
66
+ position:relative;
67
+ border-width:0px;
68
+ }
69
+ }
70
+ </style>
@@ -0,0 +1,343 @@
1
+ <template>
2
+ <div class="main" v-loading="loading">
3
+ <!-- <div class="block" v-if="entry.title">
4
+ <span class="title">{{ capitalise(entry.title) }}</span>
5
+ </div> -->
6
+ <div class="block">
7
+ <el-button
8
+ class="button"
9
+ @click="showImages = !showImages"
10
+ >
11
+ <span v-if="showImages">Hide images</span>
12
+ <span v-else>View images at this location (Gallery)</span>
13
+ </el-button>
14
+ <template :key="index" v-for="(species, index) in speciesFilterTags">
15
+ <el-tag
16
+ type="info"
17
+ class="tag"
18
+ :class="{ 'active-tag': species.taxon === activeSpecies.taxon}"
19
+ @close="removeSpeciesFilterTag(species)"
20
+ @click="filterBySpecies(species)"
21
+ :closable="species.taxon === activeSpecies.taxon"
22
+ >
23
+ {{ species.name }} ({{ species.count }})
24
+ </el-tag>
25
+ </template>
26
+ <div v-if="showImages" class="image-gallery-container">
27
+ <Gallery class="gallery" :items="filteredItems" />
28
+ </div>
29
+ <el-button
30
+ class="button"
31
+ @click="viewImage(imageIframeURL[this.entry.featureId[0]])"
32
+ >
33
+ <span>View images at this location (iFrame)</span>
34
+ </el-button>
35
+ </div>
36
+ </div>
37
+ </template>
38
+
39
+ <script>
40
+ import Gallery from "@abi-software/gallery";
41
+ import "@abi-software/gallery/dist/style.css";
42
+ import {
43
+ ArrowUp as ElIconArrowUp,
44
+ ArrowDown as ElIconArrowDown,
45
+ Warning as ElIconWarning,
46
+ } from '@element-plus/icons-vue'
47
+ /* eslint-disable no-alert, no-console */
48
+ import {
49
+ ElButton as Button,
50
+ ElContainer as Container,
51
+ ElIcon as Icon,
52
+ ElTag as Tag,
53
+ } from 'element-plus'
54
+
55
+ import flatmapImageMixin from '../../mixins/flatmapImageMixin';
56
+
57
+ const titleCase = (str) => {
58
+ return str.replace(/\w\S*/g, (t) => {
59
+ return t.charAt(0).toUpperCase() + t.substr(1).toLowerCase()
60
+ })
61
+ }
62
+
63
+ const capitalise = function (str) {
64
+ if (str) return str.charAt(0).toUpperCase() + str.slice(1)
65
+ return ''
66
+ }
67
+
68
+ const imageIframeURL = {
69
+ 'UBERON:0000948': 'https://sparc.biolucida.net/image?c=MjIzNzItY29sLTI1NA%3D%3D',
70
+ 'UBERON:0016508': 'https://sparc.biolucida.net/image?c=MjIzNzQtY29sLTI1NA%3D%3D',
71
+ 'ILX:0793082': 'https://sparc.biolucida.net/image?c=MjIzNzUtY29sLTI1NA%3D%3D'
72
+ }
73
+
74
+ export default {
75
+ name: 'ImageGalleryPopup',
76
+ mixins: [flatmapImageMixin],
77
+ components: {
78
+ Button,
79
+ Container,
80
+ Icon,
81
+ Tag,
82
+ ElIconArrowUp,
83
+ ElIconArrowDown,
84
+ ElIconWarning,
85
+ Gallery,
86
+ },
87
+ props: {
88
+ galleryItems: {
89
+ type: Array,
90
+ default: () => [],
91
+ },
92
+ entry: {
93
+ type: Object,
94
+ default: () => ({
95
+ destinations: [],
96
+ origins: [],
97
+ components: [],
98
+ destinationsWithDatasets: [],
99
+ originsWithDatasets: [],
100
+ componentsWithDatasets: [],
101
+ resource: undefined,
102
+ }),
103
+ },
104
+ },
105
+ data: function () {
106
+ return {
107
+ controller: undefined,
108
+ activeSpecies: undefined,
109
+ loading: false,
110
+ showImages: false,
111
+ activeSpecies: { taxon: '', name: ''},
112
+ imageIframeURL: imageIframeURL,
113
+ speciesFilterTags: [],
114
+ filteredItems: [],
115
+ }
116
+ },
117
+ watch: {
118
+ galleryItems: {
119
+ handler: function () {
120
+ this.populateSpeciesFilterTags()
121
+ },
122
+ deep: true,
123
+ },
124
+ },
125
+ methods: {
126
+ removeSpeciesFilterTag: function (species) {
127
+ this.activeSpecies = { taxon: '', name: '' }
128
+ this.filteredItems = this.galleryItems
129
+ },
130
+ populateSpeciesFilterTags: function () {
131
+ this.speciesFilterTags = this.findAllSpeciesFromGalleryItems(this.galleryItems)
132
+ },
133
+ filterBySpecies: function (species) {
134
+ this.activeSpecies = species
135
+ this.filteredItems = this.findImagesForSpeciesFromGalleryItems(this.galleryItems, species.taxon)
136
+ },
137
+ titleCase: function (title) {
138
+ return titleCase(title)
139
+ },
140
+ capitalise: function (text) {
141
+ return capitalise(text)
142
+ },
143
+ viewImage: function (url) {
144
+ this.$emit('view-image', url)
145
+ }
146
+ },
147
+ mounted: function () {
148
+ this.populateSpeciesFilterTags()
149
+ this.filteredItems = this.galleryItems
150
+ },
151
+ }
152
+ </script>
153
+
154
+ <style lang="scss" scoped>
155
+
156
+ .display {
157
+ width: 44px;
158
+ word-break: normal;
159
+ }
160
+
161
+ .title {
162
+ text-align: left;
163
+ width: 16em;
164
+ line-height: 1.5em !important;
165
+ font-size: 1em;
166
+ font-family: Helvetica;
167
+ font-weight: 500;
168
+ /* font-weight: bold; */
169
+ padding-bottom: 8px;
170
+ }
171
+
172
+ .block {
173
+ margin-bottom: 0.5em;
174
+ }
175
+
176
+ .tag {
177
+ margin-right: 5px;
178
+ margin-bottom: 5px;
179
+ cursor: pointer;
180
+ }
181
+
182
+ .active-tag {
183
+ background-color: $app-primary-color;
184
+ color: #fff;
185
+ }
186
+
187
+ .icon {
188
+ right: 0px;
189
+ position: absolute;
190
+ top: 10px;
191
+ }
192
+
193
+ .icon:hover {
194
+ cursor: pointer;
195
+ }
196
+
197
+
198
+ .main {
199
+ font-size: 14px;
200
+ text-align: left;
201
+ line-height: 1.5em;
202
+ font-family: Asap, sans-serif, Helvetica;
203
+ font-weight: 400;
204
+ /* outline: thin red solid; */
205
+ padding: 1em !important;
206
+ overflow: hidden;
207
+ min-width: 18rem;
208
+ }
209
+
210
+ .title {
211
+ font-size: 18px;
212
+ font-weight: 500;
213
+ font-weight: bold;
214
+ padding-bottom: 8px;
215
+ color: rgb(131, 0, 191);
216
+ }
217
+
218
+ .attribute-title {
219
+ font-size: 16px;
220
+ font-weight: 600;
221
+ /* font-weight: bold; */
222
+ text-transform: uppercase;
223
+ }
224
+
225
+ .attribute-content {
226
+ font-size: 14px;
227
+ font-weight: 500;
228
+ }
229
+
230
+ .button {
231
+ margin-left: 0px !important;
232
+ margin-top: 0px !important;
233
+ font-size: 14px !important;
234
+ background-color: $app-primary-color;
235
+ color: #fff;
236
+ & + .button {
237
+ margin-top: 10px !important;
238
+ }
239
+ &:hover {
240
+ color: #fff !important;
241
+ background: #ac76c5 !important;
242
+ border: 1px solid #ac76c5 !important;
243
+ }
244
+ }
245
+
246
+ .tooltip-container {
247
+ &::after,
248
+ &::before {
249
+ content: '';
250
+ display: block;
251
+ position: absolute;
252
+ width: 0;
253
+ height: 0;
254
+ border-style: solid;
255
+ flex-shrink: 0;
256
+ }
257
+ .tooltip {
258
+ &::after {
259
+ display: none;
260
+ }
261
+ &::before {
262
+ display: none;
263
+ }
264
+ }
265
+ }
266
+
267
+ .maplibregl-popup-anchor-bottom {
268
+ .tooltip-container {
269
+ &::after,
270
+ &::before {
271
+ top: 100%;
272
+ border-width: 12px;
273
+ }
274
+ &::after {
275
+ margin-top: -1px;
276
+ border-color: rgb(255, 255, 255) transparent transparent transparent;
277
+ }
278
+ &::before {
279
+ margin: 0 auto;
280
+ border-color: $app-primary-color transparent transparent transparent;
281
+ }
282
+ }
283
+ }
284
+
285
+ .maplibregl-popup-anchor-top {
286
+ .tooltip-container {
287
+ &::after,
288
+ &::before {
289
+ top: -24px;
290
+ border-width: 12px;
291
+ }
292
+ &::after {
293
+ margin-top: 1px;
294
+ border-color: transparent transparent rgb(255, 255, 255) transparent;
295
+ }
296
+ &::before {
297
+ margin: 0 auto;
298
+ border-color: transparent transparent $app-primary-color transparent;
299
+ }
300
+ }
301
+ }
302
+
303
+ .content-container {
304
+ overflow-y: scroll;
305
+ scrollbar-width: thin !important;
306
+ max-height: 240px;
307
+ }
308
+
309
+ .scrollbar::-webkit-scrollbar-track {
310
+ border-radius: 10px;
311
+ background-color: #f5f5f5;
312
+ }
313
+
314
+ .scrollbar::-webkit-scrollbar {
315
+ width: 12px;
316
+ right: -12px;
317
+ background-color: #f5f5f5;
318
+ }
319
+
320
+ .scrollbar::-webkit-scrollbar-thumb {
321
+ border-radius: 4px;
322
+ box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.06);
323
+ background-color: #979797;
324
+ }
325
+
326
+ .image-gallery-container {
327
+ :deep(.gallery) {
328
+ .gallery-strip {
329
+ padding: 1rem 0;
330
+ }
331
+
332
+ > div {
333
+ min-height: max-content !important;
334
+ }
335
+ }
336
+ }
337
+
338
+ /* Fix for chrome bug where under triangle pops up above one on top of it */
339
+ .selector:not(*:root),
340
+ .tooltip-container::after {
341
+ top: 99.4%;
342
+ }
343
+ </style>
@@ -31,6 +31,24 @@
31
31
  </span>
32
32
  </el-popover>
33
33
  </div>
34
+ <div class="block">
35
+ <el-button
36
+ class="button"
37
+ @click="showImages = !showImages"
38
+ >
39
+ <span v-if="showImages">Hide images</span>
40
+ <span v-else>View images at this location (Gallery)</span>
41
+ </el-button>
42
+ <div v-if="showImages" class="image-gallery-container">
43
+ <Gallery :items="galleryItems" />
44
+ </div>
45
+ <el-button
46
+ class="button"
47
+ @click="viewImage(imageIframeURL[this.tooltipEntry.featureId[0]])"
48
+ >
49
+ <span>View images at this location (iFrame)</span>
50
+ </el-button>
51
+ </div>
34
52
  <div
35
53
  v-show="showDetails"
36
54
  class="hide"
@@ -171,6 +189,20 @@
171
189
  </template>
172
190
 
173
191
  <script>
192
+ import Gallery from "@abi-software/gallery";
193
+ import "@abi-software/gallery/dist/style.css";
194
+ import {
195
+ ArrowUp as ElIconArrowUp,
196
+ ArrowDown as ElIconArrowDown,
197
+ Warning as ElIconWarning,
198
+ } from '@element-plus/icons-vue'
199
+ /* eslint-disable no-alert, no-console */
200
+ import {
201
+ ElButton as Button,
202
+ ElContainer as Container,
203
+ ElIcon as Icon,
204
+ } from 'element-plus'
205
+
174
206
  import EventBus from "../EventBus.js";
175
207
 
176
208
  const titleCase = (str) => {
@@ -184,8 +216,41 @@ const capitalise = function (str) {
184
216
  return "";
185
217
  };
186
218
 
219
+ // TODO: temp data for testing
220
+ const galleryItems = [
221
+ {
222
+ title: 'Title 1',
223
+ type: 'data1',
224
+ userData: 'https://sparc.science/',
225
+ },
226
+ {
227
+ title: 'Title 2',
228
+ type: 'data2',
229
+ link: 'https://sparc.science/',
230
+ },
231
+ {
232
+ title: 'Title 3',
233
+ type: 'data3',
234
+ link: 'https://sparc.science/',
235
+ },
236
+ ]
237
+ const imageIframeURL = {
238
+ 'UBERON:0000948': 'https://sparc.biolucida.net/image?c=MjIzNzItY29sLTI1NA%3D%3D',
239
+ 'UBERON:0016508': 'https://sparc.biolucida.net/image?c=MjIzNzQtY29sLTI1NA%3D%3D',
240
+ 'ILX:0793082': 'https://sparc.biolucida.net/image?c=MjIzNzUtY29sLTI1NA%3D%3D'
241
+ }
242
+
187
243
  export default {
188
244
  name: "ProvenancePopup",
245
+ components: {
246
+ Button,
247
+ Container,
248
+ Icon,
249
+ ElIconArrowUp,
250
+ ElIconArrowDown,
251
+ ElIconWarning,
252
+ Gallery,
253
+ },
189
254
  props: {
190
255
  tooltipEntry: {
191
256
  type: Object,
@@ -209,6 +274,9 @@ export default {
209
274
  loading: false,
210
275
  showToolip: false,
211
276
  showDetails: false,
277
+ showImages: false,
278
+ galleryItems: galleryItems,
279
+ imageIframeURL: imageIframeURL,
212
280
  originDescriptions: {
213
281
  motor: "is the location of the initial cell body of the circuit",
214
282
  sensory: "is the location of the initial cell body in the PNS circuit",
@@ -280,6 +348,9 @@ export default {
280
348
  pubmedSearchUrlUpdate: function (val) {
281
349
  this.pubmedSearchUrl = val;
282
350
  },
351
+ viewImage: function (url) {
352
+ this.$emit('view-image', url);
353
+ },
283
354
  },
284
355
  };
285
356
  </script>
@@ -510,6 +581,18 @@ export default {
510
581
  background-color: #979797;
511
582
  }
512
583
 
584
+ .image-gallery-container {
585
+ :deep(.gallery) {
586
+ .gallery-strip {
587
+ padding: 1rem 0;
588
+ }
589
+
590
+ > div {
591
+ min-height: max-content !important;
592
+ }
593
+ }
594
+ }
595
+
513
596
  /* Fix for chrome bug where under triangle pops up above one on top of it */
514
597
  .selector:not(*:root),
515
598
  .tooltip-container::after {
@@ -1,13 +1,22 @@
1
1
  <template>
2
2
  <div class="tooltip-container" id="tooltip-container">
3
- <template v-if="annotationDisplay">
3
+ <template v-if="tooltipType === 'annotation' && annotationDisplay">
4
4
  <annotation-popup
5
5
  :annotationEntry="annotationEntry"
6
6
  @annotation="$emit('annotation', $event)"
7
7
  />
8
8
  </template>
9
- <template v-else>
10
- <provenance-popup :tooltipEntry="tooltipEntry" />
9
+ <template v-if="tooltipType === 'provenance'">
10
+ <provenance-popup
11
+ :tooltipEntry="tooltipEntry"
12
+ @view-image="viewImage"
13
+ />
14
+ </template>
15
+ <template v-if="tooltipType === 'image-gallery'">
16
+ <image-gallery-popup
17
+ :galleryItems="galleryItems"
18
+ @viewImage="viewImage"
19
+ />
11
20
  </template>
12
21
  </div>
13
22
  </template>
@@ -21,6 +30,10 @@ export default {
21
30
  tooltipEntry: {
22
31
  type: Object,
23
32
  },
33
+ tooltipType: {
34
+ type: String,
35
+ default: 'provenance',
36
+ },
24
37
  annotationDisplay: {
25
38
  type: Boolean,
26
39
  default: false,
@@ -28,6 +41,10 @@ export default {
28
41
  annotationEntry: {
29
42
  type: Object,
30
43
  },
44
+ galleryItems: {
45
+ type: Array,
46
+ default: () => [],
47
+ },
31
48
  },
32
49
  mounted: function() {
33
50
  // Emit events from child components
@@ -35,6 +52,11 @@ export default {
35
52
  this.$emit("onActionClick", data);
36
53
  });
37
54
  },
55
+ methods: {
56
+ viewImage: function (url) {
57
+ this.$emit('view-image', url)
58
+ }
59
+ }
38
60
  };
39
61
  </script>
40
62
 
@@ -3,5 +3,13 @@ import HelpModeDialog from "./HelpModeDialog/HelpModeDialog.vue";
3
3
  import Tooltip from "./Tooltip/Tooltip.vue";
4
4
  import TreeControls from "./TreeControls/TreeControls.vue";
5
5
  import CopyToClipboard from "./CopyToClipboard/CopyToClipboard.vue";
6
+ import IframeImageDialog from "./ImageDialog/IframeImageDialog.vue";
6
7
 
7
- export { DrawToolbar, HelpModeDialog, Tooltip, TreeControls, CopyToClipboard };
8
+ export {
9
+ DrawToolbar,
10
+ HelpModeDialog,
11
+ Tooltip,
12
+ TreeControls,
13
+ CopyToClipboard,
14
+ IframeImageDialog
15
+ };
@@ -31,10 +31,13 @@ declare module 'vue' {
31
31
  ElPopover: typeof import('element-plus/es')['ElPopover']
32
32
  ElRow: typeof import('element-plus/es')['ElRow']
33
33
  ElSelect: typeof import('element-plus/es')['ElSelect']
34
+ ElTag: typeof import('element-plus/es')['ElTag']
34
35
  ElTooltip: typeof import('element-plus/es')['ElTooltip']
35
36
  ElTree: typeof import('element-plus/es')['ElTree']
36
37
  ExternalResourceCard: typeof import('./components/Tooltip/ExternalResourceCard.vue')['default']
37
38
  HelpModeDialog: typeof import('./components/HelpModeDialog/HelpModeDialog.vue')['default']
39
+ IframeImageDialog: typeof import('./components/ImageDialog/IframeImageDialog.vue')['default']
40
+ ImageGalleryPopup: typeof import('./components/Tooltip/ImageGalleryPopup.vue')['default']
38
41
  ProvenancePopup: typeof import('./components/Tooltip/ProvenancePopup.vue')['default']
39
42
  Tooltip: typeof import('./components/Tooltip/Tooltip.vue')['default']
40
43
  TreeControls: typeof import('./components/TreeControls/TreeControls.vue')['default']
@@ -0,0 +1,42 @@
1
+ export default {
2
+ // Note that the setting store is included in MapContent.vue
3
+ methods: {
4
+ findImagesForSpeciesFromGalleryItems: function (galleryItems, speciesToFind) {
5
+ let imageList = []
6
+ galleryItems.forEach((image) => {
7
+ if (image.species && image.species.length > 0) {
8
+ image.species.forEach((species) => {
9
+ if (species.species && species.species.curie === speciesToFind) {
10
+ imageList.push(image)
11
+ }
12
+ })
13
+ }
14
+ })
15
+ return imageList
16
+ },
17
+ findAllSpeciesFromGalleryItems: function (galleryItems) {
18
+ let speciesList = [];
19
+ let speciesSet = new Set();
20
+
21
+ galleryItems.forEach((image) => {
22
+ if (image.species && image.species.length > 0) {
23
+ image.species.forEach((species) => {
24
+ if (species.species) {
25
+ let ispc = species.species;
26
+ if (!speciesSet.has(ispc.curie)) {
27
+ speciesSet.add(ispc.curie);
28
+ speciesList.push({ 'taxon': ispc.curie, 'name': ispc.name, 'count': 1});
29
+ } else {
30
+ speciesList.forEach((sp) => {
31
+ if (sp.taxon === ispc.curie) sp.count++;
32
+ });
33
+ }
34
+ }
35
+ });
36
+ }
37
+ });
38
+
39
+ return speciesList;
40
+ },
41
+ }
42
+ }