UNPKG

mancha

Version:

Javscript HTML rendering engine

139 lines 5.09 kB
const KEY_PREFIX = "$$"; const FALLBACK_URL = "http://localhost/"; function getWindowURL() { return new URL(globalThis.window?.location?.href || FALLBACK_URL); } /** * Converts a store key to a URL parameter name by removing the KEY_PREFIX. * @param storeKey The store key (e.g., "$$page") * @returns The URL parameter name (e.g., "page") */ function _storeKeyToParamName(storeKey) { return storeKey.substring(KEY_PREFIX.length); } /** * Converts a URL parameter name to a store key by adding the KEY_PREFIX. * @param paramName The URL parameter name (e.g., "page") * @returns The store key (e.g., "$$page") */ function paramNameToStoreKey(paramName) { return `${KEY_PREFIX}${paramName}`; } /** * Gets all store keys that represent query parameters (start with KEY_PREFIX). * @param renderer The renderer instance * @returns Array of store keys */ function getQueryStoreKeys(renderer) { return renderer.keys().filter((k) => k.startsWith(KEY_PREFIX)); } /** * Updates the renderer's store with values from URL search parameters. * @param renderer The renderer instance * @param url The URL to read parameters from */ async function updateStoreFromUrl(renderer, url) { for (const [paramName, value] of url.searchParams.entries()) { const storeKey = paramNameToStoreKey(paramName); if (renderer.get(storeKey) !== value) { await renderer.set(storeKey, value); } } } /** * Updates URL search parameters based on a store key and value. * @param url The URL to modify * @param storeKey The store key * @param value The value to set * @returns Whether the URL was changed */ function updateUrlFromStoreValue(url, storeKey, value) { const paramName = _storeKeyToParamName(storeKey); let changed = false; if (!value) { if (url.searchParams.has(paramName)) { url.searchParams.delete(paramName); changed = true; } } else { const newVal = String(value); if (url.searchParams.get(paramName) !== newVal) { url.searchParams.set(paramName, newVal); changed = true; } } return changed; } /** * Updates the URL search parameters based on the renderer's store. * It checks all keys that start with KEY_PREFIX and updates the URL accordingly. * @param url The URL to modify * @param renderer The renderer instance */ function updateUrlFromStore(url, renderer) { let changed = false; for (const key of getQueryStoreKeys(renderer)) { if (updateUrlFromStoreValue(url, key, renderer.get(key))) { changed = true; } } if (changed) { globalThis.window?.history?.replaceState({}, "", url.toString()); } } /** * Creates a handler that updates the renderer's store based on the current * URL's query parameters. This is used for the `popstate` event. * @param renderer The renderer instance. * @returns A function to be used as an event listener. */ function createStoreUpdater(renderer) { return async () => { const url = getWindowURL(); const presentKeysInUrl = new Set(); // Add/update params in store from URL for (const [key, value] of url.searchParams.entries()) { const storeKey = paramNameToStoreKey(key); presentKeysInUrl.add(storeKey); if (renderer.get(storeKey) !== value) { renderer.set(storeKey, value); } } // Remove params from store that are no longer in URL const allQueryKeys = getQueryStoreKeys(renderer); for (const key of allQueryKeys) { if (!presentKeysInUrl.has(key)) { renderer.del(key); } } }; } /** * Sets up two-way binding between the renderer's keystore and URL query parameters. * It creates individual store keys for each parameter using the pattern `$$varname`, * allowing components to watch for specific parameter changes. It also listens for `popstate` * events to update the store when the user navigates through history. * * This function should be called once, for example in `IRenderer.mount()`. * * @param renderer The renderer instance. */ export async function setupQueryParamBindings(renderer) { const url = getWindowURL(); // First, update store with URL parameters (URL takes precedence over existing store values) await updateStoreFromUrl(renderer, url); // Then, update URL with any store defaults that aren't already in the URL updateUrlFromStore(url, renderer); // Set up the URL updater to listen for changes in the store. renderer.addKeyHandler(/^\$\$/, (key, value) => { const url = getWindowURL(); const changed = updateUrlFromStoreValue(url, key, value); if (changed) { globalThis.window?.history?.replaceState({}, "", url.toString()); } }); // Set up the popstate listener to update the store when the URL changes. globalThis.window?.addEventListener("popstate", createStoreUpdater(renderer)); } //# sourceMappingURL=query.js.map