Skip to content

Commit 3aa69fb

Browse files
Merge pull request #302 from WebReflection/hyperhtml-style
Added hyperhtml-style
2 parents f25bd26 + cd48f23 commit 3aa69fb

File tree

20 files changed

+496
-274
lines changed

20 files changed

+496
-274
lines changed

cjs/hyper/render.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
'use strict';
22
const WeakMap = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/weakmap'));
3-
const unique = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/template-literal'));
43

54
const {OWNER_SVG_ELEMENT} = require('../shared/constants.js');
65
const {Tagger} = require('../objects/Updates.js');
6+
const {reArguments} = require('../shared/utils.js');
77

88
// a weak collection of contexts that
99
// are already known to hyperHTML
@@ -13,12 +13,13 @@ const bewitched = new WeakMap;
1313
// the main tag function in charge of fully upgrading
1414
// or simply updating, contexts used as hyperHTML targets.
1515
// The `this` context is either a regular DOM node or a fragment.
16-
function render(template) {
16+
function render() {
1717
const wicked = bewitched.get(this);
18-
if (wicked && wicked.template === unique(template)) {
19-
wicked.tagger.apply(null, arguments);
18+
const args = reArguments.apply(null, arguments);
19+
if (wicked && wicked.template === args[0]) {
20+
wicked.tagger.apply(null, args);
2021
} else {
21-
upgrade.apply(this, arguments);
22+
upgrade.apply(this, args);
2223
}
2324
return this;
2425
}
@@ -27,13 +28,13 @@ function render(template) {
2728
// parse it once, if unknown, to map all interpolations
2829
// as single DOM callbacks, relate such template
2930
// to the current context, and render it after cleaning the context up
30-
function upgrade(template) {
31-
template = unique(template);
31+
function upgrade() {
32+
const args = reArguments.apply(null, arguments);
3233
const type = OWNER_SVG_ELEMENT in this ? 'svg' : 'html';
3334
const tagger = new Tagger(type);
34-
bewitched.set(this, {tagger, template});
35+
bewitched.set(this, {tagger, template: args[0]});
3536
this.textContent = '';
36-
this.appendChild(tagger.apply(null, arguments));
37+
this.appendChild(tagger.apply(null, args));
3738
}
3839

3940
Object.defineProperty(exports, '__esModule', {value: true}).default = render;

cjs/hyper/wire.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
'use strict';
22
const WeakMap = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/weakmap'));
3-
const unique = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/template-literal'));
43
const trim = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('@ungap/trim'));
54

65
const {ELEMENT_NODE} = require('../shared/constants.js');
76
const Wire = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('../classes/Wire.js'));
87

98
const {Tagger} = require('../objects/Updates.js');
9+
const {reArguments} = require('../shared/utils.js');
1010

1111
// all wires used per each context
1212
const wires = new WeakMap;
@@ -17,7 +17,7 @@ const wires = new WeakMap;
1717
// This provides the ability to have a unique DOM structure
1818
// related to a unique JS object through a reusable template literal.
1919
// A wire can specify a type, as svg or html, and also an id
20-
// via html:id or :id convention. Such :id allows same JS objects
20+
// via the html:id or :id convention. Such :id allows same JS objects
2121
// to be associated to different DOM structures accordingly with
2222
// the used template literal without losing previously rendered parts.
2323
const wire = (obj, type) => obj == null ?
@@ -32,22 +32,22 @@ const wire = (obj, type) => obj == null ?
3232
// in charge of updating its content like a bound element would do.
3333
const content = type => {
3434
let wire, tagger, template;
35-
return function (statics) {
36-
statics = unique(statics);
37-
if (template !== statics) {
38-
template = statics;
35+
return function () {
36+
const args = reArguments.apply(null, arguments);
37+
if (template !== args[0]) {
38+
template = args[0];
3939
tagger = new Tagger(type);
40-
wire = wireContent(tagger.apply(tagger, arguments));
40+
wire = wireContent(tagger.apply(tagger, args));
4141
} else {
42-
tagger.apply(tagger, arguments);
42+
tagger.apply(tagger, args);
4343
}
4444
return wire;
4545
};
4646
};
4747

4848
// wires are weakly created through objects.
4949
// Each object can have multiple wires associated
50-
// and this is thanks to the type + :id feature.
50+
// thanks to the type + :id feature.
5151
const weakly = (obj, type) => {
5252
const i = type.indexOf(':');
5353
let wire = wires.get(obj);
@@ -61,7 +61,7 @@ const weakly = (obj, type) => {
6161
return wire[id] || (wire[id] = content(type));
6262
};
6363

64-
// a document fragment loses its nodes as soon
64+
// A document fragment loses its nodes as soon
6565
// as it's appended into another node.
6666
// This would easily lose wired content
6767
// so that on a second render call, the parent

cjs/objects/Basic.js

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
'use strict';
2+
const CustomEvent = (m => m.__esModule ? m.default : m)(require('@ungap/custom-event'));
3+
const WeakSet = (m => m.__esModule ? m.default : m)(require('@ungap/essential-weakset'));
4+
const disconnected = (m => m.__esModule ? m.default : m)(require('disconnected'));
5+
const domdiff = (m => m.__esModule ? m.default : m)(require('domdiff'));
6+
const domtagger = (m => m.__esModule ? m.default : m)(require('domtagger'));
7+
const hyperStyle = (m => m.__esModule ? m.default : m)(require('hyperhtml-style'));
8+
9+
const CONNECTED = 'connected';
10+
const DISCONNECTED = 'dis' + CONNECTED;
11+
12+
const slice = [].slice;
13+
const observe = disconnected({Event: CustomEvent, WeakSet});
14+
15+
exports.Tagger = Tagger;
16+
exports.observe = observe;
17+
18+
// list of attributes that should not be directly assigned
19+
const readOnly = /^(?:form|list)$/i;
20+
21+
function Tagger(type) {
22+
this.type = type;
23+
return domtagger(this);
24+
}
25+
26+
Tagger.prototype = {
27+
28+
attribute(node, name, original) {
29+
const isSVG = 'ownerSVGElement' in node;
30+
let oldValue;
31+
if (name === 'style')
32+
return hyperStyle(node);
33+
else if ('on' === name.slice(0, 2)) {
34+
let type = name.slice(2);
35+
if (type === CONNECTED || type === DISCONNECTED)
36+
observe(node);
37+
else if (name.toLowerCase()
38+
in node)
39+
type = type.toLowerCase();
40+
return newValue => {
41+
if (oldValue !== newValue) {
42+
if (oldValue)
43+
node.removeEventListener(type, oldValue, false);
44+
oldValue = newValue;
45+
if (newValue)
46+
node.addEventListener(type, newValue, false);
47+
}
48+
};
49+
}
50+
else if (
51+
name === 'data' ||
52+
(!isSVG && name in node && !readOnly.test(name))
53+
) {
54+
return newValue => {
55+
if (oldValue !== newValue) {
56+
oldValue = newValue;
57+
if (node[name] !== newValue) {
58+
node[name] = newValue;
59+
if (newValue == null) {
60+
node.removeAttribute(name);
61+
}
62+
}
63+
}
64+
};
65+
}
66+
else {
67+
let owner = false;
68+
const attribute = original.cloneNode(true);
69+
return newValue => {
70+
if (oldValue !== newValue) {
71+
oldValue = newValue;
72+
if (attribute.value !== newValue) {
73+
if (newValue == null) {
74+
if (owner) {
75+
owner = false;
76+
node.removeAttributeNode(attribute);
77+
}
78+
attribute.value = newValue;
79+
} else {
80+
attribute.value = newValue;
81+
if (!owner) {
82+
owner = true;
83+
node.setAttributeNode(attribute);
84+
}
85+
}
86+
}
87+
}
88+
};
89+
}
90+
},
91+
92+
any(node, childNodes) {
93+
const diffOptions = {before: node};
94+
let fastPath = false;
95+
let oldValue;
96+
const anyContent = value => {
97+
switch (typeof value) {
98+
case 'string':
99+
case 'number':
100+
case 'boolean':
101+
if (fastPath) {
102+
if (oldValue !== value) {
103+
oldValue = value;
104+
childNodes[0].textContent = value;
105+
}
106+
} else {
107+
fastPath = true;
108+
oldValue = value;
109+
childNodes = domdiff(
110+
node.parentNode,
111+
childNodes,
112+
[node.ownerDocument.createTextNode(value)],
113+
diffOptions
114+
);
115+
}
116+
break;
117+
case 'function':
118+
anyContent(value(node));
119+
break;
120+
case 'object':
121+
case 'undefined':
122+
if (value == null) {
123+
fastPath = false;
124+
childNodes = domdiff(
125+
node.parentNode,
126+
childNodes,
127+
[],
128+
diffOptions
129+
);
130+
break;
131+
}
132+
default:
133+
fastPath = false;
134+
oldValue = value;
135+
if ('ELEMENT_NODE' in value) {
136+
childNodes = domdiff(
137+
node.parentNode,
138+
childNodes,
139+
value.nodeType === 11 ?
140+
slice.call(value.childNodes) :
141+
[value],
142+
diffOptions
143+
);
144+
}
145+
break;
146+
}
147+
};
148+
return anyContent;
149+
},
150+
151+
text(node) {
152+
let oldValue;
153+
return value => {
154+
if (oldValue !== value) {
155+
oldValue = value;
156+
node.textContent = value == null ? '' : value;
157+
}
158+
};
159+
}
160+
};

cjs/objects/Updates.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ const createContent = (m => m.__esModule ? /* istanbul ignore next */ m.default
77
const disconnected = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('disconnected'));
88
const domdiff = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('domdiff'));
99
const domtagger = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('domtagger'));
10+
const hyperStyle = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('hyperhtml-style'));
1011

1112
const {
1213
CONNECTED, DISCONNECTED, DOCUMENT_FRAGMENT_NODE, OWNER_SVG_ELEMENT
1314
} = require('../shared/constants.js');
1415

1516
const Component = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('../classes/Component.js'));
1617
const Wire = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('../classes/Wire.js'));
17-
const Style = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('./Style.js'));
1818
const Intent = (m => m.__esModule ? /* istanbul ignore next */ m.default : /* istanbul ignore next */ m)(require('./Intent.js'));
1919
const { slice, text } = require('../shared/utils.js');
2020

