@codeperate/cdp-ui-library
Version:
Codeperate UI Library
284 lines (277 loc) • 8.31 kB
JavaScript
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();