@abi-software/map-side-bar 2.5.3-beta.0 → 2.5.3-beta.10

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-side-bar",
3
- "version": "2.5.3-beta.0",
3
+ "version": "2.5.3-beta.10",
4
4
  "files": [
5
5
  "dist/*",
6
6
  "src/*",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@abi-software/gallery": "^1.1.2",
42
- "@abi-software/map-utilities": "^1.2.1",
42
+ "@abi-software/map-utilities": "^1.2.2-beta.2",
43
43
  "@abi-software/svg-sprite": "^1.0.1",
44
44
  "@element-plus/icons-vue": "^2.3.1",
45
45
  "algoliasearch": "^4.10.5",
@@ -81,7 +81,7 @@
81
81
  </div>
82
82
  </div>
83
83
 
84
- <div class="content-container content-container-connectivity" v-if="activeView === 'listView'">
84
+ <div class="content-container content-container-connectivity" v-show="activeView === 'listView'">
85
85
  {{ entry.paths }}
86
86
  <div v-if="entry.origins && entry.origins.length > 0" class="block">
87
87
  <div class="attribute-title-container">
@@ -209,13 +209,15 @@
209
209
  </div>
210
210
  </div>
211
211
 
212
- <div class="content-container" v-if="activeView === 'graphView'">
213
- <connectivity-graph
214
- :entry="entry.featureId[0]"
215
- :mapServer="envVars.FLATMAPAPI_LOCATION"
216
- @tap-node="onTapNode"
217
- ref="connectivityGraphRef"
218
- />
212
+ <div class="content-container" v-show="activeView === 'graphView'">
213
+ <template v-if="graphViewLoaded">
214
+ <connectivity-graph
215
+ :entry="entry.featureId[0]"
216
+ :mapServer="envVars.FLATMAPAPI_LOCATION"
217
+ @tap-node="onTapNode"
218
+ ref="connectivityGraphRef"
219
+ />
220
+ </template>
219
221
  </div>
220
222
  </div>
221
223
  </template>
@@ -304,6 +306,7 @@ export default {
304
306
  uberons: [{ id: undefined, name: undefined }],
305
307
  connectivityError: null,
306
308
  timeoutID: undefined,
309
+ graphViewLoaded: false,
307
310
  }
308
311
  },
