@mezereon/bigcommerce-stencil
Version:
BigCommerce UI components for Mezereon Xperience
143 lines (120 loc) • 3.45 kB
JavaScript
import { debounced, isArray } from '@/helpers'
import Vue from 'vue'
import { createBrowserHistory } from 'history'
import queryString from 'qs'
function isString(value) {
return typeof value === 'string' || value instanceof String
}
const encoder = function (val, defaultEncoder, charset, type) {
if (type === 'key') {
return val
} else if (type === 'value') {
if (!isString(val)) return val
return val.replace(/&/g, '%26')
}
}
const stringifyOptions = {
allowDots: true,
addQueryPrefix: true,
arrayFormat: 'comma',
skipNulls: true,
encoder
}
const parseOptions = {
ignoreQueryPrefix: true,
allowDots: true,
comma: true
}
class QueryState {
constructor() {
this.history = createBrowserHistory()
this.queryParam = 'q'
}
// Listen for changes to the current location.
onChange(callback) {
this.unlisten = this.history.listen((location, action) => {
// console.log(action, location.pathname, location.search)
// skip for PUSH action
if (action === 'PUSH') return
debounced(250, callback(this.parseUrl(location)))
})
}
parseUrl(location) {
// if location is empty, then take from history
location = location || this.history.location
const intermediate = queryString.parse(location.search, parseOptions)
// convert single value to array with one element
intermediate.selections = []
if (intermediate.mz) {
Object.keys(intermediate.mz).forEach((key) => {
const value = intermediate.mz[key]
if (value && !isArray(value)) {
intermediate.mz[key] = [value]
}
// initialize selections that will be split and updated inside RUN_QUERY mutation
intermediate.selections.push({
action: 'init',
name: key,
key,
value,
label: key
})
})
}
const keyword = intermediate[this.queryParam] || ''
if (keyword) {
// initialize selections that will be split and updated inside RUN_QUERY mutation
intermediate.selections.push({
action: 'replace',
name: 'Keyword',
key: '_Keyword',
value: keyword,
label: Vue.prototype.$t('Keyword') || 'Keyword'
})
}
const final = {
keyword,
page: parseInt(intermediate.page) || 1,
sort: intermediate.sort,
pageSize: intermediate.size ? parseInt(intermediate.size) : undefined,
filters: intermediate.mz,
selections: intermediate.selections
}
return final
}
push(state) {
const object = {
page: state.page,
sort: state.sort,
size: state.pageSize,
mz: state.filters
}
// do not push keyword field if empty
if (state.keyword && state.keyword !== '') {
object[this.queryParam] = state.keyword
}
this.history.push({
search: queryString.stringify(object, stringifyOptions)
})
}
// this function is called from ui-components-vue
stringifyFilters(filters) {
const object = {
mz: filters
}
// copy options
const options = { ...stringifyOptions }
// overwrite addQueryPrefix setting
options.addQueryPrefix = false
return queryString.stringify(object, options)
}
setQueryParam(queryParam) {
this.queryParam = queryParam
}
destroy() {
this.unlisten()
}
}
const instance = new QueryState()
Vue.prototype.$queryState = instance
export default instance