Skip to content

Instantly share code, notes, and snippets.

@zserge
Last active October 31, 2019 21:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zserge/298a99e65a2c8f8357a37f91f560d4f0 to your computer and use it in GitHub Desktop.
Save zserge/298a99e65a2c8f8357a37f91f560d4f0 to your computer and use it in GitHub Desktop.
const h = (e, p = {}, ...c) => ({e, p, c: [].concat(...c)});
const render = (vlist, dom) => {
vlist = [].concat(vlist);
vlist.forEach((v, i) => {
let n = dom.childNodes[i];
let s = (v.s = (n ? n.s : v.s) || {});
while (typeof v.e === 'function') {
v = v.e(v.p, s, u => Object.assign(s, u) && render(vlist, dom));
}
v.s = s;
let $ = () =>
v && v.e ? document.createElement(v.e) : document.createTextNode(v);
if (!n) {
dom.appendChild((n = $()));
} else if (n.e !== v.e && n.data !== v) {
const _n = n;
dom.replaceChild((n = $()), _n);
}
Object.assign(n, v);
if (v.e) {
for (let k in v.p) {
if (n[k] !== v.p[k]) {
n[k] = v.p[k];
}
}
render(v.c, n);
} else {
n.data = v;
}
});
for (let c; (c = dom.childNodes[vlist.length]); ) {
dom.removeChild(c);
}
};
const x = (strings, ...fields) => {
const stack = [{c: []}];
const find = (s, re, arg) => {
if (!s) {
return [s, arg];
}
let m = s.match(re);
return [s.substring(m[0].length), m[1]];
};
const MODE_TEXT = 0;
const MODE_OPEN = 1;
const MODE_CLOSE = 2;
let mode = MODE_TEXT;
strings.forEach((s, i) => {
while (s) {
s = s.trimLeft();
switch (mode) {
case MODE_TEXT:
if (s[0] === '<') {
if (s[1] === '/') {
[s, val] = find(s.substring(2), /^([a-zA-Z]+)/, fields[i]);
mode = MODE_CLOSE;
} else {
[s, val] = find(s.substring(1), /^([a-zA-Z]+)/, fields[i]);
mode = MODE_OPEN;
stack.push({e: val, p: {}, c: []});
}
} else {
[s, val] = find(s, /^([^<]+)/, '');
stack[stack.length - 1].c.push(val);
}
break;
case MODE_OPEN:
if (s[0] === '/' && s[1] === '>') {
s = s.substring(2);
stack[stack.length - 2].c.push(stack.pop());
mode = MODE_TEXT;
} else if (s[0] === '>') {
s = s.substring(1);
mode = MODE_TEXT;
} else {
let m = s.match(/^([a-zA-Z0-9]+)=/);
console.assert(m);
s = s.substring(m[0].length);
let k = m[1];
[s, val] = find(s, /^"([^"]*)"/, fields[i]);
stack[stack.length - 1].p[k] = val;
}
break;
case MODE_CLOSE:
console.assert(s[0] === '>');
stack[stack.length - 2].c.push(stack.pop());
s = s.substring(1);
mode = MODE_TEXT;
break;
}
}
if (mode === MODE_TEXT) {
stack[stack.length - 1].c.push(fields[i]);
}
});
return stack[0].c[0];
};
<html>
<head>
</head>
<body>
</body>
<script src="act.js"></script>
<script>
const Counter = (props, state, setState) => {
const {n = +props.n || 0} = state;
const add = (x) => setState({n: n + x });
return x`
<div className="foo" style="margin: 0; padding: 0">
<p>${props.name}: ${n}</p>
<button onclick=${() => add(1)}>+1</button>
<button onclick=${() => add(-1)}>-1</button>
</div>
`;
};
const app = (props, state, setState) => {
return x`
<div>
Two counters:
<${Counter} n="10" name="First" />
<hr />
<${Counter} n="5" name="Second" />
</div>
`;
};
render(h(app), document.body);
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment