@abi-software/map-side-bar 2.5.2 → 2.5.3-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-side-bar",
3
- "version": "2.5.2",
3
+ "version": "2.5.3-beta.0",
4
4
  "files": [
5
5
  "dist/*",
6
6
  "src/*",
@@ -469,7 +469,14 @@ export default {
469
469
  facetSubPropPath: facetSubPropPath, // will be used for filters if we are at the third level of the cascader
470
470
  }
471
471
  })
472
-
472
+
473
+ // if all checkboxes are checked
474
+ // there has no filter values
475
+ const filtersLength = filters.filter((item) => item.facet !== 'Show all');
476
+ if (!filtersLength.length) {
477
+ filters = [];
478
+ }
479
+
473
480
  this.$emit('loading', true) // let sidebarcontent wait for the requests
474
481
  this.$emit('filterResults', filters) // emit filters for apps above sidebar
475
482
  this.setCascader(filterKeys) //update our cascader v-model if we modified the event
@@ -631,7 +638,7 @@ export default {
631
638
  let filters = createFilter(e)
632
639
  return filters
633
640
  })
634
-
641
+
635
642
  // Unforttunately the cascader is very particular about it's v-model
636
643
  // to get around this we create a clone of it and use this clone for adding our boolean information
637
644
  this.cascadeSelectedWithBoolean = filterFacets.map((e) => {
@@ -1,33 +1,84 @@
1
1
  <template>
2
- <div class="history-container">
3
- <!-- <span v-if="reversedSearchHistory.length > 0" class="title"> Search History </span> -->
4
- <template v-for="(item, i) in reversedSearchHistory">
5
- <el-tag
6
- class="search-tag"
7
- v-if="i < 3"
8
- v-bind:key="i"
9
- @click="search(item)"
10
- size="large"
11
- >
12
- {{ item.search }}
13
- </el-tag>
14
- </template>
15
- <el-select
16
- v-if="reversedSearchHistory.length > 0"
17
- :model-value="selectValue"
18
- class="m-2 search-select"
19
- placeholder="Full search History"
20
- popper-class="sidebar-search-select-popper"
21
- @change="selectChange"
22
- :teleported="false"
23
- >
24
- <el-option
25
- v-for="(item, i) in cascaderOptions"
26
- :key="i"
27
- :label="item.label"
28
- :value="item.value"
29
- />
30
- </el-select>
2
+ <div class="history-container" v-if="searchHistory.length">
3
+ <div class="saved-search-history" v-if="savedSearchHistory.length">
4
+ <template v-for="(item, i) in savedSearchHistory">
5
+ <el-tag
6
+ class="search-tag"
7
+ v-if="i < 2"
8
+ v-bind:key="i"
9
+ @click="search(item)"
10
+ size="large"
11
+ >
12
+ {{ item.label }}
13
+ </el-tag>
14
+ </template>
15
+ </div>
16
+ <div v-else>
17
+ <span class="empty-saved-search">No Saved Searches</span>
18
+ </div>
19
+ <el-dropdown trigger="click" :max-height="350" :hide-on-click="false">
20
+ <span class="el-dropdown-select">
21
+ Search history
22
+ <el-icon class="el-icon--right">
23
+ <el-icon-arrow-down />
24
+ </el-icon>
25
+ </span>
26
+ <template #dropdown>
27
+ <el-dropdown-menu>
28
+ <el-dropdown-item v-for="(item, i) in searchHistory">
29
+ <div>{{ item.label }}</div>
30
+ <div>
31
+ <el-popover
32
+ width="auto"
33
+ trigger="hover"
34
+ :teleported="true"
35
+ :show-after="100"
36
+ popper-class="popover-dropdown"
37
+ >
38
+ <template #reference>
39
+ <el-button circle text size="small"
40
+ @click="toggleSavedSearch(item)"
41
+ :disabled="savedSearchHistory.length > 1 && !item.saved"
42
+ >
43
+ <el-icon color="#8300BF">
44
+ <el-icon-star-filled v-if="item.saved" />
45
+ <el-icon-star v-else />
46
+ </el-icon>
47
+ </el-button>
48
+ </template>
49
+ <span v-if="savedSearchHistory.length > 1 && !item.saved">
50
+ Please unsave one before adding another.
51
+ </span>
52
+ <span v-else-if="item.saved">
53
+ Remove from saved searches.
54
+ </span>
55
+ <span v-else>
56
+ Add up to two saved searches.
57
+ </span>
58
+ </el-popover>
59
+ <el-popover
60
+ width="auto"
61
+ trigger="hover"
62
+ :teleported="true"
63
+ :show-after="100"
64
+ popper-class="popover-dropdown"
65
+ >
66
+ <template #reference>
67
+ <el-button circle text size="small" @click="removeFromSavedSearch(item)">
68
+ <el-icon color="#8300BF">
69
+ <el-icon-delete />
70
+ </el-icon>
71
+ </el-button>
72
+ </template>
73
+ <span>
74
+ Remove from search history.
75
+ </span>
76
+ </el-popover>
77
+ </div>
78
+ </el-dropdown-item>
79
+ </el-dropdown-menu>
80
+ </template>
81
+ </el-dropdown>
31
82
  </div>
32
83
  </template>
33
84
 
@@ -35,7 +86,9 @@
35
86
  /* eslint-disable no-alert, no-console */
36
87
  import {
37
88
  ElTag as Tag,
38
- ElSelect as Select
89
+ ElSelect as Select,
90
+ ElDropdown as Dropdown,
91
+ ElIcon as Icon,
39
92
  } from 'element-plus'
40
93
 
41
94
  import EventBus from './EventBus.js'
@@ -47,6 +100,20 @@ const removeDuplicates = function (arrayOfAnything) {
47
100
  )
48
101
  }
49
102
 
103
+ function generateUUID() {
104
+ const arr = new Uint8Array(16);
105
+ window.crypto.getRandomValues(arr);
106
+
107
+ arr[6] = (arr[6] & 0x0f) | 0x40;
108
+ arr[8] = (arr[8] & 0x3f) | 0x80;
109
+
110
+ const hex = Array.from(arr)
111
+ .map(byte => byte.toString(16).padStart(2, '0'))
112
+ .join('');
113
+
114
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
115
+ }
116
+
50
117
  export default {
51
118
  name: 'SearchHistory',
52
119
  components: {
@@ -56,26 +123,16 @@ export default {
56
123
  data() {
57
124
  return {
58
125
  searchHistory: [],
59
- selectValue: 'Full search history',
126
+ savedSearchHistory: [],
60
127
  }
61
128
  },
62
- computed: {
63
- reversedSearchHistory: function () {
64
- return removeDuplicates(
65
- this.searchHistory
66
- .slice()
67
- .reverse()
68
- .filter((item) => item.search !== '')
69
- )
70
- },
71
- cascaderOptions: function () {
72
- return this.reversedSearchHistory.map((item) => {
73
- return {
74
- value: item.search,
75
- label: item.search,
76
- }
77
- })
78
- },
129
+ mounted: function () {
130
+ this.getSearchHistory()
131
+ EventBus.on('search-changed', (data) => {
132
+ this.setSearchHistory(data)
133
+ });
134
+ this.updateSearchHistory();
135
+ this.savedSearchHistory = this.searchHistory.filter((item) => item.saved);
79
136
  },
80
137
  methods: {
81
138
  getSearchHistory() {
@@ -91,58 +148,132 @@ export default {
91
148
  localStorage.removeItem('sparc.science-sidebar-search-history')
92
149
  this.searchHistory = []
93
150
  },
94
- addSearchToHistory(filters, search) {
95
- filters = [] // disable filters for now
151
+ addSearchToHistory(filters = [], search) {
96
152
  search = search.trim() // remove whitespace
97
153
  let searchHistory = JSON.parse(
98
154
  localStorage.getItem('sparc.science-sidebar-search-history')
99
155
  )
100
156
  if (searchHistory) {
101
- searchHistory.push({ filters: filters, search: search })
157
+ searchHistory.push({
158
+ filters: filters,
159
+ search: search,
160
+ saved: false,
161
+ label: this.searchHistoryItemLabel(search, filters),
162
+ id: generateUUID(),
163
+ });
102
164
  this.searchHistory = removeDuplicates(searchHistory)
103
165
  localStorage.setItem(
104
166
  'sparc.science-sidebar-search-history',
105
- JSON.stringify(searchHistory)
167
+ JSON.stringify(this.searchHistory)
106
168
  )
107
169
  } else {
108
170
  localStorage.setItem(
109
171
  'sparc.science-sidebar-search-history',
110
- JSON.stringify([{ filters: filters, search: search }])
172
+ JSON.stringify([{
173
+ filters: filters,
174
+ search: search,
175
+ saved: false,
176
+ label: this.searchHistoryItemLabel(search, filters),
177
+ id: generateUUID(),
178
+ }])
111
179
  )
112
180
  }
113
181
  },
182
+ updateSearchHistory: function () {
183
+ this.searchHistory.forEach((item) => {
184
+ if (!item.id) {
185
+ item['id'] = generateUUID();
186
+ }
187
+ if (!item.label) {
188
+ item['label'] = this.searchHistoryItemLabel(item.search, item.filters);
189
+ }
190
+ if (!item.saved) {
191
+ item['saved'] = false;
192
+ }
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;
200
+ }
201
+ return 0;
202
+ });
203
+ localStorage.setItem(
204
+ 'sparc.science-sidebar-search-history',
205
+ JSON.stringify(this.searchHistory)
206
+ )
207
+ },
114
208
  search: function (item) {
115
209
  this.$emit('search', item)
116
210
  },
117
- selectChange: function (value) {
118
- this.selectValue = value
119
- this.search({ search: value })
211
+ searchHistoryItemLabel: function (search, filters) {
212
+ let label = search;
213
+ let filterItems = [];
214
+
215
+ if (filters) {
216
+ filterItems = filters.filter((filterItem) => filterItem.facet !== 'Show all');
217
+ }
218
+
219
+ if (!label) {
220
+ label = filterItems.length ? filterItems[0].facet : 'Unknown search';
221
+ }
222
+
223
+ return label;
224
+ },
225
+ toggleSavedSearch: function (item) {
226
+ this.searchHistory.forEach((_item) => {
227
+ if (_item.id === item.id) {
228
+ _item.saved = !_item.saved;
229
+ }
230
+ });
231
+ this.savedSearchHistory = this.searchHistory.filter((item) => item.saved);
232
+ this.updateSearchHistory();
233
+ },
234
+ removeFromSavedSearch: function (item) {
235
+ const itemIndex = this.searchHistory.findIndex((_item) => _item.id === item.id);
236
+ this.searchHistory.splice(itemIndex, 1);
237
+ this.savedSearchHistory = this.searchHistory.filter((item) => item.saved);
238
+ this.updateSearchHistory();
120
239
  },
121
- },
122
- mounted: function () {
123
- this.getSearchHistory()
124
- EventBus.on('search-changed', (data) => {
125
- this.setSearchHistory(data)
126
- })
127
240
  },
128
241
  }
129
242
  </script>
130
243
 
131
244
  <style lang="scss" scoped>
132
245
  .history-container {
133
- padding-bottom: 3px;
246
+ display: flex;
247
+ align-items: center;
248
+ justify-content: space-between;
249
+ padding-bottom: 6px;
250
+ }
251
+
252
+ .empty-saved-search {
253
+ font-style: italic;
254
+ font-size: 14px;
255
+ font-weight: 400;
256
+ color: var(--el-text-color-secondary);
257
+ }
258
+
259
+ .saved-search-history {
260
+ display: flex;
261
+ align-items: center;
262
+ gap: 6px;
134
263
  }
135
264
 
136
265
  .search-tag.el-tag {
137
- margin: 0 5px 5px 0;
266
+ margin: 0;
138
267
  cursor: pointer;
139
- max-width: 100px;
140
- overflow: hidden;
141
- text-overflow: ellipsis;
142
- float: left;
143
268
  background: #f9f2fc!important;
144
269
  border-color: $app-primary-color!important;
145
270
  color:$app-primary-color!important;
271
+
272
+ .el-tag__content {
273
+ max-width: 15ch;
274
+ overflow: hidden;
275
+ text-overflow: ellipsis;
276
+ }
146
277
  }
147
278
 
148
279
  .title {
@@ -154,22 +285,110 @@ export default {
154
285
  align-items: center;
155
286
  }
156
287
 
157
- .search-select {
158
- float: right;
288
+ .el-dropdown {
159
289
  width: 180px;
290
+ position: relative;
291
+ box-sizing: border-box;
292
+ cursor: pointer;
293
+ text-align: left;
294
+ font-size: 14px;
295
+ padding: 4px 12px;
296
+ min-height: 32px;
297
+ line-height: 24px;
298
+ border-radius: var(--el-border-radius-base);
299
+ background-color: var(--el-fill-color-blank);
300
+ transition: var(--el-transition-duration);
301
+ transform: translateZ(0);
302
+ box-shadow: 0 0 0 1px var(--el-border-color) inset;
303
+ cursor: pointer;
304
+ }
305
+
306
+ .el-dropdown-select {
307
+ width: 100%;
308
+ display: flex;
309
+ align-items: center;
310
+ justify-content: space-between;
311
+ gap: 1rem;
312
+
313
+ .el-icon {
314
+ transform: rotate(0deg);
315
+ transition: transform 0.25s linear;
316
+ }
317
+
318
+ &[aria-expanded="true"] {
319
+ .el-icon {
320
+ transform: rotate(180deg);
321
+ }
322
+ }
323
+ }
324
+
325
+ :deep(.el-dropdown-menu__item) {
326
+ justify-content: space-between;
327
+ gap: 0.5rem;
328
+ cursor: default;
329
+ position: relative;
330
+
331
+ > div:first-child {
332
+ max-width: 148px;
333
+ text-overflow: ellipsis;
334
+ overflow: hidden;
335
+ }
336
+
337
+ + .el-dropdown-menu__item {
338
+ &::before {
339
+ content: "";
340
+ display: block;
341
+ width: calc(100% - 32px);
342
+ border-top: 1px solid var(--el-border-color);
343
+ position: absolute;
344
+ top: 0;
345
+ }
346
+ }
347
+
348
+ &:hover,
349
+ &:focus,
350
+ &:active {
351
+ color: inherit;
352
+ background-color: var(--el-bg-color-page);
353
+ }
354
+
355
+ i {
356
+ margin: 0;
357
+ }
160
358
  }
161
359
  </style>
162
360
 
163
361
  <style lang="scss">
164
- .sidebar-search-select-popper {
362
+ .el-dropdown__popper {
165
363
  font-family: Asap;
166
364
  font-size: 14px;
167
- font-weight: 500;
365
+ font-weight: 400;
168
366
  font-stretch: normal;
169
367
  font-style: normal;
170
368
  line-height: normal;
171
369
  letter-spacing: normal;
172
370
  color: #292b66;
173
- max-width: 200px;
371
+ min-width: 180px;
372
+ width: fit-content;
373
+
374
+ .el-button + .el-button {
375
+ margin: 0;
376
+ }
377
+ }
378
+
379
+ .el-popper.popover-dropdown {
380
+ padding: 4px 10px;
381
+ min-width: max-content;
382
+ font-family: Asap;
383
+ font-size: 12px;
384
+ color: inherit;
385
+ background: #f3ecf6 !important;
386
+ border: 1px solid $app-primary-color;
387
+
388
+ & .el-popper__arrow::before {
389
+ border: 1px solid;
390
+ border-color: $app-primary-color;
391
+ background: #f3ecf6;
392
+ }
174
393
  }
175
394
  </style>
@@ -95,6 +95,7 @@ var handleErrors = async function (response) {
95
95
  }
96
96
 
97
97
  var initial_state = {
98
+ filters: [],
98
99
  searchInput: '',
99
100
  lastSearch: '',
100
101
  results: [],
@@ -226,28 +227,33 @@ export default {
226
227
  this.searchInput = ''
227
228
  this.resetPageNavigation()
228
229
  this.searchAlgolia(this.filters, this.searchInput)
229
- this.$refs.searchHistory.selectValue = 'Full search history'
230
+ this.$refs.searchHistory.selectValue = 'Search history'
230
231
  },
231
232
  searchEvent: function (event = false) {
232
233
  if (event.keyCode === 13 || event instanceof MouseEvent) {
233
- this.resetPageNavigation()
234
- this.searchAlgolia(this.filters, this.searchInput)
235
- this.$refs.searchHistory.selectValue = 'Full search history'
236
- this.$refs.searchHistory.addSearchToHistory(
237
- this.filters,
238
- this.searchInput
239
- )
234
+ this.searchAndFilterUpdate();
240
235
  }
241
236
  },
242
237
  filterUpdate: function (filters) {
243
238
  this.filters = [...filters]
244
- this.resetPageNavigation()
245
- this.searchAlgolia(filters, this.searchInput)
239
+ this.searchAndFilterUpdate();
246
240
  this.$emit('search-changed', {
247
241
  value: filters,
248
242
  type: 'filter-update',
249
243
  })
250
244
  },
245
+ searchAndFilterUpdate: function () {
246
+ this.resetPageNavigation();
247
+ this.searchAlgolia(this.filters, this.searchInput);
248
+ this.$refs.searchHistory.selectValue = 'Search history';
249
+ // save history only if there has value
250
+ if (this.filters.length || this.searchInput?.trim()) {
251
+ this.$refs.searchHistory.addSearchToHistory(
252
+ this.filters,
253
+ this.searchInput
254
+ );
255
+ }
256
+ },
251
257
  searchAlgolia(filters, query = '') {
252
258
  // Algolia search
253
259
 
@@ -16,10 +16,17 @@ declare module 'vue' {
16
16
  ElCascader: typeof import('element-plus/es')['ElCascader']
17
17
  ElCol: typeof import('element-plus/es')['ElCol']
18
18
  ElDrawer: typeof import('element-plus/es')['ElDrawer']
19
+ ElDropdown: typeof import('element-plus/es')['ElDropdown']
20
+ ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
21
+ ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
19
22
  ElIcon: typeof import('element-plus/es')['ElIcon']
23
+ ElIconArrowDown: typeof import('@element-plus/icons-vue')['ArrowDown']
20
24
  ElIconArrowLeft: typeof import('@element-plus/icons-vue')['ArrowLeft']
21
25
  ElIconArrowRight: typeof import('@element-plus/icons-vue')['ArrowRight']
26
+ ElIconDelete: typeof import('@element-plus/icons-vue')['Delete']
22
27
  ElIconLocation: typeof import('@element-plus/icons-vue')['Location']
28
+ ElIconStar: typeof import('@element-plus/icons-vue')['Star']
29
+ ElIconStarFilled: typeof import('@element-plus/icons-vue')['StarFilled']
23
30
  ElIconWarning: typeof import('@element-plus/icons-vue')['Warning']
24
31
  ElIconWarnTriangleFilled: typeof import('@element-plus/icons-vue')['WarnTriangleFilled']
25
32
  ElInput: typeof import('element-plus/es')['ElInput']