ajo 0.0.16 → 0.0.17

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 +123 -128
  2. package/index.js +167 -167
  3. package/index.min.js +1 -1
  4. package/package.json +13 -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,156 @@ __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
+ var import_element_notifier = require("element-notifier");
37
+ const Fragment = ({ children }) => children, For = ({ is, each, by, children, ref: ref2, ...props }) => h(is ?? "div", {
38
+ ...props,
40
39
  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 = [];
40
+ ref: (host, root) => iterate(host, root, each, by, children, ref2)
41
+ }), h = (nodeName, props, ...children) => ({ children: children.length == 0 ? null : children.length == 1 ? children[0] : children, ...props, nodeName }), render = (h2, host, to, root = host) => {
52
42
  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)
43
+ root == host && (root.$mo ??= (0, import_element_notifier.notify)((host2, connected) => connected || root.contains(host2) || dispose(host2), root));
44
+ for (h2 of normalize(h2)) {
45
+ if (h2 instanceof Node)
46
+ node = h2;
47
+ else if (typeof h2 == "string") {
48
+ for (node = child; node != to; node = node.nextSibling)
56
49
  if (node.nodeType == 3)
57
50
  break;
58
- node ? node.data !== h2 && (node.data = h2) : node = document.createTextNode(h2);
59
- } else if (h2 instanceof Node) {
60
- node = h2;
51
+ node == to ? node = document.createTextNode(h2) : node.data != h2 && (node.data = h2);
61
52
  } 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))) {
53
+ const { nodeName, block, skip, children, ref: ref2, ...props } = h2;
54
+ for (node = child; node != to; node = node.nextSibling)
55
+ if (node.localName == nodeName)
56
+ break;
57
+ node == to && (node = document.createElement(nodeName));
58
+ if (block == null || some(node.$deps, node.$deps = block)) {
72
59
  update(props, node);
73
- render(children, node);
60
+ skip || render(children, node, null, root);
61
+ isFunction(ref2) && ref2(node, root);
74
62
  }
75
- isFn(ref2) && ref2(node);
76
63
  }
77
- nodes.push(node) && node === child && (child = child.nextSibling);
64
+ node == child ? child = child.nextSibling : before(host, node, child);
65
+ }
66
+ while (child != to) {
67
+ const next = child.nextSibling;
68
+ host.removeChild(child);
69
+ child = next;
78
70
  }
79
- arrange(host, [...host.childNodes], nodes);
80
- }, refresh = (host) => {
71
+ }, component = (setup) => ({ is, props, ref: ref2, ...params }) => h(is ?? setup.is ?? "div", {
72
+ ...setup.props,
73
+ ...props,
74
+ skip: true,
75
+ ref: (host, root) => run(host, root, setup, params, ref2)
76
+ }), refresh = (host) => {
81
77
  try {
82
- render(host.$render(host.$params, host), host);
78
+ render((host.$render ??= host.$setup(host))(host.$params), host, null, host.$root);
83
79
  } catch (error) {
84
80
  propagate(host, error);
85
81
  }
86
82
  }, 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
- }
83
+ for (let map2; host; host = host.parentNode)
84
+ if ((map2 = host.$provisions) && map2.has(key))
85
+ return map2.get(key);
93
86
  return fallback;
94
- }, intercept = (host, fn) => host.$interceptor = fn, propagate = (host, error) => {
87
+ }, intercept = (host, fn) => isFunction(fn) && (host.$interceptor = fn), propagate = (host, error) => {
95
88
  for (let fn; host; host = host.parentNode)
96
- if (fn = host.$interceptor)
97
- return render(fn(error), host);
89
+ if (isFunction(fn = host.$interceptor))
90
+ return render(fn(error));
98
91
  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);