309
312
  watch: {
@@ -406,13 +409,11 @@ export default {
406
409
  switchConnectivityView: function (val) {
407
410
  this.activeView = val;
408
411
 
409
- if (val === 'graphView') {
410
- const connectivityGraphRef = this.$refs.connectivityGraphRef;
411
- if (connectivityGraphRef && connectivityGraphRef.$el) {
412
- connectivityGraphRef.$el.scrollIntoView({
413
- behavior: 'smooth',
414
- });
415
- }
412
+ if (val === 'graphView' && !this.graphViewLoaded) {
413
+ // to load the connectivity graph only after the container is in view
414
+ this.$nextTick(() => {
415
+ this.graphViewLoaded = true;
416
+ });
416
417
  }
417
418
  },
418
419
  onTapNode: function (data) {
@@ -168,6 +168,7 @@ export default {
168
168
  showFiltersText: true,
169
169
  cascadeSelected: [],
170
170
  cascadeSelectedWithBoolean: [],
171
+ filterTimeout: null,
171
172
  numberShown: 10,
172
173
  filters: [],
173
174
  facets: ['Species', 'Gender', 'Organ', 'Datasets'],
@@ -477,10 +478,18 @@ export default {
477
478
  filters = [];
478
479
  }
479
480
 
481
+ // timeout: add delay for filter checkboxes
482
+ if (this.filterTimeout) {
483
+ clearTimeout(this.filterTimeout);
484
+ }
485
+
480
486
  this.$emit('loading', true) // let sidebarcontent wait for the requests
481
- this.$emit('filterResults', filters) // emit filters for apps above sidebar
482
487
  this.setCascader(filterKeys) //update our cascader v-model if we modified the event
483
- this.cssMods() // update css for the cascader
488
+
489
+ this.filterTimeout = setTimeout(() => {
490
+ this.$emit('filterResults', filters) // emit filters for apps above sidebar
491
+ this.cssMods() // update css for the cascader
492
+ }, 400);
484
493
  }
485
494
  },
486
495
  //this fucntion is needed as we previously stored booleans in the array of event that
@@ -1008,6 +1017,8 @@ export default {
1008
1017
 
1009
1018
  .sidebar-cascader-popper .el-checkbox__input.is-checked .el-checkbox__inner,
1010
1019
  .el-checkbox__input.is-indeterminate .el-checkbox__inner {
1020
+ --el-checkbox-checked-bg-color: #{$app-primary-color};
1021
+ --el-checkbox-checked-input-border-color: #{$app-primary-color};
1011
1022
  background-color: $app-primary-color;
1012
1023
  border-color: $app-primary-color;
1013
1024
  }
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="history-container" v-if="searchHistory.length">
3
3
  <div class="saved-search-history" v-if="savedSearchHistory.length">
4
- <template v-for="(item, i) in savedSearchHistory">
4
+ <template v-for="(item, i) in savedSearchHistory" :key="item.id">
5
5
  <el-tag
6
6
  class="search-tag"
7
7
  v-if="i < 2"
@@ -9,14 +9,30 @@
9
9
  @click="search(item)"
10
10
  size="large"
11
11
  >
12
- {{ item.label }}
12
+ <template v-if="item.longLabel">
13
+ <el-popover
14
+ width="auto"
15
+ trigger="hover"
16
+ :show-after="200"
17
+ :persistent="false"
18
+ popper-class="popover-dropdown"
19
+ >
20
+ <template #reference>
21
+ {{ item.label }}
22
+ </template>
23
+ {{ item.longLabel }}
24
+ </el-popover>
25
+ </template>
26
+ <template v-else>
27
+ {{ item.label }}
28
+ </template>
13
29
  </el-tag>
14
30
  </template>
15
31
  </div>
16
32
  <div v-else>
17
33
  <span class="empty-saved-search">No Saved Searches</span>
18
34
  </div>
19
- <el-dropdown trigger="click" :max-height="350" :hide-on-click="false">
35
+ <el-dropdown trigger="click" :hide-on-click="false">
20
36
  <span class="el-dropdown-select">
21
37
  Search history
22
38
  <el-icon class="el-icon--right">
@@ -25,14 +41,37 @@
25
41
  </span>
26
42
  <template #dropdown>
27
43
  <el-dropdown-menu>
28
- <el-dropdown-item v-for="(item, i) in searchHistory">
29
- <div>{{ item.label }}</div>
44
+ <el-dropdown-item v-for="item in searchHistory" :key="item.id">
45
+ <div>
46
+ <template v-if="item.longLabel">
47
+ <el-popover
48
+ width="auto"
49
+ trigger="hover"
50
+ :show-after="200"
51
+ :persistent="false"
52
+ popper-class="popover-dropdown"
53
+ >
54
+ <template #reference>
55
+ <span class="dropdown-clickable-item" @click="search(item)">
56
+ {{ item.label }}
57
+ </span>
58
+ </template>
59
+ {{ item.longLabel }}
60
+ </el-popover>
61
+ </template>
62
+ <template v-else>
63
+ <span class="dropdown-clickable-item" @click="search(item)">
64
+ {{ item.label }}
65
+ </span>
66
+ </template>
67
+ </div>
30
68
  <div>
31
69
  <el-popover
32
70
  width="auto"
33
71
  trigger="hover"
34
72
  :teleported="true"
35
- :show-after="100"
73
+ :show-after="200"
74
+ :persistent="false"
36
75
  popper-class="popover-dropdown"
37
76
  >
38
77
  <template #reference>
@@ -41,8 +80,20 @@
41
80
  :disabled="savedSearchHistory.length > 1 && !item.saved"
42
81
  >
43
82
  <el-icon color="#8300BF">
44
- <el-icon-star-filled v-if="item.saved" />
45
- <el-icon-star v-else />
83
+ <template v-if="item.saved">
84
+ <svg
85
+ viewBox="0 0 24 24"
86
+ >
87
+ <path d="m12 21.35-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54z"></path>
88
+ </svg>
89
+ </template>
90
+ <template v-else>
91
+ <svg
92
+ viewBox="0 0 24 24"
93
+ >
94
+ <path d="M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3m-4.4 15.55-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05"></path>
95
+ </svg>
96
+ </template>
46
97
  </el-icon>
47
98
  </el-button>
48
99
  </template>
@@ -60,11 +111,14 @@
60
111
  width="auto"
61
112
  trigger="hover"
62
113
  :teleported="true"
63
- :show-after="100"
114
+ :show-after="200"
115
+ :persistent="false"
64
116
  popper-class="popover-dropdown"
65
117
  >
66
118
  <template #reference>
67
- <el-button circle text size="small" @click="removeFromSavedSearch(item)">
119
+ <el-button circle text size="small"
120
+ @click="removeFromSavedSearch(item)"
121
+ >
68
122
  <el-icon color="#8300BF">
69
123
  <el-icon-delete />
70
124
  </el-icon>
@@ -87,18 +141,13 @@
87
141
  import {
88
142
  ElTag as Tag,
89
143
  ElSelect as Select,
90
- ElDropdown as Dropdown,
91
- ElIcon as Icon,
144
+ ElDropdown,
145
+ ElIcon,
92
146
  } from 'element-plus'
93
147
 
94
148
  import EventBus from './EventBus.js'
95
149
 
96
- // remove duplicates by stringifying the objects
97
- const removeDuplicates = function (arrayOfAnything) {
98
- return [...new Set(arrayOfAnything.map((e) => JSON.stringify(e)))].map((e) =>
99
- JSON.parse(e)
100
- )
101
- }
150
+ const MAX_SEARCH_HISTORY = 12;
102
151
 
103
152
  function generateUUID() {
104
153
  const arr = new Uint8Array(16);
@@ -148,58 +197,164 @@ export default {
148
197
  localStorage.removeItem('sparc.science-sidebar-search-history')
149
198
  this.searchHistory = []
150
199
  },
151
- addSearchToHistory(filters = [], search) {
200
+ sortFilters(a, b) {
201
+ return a.facetPropPath.localeCompare(b.facetPropPath);
202
+ },
203
+ // Sort by saved and updated
204
+ sortSearchHistory(a, b) {
205
+ if (a.saved !== b.saved) {
206
+ return b.saved - a.saved;
207
+ }
208
+ if (a.updated !== b.updated) {
209
+ return b.updated - a.updated;
210
+ }
211
+ return 0;
212
+ },
213
+ formatFilters(filterItem) {
214
+ // because filters do not work correctly with facet2
215
+ if (filterItem.facet2) {
216
+ filterItem.facet = filterItem.facet2;
217
+ delete filterItem.facet2;
218
+ }
219
+ return filterItem;
220
+ },
221
+ addSearchToHistory(filters = [], search = '') {
152
222
  search = search.trim() // remove whitespace
153
- let searchHistory = JSON.parse(
154
- localStorage.getItem('sparc.science-sidebar-search-history')
155
- )
156
- if (searchHistory) {
157
- searchHistory.push({
223
+
224
+ const isExistingItem = this.searchHistory.some((item) => {
225
+ let historyFilters = item.filters;
226
+ let newFilters = filters;
227
+
228
+ // make all filters same format
229
+ historyFilters.forEach((filter) => this.formatFilters(filter));
230
+ newFilters.forEach((filter) => this.formatFilters(filter));
231
+
232
+ // sort filters (to check duplicates in string format)
233
+ historyFilters = historyFilters.sort(this.sortFilters);
234
+ newFilters = newFilters.sort(this.sortFilters);
235
+
236
+ const historyFiltersString = JSON.stringify(historyFilters);
237
+ const newFiltersString = JSON.stringify(newFilters);
238
+
239
+ return (
240
+ item.search === search &&
241
+ historyFiltersString === newFiltersString
242
+ );
243
+ });
244
+
245
+ if (!isExistingItem) {
246
+ const {label, longLabel} = this.searchHistoryItemLabel(search, filters);
247
+ const newItem = {
158
248
  filters: filters,
159
249
  search: search,
160
250
  saved: false,
161
- label: this.searchHistoryItemLabel(search, filters),
251
+ label: label,
252
+ longLabel: longLabel,
162
253
  id: generateUUID(),
163
- });
164
- this.searchHistory = removeDuplicates(searchHistory)
254
+ updated: (new Date()).getTime(),
255
+ };
256
+
257
+ this.searchHistory.push(newItem);
258
+
259
+ this.searchHistory = this.searchHistory.sort(this.sortSearchHistory);
260
+
261
+ // trim search history to 12 items
262
+ this.trimSearchHistory();
263
+
264
+ // Save new data
165
265
  localStorage.setItem(
166
266
  'sparc.science-sidebar-search-history',
167
267
  JSON.stringify(this.searchHistory)
168
- )
169
- } else {
170
- localStorage.setItem(
171
- 'sparc.science-sidebar-search-history',
172
- JSON.stringify([{
173
- filters: filters,
174
- search: search,
175
- saved: false,
176
- label: this.searchHistoryItemLabel(search, filters),
177
- id: generateUUID(),
178
- }])
179
- )
268
+ );
269
+ }
270
+ },
271
+ /**
272
+ * Remove the duplicate items in search history.
273
+ */
274
+ removeDuplicateSearchHistory: function () {
275
+ const keys = [];
276
+ const duplicateItemIDs = [];
277
+
278
+ this.searchHistory.forEach((item) => {
279
+ const key = `${item.search}-${JSON.stringify(item.filters)}`;
280
+ const existingItem = keys.find(k => k.key === key);
281
+ // duplicate item
282
+ if (existingItem) {
283
+ // if current item is saved item
284
+ // add the other item into duplicates
285
+ if (item.saved) {
286
+ duplicateItemIDs.push(existingItem.id);
287
+ } else {
288
+ duplicateItemIDs.push(item.id);
289
+ }
290
+ } else {
291
+ keys.push({
292
+ id: item.id,
293
+ key: key
294
+ });
295
+ }
296
+ });
297
+
298
+ if (duplicateItemIDs.length) {
299
+ this.searchHistory = this.searchHistory.filter((item) => !duplicateItemIDs.includes(item.id));
300
+ }
301
+ },
302
+ /**
303
+ * Function to trim search history to maximum items,
304
+ */
305
+ trimSearchHistory: function () {
306
+ // since saved has max 2
307
+ // remove extra from unsaved
308
+ if (this.searchHistory.length > MAX_SEARCH_HISTORY) {
309
+ const savedItems = this.searchHistory.filter((item) => item.saved);
310
+ const unsavedItems = this.searchHistory.filter((item) => !item.saved);
311
+ const extra = MAX_SEARCH_HISTORY - this.searchHistory.length;
312
+
313
+ this.searchHistory = [
314
+ ...savedItems,
315
+ ...unsavedItems.slice(0, extra),
316
+ ];
180
317
  }
181
318
  },
182
319
  updateSearchHistory: function () {
320
+ // Update for missing attributes
183
321
  this.searchHistory.forEach((item) => {
184
322
  if (!item.id) {
185
323
  item['id'] = generateUUID();
186
324
  }
325
+
187
326
  if (!item.label) {
188
- item['label'] = this.searchHistoryItemLabel(item.search, item.filters);
327
+ const {label, longLabel} = this.searchHistoryItemLabel(item.search, item.filters);
328
+ item['label'] = label;
329
+ item['longLabel'] = longLabel;
189
330
  }
331
+
332
+ // make all filters same format
333
+ item.filters.forEach((filter) =>
334
+ this.formatFilters(filter)
335
+ );
336
+
337
+ // sort filters (to check duplicates in string format)
338
+ item.filters = item.filters.sort(this.sortFilters);
339
+
190
340
  if (!item.saved) {
191
341
  item['saved'] = false;
192
342
  }
193
- });
194
- this.searchHistory = this.searchHistory.sort((a, b) => {
195
- if (a.saved > b.saved) {
196
- return -1;
197
- }
198
- if (a.saved < b.saved) {
199
- return 1;
343
+
344
+ if (!item.updated) {
345
+ item['updated'] = (new Date()).getTime();
200
346
  }
201
- return 0;
202
347
  });
348
+
349
+ this.searchHistory = this.searchHistory.sort(this.sortSearchHistory);
350
+
351
+ // check and remove duplicates
352
+ this.removeDuplicateSearchHistory();
353
+
354
+ // trim search history to 12 items
355
+ this.trimSearchHistory();
356
+
357
+ // Save updated data
203
358
  localStorage.setItem(
204
359
  'sparc.science-sidebar-search-history',
205
360
  JSON.stringify(this.searchHistory)
@@ -209,18 +364,38 @@ export default {
209
364
  this.$emit('search', item)
210
365
  },
211
366
  searchHistoryItemLabel: function (search, filters) {
212
- let label = search;
367
+ let label = search ? `"${search.trim()}"` : '';
368
+ let longLabel = '';
213
369
  let filterItems = [];
370
+ let filterLabels = [];
214
371
 
215
372
  if (filters) {
216
373
  filterItems = filters.filter((filterItem) => filterItem.facet !== 'Show all');
374
+ filterLabels = filterItems.map((item) => item.facet2 || item.facet);
375
+ }
376
+
377
+ if (label && filterItems.length) {
378
+ longLabel += label;
379
+ longLabel += `, ${filterLabels.join(', ')}`;
380
+ label += ` (+${filterItems.length})`;
381
+ }
382
+
383
+ if (!label && filterItems.length) {
384
+ label = filterItems[0].facet;
385
+
386
+ if (filterItems.length > 1) {
387
+ longLabel += `${filterLabels.join(', ')}`;
388
+ label += ` (+${filterItems.length - 1})`;
389
+ }
217
390
  }
218
391
 
219
392
  if (!label) {
220
- label = filterItems.length ? filterItems[0].facet : 'Unknown search';
393
+ label = 'Unknown search';
394
+ } else if (label.length > 15 && !longLabel) {
395
+ longLabel = label;
221
396
  }
222
397
 
223
- return label;
398
+ return {label, longLabel};
224
399
  },
225
400
  toggleSavedSearch: function (item) {
226
401
  this.searchHistory.forEach((_item) => {
@@ -264,15 +439,16 @@ export default {
264
439
 
265
440
  .search-tag.el-tag {
266
441
  margin: 0;
267
- cursor: pointer;
442
+ cursor: pointer !important;
268
443
  background: #f9f2fc!important;
269
444
  border-color: $app-primary-color!important;
270
445
  color:$app-primary-color!important;
271
446
 
272
- .el-tag__content {
447
+ :deep(.el-tag__content) {
273
448
  max-width: 15ch;
274
449
  overflow: hidden;
275
450
  text-overflow: ellipsis;
451
+ line-height: 24px;
276
452
  }
277
453
  }
278
454
 
@@ -300,7 +476,6 @@ export default {
300
476
  transition: var(--el-transition-duration);
301
477
  transform: translateZ(0);
302
478
  box-shadow: 0 0 0 1px var(--el-border-color) inset;
303
- cursor: pointer;
304
479
  }
305
480
 
306
481
  .el-dropdown-select {
@@ -334,6 +509,14 @@ export default {
334
509
  overflow: hidden;
335
510
  }
336
511
 
512
+ .dropdown-clickable-item {
513
+ cursor: pointer;
514
+
515
+ &:hover {
516
+ color: $app-primary-color;
517
+ }
518
+ }
519
+
337
520
  + .el-dropdown-menu__item {
338
521
  &::before {
339
522
  content: "";
@@ -348,8 +531,8 @@ export default {
348
531
  &:hover,
349
532
  &:focus,
350
533
  &:active {
351
- color: inherit;
352
- background-color: var(--el-bg-color-page);
534
+ color: inherit !important;
535
+ background-color: var(--el-fill-color-light) !important;
353
536
  }
354
537
 
355
538
  i {
@@ -359,7 +542,7 @@ export default {
359
542
  </style>
360
543
 
361
544
  <style lang="scss">
362
- .el-dropdown__popper {
545
+ .el-popper.el-dropdown__popper {
363
546
  font-family: Asap;
364
547
  font-size: 14px;
365
548
  font-weight: 400;
@@ -371,16 +554,49 @@ export default {
371
554
  min-width: 180px;
372
555
  width: fit-content;
373
556
 
374
- .el-button + .el-button {
375
- margin: 0;
557
+ .el-button {
558
+ background-color: transparent !important;
559
+ transition: all .25s ease;
560
+
561
+ > span {
562
+ pointer-events: none;
563
+ }
564
+
565
+ &:hover,
566
+ &:focus {
567
+ background-color: transparent !important;
568
+ box-shadow: none !important;
569
+ border: 0 none !important;
570
+ }
571
+
572
+ &:not([disabled]) {
573
+ &:hover,
574
+ &:focus {
575
+ background-color: #f3e6f9 !important;
576
+ }
577
+ }
578
+
579
+ + .el-button {
580
+ margin: 0;
581
+ }
582
+ }
583
+
584
+ // element plus's dropdown max-height has problem
585
+ .el-dropdown__list {
586
+ max-height: 350px;
587
+ overflow-y: auto;
588
+ scrollbar-width: thin;
376
589
  }
377
590
  }
378
591
 
379
- .el-popper.popover-dropdown {
592
+ .el-popover.el-popper.popover-dropdown {
380
593
  padding: 4px 10px;
381
- min-width: max-content;
594
+ width: max-content;
595
+ max-width: 240px;
382
596
  font-family: Asap;
383
597
  font-size: 12px;
598
+ word-break: break-word;
599
+ text-align: left;
384
600
  color: inherit;
385
601
  background: #f3ecf6 !important;
386
602
  border: 1px solid $app-primary-color;