UNPKG

@ekwoka/alpine-history

Version:
154 lines (151 loc) 4.34 kB
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