92
+ }, 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), {});
93
+ const { isArray, from } = Array, { keys, entries } = Object, isFunction = (v) => typeof v == "function", noop = () => {
94
+ }, ref = (v) => (...args) => args.length ? v = args[0] : v, 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), proxy = { firstChild: null, insertBefore: (node) => proxy.firstChild ??= node }, search = /([a-z0-9])([A-Z])/g, replace = "$1-$2", normalize = function* (h2, buffer = ref(""), root = true) {
95
+ let text;
96
+ for (h2 of isArray(h2) ? h2 : [h2]) {
97
+ if (h2 == null || typeof h2 == "boolean")
107
98
  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;
119
- continue;
120
- }
121
- isFn(h2[it]) ? yield* normalize(h2, host, buffer, false) : buffer(buffer() + h2);
99
+ if (typeof h2.nodeName == "string")
100
+ (text = buffer()) && (buffer(""), yield text), yield h2;
101
+ else if (isFunction(h2.nodeName))
102
+ yield* normalize(h2.nodeName(h2), buffer, false);
103
+ else
104
+ isArray(h2) ? yield* normalize(h2, buffer, false) : buffer(buffer() + h2);
122
105
  }
123
- if (root && (text = buffer()))
124
- yield text;
106
+ root && (text = buffer()) && (yield text);
125
107
  }, update = (props, host) => {
126
108
  const prev = host.$props ?? (host.hasAttributes() ? reduce(host.attributes) : {});
127
109
  for (const name in { ...prev, ...props }) {
128
110
  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;
139
- }
140
- host.setAttribute(name, value === true ? "" : value);
111
+ if (value !== prev[name])
112
+ if (name.startsWith("set:"))
113
+ host[name.slice(4)] = value;
114
+ else if (value == null || value === false)
115
+ host.removeAttribute(name);
116
+ else
117
+ host.setAttribute(name, value === true ? "" : value);
141
118
  }
142
119
  host.$props = props;
143
- }, arrange = (host, a, b) => {
144
- const aLength = a.length, bLength = b.length;
145
- 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;
120
+ }, before = (host, node, child) => {
121
+ if (node.contains?.(document.activeElement)) {
122
+ const ref2 = node.nextSibling;
123
+ while (child && child !== node) {
124
+ const next = child.nextSibling;
125
+ host.insertBefore(child, ref2);
126
+ child = next;
156
127
  }
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);
128
+ } else
129
+ host.insertBefore(node, child);
130
+ }, iterate = (host, root, each, by, fn, ref2) => {
131
+ each = isArray(each) ? each : [];
132
+ by = isFunction(by) ? by : (v) => v;
133
+ fn = isFunction(fn) ? fn : noop;
134
+ const map2 = host.$for ??= /* @__PURE__ */ new Map(), del = (node) => map2.delete(node.$key), clr = each !== host.$each, len = (host.$each = each).length, a = from(host.childNodes), b = new Array(len);
135
+ clr && map2.clear();
136
+ for (let last, index = 0; index < len; index++) {
137
+ const item = each[index], key = by(item, index);
138
+ proxy.firstChild = (clr ? a[index] : map2.get(key)) ?? last?.cloneNode(true);
139
+ render(fn(item), proxy, proxy.firstChild?.nextSibling, root);
140
+ last = proxy.firstChild;
141
+ proxy.firstChild = null;
142
+ map2.set(last.$key = key, b[index] = last);
143
+ }
144
+ arrange(host, a, b, del);
145
+ isFunction(ref2) && ref2(host, root);
146
+ }, arrange = (host, a, b, dispose2 = noop) => {
147
+ const aLen = a.length, bLen = b.length;
148
+ let aIndex = 0, bIndex = 0, aValue, bValue, aMap, bMap, i;
149
+ while (aIndex !== aLen || bIndex !== bLen) {
150
+ aValue = a[aIndex];
151
+ bValue = b[bIndex];
152
+ if (aValue === null)
177
153
  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;
154
+ else if (bLen <= bIndex)
155
+ aIndex++, dispose2(host.removeChild(aValue));
156
+ else if (aLen <= aIndex)
157
+ bIndex++, host.appendChild(bValue);
158
+ else if (aValue === bValue)
159
+ aIndex++, bIndex++;
160
+ else {
161
+ aMap ??= map(a);
162
+ bMap ??= map(b);
163
+ if (bMap.get(aValue) == null)
164
+ aIndex++, dispose2(host.removeChild(aValue));
165
+ else {
166
+ host.insertBefore(bValue, aValue), bIndex++;
167
+ if ((i = aMap.get(bValue)) != null) {
168
+ if (i > aIndex + 1)
169
+ aIndex++;
170
+ a[i] = null;
171
+ }
172
+ }
186
173
  }
187
174
  }
175
+ }, run = (host, root, setup, params, ref2) => {
176
+ host.$root = root;
177
+ host.$setup ??= isFunction(setup) ? setup : noop;
178
+ host.$params = { ...setup.params, ...params };
179
+ refresh(host);
180
+ isFunction(ref2) && ref2(host, root);
188
181
  }, dispose = (host) => {
189
- for (const child of host.children)
190
- dispose(child);
191
- for (const fn of host.$cleanups ?? [])
192
- fn(host);
182
+ const { $cleanups } = host;
183
+ if ($cleanups) {
184
+ for (let fn of $cleanups)
185
+ fn(host);
186
+ $cleanups.clear();
187
+ }
193
188
  };
