77 lines
2.5 KiB
JavaScript
77 lines
2.5 KiB
JavaScript
(function exposePageModuleRuntime(global) {
|
|
"use strict";
|
|
|
|
const definitions = new Map();
|
|
const featureBindings = new Map();
|
|
let sealed = false;
|
|
|
|
function register(feature, viewIds, lifecycle = {}) {
|
|
if (sealed) throw new Error("Page module registry is already sealed");
|
|
if (!feature || !Array.isArray(viewIds) || !viewIds.length) {
|
|
throw new Error("Page modules require a feature and at least one view ID");
|
|
}
|
|
if (typeof lifecycle.bind === "function") {
|
|
const existing = featureBindings.get(feature);
|
|
if (existing && existing !== lifecycle.bind) {
|
|
throw new Error(`Duplicate feature binding owner: ${feature}`);
|
|
}
|
|
featureBindings.set(feature, lifecycle.bind);
|
|
}
|
|
viewIds.forEach((viewId) => {
|
|
if (definitions.has(viewId)) throw new Error(`Duplicate page module: ${viewId}`);
|
|
definitions.set(viewId, Object.freeze({
|
|
feature,
|
|
viewId,
|
|
enter: Object.freeze([...(lifecycle.enter || [])]),
|
|
leave: Object.freeze([...(lifecycle.leave || [])]),
|
|
}));
|
|
});
|
|
}
|
|
|
|
function create(options) {
|
|
sealed = true;
|
|
const pages = options.pages;
|
|
const actions = Object.freeze({ ...(options.actions || {}) });
|
|
const missing = pages.all.filter((page) => !definitions.has(page.id)).map((page) => page.id);
|
|
if (missing.length) throw new Error(`Missing page modules: ${missing.join(", ")}`);
|
|
let eventsBound = false;
|
|
|
|
function bind() {
|
|
if (eventsBound) return;
|
|
eventsBound = true;
|
|
featureBindings.forEach((handler) => handler());
|
|
}
|
|
|
|
function run(actionNames, context) {
|
|
actionNames.forEach((actionName) => {
|
|
const action = actions[actionName];
|
|
if (typeof action !== "function") throw new Error(`Unknown page action: ${actionName}`);
|
|
action(context);
|
|
});
|
|
}
|
|
|
|
function beforeMount(viewId, previousView) {
|
|
actions.closeTransientUi?.({ viewId, previousView });
|
|
if (previousView && previousView !== viewId) {
|
|
run(definitions.get(previousView)?.leave || [], { viewId, previousView });
|
|
}
|
|
}
|
|
|
|
function afterMount(viewId, previousView) {
|
|
const context = { viewId, previousView };
|
|
actions.applyAccess?.(context);
|
|
run(definitions.get(viewId)?.enter || [], context);
|
|
}
|
|
|
|
return Object.freeze({
|
|
afterMount,
|
|
beforeMount,
|
|
bind,
|
|
get: (viewId) => definitions.get(viewId) || null,
|
|
has: (viewId) => definitions.has(viewId),
|
|
});
|
|
}
|
|
|
|
global.XiaobaiPageModules = Object.freeze({ create, register });
|
|
})(window);
|