ajo 0.0.16 → 0.0.18

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.
Files changed (5) hide show
  1. package/index.cjs +151 -132
  2. package/index.js +195 -167
  3. package/index.min.js +1 -1
  4. package/package.json +10 -3
  5. package/readme.md +30 -6
package/index.cjs CHANGED
@@ -17,6 +17,7 @@ var __copyProps = (to, from2, except, desc) => {
17
17
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
18
  var ajo_exports = {};
19
19
  __export(ajo_exports, {
20
+ For: () => For,
20
21
  Fragment: () => Fragment,
21
22
  cleanup: () => cleanup,
22
23
  clx: () => clx,
@@ -32,162 +33,180 @@ __export(ajo_exports, {
32
33
  stx: () => stx
33
34
  });
34
35
  module.exports = __toCommonJS(ajo_exports);
35
- const Fragment = ({ children }) => children, h = (nodeName, props, ...children) => {
36
- children = children.length == 0 ? null : children.length == 1 ? children[0] : children;
37
- return { children, ...props, nodeName };
38
- }, component = (setup) => ({ nodeName, is, key, block, ref: ref2, host, ...props }) => h(is ?? setup.is ?? "div", {
39
- key,
36
+ const Fragment = ({ children }) => children, For = ({ is, each, by, children, ref, ...props }) => h(is ?? "div", {
37
+ ...props,
40
38
  skip: true,
41
- ref: (node) => {
42
- node.$params = { ...setup.props, ...props };
43
- node.$render ??= setup(node.$params, node);
44
- if (!(block != null && every(node.$deps, node.$deps = block))) {
45
- update({ ...setup.host, ...host }, node);
46
- refresh(node);
47
- }
48
- isFn(ref2) && ref2(node);
49
- }
50
- }), render = (h2, host) => {
51
- const nodes = [];
52
- let child = host.firstChild, node;
53
- for (h2 of normalize(h2, host)) {
54
- if (typeof h2 == "string") {
55
- for (node = child; node; node = node.nextSibling)
56
- if (node.nodeType == 3)
57
- break;
58
- node ? node.data !== h2 && (node.data = h2) : node = document.createTextNode(h2);
59
- } else if (h2 instanceof Node) {
39
+ ref: (host) => iterate(host, each, by, children, ref)
40
+ }), h = (nodeName, props, ...children) => {
41
+ const { length } = children;
42
+ children = length == 0 ? null : length == 1 ? children[0] : children;
43
+ return { children, ...props, nodeName };
44
+ }, render = (h2, host, ns) => {
45
+ let child = host.firstChild;
46
+ for (h2 of normalize(h2)) {
47
+ let node = child;
48
+ if (h2 instanceof Node)
60
49
  node = h2;
50
+ else if (typeof h2 == "string") {
51
+ while (node && node.nodeType != 3)
52
+ node = node.nextSibling;
53
+ node ? node.data != h2 && (node.data = h2) : node = document.createTextNode(h2);
61
54
  } else {
62
- const { nodeName, key, skip, block, ref: ref2, children, ...props } = h2;
63
- if (key != null) {
64
- (host.$keyed ??= /* @__PURE__ */ new Map()).set(key, node = host.$keyed.get(key) ?? document.createElement(nodeName));
65
- } else {
66
- for (node = child; node; node = node.nextSibling)
67
- if (node.localName == nodeName)
68
- break;
69
- node ??= document.createElement(nodeName);
70
- }
71
- if (!(skip || block != null && every(node.$deps, node.$deps = block))) {
55
+ const { xmlns = ns, nodeName, key, block, skip, children, ref, ...props } = h2;
56
+ while (node && !(node.localName == nodeName && (node.$key ??= key) == key))
57
+ node = node.nextSibling;
58
+ node ??= create(xmlns, nodeName, key);
59
+ if (block == null || some(node.$deps, node.$deps = block)) {
72
60
  update(props, node);
73
- render(children, node);
61
+ skip || render(children, node, xmlns);
62
+ isFunction(ref) && ref(node);
74
63
  }
75
- isFn(ref2) && ref2(node);
76
64
  }
77
- nodes.push(node) && node === child && (child = child.nextSibling);
65
+ node == child ? child = child.nextSibling : before(host, node, child);
78
66
  }
79
- arrange(host, [...host.childNodes], nodes);
80
- }, refresh = (host) => {
67
+ while (child) {
68
+ const next = child.nextSibling;
69
+ host.removeChild(child);
70
+ child = next;
71
+ }
72
+ }, component = (setup) => ({ is, props, ref, ...params }) => h(is ?? setup.is ?? "div", {
73
+ ...setup.props,
74
+ ...props,
75
+ skip: true,
76
+ ref: (host) => run(host, setup, params, ref)
77
+ }), refresh = (host) => {
81
78
  try {
82
- render(host.$render(host.$params, host), host);
79
+ render((host.$render ??= host.$setup(host))(host.$params), host);
83
80
  } catch (error) {
84
81
  propagate(host, error);
85
82
  }
86
83
  }, provide = (host, key, value) => (host.$provisions ??= /* @__PURE__ */ new Map()).set(key, value), consume = (host, key, fallback) => {
87
- let map;
88
- while (host) {
89
- if ((map = host.$provisions) && map.has(key))
90
- return map.get(key);
91
- host = host.parentNode;
92
- }
84
+ for (let map2; host; host = host.parentNode)
85
+ if ((map2 = host.$provisions) && map2.has(key))
86
+ return map2.get(key);
93
87
  return fallback;
94
- }, intercept = (host, fn) => host.$interceptor = fn, propagate = (host, error) => {
88
+ }, intercept = (host, fn) => isFunction(fn) && (host.$interceptor = fn), propagate = (host, error) => {
95
89
  for (let fn; host; host = host.parentNode)
96
- if (fn = host.$interceptor)
90
+ if (isFunction(fn = host.$interceptor))
97
91
  return render(fn(error), host);
98
92
  throw error;
99
- }, cleanup = (host, fn) => (host.$cleanups ??= /* @__PURE__ */ new Set()).add(fn), clx = (o) => keys(o).filter((k) => o[k]).join(" ") || null, stx = (o) => entries(o).map((t) => t.join(":")).join(";") || null, keb = (o) => keys(o).reduce((r, k) => (r[k.replace(search, replace).toLowerCase()] = o[k], r), {});
100
- const every = (a, b) => a === b || isArray(a) && isArray(b) && a.length == b.length && a.every((v, i) => v === b[i]), apply = (o, { name, value }) => (o[name] = value, o), reduce = (list) => from(list).reduce(apply, {}), ref = (v) => (...args) => args.length ? v = args[0] : v, isFn = (v) => typeof v == "function", { keys, entries } = Object, { isArray, from } = Array, search = /([a-z0-9])([A-Z])/g, replace = "$1-$2", it = Symbol.iterator, normalize = function* (h2, host, buffer = ref(""), root = true) {
101
- let type, text;
102
- for (h2 of isFn(h2?.[it]) ? h2 : [h2]) {
103
- if (h2 == null || (type = typeof h2) == "boolean")
104
- continue;
105
- if (type == "string" || type == "number") {
106
- buffer(buffer() + h2);
107
- continue;
108
- }
109
- if ("nodeName" in Object(h2)) {
110
- if (isFn(h2.nodeName)) {
111
- yield* normalize(h2.nodeName(h2), host, buffer, false);
112
- continue;
113
- }
114
- if (text = buffer()) {
115
- yield text;
116
- buffer("");
117
- }
118
- yield h2;
93
+ }, cleanup = (host, fn) => isFunction(fn) && (host.$cleanups ??= /* @__PURE__ */ new Set()).add(fn), clx = (o) => keys(o).filter((k) => o[k]).join(" ") || null, stx = (o) => entries(o).map((t) => t.join(":")).join(";") || null, keb = (o) => keys(o).reduce((r, k) => (r[k.replace(search, replace).toLowerCase()] = o[k], r), {});
94
+ const { isArray, from } = Array, { keys, entries } = Object, isFunction = (v) => typeof v == "function", noop = () => {
95
+ }, on = (host, type, v) => {
96
+ let fn, map2;
97
+ if (fn = (map2 = host.$on ??= {})[type]) {
98
+ host.removeEventListener(type, fn, fn.options), map2[type] = null;
99
+ }
100
+ if (typeof (fn = (v = isArray(v) ? v : [v])[0]) == "function") {
101
+ host.addEventListener(type, fn = map2[type] = fn.bind(null, v[1]), fn.options = v[2]);
102
+ }
103
+ }, map = (list) => list.reduce(set, /* @__PURE__ */ new Map()), set = (m, v, i) => (m.set(v, i), m), some = (a, b) => isArray(a) && isArray(b) ? a.some((v, i) => v !== b[i]) : a !== b, reduce = (v) => from(v).reduce(assign, {}), assign = (v, { name, value }) => (v[name] = value, v), create = (ns, name, key) => {
104
+ const node = ns ? document.createElementNS(ns, name) : document.createElement(name);
105
+ return node.$key = key, node;
106
+ }, proxy = { firstChild: null, insertBefore: (node) => proxy.firstChild ??= node }, handler = {
107
+ get(target, key) {
108
+ const value = key == "nextSibling" ? null : target[key];
109
+ return isFunction(value) ? value.bind(target) : value;
110
+ }
111
+ }, search = /([a-z0-9])([A-Z])/g, replace = "$1-$2", normalize = function* (h2, buffer = { t: "" }, root = true) {
112
+ let t;
113
+ for (h2 of isArray(h2) ? h2 : [h2]) {
114
+ if (h2 == null || typeof h2 == "boolean")
119
115
  continue;
120
- }
121
- isFn(h2[it]) ? yield* normalize(h2, host, buffer, false) : buffer(buffer() + h2);
116
+ else if (typeof h2.nodeName == "string")
117
+ t = buffer.t && (buffer.t = "", yield t), yield h2;
118
+ else if (isFunction(h2.nodeName))
119
+ yield* normalize(h2.nodeName(h2), buffer, false);
120
+ else
121
+ isArray(h2) ? yield* normalize(h2, buffer, false) : buffer.t += h2;
122
122
  }
123
- if (root && (text = buffer()))
124
- yield text;
123
+ root && (t = buffer.t) && (yield t);
125
124
  }, update = (props, host) => {
126
- const prev = host.$props ?? (host.hasAttributes() ? reduce(host.attributes) : {});
127
- for (const name in { ...prev, ...props }) {
125
+ const prev = host.$props ??= host.hasAttributes() ? reduce(host.attributes) : {};
126
+ for (const name in { ...prev, ...host.$props = props }) {
128
127
  let value = props[name];
129
- if (value === prev[name]) {
130
- continue;
131
- }
132
- if (name.startsWith("set:")) {
133
- host[name.slice(4)] = value;
134
- continue;
135
- }
136
- if (value == null || value === false) {
137
- host.removeAttribute(name);
138
- continue;
128
+ if (name.startsWith("on:")) {
129
+ some(value, prev[name]) && on(host, name.slice(3), value);
130
+ } else if (value !== prev[name])
131
+ if (name.startsWith("set:"))
132
+ host[name.slice(4)] = value;
133
+ else if (value == null || value === false)
134
+ host.removeAttribute(name);
135
+ else
136
+ host.setAttribute(name, value === true ? "" : value);
137
+ }
138
+ }, before = (host, node, child) => {
139
+ if (node.contains?.(document.activeElement)) {
140
+ const ref = node.nextSibling;
141
+ while (child && child != node) {
142
+ const next = child.nextSibling;
143
+ host.insertBefore(child, ref);
144
+ child = next;
139
145
  }
140
- host.setAttribute(name, value === true ? "" : value);
146
+ } else
147
+ host.insertBefore(node, child);
148
+ }, iterate = (host, each, by, fn, ref) => {
149
+ each = isArray(each) ? each : [];
150
+ by = isFunction(by) ? by : (v) => v;
151
+ fn = isFunction(fn) ? fn : noop;
152
+ const map2 = host.$for ??= /* @__PURE__ */ new Map(), del = (node) => map2.delete(node.$by), clr = each !== host.$each, len = (host.$each = each).length, a = from(host.childNodes), b = new Array(len);
153
+ clr && map2.clear();
154
+ for (let child, index = 0; index < len; index++) {
155
+ const item = each[index], key = by(item, index);
156
+ child = clr ? a[index] : map2.get(key);
157
+ proxy.firstChild = child ? new Proxy(child, handler) : null;
158
+ render(fn(item), proxy);
159
+ child ??= proxy.firstChild;
160
+ proxy.firstChild = null;
161
+ map2.set(child.$by = key, b[index] = child);
141
162
  }
142
- host.$props = props;
143
- }, arrange = (host, a, b) => {
144
- const aLength = a.length, bLength = b.length;
163
+ arrange(host, a, b, del);
164
+ isFunction(ref) && ref(host);
165
+ }, arrange = (host, a, b, dispose2 = noop) => {
166
+ const aLen = a.length, bLen = b.length;
145
167
  let aIndex = 0, bIndex = 0, aValue, bValue, aMap, bMap, i;
146
- while (aIndex !== aLength || bIndex !== bLength) {
147
- aValue = a[aIndex], bValue = b[bIndex];
148
- if (aValue === null) {
149
- aIndex++;
150
- continue;
151
- }
152
- if (bLength <= bIndex) {
153
- host.removeChild(aValue).nodeType == 1 && dispose(aValue);
154
- aIndex++;
155
- continue;
156
- }
157
- if (aLength <= aIndex) {
158
- host.insertBefore(bValue, aValue);
159
- bIndex++;
160
- continue;
161
- }
162
- if (aValue === bValue) {
163
- aIndex++;
164
- bIndex++;
165
- continue;
166
- }
167
- if (!aMap) {
168
- aMap = /* @__PURE__ */ new Map();
169
- bMap = /* @__PURE__ */ new Map();
170
- for (i = 0; i < aLength; i++)
171
- aMap.set(a[i], i);
172
- for (i = 0; i < bLength; i++)
173
- bMap.set(b[i], i);
174
- }
175
- if (bMap.get(aValue) == null) {
176
- host.removeChild(aValue).nodeType == 1 && dispose(aValue);
168
+ while (aIndex !== aLen || bIndex !== bLen) {
169
+ aValue = a[aIndex];
170
+ bValue = b[bIndex];
171
+ if (aValue === null)
177
172
  aIndex++;
178
- continue;
179
- }
180
- host.insertBefore(bValue, aValue);
181
- bIndex++;
182
- if ((i = aMap.get(bValue)) != null) {
183
- if (i > aIndex + 1)
184
- aIndex++;
185
- a[i] = null;
173
+ else if (bLen <= bIndex)
174
+ aIndex++, dispose2(host.removeChild(aValue));
175
+ else if (aLen <= aIndex)
176
+ bIndex++, host.appendChild(bValue);
177
+ else if (aValue === bValue)
178
+ aIndex++, bIndex++;
179
+ else {
180
+ aMap ??= map(a);
181
+ bMap ??= map(b);
182
+ if (bMap.get(aValue) == null)
183
+ aIndex++, dispose2(host.removeChild(aValue));
184
+ else {
185
+ host.insertBefore(bValue, aValue), bIndex++;
186
+ if ((i = aMap.get(bValue)) != null) {
187
+ if (i > aIndex + 1)
188
+ aIndex++;
189
+ a[i] = null;
190
+ }
191
+ }
186
192
  }
187
193
  }
188
- }, dispose = (host) => {
189
- for (const child of host.children)
190
- dispose(child);
191
- for (const fn of host.$cleanups ?? [])
192
- fn(host);
194
+ }, run = (host, setup, params, ref) => {
195
+ host.$setup ??= (host.addEventListener("DOMNodeRemovedFromDocument", dispose), isFunction(setup) ? setup : noop);
196
+ host.$params = { ...setup.params, ...params };
197
+ refresh(host);
198
+ isFunction(ref) && ref(host);
199
+ }, dispose = ({ target }) => {
200
+ (globalThis.queueMicrotask ?? ((v) => v()))(() => {
201
+ if (document.contains(target))
202
+ return;
203
+ if ("$cleanups" in target) {
204
+ try {
205
+ for (const fn of target.$cleanups)
206
+ fn(target);
207
+ } finally {
208
+ target.$cleanups.clear();
209
+ }
210
+ }
211
+ });
193
212
  };
package/index.js CHANGED
@@ -1,230 +1,258 @@
1
1
  export const
2
- Fragment = ({ children }) => children,
2
+ Fragment = ({ children }) => children,
3
3
 
4
- h = (nodeName, props, ...children) => {
5
- children = children.length == 0 ? null : children.length == 1 ? children[0] : children
6
- return { children, ...props, nodeName }
7
- },
4
+ For = ({ is, each, by, children, ref, ...props }) => h(is ?? 'div', {
5
+ ...props, skip: true, ref: host => iterate(host, each, by, children, ref)
6
+ }),
8
7
 
9
- component = setup => ({ nodeName, is, key, block, ref, host, ...props }) =>
10
- h(is ?? setup.is ?? 'div', {
11
- key, skip: true, ref: node => {
8
+ h = (nodeName, props, ...children) => {
9
+ const { length } = children
10
+ children = length == 0 ? null : length == 1 ? children[0] : children
11
+ return { children, ...props, nodeName }
12
+ },
12
13
 
13
- node.$params = { ...setup.props, ...props }
14
- node.$render ??= setup(node.$params, node)
14
+ render = (h, host, ns) => {
15
15
 
16
- if (!(block != null && every(node.$deps, node.$deps = block))) {
17
- update({ ...setup.host, ...host }, node)
18
- refresh(node)
19
- }
16
+ let child = host.firstChild
20
17
 
21
- isFn(ref) && ref(node)
22
- }
23
- }),
18
+ for (h of normalize(h)) {
24
19
 
25
- render = (h, host) => {
20
+ let node = child
26
21
 
27
- const nodes = []
28
- let child = host.firstChild, node
22
+ if (h instanceof Node) node = h
29
23
 
30
- for (h of normalize(h, host)) {
24
+ else if (typeof h == 'string') {
31
25
 
32
- if (typeof h == 'string') {
26
+ while (node && node.nodeType != 3) node = node.nextSibling
27
+ node ? node.data != h && (node.data = h) : node = document.createTextNode(h)
33
28
 
34
- for (node = child; node; node = node.nextSibling) if (node.nodeType == 3) break
35
- node ? node.data !== h && (node.data = h) : node = document.createTextNode(h)
29
+ } else {
36
30
 
37
- } else if (h instanceof Node) {
31
+ const { xmlns = ns, nodeName, key, block, skip, children, ref, ...props } = h
38
32
 
39
- node = h
33
+ while (node && !(node.localName == nodeName && (node.$key ??= key) == key)) node = node.nextSibling
34
+ node ??= create(xmlns, nodeName, key)
40
35
 
41
- } else {
36
+ if (block == null || some(node.$deps, node.$deps = block)) {
37
+ update(props, node)
38
+ skip || render(children, node, xmlns)
39
+ isFunction(ref) && ref(node)
40
+ }
41
+ }
42
42
 
43
- const { nodeName, key, skip, block, ref, children, ...props } = h
43
+ node == child ? child = child.nextSibling : before(host, node, child)
44
+ }
44
45
 
45
- if (key != null) {
46
- (host.$keyed ??= new Map).set(key, node = host.$keyed.get(key) ?? document.createElement(nodeName))
47
- } else {
48
- for (node = child; node; node = node.nextSibling) if (node.localName == nodeName) break
49
- node ??= document.createElement(nodeName)
50
- }
46
+ while (child) {
47
+ const next = child.nextSibling
48
+ host.removeChild(child)
49
+ child = next
50
+ }
51
+ },
51
52
 
52
- if (!(skip || block != null && every(node.$deps, node.$deps = block))) {
53
- update(props, node)
54
- render(children, node)
55
- }
53
+ component = setup => ({ is, props, ref, ...params }) => h(is ?? setup.is ?? 'div', {
54
+ ...setup.props, ...props, skip: true, ref: host => run(host, setup, params, ref)
55
+ }),
56
56
 
57
- isFn(ref) && ref(node)
58
- }
57
+ refresh = host => {
58
+ try {
59
+ render((host.$render ??= host.$setup(host))(host.$params), host)
60
+ } catch (error) {
61
+ propagate(host, error)
62
+ }
63
+ },
59
64
 
60
- nodes.push(node) && node === child && (child = child.nextSibling)
61
- }
65
+ provide = (host, key, value) => (host.$provisions ??= new Map).set(key, value),
62
66
 
63
- arrange(host, [...host.childNodes], nodes)
64
- },
67
+ consume = (host, key, fallback) => {
68
+ for (let map; host; host = host.parentNode) if ((map = host.$provisions) && map.has(key)) return map.get(key)
69
+ return fallback
70
+ },
65
71
 
66
- refresh = host => {
67
- try {
68
- render(host.$render(host.$params, host), host)
69
- } catch (error) {
70
- propagate(host, error)
71
- }
72
- },
72
+ intercept = (host, fn) => isFunction(fn) && (host.$interceptor = fn),
73
73
 
74
- provide = (host, key, value) => (host.$provisions ??= new Map).set(key, value),
74
+ propagate = (host, error) => {
75
+ for (let fn; host; host = host.parentNode) if (isFunction(fn = host.$interceptor)) return render(fn(error), host)
76
+ throw error
77
+ },
75
78
 
76
- consume = (host, key, fallback) => {
77
- let map
78
- while (host) {
79
- if ((map = host.$provisions) && map.has(key)) return map.get(key)
80
- host = host.parentNode
81
- }
82
- return fallback
83
- },
79
+ cleanup = (host, fn) => isFunction(fn) && (host.$cleanups ??= new Set).add(fn),
84
80
 
85
- intercept = (host, fn) => host.$interceptor = fn,
81
+ clx = o => keys(o).filter(k => o[k]).join(' ') || null,
86
82
 
87
- propagate = (host, error) => {
88
- for (let fn; host; host = host.parentNode) if (fn = host.$interceptor) return render(fn(error), host)
89
- throw error
90
- },
83
+ stx = o => entries(o).map(t => t.join(':')).join(';') || null,
91
84
 
92
- cleanup = (host, fn) => (host.$cleanups ??= new Set).add(fn),
93
-
94
- clx = o => keys(o).filter(k => o[k]).join(' ') || null,
95
- stx = o => entries(o).map(t => t.join(':')).join(';') || null,
96
- keb = o => keys(o).reduce((r, k) => ((r[k.replace(search, replace).toLowerCase()] = o[k]), r), {})
85
+ keb = o => keys(o).reduce((r, k) => ((r[k.replace(search, replace).toLowerCase()] = o[k]), r), {})
97
86
 
98
87
  const
99
- every = (a, b) => a === b || isArray(a) && isArray(b) && a.length == b.length && a.every((v, i) => v === b[i]),
100
- apply = (o, { name, value }) => ((o[name] = value), o),
101
- reduce = list => from(list).reduce(apply, {}),
102
- ref = v => (...args) => args.length ? v = args[0] : v,
103
- isFn = v => typeof v == 'function',
88
+ { isArray, from } = Array, { keys, entries } = Object,
89
+
90
+ isFunction = v => typeof v == 'function', noop = () => { }, on = (host, type, v) => {
91
+
92
+ let fn, map
93
+
94
+ if (fn = (map = host.$on ??= {})[type]) {
95
+ host.removeEventListener(type, fn, fn.options), map[type] = null
96
+ }
97
+
98
+ if (typeof (fn = (v = isArray(v) ? v : [v])[0]) == 'function') {
99
+ host.addEventListener(type, fn = map[type] = fn.bind(null, v[1]), fn.options = v[2])
100
+ }
101
+ },
102
+
103
+ map = list => list.reduce(set, new Map), set = (m, v, i) => (m.set(v, i), m),
104
+
105
+ some = (a, b) => (isArray(a) && isArray(b)) ? a.some((v, i) => v !== b[i]) : a !== b,
106
+
107
+ reduce = v => from(v).reduce(assign, {}), assign = (v, { name, value }) => ((v[name] = value), v),
108
+
109
+ create = (ns, name, key) => {
110
+ const node = ns ? document.createElementNS(ns, name) : document.createElement(name)
111
+ return node.$key = key, node
112
+ },
113
+
114
+ proxy = { firstChild: null, insertBefore: node => proxy.firstChild ??= node }, handler = {
115
+ get(target, key) {
116
+ const value = key == 'nextSibling' ? null : target[key]
117
+ return isFunction(value) ? value.bind(target) : value
118
+ },
119
+ },
120
+
121
+ search = /([a-z0-9])([A-Z])/g, replace = '$1-$2',
122
+
123
+ normalize = function* (h, buffer = { t: '' }, root = true) {
124
+
125
+ let t
126
+
127
+ for (h of isArray(h) ? h : [h]) {
128
+ if (h == null || typeof h == 'boolean') continue
129
+ else if (typeof h.nodeName == 'string') (t = buffer.t && (buffer.t = '', yield t)), yield h
130
+ else if (isFunction(h.nodeName)) yield* normalize(h.nodeName(h), buffer, false)
131
+ else isArray(h) ? yield* normalize(h, buffer, false) : buffer.t += h
132
+ }
133
+
134
+ root && (t = buffer.t) && (yield t)
135
+ },
104
136
 
105
- { keys, entries } = Object, { isArray, from } = Array,
137
+ update = (props, host) => {
106
138
 
107
- search = /([a-z0-9])([A-Z])/g, replace = '$1-$2', it = Symbol.iterator,
139
+ const prev = host.$props ??= host.hasAttributes() ? reduce(host.attributes) : {}
108
140
 
109
- normalize = function* (h, host, buffer = ref(''), root = true) {
141
+ for (const name in { ...prev, ...(host.$props = props) }) {
110
142
 
111
- let type, text
143
+ let value = props[name]
112
144
 
113
- for (h of isFn(h?.[it]) ? h : [h]) {
145
+ if (name.startsWith('on:')) {
146
+ some(value, prev[name]) && on(host, name.slice(3), value)
147
+ } else if (value !== prev[name])
148
+ if (name.startsWith('set:')) host[name.slice(4)] = value
149
+ else if (value == null || value === false) host.removeAttribute(name)
150
+ else host.setAttribute(name, value === true ? '' : value)
151
+ }
152
+ },
114
153
 
115
- if (h == null || (type = typeof h) == 'boolean') continue
154
+ before = (host, node, child) => {
155
+ if (node.contains?.(document.activeElement)) {
116
156
 
117
- if (type == 'string' || type == 'number') {
118
- buffer(buffer() + h)
119
- continue
120
- }
157
+ const ref = node.nextSibling
121
158
 
122
- if ('nodeName' in Object(h)) {
159
+ while (child && child != node) {
160
+ const next = child.nextSibling
161
+ host.insertBefore(child, ref)
162
+ child = next
163
+ }
123
164
 
124
- if (isFn(h.nodeName)) {
125
- yield* normalize(h.nodeName(h), host, buffer, false)
126
- continue
127
- }
165
+ } else host.insertBefore(node, child)
166
+ },
128
167
 
129
- if (text = buffer()) {
130
- yield text
131
- buffer('')
132
- }
168
+ iterate = (host, each, by, fn, ref) => {
133
169
 
134
- yield h
135
- continue
136
- }
170
+ each = isArray(each) ? each : []
171
+ by = isFunction(by) ? by : v => v
172
+ fn = isFunction(fn) ? fn : noop
137
173
 
138
- isFn(h[it]) ? yield* normalize(h, host, buffer, false) : buffer(buffer() + h)
139
- }
174
+ const
175
+ map = host.$for ??= new Map,
176
+ del = node => map.delete(node.$by),
177
+ clr = each !== host.$each,
178
+ len = (host.$each = each).length,
179
+ a = from(host.childNodes),
180
+ b = new Array(len)
140
181
 
141
- if (root && (text = buffer())) yield text
142
- },
182
+ clr && map.clear()
143
183
 
144
- update = (props, host) => {
184
+ for (let child, index = 0; index < len; index++) {
145
185
 
146
- const prev = host.$props ?? (host.hasAttributes() ? reduce(host.attributes) : {})
186
+ const item = each[index], key = by(item, index)
147
187
 
148
- for (const name in { ...prev, ...props }) {
188
+ child = (clr ? a[index] : map.get(key))
149
189
 
150
- let value = props[name]
190
+ proxy.firstChild = child ? new Proxy(child, handler) : null
191
+ render(fn(item), proxy)
151
192
 
152
- if (value === prev[name]) {
153
- continue
154
- }
193
+ child ??= proxy.firstChild
194
+ proxy.firstChild = null
155
195
 
156
- if (name.startsWith('set:')) {
157
- host[name.slice(4)] = value
158
- continue
159
- }
196
+ map.set(child.$by = key, b[index] = child)
197
+ }
160
198
 
161
- if (value == null || value === false) {
162
- host.removeAttribute(name)
163
- continue
164
- }
199
+ arrange(host, a, b, del)
200
+ isFunction(ref) && ref(host)
201
+ },
165
202
 
166
- host.setAttribute(name, value === true ? '' : value)
167
- }
203
+ arrange = (host, a, b, dispose = noop) => {
168
204
 
169
- host.$props = props
170
- },
205
+ const aLen = a.length, bLen = b.length
171
206
 
172
- arrange = (host, a, b) => {
207
+ let aIndex = 0, bIndex = 0, aValue, bValue, aMap, bMap, i
173
208
 
174
- const aLength = a.length, bLength = b.length
175
- let aIndex = 0, bIndex = 0, aValue, bValue, aMap, bMap, i
209
+ while (aIndex !== aLen || bIndex !== bLen) {
176
210
 
177
- while (aIndex !== aLength || bIndex !== bLength) {
211
+ aValue = a[aIndex]
212
+ bValue = b[bIndex]
178
213
 
179
- aValue = a[aIndex], bValue = b[bIndex]
214
+ if (aValue === null) aIndex++
215
+ else if (bLen <= bIndex) aIndex++, dispose(host.removeChild(aValue))
216
+ else if (aLen <= aIndex) bIndex++, host.appendChild(bValue)
217
+ else if (aValue === bValue) aIndex++, bIndex++
218
+ else {
180
219
 
181
- if (aValue === null) {
182
- aIndex++
183
- continue
184
- }
220
+ aMap ??= map(a)
221
+ bMap ??= map(b)
185
222
 
186
- if (bLength <= bIndex) {
187
- host.removeChild(aValue).nodeType == 1 && dispose(aValue)
188
- aIndex++
189
- continue
190
- }
223
+ if (bMap.get(aValue) == null) aIndex++, dispose(host.removeChild(aValue))
224
+ else {
191
225
 
192
- if (aLength <= aIndex) {
193
- host.insertBefore(bValue, aValue)
194
- bIndex++
195
- continue
196
- }
226
+ host.insertBefore(bValue, aValue), bIndex++
197
227
 
198
- if (aValue === bValue) {
199
- aIndex++
200
- bIndex++
201
- continue
202
- }
228
+ if ((i = aMap.get(bValue)) != null) {
229
+ if (i > aIndex + 1) aIndex++
230
+ a[i] = null
231
+ }
232
+ }
233
+ }
234
+ }
235
+ },
203
236
 
204
- if (!aMap) {
205
- aMap = new Map()
206
- bMap = new Map()
207
- for (i = 0; i < aLength; i++) aMap.set(a[i], i)
208
- for (i = 0; i < bLength; i++) bMap.set(b[i], i)
209
- }
237
+ run = (host, setup, params, ref) => {
210
238
 
211
- if (bMap.get(aValue) == null) {
212
- host.removeChild(aValue).nodeType == 1 && dispose(aValue)
213
- aIndex++
214
- continue
215
- }
239
+ host.$setup ??= (host.addEventListener('DOMNodeRemovedFromDocument', dispose), isFunction(setup) ? setup : noop)
240
+ host.$params = { ...setup.params, ...params }
216
241
 
217
- host.insertBefore(bValue, aValue)
218
- bIndex++
242
+ refresh(host)
243
+ isFunction(ref) && ref(host)
244
+ },
219
245
 
220
- if ((i = aMap.get(bValue)) != null) {
221
- if (i > aIndex + 1) aIndex++
222
- a[i] = null
223
- }
224
- }
225
- },
246
+ dispose = ({ target }) => {
247
+ (globalThis.queueMicrotask ?? (v => v()))(() => {
248
+ if (document.contains(target)) return
226
249
 
227
- dispose = host => {
228
- for (const child of host.children) dispose(child)
229
- for (const fn of host.$cleanups ?? []) fn(host)
230
- }
250
+ if ('$cleanups' in target) {
251
+ try {
252
+ for (const fn of target.$cleanups) fn(target)
253
+ } finally {
254
+ target.$cleanups.clear()
255
+ }
256
+ }
257
+ })
258
+ }
package/index.min.js CHANGED
@@ -1 +1 @@
1
- var ajo=(()=>{var m=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var S=Object.prototype.hasOwnProperty;var C=(e,n)=>{for(var t in n)m(e,t,{get:n[t],enumerable:!0})},T=(e,n,t,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of j(n))!S.call(e,i)&&i!==t&&m(e,i,{get:()=>n[i],enumerable:!(r=M(n,i))||r.enumerable});return e};var L=e=>T(m({},"__esModule",{value:!0}),e);var R={};C(R,{Fragment:()=>z,cleanup:()=>O,clx:()=>V,component:()=>B,consume:()=>F,h:()=>v,intercept:()=>I,keb:()=>Z,propagate:()=>w,provide:()=>E,refresh:()=>x,render:()=>s,stx:()=>W});const z=({children:e})=>e,v=(e,n,...t)=>(t=t.length==0?null:t.length==1?t[0]:t,{children:t,...n,nodeName:e}),B=e=>({nodeName:n,is:t,key:r,block:i,ref:l,host:p,...c})=>v(t??e.is??"div",{key:r,skip:!0,ref:a=>{a.$params={...e.props,...c},a.$render??=e(a.$params,a),i!=null&&k(a.$deps,a.$deps=i)||(A({...e.host,...p},a),x(a)),d(l)&&l(a)}}),s=(e,n)=>{const t=[];let r=n.firstChild,i;for(e of y(e,n)){if(typeof e=="string"){for(i=r;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:l,key:p,skip:c,block:a,ref:f,children:u,...o}=e;if(p!=null)(n.$keyed??=new Map).set(p,i=n.$keyed.get(p)??document.createElement(l));else{for(i=r;i&&i.localName!=l;i=i.nextSibling);i??=document.createElement(l)}c||a!=null&&k(i.$deps,i.$deps=a)||(A(o,i),s(u,i)),d(f)&&f(i)}t.push(i)&&i===r&&(r=r.nextSibling)}Q(n,[...n.childNodes],t)},x=e=>{try{s(e.$render(e.$params,e),e)}catch(n){w(e,n)}},E=(e,n,t)=>(e.$provisions??=new Map).set(n,t),F=(e,n,t)=>{let r;for(;e;){if((r=e.$provisions)&&r.has(n))return r.get(n);e=e.parentNode}return t},I=(e,n)=>e.$interceptor=n,w=(e,n)=>{for(let t;e;e=e.parentNode)if(t=e.$interceptor)return s(t(n),e);throw n},O=(e,n)=>(e.$cleanups??=new Set).add(n),V=e=>b(e).filter(n=>e[n]).join(" ")||null,W=e=>H(e).map(n=>n.join(":")).join(";")||null,Z=e=>b(e).reduce((n,t)=>(n[t.replace(K,P).toLowerCase()]=e[t],n),{}),k=(e,n)=>e===n||$(e)&&$(n)&&e.length==n.length&&e.every((t,r)=>t===n[r]),q=(e,{name:n,value:t})=>(e[n]=t,e),D=e=>J(e).reduce(q,{}),G=e=>(...n)=>n.length?e=n[0]:e,d=e=>typeof e=="function",{keys:b,entries:H}=Object,{isArray:$,from:J}=Array,K=/([a-z0-9])([A-Z])/g,P="$1-$2",N=Symbol.iterator,y=function*(e,n,t=G(""),r=!0){let i,l;for(e of d(e?.[N])?e:[e])if(!(e==null||(i=typeof e)=="boolean")){if(i=="string"||i=="number"){t(t()+e);continue}if("nodeName"in Object(e)){if(d(e.nodeName)){yield*y(e.nodeName(e),n,t,!1);continue}(l=t())&&(yield l,t("")),yield e;continue}d(e[N])?yield*y(e,n,t,!1):t(t()+e)}r&&(l=t())&&(yield l)},A=(e,n)=>{const t=n.$props??(n.hasAttributes()?D(n.attributes):{});for(const r in{...t,...e}){let i=e[r];if(i!==t[r]){if(r.startsWith("set:")){n[r.slice(4)]=i;continue}if(i==null||i===!1){n.removeAttribute(r);continue}n.setAttribute(r,i===!0?"":i)}}n.$props=e},Q=(e,n,t)=>{const r=n.length,i=t.length;let l=0,p=0,c,a,f,u,o;for(;l!==r||p!==i;){if(c=n[l],a=t[p],c===null){l++;continue}if(i<=p){e.removeChild(c).nodeType==1&&g(c),l++;continue}if(r<=l){e.insertBefore(a,c),p++;continue}if(c===a){l++,p++;continue}if(!f){for(f=new Map,u=new Map,o=0;o<r;o++)f.set(n[o],o);for(o=0;o<i;o++)u.set(t[o],o)}if(u.get(c)==null){e.removeChild(c).nodeType==1&&g(c),l++;continue}e.insertBefore(a,c),p++,(o=f.get(a))!=null&&(o>l+1&&l++,n[o]=null)}},g=e=>{for(const n of e.children)g(n);for(const n of e.$cleanups??[])n(e)};return L(R);})();
1
+ var ajo=(()=>{var x=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var B=Object.prototype.hasOwnProperty;var F=(e,n)=>{for(var i in n)x(e,i,{get:n[i],enumerable:!0})},T=(e,n,i,l)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of j(n))!B.call(e,r)&&r!==i&&x(e,r,{get:()=>n[r],enumerable:!(l=L(n,r))||l.enumerable});return e};var z=e=>T(x({},"__esModule",{value:!0}),e);var ae={};F(ae,{For:()=>I,Fragment:()=>D,cleanup:()=>P,clx:()=>R,component:()=>O,consume:()=>W,h:()=>b,intercept:()=>q,keb:()=>G,propagate:()=>k,provide:()=>V,refresh:()=>S,render:()=>g,stx:()=>Z});const D=({children:e})=>e,I=({is:e,each:n,by:i,children:l,ref:r,...o})=>b(e??"div",{...o,skip:!0,ref:t=>ie(t,n,i,l,r)}),b=(e,n,...i)=>{const{length:l}=i;return i=l==0?null:l==1?i[0]:i,{children:i,...n,nodeName:e}},g=(e,n,i)=>{let l=n.firstChild;for(e of y(e)){let r=l;if(e instanceof Node)r=e;else if(typeof e=="string"){for(;r&&r.nodeType!=3;)r=r.nextSibling;r?r.data!=e&&(r.data=e):r=document.createTextNode(e)}else{const{xmlns:o=i,nodeName:t,key:s,block:c,skip:f,children:p,ref:a,...d}=e;for(;r&&!(r.localName==t&&(r.$key??=s)==s);)r=r.nextSibling;r??=X(o,t,s),(c==null||E(r.$deps,r.$deps=c))&&(ee(d,r),f||g(p,r,o),u(a)&&a(r))}r==l?l=l.nextSibling:ne(n,r,l)}for(;l;){const r=l.nextSibling;n.removeChild(l),l=r}},O=e=>({is:n,props:i,ref:l,...r})=>b(n??e.is??"div",{...e.props,...i,skip:!0,ref:o=>re(o,e,r,l)}),S=e=>{try{g((e.$render??=e.$setup(e))(e.$params),e)}catch(n){k(e,n)}},V=(e,n,i)=>(e.$provisions??=new Map).set(n,i),W=(e,n,i)=>{for(let l;e;e=e.parentNode)if((l=e.$provisions)&&l.has(n))return l.get(n);return i},q=(e,n)=>u(n)&&(e.$interceptor=n),k=(e,n)=>{for(let i;e;e=e.parentNode)if(u(i=e.$interceptor))return g(i(n),e);throw n},P=(e,n)=>u(n)&&(e.$cleanups??=new Set).add(n),R=e=>M(e).filter(n=>e[n]).join(" ")||null,Z=e=>H(e).map(n=>n.join(":")).join(";")||null,G=e=>M(e).reduce((n,i)=>(n[i.replace(_,h).toLowerCase()]=e[i],n),{}),{isArray:m,from:A}=Array,{keys:M,entries:H}=Object,u=e=>typeof e=="function",w=()=>{},J=(e,n,i)=>{let l,r;(l=(r=e.$on??={})[n])&&(e.removeEventListener(n,l,l.options),r[n]=null),typeof(l=(i=m(i)?i:[i])[0])=="function"&&e.addEventListener(n,l=r[n]=l.bind(null,i[1]),l.options=i[2])},C=e=>e.reduce(K,new Map),K=(e,n,i)=>(e.set(n,i),e),E=(e,n)=>m(e)&&m(n)?e.some((i,l)=>i!==n[l]):e!==n,Q=e=>A(e).reduce(U,{}),U=(e,{name:n,value:i})=>(e[n]=i,e),X=(e,n,i)=>{const l=e?document.createElementNS(e,n):document.createElement(n);return l.$key=i,l},$={firstChild:null,insertBefore:e=>$.firstChild??=e},Y={get(e,n){const i=n=="nextSibling"?null:e[n];return u(i)?i.bind(e):i}},_=/([a-z0-9])([A-Z])/g,h="$1-$2",y=function*(e,n={t:""},i=!0){let l;for(e of m(e)?e:[e])e==null||typeof e=="boolean"||(typeof e.nodeName=="string"?(l=n.t&&(n.t="",yield l),yield e):u(e.nodeName)?yield*y(e.nodeName(e),n,!1):m(e)?yield*y(e,n,!1):n.t+=e);i&&(l=n.t)&&(yield l)},ee=(e,n)=>{const i=n.$props??=n.hasAttributes()?Q(n.attributes):{};for(const l in{...i,...n.$props=e}){let r=e[l];l.startsWith("on:")?E(r,i[l])&&J(n,l.slice(3),r):r!==i[l]&&(l.startsWith("set:")?n[l.slice(4)]=r:r==null||r===!1?n.removeAttribute(l):n.setAttribute(l,r===!0?"":r))}},ne=(e,n,i)=>{if(n.contains?.(document.activeElement)){const l=n.nextSibling;for(;i&&i!=n;){const r=i.nextSibling;e.insertBefore(i,l),i=r}}else e.insertBefore(n,i)},ie=(e,n,i,l,r)=>{n=m(n)?n:[],i=u(i)?i:a=>a,l=u(l)?l:w;const o=e.$for??=new Map,t=a=>o.delete(a.$by),s=n!==e.$each,c=(e.$each=n).length,f=A(e.childNodes),p=new Array(c);s&&o.clear();for(let a,d=0;d<c;d++){const v=n[d],N=i(v,d);a=s?f[d]:o.get(N),$.firstChild=a?new Proxy(a,Y):null,g(l(v),$),a??=$.firstChild,$.firstChild=null,o.set(a.$by=N,p[d]=a)}le(e,f,p,t),u(r)&&r(e)},le=(e,n,i,l=w)=>{const r=n.length,o=i.length;let t=0,s=0,c,f,p,a,d;for(;t!==r||s!==o;)c=n[t],f=i[s],c===null?t++:o<=s?(t++,l(e.removeChild(c))):r<=t?(s++,e.appendChild(f)):c===f?(t++,s++):(p??=C(n),a??=C(i),a.get(c)==null?(t++,l(e.removeChild(c))):(e.insertBefore(f,c),s++,(d=p.get(f))!=null&&(d>t+1&&t++,n[d]=null)))},re=(e,n,i,l)=>{e.$setup??=(e.addEventListener("DOMNodeRemovedFromDocument",te),u(n)?n:w),e.$params={...n.params,...i},S(e),u(l)&&l(e)},te=({target:e})=>{(globalThis.queueMicrotask??(n=>n()))(()=>{if(!document.contains(e)&&"$cleanups"in e)try{for(const n of e.$cleanups)n(e)}finally{e.$cleanups.clear()}})};return z(ae);})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ajo",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "ajo is a JavaScript view library for building user interfaces",
5
5
  "type": "module",
6
6
  "module": "index.js",
@@ -17,6 +17,13 @@
17
17
  "index.js",
18
18
  "index.min.js"
19
19
  ],
20
+ "keywords": [
21
+ "ui",
22
+ "frontend",
23
+ "web",
24
+ "dom",
25
+ "jsx"
26
+ ],
20
27
  "scripts": {
21
28
  "build": "npm run build:require && npm run build:browser",
22
29
  "build:require": "esbuild --format=cjs --out-extension:.js=.cjs --outdir=. index.js",
@@ -30,8 +37,8 @@
30
37
  "bugs": "https://github.com/cristianfalcone/ajo/issues",
31
38
  "homepage": "https://github.com/cristianfalcone/ajo#readme",
32
39
  "devDependencies": {
33
- "backdom": "^0.0.5",
34
- "esbuild": "^0.15.2",
40
+ "backdom": "^0.0.8",
41
+ "esbuild": "^0.15.7",
35
42
  "uvu": "^0.5.6"
36
43
  }
37
44
  }
package/readme.md CHANGED
@@ -35,22 +35,46 @@ render(<Greet name="World" />, document.body)
35
35
  /** @jsx h */
36
36
  import { h, component, refresh, render } from 'ajo'
37
37
 
38
- const Counter = component(({ start = 0 }, host) => {
39
-
40
- let count = start
41
-
38
+ const Counter = component(host => {
39
+
40
+ let count = 0
41
+
42
42
  const increment = () => {
43
43
  count++
44
44
  refresh(host)
45
45
  }
46
-
46
+
47
47
  return () =>
48
48
  <button set:onclick={increment}>
49
49
  Current: {count}
50
50
  </button>
51
51
  })
52
52
 
53
- render(<Counter start={1} />, document.body)
53
+ render(<Counter />, document.body)
54
+ ```
55
+
56
+ ## rendering lists
57
+
58
+ ```jsx
59
+ /** @jsx h */
60
+ import { h, render, component, For } from 'ajo'
61
+
62
+ const products = [
63
+ { title: 'Cabbage', isFruit: false, id: 1 },
64
+ { title: 'Garlic', isFruit: false, id: 2 },
65
+ { title: 'Apple', isFruit: true, id: 3 },
66
+ ];
67
+
68
+ const ShoppingList = component(() => {
69
+ const listItem = product =>
70
+ <li style={stx({ color: product.isFruit ? 'magenta' : 'darkgreen' })}>
71
+ {product.title}
72
+ </li>
73
+
74
+ return ({ products }) => <For each={products} is="ul">{listItem}</For>
75
+ })
76
+
77
+ render(<ShoppingList producst={products} />, document.body)
54
78
  ```
55
79
 
56
80
  ## acknowledgments