package/index.js CHANGED
@@ -1,230 +1,230 @@
1
- export const
2
- Fragment = ({ children }) => children,
1
+ import { notify } from 'element-notifier'
3
2
 
4
- h = (nodeName, props, ...children) => {
5
- children = children.length == 0 ? null : children.length == 1 ? children[0] : children
6
- return { children, ...props, nodeName }
7
- },
3
+ export const
4
+ Fragment = ({ children }) => children,
8
5
 
9
- component = setup => ({ nodeName, is, key, block, ref, host, ...props }) =>
10
- h(is ?? setup.is ?? 'div', {
11
- key, skip: true, ref: node => {
6
+ For = ({ is, each, by, children, ref, ...props }) => h(is ?? 'div', {
7
+ ...props, skip: true, ref: (host, root) => iterate(host, root, each, by, children, ref)
8
+ }),
12
9
 
13
- node.$params = { ...setup.props, ...props }
14
- node.$render ??= setup(node.$params, node)
10
+ h = (nodeName, props, ...children) => ({ children: children.length == 0 ? null : children.length == 1 ? children[0] : children, ...props, nodeName }),
15
11
 
16
- if (!(block != null && every(node.$deps, node.$deps = block))) {
17
- update({ ...setup.host, ...host }, node)
18
- refresh(node)
19
- }
12
+ render = (h, host, to, root = host) => {
20
13
 
21
- isFn(ref) && ref(node)
22
- }
23
- }),
14
+ let child = host.firstChild, node
24
15
 
25
- render = (h, host) => {
16
+ root == host && (root.$mo ??= notify((host, connected) => connected || root.contains(host) || dispose(host), root))
26
17
 
27
- const nodes = []
28
- let child = host.firstChild, node
18
+ for (h of normalize(h)) {
29
19
 
30
- for (h of normalize(h, host)) {
20
+ if (h instanceof Node) node = h
31
21
 
32
- if (typeof h == 'string') {
22
+ else if (typeof h == 'string') {
33
23
 
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)
24
+ for (node = child; node != to; node = node.nextSibling) if (node.nodeType == 3) break
25
+ node == to ? node = document.createTextNode(h) : node.data != h && (node.data = h)
36
26
 
37
- } else if (h instanceof Node) {
27
+ } else {
38
28
 
39
- node = h
29
+ const { nodeName, block, skip, children, ref, ...props } = h
40
30
 
41
- } else {
31
+ for (node = child; node != to; node = node.nextSibling) if (node.localName == nodeName) break
32
+ node == to && (node = document.createElement(nodeName))
42
33
 
43
- const { nodeName, key, skip, block, ref, children, ...props } = h
34
+ if (block == null || some(node.$deps, node.$deps = block)) {
35
+ update(props, node)
36
+ skip || render(children, node, null, root)
37
+ isFunction(ref) && ref(node, root)
38
+ }
39
+ }
44
40
 
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
- }
41
+ node == child ? child = child.nextSibling : before(host, node, child)
42
+ }
51
43
 
52
- if (!(skip || block != null && every(node.$deps, node.$deps = block))) {
53
- update(props, node)
54
- render(children, node)
55
- }
44
+ while (child != to) {
45
+ const next = child.nextSibling
46
+ host.removeChild(child)
47
+ child = next
48
+ }
49
+ },
56
50
 
57
- isFn(ref) && ref(node)
58
- }
51
+ component = setup => ({ is, props, ref, ...params }) => h(is ?? setup.is ?? 'div', {
52
+ ...setup.props, ...props, skip: true, ref: (host, root) => run(host, root, setup, params, ref)
53
+ }),
59
54
 
