Refactor components

Implement context mechanism to pass default attributes to descendant
components.
This commit is contained in:
Zaid Abdulla committed 2019-02-12 23:11:27 -08:00
1 parent d46d133878
commit ee8c43747e
35 files changed
+1903 -1831

No files matched your search

+71 -4
View File
@@ -1,5 +1,6 @@
"use strict";
import m from "mithril";
import parameter from "./components/parameter";
import parameterList from "./components/parameter-list";
import parameterTable from "./components/parameter-table";
@@ -12,6 +13,7 @@ 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,
@@ -25,11 +27,76 @@ const comps = {
"device-actions": deviceActions,
tags,
ping,
"device-link": deviceLink
"device-link": deviceLink,
"long-text": longTextComponent
};
export function get(name) {
const c = comps[name];
if (!c) throw new Error(`No such component '${name}'`);
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 };