chocola 2.0.0-next.6 → 2.0.0-next.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -5
- package/compiler/chalk.js +32 -32
- package/compiler/component-processor.js +129 -3
- package/compiler/dom-processor.js +0 -1
- package/compiler/index.js +6 -3
- package/compiler/runtime-generator.js +22 -5
- package/compiler/utils.js +52 -52
- package/package.json +1 -1
- package/parser/component.js +129 -129
- package/parser/context.js +29 -29
- package/parser/css.js +96 -96
- package/parser/index.js +5 -5
- package/parser/template.js +120 -121
- package/parser/utils.js +13 -13
- package/runtime/index.js +309 -0
package/parser/template.js
CHANGED
|
@@ -1,121 +1,120 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
condChain.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
condChain.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
1
|
+
import { compileExpression } from "./utils.js";
|
|
2
|
+
import { hasDelIfAttr, getDelIfAttr, removeDelIfAttr } from "./context.js";
|
|
3
|
+
|
|
4
|
+
export const reservedAttrs = ["if", "del:if", "elif", "else"];
|
|
5
|
+
|
|
6
|
+
export function getLineNumber(sourceContent, parentContent, idxInParent) {
|
|
7
|
+
if (sourceContent === parentContent) {
|
|
8
|
+
return parentContent.substring(0, idxInParent).split("\n").length;
|
|
9
|
+
}
|
|
10
|
+
const templateStartMatch = sourceContent.match(/<template[^>]*>/);
|
|
11
|
+
if (templateStartMatch) {
|
|
12
|
+
const contentStart = templateStartMatch.index + templateStartMatch[0].length;
|
|
13
|
+
const beforeContent = sourceContent.substring(0, contentStart);
|
|
14
|
+
const linesInBefore = beforeContent.split("\n").length;
|
|
15
|
+
const linesInTemplate = parentContent.substring(0, idxInParent).split("\n").length;
|
|
16
|
+
return linesInBefore + linesInTemplate - 1;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function validateChainStructure(parent, sourceFile, sourceContent, parentContent) {
|
|
22
|
+
const children = [...parent.children];
|
|
23
|
+
let chainActive = false;
|
|
24
|
+
|
|
25
|
+
for (const child of children) {
|
|
26
|
+
const hasIf = child.hasAttribute("if");
|
|
27
|
+
const hasDelIf = hasDelIfAttr(child);
|
|
28
|
+
const hasElif = child.hasAttribute("elif");
|
|
29
|
+
const hasElse = child.hasAttribute("else");
|
|
30
|
+
|
|
31
|
+
if (hasElif || hasElse) {
|
|
32
|
+
if (!chainActive) {
|
|
33
|
+
const tag = child.tagName.toLowerCase();
|
|
34
|
+
const attr = hasElif ? "elif" : "else";
|
|
35
|
+
let loc = sourceFile;
|
|
36
|
+
if (sourceContent && parentContent) {
|
|
37
|
+
const idx = parentContent.indexOf(child.outerHTML);
|
|
38
|
+
if (idx !== -1) {
|
|
39
|
+
const lineNum = getLineNumber(sourceContent, parentContent, idx);
|
|
40
|
+
if (lineNum !== null) loc = `${sourceFile}:${lineNum}`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
throw new Error(`${loc}\n <${tag}> has ${attr} without a preceding if/del-if sibling`);
|
|
44
|
+
}
|
|
45
|
+
if (hasElse) {
|
|
46
|
+
chainActive = false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (hasIf || hasDelIf) {
|
|
51
|
+
chainActive = true;
|
|
52
|
+
} else if (!hasElif && !hasElse) {
|
|
53
|
+
chainActive = false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
validateChainStructure(child, sourceFile, sourceContent, parentContent);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function applyConditionalToElement(child, ctxProxy, condChain, hasIf, hasDelIf, hasElif, hasElse) {
|
|
61
|
+
if (hasIf) {
|
|
62
|
+
const expr = child.getAttribute("if").slice(1, -1);
|
|
63
|
+
const fn = compileExpression(expr, true);
|
|
64
|
+
condChain.active = true;
|
|
65
|
+
if (fn(ctxProxy)) {
|
|
66
|
+
condChain.rendered = true;
|
|
67
|
+
} else {
|
|
68
|
+
child.style.display = "none";
|
|
69
|
+
condChain.rendered = false;
|
|
70
|
+
}
|
|
71
|
+
child.removeAttribute("if");
|
|
72
|
+
} else if (hasDelIf) {
|
|
73
|
+
const expr = getDelIfAttr(child).slice(1, -1);
|
|
74
|
+
const fn = compileExpression(expr, true);
|
|
75
|
+
condChain.active = true;
|
|
76
|
+
if (fn(ctxProxy)) {
|
|
77
|
+
condChain.rendered = true;
|
|
78
|
+
} else {
|
|
79
|
+
child.remove();
|
|
80
|
+
condChain.rendered = false;
|
|
81
|
+
}
|
|
82
|
+
removeDelIfAttr(child);
|
|
83
|
+
} else if (hasElif) {
|
|
84
|
+
const expr = child.getAttribute("elif").slice(1, -1);
|
|
85
|
+
const fn = compileExpression(expr, true);
|
|
86
|
+
if (fn(ctxProxy)) {
|
|
87
|
+
condChain.rendered = true;
|
|
88
|
+
} else {
|
|
89
|
+
child.remove();
|
|
90
|
+
}
|
|
91
|
+
child.removeAttribute("elif");
|
|
92
|
+
} else if (hasElse) {
|
|
93
|
+
condChain.rendered = true;
|
|
94
|
+
condChain.active = false;
|
|
95
|
+
child.removeAttribute("else");
|
|
96
|
+
} else {
|
|
97
|
+
condChain.active = false;
|
|
98
|
+
condChain.rendered = false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function interpolateNode(root, ctxProxy) {
|
|
103
|
+
const stack = [root];
|
|
104
|
+
while (stack.length) {
|
|
105
|
+
const node = stack.pop();
|
|
106
|
+
if (node.nodeType === 3) {
|
|
107
|
+
node.textContent = node.textContent.replace(/\{([^}]+)\}/g, (_, expr) => {
|
|
108
|
+
try {
|
|
109
|
+
return compileExpression(expr, true)(ctxProxy);
|
|
110
|
+
} catch {
|
|
111
|
+
return "";
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
} else {
|
|
115
|
+
for (let i = node.childNodes.length - 1; i >= 0; i--) {
|
|
116
|
+
stack.push(node.childNodes[i]);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
package/parser/utils.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
const compiledCache = new Map();
|
|
2
|
-
|
|
3
|
-
export function compileExpression(expr, useCtx) {
|
|
4
|
-
const key = useCtx ? `ctx:${expr}` : `raw:${expr}`;
|
|
5
|
-
let fn = compiledCache.get(key);
|
|
6
|
-
if (!fn) {
|
|
7
|
-
fn = useCtx
|
|
8
|
-
? new Function("ctx", `with(ctx) { return (${expr}); }`)
|
|
9
|
-
: new Function(`"use strict"; return (${expr})`);
|
|
10
|
-
compiledCache.set(key, fn);
|
|
11
|
-
}
|
|
12
|
-
return fn;
|
|
13
|
-
}
|
|
1
|
+
const compiledCache = new Map();
|
|
2
|
+
|
|
3
|
+
export function compileExpression(expr, useCtx) {
|
|
4
|
+
const key = useCtx ? `ctx:${expr}` : `raw:${expr}`;
|
|
5
|
+
let fn = compiledCache.get(key);
|
|
6
|
+
if (!fn) {
|
|
7
|
+
fn = useCtx
|
|
8
|
+
? new Function("ctx", `with(ctx) { return (${expr}); }`)
|
|
9
|
+
: new Function(`"use strict"; return (${expr})`);
|
|
10
|
+
compiledCache.set(key, fn);
|
|
11
|
+
}
|
|
12
|
+
return fn;
|
|
13
|
+
}
|
package/runtime/index.js
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
const exprCache = new Map();
|
|
2
|
+
|
|
3
|
+
function compileExpression(expr, useCtx) {
|
|
4
|
+
const key = useCtx ? "ctx:" + expr : "raw:" + expr;
|
|
5
|
+
let fn = exprCache.get(key);
|
|
6
|
+
if (!fn) {
|
|
7
|
+
fn = useCtx
|
|
8
|
+
? new Function("ctx", "with(ctx) { return (" + expr + "); }")
|
|
9
|
+
: new Function('"use strict"; return (' + expr + ")");
|
|
10
|
+
exprCache.set(key, fn);
|
|
11
|
+
}
|
|
12
|
+
return fn;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function interpolateNode(root, ctx) {
|
|
16
|
+
const stack = [root];
|
|
17
|
+
while (stack.length) {
|
|
18
|
+
const node = stack.pop();
|
|
19
|
+
if (node.nodeType === 3) {
|
|
20
|
+
const text = node.textContent
|
|
21
|
+
.replace(new RegExp(LBRACE_PH, "g"), "{")
|
|
22
|
+
.replace(new RegExp(RBRACE_PH, "g"), "}");
|
|
23
|
+
node.textContent = text.replace(/\{([^}]+)\}/g, (_, expr) => {
|
|
24
|
+
try {
|
|
25
|
+
return String(compileExpression(expr, true)(ctx));
|
|
26
|
+
} catch {
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
} else {
|
|
31
|
+
for (let i = node.childNodes.length - 1; i >= 0; i--) {
|
|
32
|
+
stack.push(node.childNodes[i]);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function applyConditionalToElement(child, ctx, chain, hasIf, hasDelIf, hasElif, hasElse) {
|
|
39
|
+
if (hasIf) {
|
|
40
|
+
const expr = child.getAttribute("if").slice(1, -1);
|
|
41
|
+
chain.active = true;
|
|
42
|
+
if (compileExpression(expr, true)(ctx)) {
|
|
43
|
+
chain.rendered = true;
|
|
44
|
+
} else {
|
|
45
|
+
child.style.display = "none";
|
|
46
|
+
chain.rendered = false;
|
|
47
|
+
}
|
|
48
|
+
child.removeAttribute("if");
|
|
49
|
+
} else if (hasDelIf) {
|
|
50
|
+
const expr = child.getAttribute("del:if").slice(1, -1);
|
|
51
|
+
chain.active = true;
|
|
52
|
+
if (compileExpression(expr, true)(ctx)) {
|
|
53
|
+
chain.rendered = true;
|
|
54
|
+
} else {
|
|
55
|
+
child.remove();
|
|
56
|
+
chain.rendered = false;
|
|
57
|
+
}
|
|
58
|
+
child.removeAttribute("del:if");
|
|
59
|
+
} else if (hasElif) {
|
|
60
|
+
const expr = child.getAttribute("elif").slice(1, -1);
|
|
61
|
+
if (compileExpression(expr, true)(ctx)) {
|
|
62
|
+
chain.rendered = true;
|
|
63
|
+
} else {
|
|
64
|
+
child.remove();
|
|
65
|
+
}
|
|
66
|
+
child.removeAttribute("elif");
|
|
67
|
+
} else if (hasElse) {
|
|
68
|
+
chain.rendered = true;
|
|
69
|
+
chain.active = false;
|
|
70
|
+
child.removeAttribute("else");
|
|
71
|
+
} else {
|
|
72
|
+
chain.active = false;
|
|
73
|
+
chain.rendered = false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const LBRACE_PH = "_%%CHOCOLA-LBRACE%%_";
|
|
78
|
+
const RBRACE_PH = "_%%CHOCOLA-RBRACE%%_";
|
|
79
|
+
|
|
80
|
+
function interpolateAttributes(element, ctx) {
|
|
81
|
+
for (const el of [element, ...element.querySelectorAll("*")]) {
|
|
82
|
+
for (const attr of [...el.attributes]) {
|
|
83
|
+
const val = attr.value;
|
|
84
|
+
if (!val.includes("{") && !val.includes(LBRACE_PH)) continue;
|
|
85
|
+
const raw = val
|
|
86
|
+
.replace(new RegExp(LBRACE_PH, "g"), "{")
|
|
87
|
+
.replace(new RegExp(RBRACE_PH, "g"), "}");
|
|
88
|
+
attr.value = raw.replace(/\{([^}]+)\}/g, (_, expr) => {
|
|
89
|
+
try {
|
|
90
|
+
return String(compileExpression(expr, true)(ctx));
|
|
91
|
+
} catch {
|
|
92
|
+
return "";
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function processConditionals(element, ctx, chain) {
|
|
100
|
+
for (const child of [...element.children]) {
|
|
101
|
+
const hasIf = child.hasAttribute("if");
|
|
102
|
+
const hasDelIf = child.hasAttribute("del:if");
|
|
103
|
+
const hasElif = child.hasAttribute("elif");
|
|
104
|
+
const hasElse = child.hasAttribute("else");
|
|
105
|
+
|
|
106
|
+
if ((hasElif || hasElse) && chain.rendered) {
|
|
107
|
+
child.remove();
|
|
108
|
+
if (hasElse) chain.active = false;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
applyConditionalToElement(child, ctx, chain, hasIf, hasDelIf, hasElif, hasElse);
|
|
113
|
+
|
|
114
|
+
if (child.parentNode) {
|
|
115
|
+
processConditionals(child, ctx, chain);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function processSlots(element, projectedChildren) {
|
|
121
|
+
const slots = element.querySelectorAll("slot");
|
|
122
|
+
for (const slot of slots) {
|
|
123
|
+
const clone = projectedChildren.map(c => c.cloneNode(true));
|
|
124
|
+
slot.replaceWith(...clone);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let bindCounter = 0;
|
|
129
|
+
|
|
130
|
+
function processBindAttributes(element) {
|
|
131
|
+
const bindings = [];
|
|
132
|
+
for (const attr of [...element.attributes]) {
|
|
133
|
+
if (attr.name.startsWith("bind:")) {
|
|
134
|
+
const prop = attr.name.slice(5);
|
|
135
|
+
const varName = attr.value;
|
|
136
|
+
const bindId = "b" + (bindCounter++);
|
|
137
|
+
element.setAttribute("data-chbind-" + bindId, "");
|
|
138
|
+
bindings.push({ prop, varName, bindId });
|
|
139
|
+
element.removeAttribute(attr.name);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return bindings;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function resolveDataBindings(element) {
|
|
146
|
+
const ids = [];
|
|
147
|
+
for (const attr of [...element.attributes]) {
|
|
148
|
+
const match = attr.name.match(/^data-chbind-(b\d+)$/);
|
|
149
|
+
if (match) {
|
|
150
|
+
ids.push(match[1]);
|
|
151
|
+
element.removeAttribute(attr.name);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return ids;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function collectBindings(root) {
|
|
158
|
+
const bindingDefs = [];
|
|
159
|
+
const idToEl = {};
|
|
160
|
+
|
|
161
|
+
for (const el of [root, ...root.querySelectorAll("*")]) {
|
|
162
|
+
bindingDefs.push(...processBindAttributes(el));
|
|
163
|
+
for (const id of resolveDataBindings(el)) {
|
|
164
|
+
idToEl[id] = el;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return { bindingDefs, idToEl };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
class ChocolaComponent {
|
|
171
|
+
#template;
|
|
172
|
+
#hash;
|
|
173
|
+
#runtime;
|
|
174
|
+
#defaultProps;
|
|
175
|
+
#bakedComponent;
|
|
176
|
+
#ctx;
|
|
177
|
+
#eventListeners = [];
|
|
178
|
+
#children;
|
|
179
|
+
#childComponents;
|
|
180
|
+
|
|
181
|
+
constructor(config = {}) {
|
|
182
|
+
this.#template = config.template || "";
|
|
183
|
+
this.#hash = config.hash || "";
|
|
184
|
+
this.#runtime = config.runtime || null;
|
|
185
|
+
this.#defaultProps = config.props || {};
|
|
186
|
+
this.#childComponents = config.children || [];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
#collectListeners(root, fn) {
|
|
190
|
+
const allEls = [root, ...root.querySelectorAll("*")];
|
|
191
|
+
const patches = [];
|
|
192
|
+
|
|
193
|
+
for (const el of allEls) {
|
|
194
|
+
const original = el.addEventListener;
|
|
195
|
+
el.addEventListener = (event, handler, options) => {
|
|
196
|
+
this.#eventListeners.push({ el, event, handler, options });
|
|
197
|
+
original.call(el, event, handler, options);
|
|
198
|
+
};
|
|
199
|
+
patches.push({ el, original });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
fn(root, this.#ctx);
|
|
204
|
+
} finally {
|
|
205
|
+
for (const { el, original } of patches) {
|
|
206
|
+
el.addEventListener = original;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
#init(ctx) {
|
|
212
|
+
const mergedCtx = { ...this.#defaultProps, ...ctx };
|
|
213
|
+
|
|
214
|
+
const container = document.createElement("template");
|
|
215
|
+
container.innerHTML = this.#template;
|
|
216
|
+
const fragment = container.content;
|
|
217
|
+
const root = fragment.firstElementChild;
|
|
218
|
+
|
|
219
|
+
if (!root) {
|
|
220
|
+
console.error(`${this.#hash}: component template must have a single root element`);
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
processSlots(root, this.#children || []);
|
|
225
|
+
processConditionals(root, mergedCtx, { active: false, rendered: false });
|
|
226
|
+
interpolateAttributes(root, mergedCtx);
|
|
227
|
+
interpolateNode(root, mergedCtx);
|
|
228
|
+
root.classList.add(this.#hash);
|
|
229
|
+
|
|
230
|
+
const { bindingDefs, idToEl } = collectBindings(root);
|
|
231
|
+
this.#ctx = { ...mergedCtx };
|
|
232
|
+
for (const { prop, varName, bindId } of bindingDefs) {
|
|
233
|
+
const el = idToEl[bindId];
|
|
234
|
+
if (el) {
|
|
235
|
+
this.#ctx[varName] = prop === "self" ? el : el[prop];
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (this.#runtime) {
|
|
240
|
+
this.#collectListeners(root, (self, ctx) => {
|
|
241
|
+
this.#runtime(self, ctx);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
this.#bakedComponent = root;
|
|
246
|
+
container.remove();
|
|
247
|
+
return root;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
#childInstances = [];
|
|
251
|
+
|
|
252
|
+
#mountChildren(root) {
|
|
253
|
+
for (const { tag, compClass } of this.#childComponents) {
|
|
254
|
+
const els = root.querySelectorAll(tag);
|
|
255
|
+
for (const el of els) {
|
|
256
|
+
const props = {};
|
|
257
|
+
for (const attr of [...el.attributes]) {
|
|
258
|
+
if (attr.name.startsWith("data-ch") || attr.name === "if" || attr.name === "del:if" || attr.name === "elif" || attr.name === "else") continue;
|
|
259
|
+
props[attr.name] = attr.value;
|
|
260
|
+
}
|
|
261
|
+
const instance = new compClass();
|
|
262
|
+
instance.mount(el.parentNode, props);
|
|
263
|
+
this.#childInstances.push(instance);
|
|
264
|
+
el.remove();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
mount(target, ctx = {}) {
|
|
270
|
+
if (target.nodeType !== 1) {
|
|
271
|
+
console.error(`${this.#hash}: mount target is not a valid DOM node`);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
this.#children = [...target.children];
|
|
275
|
+
this.#init(ctx);
|
|
276
|
+
if (this.#bakedComponent) {
|
|
277
|
+
target.appendChild(this.#bakedComponent);
|
|
278
|
+
this.#mountChildren(this.#bakedComponent);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
remove() {
|
|
283
|
+
for (const { el, event, handler, options } of this.#eventListeners) {
|
|
284
|
+
el.removeEventListener(event, handler, options);
|
|
285
|
+
}
|
|
286
|
+
this.#eventListeners = [];
|
|
287
|
+
for (const child of this.#childInstances) {
|
|
288
|
+
child.remove();
|
|
289
|
+
}
|
|
290
|
+
this.#childInstances = [];
|
|
291
|
+
if (this.#bakedComponent) {
|
|
292
|
+
this.#bakedComponent.remove();
|
|
293
|
+
this.#bakedComponent = null;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
update(ctx = {}) {
|
|
298
|
+
const mergedCtx = { ...this.#defaultProps, ...ctx };
|
|
299
|
+
const parent = this.#bakedComponent?.parentNode;
|
|
300
|
+
parent && this.remove();
|
|
301
|
+
this.#init(mergedCtx);
|
|
302
|
+
if (parent && this.#bakedComponent) {
|
|
303
|
+
parent.appendChild(this.#bakedComponent);
|
|
304
|
+
this.#mountChildren(this.#bakedComponent);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export { ChocolaComponent };
|