60
- nodes.push(node) && node === child && (child = child.nextSibling)
61
- }
55
+ refresh = host => {
56
+ try {
57
+ render((host.$render ??= host.$setup(host))(host.$params), host, null, host.$root)
58
+ } catch (error) {
59
+ propagate(host, error)
60
+ }
61
+ },
62
62
 
63
- arrange(host, [...host.childNodes], nodes)
64
- },
63
+ provide = (host, key, value) => (host.$provisions ??= new Map).set(key, value),
65
64
 
66
- refresh = host => {
67
- try {
68
- render(host.$render(host.$params, host), host)
69
- } catch (error) {
70
- propagate(host, error)
71
- }
72
- },
65
+ consume = (host, key, fallback) => {
66
+ for (let map; host; host = host.parentNode) if ((map = host.$provisions) && map.has(key)) return map.get(key)
67
+ return fallback
68
+ },
73
69
 
74
- provide = (host, key, value) => (host.$provisions ??= new Map).set(key, value),
70
+ intercept = (host, fn) => isFunction(fn) && (host.$interceptor = fn),
75
71
 
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
- },
72
+ propagate = (host, error) => {
73
+ for (let fn; host; host = host.parentNode) if (isFunction(fn = host.$interceptor)) return render(fn(error))
74
+ throw error
75
+ },
84
76
 
85
- intercept = (host, fn) => host.$interceptor = fn,
77
+ cleanup = (host, fn) => isFunction(fn) && (host.$cleanups ??= new Set).add(fn),
86
78
 
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
- },
79
+ clx = o => keys(o).filter(k => o[k]).join(' ') || null,
91
80
 
92
- cleanup = (host, fn) => (host.$cleanups ??= new Set).add(fn),
81
+ stx = o => entries(o).map(t => t.join(':')).join(';') || null,
93
82
 
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), {})
83
+ keb = o => keys(o).reduce((r, k) => ((r[k.replace(search, replace).toLowerCase()] = o[k]), r), {})
97
84
 
98
85
  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',
