henry-search 1.0.30 → 1.0.32

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.
@@ -1,470 +1,483 @@
1
1
  <script setup>
2
- import { ref, onMounted, useTemplateRef } from 'vue'
3
- import _ from 'lodash';
4
- import {
5
- AisClearRefinements,
6
- AisCurrentRefinements,
7
- AisHits,
8
- AisInstantSearch,
9
- AisMenuSelect,
10
- AisPagination,
11
- AisRangeInput,
12
- AisRefinementList,
13
- AisSearchBox,
14
- AisStats
15
- } from 'vue-instantsearch/vue3/es'
16
- import TypesenseInstantSearchAdapter from 'typesense-instantsearch-adapter'
17
- import { history as historyRouter } from 'instantsearch.js/es/lib/routers'
18
- import { simple as simpleStateMapping } from 'instantsearch.js/es/lib/stateMappings'
19
- import VueDatePicker from '@vuepic/vue-datepicker'
20
-
21
- import slugify from '../composables/slugify'
22
- import PAccordion from './PAccordion.vue'
23
- import formatDate from '../composables/formatDate'
24
- import formatSearchTitle from '../composables/formatSearchTitle'
25
-
26
-
27
- import pSelect from '@vueform/multiselect'
28
- import { truncate } from 'lodash';
2
+ import { useTemplateRef, ref, defineEmits } from 'vue'
3
+ import _ from 'lodash';
4
+ import {
5
+ AisClearRefinements,
6
+ AisCurrentRefinements,
7
+ AisHits,
8
+ AisInstantSearch,
9
+ AisMenuSelect,
10
+ AisPagination,
11
+ AisRangeInput,
12
+ AisRefinementList,
13
+ AisSearchBox,
14
+ AisStats
15
+ } from 'vue-instantsearch/vue3/es'
16
+ import TypesenseInstantSearchAdapter from 'typesense-instantsearch-adapter'
17
+ import { history as historyRouter } from 'instantsearch.js/es/lib/routers'
18
+ import { simple as simpleStateMapping } from 'instantsearch.js/es/lib/stateMappings'
19
+ import VueDatePicker from '@vuepic/vue-datepicker'
20
+ import { useDebounceFn } from "@vueuse/core"
21
+
22
+ import slugify from '../composables/slugify'
23
+ import PAccordion from './PAccordion.vue'
24
+ import formatDate from '../composables/formatDate'
25
+ import formatSearchTitle from '../composables/formatSearchTitle'
29
26
 
30
- const props = defineProps({
31
- indexName: {
32
- type: String,
33
- default: "archived_performances"
34
- },
35
- mainRefinements: {
36
- type: Object,
37
- require: true
38
- },
39
- addlRefinements: {
40
- type: Object,
41
- default: null
42
- },
43
- queryByFields: {
44
- type: String,
45
- default: "works, season, venue, event_types, notes, event_title, ensembles, conductors"
46
- },
47
- includeFields: {
48
- type: String,
49
- default: "works, season, venue, event_types, notes, event_title, orchestras, conductors"
50
- },
51
- sortField: {
52
- type: String,
53
- default: "performance_date"
54
- },
55
- searchPlaceholder: {
56
- type: String,
57
- default: "Search for performances..."
58
- },
59
- resultsTitle: {
60
- type: String,
61
- default: "Performances"
62
- },
63
- searchKey: {
64
- type: String,
65
- require: true
66
- },
67
- searchHost: {
68
- type: String,
69
- require: true
70
- }
71
- })
27
+ const props = defineProps({
28
+ indexName: {
29
+ type: String,
30
+ default: "archived_performances"
31
+ },
32
+ mainRefinements: {
33
+ type: Object,
34
+ require: true
35
+ },
36
+ addlRefinements: {
37
+ type: Object,
38
+ default: null
39
+ },
40
+ queryByFields: {
41
+ type: String,
42
+ default: "works, season, venue, event_types, notes, event_title, ensembles, conductors"
43
+ },
44
+ includeFields: {
45
+ type: String,
46
+ default: "works, season, venue, event_types, notes, event_title, orchestras, conductors"
47
+ },
48
+ sortField: {
49
+ type: String,
50
+ default: "performance_date"
51
+ },
52
+ searchPlaceholder: {
53
+ type: String,
54
+ default: "Search for performances..."
55
+ },
56
+ resultsTitle: {
57
+ type: String,
58
+ default: "Performances"
59
+ },
60
+ searchKey: {
61
+ type: String,
62
+ require: true
63
+ },
64
+ searchHost: {
65
+ type: String,
66
+ require: true
67
+ }
68
+ })
72
69
 
73
- const sortView = ref('Most Recent')
74
- const showNumHits = ref(false)
75
- const showByWorks = ref(false)
76
- const workFilters = ref(null)
77
- const currentQuery = ref(null)
78
- const filtersClosed = ref(false)
79
- const mobileFiltersClosed = ref(true)
80
-
81
- const updateNow = ref(0)
70
+ const sortView = ref('Most Recent')
71
+ const showNumHits = ref(false)
72
+ const showByWorks = ref(false)
73
+ const workFilters = ref(null)
74
+ const currentQuery = ref(null)
75
+ const filtersClosed = ref(false)
76
+ const mobileFiltersClosed = ref(true)
82
77
 
