Skip to content

Commit 436a622

Browse files
committed
hyperHTML v2.4
* created a `Wire` class to handle via `domdiff` multiple wired nodes. * brought back multi nodes per wire, a feature lost since **v2.0** * simplified `Component` handling too, making it compatible again with multi wired content. * fixed some check to make IE9+ tests green again
1 parent b95db1e commit 436a622

19 files changed

Lines changed: 1590 additions & 1282 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# hyper(html) Changelog
22

3+
### v2.4.0
4+
* created a `Wire` class to handle via `domdiff` multiple wired nodes.
5+
* brought back multi nodes per wire, a feature lost since **v2.0**
6+
* simplified `Component` handling too, making it compatible again with multi wired content.
7+
* fixed some check to make IE9+ tests green again
8+
39
### v2.3.0
410
* dropped the `engine` already. Too complex, no real benefits, refactored the whole internal logic to use [domdiff](https://github.com/WebReflection/domdiff) instead. Deprecated [hyperhtml-majinbuu](https://github.com/WebReflection/hyperhtml-majinbuu) and solved diffing "_forever_".
511

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Following most important changes in version 2:
2323
* **added** support for objects as `style` attribute, fully compatible with [Preact](https://github.com/developit/preact) implementation
2424
* **improved** performance in numerous ways
2525
* **custom elements** V0 and V1 are now fully, and properly, supported through `document.importNode` and/or regular `cloneNode` tested against common polyfills
26-
* back to 4.35K thanks to **rollup** and its ability to merge all the things together like it was already in V1
26+
* back to 4.5K thanks to **rollup** and its ability to merge all the things together like it was already in V1
2727

2828
## Documentation
2929

@@ -48,7 +48,7 @@ setInterval(tick, 1000,
4848

4949
## Features
5050

51-
* Zero dependencies, no polyfills needed, and it fits in about **4.35KB** (minified + brotli)
51+
* Zero dependencies, no polyfills needed, and it fits in about **4.5KB** (minified + brotli)
5252
* Uses directly native DOM, no virtual DOM involved
5353
* Designed for [template literals](http://www.ecma-international.org/ecma-262/6.0/#sec-template-literals), a templating feature built in to JS
5454
* Compatible with plain DOM elements and plain JS data structures

cjs/classes/Wire.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
'use strict';
2+
const { append } = require('../shared/utils.js');
3+
const { doc, fragment } = require('../shared/easy-dom.js');
4+
5+
function Wire(childNodes) {
6+
this.childNodes = childNodes;
7+
this.length = childNodes.length;
8+
this.first = childNodes[0];
9+
this.last = childNodes[this.length - 1];
10+
}
11+
Object.defineProperty(exports, '__esModule', {value: true}).default = Wire
12+
13+
// when a wire is inserted, all its nodes will follow
14+
Wire.prototype.insert = function insert() {
15+
const df = fragment(this.first);
16+
append(df, this.childNodes);
17+
return df;
18+
};
19+
20+
// when a wire is removed, all its nodes must be removed as well
21+
Wire.prototype.remove = function remove() {
22+
const first = this.first;
23+
const last = this.last;
24+
if (this.length === 2) {
25+
last.parentNode.removeChild(last);
26+
} else {
27+
const range = doc(first).createRange();
28+
range.setStartBefore(this.childNodes[1]);
29+
range.setEndAfter(last);
30+
range.deleteContents();
31+
}
32+
return first;
33+
};

cjs/hyper/wire.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const {ELEMENT_NODE, SVG_NAMESPACE} = require('../shared/constants.js');
33
const {WeakMap, trim} = require('../shared/poorlyfills.js');
44
const {fragment} = require('../shared/easy-dom.js');
55
const {append, slice, unique} = require('../shared/utils.js');
6+
const Wire = (m => m.__esModule ? m.default : m)(require('../classes/Wire.js'));
67
const render = (m => m.__esModule ? m.default : m)(require('./render.js'));
78

89
// all wires used per each context
@@ -79,17 +80,17 @@ const weakly = (obj, type) => {
7980
const wireContent = node => {
8081
const childNodes = node.childNodes;
8182
const length = childNodes.length;
82-
const wire = [];
83+
const wireNodes = [];
8384
for (let i = 0; i < length; i++) {
8485
let child = childNodes[i];
8586
if (
8687
child.nodeType === ELEMENT_NODE ||
8788
trim.call(child.textContent).length !== 0
8889
) {
89-
wire.push(child);
90+
wireNodes.push(child);
9091
}
9192
}
92-
return wire.length === 1 ? wire[0] : wire;
93+
return wireNodes.length === 1 ? wireNodes[0] : new Wire(wireNodes);
9394
};
9495

9596
exports.content = content;

cjs/objects/Updates.js

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ const {
44
} = require('../shared/constants.js');
55

66
const Component = (m => m.__esModule ? m.default : m)(require('../classes/Component.js'));
7+
const Wire = (m => m.__esModule ? m.default : m)(require('../classes/Wire.js'));
78
const Path = (m => m.__esModule ? m.default : m)(require('./Path.js'));
89
const Style = (m => m.__esModule ? m.default : m)(require('./Style.js'));
910
const Intent = (m => m.__esModule ? m.default : m)(require('./Intent.js'));
1011
const domdiff = (m => m.__esModule ? m.default : m)(require('../shared/domdiff.js'));
11-
const {text} = require('../shared/easy-dom.js');
12-
const {Event, WeakSet, isArray, trim} = require('../shared/poorlyfills.js');
13-
const {createFragment, slice} = require('../shared/utils.js');
12+
const { text } = require('../shared/easy-dom.js');
13+
const { Event, WeakSet, isArray, trim } = require('../shared/poorlyfills.js');
14+
const { createFragment, slice } = require('../shared/utils.js');
1415

1516
// hyper.Component have a connected/disconnected
1617
// mechanism provided by MutationObserver
@@ -26,7 +27,25 @@ Cache.prototype = Object.create(null);
2627
// returns an intent to explicitly inject content as html
2728
const asHTML = html => ({html});
2829

29-
const asNode = item => item instanceof Component ? item.render() : item;
30+
// returns nodes from wires and components
31+
const asNode = (item, i) => {
32+
return 'ELEMENT_NODE' in item ?
33+
item :
34+
(item.constructor === Wire ?
35+
// in the Wire case, the content can be
36+
// removed, post-pended, inserted, or pre-pended and
37+
// all these cases are handled by domdiff already
38+
/* istanbul ignore next */
39+
((1 / i) < 0 ?
40+
(i ? item.remove() : item.last) :
41+
(i ? item.insert() : item.first)) :
42+
asNode(item.render(), i));
43+
}
44+
45+
// returns true if domdiff can handle the value
46+
const canDiff = value => 'ELEMENT_NODE' in value ||
47+
value instanceof Wire ||
48+
value instanceof Component;
3049

3150
// updates are created once per context upgrade
3251
// within the main render function (../hyper/render.js)
@@ -162,8 +181,7 @@ const invokeAtDistance = (value, callback) => {
162181
}
163182
};
164183

165-
// quick and dirty ways to check a value type without abusing instanceof
166-
const isNode_ish = value => 'ELEMENT_NODE' in value;
184+
// quick and dirty way to check for Promise/ish values
167185
const isPromise_ish = value => value != null && 'then' in value;
168186

169187
// in a hyper(node)`<div>${content}</div>` case
@@ -253,15 +271,7 @@ const setAnyContent = (node, childNodes) => {
253271
break;
254272
}
255273
}
256-
} else if (value instanceof Component) {
257-
childNodes = domdiff(
258-
node.parentNode,
259-
childNodes,
260-
[value],
261-
asNode,
262-
node
263-
);
264-
} else if (isNode_ish(value)) {
274+
} else if (canDiff(value)) {
265275
childNodes = domdiff(
266276
node.parentNode,
267277
childNodes,

cjs/shared/domdiff.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const domdiff = (
1818
beforeNode // optional item/node to use as insertBefore delimiter
1919
) => {
2020
const get = getNode || identity;
21-
const before = beforeNode == null ? null : get(beforeNode);
21+
const before = beforeNode == null ? null : get(beforeNode, 0);
2222
let currentStart = 0, futureStart = 0;
2323
let currentEnd = currentNodes.length - 1;
2424
let currentStartNode = currentNodes[0];
@@ -49,16 +49,16 @@ const domdiff = (
4949
}
5050
else if (currentStartNode == futureEndNode) {
5151
parentNode.insertBefore(
52-
get(currentStartNode),
53-
get(currentEndNode).nextSibling || before
52+
get(currentStartNode, 1),
53+
get(currentEndNode, -0).nextSibling
5454
);
5555
currentStartNode = currentNodes[++currentStart];
5656
futureEndNode = futureNodes[--futureEnd];
5757
}
5858
else if (currentEndNode == futureStartNode) {
5959
parentNode.insertBefore(
60-
get(currentEndNode),
61-
get(currentStartNode)
60+
get(currentEndNode, 1),
61+
get(currentStartNode, 0)
6262
);
6363
currentEndNode = currentNodes[--currentEnd];
6464
futureStartNode = futureNodes[++futureStart];
@@ -67,31 +67,31 @@ const domdiff = (
6767
let index = currentNodes.indexOf(futureStartNode);
6868
if (index < 0) {
6969
parentNode.insertBefore(
70-
get(futureStartNode),
71-
get(currentStartNode)
70+
get(futureStartNode, 1),
71+
get(currentStartNode, 0)
7272
);
7373
futureStartNode = futureNodes[++futureStart];
7474
}
7575
else {
7676
let el = currentNodes[index];
7777
currentNodes[index] = null;
7878
parentNode.insertBefore(
79-
get(el),
80-
get(currentStartNode)
79+
get(el, 1),
80+
get(currentStartNode, 0)
8181
);
8282
futureStartNode = futureNodes[++futureStart];
8383
}
8484
}
8585
}
8686
if (currentStart > currentEnd) {
8787
const pin = futureNodes[futureEnd + 1];
88-
const place = pin != null ? get(pin) : before;
88+
const place = pin != null ? get(pin, 0) : before;
8989
while (futureStart <= futureEnd) {
9090
const ch = futureNodes[futureStart++];
9191
// ignore until I am sure the else could never happen.
9292
// it might be a vDOM thing 'cause it never happens here.
9393
/* istanbul ignore else */
94-
if (ch != null) parentNode.insertBefore(get(ch), place);
94+
if (ch != null) parentNode.insertBefore(get(ch, 1), place);
9595
}
9696
}
9797
// ignore until I am sure the else could never happen.
@@ -100,7 +100,7 @@ const domdiff = (
100100
else if (futureStart > futureEnd) {
101101
while (currentStart <= currentEnd) {
102102
const ch = currentNodes[currentStart++];
103-
if (ch != null) parentNode.removeChild(get(ch));
103+
if (ch != null) parentNode.removeChild(get(ch, -1));
104104
}
105105
}
106106
return futureNodes;

coverage/coverage.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)