86
+ { isArray, from } = Array, { keys, entries } = Object, isFunction = v => typeof v == 'function',
87
+
88
+ noop = () => { }, ref = v => (...args) => args.length ? v = args[0] : v,
89
+
90
+ map = list => list.reduce(set, new Map), set = (m, v, i) => (m.set(v, i), m),
91
+
92
+ some = (a, b) => (isArray(a) && isArray(b)) ? a.some((v, i) => v !== b[i]) : a !== b,
93
+
94
+ reduce = v => from(v).reduce(assign, {}), assign = (v, { name, value }) => ((v[name] = value), v),
95
+
96
+ proxy = { firstChild: null, insertBefore: node => proxy.firstChild ??= node },
97
+
98
+ search = /([a-z0-9])([A-Z])/g, replace = '$1-$2',
99
+
100
+ normalize = function* (h, buffer = ref(''), root = true) {
101
+
102
+ let text
103
+
104
+ for (h of isArray(h) ? h : [h]) {
105
+ if (h == null || typeof h == 'boolean') continue
106
+ if (typeof h.nodeName == 'string') ((text = buffer()) && (buffer(''), yield text)), yield h
107
+ else if (isFunction(h.nodeName)) yield* normalize(h.nodeName(h), buffer, false)
108
+ else isArray(h) ? yield* normalize(h, buffer, false) : buffer(buffer() + h)
109
+ }
110
+
111
+ root && (text = buffer()) && (yield text)
112
+ },
113
+
114
+ update = (props, host) => {
104
115
 
105
- { keys, entries } = Object, { isArray, from } = Array,
116
+ const prev = host.$props ?? (host.hasAttributes() ? reduce(host.attributes) : {})
106
117
 
107
- search = /([a-z0-9])([A-Z])/g, replace = '$1-$2', it = Symbol.iterator,
118
+ for (const name in { ...prev, ...props }) {
108
119
 
109
- normalize = function* (h, host, buffer = ref(''), root = true) {
120
+ let value = props[name]
110
121
 
111
- let type, text
122
+ if (value !== prev[name])
123
+ if (name.startsWith('set:')) host[name.slice(4)] = value
124
+ else if (value == null || value === false) host.removeAttribute(name)
125
+ else host.setAttribute(name, value === true ? '' : value)
126
+ }
112
127
 
113
- for (h of isFn(h?.[it]) ? h : [h]) {
128
+ host.$props = props
129
+ },
114
130
 
115
- if (h == null || (type = typeof h) == 'boolean') continue
131
+ before = (host, node, child) => {
132
+ if (node.contains?.(document.activeElement)) {
116
133
 
117
- if (type == 'string' || type == 'number') {
118
- buffer(buffer() + h)
119
- continue
120
- }
134
+ const ref = node.nextSibling
121
135
 
122
- if ('nodeName' in Object(h)) {
136
+ while (child && child !== node) {
137
+ const next = child.nextSibling
138
+ host.insertBefore(child, ref)
139
+ child = next
140
+ }
123
141
 
124
- if (isFn(h.nodeName)) {
125
- yield* normalize(h.nodeName(h), host, buffer, false)
126
- continue
127
- }
142
+ } else host.insertBefore(node, child)
143
+ },
128
144
 
129
- if (text = buffer()) {
130
- yield text
131
- buffer('')
132
- }
145
+ iterate = (host, root, each, by, fn, ref) => {
133
146
 
134
- yield h
135
- continue
136
- }
147
+ each = isArray(each) ? each : []
148
+ by = isFunction(by) ? by : v => v
149
+ fn = isFunction(fn) ? fn : noop
137
150
 
138
- isFn(h[it]) ? yield* normalize(h, host, buffer, false) : buffer(buffer() + h)
139
- }
151
+ const
152
+ map = host.$for ??= new Map,
153
+ del = node => map.delete(node.$key),
154
+ clr = each !== host.$each,
155
+ len = (host.$each = each).length,
156
+ a = from(host.childNodes),
157
+ b = new Array(len)
140
158
 
141
- if (root && (text = buffer())) yield text
142
- },
159
+ clr && map.clear()
143
160
 
144
- update = (props, host) => {
161
+ for (let last, index = 0; index < len; index++) {
145
162
 
146
- const prev = host.$props ?? (host.hasAttributes() ? reduce(host.attributes) : {})
163
+ const item = each[index], key = by(item, index)
147
164
 
148
- for (const name in { ...prev, ...props }) {
165
+ proxy.firstChild = (clr ? a[index] : map.get(key)) ?? last?.cloneNode(true)
166
+ render(fn(item), proxy, proxy.firstChild?.nextSibling, root)
149
167
 
150
- let value = props[name]
168
+ last = proxy.firstChild
169
+ proxy.firstChild = null
151
170
 
152
- if (value === prev[name]) {
153
- continue
154
- }
171
+ map.set(last.$key = key, b[index] = last)
172
+ }
155
173
 
156
- if (name.startsWith('set:')) {
157
- host[name.slice(4)] = value
158
- continue
159
- }
174
+ arrange(host, a, b, del)
175
+ isFunction(ref) && ref(host, root)
176
+ },
160
177
 
161
- if (value == null || value === false) {
162
- host.removeAttribute(name)
163
- continue
164
- }
178
+ arrange = (host, a, b, dispose = noop) => {
165
179
 
166
- host.setAttribute(name, value === true ? '' : value)
167
- }
180
+ const aLen = a.length, bLen = b.length
168
181
 
169
- host.$props = props
170
- },
182
+ let aIndex = 0, bIndex = 0, aValue, bValue, aMap, bMap, i
171
183
 
172
- arrange = (host, a, b) => {
184
+ while (aIndex !== aLen || bIndex !== bLen) {
173
185
 
174
- const aLength = a.length, bLength = b.length
175
- let aIndex = 0, bIndex = 0, aValue, bValue, aMap, bMap, i
186
+ aValue = a[aIndex]
187
+ bValue = b[bIndex]
176
188
 
177
- while (aIndex !== aLength || bIndex !== bLength) {
189
+ if (aValue === null) aIndex++
190
+ else if (bLen <= bIndex) aIndex++, dispose(host.removeChild(aValue))
191
+ else if (aLen <= aIndex) bIndex++, host.appendChild(bValue)
192
+ else if (aValue === bValue) aIndex++, bIndex++
193
+ else {
178
194
 
179
- aValue = a[aIndex], bValue = b[bIndex]
195
+ aMap ??= map(a)
196
+ bMap ??= map(b)
180
197
 
181
- if (aValue === null) {
182
- aIndex++
183
- continue
184
- }
198
+ if (bMap.get(aValue) == null) aIndex++, dispose(host.removeChild(aValue))
199
+ else {
185
200
 
186
- if (bLength <= bIndex) {
187
- host.removeChild(aValue).nodeType == 1 && dispose(aValue)
188
- aIndex++
189
- continue
190
- }
201
+ host.insertBefore(bValue, aValue), bIndex++
191
202
 
192
- if (aLength <= aIndex) {
193
- host.insertBefore(bValue, aValue)
194
- bIndex++
195
- continue
196
- }
203
+ if ((i = aMap.get(bValue)) != null) {
204
+ if (i > aIndex + 1) aIndex++
205
+ a[i] = null
206
+ }
207
+ }
208
+ }
209
+ }
210
+ },
197
211
 
198
- if (aValue === bValue) {
199
- aIndex++
200
- bIndex++
201
- continue
202
- }
212
+ run = (host, root, setup, params, ref) => {
203
213
 
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
- }
214
+ host.$root = root
215
+ host.$setup ??= isFunction(setup) ? setup : noop
216
+ host.$params = { ...setup.params, ...params }
210
217
 
211
- if (bMap.get(aValue) == null) {
212
- host.removeChild(aValue).nodeType == 1 && dispose(aValue)
213
- aIndex++
214
- continue
215
- }
218
+ refresh(host)
219
+ isFunction(ref) && ref(host, root)
220
+ },
216
221
 
217
- host.insertBefore(bValue, aValue)
218
- bIndex++
222
+ dispose = host => {
219
223
 
220
- if ((i = aMap.get(bValue)) != null) {
221
- if (i > aIndex + 1) aIndex++
222
- a[i] = null
223
- }
224
- }
225
- },
224
+ const { $cleanups } = host
226
225
 
