UNPKG

@mezereon/ui-shopify

Version:

Shopify UI components for Mezereon Xperience

394 lines (387 loc) 10.8 kB
/* global mz */ import { debounce, indexOf } from 'lodash' import { mapState, mapGetters } from 'vuex' import { getKeyFromComplex, getValueFromComplex, isAbsoluteUrl, pathCombine } from '@/helpers' import Fa from 'vue-fa' import { faSearch, faBars, faTimes, faThLarge } from '@fortawesome/free-solid-svg-icons' export default { components: { Fa }, props: { searchUrl: { type: String, required: false, default: undefined }, queryParam: { type: String, required: false, default: 'q' }, placement: { type: String, required: false, default: undefined }, delay: { type: Number, required: false, default: 0 }, queryOnHover: { type: Boolean, required: false, default: true } }, data() { return { search: '', internalView: false, autocomplete: false, faSearch, faBars, faTimes, faThLarge } }, computed: { ...mapGetters('history', ['recentSearches']), ...mapState('search', [ 'suggestHidden', 'hits', 'primaryKey', 'pagination', 'pageSize', 'keyword', 'queryId', 'complete', 'suggestResult', 'suggestItems', 'currentSuggestion', 'config', 'translation', 'redirect' ]), resultsCount() { const topItems = this.config.mainAreaSections.find( (x) => x.modal === 'modal-top-items' ) if (!topItems || !topItems.config) { return 10 } return topItems.config.count || 10 }, suggestRecent() { if (!this.recentSearches) { return [] } const count = this.config.recentCount || 5 return this.recentSearches(count) } }, methods: { isHidden(hit) { if (!this.suggestHidden || !hit.item || !hit.item[this.primaryKey]) { return false } return ( indexOf(this.suggestHidden, hit.item[this.primaryKey].toString()) !== -1 ) }, addSearch(payload) { this.$store.dispatch('history/addSearch', payload, { root: true }) }, deleteSearch(payload) { this.$store.dispatch('history/deleteSearch', payload, { root: true }) }, trackEvent(event) { const final = { ...event, context: { visitId: this.$cookies ? this.$cookies.get('mz_visitId') : '', visitorId: this.$cookies ? this.$cookies.get('mz_visitorId') : '' } } if (mz && mz.track) { mz.track('autocomplete', final) } }, onItemClick(event) { const final = { ...event, keyword: this.query(), complete: this.complete, suggestion: this.currentSuggestion } this.trackEvent(final) }, changeView(view) { this.view = view this.$emit('change-autocomplete-view') }, keyHandler(event) { if (event.key === 'Escape') { this.autocomplete = false return } // arrow down if (event.key === 'ArrowDown') { const nextElement = event.target.nextElementSibling if (nextElement) { nextElement.focus() } event.preventDefault() } // arrow up if (event.key === 'ArrowUp') { const previousElement = event.target.previousElementSibling if (previousElement) { previousElement.focus() } event.preventDefault() } }, setCurrentSuggestion(sug) { if (sug !== this.currentSuggestion) { this.$store.dispatch('search/setCurrentSuggestion', sug, { root: true }) this.getAutocomplete( this, this.currentSuggestion, true, this.resultsCount ) } }, emitChange(value) { this.$emit('change', value) this.search = value }, clearKeyword() { this.emitChange('') this.$store.dispatch('search/setComplete', '', { root: true }) this.$store.dispatch('search/setCurrentSuggestion', '', { root: true }) this.$store.dispatch('search/clearAutocomplete', '', { root: true }) this.doSearch(false) }, processClick(event, keyword) { this.trackEvent(event) this.addSearch(event) this.$store.dispatch('search/setComplete', '', { root: true }) this.$store.dispatch('search/setCurrentSuggestion', '', { root: true }) this.emitChange(keyword) this.doSearch(true) }, clickRecent(keyword) { // clear autocomplete to run autocomplete query on focus this.$store.dispatch('search/clearAutocomplete', '', { root: true }) // track suggestion click event const event = { type: 'recent', keyword: '', complete: '', suggestion: keyword } this.processClick(event, keyword) }, clickSearch(keyword) { if (!this.queryOnHover) { this.setCurrentSuggestion(keyword) return } // track suggestion click event const event = { type: 'suggestion', keyword: this.query(), complete: '', suggestion: keyword } this.processClick(event, keyword) }, clickFacet(facet, original) { this.trackEvent({ type: 'facet', keyword: this.query(), complete: this.complete, suggestion: this.currentSuggestion, name: facet.name, value: original }) const key = getKeyFromComplex(original) const value = getValueFromComplex(original) if (this.searchUrl) { if (window.location.pathname !== this.searchUrl) { window.location = `${this.searchUrl}?mz.${facet.name}=${key}` return } } const last = { name: facet.name, label: facet.label, key: key, value: value, action: 'add' } this.emitChange('') this.$store.dispatch('search/setComplete', '', { root: true }) this.$store.dispatch('search/setCurrentSuggestion', '', { root: true }) this.$store.dispatch('search/clearAutocomplete', '', { root: true }) this.$store.dispatch('search/clearAllSelections', {}, { root: true }) this.$store.dispatch('search/updateSelections', last, { root: true }) this.$store.dispatch('search/setPage', 1, { root: true }) // close autocomplete this.autocomplete = false // run query if (this.$bus) this.$bus.emit('run-query') }, doEnter() { // track complete event if (this.complete && this.complete !== this.query()) { const event = { type: 'complete', complete: this.complete, keyword: this.query(), suggestion: this.currentSuggestion } this.trackEvent(event) this.addSearch(event) this.doSearch(true) return } // track suggest event if (this.currentSuggestion && this.currentSuggestion !== this.query()) { const event = { type: 'suggestion', complete: this.complete, keyword: this.query(), suggestion: this.currentSuggestion } this.trackEvent(event) this.addSearch(event) this.doSearch(true) return } const event = { type: 'search', complete: this.complete, keyword: this.query(), suggestion: this.currentSuggestion } this.addSearch(event) this.doSearch(true) }, doSearch(clearSelections) { this.autocomplete = false if (this.redirect) { let redirect = this.redirect if (isAbsoluteUrl(redirect)) { window.location.replace(redirect) return } if (mz && mz.context && mz.context.root) { redirect = pathCombine(mz.context.root, redirect) } window.location.replace(redirect) return } this.$emit( 'change', this.complete || this.currentSuggestion || this.query() ) if (this.searchUrl) { if ( this.input.form && this.input.form.action.includes(this.searchUrl) ) { this.input.form.submit() return } if (window.location.pathname !== this.searchUrl) { window.location = `${this.searchUrl}?${ this.queryParam }=${encodeURIComponent(this.query())}` return } } const last = { name: 'Keyword', label: this.$t('Keyword'), key: '_Keyword', value: this.query(), action: this.query() === '' ? 'remove-all' : 'replace' } if (clearSelections) { this.$store.dispatch('search/clearAllSelections', {}, { root: true }) } this.$store.dispatch('search/updateSelections', last, { root: true }) this.$store.dispatch('search/setKeyword', this.query(), { root: true }) this.$store.dispatch('search/setPage', 1, { root: true }) if (this.$bus) this.$bus.emit('run-query') }, doSuggest(event) { if (event.key === 'Escape' || event.key === 'Enter') { this.autocomplete = false } if (event.key === 'Enter') { if (this.complete || this.currentSuggestion) { this.emitChange(this.complete || this.currentSuggestion) this.$store.dispatch('search/setComplete', '', { root: true }) } } }, getAutocomplete: debounce((vm, keyword, skipSuggestions, resultsCount) => { vm.runAutocomplete(keyword, skipSuggestions, resultsCount) }, 250), runAutocomplete(keyword, skipSuggestions, resultsCount) { if (this.$bus) { this.$bus.emit( 'run-autocomplete', keyword, skipSuggestions, resultsCount, this.includeBanners ) } }, changeSuggest() { this.$store.dispatch('search/setComplete', '', { root: true }) this.$store.dispatch('search/setCurrentSuggestion', '', { root: true }) this.autocomplete = true this.getAutocomplete(this, this.query(), false, this.resultsCount) }, keyDown() { if (event.key === 'Escape') { this.autocomplete = false return } if ( (event.key === 'Tab' && event.shiftKey === false) || event.key === 'ArrowDown' || event.key === 'ArrowUp' ) { let active = '' // find current suggestion and set focus active = document.querySelector('.mz-suggest-item.mz-active') // set focus on first suggestion active = active || document.querySelector('.mz-suggest-item') if (active) { active.focus() event.preventDefault() } } } } }