ajo 0.1.9 → 0.1.10
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 +6 -6
- package/dist/index.cjs +1 -1
- package/dist/index.js +85 -78
- package/package.json +1 -1
- package/readme.md +73 -76
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:$,hasOwn:g}=Object,m=e=>typeof e!="string"&&typeof(e==null?void 0:e[Symbol.iterator])=="function",y=e=>[...u(e)].join(""),u=function*(e){for(e of f(e))if(typeof e=="string")yield p(e);else{const{nodeName:t,key:r,skip:l,memo:n,ref:a,children:o,...c}=e;let i="";for(const[s,d]of $(c))s.startsWith("set:")||d==null||d===!1||(i+=d===!0?`${i} ${s}`:`${i} ${s}="${p(String(d))}"`);v.has(t)?yield`<${t}${i}>`:l?yield`<${t}${i}></${t}>`:typeof o=="string"?yield`<${t}${i}>${o}</${t}>`:(yield`<${t}${i}>`,yield*u(o),yield`</${t}>`)}},f=function*(e,t={value:""},r=!0){for(e of m(e)?e:[e])if(!(e==null||typeof e=="boolean"))if(g(e,"nodeName")){const{value:l}=t,{nodeName:n}=e,a=typeof n;if(l&&(yield l,t.value=""),a==="function")if(n.constructor.name==="GeneratorFunction"){const o={},c={};for(const[i,s]of $(e))i!=="is"&&(i==="children"?c.children=s:i.startsWith("arg:")?c[i.slice(4)]=s:o[i]=s);o.nodeName=e.is??n.is??"div",o.children=k(n,c),yield o}else delete e.nodeName,yield*f(n(e),t,!1);else a==="string"&&(yield e)}else m(e)?yield*f(e,t,!1):t.value+=e;r&&t.value&&(yield t.value)},v=new Set("area,base,br,col,command,embed,hr,img,input,keygen,link,meta,param,source,track,wbr".split(",")),p=e=>e.replace(/[&<>"']/g,t=>`&#${t.charCodeAt(0)};`),k=(e,t)=>{let r,l;try{const n=e.call(r={$args:t,*[Symbol.iterator](){for(;;)yield t},refresh(){},next(){l=y(n.next().value)},throw(a){l=y(n.throw(a).value)},return(){n.return()}},t);r.next()}catch(n){r.throw(n)}finally{r.return()}return l};exports.html=u;exports.render=y;
|
package/dist/html.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
const { entries:
|
|
1
|
+
const { entries: p, hasOwn: g } = Object, f = (e) => typeof e != "string" && typeof (e == null ? void 0 : e[Symbol.iterator]) == "function", u = (e) => [...$(e)].join(""), $ = function* (e) {
|
|
2
2
|
for (e of y(e))
|
|
3
3
|
if (typeof e == "string")
|
|
4
4
|
yield m(e);
|
|
5
5
|
else {
|
|
6
6
|
const { nodeName: t, key: r, skip: l, memo: n, ref: a, children: o, ...c } = e;
|
|
7
7
|
let i = "";
|
|
8
|
-
for (const [s, d] of
|
|
8
|
+
for (const [s, d] of p(c))
|
|
9
9
|
s.startsWith("set:") || d == null || d === !1 || (i += d === !0 ? `${i} ${s}` : `${i} ${s}="${m(String(d))}"`);
|
|
10
|
-
k.has(t) ? yield `<${t}${i}>` : l ? yield `<${t}${i}></${t}>` : typeof o == "string" ? yield `<${t}${i}>${o}</${t}>` : (yield `<${t}${i}>`, yield*
|
|
10
|
+
k.has(t) ? yield `<${t}${i}>` : l ? yield `<${t}${i}></${t}>` : typeof o == "string" ? yield `<${t}${i}>${o}</${t}>` : (yield `<${t}${i}>`, yield* $(o), yield `</${t}>`);
|
|
11
11
|
}
|
|
12
12
|
}, y = function* (e, t = { value: "" }, r = !0) {
|
|
13
13
|
for (e of f(e) ? e : [e])
|
|
@@ -17,7 +17,7 @@ const { entries: $, hasOwn: g } = Object, f = (e) => typeof e != "string" && typ
|
|
|
17
17
|
if (l && (yield l, t.value = ""), a === "function")
|
|
18
18
|
if (n.constructor.name === "GeneratorFunction") {
|
|
19
19
|
const o = {}, c = {};
|
|
20
|
-
for (const [i, s] of
|
|
20
|
+
for (const [i, s] of p(e))
|
|
21
21
|
i !== "is" && (i === "children" ? c.children = s : i.startsWith("arg:") ? c[i.slice(4)] = s : o[i] = s);
|
|
22
22
|
o.nodeName = e.is ?? n.is ?? "div", o.children = v(n, c), yield o;
|
|
23
23
|
} else
|
|
@@ -31,7 +31,7 @@ const { entries: $, hasOwn: g } = Object, f = (e) => typeof e != "string" && typ
|
|
|
31
31
|
let r, l;
|
|
32
32
|
try {
|
|
33
33
|
const n = e.call(r = {
|
|
34
|
-
args: t,
|
|
34
|
+
$args: t,
|
|
35
35
|
*[Symbol.iterator]() {
|
|
36
36
|
for (; ; )
|
|
37
37
|
yield t;
|
|
@@ -57,6 +57,6 @@ const { entries: $, hasOwn: g } = Object, f = (e) => typeof e != "string" && typ
|
|
|
57
57
|
return l;
|
|
58
58
|
};
|
|
59
59
|
export {
|
|
60
|
-
|
|
60
|
+
$ as html,
|
|
61
61
|
u 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:w,prototype:{slice:k}}=Array,{assign:$,setPrototypeOf:x,hasOwn:T,keys:A}=Object,h=e=>typeof e!="string"&&typeof(e==null?void 0:e[Symbol.iterator])=="function",E=({children:e})=>e,C=function(e,n){const{length:i}=arguments;return(n??(n={})).nodeName=e,"children"in n||i<3||(n.children=i===3?arguments[2]:k.call(arguments,2)),n},d=(e,n)=>{let i=n.firstChild;for(e of m(e)){let t=i;if(typeof e=="string"){for(;t&&t.nodeType!=3;)t=t.nextSibling;t?t.data!=e&&(t.data=e):t=document.createTextNode(e)}else if(e instanceof Node)t=e;else{const{nodeName:o,key:c,skip:a,memo:f,ref:y,children:b}=e;for(;t&&!(t.localName===o&&(t.$key??(t.$key=c))==c);)t=t.nextSibling;if(t??(t=$(document.createElementNS(e.xmlns??o==="svg"?"http://www.w3.org/2000/svg":n.namespaceURI,o),{$key:c})),f==null||O(t.$memo,t.$memo=f)){const{$props:u}=t,g={},p={};for(const r in $({},u,e)){if(q.has(r))continue;if(r.startsWith("arg:")){p[r.slice(4)]=e[r];continue}const l=g[r]=e[r];l!==(u==null?void 0:u[r])&&(r.startsWith("set:")?t[r.slice(4)]=l:l==null||l===!1?t.removeAttribute(r):t.setAttribute(r,l===!0?"":l))}t.$props=g,a||d(b,t),typeof y=="function"&&(t.$ref=y)(t,p)}}t===i?i=i.nextSibling:I(n,t,i)}for(;i;){const t=i.nextSibling;i.nodeType===1&&v(i),n.removeChild(i),i=t}},m=function*(e,n={value:""},i=!0){for(e of h(e)?e:[e])if(!(e==null||typeof e=="boolean"))if(T(e,"nodeName")){const{value:t}=n,{nodeName:o}=e,c=typeof o;if(t&&(yield t,n.value=""),c==="function")if(o.constructor.name==="GeneratorFunction"){const{is:a=o.is??"div",ref:f}=e;e.nodeName=a,delete e.is,e.skip=!0,e.ref=F.bind(null,o,a,f),"children"in e&&(e["arg:children"]=e.children,delete e.children),yield e}else delete e.nodeName,yield*m(o(e),n,!1);else c==="string"&&(yield e)}else h(e)?yield*m(e,n,!1):n.value+=e;i&&n.value&&(yield n.value)},F=(e,n,i,t,o)=>{t&&(t.$gen??(t.$gen=(new U(t,n),e)),t.$ref=M.bind(null,t,i),t.$args=o,t.next())},M=(e,n,i,t)=>{i||e.return(),typeof n=="function"&&n(i,t)},O=(e,n)=>w(e)&&w(n)?e.some((i,t)=>i!==n[t]):e!==n,q=new Set("nodeName,key,skip,memo,ref,children".split(",")),I=(e,n,i)=>{if(n.contains(document.activeElement)){const t=n.nextSibling;for(;i&&i!=n;){const o=i.nextSibling;e.insertBefore(i,t),i=o}}else e.insertBefore(n,i)},v=e=>{for(const i of e.children)v(i);const{$ref:n}=e;typeof n=="function"&&n(null);for(const i of A(e))e[i]=null};class U{constructor(n,i){return x(n,x(this.constructor.prototype,j(n,i).prototype))}*[Symbol.iterator](){for(;;)yield this.$args}refresh(){B(this)}next(){try{d((this.$it??(this.$it=this.$gen.call(this,this.$args))).next().value,this),typeof this.$ref=="function"&&this.$ref(this)}catch(n){this.throw(n)}}throw(n){var i;for(let t=this;t;t=t.parentNode)if(typeof((i=t.$it)==null?void 0:i.throw)=="function")try{return d(t.$it.throw(n).value,t)}catch{}throw n}return(){var n;try{(n=this.$it)==null||n.return()}catch(i){this.throw(i)}finally{this.$it=null}}}let S,s,N;const j=(e,n)=>{let i=(S??(S=new Map)).get(n);return i||({constructor:i}=document.createElementNS(e.namespaceURI,n),S.set(n,i===HTMLUnknownElement?i=HTMLElement:i)),i},B=e=>{(s??(s=new Set)).has(e)&&s.delete(e);for(const n of s){if(n.contains(e))return;e.contains(n)&&s.delete(n)}s.add(e),N??(N=requestAnimationFrame(H))},H=()=>{for(const e of s)e.isConnected&&e.next();s.clear(),N=null};exports.Fragment=E;exports.h=C;exports.render=d;
|
package/dist/index.js
CHANGED
|
@@ -1,103 +1,110 @@
|
|
|
1
|
-
const { isArray:
|
|
1
|
+
const { isArray: w, prototype: { slice: b } } = Array, { assign: $, setPrototypeOf: x, hasOwn: A, keys: E } = Object, h = (e) => typeof e != "string" && typeof (e == null ? void 0 : e[Symbol.iterator]) == "function", H = ({ children: e }) => e, L = function(e, n) {
|
|
2
2
|
const { length: i } = arguments;
|
|
3
|
-
return (
|
|
4
|
-
},
|
|
5
|
-
let
|
|
6
|
-
for (e of
|
|
7
|
-
let
|
|
3
|
+
return (n ?? (n = {})).nodeName = e, "children" in n || i < 3 || (n.children = i === 3 ? arguments[2] : b.call(arguments, 2)), n;
|
|
4
|
+
}, m = (e, n) => {
|
|
5
|
+
let i = n.firstChild;
|
|
6
|
+
for (e of d(e)) {
|
|
7
|
+
let t = i;
|
|
8
8
|
if (typeof e == "string") {
|
|
9
|
-
for (;
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
for (; t && t.nodeType != 3; )
|
|
10
|
+
t = t.nextSibling;
|
|
11
|
+
t ? t.data != e && (t.data = e) : t = document.createTextNode(e);
|
|
12
12
|
} else if (e instanceof Node)
|
|
13
|
-
|
|
13
|
+
t = e;
|
|
14
14
|
else {
|
|
15
|
-
const { nodeName:
|
|
16
|
-
for (;
|
|
17
|
-
|
|
18
|
-
if (
|
|
19
|
-
const { $
|
|
20
|
-
for (const s in
|
|
15
|
+
const { nodeName: o, key: c, skip: a, memo: f, ref: y, children: k } = e;
|
|
16
|
+
for (; t && !(t.localName === o && (t.$key ?? (t.$key = c)) == c); )
|
|
17
|
+
t = t.nextSibling;
|
|
18
|
+
if (t ?? (t = $(document.createElementNS(e.xmlns ?? o === "svg" ? "http://www.w3.org/2000/svg" : n.namespaceURI, o), { $key: c })), f == null || q(t.$memo, t.$memo = f)) {
|
|
19
|
+
const { $props: u } = t, g = {}, p = {};
|
|
20
|
+
for (const s in $({}, u, e)) {
|
|
21
|
+
if (F.has(s))
|
|
22
|
+
continue;
|
|
21
23
|
if (s.startsWith("arg:")) {
|
|
22
|
-
|
|
24
|
+
p[s.slice(4)] = e[s];
|
|
23
25
|
continue;
|
|
24
26
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const f = $[s] = e[s];
|
|
28
|
-
f !== (y == null ? void 0 : y[s]) && (s.startsWith("set:") ? n[s.slice(4)] = f : f == null || f === !1 ? n.removeAttribute(s) : n.setAttribute(s, f === !0 ? "" : f));
|
|
27
|
+
const l = g[s] = e[s];
|
|
28
|
+
l !== (u == null ? void 0 : u[s]) && (s.startsWith("set:") ? t[s.slice(4)] = l : l == null || l === !1 ? t.removeAttribute(s) : t.setAttribute(s, l === !0 ? "" : l));
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
t.$props = g, a || m(k, t), typeof y == "function" && (t.$ref = y)(t, p);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
t === i ? i = i.nextSibling : I(n, t, i);
|
|
34
34
|
}
|
|
35
|
-
for (;
|
|
36
|
-
const
|
|
37
|
-
|
|
35
|
+
for (; i; ) {
|
|
36
|
+
const t = i.nextSibling;
|
|
37
|
+
i.nodeType === 1 && v(i), n.removeChild(i), i = t;
|
|
38
38
|
}
|
|
39
|
-
},
|
|
40
|
-
for (e of
|
|
39
|
+
}, d = function* (e, n = { value: "" }, i = !0) {
|
|
40
|
+
for (e of h(e) ? e : [e])
|
|
41
41
|
if (!(e == null || typeof e == "boolean"))
|
|
42
|
-
if (
|
|
43
|
-
const { value:
|
|
44
|
-
if (
|
|
45
|
-
if (
|
|
46
|
-
const { is:
|
|
47
|
-
e.
|
|
42
|
+
if (A(e, "nodeName")) {
|
|
43
|
+
const { value: t } = n, { nodeName: o } = e, c = typeof o;
|
|
44
|
+
if (t && (yield t, n.value = ""), c === "function")
|
|
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
48
|
} else
|
|
49
|
-
delete e.nodeName, yield*
|
|
49
|
+
delete e.nodeName, yield* d(o(e), n, !1);
|
|
50
50
|
else
|
|
51
51
|
c === "string" && (yield e);
|
|
52
52
|
} else
|
|
53
|
-
|
|
54
|
-
i &&
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
h(e) ? yield* d(e, n, !1) : n.value += e;
|
|
54
|
+
i && n.value && (yield n.value);
|
|
55
|
+
}, T = (e, n, i, t, o) => {
|
|
56
|
+
t && (t.$gen ?? (t.$gen = (new M(t, n), e)), t.$ref = C.bind(null, t, i), t.$args = o, t.next());
|
|
57
|
+
}, C = (e, n, i, t) => {
|
|
58
|
+
i || e.return(), typeof n == "function" && n(i, t);
|
|
59
|
+
}, q = (e, n) => w(e) && w(n) ? e.some((i, t) => i !== n[t]) : e !== n, F = new Set("nodeName,key,skip,memo,ref,children".split(",")), I = (e, n, i) => {
|
|
60
|
+
if (n.contains(document.activeElement)) {
|
|
61
|
+
const t = n.nextSibling;
|
|
62
|
+
for (; i && i != n; ) {
|
|
63
|
+
const o = i.nextSibling;
|
|
64
|
+
e.insertBefore(i, t), i = o;
|
|
61
65
|
}
|
|
62
66
|
} else
|
|
63
|
-
e.insertBefore(
|
|
64
|
-
},
|
|
65
|
-
for (const i of e)
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
e.insertBefore(n, i);
|
|
68
|
+
}, v = (e) => {
|
|
69
|
+
for (const i of e.children)
|
|
70
|
+
v(i);
|
|
71
|
+
const { $ref: n } = e;
|
|
72
|
+
typeof n == "function" && n(null);
|
|
73
|
+
for (const i of E(e))
|
|
74
|
+
e[i] = null;
|
|
68
75
|
};
|
|
69
|
-
class
|
|
70
|
-
constructor(
|
|
71
|
-
return
|
|
76
|
+
class M {
|
|
77
|
+
constructor(n, i) {
|
|
78
|
+
return x(n, x(this.constructor.prototype, O(n, i).prototype));
|
|
72
79
|
}
|
|
73
80
|
*[Symbol.iterator]() {
|
|
74
81
|
for (; ; )
|
|
75
82
|
yield this.$args;
|
|
76
83
|
}
|
|
77
84
|
refresh() {
|
|
78
|
-
|
|
85
|
+
U(this);
|
|
79
86
|
}
|
|
80
87
|
next() {
|
|
81
88
|
try {
|
|
82
|
-
|
|
83
|
-
} catch (
|
|
84
|
-
this.throw(
|
|
89
|
+
m((this.$it ?? (this.$it = this.$gen.call(this, this.$args))).next().value, this), typeof this.$ref == "function" && this.$ref(this);
|
|
90
|
+
} catch (n) {
|
|
91
|
+
this.throw(n);
|
|
85
92
|
}
|
|
86
93
|
}
|
|
87
|
-
throw(
|
|
94
|
+
throw(n) {
|
|
88
95
|
var i;
|
|
89
|
-
for (let
|
|
90
|
-
if (typeof ((i =
|
|
96
|
+
for (let t = this; t; t = t.parentNode)
|
|
97
|
+
if (typeof ((i = t.$it) == null ? void 0 : i.throw) == "function")
|
|
91
98
|
try {
|
|
92
|
-
return
|
|
99
|
+
return m(t.$it.throw(n).value, t);
|
|
93
100
|
} catch {
|
|
94
101
|
}
|
|
95
|
-
throw
|
|
102
|
+
throw n;
|
|
96
103
|
}
|
|
97
104
|
return() {
|
|
98
|
-
var
|
|
105
|
+
var n;
|
|
99
106
|
try {
|
|
100
|
-
(
|
|
107
|
+
(n = this.$it) == null || n.return();
|
|
101
108
|
} catch (i) {
|
|
102
109
|
this.throw(i);
|
|
103
110
|
} finally {
|
|
@@ -105,25 +112,25 @@ class I {
|
|
|
105
112
|
}
|
|
106
113
|
}
|
|
107
114
|
}
|
|
108
|
-
let
|
|
109
|
-
const
|
|
110
|
-
let i = (
|
|
111
|
-
return i || ({ constructor: i } = document.createElementNS(
|
|
112
|
-
},
|
|
113
|
-
(
|
|
114
|
-
for (const
|
|
115
|
-
if (
|
|
115
|
+
let N, r, S;
|
|
116
|
+
const O = (e, n) => {
|
|
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) => {
|
|
120
|
+
(r ?? (r = /* @__PURE__ */ new Set())).has(e) && r.delete(e);
|
|
121
|
+
for (const n of r) {
|
|
122
|
+
if (n.contains(e))
|
|
116
123
|
return;
|
|
117
|
-
e.contains(
|
|
124
|
+
e.contains(n) && r.delete(n);
|
|
118
125
|
}
|
|
119
|
-
|
|
120
|
-
},
|
|
121
|
-
for (const e of
|
|
126
|
+
r.add(e), S ?? (S = requestAnimationFrame(B));
|
|
127
|
+
}, B = () => {
|
|
128
|
+
for (const e of r)
|
|
122
129
|
e.isConnected && e.next();
|
|
123
|
-
|
|
130
|
+
r.clear(), S = null;
|
|
124
131
|
};
|
|
125
132
|
export {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
133
|
+
H as Fragment,
|
|
134
|
+
L as h,
|
|
135
|
+
m as render
|
|
129
136
|
};
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -83,19 +83,18 @@ render(<Counter />, document.body)
|
|
|
83
83
|
|
|
84
84
|
## API
|
|
85
85
|
|
|
86
|
-
### render(
|
|
86
|
+
### render(h, el)
|
|
87
87
|
|
|
88
|
-
Renders a virtual DOM
|
|
88
|
+
Renders a virtual DOM tree into the actual DOM element. It is the primary method used for updating the DOM with new content.
|
|
89
89
|
|
|
90
|
-
When called, it efficiently updates
|
|
90
|
+
When called, it efficiently updates `el` with the new content, adding, updating, or removing DOM nodes as needed.
|
|
91
91
|
|
|
92
92
|
This function enables the declarative description of the UI to be transformed into actual UI elements in the browser. It's designed to be efficient, minimizing updates to the actual DOM to improve performance and user experience.
|
|
93
93
|
|
|
94
94
|
#### Parameters:
|
|
95
95
|
|
|
96
|
-
- **
|
|
97
|
-
- **
|
|
98
|
-
- **namespace** (String, optional): An optional XML namespace URI. If specified, it allows for the creation of elements within a certain XML namespace, useful for SVG elements and other XML-based documents.
|
|
96
|
+
- **h** (Any): The virtual DOM tree to render. This can be any value, a simple string, a virtual DOM tree created by the `h` function, etc.
|
|
97
|
+
- **el** (HTMLElement): The DOM element into which the `h` should be rendered. This is typically a container element in your application.
|
|
99
98
|
|
|
100
99
|
#### Returns:
|
|
101
100
|
|
|
@@ -113,21 +112,21 @@ const App = () => <div>Hello, World!</div>
|
|
|
113
112
|
// Render the App component into the #root element
|
|
114
113
|
render(<App />, document.getElementById('root'))
|
|
115
114
|
```
|
|
116
|
-
In this example, the `App` component is a simple function returning a `div` with text content. The `render` function then mounts this component into the DOM element with the ID `root`.
|
|
115
|
+
> In this example, the `App` component is a simple function returning a `div` with text content. The `render` function then mounts this component into the DOM element with the ID `root`.
|
|
117
116
|
|
|
118
|
-
### h(
|
|
117
|
+
### h(type, [props], [...children])
|
|
119
118
|
|
|
120
|
-
Creates a virtual DOM
|
|
119
|
+
Creates a virtual DOM tree for rendering. It's the core function for defining UI components in JSX syntax. The `h` function is a hyperscript function that returns a virtual DOM tree representing the UI component. This object can then be rendered to an actual DOM using the `render` function.
|
|
121
120
|
|
|
122
121
|
#### Parameters:
|
|
123
122
|
|
|
124
|
-
- **
|
|
125
|
-
- **
|
|
126
|
-
- **children** (Any, optional): Child nodes. Can be a nested array of
|
|
123
|
+
- **type** (String | Function | Generator Function): The name of the tag for the DOM element you want to create. If it's a function, it's treated as a stateless component, and if it's a generator function, it's treated as a stateful component.
|
|
124
|
+
- **props** (Object, optional): An object containing properties you want to set on the virtual DOM element.
|
|
125
|
+
- **children** (Any, optional): Child virtual nodes. Can be a nested array of child nodes, a string, or any other renderable JSX elements. Booleans, null, and undefined child nodes will be ignored, which is useful for conditional rendering.
|
|
127
126
|
|
|
128
127
|
#### Returns:
|
|
129
128
|
|
|
130
|
-
- **Object**: A virtual DOM
|
|
129
|
+
- **Object**: A virtual DOM tree.
|
|
131
130
|
|
|
132
131
|
#### Example Usage:
|
|
133
132
|
|
|
@@ -135,13 +134,13 @@ Creates a virtual DOM element (vnode) for rendering. It's the core function for
|
|
|
135
134
|
/** @jsx h */
|
|
136
135
|
import { h } from 'ajo'
|
|
137
136
|
|
|
138
|
-
// Creating a simple
|
|
137
|
+
// Creating a simple virtual element
|
|
139
138
|
const myElement = h('div', { id: 'my-div' }, 'Hello World')
|
|
140
139
|
|
|
141
140
|
// or using JSX syntax
|
|
142
141
|
const myElementJSX = <div id="my-div">Hello World</div>
|
|
143
142
|
|
|
144
|
-
// Creating a
|
|
143
|
+
// Creating a virtual tree for a stateless component with children
|
|
145
144
|
const MyComponent = ({ class: className }) => h('div', { class: className },
|
|
146
145
|
h('h1', null, 'Header'),
|
|
147
146
|
'Text Content',
|
|
@@ -169,7 +168,7 @@ render(h(MyApp), document.body)
|
|
|
169
168
|
// or using JSX syntax
|
|
170
169
|
render(<MyAppJSX />, document.body)
|
|
171
170
|
```
|
|
172
|
-
You won't typically use the `h` function, it's automatically used when you write JSX code. The previous examples demonstrate how to use the `h` function directly if you need to.
|
|
171
|
+
> You won't typically use the `h` function, it's automatically used when you write JSX code. The previous examples demonstrate how to use the `h` function directly if you need to.
|
|
173
172
|
|
|
174
173
|
### `Fragment({ children })`
|
|
175
174
|
|
|
@@ -202,11 +201,11 @@ const MyComponentJSX = () => (
|
|
|
202
201
|
|
|
203
202
|
### `set:`
|
|
204
203
|
|
|
205
|
-
The `set:` prefix in Ajo allows you to directly set properties on DOM
|
|
204
|
+
The `set:` prefix in Ajo allows you to directly set properties on DOM elements from within your JSX. This is distinct from simply setting attributes, as it interacts with the properties of the DOM elements, much like how you would in plain JavaScript.
|
|
206
205
|
|
|
207
206
|
#### Purpose:
|
|
208
207
|
|
|
209
|
-
- **Direct DOM Property Manipulation:** The `set:` prefix is used for directly setting properties on DOM
|
|
208
|
+
- **Direct DOM Property Manipulation:** The `set:` prefix is used for directly setting properties on DOM elements. This is crucial for cases where you need to interact with the DOM API, or when a property does not have a direct attribute equivalent.
|
|
210
209
|
|
|
211
210
|
#### Usage:
|
|
212
211
|
|
|
@@ -222,16 +221,16 @@ function* MyComponent() {
|
|
|
222
221
|
yield <div set:textContent={text} skip></div>
|
|
223
222
|
}
|
|
224
223
|
```
|
|
225
|
-
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.
|
|
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.
|
|
226
225
|
|
|
227
|
-
**Setting
|
|
226
|
+
**Setting Inner HTML:**
|
|
228
227
|
```jsx
|
|
229
228
|
function* MyComponent() {
|
|
230
229
|
const html = "<p>Hello, Ajo!</p>"
|
|
231
230
|
yield <div set:innerHTML={html} skip></div>
|
|
232
231
|
}
|
|
233
232
|
```
|
|
234
|
-
In this case, `set:innerHTML` is used to set the `innerHTML` property of the `div`'s DOM
|
|
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.
|
|
235
234
|
|
|
236
235
|
**Event Handlers (e.g., onclick):**
|
|
237
236
|
```jsx
|
|
@@ -240,7 +239,7 @@ function* MyComponent() {
|
|
|
240
239
|
yield <button set:onclick={handleClick}>Click Me</button>
|
|
241
240
|
}
|
|
242
241
|
```
|
|
243
|
-
`set:onclick` assigns the `handleClick` function as the click event listener for the button.
|
|
242
|
+
> `set:onclick` assigns the `handleClick` function as the click event listener for the button.
|
|
244
243
|
|
|
245
244
|
### Special Attributes in Ajo
|
|
246
245
|
|
|
@@ -257,93 +256,85 @@ In Ajo, there are several special attributes (`key`, `skip`, `memo`, and `ref`)
|
|
|
257
256
|
- **Example:** `h('div', { skip: shouldSkip })` - here, if `shouldSkip` is `true`, Ajo will not render or update the `div`'s child nodes.
|
|
258
257
|
|
|
259
258
|
#### `memo` Attribute:
|
|
260
|
-
- **Purpose:** The `memo` attribute is used for
|
|
261
|
-
- **Behavior:** When the `memo` attribute is provided, Ajo will shallow compare the memoized values with the new ones. If they are the same, Ajo will skip rendering the element attributes and child nodes. For stateful components, this also prevents the component from re-rendering.
|
|
262
|
-
- **Example:** `h(div, { memo: [dependency1, dependency2] })` - the element
|
|
259
|
+
- **Purpose:** The `memo` attribute is used for memoization. It's a performance optimization technique to prevent unnecessary renders.
|
|
260
|
+
- **Behavior:** When the `memo` attribute is provided, Ajo will shallow compare the memoized values with the new ones. If they are the same, Ajo will skip rendering the element attributes, properties and child nodes. For stateful components, this also prevents the component from re-rendering.
|
|
261
|
+
- **Example:** `h(div, { memo: [dependency1, dependency2] })` - the element will re-render only if `dependency1` or `dependency2` change.
|
|
263
262
|
|
|
264
263
|
#### `ref` Attribute:
|
|
265
|
-
- **Purpose:** The `ref` attribute provides a way to access the underlying DOM
|
|
266
|
-
- **Behavior:** When an element is mounted or updated, the `ref` callback is called with the DOM
|
|
267
|
-
- **Example:** `h('input', { ref:
|
|
268
|
-
|
|
269
|
-
These special attributes in Ajo offer powerful ways to manage rendering performance and interact with DOM elements and components elements directly. They provide developers with finer control over the update behavior and lifecycle of components in their applications.
|
|
264
|
+
- **Purpose:** The `ref` attribute provides a way to access the underlying DOM element.
|
|
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
|
+
- **Example:** `h('input', { ref: el => (this.inputNode = el) })` - stores a reference to the input element.
|
|
270
267
|
|
|
271
268
|
## Stateful components
|
|
272
269
|
|
|
273
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.
|
|
274
271
|
|
|
275
|
-
The following example demonstrates
|
|
272
|
+
The following example demonstrates key features of stateful components in Ajo:
|
|
276
273
|
|
|
277
274
|
```jsx
|
|
278
|
-
function* ChatComponent({
|
|
279
|
-
|
|
280
|
-
// Define mutable state variables.
|
|
281
|
-
let messageToSend = '', isConnected = false
|
|
275
|
+
function* ChatComponent({ user = 'Anonymous', room }) { // Receive arguments initial values.
|
|
282
276
|
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
const chatConnection = new WebSocket(chatServerURL)
|
|
277
|
+
// Define mutable state variables.
|
|
278
|
+
let message = '', connected = false
|
|
286
279
|
|
|
287
280
|
// Define event handlers.
|
|
288
281
|
const handleMessageChange = event => {
|
|
289
282
|
|
|
290
|
-
|
|
283
|
+
message = event.target.value
|
|
291
284
|
|
|
292
|
-
// Render
|
|
285
|
+
// Render synchronously.
|
|
293
286
|
this.next()
|
|
294
287
|
}
|
|
295
288
|
|
|
296
|
-
const
|
|
289
|
+
const send = () => {
|
|
297
290
|
|
|
298
|
-
|
|
299
|
-
if (messageToSend) {
|
|
291
|
+
if (message) {
|
|
300
292
|
|
|
301
|
-
|
|
293
|
+
connection.send(JSON.stringify({ user: this.$args.user, message }))
|
|
302
294
|
|
|
303
|
-
|
|
304
|
-
messageToSend = ''
|
|
295
|
+
message = ''
|
|
305
296
|
|
|
306
|
-
//
|
|
297
|
+
// Render asynchronously.
|
|
307
298
|
this.refresh()
|
|
308
299
|
}
|
|
309
300
|
}
|
|
310
301
|
|
|
311
302
|
const handleConnectionOpen = () => {
|
|
312
|
-
|
|
313
|
-
isConnected = true
|
|
314
|
-
|
|
315
|
-
// Refresh to update connection status.
|
|
303
|
+
connected = true
|
|
316
304
|
this.refresh()
|
|
317
305
|
}
|
|
318
306
|
|
|
319
307
|
const handleConnectionError = error => {
|
|
308
|
+
// Throw error to be caught by the component itself or a parent component.
|
|
320
309
|
this.throw(new Error('Connection error: ' + error.message))
|
|
321
310
|
}
|
|
322
311
|
|
|
323
|
-
//
|
|
324
|
-
|
|
325
|
-
|
|
312
|
+
// Setup resources.
|
|
313
|
+
const server = `ws://chat.com/${room}`
|
|
314
|
+
const connection = new WebSocket(server)
|
|
326
315
|
|
|
327
|
-
|
|
316
|
+
connection.onopen = handleConnectionOpen
|
|
317
|
+
connection.onerror = handleConnectionError
|
|
318
|
+
|
|
319
|
+
// 'this' is a DOM element, so we can use DOM APIs on it.
|
|
328
320
|
this.classList.add('chat-component')
|
|
329
321
|
|
|
330
322
|
try { // Optional try/finally block for cleanup logic.
|
|
331
323
|
|
|
332
|
-
for ({
|
|
324
|
+
for ({ user } of this) { // Iterates over generator, optionally receiving updated arguments.
|
|
333
325
|
|
|
334
326
|
try { // Optional try/catch block for error handling.
|
|
335
327
|
|
|
336
328
|
// Compute derived values.
|
|
337
|
-
const
|
|
329
|
+
const status = connected ? `You are connected as ${user}.` : "Connecting to chat..."
|
|
338
330
|
|
|
339
|
-
// Render the
|
|
340
|
-
// Use set: prefix to set properties on DOM nodes, like event handlers.
|
|
331
|
+
// Render the component UI.
|
|
341
332
|
yield (
|
|
342
333
|
<>
|
|
343
|
-
<div class="status-message">{
|
|
344
|
-
<div class="connection-status">{
|
|
345
|
-
<input type="text" value={
|
|
346
|
-
<button set:onclick={
|
|
334
|
+
<div class="status-message">{status}</div>
|
|
335
|
+
<div class="connection-status">{connected ? 'Connected' : 'Connecting...'}</div>
|
|
336
|
+
<input type="text" value={message} set:onchange={handleMessageChange} />
|
|
337
|
+
<button set:onclick={send}>Send</button>
|
|
347
338
|
</>
|
|
348
339
|
)
|
|
349
340
|
} catch (e) {
|
|
@@ -352,8 +343,8 @@ function* ChatComponent({ userName = 'Anonymous', chatRoom }) { // Receive argum
|
|
|
352
343
|
}
|
|
353
344
|
}
|
|
354
345
|
} finally {
|
|
355
|
-
// Cleanup logic: close
|
|
356
|
-
|
|
346
|
+
// Cleanup logic: release resources, close connections, etc.
|
|
347
|
+
connection.close()
|
|
357
348
|
}
|
|
358
349
|
}
|
|
359
350
|
```
|
|
@@ -401,9 +392,10 @@ function* DataFetcher() {
|
|
|
401
392
|
}
|
|
402
393
|
}
|
|
403
394
|
```
|
|
404
|
-
In this example, `DataFetcher` uses `this.refresh()` to update its display after data is fetched. The use of `this.refresh()` ensures that the rendering is efficient and aligned with the browser's rendering cycle.
|
|
395
|
+
> In this example, `DataFetcher` uses `this.refresh()` to update its display after data is fetched. The use of `this.refresh()` ensures that the rendering is efficient and aligned with the browser's rendering cycle.
|
|
405
396
|
|
|
406
397
|
### `this.next()`
|
|
398
|
+
> **Note:** `this.next()` is called asynchronously when calling `this.refresh()`.
|
|
407
399
|
|
|
408
400
|
The `next` method is used within stateful components in Ajo to manually advance the component's generator function to its next yield point. This method is crucial for synchronously rendering the next state of the component.
|
|
409
401
|
|
|
@@ -436,9 +428,10 @@ function* Counter() {
|
|
|
436
428
|
}
|
|
437
429
|
}
|
|
438
430
|
```
|
|
439
|
-
In this example, `Counter` uses `this.next()` in its `increment` function to immediately render the updated count whenever the button is clicked.
|
|
431
|
+
> In this example, `Counter` uses `this.next()` in its `increment` function to immediately render the updated count whenever the button is clicked.
|
|
440
432
|
|
|
441
433
|
### `this.throw()`
|
|
434
|
+
> **Note:** `this.throw()` is automatically called when an error is thrown from a component's generator function.
|
|
442
435
|
|
|
443
436
|
The `throw` method in Ajo stateful components is designed for error propagation within the component hierarchy. It allows developers to throw errors from a child component to be caught and handled by itself or a parent component, facilitating a structured approach to error management.
|
|
444
437
|
|
|
@@ -484,10 +477,10 @@ function* ParentComponent() {
|
|
|
484
477
|
}
|
|
485
478
|
}
|
|
486
479
|
```
|
|
487
|
-
|
|
488
|
-
In this example, `ChildComponent` uses `this.throw()` within an event handler to propagate errors upwards to its parent component, `ParentComponent`. The parent component then catches the error and renders it to the DOM.
|
|
480
|
+
> In this example, `ChildComponent` uses `this.throw()` within an event handler to propagate errors upwards to its parent component, `ParentComponent`. The parent component then catches the error and renders it to the DOM.
|
|
489
481
|
|
|
490
482
|
### `this.return()`
|
|
483
|
+
> **Note:** `this.return()` is automatically called when a stateful component is unmounted.
|
|
491
484
|
|
|
492
485
|
The `return` method in Ajo is used to reset and restart the generator function of a stateful component. It effectively ends the current execution of the component's generator function, and optionally re-execute it from scratch allowing for a complete reset of the component's state and behavior.
|
|
493
486
|
|
|
@@ -521,11 +514,11 @@ function* MultiStepForm({ initialData }) {
|
|
|
521
514
|
|
|
522
515
|
// Reset the generator function
|
|
523
516
|
this.return()
|
|
524
|
-
|
|
517
|
+
|
|
525
518
|
// Re-render the component in its initial state
|
|
526
519
|
this.refresh()
|
|
527
520
|
}
|
|
528
|
-
|
|
521
|
+
|
|
529
522
|
while (true) {
|
|
530
523
|
switch(currentStep) {
|
|
531
524
|
case 0:
|
|
@@ -551,19 +544,19 @@ function* MultiStepForm({ initialData }) {
|
|
|
551
544
|
}
|
|
552
545
|
}
|
|
553
546
|
```
|
|
554
|
-
In `handleRestart`, `this.return()` is first called to reset the generator function. This effectively ends the current execution of the component's generator function and prepares it to start from the beginning. Immediately after, `this.refresh()` is called to trigger a re-render of the component. This ensures that after the state is reset, the component's UI is also updated to reflect its initial state.
|
|
547
|
+
> In `handleRestart`, `this.return()` is first called to reset the generator function. This effectively ends the current execution of the component's generator function and prepares it to start from the beginning. Immediately after, `this.refresh()` is called to trigger a re-render of the component. This ensures that after the state is reset, the component's UI is also updated to reflect its initial state.
|
|
555
548
|
|
|
556
549
|
### `arg:`
|
|
557
550
|
|
|
558
551
|
- **Purpose:** The `arg:` prefix is used in Ajo to explicitly pass arguments to generator functions. This prefix distinguishes component arguments from regular HTML attributes and other special properties.
|
|
559
552
|
|
|
560
553
|
- **Behavior:**
|
|
561
|
-
- When a stateful component is rendered in Ajo, any
|
|
554
|
+
- When a stateful component is rendered in Ajo, any property on it that starts with `arg:` is treated as an argument to be passed to the component's generator function.
|
|
562
555
|
- This mechanism ensures that the arguments are clearly identified and separated from other attributes or DOM properties.
|
|
563
556
|
|
|
564
557
|
- **Usage:**
|
|
565
558
|
- Use `arg:` prefixed attributes when you need to pass data or event handlers to a component's generator function.
|
|
566
|
-
- This approach is particularly useful in maintaining a clear separation between component-specific
|
|
559
|
+
- This approach is particularly useful in maintaining a clear separation between component-specific arguments and other attributes that might be used for styling or DOM manipulation.
|
|
567
560
|
|
|
568
561
|
- **Example:**
|
|
569
562
|
```jsx
|
|
@@ -572,14 +565,18 @@ function* ParentComponent() {
|
|
|
572
565
|
const someData = { /* ... */ }
|
|
573
566
|
const handleEvent = () => { /* ... */ }
|
|
574
567
|
|
|
575
|
-
yield <ChildComponent
|
|
568
|
+
yield <ChildComponent
|
|
569
|
+
class="my-class"
|
|
570
|
+
arg:data={someData}
|
|
571
|
+
arg:onEvent={handleEvent}
|
|
572
|
+
/>
|
|
576
573
|
}
|
|
577
574
|
|
|
578
575
|
function* ChildComponent({ data, onEvent }) {
|
|
579
576
|
// ...
|
|
580
577
|
}
|
|
581
578
|
```
|
|
582
|
-
In this example, `ParentComponent` renders `ChildComponent`, passing `someData` and `handleEvent` as arguments using the `arg:` prefix. `class` is a regular HTML attribute and is not passed to the component's generator function, it is applied to the DOM element associated with the component.
|
|
579
|
+
> In this example, `ParentComponent` renders `ChildComponent`, passing `someData` and `handleEvent` as arguments using the `arg:` prefix. `class` is a regular HTML attribute and is not passed to the component's generator function, it is applied to the DOM element associated with the component.
|
|
583
580
|
|
|
584
581
|
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.
|
|
585
582
|
|