Skip to content

Commit f87c218

Browse files
author
Andrea Giammarchi
committed
Fix #51 - Automatically addressed refs
1 parent b7d4bf4 commit f87c218

File tree

10 files changed

+140
-42
lines changed

10 files changed

+140
-42
lines changed

cjs/index.js

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ const ownKeys = typeof Reflect === 'object' && Reflect.ownKeys ||
1717
const setPrototypeOf = O.setPrototypeOf ||
1818
((o, p) => (o.__proto__ = p, o));
1919
const camel = name => name.replace(/-([a-z])/g, ($0, $1) => $1.toUpperCase());
20+
const {attachShadow} = HTMLElement.prototype;
21+
const sr = new WeakMap;
2022

2123
class HyperHTMLElement extends HTMLElement {
2224

@@ -229,21 +231,37 @@ class HyperHTMLElement extends HTMLElement {
229231
return Class;
230232
}
231233

234+
// weakly relate the shadowRoot for refs usage
235+
attachShadow() {
236+
const shadowRoot = attachShadow.apply(this, arguments);
237+
sr.set(this, shadowRoot);
238+
return shadowRoot;
239+
}
240+
241+
// returns elements by ref
242+
get refs() {
243+
const value = {};
244+
if ('_html$' in this) {
245+
const all = (sr.get(this) || this).querySelectorAll('[ref]');
246+
for (let {length} = all, i = 0; i < length; i++) {
247+
const node = all[i];
248+
value[node.getAttribute('ref')] = node;
249+
}
250+
Object.defineProperty(this, 'refs', {value});
251+
return value;
252+
}
253+
return value;
254+
}
255+
232256
// lazily bind once hyperHTML logic
233257
// to either the shadowRoot, if present and open,
234258
// the _shadowRoot property, if set due closed shadow root,
235259
// or the custom-element itself if no Shadow DOM is used.
236260
get html() {
237261
return this._html$ || (this.html = bind(
238-
// in case of Shadow DOM {mode: "open"}, use it
239-
this.shadowRoot ||
240-
// in case of Shadow DOM {mode: "close"}, use it
241-
// this needs the following reference created upfront
242-
// this._shadowRoot = this.attachShadow({mode: "close"});
243-
this._shadowRoot ||
244-
// if no Shadow DOM is used, simply use the component
245-
// as container for its own content (it just works too)
246-
this
262+
// in a way or another, bind to the right node
263+
// backward compatible, first two could probably go already
264+
this.shadowRoot || this._shadowRoot || sr.get(this) || this
247265
));
248266
}
249267

es5.js

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2011,6 +2011,9 @@ var HyperHTMLElement = (function (exports) {
20112011
});
20122012
};
20132013

2014+
var _attachShadow = HTMLElement.prototype.attachShadow;
2015+
var sr = new WeakMap();
2016+
20142017
var HyperHTMLElement =
20152018
/*#__PURE__*/
20162019
function (_HTMLElement) {
@@ -2023,6 +2026,16 @@ var HyperHTMLElement = (function (exports) {
20232026
}
20242027

20252028
_createClass(HyperHTMLElement, [{
2029+
key: "attachShadow",
2030+
// weakly relate the shadowRoot for refs usage
2031+
value: function attachShadow() {
2032+
var shadowRoot = _attachShadow.apply(this, arguments);
2033+
2034+
sr.set(this, shadowRoot);
2035+
return shadowRoot;
2036+
} // returns elements by ref
2037+
2038+
}, {
20262039
key: "render",
20272040
// overwrite this method with your own render
20282041
value: function render() {} // ---------------------//
@@ -2048,19 +2061,36 @@ var HyperHTMLElement = (function (exports) {
20482061
return this;
20492062
}
20502063
}, {
2051-
key: "html",
2052-
// lazily bind once hyperHTML logic
2064+
key: "refs",
2065+
get: function get() {
2066+
var value = {};
2067+
2068+
if ('_html$' in this) {
2069+
var all = (sr.get(this) || this).querySelectorAll('[ref]');
2070+
2071+
for (var length = all.length, i = 0; i < length; i++) {
2072+
var node = all[i];
2073+
value[node.getAttribute('ref')] = node;
2074+
}
2075+
2076+
Object.defineProperty(this, 'refs', {
2077+
value: value
2078+
});
2079+
return value;
2080+
}
2081+
2082+
return value;
2083+
} // lazily bind once hyperHTML logic
20532084
// to either the shadowRoot, if present and open,
20542085
// the _shadowRoot property, if set due closed shadow root,
20552086
// or the custom-element itself if no Shadow DOM is used.
2087+
2088+
}, {
2089+
key: "html",
20562090
get: function get() {
2057-
return this._html$ || (this.html = bind( // in case of Shadow DOM {mode: "open"}, use it
2058-
this.shadowRoot || // in case of Shadow DOM {mode: "close"}, use it
2059-
// this needs the following reference created upfront
2060-
// this._shadowRoot = this.attachShadow({mode: "close"});
2061-
this._shadowRoot || // if no Shadow DOM is used, simply use the component
2062-
// as container for its own content (it just works too)
2063-
this));
2091+
return this._html$ || (this.html = bind( // in a way or another, bind to the right node
2092+
// backward compatible, first two could probably go already
2093+
this.shadowRoot || this._shadowRoot || sr.get(this) || this));
20642094
} // it can be set too if necessary, it won't invoke render()
20652095
,
20662096
set: function set(value) {

es5.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

esm/index.js

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ const ownKeys = typeof Reflect === 'object' && Reflect.ownKeys ||
1616
const setPrototypeOf = O.setPrototypeOf ||
1717
((o, p) => (o.__proto__ = p, o));
1818
const camel = name => name.replace(/-([a-z])/g, ($0, $1) => $1.toUpperCase());
19+
const {attachShadow} = HTMLElement.prototype;
20+
const sr = new WeakMap;
1921

2022
class HyperHTMLElement extends HTMLElement {
2123

@@ -228,21 +230,37 @@ class HyperHTMLElement extends HTMLElement {
228230
return Class;
229231
}
230232

233+
// weakly relate the shadowRoot for refs usage
234+
attachShadow() {
235+
const shadowRoot = attachShadow.apply(this, arguments);
236+
sr.set(this, shadowRoot);
237+
return shadowRoot;
238+
}
239+
240+
// returns elements by ref
241+
get refs() {
242+
const value = {};
243+
if ('_html$' in this) {
244+
const all = (sr.get(this) || this).querySelectorAll('[ref]');
245+
for (let {length} = all, i = 0; i < length; i++) {
246+
const node = all[i];
247+
value[node.getAttribute('ref')] = node;
248+
}
249+
Object.defineProperty(this, 'refs', {value});
250+
return value;
251+
}
252+
return value;
253+
}
254+
231255
// lazily bind once hyperHTML logic
232256
// to either the shadowRoot, if present and open,
233257
// the _shadowRoot property, if set due closed shadow root,
234258
// or the custom-element itself if no Shadow DOM is used.
235259
get html() {
236260
return this._html$ || (this.html = bind(
237-
// in case of Shadow DOM {mode: "open"}, use it
238-
this.shadowRoot ||
239-
// in case of Shadow DOM {mode: "close"}, use it
240-
// this needs the following reference created upfront
241-
// this._shadowRoot = this.attachShadow({mode: "close"});
242-
this._shadowRoot ||
243-
// if no Shadow DOM is used, simply use the component
244-
// as container for its own content (it just works too)
245-
this
261+
// in a way or another, bind to the right node
262+
// backward compatible, first two could probably go already
263+
this.shadowRoot || this._shadowRoot || sr.get(this) || this
246264
));
247265
}
248266

index.js

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2061,6 +2061,8 @@ var HyperHTMLElement = (function (exports) {
20612061
const setPrototypeOf = O.setPrototypeOf ||
20622062
((o, p) => (o.__proto__ = p, o));
20632063
const camel = name => name.replace(/-([a-z])/g, ($0, $1) => $1.toUpperCase());
2064+
const {attachShadow} = HTMLElement.prototype;
2065+
const sr = new WeakMap;
20642066

20652067
class HyperHTMLElement extends HTMLElement {
20662068

@@ -2273,21 +2275,37 @@ var HyperHTMLElement = (function (exports) {
22732275
return Class;
22742276
}
22752277

2278+
// weakly relate the shadowRoot for refs usage
2279+
attachShadow() {
2280+
const shadowRoot = attachShadow.apply(this, arguments);
2281+
sr.set(this, shadowRoot);
2282+
return shadowRoot;
2283+
}
2284+
2285+
// returns elements by ref
2286+
get refs() {
2287+
const value = {};
2288+
if ('_html$' in this) {
2289+
const all = (sr.get(this) || this).querySelectorAll('[ref]');
2290+
for (let {length} = all, i = 0; i < length; i++) {
2291+
const node = all[i];
2292+
value[node.getAttribute('ref')] = node;
2293+
}
2294+
Object.defineProperty(this, 'refs', {value});
2295+
return value;
2296+
}
2297+
return value;
2298+
}
2299+
22762300
// lazily bind once hyperHTML logic
22772301
// to either the shadowRoot, if present and open,
22782302
// the _shadowRoot property, if set due closed shadow root,
22792303
// or the custom-element itself if no Shadow DOM is used.
22802304
get html() {
22812305
return this._html$ || (this.html = bind(
2282-
// in case of Shadow DOM {mode: "open"}, use it
2283-
this.shadowRoot ||
2284-
// in case of Shadow DOM {mode: "close"}, use it
2285-
// this needs the following reference created upfront
2286-
// this._shadowRoot = this.attachShadow({mode: "close"});
2287-
this._shadowRoot ||
2288-
// if no Shadow DOM is used, simply use the component
2289-
// as container for its own content (it just works too)
2290-
this
2306+
// in a way or another, bind to the right node
2307+
// backward compatible, first two could probably go already
2308+
this.shadowRoot || this._shadowRoot || sr.get(this) || this
22912309
));
22922310
}
22932311

min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
"@babel/core": "^7.2.2",
9494
"@babel/preset-env": "^7.3.1",
9595
"ascjs": "^3.0.0",
96-
"basichtml": "^0.21.0",
96+
"basichtml": "^0.21.1",
9797
"coveralls": "^3.0.2",
9898
"istanbul": "^0.4.5",
9999
"npm-dollar": "^2.1.3",

test.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,12 +257,13 @@ setTimeout(function () {
257257
// delegated handleEvent
258258
class MyDelegatedHandler extends HyperHTMLElement {
259259
whenClickHappens() { tressa.assert(true, 'whenClickHappens event dispatched'); }
260-
created() { this.html`<span data-call="whenClickHappens" onclick="${this}">click me</span>`; }
260+
created() { this.html`<span ref="span" data-call="whenClickHappens" onclick="${this}">click me</span>`; }
261261
}
262262

263263
MyDelegatedHandler.define('my-delegated-handler');
264264

265265
el = new MyDelegatedHandler();
266+
tressa.assert(!el.refs.span, 'no span until created');
266267
document.body.appendChild(el);
267268
var evt = new CustomEvent('click');
268269
el.firstChild.dispatchEvent(evt);
@@ -271,6 +272,17 @@ setTimeout(function () {
271272
el.firstChild.onclick = el.whenClickHappens;
272273
el.firstChild.dispatchEvent(new CustomEvent('click'));
273274

275+
tressa.assert(el.refs.span === el.querySelector('span'), 'span after creation');
276+
277+
class MyShadow extends HyperHTMLElement {
278+
constructor() {
279+
super();
280+
this.attachShadow({mode: 'open'});
281+
this.html`<p ref="gotcha" />`;
282+
}
283+
}
284+
285+
tressa.assert(!!(new MyShadow).refs.gotcha, 'ref works in shadow dom too');
274286

275287
// double created
276288
let createdInstances = 0;

test/test.es5.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ class MyInput extends HyperHTMLElement {
3737
oninput(e) {
3838
this.value = e.target.value;
3939
this.render();
40+
console.assert(this.refs.input === this.querySelector('input'), 'input as ref');
4041
}
4142

4243
render() {
4344
return this.html`
44-
<input value="${this.value}" oninput="${this}">`;
45+
<input ref="input" value="${this.value}" oninput="${this}">`;
4546
}
4647

4748
}

test/test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ class MyInput extends HyperHTMLElement {
1616
oninput(e) {
1717
this.value = e.target.value;
1818
this.render();
19+
console.assert(this.refs.input === this.querySelector('input'), 'input as ref');
1920
}
2021
render() { return this.html`
21-
<input value="${this.value}" oninput="${this}">`;
22+
<input ref="input" value="${this.value}" oninput="${this}">`;
2223
}
2324
}
2425
MyInput.define('my-input');

0 commit comments

Comments
 (0)