UNPKG

fluxel

Version:

An ultra-lightweight, high-performance library for efficient DOM building and dynamic web UIs

236 lines (235 loc) 9.57 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: all[name] }); } _export(exports, { createRouter: function() { return createRouter; }, globalNavigate: function() { return globalNavigate; }, routerEventTarget: function() { return routerEventTarget; }, useRouter: function() { return useRouter; } }); const _baseReactive = /*#__PURE__*/ _interop_require_wildcard(require("../baseReactive.js")); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interop_require_wildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = { __proto__: null }; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for(var key in obj){ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } const routerEventTarget = new EventTarget(); if (typeof window !== "undefined") { window.addEventListener("popstate", (event)=>{ routerEventTarget.dispatchEvent(new CustomEvent("popstate", { detail: event })); }); window.addEventListener("hashchange", (event)=>{ routerEventTarget.dispatchEvent(new CustomEvent("popstate", { detail: event })); }); } const defaultFallback = ()=>_baseReactive.default.div({ dataset: { fluxelRouterFallback: "1" } }); const routerInstanceStack = []; const noop = ()=>{}; function createRouter(config, options = {}) { const basePath = options.basePath || ""; const mode = options.mode || "history"; const regexMap = []; Object.keys(config).forEach((pattern)=>{ const regex = new RegExp(`^${pattern.replace(/\/\*?$/, "").replace(RegExp("(?<cap>:[^\\s/]+)\\?\\/", "g"), "($<cap>\/)?").replace(RegExp(":(?<name>[^\\s\\?/]+)", "g"), "(?<$<name>>[^/]+)")}${pattern.endsWith("/*") ? "(/.*)?" : "/?"}$`); regexMap.push([ regex, pattern ]); }); const generateReactiveState = (query)=>{ let state = null; _baseReactive.default.reactive(query, (_state)=>{ state = _state; }); return state; }; let onceRendered = false; let deferredPromise = null; const router = { basePath, pathname: "/", route: null, params: {}, query: null, get mode () { return mode; }, Provider (props) { return _baseReactive.default.reactive({ children: null }, (state)=>{ const updateChildrenInternal = (route, children)=>{ onceRendered = true; if (deferredPromise) { Promise.all(deferredPromise).then(()=>{ if (!route || router.route === route) { state.children = children; } deferredPromise = null; }); return; } else { state.children = children; } }; const updateChildren = ()=>{ const url = new URL(location.href); const path = mode === "history" ? url.pathname.replace(basePath, "") : url.hash.replace(/^#/, "").replace(basePath, ""); const searchParams = new URLSearchParams(url.search); router.pathname = path; const matched = regexMap.find((regexPattern)=>regexPattern[0].test(path)); if (matched) { const regex = matched[0]; const route = matched[1]; const matchGroups = regex.exec(path).groups || {}; if (router.route === route && JSON.stringify(router.params) === JSON.stringify(matchGroups)) { Object.keys(router.query).filter((key)=>!searchParams.has(key) && !_baseReactive.pureStateReservedKeys.includes(key)).forEach((key)=>{ router.query[key] = undefined; }); searchParams.forEach((value, key)=>{ router.query[key] = value; }); return; } router.route = route; router.params = matchGroups; const query = {}; searchParams.forEach((value, key)=>{ query[key] = value; }); router.query = generateReactiveState(query); routerInstanceStack.push(router); const children = config[route](); routerInstanceStack.pop(); if (children instanceof Promise) { var _options_fallback; children.then((resolvedChildren)=>{ config[route] = resolvedChildren; if (router.route !== route) return; routerInstanceStack.push(router); updateChildrenInternal(route, resolvedChildren()); routerInstanceStack.pop(); }); updateChildrenInternal(route, ((_options_fallback = options.fallback) === null || _options_fallback === void 0 ? void 0 : _options_fallback.call(options)) || defaultFallback()); } else { updateChildrenInternal(route, children); } } else { var _options_fallback1; router.route = null; router.params = {}; const query = {}; searchParams.forEach((value, key)=>{ query[key] = value; }); router.query = generateReactiveState(query); updateChildrenInternal(null, ((_options_fallback1 = options.fallback) === null || _options_fallback1 === void 0 ? void 0 : _options_fallback1.call(options)) || defaultFallback()); } }; routerEventTarget.addEventListener("popstate", ()=>updateChildren()); updateChildren(); return _baseReactive.default.div({ dataset: { fluxelRouter: "1" }, classList: (props === null || props === void 0 ? void 0 : props.classList) || [], children: state.use("children", (children)=>{ var _options_fallback; return children ? children : ((_options_fallback = options.fallback) === null || _options_fallback === void 0 ? void 0 : _options_fallback.call(options)) || defaultFallback(); }) }); }); }, navigate (path, options = {}) { if (router.mode === "hash") { path = `#${router.basePath}${path}`; } globalNavigate(path, options); }, deferUpdate (process) { if (!onceRendered) { process().catch(noop); } else { deferredPromise = deferredPromise || []; deferredPromise.push(process().catch(noop)); } } }; return router; } const useRouter = (option)=>{ const optional = (option === null || option === void 0 ? void 0 : option.optional) || false; const result = routerInstanceStack[routerInstanceStack.length - 1]; if (!result && !optional) { throw new Error("No router instance found. Make sure the component is rendered within a Router Provider."); } return result || null; }; function globalNavigate(path, options = {}) { const replace = options.replace || false; if (replace) { window.history.replaceState({}, "", path); } else { window.history.pushState({}, "", path); } routerEventTarget.dispatchEvent(new CustomEvent("popstate")); }