@ekwoka/alpine-history
Version:
Sync Component an Store values to the URL Search Params!!!
154 lines (151 loc) • 4.34 kB
JavaScript
import { fromQueryString, toQueryString } from './querystring.js';
import { retrieveDotNotatedValueFromData, objectAtPath, deleteDotNotatedValueFromData, insertDotNotatedValueIntoData } from './pathresolve.js';
import { onURLChange, UpdateMethod, untrack } from './history.js';
export { observeHistory } from './history.js';
export { base64, base64URL } from './encoding.js';
class QueryInterceptor {
constructor(initialValue, Alpine, reactiveParams) {
this.Alpine = Alpine;
this.reactiveParams = reactiveParams;
this.initialValue = initialValue;
}
_x_interceptor = true;
alias = void 0;
encoder = {
to: (v) => v,
from: (v) => v
};
method = UpdateMethod.replace;
show = false;
initialValue;
/**
* Self Initializing interceptor called by Alpine during component initialization
* @param {object} data The Alpine Data Object (component or store)
* @param {string} path dot notated path from the data root to the interceptor
* @returns {T} The value of the interceptor after initialization
*/
initialize(data, path) {
const {
alias = path,
Alpine,
initialValue,
method,
reactiveParams,
show,
encoder
} = this;
const existing = retrieveDotNotatedValueFromData(
alias,
reactiveParams
);
const initial = existing ? encoder.from(existing) : initialValue;
const keys = path.split(".");
const final = keys.pop();
const obj = objectAtPath(keys, data, final);
Object.defineProperty(obj, final, {
set: (value) => {
!show && value === initialValue ? deleteDotNotatedValueFromData(alias, reactiveParams) : insertDotNotatedValueIntoData(
alias,
encoder.to(value),
reactiveParams
);
},
get: () => {
const existing2 = retrieveDotNotatedValueFromData(
alias,
reactiveParams
);
const value = existing2 ? encoder.from(existing2) : initialValue;
return value;
},
enumerable: true
});
Alpine.effect(paramEffect(alias, reactiveParams, method));
return initial;
}
/**
* Changes the keyname for using in the query string
* Keyname defaults to path to data
* @param {string} name Key alias
*/
as(name) {
this.alias = name;
return this;
}
/**
* Transforms the value of the query param before it is set on the data
* @param {function} fn Transformer function
*/
into(fn) {
this.encoder.from = fn;
return this;
}
/**
* Always show the initial value in the query string
*/
alwaysShow() {
this.show = true;
return this;
}
/**
* Use pushState instead of replaceState
*/
usePush() {
this.method = UpdateMethod.push;
return this;
}
/**
* Registers encoding and decoding functions to transform the value
* before it is set on the query string
*/
encoding(encoder) {
this.encoder = encoder;
return this;
}
}
const query = (Alpine) => {
const reactiveParams = Alpine.reactive(
fromQueryString(location.search)
);
const updateParams = (obj) => {
Object.assign(reactiveParams, obj);
for (const key in Alpine.raw(reactiveParams))
if (!(key in obj))
delete reactiveParams[key];
};
window.addEventListener("popstate", (event) => {
if (!event.state?.query)
return;
updateParams(event.state.query);
});
onURLChange((url) => {
const query2 = fromQueryString(url.search);
updateParams(query2);
});
const bindQuery = (initial) => new QueryInterceptor(initial, Alpine, reactiveParams);
Alpine.query = bindQuery;
Alpine.magic("query", () => bindQuery);
};
const intoState = (data) => Object.assign({}, history.state ?? {}, {
query: JSON.parse(JSON.stringify(data))
});
const paramEffect = (key, params, method) => {
let previous = JSON.stringify(params[key]);
return () => {
const current = JSON.stringify(params[key]);
if (current === previous)
return;
untrack(() => setParams(params, method));
previous = current;
};
};
const setParams = (params, method) => {
const queryString = toQueryString(params);
history[method](
intoState(params),
"",
queryString ? `?${queryString}` : location.pathname
);
};
export { query as default, query };
//# sourceMappingURL=index.js.map