@abi-software/map-side-bar 1.1.2 → 1.1.5-beta-1

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.
@@ -18,7 +18,8 @@
18
18
  @tags-changed="tagsChangedCallback"
19
19
  ></custom-cascader>
20
20
  <div v-if="showFiltersText" class="filter-default-value">
21
- <svg-icon icon="noun-filter" class="filter-icon-inside" />Apply Filters
21
+ <svg-icon icon="noun-filter" class="filter-icon-inside" />Apply
22
+ Filters
22
23
  </div>
23
24
  </span>
24
25
  </transition>
@@ -29,9 +30,14 @@
29
30
  placeholder="10"
30
31
  @change="numberShownChanged($event)"
31
32
  >
32
- <el-option v-for="item in numberDatasetsShown" :key="item" :label="item" :value="item"></el-option>
33
+ <el-option
34
+ v-for="item in numberDatasetsShown"
35
+ :key="item"
36
+ :label="item"
37
+ :value="item"
38
+ ></el-option>
33
39
  </el-select>
34
- <span class="dataset-results-feedback">{{this.numberOfResultsText}}</span>
40
+ <span class="dataset-results-feedback">{{ this.numberOfResultsText }}</span>
35
41
  </div>
36
42
  </template>
37
43
 
@@ -46,30 +52,33 @@ import locale from "element-ui/lib/locale";
46
52
  import speciesMap from "./species-map";
47
53
  import { SvgIcon, SvgSpriteColor } from "@abi-software/svg-sprite";
48
54
 
55
+ import {AlgoliaClient} from "../algolia/algolia.js";
56
+ import { facetPropPathMapping } from "../algolia/utils.js";
57
+
49
58
  Vue.component("svg-icon", SvgIcon);
50
59
 
51
60
  locale.use(lang);
52
61
  Vue.use(Option);
53
62
  Vue.use(Select);
54
63
 
