UNPKG

@codeperate/cdp-ui-library

Version:

Codeperate UI Library

284 lines (277 loc) 8.31 kB
import { g as getRenderingRef, f as forceUpdate } from './index-de893d6b.js'; const appendToMap = (map, propName, value) => { const items = map.get(propName); if (!items) { map.set(propName, [value]); } else if (!items.includes(value)) { items.push(value); } }; const debounce = (fn, ms) => { let timeoutId; return (...args) => { if (timeoutId) { clearTimeout(timeoutId); } timeoutId = setTimeout(() => { timeoutId = 0; fn(...args); }, ms); }; }; /** * Check if a possible element isConnected. * The property might not be there, so we check for it. * * We want it to return true if isConnected is not a property, * otherwise we would remove these elements and would not update. * * Better leak in Edge than to be useless. */ const isConnected = (maybeElement) => !('isConnected' in maybeElement) || maybeElement.isConnected; const cleanupElements = debounce((map) => { for (let key of map.keys()) { map.set(key, map.get(key).filter(isConnected)); } }, 2000); const stencilSubscription = ({ on }) => { const elmsToUpdate = new Map(); if (typeof getRenderingRef === 'function') { // If we are not in a stencil project, we do nothing. // This function is not really exported by @stencil/core. on('dispose', () => { elmsToUpdate.clear(); }); on('get', (propName) => { const elm = getRenderingRef(); if (elm) { appendToMap(elmsToUpdate, propName, elm); } }); on('set', (propName) => { const elements = elmsToUpdate.get(propName); if (elements) { elmsToUpdate.set(propName, elements.filter(forceUpdate)); } cleanupElements(elmsToUpdate); }); on('reset', () => { elmsToUpdate.forEach((elms) => elms.forEach(forceUpdate)); cleanupElements(elmsToUpdate); }); } }; const createObservableMap = (defaultState, shouldUpdate = (a, b) => a !== b) => { let states = new Map(Object.entries(defaultState !== null && defaultState !== void 0 ? defaultState : {})); const handlers = { dispose: [], get: [], set: [], reset: [], }; const reset = () => { states = new Map(Object.entries(defaultState !== null && defaultState !== void 0 ? defaultState : {})); handlers.reset.forEach((cb) => cb()); }; const dispose = () => { // Call first dispose as resetting the state would // cause less updates ;) handlers.dispose.forEach((cb) => cb()); reset(); }; const get = (propName) => { handlers.get.forEach((cb) => cb(propName)); return states.get(propName); }; const set = (propName, value) => { const oldValue = states.get(propName); if (shouldUpdate(value, oldValue, propName)) { states.set(propName, value); handlers.set.forEach((cb) => cb(propName, value, oldValue)); } }; const state = (typeof Proxy === 'undefined' ? {} : new Proxy(defaultState, { get(_, propName) { return get(propName); }, ownKeys(_) { return Array.from(states.keys()); }, getOwnPropertyDescriptor() { return { enumerable: true, configurable: true, }; }, has(_, propName) { return states.has(propName); }, set(_, propName, value) { set(propName, value); return true; }, })); const on = (eventName, callback) => { handlers[eventName].push(callback); return () => { removeFromArray(handlers[eventName], callback); }; }; const onChange = (propName, cb) => { const unSet = on('set', (key, newValue) => { if (key === propName) { cb(newValue); } }); const unReset = on('reset', () => cb(defaultState[propName])); return () => { unSet(); unReset(); }; }; const use = (...subscriptions) => subscriptions.forEach((subscription) => { if (subscription.set) { on('set', subscription.set); } if (subscription.get) { on('get', subscription.get); } if (subscription.reset) { on('reset', subscription.reset); } }); const forceUpdate = (key) => { const oldValue = states.get(key); handlers.set.forEach((cb) => cb(key, oldValue, oldValue)); }; return { state, get, set, on, onChange, use, dispose, reset, forceUpdate, }; }; const removeFromArray = (array, item) => { const index = array.indexOf(item); if (index >= 0) { array[index] = array[array.length - 1]; array.length--; } }; const createStore = (defaultState, shouldUpdate) => { const map = createObservableMap(defaultState, shouldUpdate); stencilSubscription(map); return map; }; const createRouter = (opts) => { var _a; const win = window; const url = new URL(win.location.href); const parseURL = (_a = opts === null || opts === void 0 ? void 0 : opts.parseURL) !== null && _a !== void 0 ? _a : DEFAULT_PARSE_URL; const { state, onChange, dispose } = createStore({ url, activePath: parseURL(url) }, (newV, oldV, prop) => { if (prop === 'url') { return newV.href !== oldV.href; } return newV !== oldV; }); const push = (href) => { history.pushState(null, null, href); const url = new URL(href, document.baseURI); state.url = url; state.activePath = parseURL(url); }; const match = (routes) => { const { activePath } = state; for (let route of routes) { const params = matchPath(activePath, route.path); if (params) { if (route.to != null) { const to = (typeof route.to === 'string') ? route.to : route.to(activePath); push(to); return match(routes); } else { return { params, route }; } } } return undefined; }; const navigationChanged = () => { const url = new URL(win.location.href); state.url = url; state.activePath = parseURL(url); }; const Switch = (_, childrenRoutes) => { const result = match(childrenRoutes); if (result) { if (typeof result.route.jsx === 'function') { return result.route.jsx(result.params); } else { return result.route.jsx; } } }; const disposeRouter = () => { win.removeEventListener('popstate', navigationChanged); dispose(); }; const router = { Switch, get url() { return state.url; }, get activePath() { return state.activePath; }, push, onChange: onChange, dispose: disposeRouter, }; // Initial update navigationChanged(); // Listen URL changes win.addEventListener('popstate', navigationChanged); return router; }; const matchPath = (pathname, path) => { if (typeof path === 'string') { if (path === pathname) { return {}; } } else if (typeof path === 'function') { const params = path(pathname); if (params) { return params === true ? {} : { ...params }; } } else { const results = path.exec(pathname); if (results) { path.lastIndex = 0; return { ...results }; } } return undefined; }; const DEFAULT_PARSE_URL = (url) => { return url.pathname.toLowerCase(); }; createRouter();