ajo 0.1.10 → 0.1.12
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/dist/html.cjs +1 -1
- package/dist/html.js +28 -28
- package/dist/index.cjs +1 -1
- package/dist/index.js +78 -87
- package/package.json +1 -1
- package/readme.md +147 -4
package/dist/html.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const{entries
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const{entries:p,hasOwn:$}=Object,u=e=>typeof e!="string"&&typeof(e==null?void 0:e[Symbol.iterator])=="function",d=e=>[...f(e)].join(""),f=function*(e){for(e of y(e))if(typeof e=="string")yield m(e);else{const{nodeName:t,skip:r,children:i=""}=e;let n="";for(const[l,o]of p(e))k.has(l)||l.startsWith("set:")||o==null||o===!1||(n+=o===!0?`${n} ${l}`:`${n} ${l}="${m(String(o))}"`);g.has(t)?yield`<${t}${n}>`:r?yield`<${t}${n}></${t}>`:typeof i=="string"?yield`<${t}${n}>${i}</${t}>`:(yield`<${t}${n}>`,yield*f(i),yield`</${t}>`)}},y=function*(e,t={value:""},r=!0){for(e of u(e)?e:[e])if(!(e==null||typeof e=="boolean"))if($(e,"nodeName")){const{value:i}=t,{nodeName:n}=e,l=typeof n;if(i&&(yield i,t.value=""),l==="function")if(n.constructor.name==="GeneratorFunction"){const o={},a={};for(const[s,c]of p(e))s==="children"?a.children=c:s.startsWith("arg:")?a[s.slice(4)]=c:o[s]=c;o.nodeName=n.is??"div",o.children=v(n,a),yield o}else delete e.nodeName,yield*y(n(e),t,!1);else l==="string"&&(yield e)}else u(e)?yield*y(e,t,!1):t.value+=e;r&&t.value&&(yield t.value)},g=new Set("area,base,br,col,command,embed,hr,img,input,keygen,link,meta,param,source,track,wbr".split(",")),k=new Set("nodeName,key,skip,memo,ref,children".split(",")),m=e=>e.replace(/[&<>"']/g,t=>`&#${t.charCodeAt(0)};`),v=(e,t)=>{let r,i;try{const n=e.call(r={$args:t,*[Symbol.iterator](){for(;;)yield t},refresh(){},next(){i=d(n.next().value)},throw(l){i=d(n.throw(l).value)},return(){n.return()}},t);r.next()}catch(n){r.throw(n)}finally{r.return()}return i};exports.html=f;exports.render=d;
|
package/dist/html.js
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
const { entries:
|
|
2
|
-
for (e of
|
|
1
|
+
const { entries: m, hasOwn: $ } = Object, y = (e) => typeof e != "string" && typeof (e == null ? void 0 : e[Symbol.iterator]) == "function", f = (e) => [...p(e)].join(""), p = function* (e) {
|
|
2
|
+
for (e of d(e))
|
|
3
3
|
if (typeof e == "string")
|
|
4
|
-
yield
|
|
4
|
+
yield u(e);
|
|
5
5
|
else {
|
|
6
|
-
const { nodeName: t,
|
|
7
|
-
let
|
|
8
|
-
for (const [
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
const { nodeName: t, skip: r, children: i = "" } = e;
|
|
7
|
+
let n = "";
|
|
8
|
+
for (const [l, o] of m(e))
|
|
9
|
+
k.has(l) || l.startsWith("set:") || o == null || o === !1 || (n += o === !0 ? `${n} ${l}` : `${n} ${l}="${u(String(o))}"`);
|
|
10
|
+
g.has(t) ? yield `<${t}${n}>` : r ? yield `<${t}${n}></${t}>` : typeof i == "string" ? yield `<${t}${n}>${i}</${t}>` : (yield `<${t}${n}>`, yield* p(i), yield `</${t}>`);
|
|
11
11
|
}
|
|
12
|
-
},
|
|
13
|
-
for (e of
|
|
12
|
+
}, d = function* (e, t = { value: "" }, r = !0) {
|
|
13
|
+
for (e of y(e) ? e : [e])
|
|
14
14
|
if (!(e == null || typeof e == "boolean"))
|
|
15
|
-
if (
|
|
16
|
-
const { value:
|
|
17
|
-
if (
|
|
15
|
+
if ($(e, "nodeName")) {
|
|
16
|
+
const { value: i } = t, { nodeName: n } = e, l = typeof n;
|
|
17
|
+
if (i && (yield i, t.value = ""), l === "function")
|
|
18
18
|
if (n.constructor.name === "GeneratorFunction") {
|
|
19
|
-
const o = {},
|
|
20
|
-
for (const [
|
|
21
|
-
|
|
22
|
-
o.nodeName =
|
|
19
|
+
const o = {}, a = {};
|
|
20
|
+
for (const [s, c] of m(e))
|
|
21
|
+
s === "children" ? a.children = c : s.startsWith("arg:") ? a[s.slice(4)] = c : o[s] = c;
|
|
22
|
+
o.nodeName = n.is ?? "div", o.children = w(n, a), yield o;
|
|
23
23
|
} else
|
|
24
|
-
delete e.nodeName, yield*
|
|
24
|
+
delete e.nodeName, yield* d(n(e), t, !1);
|
|
25
25
|
else
|
|
26
|
-
|
|
26
|
+
l === "string" && (yield e);
|
|
27
27
|
} else
|
|
28
|
-
|
|
28
|
+
y(e) ? yield* d(e, t, !1) : t.value += e;
|
|
29
29
|
r && t.value && (yield t.value);
|
|
30
|
-
},
|
|
31
|
-
let r,
|
|
30
|
+
}, g = new Set("area,base,br,col,command,embed,hr,img,input,keygen,link,meta,param,source,track,wbr".split(",")), k = new Set("nodeName,key,skip,memo,ref,children".split(",")), u = (e) => e.replace(/[&<>"']/g, (t) => `&#${t.charCodeAt(0)};`), w = (e, t) => {
|
|
31
|
+
let r, i;
|
|
32
32
|
try {
|
|
33
33
|
const n = e.call(r = {
|
|
34
34
|
$args: t,
|
|
@@ -39,10 +39,10 @@ const { entries: p, hasOwn: g } = Object, f = (e) => typeof e != "string" && typ
|
|
|
39
39
|
refresh() {
|
|
40
40
|
},
|
|
41
41
|
next() {
|
|
42
|
-
|
|
42
|
+
i = f(n.next().value);
|
|
43
43
|
},
|
|
44
|
-
throw(
|
|
45
|
-
|
|
44
|
+
throw(l) {
|
|
45
|
+
i = f(n.throw(l).value);
|
|
46
46
|
},
|
|
47
47
|
return() {
|
|
48
48
|
n.return();
|
|
@@ -54,9 +54,9 @@ const { entries: p, hasOwn: g } = Object, f = (e) => typeof e != "string" && typ
|
|
|
54
54
|
} finally {
|
|
55
55
|
r.return();
|
|
56
56
|
}
|
|
57
|
-
return
|
|
57
|
+
return i;
|
|
58
58
|
};
|
|
59
59
|
export {
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
p as html,
|
|
61
|
+
f as render
|
|
62
62
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const{isArray:
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const{isArray:p,prototype:{slice:k}}=Array,{keys:A,assign:w,hasOwn:O,setPrototypeOf:$,getPrototypeOf:h}=Object,x=e=>typeof e!="string"&&typeof(e==null?void 0:e[Symbol.iterator])=="function",C=({children:e})=>e,F=function(e,t){const{length:n}=arguments;return(t??(t={})).nodeName=e,"children"in t||n<3||(t.children=n===3?arguments[2]:k.call(arguments,2)),t},a=(e,t)=>{let n=t.firstChild;for(e of d(e)){let i=n;if(typeof e=="string"){for(;i&&i.nodeType!=3;)i=i.nextSibling;i?i.data!=e&&(i.data=e):i=document.createTextNode(e)}else if(e instanceof Node)i=e;else{const{nodeName:o,key:c,skip:N,memo:m,ref:y,children:b}=e;for(;i&&!(i.localName===o&&(i.$key??(i.$key=c))==c);)i=i.nextSibling;if(i??(i=w(document.createElementNS(e.xmlns??o==="svg"?"http://www.w3.org/2000/svg":t.namespaceURI,o),{$key:c})),m==null||P(i.$memo,i.$memo=m)){const{$props:f}=i,g={};let u;for(const s in w({},f,e)){if(j.has(s))continue;if(s.startsWith("arg:")){(u??(u={}))[s.slice(4)]=e[s];continue}const l=g[s]=e[s];l!==(f==null?void 0:f[s])&&(s.startsWith("set:")?i[s.slice(4)]=l:l==null||l===!1?i.removeAttribute(s):i.setAttribute(s,l===!0?"":l))}u&&(i.$args=u),i.$props=g,N||a(b,i),typeof y=="function"&&(i.$ref=y)(i)}}i===n?n=n.nextSibling:B(t,i,n)}for(;n;){const i=n.nextSibling;n.nodeType===1&&S(n),t.removeChild(n),n=i}},d=function*(e,t={value:""},n=!0){for(e of x(e)?e:[e])if(!(e==null||typeof e=="boolean"))if(O(e,"nodeName")){const{value:i}=t,{nodeName:o}=e,c=typeof o;i&&(yield i,t.value=""),c==="function"?o.constructor.name==="GeneratorFunction"?(e.nodeName=o.is??"div",e.skip=!0,e.ref=T.bind(null,o,e.ref),"children"in e&&(e["arg:children"]=e.children,delete e.children),yield e):(delete e.nodeName,yield*d(o(e),t,!1)):c==="string"&&(yield e)}else x(e)?yield*d(e,t,!1):t.value+=e;n&&t.value&&(yield t.value)},T=(e,t,n)=>{n&&(n.$gen??(n.$gen=(new E(n),e)),n.$ref=q.bind(null,n,t),n.next())},q=(e,t,n)=>{n||e.return(),typeof t=="function"&&t(n)},P=(e,t)=>p(e)&&p(t)?e.some((n,i)=>n!==t[i]):e!==t,j=new Set("nodeName,key,skip,memo,ref,children".split(",")),B=(e,t,n)=>{if(t.contains(document.activeElement)){const i=t.nextSibling;for(;n&&n!=t;){const o=n.nextSibling;e.insertBefore(n,i),n=o}}else e.insertBefore(t,n)},S=e=>{for(const n of e.children)S(n);const{$ref:t}=e;typeof t=="function"&&t(null);for(const n of A(e))e[n]=null};class E{constructor(t){$(t,$(h(this),h(t)))}*[Symbol.iterator](){for(;;)yield this.$args??{}}refresh(){I(this)}next(){try{a((this.$it??(this.$it=this.$gen.call(this,this.$args??{}))).next().value,this),typeof this.$ref=="function"&&this.$ref(this)}catch(t){this.throw(t)}}throw(t){var n;for(let i=this;i;i=i.parentNode)if(typeof((n=i.$it)==null?void 0:n.throw)=="function")try{return a(i.$it.throw(t).value,i)}catch{}throw t}return(){var t;try{(t=this.$it)==null||t.return()}catch(n){this.throw(n)}finally{this.$it=null}}}let r,v;const I=e=>{(r??(r=new Set)).has(e)&&r.delete(e);for(const t of r){if(t.contains(e))return;e.contains(t)&&r.delete(t)}r.add(e),v??(v=requestAnimationFrame(W))},W=()=>{for(const e of r)e.isConnected&&e.next();r.clear(),v=null};exports.Fragment=C;exports.h=F;exports.render=a;
|
package/dist/index.js
CHANGED
|
@@ -1,136 +1,127 @@
|
|
|
1
|
-
const { isArray:
|
|
2
|
-
const { length:
|
|
3
|
-
return (
|
|
4
|
-
},
|
|
5
|
-
let
|
|
1
|
+
const { isArray: p, prototype: { slice: b } } = Array, { keys: A, assign: w, hasOwn: C, setPrototypeOf: $, getPrototypeOf: x } = Object, h = (e) => typeof e != "string" && typeof (e == null ? void 0 : e[Symbol.iterator]) == "function", W = ({ children: e }) => e, j = function(e, t) {
|
|
2
|
+
const { length: n } = arguments;
|
|
3
|
+
return (t ?? (t = {})).nodeName = e, "children" in t || n < 3 || (t.children = n === 3 ? arguments[2] : b.call(arguments, 2)), t;
|
|
4
|
+
}, u = (e, t) => {
|
|
5
|
+
let n = t.firstChild;
|
|
6
6
|
for (e of d(e)) {
|
|
7
|
-
let
|
|
7
|
+
let i = n;
|
|
8
8
|
if (typeof e == "string") {
|
|
9
|
-
for (;
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
for (; i && i.nodeType != 3; )
|
|
10
|
+
i = i.nextSibling;
|
|
11
|
+
i ? i.data != e && (i.data = e) : i = document.createTextNode(e);
|
|
12
12
|
} else if (e instanceof Node)
|
|
13
|
-
|
|
13
|
+
i = e;
|
|
14
14
|
else {
|
|
15
|
-
const { nodeName: o, key: c, skip:
|
|
16
|
-
for (;
|
|
17
|
-
|
|
18
|
-
if (
|
|
19
|
-
const { $props:
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
const { nodeName: o, key: c, skip: S, memo: m, ref: y, children: k } = e;
|
|
16
|
+
for (; i && !(i.localName === o && (i.$key ?? (i.$key = c)) == c); )
|
|
17
|
+
i = i.nextSibling;
|
|
18
|
+
if (i ?? (i = w(document.createElementNS(e.xmlns ?? o === "svg" ? "http://www.w3.org/2000/svg" : t.namespaceURI, o), { $key: c })), m == null || F(i.$memo, i.$memo = m)) {
|
|
19
|
+
const { $props: f } = i, g = {};
|
|
20
|
+
let a;
|
|
21
|
+
for (const s in w({}, f, e)) {
|
|
22
|
+
if (T.has(s))
|
|
22
23
|
continue;
|
|
23
24
|
if (s.startsWith("arg:")) {
|
|
24
|
-
|
|
25
|
+
(a ?? (a = {}))[s.slice(4)] = e[s];
|
|
25
26
|
continue;
|
|
26
27
|
}
|
|
27
28
|
const l = g[s] = e[s];
|
|
28
|
-
l !== (
|
|
29
|
+
l !== (f == null ? void 0 : f[s]) && (s.startsWith("set:") ? i[s.slice(4)] = l : l == null || l === !1 ? i.removeAttribute(s) : i.setAttribute(s, l === !0 ? "" : l));
|
|
29
30
|
}
|
|
30
|
-
|
|
31
|
+
a && (i.$args = a), i.$props = g, S || u(k, i), typeof y == "function" && (i.$ref = y)(i);
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
+
i === n ? n = n.nextSibling : B(t, i, n);
|
|
34
35
|
}
|
|
35
|
-
for (;
|
|
36
|
-
const
|
|
37
|
-
|
|
36
|
+
for (; n; ) {
|
|
37
|
+
const i = n.nextSibling;
|
|
38
|
+
n.nodeType === 1 && N(n), t.removeChild(n), n = i;
|
|
38
39
|
}
|
|
39
|
-
}, d = function* (e,
|
|
40
|
+
}, d = function* (e, t = { value: "" }, n = !0) {
|
|
40
41
|
for (e of h(e) ? e : [e])
|
|
41
42
|
if (!(e == null || typeof e == "boolean"))
|
|
42
|
-
if (
|
|
43
|
-
const { value:
|
|
44
|
-
|
|
45
|
-
if (o.constructor.name === "GeneratorFunction") {
|
|
46
|
-
const { is: a = o.is ?? "div", ref: f } = e;
|
|
47
|
-
e.nodeName = a, delete e.is, e.skip = !0, e.ref = T.bind(null, o, a, f), "children" in e && (e["arg:children"] = e.children, delete e.children), yield e;
|
|
48
|
-
} else
|
|
49
|
-
delete e.nodeName, yield* d(o(e), n, !1);
|
|
50
|
-
else
|
|
51
|
-
c === "string" && (yield e);
|
|
43
|
+
if (C(e, "nodeName")) {
|
|
44
|
+
const { value: i } = t, { nodeName: o } = e, c = typeof o;
|
|
45
|
+
i && (yield i, t.value = ""), c === "function" ? o.constructor.name === "GeneratorFunction" ? (e.nodeName = o.is ?? "div", e.skip = !0, e.ref = O.bind(null, o, e.ref), "children" in e && (e["arg:children"] = e.children, delete e.children), yield e) : (delete e.nodeName, yield* d(o(e), t, !1)) : c === "string" && (yield e);
|
|
52
46
|
} else
|
|
53
|
-
h(e) ? yield* d(e,
|
|
54
|
-
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
},
|
|
60
|
-
if (
|
|
61
|
-
const
|
|
62
|
-
for (;
|
|
63
|
-
const o =
|
|
64
|
-
e.insertBefore(
|
|
47
|
+
h(e) ? yield* d(e, t, !1) : t.value += e;
|
|
48
|
+
n && t.value && (yield t.value);
|
|
49
|
+
}, O = (e, t, n) => {
|
|
50
|
+
n && (n.$gen ?? (n.$gen = (new E(n), e)), n.$ref = q.bind(null, n, t), n.next());
|
|
51
|
+
}, q = (e, t, n) => {
|
|
52
|
+
n || e.return(), typeof t == "function" && t(n);
|
|
53
|
+
}, F = (e, t) => p(e) && p(t) ? e.some((n, i) => n !== t[i]) : e !== t, T = new Set("nodeName,key,skip,memo,ref,children".split(",")), B = (e, t, n) => {
|
|
54
|
+
if (t.contains(document.activeElement)) {
|
|
55
|
+
const i = t.nextSibling;
|
|
56
|
+
for (; n && n != t; ) {
|
|
57
|
+
const o = n.nextSibling;
|
|
58
|
+
e.insertBefore(n, i), n = o;
|
|
65
59
|
}
|
|
66
60
|
} else
|
|
67
|
-
e.insertBefore(
|
|
68
|
-
},
|
|
69
|
-
for (const
|
|
70
|
-
|
|
71
|
-
const { $ref:
|
|
72
|
-
typeof
|
|
73
|
-
for (const
|
|
74
|
-
e[
|
|
61
|
+
e.insertBefore(t, n);
|
|
62
|
+
}, N = (e) => {
|
|
63
|
+
for (const n of e.children)
|
|
64
|
+
N(n);
|
|
65
|
+
const { $ref: t } = e;
|
|
66
|
+
typeof t == "function" && t(null);
|
|
67
|
+
for (const n of A(e))
|
|
68
|
+
e[n] = null;
|
|
75
69
|
};
|
|
76
|
-
class
|
|
77
|
-
constructor(
|
|
78
|
-
|
|
70
|
+
class E {
|
|
71
|
+
constructor(t) {
|
|
72
|
+
$(t, $(x(this), x(t)));
|
|
79
73
|
}
|
|
80
74
|
*[Symbol.iterator]() {
|
|
81
75
|
for (; ; )
|
|
82
|
-
yield this.$args;
|
|
76
|
+
yield this.$args ?? {};
|
|
83
77
|
}
|
|
84
78
|
refresh() {
|
|
85
|
-
|
|
79
|
+
I(this);
|
|
86
80
|
}
|
|
87
81
|
next() {
|
|
88
82
|
try {
|
|
89
|
-
|
|
90
|
-
} catch (
|
|
91
|
-
this.throw(
|
|
83
|
+
u((this.$it ?? (this.$it = this.$gen.call(this, this.$args ?? {}))).next().value, this), typeof this.$ref == "function" && this.$ref(this);
|
|
84
|
+
} catch (t) {
|
|
85
|
+
this.throw(t);
|
|
92
86
|
}
|
|
93
87
|
}
|
|
94
|
-
throw(
|
|
95
|
-
var
|
|
96
|
-
for (let
|
|
97
|
-
if (typeof ((
|
|
88
|
+
throw(t) {
|
|
89
|
+
var n;
|
|
90
|
+
for (let i = this; i; i = i.parentNode)
|
|
91
|
+
if (typeof ((n = i.$it) == null ? void 0 : n.throw) == "function")
|
|
98
92
|
try {
|
|
99
|
-
return
|
|
93
|
+
return u(i.$it.throw(t).value, i);
|
|
100
94
|
} catch {
|
|
101
95
|
}
|
|
102
|
-
throw
|
|
96
|
+
throw t;
|
|
103
97
|
}
|
|
104
98
|
return() {
|
|
105
|
-
var
|
|
99
|
+
var t;
|
|
106
100
|
try {
|
|
107
|
-
(
|
|
108
|
-
} catch (
|
|
109
|
-
this.throw(
|
|
101
|
+
(t = this.$it) == null || t.return();
|
|
102
|
+
} catch (n) {
|
|
103
|
+
this.throw(n);
|
|
110
104
|
} finally {
|
|
111
105
|
this.$it = null;
|
|
112
106
|
}
|
|
113
107
|
}
|
|
114
108
|
}
|
|
115
|
-
let
|
|
116
|
-
const
|
|
117
|
-
let i = (N ?? (N = /* @__PURE__ */ new Map())).get(n);
|
|
118
|
-
return i || ({ constructor: i } = document.createElementNS(e.namespaceURI, n), N.set(n, i === HTMLUnknownElement ? i = HTMLElement : i)), i;
|
|
119
|
-
}, U = (e) => {
|
|
109
|
+
let r, v;
|
|
110
|
+
const I = (e) => {
|
|
120
111
|
(r ?? (r = /* @__PURE__ */ new Set())).has(e) && r.delete(e);
|
|
121
|
-
for (const
|
|
122
|
-
if (
|
|
112
|
+
for (const t of r) {
|
|
113
|
+
if (t.contains(e))
|
|
123
114
|
return;
|
|
124
|
-
e.contains(
|
|
115
|
+
e.contains(t) && r.delete(t);
|
|
125
116
|
}
|
|
126
|
-
r.add(e),
|
|
127
|
-
},
|
|
117
|
+
r.add(e), v ?? (v = requestAnimationFrame(P));
|
|
118
|
+
}, P = () => {
|
|
128
119
|
for (const e of r)
|
|
129
120
|
e.isConnected && e.next();
|
|
130
|
-
r.clear(),
|
|
121
|
+
r.clear(), v = null;
|
|
131
122
|
};
|
|
132
123
|
export {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
124
|
+
W as Fragment,
|
|
125
|
+
j as h,
|
|
126
|
+
u as render
|
|
136
127
|
};
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -218,7 +218,7 @@ The `set:` prefix in Ajo allows you to directly set properties on DOM elements f
|
|
|
218
218
|
```jsx
|
|
219
219
|
function* MyComponent() {
|
|
220
220
|
const text = "Hello, Ajo!"
|
|
221
|
-
yield <div set:textContent={text} skip></div>
|
|
221
|
+
while (true) yield <div set:textContent={text} skip></div>
|
|
222
222
|
}
|
|
223
223
|
```
|
|
224
224
|
> Here, `set:textContent` directly sets the `textContent` property of the `div`'s DOM node. `skip` is used to prevent Ajo from overriding the `div`'s children.
|
|
@@ -227,7 +227,7 @@ function* MyComponent() {
|
|
|
227
227
|
```jsx
|
|
228
228
|
function* MyComponent() {
|
|
229
229
|
const html = "<p>Hello, Ajo!</p>"
|
|
230
|
-
yield <div set:innerHTML={html} skip></div>
|
|
230
|
+
while (true) yield <div set:innerHTML={html} skip></div>
|
|
231
231
|
}
|
|
232
232
|
```
|
|
233
233
|
> In this case, `set:innerHTML` is used to set the `innerHTML` property of the `div`'s DOM element. `skip` is used to prevent Ajo from overriding the `div`'s children.
|
|
@@ -236,7 +236,7 @@ function* MyComponent() {
|
|
|
236
236
|
```jsx
|
|
237
237
|
function* MyComponent() {
|
|
238
238
|
const handleClick = () => console.log('Clicked')
|
|
239
|
-
yield <button set:onclick={handleClick}>Click Me</button>
|
|
239
|
+
while (true) yield <button set:onclick={handleClick}>Click Me</button>
|
|
240
240
|
}
|
|
241
241
|
```
|
|
242
242
|
> `set:onclick` assigns the `handleClick` function as the click event listener for the button.
|
|
@@ -265,7 +265,7 @@ In Ajo, there are several special attributes (`key`, `skip`, `memo`, and `ref`)
|
|
|
265
265
|
- **Behavior:** When an element is mounted or updated, the `ref` callback is called with the DOM element as an argument. This allows you to store a reference to it for later use, such as focusing an input or measuring dimensions.
|
|
266
266
|
- **Example:** `h('input', { ref: el => (this.inputNode = el) })` - stores a reference to the input element.
|
|
267
267
|
|
|
268
|
-
## Stateful
|
|
268
|
+
## Stateful Components
|
|
269
269
|
|
|
270
270
|
Stateful components in Ajo are defined using generator functions. These components are designed with a minimalistic API for controlling rendering and state updates. They are equipped with several lifecycle methods that allow for advanced control over component behavior, error handling, and rendering processes.
|
|
271
271
|
|
|
@@ -349,6 +349,40 @@ function* ChatComponent({ user = 'Anonymous', room }) { // Receive arguments ini
|
|
|
349
349
|
}
|
|
350
350
|
```
|
|
351
351
|
|
|
352
|
+
### Default Rendering Behavior
|
|
353
|
+
|
|
354
|
+
By default, when a stateful component is rendered in Ajo, it wraps its yielded content within a `<div>` element. This `<div>` becomes the context of the component's generator function, referred to as `this` within the function.
|
|
355
|
+
|
|
356
|
+
For example:
|
|
357
|
+
|
|
358
|
+
```javascript
|
|
359
|
+
function* MyComponent() {
|
|
360
|
+
// The 'this' variable here refers to the default 'div' wrapper element
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
> This default behavior ensures a consistent and predictable wrapper for your component's content, making it easier to manage and style.
|
|
364
|
+
|
|
365
|
+
### Customizing the Wrapper Element
|
|
366
|
+
|
|
367
|
+
While the default wrapper is a `<div>`, Ajo provides the flexibility to change this to any other HTML element type. This is particularly useful for semantic correctness or when integrating with existing HTML structures, especially in SSR scenarios.
|
|
368
|
+
|
|
369
|
+
To specify a different element type for the wrapper, set the `is` property on the Generator Function of your component. For example:
|
|
370
|
+
|
|
371
|
+
```javascript
|
|
372
|
+
function* MyCustomRow() {
|
|
373
|
+
// The 'this' variable here refers to the default 'tr' wrapper element
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
MyCustomRow.is = 'tr'
|
|
377
|
+
```
|
|
378
|
+
> This code will instruct Ajo to render a `<tr>` element instead of the default `<div>`. This capability is crucial for rendering and hydrating any type of HTML element when using stateful components.
|
|
379
|
+
|
|
380
|
+
#### SSR and Hydration
|
|
381
|
+
|
|
382
|
+
In the context of Server-Side Rendering (SSR), this feature allows Ajo to gracefully hydrate existing SSR-generated DOM elements. It means that Ajo can extend the functionality of built-in browser DOM elements without relying on Web Components or standards like Declarative Shadow DOM.
|
|
383
|
+
|
|
384
|
+
This approach provides a streamlined, efficient method for enhancing and manipulating built-in elements, offering a more practical solution compared to the complexities of Web Components.
|
|
385
|
+
|
|
352
386
|
## Lifecycle methods
|
|
353
387
|
|
|
354
388
|
Stateful components in Ajo are equipped with several methods that allow for advanced control over component behavior, error handling, and rendering processes. These methods are called lifecycle methods and are invoked at different stages of the component's lifecycle.
|
|
@@ -580,6 +614,115 @@ function* ChildComponent({ data, onEvent }) {
|
|
|
580
614
|
|
|
581
615
|
This `arg:` prefixed attribute system in Ajo enhances the clarity and readability of component composition. It makes the intent of passing down arguments more explicit, reducing confusion between HTML attributes, and other special properties. This is especially beneficial in complex applications where components have multiple responsibilities and interact with both their children and the DOM.
|
|
582
616
|
|
|
617
|
+
## Server-Side Rendering (SSR)
|
|
618
|
+
|
|
619
|
+
Ajo supports Server-Side Rendering (SSR), enabling components to be rendered to HTML in server-side JavaScript environments. This feature enhances the capabilities of Ajo for projects requiring SEO-friendly pages and faster initial page loads.
|
|
620
|
+
|
|
621
|
+
### SSR Implementation with `ajo/html`
|
|
622
|
+
|
|
623
|
+
For SSR in Ajo, use the `render` and `html` functions from the `ajo/html` module. These functions are designed to convert a virtual DOM tree into an HTML string or HTML chunks for streaming, suitable for server-side environments.
|
|
624
|
+
|
|
625
|
+
### Client-Side Hydration
|
|
626
|
+
|
|
627
|
+
Once HTML is rendered on the server, it can be hydrated on the client-side by Ajo to become interactive. The client-side `render` function can render into the root element containing the SSR-generated DOM.
|
|
628
|
+
|
|
629
|
+
### Example Usage
|
|
630
|
+
|
|
631
|
+
Server-side:
|
|
632
|
+
|
|
633
|
+
```javascript
|
|
634
|
+
import express from 'express'
|
|
635
|
+
import { render } from 'ajo/html'
|
|
636
|
+
import { App } from './components'
|
|
637
|
+
|
|
638
|
+
const app = express()
|
|
639
|
+
|
|
640
|
+
app.get('/', (req, res) => {
|
|
641
|
+
|
|
642
|
+
const html = render(<App />)
|
|
643
|
+
|
|
644
|
+
res.send(`
|
|
645
|
+
<!DOCTYPE html>
|
|
646
|
+
<html>
|
|
647
|
+
<head>
|
|
648
|
+
<title>My App</title>
|
|
649
|
+
</head>
|
|
650
|
+
<body>
|
|
651
|
+
<div id="root">${html}</div>
|
|
652
|
+
</body>
|
|
653
|
+
</html>`)
|
|
654
|
+
})
|
|
655
|
+
|
|
656
|
+
app.listen(3000)
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
Client-side:
|
|
660
|
+
|
|
661
|
+
```javascript
|
|
662
|
+
import { render } from 'ajo'
|
|
663
|
+
import { App } from './components'
|
|
664
|
+
|
|
665
|
+
// Hydrate the #root element with the server-rendered DOM
|
|
666
|
+
render(<App />, document.getElementById('root'))
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
### Streaming HTML Chunks
|
|
670
|
+
|
|
671
|
+
The `html` function is designed to be used with various streaming technologies. Its output can be seamlessly integrated into different streaming environments, offering developers the flexibility to choose the streaming solution that best fits their project requirements.
|
|
672
|
+
|
|
673
|
+
This streaming capability is useful for progressively rendering content, enhancing Time to First Paint (TTFP) and user experience. It allows browsers to begin rendering content as soon as the initial chunks arrive.
|
|
674
|
+
|
|
675
|
+
## SSR API
|
|
676
|
+
|
|
677
|
+
### `render(h: Any): String`
|
|
678
|
+
|
|
679
|
+
Renders a virtual DOM tree (`h`) into a complete HTML string.
|
|
680
|
+
|
|
681
|
+
#### Parameters:
|
|
682
|
+
|
|
683
|
+
- **h** (Any): The virtual DOM tree to render. This can be any value, a simple string, or a virtual DOM tree created by the `h` function.
|
|
684
|
+
|
|
685
|
+
#### Returns:
|
|
686
|
+
|
|
687
|
+
- A string representation of the rendered HTML.
|
|
688
|
+
|
|
689
|
+
#### Example Usage:
|
|
690
|
+
|
|
691
|
+
```javascript
|
|
692
|
+
import { render } from 'ajo/html'
|
|
693
|
+
import { App } from './components'
|
|
694
|
+
|
|
695
|
+
const html = render(<App />)
|
|
696
|
+
// The `html` can be sent as part of an HTTP response
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
### `html(h: Any): Generator`
|
|
700
|
+
|
|
701
|
+
A generator function that iterates through a virtual DOM tree, yielding HTML strings.
|
|
702
|
+
|
|
703
|
+
#### Parameters:
|
|
704
|
+
|
|
705
|
+
- **h** (Any): The virtual DOM tree to iterate through.
|
|
706
|
+
|
|
707
|
+
#### Yield:
|
|
708
|
+
|
|
709
|
+
- Yields HTML strings corresponding to each node in the virtual DOM.
|
|
710
|
+
|
|
711
|
+
#### Usage:
|
|
712
|
+
|
|
713
|
+
- Suitable for streaming HTML chunks to the client, compatible with any streaming technology.
|
|
714
|
+
|
|
715
|
+
#### Example Usage:
|
|
716
|
+
|
|
717
|
+
```javascript
|
|
718
|
+
import { html } from 'ajo/html'
|
|
719
|
+
import { App } from './components'
|
|
720
|
+
|
|
721
|
+
for (const chunk of html(<App />)) {
|
|
722
|
+
stream.push(chunk)
|
|
723
|
+
}
|
|
724
|
+
```
|
|
725
|
+
|
|
583
726
|
## Acknowledgments
|
|
584
727
|
Ajo takes heavy inspiration from [Incremental DOM](https://github.com/google/incremental-dom) and [Crank.js](https://github.com/bikeshaving/crank)
|
|
585
728
|
|