@@ -89,9 +89,8 @@ Tagger.prototype = {
8989
let oldValue;
9090
// if the attribute is the style one
9191
// handle it differently from others
92-
if (name === 'style') {
93-
return Style(node, original, isSVG);
94-
}
92+
if (name === 'style')
93+
return hyperStyle(node, original, isSVG);
9594
// the name is an event one,
9695
// add/remove event listeners accordingly
9796
else if (/^on/.test(name)) {

cjs/objects/hyperstyle.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
'use strict';
2+
var hyperStyle = (function (){'use strict';
3+
// from https://github.com/developit/preact/blob/33fc697ac11762a1cb6e71e9847670d047af7ce5/src/varants.js
4+
var IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i;
5+
var hyphen = /([^A-Z])([A-Z]+)/g;
6+
return function hyperStyle(node) {
7+
return 'ownerSVGElement' in node ? svg(node) : update(node.style, false);
8+
};
9+
function ized($0, $1, $2) {
10+
return $1 + '-' + $2.toLowerCase();
11+
}
12+
function svg(node) {
13+
node.setAttribute('style', '');
14+
return update(node.getAttributeNode('style'), true);
15+
}
16+
function toStyle(object) {
17+
var key, css = [];
18+
for (key in object) {
19+
css.push(key.replace(hyphen, ized), ':', object[key], ';');
20+
}
21+
return css.join('');
22+
}
23+
function update(style, isSVG) {
24+
var oldType, oldValue;
25+
return function (newValue) {
26+
var info, key, styleValue, value;
27+
switch (typeof newValue) {
28+
case 'object':
29+
if (newValue) {
30+
if (oldType === 'object') {
31+
if (!isSVG) {
32+
if (oldValue !== newValue) {
33+
for (key in oldValue) {
34+
if (!(key in newValue)) {
35+
style[key] = '';
36+
}
37+
}
38+
}
39+
}
40+
} else {
41+
if (isSVG)
42+
style.value = '';
43+
else
44+
style.cssText = '';
45+
}
46+
info = isSVG ? {} : style;
47+
for (key in newValue) {
48+
value = newValue[key];
49+
styleValue = typeof value === 'number' &&
50+
!IS_NON_DIMENSIONAL.test(key) ?
51+
(value + 'px') : value;
52+
if (!isSVG && /^--/.test(key))
53+
info.setProperty(key, styleValue);
54+
else
55+
info[key] = styleValue;
56+
}
57+
oldType = 'object';
58+
if (isSVG)
59+
style.value = toStyle((oldValue = info));
60+
else
61+
oldValue = newValue;
62+
break;
63+
}
64+
default:
65+
if (oldValue != newValue) {
66+
oldType = 'string';
67+
oldValue = newValue;
68+
if (isSVG)
69+
style.value = newValue || '';
70+
else
71+
style.cssText = newValue || '';
72+
}
73+
break;
74+
}
75+
};
76+
}
77+
}());
78+
79+
Object.defineProperty(exports, '__esModule', {value: true}).default = hyperStyle;

0 commit comments

Comments
 (0)