ajo 0.1.11 → 0.1.13
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 +35 -33
- package/dist/index.cjs +1 -1
- package/dist/index.js +77 -73
- package/package.json +3 -3
- package/readme.md +196 -57
package/dist/html.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const{entries:$,hasOwn:g}=Object,
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const{assign:p,entries:$,hasOwn:g}=Object,f=e=>typeof e!="string"&&typeof(e==null?void 0:e[Symbol.iterator])=="function",c=e=>[...y(e)].join(""),y=function*(e){for(e of d(e))if(typeof e=="string")yield m(e);else{const{nodeName:t,skip:o,children:i=""}=e;let n="";for(const[l,r]of $(e))v.has(l)||l.startsWith("set:")||r==null||r===!1||(n+=r===!0?`${n} ${l}`:`${n} ${l}="${m(String(r))}"`);k.has(t)?yield`<${t}${n}>`:o?yield`<${t}${n}></${t}>`:typeof i=="string"?yield`<${t}${n}>${i}</${t}>`:(yield`<${t}${n}>`,yield*y(i),yield`</${t}>`)}},d=function*(e,t={value:""},o=!0){for(e of f(e)?e:[e])if(!(e==null||typeof e=="boolean"))if(g(e,"nodeName")){const{value:i}=t,{nodeName:n}=e,l=typeof n;if(i&&(yield i,t.value=""),l==="function")if(delete e.nodeName,n.constructor.name==="GeneratorFunction"){const r={},s=p({},n.attrs);for(const a in e){const u=e[a];a.startsWith("attr:")?s[a.slice(5)]=u:r[a]=u}s.nodeName=n.is??"div",s.children=w(n,r),yield s}else delete e.nodeName,yield*d(n(e),t,!1);else l==="string"&&(yield e)}else f(e)?yield*d(e,t,!1):t.value+=e;o&&t.value&&(yield t.value)},k=new Set("area,base,br,col,command,embed,hr,img,input,keygen,link,meta,param,source,track,wbr".split(",")),v=new Set("nodeName,key,skip,memo,ref,children".split(",")),m=e=>e.replace(/[&<>"']/g,t=>`&#${t.charCodeAt(0)};`),w=(e,t)=>{let o,i;try{const n=e.call(o={$args:t,*[Symbol.iterator](){for(;;)yield t},refresh(){},next(){i=c(n.next().value)},throw(l){i=c(n.throw(l).value)},return(){n.return()}},t);o.next()}catch(n){o.throw(n)}finally{o.return()}return i};exports.html=y;exports.render=c;
|
package/dist/html.js
CHANGED
|
@@ -1,36 +1,38 @@
|
|
|
1
|
-
const {
|
|
2
|
-
for (e of
|
|
1
|
+
const { assign: p, entries: $, hasOwn: g } = Object, y = (e) => typeof e != "string" && typeof (e == null ? void 0 : e[Symbol.iterator]) == "function", f = (e) => [...m(e)].join(""), m = function* (e) {
|
|
2
|
+
for (e of c(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
|
-
k.has(t) ? yield `<${t}${
|
|
6
|
+
const { nodeName: t, skip: o, children: i = "" } = e;
|
|
7
|
+
let n = "";
|
|
8
|
+
for (const [l, s] of $(e))
|
|
9
|
+
w.has(l) || l.startsWith("set:") || s == null || s === !1 || (n += s === !0 ? `${n} ${l}` : `${n} ${l}="${u(String(s))}"`);
|
|
10
|
+
k.has(t) ? yield `<${t}${n}>` : o ? yield `<${t}${n}></${t}>` : typeof i == "string" ? yield `<${t}${n}>${i}</${t}>` : (yield `<${t}${n}>`, yield* m(i), yield `</${t}>`);
|
|
11
11
|
}
|
|
12
|
-
},
|
|
13
|
-
for (e of
|
|
12
|
+
}, c = function* (e, t = { value: "" }, o = !0) {
|
|
13
|
+
for (e of y(e) ? e : [e])
|
|
14
14
|
if (!(e == null || typeof e == "boolean"))
|
|
15
15
|
if (g(e, "nodeName")) {
|
|
16
|
-
const { value:
|
|
17
|
-
if (
|
|
18
|
-
if (n.constructor.name === "GeneratorFunction") {
|
|
19
|
-
const
|
|
20
|
-
for (const
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
const { value: i } = t, { nodeName: n } = e, l = typeof n;
|
|
17
|
+
if (i && (yield i, t.value = ""), l === "function")
|
|
18
|
+
if (delete e.nodeName, n.constructor.name === "GeneratorFunction") {
|
|
19
|
+
const s = {}, r = p({}, n.attrs);
|
|
20
|
+
for (const a in e) {
|
|
21
|
+
const d = e[a];
|
|
22
|
+
a.startsWith("attr:") ? r[a.slice(5)] = d : s[a] = d;
|
|
23
|
+
}
|
|
24
|
+
r.nodeName = n.is ?? "div", r.children = v(n, s), yield r;
|
|
23
25
|
} else
|
|
24
|
-
delete e.nodeName, yield*
|
|
26
|
+
delete e.nodeName, yield* c(n(e), t, !1);
|
|
25
27
|
else
|
|
26
|
-
|
|
28
|
+
l === "string" && (yield e);
|
|
27
29
|
} else
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}, k = new Set("area,base,br,col,command,embed,hr,img,input,keygen,link,meta,param,source,track,wbr".split(",")),
|
|
31
|
-
let
|
|
30
|
+
y(e) ? yield* c(e, t, !1) : t.value += e;
|
|
31
|
+
o && t.value && (yield t.value);
|
|
32
|
+
}, k = new Set("area,base,br,col,command,embed,hr,img,input,keygen,link,meta,param,source,track,wbr".split(",")), w = new Set("nodeName,key,skip,memo,ref,children".split(",")), u = (e) => e.replace(/[&<>"']/g, (t) => `&#${t.charCodeAt(0)};`), v = (e, t) => {
|
|
33
|
+
let o, i;
|
|
32
34
|
try {
|
|
33
|
-
const n = e.call(
|
|
35
|
+
const n = e.call(o = {
|
|
34
36
|
$args: t,
|
|
35
37
|
*[Symbol.iterator]() {
|
|
36
38
|
for (; ; )
|
|
@@ -39,24 +41,24 @@ const { entries: p, hasOwn: g } = Object, f = (e) => typeof e != "string" && typ
|
|
|
39
41
|
refresh() {
|
|
40
42
|
},
|
|
41
43
|
next() {
|
|
42
|
-
|
|
44
|
+
i = f(n.next().value);
|
|
43
45
|
},
|
|
44
|
-
throw(
|
|
45
|
-
|
|
46
|
+
throw(l) {
|
|
47
|
+
i = f(n.throw(l).value);
|
|
46
48
|
},
|
|
47
49
|
return() {
|
|
48
50
|
n.return();
|
|
49
51
|
}
|
|
50
52
|
}, t);
|
|
51
|
-
|
|
53
|
+
o.next();
|
|
52
54
|
} catch (n) {
|
|
53
|
-
|
|
55
|
+
o.throw(n);
|
|
54
56
|
} finally {
|
|
55
|
-
|
|
57
|
+
o.return();
|
|
56
58
|
}
|
|
57
|
-
return
|
|
59
|
+
return i;
|
|
58
60
|
};
|
|
59
61
|
export {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
m as html,
|
|
63
|
+
f as render
|
|
62
64
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const b=({children:e})=>e,A=function(e,t){const{length:i}=arguments;return(t??(t=C(null))).nodeName=e,"children"in t||i<3||(t.children=i===3?arguments[2]:O.call(arguments,2)),t},y=(e,t)=>{let i=t.firstChild;for(e of p(e)){let n=i;if(typeof e=="string"){for(;n&&n.nodeType!=3;)n=n.nextSibling;n?n.data!=e&&(n.data=e):n=document.createTextNode(e)}else if(e instanceof Node)n=e;else{const{nodeName:o,key:a,skip:m,memo:r,ref:s,children:u}=e;for(;n&&!(n.localName===o&&(n.$key??(n.$key=a))==a);)n=n.nextSibling;if(n??(n=g(document.createElementNS(e.xmlns??o==="svg"?T:t.namespaceURI,o),{$key:a})),r==null||P(n.$memo,n.$memo=r)){const{$props:d}=n,w={};for(const c of h(g({},d,e))){if(q.has(c))continue;const f=w[c]=e[c];f!==(d==null?void 0:d[c])&&(c.startsWith("set:")?n[c.slice(4)]=f:f==null||f===!1?n.removeAttribute(c):n.setAttribute(c,f===!0?"":f))}n.$props=w,m||y(u,n),typeof s=="function"&&(n.$ref=s)(n)}}n===i?i=i.nextSibling:E(t,n,i)}for(;i;){const n=i.nextSibling;i.nodeType===1&&N(i),t.removeChild(i),i=n}},{isArray:$,prototype:{slice:O}}=Array,{create:C,keys:h,assign:g,hasOwn:F,setPrototypeOf:x,getPrototypeOf:v}=Object,T="http://www.w3.org/2000/svg",q=new Set("nodeName,key,skip,memo,ref,children".split(",")),S=e=>typeof e!="string"&&typeof(e==null?void 0:e[Symbol.iterator])=="function",P=(e,t)=>$(e)&&$(t)?e.some((i,n)=>i!==t[n]):e!==t,p=function*(e,t={value:""},i=!0){for(e of S(e)?e:[e])if(!(e==null||typeof e=="boolean"))if(F(e,"nodeName")){const{value:n}=t,{nodeName:o}=e,a=typeof o;if(n&&(yield n,t.value=""),a==="function")if(delete e.nodeName,o.constructor.name==="GeneratorFunction"){const m={},r=g({},o.attrs);for(const s of h(e)){const u=e[s];s.startsWith("attr:")&&(r[s.slice(5)]=u),s==="key"||s==="memo"?r[s]=u:m[s]=u}r.nodeName=o.is??"div",r.skip=!0,r.ref=j.bind(null,o,m),yield r}else yield*p(o(e),t,!1);else a==="string"&&(yield e)}else S(e)?yield*p(e,t,!1):t.value+=e;i&&t.value&&(yield t.value)},j=(e,t,i)=>{i&&(i.$gen??(i.$gen=(new I(i),e)),i.$ref=B.bind(null,i),i.$args=t,i.next())},B=(e,t)=>t??e.return(),E=(e,t,i)=>{if(t.contains(document.activeElement)){const n=t.nextSibling;for(;i&&i!=t;){const o=i.nextSibling;e.insertBefore(i,n),i=o}}else e.insertBefore(t,i)},N=e=>{for(const i of e.children)N(i);const{$ref:t}=e;typeof t=="function"&&t(null);for(const i of h(e))e[i]=null};class I{constructor(t){x(t,x(v(this),v(t)))}*[Symbol.iterator](){for(;;)yield this.$args??{}}refresh(){W(this)}next(){try{y((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 i;for(let n=this;n;n=n.parentNode)if(typeof((i=n.$it)==null?void 0:i.throw)=="function")try{return y(n.$it.throw(t).value,n)}catch{}throw t}return(){var t;try{(t=this.$it)==null||t.return()}catch(i){this.throw(i)}finally{this.$it=null}}}let l,k;const W=e=>{(l??(l=new Set)).has(e)&&l.delete(e);for(const t of l){if(t.contains(e))return;e.contains(t)&&l.delete(t)}l.add(e),k??(k=requestAnimationFrame(z))},z=()=>{for(const e of l)e.isConnected&&e.next();l.clear(),k=null};exports.Fragment=b;exports.h=A;exports.render=y;
|
package/dist/index.js
CHANGED
|
@@ -1,96 +1,100 @@
|
|
|
1
|
-
const
|
|
2
|
-
const { length:
|
|
3
|
-
return (t ?? (t =
|
|
4
|
-
},
|
|
5
|
-
let
|
|
6
|
-
for (e of
|
|
7
|
-
let
|
|
1
|
+
const j = ({ children: e }) => e, z = function(e, t) {
|
|
2
|
+
const { length: i } = arguments;
|
|
3
|
+
return (t ?? (t = A(null))).nodeName = e, "children" in t || i < 3 || (t.children = i === 3 ? arguments[2] : b.call(arguments, 2)), t;
|
|
4
|
+
}, d = (e, t) => {
|
|
5
|
+
let i = t.firstChild;
|
|
6
|
+
for (e of p(e)) {
|
|
7
|
+
let n = i;
|
|
8
8
|
if (typeof e == "string") {
|
|
9
|
-
for (;
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
for (; n && n.nodeType != 3; )
|
|
10
|
+
n = n.nextSibling;
|
|
11
|
+
n ? n.data != e && (n.data = e) : n = document.createTextNode(e);
|
|
12
12
|
} else if (e instanceof Node)
|
|
13
|
-
|
|
13
|
+
n = e;
|
|
14
14
|
else {
|
|
15
|
-
const { nodeName: o, key:
|
|
16
|
-
for (;
|
|
17
|
-
|
|
18
|
-
if (
|
|
19
|
-
const { $props:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (T.has(s))
|
|
15
|
+
const { nodeName: o, key: a, skip: m, memo: r, ref: s, children: u } = e;
|
|
16
|
+
for (; n && !(n.localName === o && (n.$key ?? (n.$key = a)) == a); )
|
|
17
|
+
n = n.nextSibling;
|
|
18
|
+
if (n ?? (n = g(document.createElementNS(e.xmlns ?? o === "svg" ? O : t.namespaceURI, o), { $key: a })), r == null || F(n.$memo, n.$memo = r)) {
|
|
19
|
+
const { $props: y } = n, $ = {};
|
|
20
|
+
for (const c of w(g({}, y, e))) {
|
|
21
|
+
if (q.has(c))
|
|
23
22
|
continue;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
continue;
|
|
27
|
-
}
|
|
28
|
-
const l = g[s] = e[s];
|
|
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));
|
|
23
|
+
const f = $[c] = e[c];
|
|
24
|
+
f !== (y == null ? void 0 : y[c]) && (c.startsWith("set:") ? n[c.slice(4)] = f : f == null || f === !1 ? n.removeAttribute(c) : n.setAttribute(c, f === !0 ? "" : f));
|
|
30
25
|
}
|
|
31
|
-
|
|
26
|
+
n.$props = $, m || d(u, n), typeof s == "function" && (n.$ref = s)(n);
|
|
32
27
|
}
|
|
33
28
|
}
|
|
34
|
-
|
|
29
|
+
n === i ? i = i.nextSibling : E(t, n, i);
|
|
35
30
|
}
|
|
36
|
-
for (;
|
|
37
|
-
const
|
|
38
|
-
|
|
31
|
+
for (; i; ) {
|
|
32
|
+
const n = i.nextSibling;
|
|
33
|
+
i.nodeType === 1 && k(i), t.removeChild(i), i = n;
|
|
39
34
|
}
|
|
40
|
-
},
|
|
41
|
-
for (e of
|
|
35
|
+
}, { isArray: h, prototype: { slice: b } } = Array, { create: A, keys: w, assign: g, hasOwn: C, setPrototypeOf: x, getPrototypeOf: v } = Object, O = "http://www.w3.org/2000/svg", q = new Set("nodeName,key,skip,memo,ref,children".split(",")), N = (e) => typeof e != "string" && typeof (e == null ? void 0 : e[Symbol.iterator]) == "function", F = (e, t) => h(e) && h(t) ? e.some((i, n) => i !== t[n]) : e !== t, p = function* (e, t = { value: "" }, i = !0) {
|
|
36
|
+
for (e of N(e) ? e : [e])
|
|
42
37
|
if (!(e == null || typeof e == "boolean"))
|
|
43
38
|
if (C(e, "nodeName")) {
|
|
44
|
-
const { value:
|
|
45
|
-
|
|
39
|
+
const { value: n } = t, { nodeName: o } = e, a = typeof o;
|
|
40
|
+
if (n && (yield n, t.value = ""), a === "function")
|
|
41
|
+
if (delete e.nodeName, o.constructor.name === "GeneratorFunction") {
|
|
42
|
+
const m = {}, r = g({}, o.attrs);
|
|
43
|
+
for (const s of w(e)) {
|
|
44
|
+
const u = e[s];
|
|
45
|
+
s.startsWith("attr:") && (r[s.slice(5)] = u), s === "key" || s === "memo" ? r[s] = u : m[s] = u;
|
|
46
|
+
}
|
|
47
|
+
r.nodeName = o.is ?? "div", r.skip = !0, r.ref = T.bind(null, o, m), yield r;
|
|
48
|
+
} else
|
|
49
|
+
yield* p(o(e), t, !1);
|
|
50
|
+
else
|
|
51
|
+
a === "string" && (yield e);
|
|
46
52
|
} else
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
},
|
|
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) => {
|
|
53
|
+
N(e) ? yield* p(e, t, !1) : t.value += e;
|
|
54
|
+
i && t.value && (yield t.value);
|
|
55
|
+
}, T = (e, t, i) => {
|
|
56
|
+
i && (i.$gen ?? (i.$gen = (new I(i), e)), i.$ref = B.bind(null, i), i.$args = t, i.next());
|
|
57
|
+
}, B = (e, t) => t ?? e.return(), E = (e, t, i) => {
|
|
54
58
|
if (t.contains(document.activeElement)) {
|
|
55
|
-
const
|
|
56
|
-
for (;
|
|
57
|
-
const o =
|
|
58
|
-
e.insertBefore(
|
|
59
|
+
const n = t.nextSibling;
|
|
60
|
+
for (; i && i != t; ) {
|
|
61
|
+
const o = i.nextSibling;
|
|
62
|
+
e.insertBefore(i, n), i = o;
|
|
59
63
|
}
|
|
60
64
|
} else
|
|
61
|
-
e.insertBefore(t,
|
|
62
|
-
},
|
|
63
|
-
for (const
|
|
64
|
-
|
|
65
|
+
e.insertBefore(t, i);
|
|
66
|
+
}, k = (e) => {
|
|
67
|
+
for (const i of e.children)
|
|
68
|
+
k(i);
|
|
65
69
|
const { $ref: t } = e;
|
|
66
70
|
typeof t == "function" && t(null);
|
|
67
|
-
for (const
|
|
68
|
-
e[
|
|
71
|
+
for (const i of w(e))
|
|
72
|
+
e[i] = null;
|
|
69
73
|
};
|
|
70
|
-
class
|
|
74
|
+
class I {
|
|
71
75
|
constructor(t) {
|
|
72
|
-
|
|
76
|
+
x(t, x(v(this), v(t)));
|
|
73
77
|
}
|
|
74
78
|
*[Symbol.iterator]() {
|
|
75
79
|
for (; ; )
|
|
76
80
|
yield this.$args ?? {};
|
|
77
81
|
}
|
|
78
82
|
refresh() {
|
|
79
|
-
|
|
83
|
+
P(this);
|
|
80
84
|
}
|
|
81
85
|
next() {
|
|
82
86
|
try {
|
|
83
|
-
|
|
87
|
+
d((this.$it ?? (this.$it = this.$gen.call(this, this.$args ?? {}))).next().value, this), typeof this.$ref == "function" && this.$ref(this);
|
|
84
88
|
} catch (t) {
|
|
85
89
|
this.throw(t);
|
|
86
90
|
}
|
|
87
91
|
}
|
|
88
92
|
throw(t) {
|
|
89
|
-
var
|
|
90
|
-
for (let
|
|
91
|
-
if (typeof ((
|
|
93
|
+
var i;
|
|
94
|
+
for (let n = this; n; n = n.parentNode)
|
|
95
|
+
if (typeof ((i = n.$it) == null ? void 0 : i.throw) == "function")
|
|
92
96
|
try {
|
|
93
|
-
return
|
|
97
|
+
return d(n.$it.throw(t).value, n);
|
|
94
98
|
} catch {
|
|
95
99
|
}
|
|
96
100
|
throw t;
|
|
@@ -99,29 +103,29 @@ class E {
|
|
|
99
103
|
var t;
|
|
100
104
|
try {
|
|
101
105
|
(t = this.$it) == null || t.return();
|
|
102
|
-
} catch (
|
|
103
|
-
this.throw(
|
|
106
|
+
} catch (i) {
|
|
107
|
+
this.throw(i);
|
|
104
108
|
} finally {
|
|
105
109
|
this.$it = null;
|
|
106
110
|
}
|
|
107
111
|
}
|
|
108
112
|
}
|
|
109
|
-
let
|
|
110
|
-
const
|
|
111
|
-
(
|
|
112
|
-
for (const t of
|
|
113
|
+
let l, S;
|
|
114
|
+
const P = (e) => {
|
|
115
|
+
(l ?? (l = /* @__PURE__ */ new Set())).has(e) && l.delete(e);
|
|
116
|
+
for (const t of l) {
|
|
113
117
|
if (t.contains(e))
|
|
114
118
|
return;
|
|
115
|
-
e.contains(t) &&
|
|
119
|
+
e.contains(t) && l.delete(t);
|
|
116
120
|
}
|
|
117
|
-
|
|
118
|
-
},
|
|
119
|
-
for (const e of
|
|
121
|
+
l.add(e), S ?? (S = requestAnimationFrame(W));
|
|
122
|
+
}, W = () => {
|
|
123
|
+
for (const e of l)
|
|
120
124
|
e.isConnected && e.next();
|
|
121
|
-
|
|
125
|
+
l.clear(), S = null;
|
|
122
126
|
};
|
|
123
127
|
export {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
128
|
+
j as Fragment,
|
|
129
|
+
z as h,
|
|
130
|
+
d as render
|
|
127
131
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ajo",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "ajo is a JavaScript view library for building user interfaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
],
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"jsdom": "^23.0.1",
|
|
23
|
-
"vite": "^5.0.
|
|
24
|
-
"vitest": "^0.
|
|
23
|
+
"vite": "^5.0.10",
|
|
24
|
+
"vitest": "^1.0.4"
|
|
25
25
|
},
|
|
26
26
|
"keywords": [
|
|
27
27
|
"ui",
|
package/readme.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
# Ajo
|
|
17
17
|
|
|
18
|
-
Ajo is a
|
|
18
|
+
Ajo is a library designed for building dynamic UI components using JSX. Integrating ideas from Incremental DOM and Crank.js, Ajo offers a unique approach in the landscape of UI libraries.
|
|
19
19
|
|
|
20
20
|
Key features:
|
|
21
21
|
|
|
@@ -23,7 +23,7 @@ Key features:
|
|
|
23
23
|
- **Generator-Based State Management**: Leverages JavaScript Generators for managing component states and effects, offering developers a robust tool for controlling UI lifecycle events.
|
|
24
24
|
- **Minimalistic Rendering Approach**: Ajo’s rendering system is optimized for minimal overhead, enhancing the speed of DOM updates and overall application performance.
|
|
25
25
|
- **JSX Syntax for Intuitive Development**: Supports JSX, making it easy for developers familiar with React or similar libraries to adopt and use Ajo effectively.
|
|
26
|
-
- **Lifecycle Management for Components**: Provides a
|
|
26
|
+
- **Lifecycle Management for Components**: Provides a suite of lifecycle methods for stateful components, facilitating precise control over component behaviors during their lifecycle.
|
|
27
27
|
- **Flexibility and Lightweight Design**: Ajo is designed to be both adaptable for various use cases and lightweight, ensuring minimal impact on project size.
|
|
28
28
|
|
|
29
29
|
## Install
|
|
@@ -137,9 +137,6 @@ import { h } from 'ajo'
|
|
|
137
137
|
// Creating a simple virtual element
|
|
138
138
|
const myElement = h('div', { id: 'my-div' }, 'Hello World')
|
|
139
139
|
|
|
140
|
-
// or using JSX syntax
|
|
141
|
-
const myElementJSX = <div id="my-div">Hello World</div>
|
|
142
|
-
|
|
143
140
|
// Creating a virtual tree for a stateless component with children
|
|
144
141
|
const MyComponent = ({ class: className }) => h('div', { class: className },
|
|
145
142
|
h('h1', null, 'Header'),
|
|
@@ -147,26 +144,11 @@ const MyComponent = ({ class: className }) => h('div', { class: className },
|
|
|
147
144
|
h('p', null, 'Paragraph'),
|
|
148
145
|
)
|
|
149
146
|
|
|
150
|
-
// or using JSX syntax
|
|
151
|
-
const MyComponentJSX = ({ class: className }) => (
|
|
152
|
-
<div class={className}>
|
|
153
|
-
<h1>Header</h1>
|
|
154
|
-
Text Content
|
|
155
|
-
<p>Paragraph</p>
|
|
156
|
-
</div>
|
|
157
|
-
)
|
|
158
|
-
|
|
159
147
|
// Composing
|
|
160
148
|
const MyApp = () => h(MyComponent, { class: 'my-class' })
|
|
161
149
|
|
|
162
|
-
// or using JSX syntax
|
|
163
|
-
const MyAppJSX = () => <MyComponent class="my-class" />
|
|
164
|
-
|
|
165
150
|
// Render into a DOM element
|
|
166
151
|
render(h(MyApp), document.body)
|
|
167
|
-
|
|
168
|
-
// or using JSX syntax
|
|
169
|
-
render(<MyAppJSX />, document.body)
|
|
170
152
|
```
|
|
171
153
|
> 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.
|
|
172
154
|
|
|
@@ -183,6 +165,7 @@ In JSX, fragments are typically represented with empty tags (`<>...</>`), but th
|
|
|
183
165
|
//* @jsxFrag Fragment */
|
|
184
166
|
import { h, Fragment } from 'ajo'
|
|
185
167
|
|
|
168
|
+
// Using the h function
|
|
186
169
|
const MyComponent = () => {
|
|
187
170
|
return h(Fragment, null,
|
|
188
171
|
h('h1', null, 'Hello'),
|
|
@@ -201,16 +184,15 @@ const MyComponentJSX = () => (
|
|
|
201
184
|
|
|
202
185
|
### `set:`
|
|
203
186
|
|
|
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.
|
|
187
|
+
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. Ideal for situations where setting a DOM property is more appropriate or efficient than setting an HTML attribute.
|
|
205
188
|
|
|
206
189
|
#### Purpose:
|
|
207
190
|
|
|
208
|
-
-
|
|
191
|
+
- 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.
|
|
209
192
|
|
|
210
193
|
#### Usage:
|
|
211
194
|
|
|
212
|
-
-
|
|
213
|
-
- **JavaScript-centric DOM Interaction:** Ideal for situations where setting a DOM property is more appropriate or efficient than setting an HTML attribute.
|
|
195
|
+
- Use `set:` to assign various types of properties to DOM elements, including but not limited to event handlers. It can be used for properties like `textContent`, `scrollTop`, custom properties, and more.
|
|
214
196
|
|
|
215
197
|
#### Examples:
|
|
216
198
|
|
|
@@ -265,7 +247,7 @@ In Ajo, there are several special attributes (`key`, `skip`, `memo`, and `ref`)
|
|
|
265
247
|
- **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
248
|
- **Example:** `h('input', { ref: el => (this.inputNode = el) })` - stores a reference to the input element.
|
|
267
249
|
|
|
268
|
-
## Stateful
|
|
250
|
+
## Stateful Components
|
|
269
251
|
|
|
270
252
|
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
253
|
|
|
@@ -290,6 +272,7 @@ function* ChatComponent({ user = 'Anonymous', room }) { // Receive arguments ini
|
|
|
290
272
|
|
|
291
273
|
if (message) {
|
|
292
274
|
|
|
275
|
+
// Access current arguments values with 'this.$args'.
|
|
293
276
|
connection.send(JSON.stringify({ user: this.$args.user, message }))
|
|
294
277
|
|
|
295
278
|
message = ''
|
|
@@ -349,6 +332,87 @@ function* ChatComponent({ user = 'Anonymous', room }) { // Receive arguments ini
|
|
|
349
332
|
}
|
|
350
333
|
```
|
|
351
334
|
|
|
335
|
+
### Default Rendering Behavior
|
|
336
|
+
|
|
337
|
+
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.
|
|
338
|
+
|
|
339
|
+
For example:
|
|
340
|
+
|
|
341
|
+
```javascript
|
|
342
|
+
function* MyComponent() {
|
|
343
|
+
// The 'this' variable here refers to the default 'div' wrapper element
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
> This default behavior ensures a consistent and predictable wrapper for your component's content, making it easier to manage and style.
|
|
347
|
+
|
|
348
|
+
### Customizing the Wrapper Element
|
|
349
|
+
|
|
350
|
+
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.
|
|
351
|
+
|
|
352
|
+
To specify a different element type for the wrapper, set the `is` property on the Generator Function of your component. For example:
|
|
353
|
+
|
|
354
|
+
```javascript
|
|
355
|
+
function* MyCustomRow() {
|
|
356
|
+
// The 'this' variable here refers to the default 'tr' wrapper element
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
MyCustomRow.is = 'tr'
|
|
360
|
+
```
|
|
361
|
+
> 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.
|
|
362
|
+
|
|
363
|
+
### Wrapper Element Default Attributes
|
|
364
|
+
|
|
365
|
+
When a stateful component is rendered in Ajo, you can specify default attributes for all components instances of that type. This is useful for setting default attributes that are common to all instances of a component, such as `class` or `style`.
|
|
366
|
+
|
|
367
|
+
To specify default attributes for a component, set the `attrs` property on the Generator Function of your component. For example:
|
|
368
|
+
|
|
369
|
+
```javascript
|
|
370
|
+
function* MyComponent() {
|
|
371
|
+
// ...
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
MyComponent.attrs = { class: 'my-class' }
|
|
375
|
+
```
|
|
376
|
+
> This code will instruct Ajo to set the `class` attribute to `my-class` on all instances of `MyComponent`.
|
|
377
|
+
|
|
378
|
+
### `attr:`
|
|
379
|
+
|
|
380
|
+
- **Purpose:** The `attr:` prefix is used in Ajo to explicitly set attributes to the underlying DOM element of a stateful component.
|
|
381
|
+
This prefix distinguishes regular HTML attributes from component arguments, making it easier to identify and manage them.
|
|
382
|
+
|
|
383
|
+
- **Behavior:**
|
|
384
|
+
- When a stateful component is rendered in Ajo, any property on it that starts with `attr:` is treated as a regular HTML attribute and is applied to the component's underlying DOM element.
|
|
385
|
+
- This mechanism ensures that the arguments are clearly identified and separated from the HTML attributes.
|
|
386
|
+
|
|
387
|
+
- **Usage:**
|
|
388
|
+
- Use `attr:` prefixed attributes when you need to pass attributes to a component's underlying DOM element.
|
|
389
|
+
|
|
390
|
+
- **Example:**
|
|
391
|
+
```jsx
|
|
392
|
+
function* ParentComponent() {
|
|
393
|
+
|
|
394
|
+
const someData = { /* ... */ }
|
|
395
|
+
const handleEvent = () => { /* ... */ }
|
|
396
|
+
|
|
397
|
+
yield <ChildComponent
|
|
398
|
+
attr:class="my-class"
|
|
399
|
+
data={someData}
|
|
400
|
+
onEvent={handleEvent}
|
|
401
|
+
/>
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function* ChildComponent({ data, onEvent }) {
|
|
405
|
+
// ...
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
> In this example, `ParentComponent` renders `ChildComponent`, passing `someData` and `handleEvent` as arguments. `attr: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.
|
|
409
|
+
|
|
410
|
+
This `attr:` prefixed attribute system in Ajo enhances the clarity and readability of component composition. It makes the intent of passing DOM attributes more explicit, reducing confusion between function arguments and HTML attributes.
|
|
411
|
+
|
|
412
|
+
### SSR and Hydration
|
|
413
|
+
|
|
414
|
+
In the context of Server-Side Rendering (SSR), this features allows Ajo to gracefully hydrate existing SSR-generated DOM elements. Ajo can extend the functionality of built-in browser DOM elements without relying on Web Components or standards like Declarative Shadow DOM. It provides a streamlined, efficient method for enhancing and manipulating built-in elements, offering a more practical solution compared to the complexities of Web Components.
|
|
415
|
+
|
|
352
416
|
## Lifecycle methods
|
|
353
417
|
|
|
354
418
|
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.
|
|
@@ -523,22 +587,22 @@ function* MultiStepForm({ initialData }) {
|
|
|
523
587
|
switch(currentStep) {
|
|
524
588
|
case 0:
|
|
525
589
|
yield <StepOne
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
590
|
+
data={formData}
|
|
591
|
+
onNext={handleNextStep}
|
|
592
|
+
onRestart={handleRestart}
|
|
529
593
|
/>
|
|
530
594
|
break
|
|
531
595
|
case 1:
|
|
532
596
|
yield <StepTwo
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
597
|
+
data={formData}
|
|
598
|
+
onNext={handleNextStep}
|
|
599
|
+
onRestart={handleRestart}
|
|
536
600
|
/>
|
|
537
601
|
break
|
|
538
602
|
default:
|
|
539
603
|
yield <FinalStep
|
|
540
|
-
|
|
541
|
-
|
|
604
|
+
data={formData}
|
|
605
|
+
onRestart={handleRestart}
|
|
542
606
|
/>
|
|
543
607
|
}
|
|
544
608
|
}
|
|
@@ -546,39 +610,114 @@ function* MultiStepForm({ initialData }) {
|
|
|
546
610
|
```
|
|
547
611
|
> 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.
|
|
548
612
|
|
|
549
|
-
|
|
613
|
+
## Server-Side Rendering (SSR)
|
|
550
614
|
|
|
551
|
-
-
|
|
615
|
+
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.
|
|
552
616
|
|
|
553
|
-
|
|
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.
|
|
555
|
-
- This mechanism ensures that the arguments are clearly identified and separated from other attributes or DOM properties.
|
|
617
|
+
### SSR Implementation with `ajo/html`
|
|
556
618
|
|
|
557
|
-
-
|
|
558
|
-
- Use `arg:` prefixed attributes when you need to pass data or event handlers to a component's generator function.
|
|
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.
|
|
619
|
+
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.
|
|
560
620
|
|
|
561
|
-
-
|
|
562
|
-
```jsx
|
|
563
|
-
function* ParentComponent() {
|
|
621
|
+
### Client-Side Hydration
|
|
564
622
|
|
|
565
|
-
|
|
566
|
-
const handleEvent = () => { /* ... */ }
|
|
623
|
+
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.
|
|
567
624
|
|
|
568
|
-
|
|
569
|
-
class="my-class"
|
|
570
|
-
arg:data={someData}
|
|
571
|
-
arg:onEvent={handleEvent}
|
|
572
|
-
/>
|
|
573
|
-
}
|
|
625
|
+
### Example Usage
|
|
574
626
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
627
|
+
Server-side:
|
|
628
|
+
|
|
629
|
+
```javascript
|
|
630
|
+
import express from 'express'
|
|
631
|
+
import { render } from 'ajo/html'
|
|
632
|
+
import { App } from './components'
|
|
633
|
+
|
|
634
|
+
const app = express()
|
|
635
|
+
|
|
636
|
+
app.get('/', (req, res) => {
|
|
637
|
+
|
|
638
|
+
const html = render(<App />)
|
|
639
|
+
|
|
640
|
+
res.send(`
|
|
641
|
+
<!DOCTYPE html>
|
|
642
|
+
<html>
|
|
643
|
+
<head>
|
|
644
|
+
<title>My App</title>
|
|
645
|
+
</head>
|
|
646
|
+
<body>
|
|
647
|
+
<div id="root">${html}</div>
|
|
648
|
+
</body>
|
|
649
|
+
</html>`)
|
|
650
|
+
})
|
|
651
|
+
|
|
652
|
+
app.listen(3000)
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
Client-side:
|
|
656
|
+
|
|
657
|
+
```javascript
|
|
658
|
+
import { render } from 'ajo'
|
|
659
|
+
import { App } from './components'
|
|
660
|
+
|
|
661
|
+
// Hydrate the #root element with the server-rendered DOM
|
|
662
|
+
render(<App />, document.getElementById('root'))
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
### Streaming HTML Chunks
|
|
666
|
+
|
|
667
|
+
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.
|
|
668
|
+
|
|
669
|
+
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.
|
|
670
|
+
|
|
671
|
+
## SSR API
|
|
672
|
+
|
|
673
|
+
### `render(h: Any): String`
|
|
674
|
+
|
|
675
|
+
Renders a virtual DOM tree (`h`) into a complete HTML string.
|
|
676
|
+
|
|
677
|
+
#### Parameters:
|
|
678
|
+
|
|
679
|
+
- **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.
|
|
680
|
+
|
|
681
|
+
#### Returns:
|
|
682
|
+
|
|
683
|
+
- A string representation of the rendered HTML.
|
|
684
|
+
|
|
685
|
+
#### Example Usage:
|
|
686
|
+
|
|
687
|
+
```javascript
|
|
688
|
+
import { render } from 'ajo/html'
|
|
689
|
+
import { App } from './components'
|
|
690
|
+
|
|
691
|
+
const html = render(<App />)
|
|
692
|
+
// The `html` can be sent as part of an HTTP response
|
|
578
693
|
```
|
|
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.
|
|
580
694
|
|
|
581
|
-
|
|
695
|
+
### `html(h: Any): Generator`
|
|
696
|
+
|
|
697
|
+
A generator function that iterates through a virtual DOM tree, yielding HTML strings.
|
|
698
|
+
|
|
699
|
+
#### Parameters:
|
|
700
|
+
|
|
701
|
+
- **h** (Any): The virtual DOM tree to iterate through.
|
|
702
|
+
|
|
703
|
+
#### Yield:
|
|
704
|
+
|
|
705
|
+
- Yields HTML strings corresponding to each node in the virtual DOM.
|
|
706
|
+
|
|
707
|
+
#### Usage:
|
|
708
|
+
|
|
709
|
+
- Suitable for streaming HTML chunks to the client, compatible with any streaming technology.
|
|
710
|
+
|
|
711
|
+
#### Example Usage:
|
|
712
|
+
|
|
713
|
+
```javascript
|
|
714
|
+
import { html } from 'ajo/html'
|
|
715
|
+
import { App } from './components'
|
|
716
|
+
|
|
717
|
+
for (const chunk of html(<App />)) {
|
|
718
|
+
stream.push(chunk)
|
|
719
|
+
}
|
|
720
|
+
```
|
|
582
721
|
|
|
583
722
|
## Acknowledgments
|
|
584
723
|
Ajo takes heavy inspiration from [Incremental DOM](https://github.com/google/incremental-dom) and [Crank.js](https://github.com/bikeshaving/crank)
|