55
- const capitalise = function(txt) {
64
+ const capitalise = function (txt) {
56
65
  return txt.charAt(0).toUpperCase() + txt.slice(1);
57
66
  };
58
67
 
59
- const convertReadableLabel = function(original) {
68
+ const convertReadableLabel = function (original) {
60
69
  const name = original.toLowerCase();
61
- if (speciesMap[name]){
70
+ if (speciesMap[name]) {
62
71
  return capitalise(speciesMap[name]);
63
72
  } else {
64
73
  return capitalise(name);
65
74
  }
66
- }
75
+ };
67
76
 
68
77
  export default {
69
78
  name: "SearchFilters",
70
79
  components: {
71
80
  CustomCascader,
72
- SvgSpriteColor
81
+ SvgSpriteColor,
73
82
  },
74
83
  props: {
75
84
  /**
@@ -77,19 +86,19 @@ export default {
77
86
  * the required viewing.
78
87
  */
79
88
  entry: Object,
80
- apiLocation: {
81
- type: String,
82
- default: ""
83
- }
89
+ envVars: {
90
+ type: Object,
91
+ default: ()=>{}
92
+ },
84
93
  },
85
- data: function() {
94
+ data: function () {
86
95
  return {
87
96
  cascaderIsReady: false,
88
97
  previousShowAllChecked: {
89
98
  species: false,
90
99
  gender: false,
91
100
  organ: false,
92
- datasets: false
101
+ datasets: false,
93
102
  },
94
103
  showFilters: true,
95
104
  showFiltersText: true,
@@ -103,110 +112,91 @@ export default {
103
112
  {
104
113
  value: "Species",
105
114
  label: "Species",
106
- children: [{}]
107
- }
108
- ]
115
+ children: [{}],
116
+ },
117
+ ],
109
118
  };
110
119
  },
111
120
  computed: {
112
- numberOfResultsText: function() {
121
+ numberOfResultsText: function () {
113
122
  return `${this.entry.numberOfHits} results | Showing`;
114
- }
123
+ },
115
124
  },
116
125
  methods: {
117
- createCascaderItemValue: function(term, facet) {
126
+ createCascaderItemValue: function (term, facet) {
118
127
  if (facet) return term + "/" + facet;
119
128
  else return term;
120
129
  },
121
- populateCascader: function() {
122
- return new Promise(resolve => {
123
- this.options = [];
124
- let promiseList = [];
125
- for (let i in this.facets) {
126
- this.options.push({
127
- value: this.createCascaderItemValue(
128
- this.facets[i].toLowerCase(),
129
- undefined
130
- ),
131
- label: convertReadableLabel(this.facets[i]),
132
- children: []
133
- });
134
- promiseList.push(
135
- this.getFacet(this.facets[i]).then(labels => {
136
- // Populate children of each facet with scicrunch's facets
137
- for (let j in labels) {
138
- this.options[i].children.push({
139
- value: this.createCascaderItemValue(
140
- this.facets[i].toLowerCase(),
141
- labels[j].toLowerCase()
142
- ),
143
- label: convertReadableLabel(labels[j]) // Capitalisation is to match design specs
144
- });
145
- }
146
- })
147
- );
148
- }
149
- Promise.allSettled(promiseList).then(() => {
150
- resolve();
151
- });
152
- });
153
- },
154
- getFacet: function(facetLabel) {
155
- if (facetLabel === "Datasets") {
156
- // The datasets facet doesn't exist on SciCrunch yet, so manually set it
157
- // for now.
158
- return new Promise(resolve => {
159
- resolve([...new Set(["Show all", "Scaffolds", "Simulations"])]);
160
- });
161
- }
162
- return new Promise(resolve => {
163
- let facets = ["Show all"]; // Set 'Show all' as our first label
164
- let facet = facetLabel.toLowerCase();
165
- this.callSciCrunch(this.apiLocation, this.facetEndpoint, facet).then(
166
- facet_terms => {
167
- facet_terms.forEach(element => {
168
- facets.push(element["key"]); // add facets that scicrunch includes
130
+ populateCascader: function () {
131
+ return new Promise((resolve) => {
132
+ // Algolia facet serach
133
+ this.algoliaClient.getAlgoliaFacets(facetPropPathMapping)
134
+ .then((data) => {
135
+ this.facets = data;
136
+ this.options = data;
137
+
138
+ // create top level of options in cascader
139
+ this.options.forEach((facet, i) => {
140
+ this.options[i].label = convertReadableLabel(facet.label);
141
+ this.options[i].value = this.createCascaderItemValue(
142
+ facet.key,
143
+ undefined
144
+ );
145
+
146
+ // put "Show all" as first option
147
+ this.options[i].children.unshift({
148
+ value: this.createCascaderItemValue("Show all"),
149
+ label: "Show all",
150
+ });
151
+
152
+ // populate second level of options
153
+ this.options[i].children.forEach((facetItem, j) => {
154
+ this.options[i].children[j].label = convertReadableLabel(
155
+ facetItem.label
156
+ );
157
+ this.options[i].children[j].value =
158
+ this.createCascaderItemValue(facet.label, facetItem.label);
159
+ });
169
160
  });
170
- resolve([...new Set(facets)]); // return no duplicates
171
- }
172
- );
161
+ })
162
+ .finally(() => {
163
+ resolve();
164
+ });
173
165
  });
174
166
  },
175
- // switchTermToRequest is used to remove the count for sending a request to scicrunch
176
- switchTermToRequest: function(term) {
177
- return term.split(" ")[0].toLowerCase();
178
- },
179
- tagsChangedCallback: function(presentTags) {
167
+ tagsChangedCallback: function (presentTags) {
180
168
  if (presentTags.length > 0) {
181
169
  this.showFiltersText = false;
182
170
  } else {
183
171
  this.showFiltersText = true;
184
172
  }
185
173
  },
186
- cascadeEvent: function(event) {
187
- let filters = [];
174
+ // cascadeEvent: initiate searches based off cascader changes
175
+ cascadeEvent: function (event) {
188
176
  if (event) {
189
177
  // Check for show all in selected cascade options
190
178
  event = this.showAllEventModifier(event);
191
- for (let i in event) {
192
- if (event[i] !== undefined) {
193
- let value = event[i][1];
194
- let data = value.split("/");
195
- let output = {};
196
- output.term = this.switchTermToRequest(data[0]);
197
- output.facet = data[1];
198
- filters.push(output);
199
- }
200
- }
179
+
180
+ // Move results from arrays to object
181
+ let filters = event.filter( selection => selection !== undefined).map( fs => ({
182
+ facetPropPath: fs[0],
183
+ facet: fs[1].split("/")[1],
184
+ term: fs[1].split("/")[0],
185
+ }))
186
+
187
+ this.$emit('loading', true) // let sidebarcontent wait for the requests
188
+
189
+ this.$emit("filterResults", filters); // emit filters for apps above sidebar
190
+ this.setCascader(filters); //update our cascader v-model if we modified the event
191
+ this.makeCascadeLabelsClickable();
201
192
  }
202
- this.$emit("filterResults", filters);
203
- this.setCascader(filters); //update our cascader v-model if we modified the event
204
- this.makeCascadeLabelsClickable();
205
193
  },
206
- showAllEventModifier: function(event) {
194
+ // showAllEventModifier: Modifies a cascade event to unlick all selections in category if "show all" is clicked. Also unchecks "Show all" if any secection is clicked
195
+ // *NOTE* Does NOT remove 'Show all' selections from showing in 'cascadeSelected'
196
+ showAllEventModifier: function (event) {
207
197
  // check if show all is in the cascader checked option list
208
198
  let hasShowAll = event
209
- .map(ev => (ev ? ev[1].toLowerCase().includes("show all") : false))
199
+ .map((ev) => (ev ? ev[1].toLowerCase().includes("show all") : false))
210
200
  .includes(true);
211
201
  // remove all selected options below the show all if checked
212
202
  if (hasShowAll) {
@@ -217,7 +207,6 @@ export default {
217
207
  if (facetMaps[event[i][0]] === undefined) facetMaps[event[i][0]] = [];
218
208
  facetMaps[event[i][0]].push(event[i]);
219
209
  }
220
-
221
210
  // go through each facets
222
211
  for (const facet in facetMaps) {
223
212
  let showAll = undefined;
@@ -244,59 +233,96 @@ export default {
244
233
  modifiedEvent.push(...facetMaps[facet]);
245
234
  }
246
235
  }
247
- return modifiedEvent;
236
+ //Make sure the expanded item are sorted first.
237
+ return modifiedEvent.sort((a, b) => {
238
+ if (this.__expandItem__) {
239
+ if (a[0] == this.__expandItem__) {
240
+ if (b[0] == this.__expandItem__) {
241
+ return 0;
242
+ } else {
243
+ return -1;
244
+ }
245
+ } else if (b[0] == this.__expandItem__) {
246
+ if (a[0] == this.__expandItem__) {
247
+ return 0;
248
+ } else {
249
+ return 1;
250
+ }
251
+ } else {
252
+ return 0;
253
+ }
254
+ } else return 0;
255
+ });
248
256
  }
249
257
  return event;
250
258
  },
251
- cascadeExpandChange: function() {
259
+ cascadeExpandChange: function (event) {
260
+ //work around as the expand item may change on modifying the cascade props
261
+ this.__expandItem__ = event;
252
262
  this.makeCascadeLabelsClickable();
253
263
  },
254
- numberShownChanged: function(event) {
264
+ numberShownChanged: function (event) {
255
265
  this.$emit("numberPerPage", parseInt(event));
256
266
  },
257
- callSciCrunch: function(apiLocation, endpoint, term) {
258
- return new Promise(resolve => {
259
- fetch(apiLocation + endpoint + term)
260
- .then(response => response.json())
261
- .then(data => {
262
- resolve(data);
263
- });
264
- });
265
- },
266
- updatePreviousShowAllChecked: function(options) {
267
+ updatePreviousShowAllChecked: function (options) {
267
268
  //Reset the states
268
269
  for (const facet in this.previousShowAllChecked) {
269
270
  this.previousShowAllChecked[facet] = false;
270
271
  }
271
- options.forEach(element => {
272
+ options.forEach((element) => {
272
273
  if (element[1].toLowerCase().includes("show all"))
273
274
  this.previousShowAllChecked[element[0]] = true;
274
275
  });
275
276
  },
276
- setCascader: function(filterFacets) {
277
+ // setCascader: Clears previous selections and takes in an array of facets to select: filterFacets
278
+ // facets are in the form:
279
+ // {
280
+ // facetPropPath: 'anatomy.organ.name',
281
+ // term: 'Sex',
282
+ // facet: 'Male'
283
+ // }
284
+ setCascader: function (filterFacets) {
277
285
  //Do not set the value unless it is ready
278
- if (this.cascaderIsReady) {
279
- this.cascadeSelected = [];
280
- filterFacets.forEach(e => {
281
- this.cascadeSelected.push([
282
- e.term.toLowerCase(),
283
- this.createCascaderItemValue(
284
- e.term.toLowerCase(),
285
- e.facet.toLowerCase()
286
- )
287
- ]);
286
+ if (this.cascaderIsReady && filterFacets && filterFacets.length != 0) {
287
+ this.cascadeSelected = filterFacets.map(e => {
288
+ return [
289
+ e.facetPropPath,
290
+ this.createCascaderItemValue(capitalise(e.term), e.facet),
291
+ ]
288
292
  });
289
293
  this.updatePreviousShowAllChecked(this.cascadeSelected);
290
294
  }
291
295
  },
292
- makeCascadeLabelsClickable: function() {
296
+ addFilter: function (filter) {
297
+ //Do not set the value unless it is ready
298
+ if (this.cascaderIsReady && filter) {
299
+ this.cascadeSelected.filter(f=>f.term != filter.term)
300
+ this.cascadeSelected.push([filter.facetPropPath, this.createCascaderItemValue(filter.term, filter.facet)])
301
+ }
302
+ },
303
+ initiateSearch: function() {
304
+ this.cascadeEvent(this.cascadeSelected)
305
+ },
306
+ // checkShowAllBoxes: Checks each 'Show all' cascade option by using the setCascader function
307
+ checkShowAllBoxes: function(){
308
+ this.setCascader(
309
+ this.options.map(option => {
310
+ return {
311
+ facetPropPath: option.value,
312
+ term: option.label,
313
+ facet: 'Show all'
314
+ }
315
+ })
316
+ )
317
+ },
318
+ makeCascadeLabelsClickable: function () {
293
319
  // Next tick allows the cascader menu to change
294
320
  this.$nextTick(() => {
295
321
  this.$refs.cascader.$el
296
322
  .querySelectorAll(".el-cascader-node__label")
297
- .forEach(el => {
323
+ .forEach((el) => {
298
324
  // step through each cascade label
299
- el.onclick = function() {
325
+ el.onclick = function () {
300
326
  const checkbox = this.previousElementSibling;
301
327
  if (checkbox) {
302
328
  if (!checkbox.parentElement.attributes["aria-owns"]) {
@@ -307,19 +333,18 @@ export default {
307
333
  };
308
334
  });
309
335
  });
310
- }
311
- },
312
- created: function() {
313
- //Create non-reactive local variables
314
- this.facetEndpoint = "get-facets/";
336
+ },
315
337
  },
316
- mounted: function() {
338
+ mounted: function () {
339
+ this.algoliaClient = new AlgoliaClient(this.envVars.ALGOLIA_ID, this.envVars.ALGOLIA_KEY, this.envVars.PENNSIEVE_API_LOCATION);
340
+ this.algoliaClient.initIndex(this.envVars.ALGOLIA_INDEX);
317
341
  this.populateCascader().then(() => {
318
342
  this.cascaderIsReady = true;
343
+ this.checkShowAllBoxes()
319
344
  this.setCascader(this.entry.filterFacets);
320
345
  this.makeCascadeLabelsClickable();
321
346
  });
322
- }
347
+ },
323
348
  };
324
349
  </script>
325
350
 
@@ -355,6 +380,10 @@ export default {
355
380
  padding-bottom: 6px;
356
381
  }
357
382
 
383
+ .cascader >>> .el-cascder-panel {
384
+ max-height: 500px;
385
+ }
386
+
358
387
  .cascader >>> .el-scrollbar__wrap {
359
388
  overflow-x: hidden;
360
389
  margin-bottom: 2px !important;
@@ -24,7 +24,7 @@
24
24
  <template v-for="tab in tabs">
25
25
  <sidebar-content class="sidebar-content-container"
26
26
  v-show="tab.id===activeId" :contextCardEntry="tab.contextCard"
27
- :firstSearch="tab.title" :apiLocation="apiLocation"
27
+ :envVars="envVars"
28
28
  v-bind:key="tab.id" :ref="tab.id"
29
29
  @search-changed="searchChanged(tab.id, $event)"/>
30
30
  </template>
@@ -81,9 +81,9 @@ export default {
81
81
  type: Object,
82
82
  default: () => (initial_state)
83
83
  },
84
- apiLocation: {
85
- type: String,
86
- default: ""
84
+ envVars: {
85
+ type: Object,
86
+ default: () => {}
87
87
  },
88
88
  tabs: {
89
89
  type: Array,
@@ -113,10 +113,15 @@ export default {
113
113
  toggleDrawer: function () {
114
114
  this.drawerOpen = !this.drawerOpen;
115
115
  },
116
- openSearch: function(term, facets){
116
+ openSearch: function(facets, query){
117
+ this.drawerOpen = true;
118
+ // Because refs are in v-for, nextTick is needed here
119
+ Vue.nextTick(()=>{this.$refs[this.activeId][0].openSearch(facets, query)})
120
+ },
121
+ addFilter: function(filter){
117
122
  this.drawerOpen = true;
118
123
  // Because refs are in v-for, nextTick is needed here
119
- Vue.nextTick(()=>{this.$refs[this.activeId][0].openSearch(term, facets)})
124
+ Vue.nextTick(()=>{this.$refs[this.activeId][0].addFilter(filter)})
120
125
  },
121
126
  openNeuronSearch: function(neuron){
122
127
  this.drawerOpen = true;