-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreateComponent.js
60 lines (60 loc) · 2.52 KB
/
createComponent.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import ComponentProxyHandler from './ComponentProxyHandler.js'
import Expression from './Expression.js';
export default function createComponent(tagName, templateSelector, componentFactory) {
let Base = class {
constructor() {
let proxyHandler = new ComponentProxyHandler();
return new Proxy(this, proxyHandler);
}
};
let ComponentClass = componentFactory(Base);
const ComponentElement = class extends HTMLElement {
constructor() {
super();
const scope = new ComponentClass();
const template = document.querySelector(templateSelector);
const stampedTemplate = document.importNode(template.content, true);
this.compileContentExpressions(scope, template);
this.compileAttributeExpressions(scope, template);
this.compileEventListeners(scope, template);
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.appendChild(stampedTemplate);
}
compileContentExpressions(scope, template) {
const xPath = '//text()[contains(.,"${") and contains(substring-after(.,"${"),"}")]';
for (let textNode of this.evaluateXPaht(template, xPath)) {
const callable = new Function('', `with (this) return \`${textNode.nodeValue}\`;`);
const observer = value => { textNode.nodeValue = value; };
new Expression(scope, callable, observer);
}
}
compileAttributeExpressions(scope, template) {
const xPath = '//@*[starts-with(name(), ":")]';
for (let attribute of this.evaluateXPaht(template, xPath)) {
const callable = new Function('', `with (this) return ${attribute.value};`);
const name = attribute.name.substr(1);
const element = attribute.ownerElement;
const observer = value => { element.setAttribute(name, value); };
new Expression(scope, callable, observer);
element.removeAttribute(attribute.name);
}
}
compileEventListeners(scope, template) {
const xPath = '//@*[starts-with(name(), "@")]';
for (let attribute of this.evaluateXPaht(template, xPath)) {
const callable = new Function('$event', `with (this) { ${attribute.value}; }`);
attribute.ownerElement.addEventListener(attribute.name.substr(1), event => callable.call(scope, event));
attribute.ownerElement.removeAttribute(attribute.name);
}
}
*evaluateXPaht(template, xPath) {
const xPathResult = document.evaluate(
xPath, template.firstElementChild, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null
);
for (let index = 0; index < xPathResult.snapshotLength; index += 1) {
yield xPathResult.snapshotItem(index);
}
}
};
customElements.define(tagName, ComponentElement);
}