Implement context mechanism to pass default attributes to descendant components.
103 lines
3.2 KiB
JavaScript
103 lines
3.2 KiB
JavaScript
"use strict";
|
|
|
|
import m from "mithril";
|
|
import parameter from "./components/parameter";
|
|
import parameterList from "./components/parameter-list";
|
|
import parameterTable from "./components/parameter-table";
|
|
import overviewDot from "./components/overview-dot";
|
|
import container from "./components/container";
|
|
import summonButton from "./components/summon-button";
|
|
import deviceFaults from "./components/device-faults";
|
|
import allParameters from "./components/all-parameters";
|
|
import deviceActions from "./components/device-actions";
|
|
import tags from "./components/tags";
|
|
import ping from "./components/ping";
|
|
import deviceLink from "./components/device-link";
|
|
import longTextComponent from "./long-text-component";
|
|
|
|
const comps = {
|
|
parameter,
|
|
"parameter-list": parameterList,
|
|
"parameter-table": parameterTable,
|
|
"overview-dot": overviewDot,
|
|
container,
|
|
"summon-button": summonButton,
|
|
"device-faults": deviceFaults,
|
|
"all-parameters": allParameters,
|
|
"device-actions": deviceActions,
|
|
tags,
|
|
ping,
|
|
"device-link": deviceLink,
|
|
"long-text": longTextComponent
|
|
};
|
|
|
|
const contextifiedComponents = new WeakMap();
|
|
const vnodeContext = new WeakMap();
|
|
|
|
function contextFn(context, ...argumentsList) {
|
|
const vnode = Reflect.apply(M, undefined, argumentsList);
|
|
vnodeContext.set(vnode, context);
|
|
return vnode;
|
|
}
|
|
|
|
const M = new Proxy(m, {
|
|
apply: (target, thisArg, argumentsList) => {
|
|
const c = argumentsList[0];
|
|
if (typeof c !== "string") argumentsList[0] = contextifyComponent(c);
|
|
else if (comps[c]) argumentsList[0] = contextifyComponent(comps[c]);
|
|
|
|
return Reflect.apply(target, undefined, argumentsList);
|
|
},
|
|
get: (target, prop) => {
|
|
if (prop === "context") return contextFn;
|
|
else return Reflect.get(target, prop);
|
|
}
|
|
});
|
|
|
|
function applyContext(vnode, parentContext) {
|
|
if (Array.isArray(vnode)) {
|
|
for (const c of vnode) applyContext(c, parentContext);
|
|
} else if (vnode && typeof vnode === "object" && vnode.tag) {
|
|
const vc = Object.assign({}, parentContext, vnodeContext.get(vnode));
|
|
vnodeContext.set(vnode, vc);
|
|
vnode.attrs = Object.assign({}, vc, vnode.attrs);
|
|
if (vnode.children && vnode.children.length)
|
|
for (const c of vnode.children) applyContext(c, vc);
|
|
}
|
|
}
|
|
|
|
export function contextifyComponent(component) {
|
|
let c = contextifiedComponents.get(component);
|
|
if (!c) {
|
|
if (typeof component !== "function") {
|
|
c = Object.assign({}, component);
|
|
const view = component.view;
|
|
c.view = vnode => {
|
|
const context = vnodeContext.get(vnode) || {};
|
|
const res = view(vnode, M);
|
|
applyContext(res, context);
|
|
return res;
|
|
};
|
|
} else if (!component.prototype.view) {
|
|
c = initialNode => {
|
|
const state = component(initialNode);
|
|
const view = state.view;
|
|
state.view = vnode => {
|
|
const context = vnodeContext.get(vnode) || {};
|
|
const res = view(vnode, M);
|
|
applyContext(res, context);
|
|
return res;
|
|
};
|
|
return state;
|
|
};
|
|
} else {
|
|
// TODO support class components
|
|
throw new Error("Class components not supported");
|
|
}
|
|
contextifiedComponents.set(component, c);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
export { M as m };
|