83
- const routing = ref({
84
- router: historyRouter({
85
- // Disable scroll restoration to prevent erratic behavior
86
- writeDelay: 0,
87
- parseURL({ qsModule, location }) {
88
- const uiState = qsModule.parse(location.search.slice(1))
78
+ const updateNow = ref(0)
89
79
 
90
- updateStateRefs(uiState)
91
-
92
- return uiState
93
- },
94
- createURL({ qsModule, location, routeState }) {
95
- const { origin, pathname, hash } = location;
96
- const indexState = routeState["instant_search"] || {};
97
- const queryString = qsModule.stringify(routeState);
98
-
99
- const uiState = qsModule.parse(location.search.slice(1))
100
- sessionStorage.setItem('previousPageUrl', window.location.href)
101
-
102
- updateSearchHistory(uiState)
80
+ const emit = defineEmits(['push'])
103
81
 
104
- // if (!indexState.query) {
105
- // return `${origin}${pathname}${hash}`;
106
- // }
82
+ const routing = ref({
83
+ router: historyRouter({
84
+ // Disable scroll restoration to prevent erratic behavior
85
+ writeDelay: 0,
86
+ parseURL({ qsModule, location }) {
87
+ const uiState = qsModule.parse(location.search.slice(1))
107
88
 
108
- return `${origin}${pathname}?${queryString}${hash}`;
109
- },
110
- }),
111
-
112
- stateMapping: simpleStateMapping()
113
- })
89
+ updateStateRefs(uiState)
90
+
91
+ return uiState
92
+ },
93
+ createURL({ qsModule, location, routeState }) {
94
+ const { origin, pathname, hash } = location;
95
+ const indexState = routeState["instant_search"] || {};
96
+ const queryString = qsModule.stringify(routeState);
97
+
98
+ const uiState = qsModule.parse(location.search.slice(1))
99
+ sessionStorage.setItem('previousPageUrl', window.location.href)
100
+
101
+ updateSearchHistory(uiState)
114
102
 
115
- const mainRefinementList = ref({})
116
-
117
- const server = {
118
- connectionTimeoutSeconds: 20,
119
- apiKey: props.searchKey,
120
- nodes: [
121
- {
122
- host: props.searchHost,
123
- path: '',
124
- port: '443',
125
- protocol: 'https',
103
+ // if (!indexState.query) {
104
+ // return `${origin}${pathname}${hash}`;
105
+ // }
106
+
107
+ return `${origin}${pathname}?${queryString}${hash}`;
126
108
  },
127
- ],
128
- cacheSearchResultsForSeconds: 0,
109
+ }),
110
+
111
+ stateMapping: simpleStateMapping()
112
+ })
113
+
114
+ const mainRefinementList = ref({})
115
+ const refinementSearchBoxes = ref([])
116
+
117
+ const server = {
118
+ connectionTimeoutSeconds: 20,
119
+ apiKey: props.searchKey,
120
+ nodes: [
121
+ {
122
+ host: props.searchHost,
123
+ path: '',
124
+ port: '443',
125
+ protocol: 'https',
126
+ },
127
+ ],
128
+ cacheSearchResultsForSeconds: 0,
129
+ }
130
+ const adapter = new TypesenseInstantSearchAdapter({
131
+ server: server,
132
+ additionalSearchParameters: {
133
+ query_by: props.queryByFields,
134
+ sort_by: `${props.sortField}:desc`,
135
+ include_fields: props.includeFields,
136
+ highlight_fields: 'none'
137
+ },
138
+ })
139
+
140
+ const searchClient = adapter.searchClient
141
+
142
+ const toggleFilters = () => {
143
+ filtersClosed.value = !filtersClosed.value
144
+ const wrapper = document.getElementById(`${props.indexName}_filterRail`)
145
+ const allNestedElements = wrapper.querySelectorAll("*")
146
+ if (wrapper && wrapper.classList.contains('closed')) {
147
+ allNestedElements.forEach((el) => {
148
+ el.setAttribute("tabindex", -1)
149
+ })
150
+ } else {
151
+ allNestedElements.forEach((el) => {
152
+ el.removeAttribute("tabindex")
153
+ })
129
154
  }
130
- const adapter = new TypesenseInstantSearchAdapter({
131
- server: server,
132
- additionalSearchParameters: {
133
- query_by: props.queryByFields,
134
- sort_by: `${props.sortField}:desc`,
135
- include_fields: props.includeFields,
136
- highlight_fields: 'none'
137
- },
155
+
156
+ }
157
+
158
+ const toggleFiltersMobile = () => {
159
+ mobileFiltersClosed.value = !mobileFiltersClosed.value
160
+ const wrapper = document.getElementById(`${props.indexName}_filterRail`)
161
+ const leftPane = document.getElementById(`${props.indexName}_eventsSearch__results`)
162
+ const containers = document.querySelectorAll('.container')
163
+ const otherHiddenEls = document.querySelectorAll('.mobileHide')
164
+ wrapper.classList.remove('closed')
165
+ wrapper.classList.toggle('openMobile')
166
+ leftPane.classList.toggle('openMobile')
167
+ containers.forEach((el) => {
168
+ if (el.style.display != "none") {
169
+ el.style.display = "none"
170
+ } else {
171
+ el.style.display = "grid"
172
+ }
173
+
138
174
  })
139
-
140
- const searchClient = adapter.searchClient
141
-
142
- const toggleFilters = () => {
143
- filtersClosed.value = !filtersClosed.value
144
- const wrapper = document.getElementById(`${props.indexName}_filterRail`)
145
- const allNestedElements = wrapper.querySelectorAll("*")
146
- if (wrapper && wrapper.classList.contains('closed')) {
147
- allNestedElements.forEach((el) => {
148
- el.setAttribute("tabindex", -1)
149
- })
175
+ otherHiddenEls.forEach((el) => {
176
+ if (el.style.display != "none") {
177
+ el.style.display = "none"
150
178
  } else {
151
- allNestedElements.forEach((el) => {
152
- el.removeAttribute("tabindex")
153
- })
179
+ el.style.display = "grid"
154
180
  }
155
181
 
156
- }
157
-
158
- const toggleFiltersMobile = () => {
159
- mobileFiltersClosed.value = !mobileFiltersClosed.value
160
- const wrapper = document.getElementById(`${props.indexName}_filterRail`)
161
- const leftPane = document.getElementById(`${props.indexName}_eventsSearch__results`)
162
- const containers = document.querySelectorAll('.container')
163
- const otherHiddenEls = document.querySelectorAll('.mobileHide')
164
- wrapper.classList.remove('closed')
165
- wrapper.classList.toggle('openMobile')
166
- leftPane.classList.toggle('openMobile')
167
- containers.forEach((el) => {
168
- if (el.style.display != "none") {
169
- el.style.display = "none"
170
- } else {
171
- el.style.display = "grid"
182
+ })
183
+ const detailsEls = document.querySelectorAll('details')
184
+ detailsEls.forEach((el) => {
185
+ const summary = el.querySelector('summary:first-of-type')
186
+ const summaryHeight = summary?.clientHeight
187
+ el.classList.add('-closing')
188
+ el.style.setProperty('--accordion-height-closed', `${summaryHeight}px`)
189
+
190
+ setTimeout(() => {
191
+ el.open = false
192
+ el.classList.remove('-closing')
193
+ el.style.setProperty('--accordion-height-closed', 'auto')
194
+ }, 0)
195
+ })
196
+ }
197
+
198
+ const updateStateRefs = (uiState) => {
199
+ if (uiState && uiState[props.indexName]) {
200
+ currentQuery.value = uiState[props.indexName].query
201
+ if (currentQuery.value) {
202
+ if (sortView.value != 'Most Relevant') {
203
+ sortView.value = 'Most Relevant'
172
204
  }
173
-
174
- })
175
- otherHiddenEls.forEach((el) => {
176
- if (el.style.display != "none") {
177
- el.style.display = "none"
178
- } else {
179
- el.style.display = "grid"
205
+ } else {
206
+ if (sortView.value != 'Most Recent') {
207
+ sortView.value = 'Most Recent'
180
208
  }
181
-
182
- })
183
- const detailsEls = document.querySelectorAll('details')
184
- detailsEls.forEach((el) => {
185
- const summary = el.querySelector('summary:first-of-type')
186
- const summaryHeight = summary?.clientHeight
187
- el.classList.add('-closing')
188
- el.style.setProperty('--accordion-height-closed', `${summaryHeight}px`)
189
-
190
- setTimeout(() => {
191
- el.open = false
192
- el.classList.remove('-closing')
193
- el.style.setProperty('--accordion-height-closed', 'auto')
194
- }, 0)
195
- })
209
+ }
210
+ setView()
211
+ showNumHits.value = currentQuery.value || uiState[props.indexName].refinementList || uiState[props.indexName].range || uiState[props.indexName].menu
212
+ workFilters.value = uiState[props.indexName].refinementList && Object.keys(uiState[props.indexName].refinementList).length !== 0 ? getWorkFilters(uiState[props.indexName].refinementList) : []
213
+ showByWorks.value = Object.keys(workFilters.value).length !== 0
214
+
215
+ // update search history
216
+
217
+
218
+ updateTitle(uiState[props.indexName])
219
+
196
220
  }
221
+ }
197
222
 
198
- const updateStateRefs = (uiState) => {
199
- if (uiState && uiState[props.indexName]) {
200
- currentQuery.value = uiState[props.indexName].query
201
- if (currentQuery.value) {
202
- if (sortView.value != 'Most Relevant') {
203
- sortView.value = 'Most Relevant'
204
- }
205
- } else {
206
- if (sortView.value != 'Most Recent') {
207
- sortView.value = 'Most Recent'
208
- }
223
+ const updateSearchHistory = (uiState) => {
224
+ let searchHistory = sessionStorage.getItem('searchHistory') ? JSON.parse(sessionStorage.getItem('searchHistory')) : []
225
+ // if only pagination, don't add to search history
226
+ if (!uiState[props.indexName]) {
227
+ return
228
+ }
229
+ if (uiState[props.indexName].page && !uiState[props.indexName].query && !uiState[props.indexName].refinementList && !uiState[props.indexName].menu) {
230
+ return
231
+ }
232
+ searchHistory = searchHistory.map((item) => {
233
+ // make sure that all queries aren't entered into the history as a user types each letter
234
+ if (uiState[props.indexName].query?.includes(item.query) && uiState[props.indexName].query != item.query) {
235
+ return {
236
+ date: new Date(),
237
+ uiState: uiState,
238
+ query: uiState[props.indexName].query,
239
+ link: document.location.href
209
240
  }
210
- setView()
211
- showNumHits.value = currentQuery.value || uiState[props.indexName].refinementList || uiState[props.indexName].range || uiState[props.indexName].menu
212
- workFilters.value = uiState[props.indexName].refinementList && Object.keys(uiState[props.indexName].refinementList).length !== 0 ? getWorkFilters(uiState[props.indexName].refinementList) : []
213
- showByWorks.value = Object.keys(workFilters.value).length !== 0
214
-
215
- // update search history
216
-
217
-
218
- updateTitle(uiState[props.indexName])
219
-
241
+ } else {
242
+ return item
220
243
  }
221
- }
244
+ })
222
245
 
223
- const updateSearchHistory = (uiState) => {
224
- let searchHistory = sessionStorage.getItem('searchHistory') ? JSON.parse(sessionStorage.getItem('searchHistory')) : []
225
- // if only pagination, don't add to search history
226
- if (!uiState[props.indexName]) {
246
+ //make sure we're not just adding pagination
247
+ let addState = true
248
+ searchHistory.forEach((item) => {
249
+ if (item.link.replace(/\&page=\d*/, "") == document.location.href.replace(/\&page=\d*/, "")) {
250
+ addState = false
227
251
  return
228
252
  }
229
- if (uiState[props.indexName].page && !uiState[props.indexName].query && !uiState[props.indexName].refinementList && !uiState[props.indexName].menu) {
253
+ if (_.isEqual(item.uiState[props.indexName], uiState[props.indexName])) {
254
+ addState = false
230
255
  return
231
256
  }
232
- searchHistory = searchHistory.map((item) => {
233
- // make sure that all queries aren't entered into the history as a user types each letter
234
- if (uiState[props.indexName].query?.includes(item.query) && uiState[props.indexName].query != item.query) {
235
- return {
236
- date: new Date(),
237
- uiState: uiState,
238
- query: uiState[props.indexName].query,
239
- link: document.location.href
240
- }
241
- } else {
242
- return item
243
- }
257
+ })
258
+
259
+
260
+ if (addState && !(searchHistory.map((a) => a.link)).includes(document.location.href) &&
261
+ document.location.search != '') {
262
+ searchHistory.push({
263
+ date: new Date(),
264
+ uiState: uiState,
265
+ query: uiState[props.indexName].query,
266
+ link: document.location.href
244
267
  })
245
-
246
- //make sure we're not just adding pagination
247
- let addState = true
248
- searchHistory.forEach((item) => {
249
- if (item.link.replace(/\&page=\d*/, "") == document.location.href.replace(/\&page=\d*/, "")) {
250
- addState = false
251
- return
252
- }
253
- if (_.isEqual(item.uiState[props.indexName], uiState[props.indexName])) {
254
- addState = false
255
- return
268
+ }
269
+
270
+ sessionStorage.setItem('searchHistory', JSON.stringify(searchHistory))
271
+ }
272
+
273
+ const getWorkFilters = (refinementList) => {
274
+ let returnFilters = {}
275
+ Object.entries(refinementList).forEach(([k, v]) => {
276
+ if (k.includes('works')) {
277
+ let workAttribute = k.substring(k.indexOf('works.') + 6, k.length)
278
+ const subFilter = {}
279
+ if (workAttribute.includes('.')) {
280
+ const workSubAttribute = workAttribute.substring(workAttribute.indexOf('.') + 1, workAttribute.length)
281
+ workAttribute = workAttribute.substring(0, workAttribute.indexOf('.'))
282
+ const subSubFilter = {}
283
+ subSubFilter[workSubAttribute] = v
284
+ subFilter[workAttribute] = subSubFilter
285
+
286
+ } else {
287
+ subFilter[workAttribute] = v
256
288
  }
257
- })
258
-
259
-
260
- if (addState && !(searchHistory.map((a) => a.link)).includes(document.location.href) &&
261
- document.location.search != '') {
262
- searchHistory.push({
263
- date: new Date(),
264
- uiState: uiState,
265
- query: uiState[props.indexName].query,
266
- link: document.location.href
267
- })
289
+ returnFilters = {...returnFilters, ...subFilter}
268
290
  }
269
-
270
- sessionStorage.setItem('searchHistory', JSON.stringify(searchHistory))
271
- }
291
+ })
292
+ return returnFilters
293
+ }
272
294
 
273
- const getWorkFilters = (refinementList) => {
274
- let returnFilters = {}
275
- Object.entries(refinementList).forEach(([k, v]) => {
276
- if (k.includes('works')) {
277
- let workAttribute = k.substring(k.indexOf('works.') + 6, k.length)
278
- const subFilter = {}
279
- if (workAttribute.includes('.')) {
280
- const workSubAttribute = workAttribute.substring(workAttribute.indexOf('.') + 1, workAttribute.length)
281
- workAttribute = workAttribute.substring(0, workAttribute.indexOf('.'))
282
- const subSubFilter = {}
283
- subSubFilter[workSubAttribute] = v
284
- subFilter[workAttribute] = subSubFilter
285
-
286
- } else {
287
- subFilter[workAttribute] = v
288
- }
289
- returnFilters = {...returnFilters, ...subFilter}
290
- }
291
- })
292
- return returnFilters
293
- }
295
+ const onStateChange = ({ uiState, setUiState }) => {
296
+ updateStateRefs(uiState)
297
+ setUiState(uiState)
298
+ }
294
299
 
295
- const onStateChange = ({ uiState, setUiState }) => {
296
- updateStateRefs(uiState)
297
- setUiState(uiState)
298
-
299
- // console.log('before', new Date())
300
+ const updateTitle = (state) => {
301
+ document.title = "BSO HENRY | " + formatSearchTitle(state)
302
+ }
300
303
 
301
- // setTimeout(() => , 3000)
302
- // console.log('after', new Date())
304
+ const setView = () => {
305
+ if (sortView.value == 'Oldest First') {
306
+ adapter.updateConfiguration({...adapter.configuration, additionalSearchParameters: {
307
+ query_by: props.queryByFields,
308
+ sort_by: `${props.sortField}:asc`
309
+ }})
310
+ } else if (sortView.value == 'Most Relevant') {
311
+ adapter.updateConfiguration({...adapter.configuration, additionalSearchParameters: {
312
+ query_by: props.queryByFields,
313
+ sort_by: `_text_match:desc,${props.sortField}:desc`
314
+ }})
315
+ } else {
316
+ adapter.updateConfiguration({...adapter.configuration, additionalSearchParameters: {
317
+ query_by: props.queryByFields,
318
+ sort_by: `${props.sortField}:desc`
319
+ }})
303
320
  }
304
-
305
- const updateTitle = (state) => {
306
- document.title = "BSO HENRY | " + formatSearchTitle(state)
321
+ updateNow.value++
322
+ }
323
+
324
+ const formatRefinement = (refinement) => {
325
+ const attributeMap = {
326
+ "works.composers" : "Composer",
327
+ "works.title" : "Work",
328
+ "works.conductors" : "Conductor",
329
+ "ensembles" : "Orchestra",
330
+ "works.ensembles" : "Orchestra",
331
+ "works.artists.name" : "Artist",
332
+ "query" : "Keyword",
333
+ 'works.artists.role': 'Instrument/Role',
334
+ 'works.additional_creators.name': 'Additional Creator',
335
+ 'works.additional_creators.role': 'Creator Role',
336
+ 'season': 'Season',
337
+ 'event_title': 'Event Title',
338
+ 'event_types': 'Series',
339
+ 'venue': 'Venue',
340
+ 'works.commission': 'Commission',
341
+ 'works.premiere': 'Premiere',
342
+ 'location.city': 'City',
343
+ 'location.country': 'Country',
344
+ 'location.state': 'State',
345
+ 'artist_name': 'Artist',
346
+ 'artist_role': 'Instrument/Role',
347
+ 'title': 'Work Title',
348
+ 'composers': 'Composer',
349
+ 'composer': 'Composer',
350
+ 'media': 'Media',
351
+ 'creators.name': 'Additional Creator',
352
+ 'creators.role': 'Creator Role'
307
353
  }
308
-
309
- const setView = () => {
310
- if (sortView.value == 'Oldest First') {
311
- adapter.updateConfiguration({...adapter.configuration, additionalSearchParameters: {
312
- query_by: props.queryByFields,
313
- sort_by: `${props.sortField}:asc`
314
- }})
315
- } else if (sortView.value == 'Most Relevant') {
316
- adapter.updateConfiguration({...adapter.configuration, additionalSearchParameters: {
317
- query_by: props.queryByFields,
318
- sort_by: `_text_match:desc,${props.sortField}:desc`
319
- }})
320
- } else {
321
- adapter.updateConfiguration({...adapter.configuration, additionalSearchParameters: {
322
- query_by: props.queryByFields,
323
- sort_by: `${props.sortField}:desc`
324
- }})
325
- }
326
- updateNow.value++
354
+ if (refinement.attribute == 'performance_date' || refinement.attribute == 'last_performance_date') {
355
+ return 'Date: ' + refinement.label[0] + ' ' + formatDate(refinement.value) + ' ×'
327
356
  }
328
-
329
- const formatRefinement = (refinement) => {
330
- const attributeMap = {
331
- "works.composers" : "Composer",
332
- "works.title" : "Work",
333
- "works.conductors" : "Conductor",
334
- "ensembles" : "Orchestra",
335
- "works.ensembles" : "Orchestra",
336
- "works.artists.name" : "Artist",
337
- "query" : "Keyword",
338
- 'works.artists.role': 'Instrument/Role',
339
- 'works.additional_creators.name': 'Additional Creator',
340
- 'works.additional_creators.role': 'Creator Role',
341
- 'season': 'Season',
342
- 'event_title': 'Event Title',
343
- 'event_types': 'Series',
344
- 'venue': 'Venue',
345
- 'works.commission': 'Commission',
346
- 'works.premiere': 'Premiere',
347
- 'location.city': 'City',
348
- 'location.country': 'Country',
349
- 'location.state': 'State',
350
- 'artist_name': 'Artist',
351
- 'artist_role': 'Instrument/Role',
352
- 'title': 'Work Title',
353
- 'composers': 'Composer',
354
- 'composer': 'Composer',
355
- 'media': 'Media',
356
- 'creators.name': 'Additional Creator',
357
- 'creators.role': 'Creator Role'
358
- }
359
- if (refinement.attribute == 'performance_date' || refinement.attribute == 'last_performance_date') {
360
- return 'Date: ' + refinement.label[0] + ' ' + formatDate(refinement.value) + ' ×'
361
- }
362
- if (attributeMap[refinement.attribute]) {
363
- return attributeMap[refinement.attribute] + ': ' + refinement.value + ' <span="activeFilters__removeIcon">×</span>'
364
- } else {
365
- return refinement.attribute + ': ' + refinement.value + ' ×'
366
- }
367
-
357
+ if (attributeMap[refinement.attribute]) {
358
+ return attributeMap[refinement.attribute] + ': ' + refinement.value + ' <span="activeFilters__removeIcon">×</span>'
359
+ } else {
360
+ return refinement.attribute + ': ' + refinement.value + ' ×'
368
361
  }
369
-
370
- const intersect = (filters, work) => {
371
- let intersectKeys = Object.keys(filters).filter(k => Object.hasOwn(work, k))
372
- let intersectArray = []
373
- intersectKeys.forEach((key) => {
374
- if (typeof filters[key] == 'object' && typeof work[key] == 'object') {
375
- if (Array.isArray(work[key]) && Array.isArray(filters[key])) {
376
- work[key].forEach((w) => {
377
- filters[key].forEach((f) => {
378
- if (w == f) {
379
- intersectArray.push(w)
380
- }
381
- })
362
+
363
+ }
364
+
365
+ const intersect = (filters, work) => {
366
+ let intersectKeys = Object.keys(filters).filter(k => Object.hasOwn(work, k))
367
+ let intersectArray = []
368
+ intersectKeys.forEach((key) => {
369
+ if (typeof filters[key] == 'object' && typeof work[key] == 'object') {
370
+ if (Array.isArray(work[key]) && Array.isArray(filters[key])) {
371
+ work[key].forEach((w) => {
372
+ filters[key].forEach((f) => {
373
+ if (w == f) {
374
+ intersectArray.push(w)
375
+ }
382
376
  })
383
- } else if (Array.isArray(work[key])) {
384
- Object.entries(filters[key])?.forEach(([k1, v1]) => {
385
- work[key]?.forEach((workvalue) => {
386
- if (typeof workvalue == 'object') {
387
- Object.entries(workvalue).forEach(([k2, v2]) => {
388
- if (k1 == k2 && v1.includes(v2)) {
389
- intersectArray.push(workvalue[k2])
390
- }
391
- })
392
- }
393
-
394
- })
395
- })
396
- }
397
- } else if (filters[key].includes(work[key])) {
398
- intersectArray.push(work[key])
377
+ })
378
+ } else if (Array.isArray(work[key])) {
379
+ Object.entries(filters[key])?.forEach(([k1, v1]) => {
380
+ work[key]?.forEach((workvalue) => {
381
+ if (typeof workvalue == 'object') {
382
+ Object.entries(workvalue).forEach(([k2, v2]) => {
383
+ if (k1 == k2 && v1.includes(v2)) {
384
+ intersectArray.push(workvalue[k2])
385
+ }
386
+ })
387
+ }
388
+
389
+ })
390
+ })
399
391
  }
400
- })
401
- return intersectArray
392
+ } else if (filters[key].includes(work[key])) {
393
+ intersectArray.push(work[key])
394
+ }
395
+ })
396
+ return intersectArray
402
397
 
403
- }
398
+ }
404
399
 
405
- const filterItems = (items) => {
406
- if (showByWorks.value && props.indexName == "dev_henry_perfs") {
407
-
408
- let returnItems = items
409
- let itemIndex = 0
410
- returnItems.forEach((item) => {
411
- let shownWorks = []
412
- item?.works?.forEach((work) => {
413
- let workAdded = false
414
- if (intersect(workFilters.value, work)?.length) {
415
- shownWorks.push(work)
416
- workAdded = true
417
- }
418
- if (!workAdded && currentQuery.value && JSON.stringify(work).includes(currentQuery.value)) {
419
- shownWorks.push(work)
420
- }
421
- })
422
- returnItems[itemIndex].works = shownWorks
423
- itemIndex++
400
+ const filterItems = (items) => {
401
+ if (!items.length) {
402
+ emit('push', {event: 'no_results', data: currentQuery.value})
403
+ }
404
+ if (showByWorks.value && props.indexName == "dev_henry_perfs") {
405
+
406
+ let returnItems = items
407
+ let itemIndex = 0
408
+ returnItems.forEach((item) => {
409
+ let shownWorks = []
410
+ item?.works?.forEach((work) => {
411
+ let workAdded = false
412
+ if (intersect(workFilters.value, work)?.length) {
413
+ shownWorks.push(work)
414
+ workAdded = true
415
+ }
416
+ if (!workAdded && currentQuery.value && JSON.stringify(work).includes(currentQuery.value)) {
417
+ shownWorks.push(work)
418
+ }
424
419
  })
425
- return returnItems
426
- } else {
427
- return items
428
- }
420
+ returnItems[itemIndex].works = shownWorks
421
+ itemIndex++
422
+ })
423
+ return returnItems
424
+ } else {
425
+ return items
429
426
  }
427
+ }
430
428
 
431
- const refineAndScroll = (refine, params, scrollId) => {
432
- refine(params)
433
- document.getElementById(scrollId).scrollIntoView()
434
- }
429
+ const refineAndScroll = (refine, params, scrollId) => {
430
+ refine(params)
431
+ document.getElementById(scrollId).scrollIntoView()
432
+ }
435
433
 
436
- const toMinValue = (value, range) => {
437
- return typeof value.min === "number" && value.min != 0 ? value.min * 1000 : range.min * 1000
438
- }
434
+ const toMinValue = (value, range) => {
435
+ return typeof value.min === "number" && value.min != 0 ? value.min * 1000 : range.min * 1000
436
+ }
439
437
 
440
- const toMaxValue = (value, range) => {
441
- return typeof value.max === "number" && value.max != 0 ? value.max * 1000 : range.max * 1000
442
- }
438
+ const toMaxValue = (value, range) => {
439
+ return typeof value.max === "number" && value.max != 0 ? value.max * 1000 : range.max * 1000
440
+ }
443
441
 
444
- const formatMinValue = (minValue, minRange) => {
445
- return typeof minValue === "number" && minValue !== null && minValue !== minRange ? minValue : minRange
446
- }
447
-
448
- const formatMaxValue = (maxValue, maxRange) => {
449
- return typeof maxValue === "number" && maxValue !== null && maxValue !== maxRange ? maxValue : maxRange
450
- }
442
+ const formatMinValue = (minValue, minRange) => {
443
+ return typeof minValue === "number" && minValue !== null && minValue !== minRange ? minValue : minRange
444
+ }
451
445
 
446
+ const formatMaxValue = (maxValue, maxRange) => {
447
+ return typeof maxValue === "number" && maxValue !== null && maxValue !== maxRange ? maxValue : maxRange
448
+ }
452
449
 
453
- const getHeadingStyle = (attribute) => {
454
- if (mainRefinementList.value && typeof(mainRefinementList.value[attribute]) !== 'undefined' && mainRefinementList.value[attribute]) {
455
- try {
456
- if (mainRefinementList.value[attribute] && typeof(mainRefinementList.value[attribute].items) !== 'undefined' && mainRefinementList.value[attribute].items.length) {
457
- return ''
458
- } else {
459
- return '-gray'
460
- }
461
- } catch (e) {
450
+
451
+ const getHeadingStyle = (attribute) => {
452
+ if (mainRefinementList.value && typeof(mainRefinementList.value[attribute]) !== 'undefined' && mainRefinementList.value[attribute]) {
453
+ try {
454
+ if (mainRefinementList.value[attribute] && typeof(mainRefinementList.value[attribute].items) !== 'undefined' && mainRefinementList.value[attribute].items.length) {
462
455
  return ''
456
+ } else {
457
+ return '-gray'
463
458
  }
459
+ } catch (e) {
460
+ return ''
464
461
  }
465
- return ''
466
462
  }
463
+ return ''
464
+ }
465
+
466
+ const clearRefinementSearches = () => {
467
+ if (refinementSearchBoxes.value && refinementSearchBoxes.value.length) {
468
+ refinementSearchBoxes.value.forEach((el) => {
469
+ if (el) {
470
+ el.value = ""
471
+ }
472
+ })
473
+ }
474
+ }
467
475
 
476
+ const emitQuery = useDebounceFn(() => {
477
+ if (currentQuery.value) {
478
+ emit('push', {event: 'search_form', data: currentQuery.value})
479
+ }
480
+ }, 1000)
468
481
 
469
482
  </script>
470
483
 
@@ -493,7 +506,7 @@ import { truncate } from 'lodash';
493
506
  id="searchbox"
494
507
  type="search"
495
508
  :value="currentRefinement"
496
- @input="refine($event.currentTarget.value)"
509
+ @input="refine($event.currentTarget.value);emitQuery()"
497
510
  :placeholder="props.searchPlaceholder"
498
511
  >
499
512
 
@@ -606,7 +619,7 @@ import { truncate } from 'lodash';
606
619
  <div class="ais-SearchBox searchBox -filter">
607
620
  <label class="label__hidden" :for="refinement.attribute">{{ refinement.placeholder }}</label>
608
621
  <form class="searchBox__form" @submit="$event.preventDefault()" @reset="searchForItems('')">
609
- <input type="text" :id="refinement.attribute" @input="searchForItems($event.currentTarget.value)" :placeholder="refinement.placeholder" class="ais-SearchBox-input -filter" v-if="!refinement.hideSearch">
622
+ <input type="text" :id="refinement.attribute" :ref="(el) => refinementSearchBoxes.push(el)" @input="searchForItems($event.currentTarget.value)" :placeholder="refinement.placeholder" class="ais-SearchBox-input -filter" v-if="!refinement.hideSearch">
610
623
  <button v-if="isFromSearch"
611
624
  class="ais-SearchBox-reset"
612
625
  type="reset"
@@ -628,7 +641,7 @@ import { truncate } from 'lodash';
628
641
  :id="slugify(refinement.title + ' ' + item.value)"
629
642
  :value="item.value"
630
643
  :checked="item.isRefined"
631
- @click="refine(item.value)" />
644
+ @click="refine(item.value);$emit('push', {event: item.isRefined ? 'unfilter' : 'search_filter', filter_category: refinement.title, filter_value : item.value})" />
632
645
  <label :for="slugify(refinement.title + ' ' + item.value)">{{ item.value }}</label><span class="eventsSearch__refinementCount">{{ item.count }}</span>
633
646
  </label>
634
647
  </div>
@@ -676,7 +689,7 @@ import { truncate } from 'lodash';
676
689
  <div class="checkBoxes">
677
690
  <span class="checkBoxes__alert" v-if="!items.length"><span class="checkBoxes__alertIcon">!</span>No matches found</span>
678
691
  <label v-for="item in items" class="checkBoxes__boxLabel" :for="slugify(refinement.title + ' ' + item.value)">
679
- <input :class="`checkbox ${item.isRefined ? '-boxChecked' : ''}`" type="checkbox" :id="slugify(refinement.title + ' ' + item.value)" :value="item.value" :checked="item.isRefined" @click="refine(item.value)">
692
+ <input :class="`checkbox ${item.isRefined ? '-boxChecked' : ''}`" type="checkbox" :id="slugify(refinement.title + ' ' + item.value)" :value="item.value" :checked="item.isRefined" @click="refine(item.value);$emit('push', {event: item.isRefined ? 'unfilter' : 'filter', type: refinement.title, data: item.value})">
680
693
  <label :for="slugify(refinement.title + ' ' + item.value)">{{ item.value }}</label><span class="eventsSearch__refinementCount">{{ item.count }}</span>
681
694
  </label>
682
695
  </div>
@@ -696,7 +709,7 @@ import { truncate } from 'lodash';
696
709
  <div class="accordion__content">
697
710
  <ais-menu-select v-if="refinement.type == 'location'" :attribute="'location.country'" operator="and" :limit="100" >
698
711
  <template v-slot="{ items, refine }">
699
- <select @change="refine($event.currentTarget.value)">
712
+ <select @change="refine($event.currentTarget.value);$emit('push', {event: 'filter', type: 'location.country', data: $event.currentTarget.value})">
700
713
  <option value="">Select Country</option>
701
714
  <option v-for="item in items" :selected="item.isRefined" :key="item.value" :value="item.value">{{ item.label }}</option>
702
715
  </select>
@@ -704,7 +717,7 @@ import { truncate } from 'lodash';
704
717
  </ais-menu-select>
705
718
  <ais-menu-select v-if="refinement.type == 'location'" :attribute="'location.state'" operator="and" :limit="100">
706
719
  <template v-slot="{ items, refine }">
707
- <select @change="refine($event.currentTarget.value)">
720
+ <select @change="refine($event.currentTarget.value);$emit('push', {event: 'filter', type: 'location.state', data: $event.currentTarget.value})">
708
721
  <option value="">Select State</option>
709
722
  <option v-for="item in items" :selected="item.isRefined" :key="item.value" :value="item.value">{{ item.label }}</option>
710
723
  </select>
@@ -712,7 +725,7 @@ import { truncate } from 'lodash';
712
725
  </ais-menu-select>
713
726
  <ais-menu-select placeholder="Select City" v-if="refinement.type == 'location'" :attribute="'location.city'" operator="and" :limit="100">
714
727
  <template v-slot="{ items, refine }">
715
- <select @change="refine($event.currentTarget.value)">
728
+ <select @change="refine($event.currentTarget.value);$emit('push', {event: 'filter', type: 'location.city', data: $event.currentTarget.value})">
716
729
  <option value="">Select City</option>
717
730
  <option v-for="item in items" :selected="item.isRefined" :key="item.value" :value="item.value">{{ item.label }}</option>
718
731
  </select>
@@ -730,7 +743,7 @@ import { truncate } from 'lodash';
730
743
  <template v-slot="{ refine, createURL }">
731
744
  <a
732
745
  :href="createURL()"
733
- @click.prevent="refine"
746
+ @click.prevent="refine();clearRefinementSearches()"
734
747
  >
735
748
  <button @click="toggleFiltersMobile()" class="filterClear">Clear</button>
736
749
 
@@ -802,7 +815,7 @@ import { truncate } from 'lodash';
802
815
  </div>
803
816
  <div v-if="showNumHits" class="resultActions__buttons">
804
817
  <div class="toolTip">
805
- <a onclick="navigator.clipboard.writeText(window.location.href);">
818
+ <a onclick="navigator.clipboard.writeText(window.location.href);" id="shareActions__icon">
806
819
  <svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="resultActions__icon" outline="black">
807
820
  <!-- <circle cx="12" cy="12" stroke="none" r="12"/> -->
808
821
  <path d="M11.3334 12.5068C11.5012 12.7435 11.7153 12.9393 11.9611 13.081C12.2069 13.2227 12.4788 13.307 12.7582 13.3281C13.0376 13.3492 13.3181 13.3066 13.5806 13.2034C13.8431 13.1001 14.0814 12.9384 14.2795 12.7294L15.4516 11.4928C15.8075 11.1041 16.0044 10.5835 16 10.0431C15.9955 9.50271 15.7901 8.98578 15.4278 8.60365C15.0656 8.22153 14.5756 8.00477 14.0634 8.00008C13.5511 7.99538 13.0577 8.20312 12.6892 8.57855L12.0171 9.28341" stroke-linecap="round" stroke-linejoin="round"/>
@@ -812,7 +825,7 @@ import { truncate } from 'lodash';
812
825
  <span class="toolTip__text">Copy Link</span>
813
826
  </div>
814
827
  <div class="toolTip">
815
- <a :href="`/actions/csvexport/csv-export${ routing.router.getLocation().search }`" >
828
+ <a :href="`/actions/csvexport/csv-export${ routing.router.getLocation().search }`" id="downloadActions__icon">
816
829
  <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="resultActions__icon">
817
830
  <!-- <circle cx="12" cy="12" r="12" stroke="none "/> -->
818
831
  <path d="M15.9785 17.41L9.00049 17.41" stroke-linecap="round" stroke-linejoin="round"/>
@@ -834,7 +847,7 @@ import { truncate } from 'lodash';
834
847
  <template v-slot="{ canRefine, refine, createURL }">
835
848
  <a
836
849
  :href="createURL()"
837
- @click.prevent="refine"
850
+ @click.prevent="refine();clearRefinementSearches()"
838
851
  v-if="canRefine"
839
852
  class="activeFilters__clearFilters"
840
853
  >