fluxel
Version:
An ultra-lightweight, high-performance library for efficient DOM building and dynamic web UIs
236 lines (235 loc) • 9.57 kB
JavaScript
;
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"));
}