227
- dispose = host => {
228
- for (const child of host.children) dispose(child)
229
- for (const fn of host.$cleanups ?? []) fn(host)
230
- }
226
+ if ($cleanups) {
227
+ for (let fn of $cleanups) fn(host)
228
+ $cleanups.clear()
229
+ }
230
+ }
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 y=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var z=Object.prototype.hasOwnProperty;var E=(e,n)=>{for(var i in n)y(e,i,{get:n[i],enumerable:!0})},I=(e,n,i,l)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of L(n))!z.call(e,r)&&r!==i&&y(e,r,{get:()=>n[r],enumerable:!(l=F(n,r))||l.enumerable});return e};var T=e=>I(y({},"__esModule",{value:!0}),e);var ae={};E(ae,{For:()=>O,Fragment:()=>V,cleanup:()=>G,clx:()=>H,component:()=>W,consume:()=>q,h:()=>N,intercept:()=>D,keb:()=>K,propagate:()=>M,provide:()=>Z,refresh:()=>A,render:()=>x,stx:()=>J});var S=require("element-notifier");const V=({children:e})=>e,O=({is:e,each:n,by:i,children:l,ref:r,...t})=>N(e??"div",{...t,skip:!0,ref:(a,f)=>ie(a,f,n,i,l,r)}),N=(e,n,...i)=>({children:i.length==0?null:i.length==1?i[0]:i,...n,nodeName:e}),x=(e,n,i,l=n)=>{let r=n.firstChild,t;l==n&&(l.$mo??=(0,S.notify)((a,f)=>f||l.contains(a)||te(a),l));for(e of C(e)){if(e instanceof Node)t=e;else if(typeof e=="string"){for(t=r;t!=i&&t.nodeType!=3;t=t.nextSibling);t==i?t=document.createTextNode(e):t.data!=e&&(t.data=e)}else{const{nodeName:a,block:f,skip:p,children:d,ref:o,...u}=e;for(t=r;t!=i&&t.localName!=a;t=t.nextSibling);t==i&&(t=document.createElement(a)),(f==null||U(t.$deps,t.$deps=f))&&(ee(u,t),p||x(d,t,null,l),c(o)&&o(t,l))}t==r?r=r.nextSibling:ne(n,t,r)}for(;r!=i;){const a=r.nextSibling;n.removeChild(r),r=a}},W=e=>({is:n,props:i,ref:l,...r})=>N(n??e.is??"div",{...e.props,...i,skip:!0,ref:(t,a)=>re(t,a,e,r,l)}),A=e=>{try{x((e.$render??=e.$setup(e))(e.$params),e,null,e.$root)}catch(n){M(e,n)}},Z=(e,n,i)=>(e.$provisions??=new Map).set(n,i),q=(e,n,i)=>{for(let l;e;e=e.parentNode)if((l=e.$provisions)&&l.has(n))return l.get(n);return i},D=(e,n)=>c(n)&&(e.$interceptor=n),M=(e,n)=>{for(let i;e;e=e.parentNode)if(c(i=e.$interceptor))return x(i(n));throw n},G=(e,n)=>c(n)&&(e.$cleanups??=new Set).add(n),H=e=>B(e).filter(n=>e[n]).join(" ")||null,J=e=>P(e).map(n=>n.join(":")).join(";")||null,K=e=>B(e).reduce((n,i)=>(n[i.replace(_,h).toLowerCase()]=e[i],n),{}),{isArray:g,from:j}=Array,{keys:B,entries:P}=Object,c=e=>typeof e=="function",b=()=>{},Q=e=>(...n)=>n.length?e=n[0]:e,k=e=>e.reduce(R,new Map),R=(e,n,i)=>(e.set(n,i),e),U=(e,n)=>g(e)&&g(n)?e.some((i,l)=>i!==n[l]):e!==n,X=e=>j(e).reduce(Y,{}),Y=(e,{name:n,value:i})=>(e[n]=i,e),$={firstChild:null,insertBefore:e=>$.firstChild??=e},_=/([a-z0-9])([A-Z])/g,h="$1-$2",C=function*(e,n=Q(""),i=!0){let l;for(e of g(e)?e:[e])e==null||typeof e=="boolean"||(typeof e.nodeName=="string"?((l=n())&&(n(""),yield l),yield e):c(e.nodeName)?yield*C(e.nodeName(e),n,!1):g(e)?yield*C(e,n,!1):n(n()+e));i&&(l=n())&&(yield l)},ee=(e,n)=>{const i=n.$props??(n.hasAttributes()?X(n.attributes):{});for(const l in{...i,...e}){let r=e[l];r!==i[l]&&(l.startsWith("set:")?n[l.slice(4)]=r:r==null||r===!1?n.removeAttribute(l):n.setAttribute(l,r===!0?"":r))}n.$props=e},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,t)=>{i=g(i)?i:[],l=c(l)?l:s=>s,r=c(r)?r:b;const a=e.$for??=new Map,f=s=>a.delete(s.$key),p=i!==e.$each,d=(e.$each=i).length,o=j(e.childNodes),u=new Array(d);p&&a.clear();for(let s,m=0;m<d;m++){const v=i[m],w=l(v,m);$.firstChild=(p?o[m]:a.get(w))??s?.cloneNode(!0),x(r(v),$,$.firstChild?.nextSibling,n),s=$.firstChild,$.firstChild=null,a.set(s.$key=w,u[m]=s)}le(e,o,u,f),c(t)&&t(e,n)},le=(e,n,i,l=b)=>{const r=n.length,t=i.length;let a=0,f=0,p,d,o,u,s;for(;a!==r||f!==t;)p=n[a],d=i[f],p===null?a++:t<=f?(a++,l(e.removeChild(p))):r<=a?(f++,e.appendChild(d)):p===d?(a++,f++):(o??=k(n),u??=k(i),u.get(p)==null?(a++,l(e.removeChild(p))):(e.insertBefore(d,p),f++,(s=o.get(d))!=null&&(s>a+1&&a++,n[s]=null)))},re=(e,n,i,l,r)=>{e.$root=n,e.$setup??=c(i)?i:b,e.$params={...i.params,...l},A(e),c(r)&&r(e,n)},te=e=>{const{$cleanups:n}=e;if(n){for(let i of n)i(e);n.clear()}};return T(ae);})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ajo",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
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,11 @@
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.6",
41
+ "esbuild": "^0.15.6",
35
42
  "uvu": "^0.5.6"
43
+ },
44
+ "dependencies": {
45
+ "element-notifier": "^1.1.2"
36
46
  }
37
47
  }
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