Skip to content

Commit 2866b11

Browse files
committed
WIP towards #984
1 parent dbd5a76 commit 2866b11

File tree

7 files changed

+278
-6
lines changed

7 files changed

+278
-6
lines changed

src/compile/nodes/Binding.ts

+14-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ const readOnlyMediaAttributes = new Set([
1414
'played'
1515
]);
1616

17+
// TODO a lot of this element-specific stuff should live in Element —
18+
// Binding should ideally be agnostic between Element and Component
19+
1720
export default class Binding extends Node {
1821
name: string;
1922
value: Expression;
@@ -57,7 +60,10 @@ export default class Binding extends Node {
5760
const node: Element = this.parent;
5861

5962
const needsLock = node.name !== 'input' || !/radio|checkbox|range|color/.test(node.getStaticAttributeValue('type'));
60-
const isReadOnly = node.isMediaNode() && readOnlyMediaAttributes.has(this.name);
63+
const isReadOnly = (
64+
(node.isMediaNode() && readOnlyMediaAttributes.has(this.name)) ||
65+
this.name === 'width' || this.name === 'height'
66+
);
6167

6268
let updateCondition: string;
6369

@@ -103,8 +109,7 @@ export default class Binding extends Node {
103109
if (this.name === 'currentTime' || this.name === 'volume') {
104110
updateCondition = `!isNaN(${snippet})`;
105111

106-
if (this.name === 'currentTime')
107-
initialUpdate = null;
112+
if (this.name === 'currentTime') initialUpdate = null;
108113
}
109114

110115
if (this.name === 'paused') {
@@ -117,6 +122,12 @@ export default class Binding extends Node {
117122
initialUpdate = null;
118123
}
119124

125+
// bind:width and bind:height
126+
if (this.name === 'width' || this.name === 'height') {
127+
initialUpdate = null;
128+
updateDom = null;
129+
}
130+
120131
return {
121132
name: this.name,
122133
object: name,

src/compile/nodes/Element.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ export default class Element extends Node {
262262
parentNode;
263263

264264
block.addVariable(name);
265-
const renderStatement = getRenderStatement(this.compiler, this.namespace, this.name);
265+
const renderStatement = getRenderStatement(this.namespace, this.name);
266266
block.builders.create.addLine(
267267
`${name} = ${renderStatement};`
268268
);
@@ -916,7 +916,6 @@ export default class Element extends Node {
916916
}
917917

918918
function getRenderStatement(
919-
compiler: Compiler,
920919
namespace: string,
921920
name: string
922921
) {
@@ -971,6 +970,12 @@ const events = [
971970
node.name === 'input' && /radio|checkbox|range/.test(node.getStaticAttributeValue('type'))
972971
},
973972

973+
{
974+
eventNames: ['resize'],
975+
filter: (node: Element, name: string) =>
976+
(name === 'width' || name === 'height')
977+
},
978+
974979
// media events
975980
{
976981
eventNames: ['timeupdate'],

src/shared/dom.js

+25
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,28 @@ export function selectMultipleValue(select) {
193193
return option.__value;
194194
});
195195
}
196+
197+
export function addResizeListener(element, fn) {
198+
if (getComputedStyle(element).position === 'static') {
199+
element.style.position = 'relative';
200+
}
201+
202+
const object = document.createElement('object');
203+
object.setAttribute('style', 'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; pointer-events: none; z-index: -1;');
204+
object.type = 'text/html';
205+
206+
if (isIE) element.appendChild(object);
207+
object.data = 'about:blank';
208+
if (!isIE) element.appendChild(object);
209+
210+
object.onload = () => {
211+
object.contentDocument.defaultView.addEventListener('resize', fn);
212+
};
213+
214+
return {
215+
cancel: () => {
216+
object.contentDocument.defaultView.removeEventListener('resize', fn);
217+
element.removeChild(object);
218+
}
219+
};
220+
}

src/validate/html/validateElement.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ export default function validateElement(
157157
message: `'${name}' binding can only be used with <audio> or <video>`
158158
});
159159
}
160-
} else {
160+
} else if (name !== 'width' && name !== 'height') {
161161
validator.error(attribute, {
162162
code: `invalid-binding`,
163163
message: `'${attribute.name}' is not a valid binding`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
function noop() {}
2+
3+
function assign(tar, src) {
4+
for (var k in src) tar[k] = src[k];
5+
return tar;
6+
}
7+
8+
function insertNode(node, target, anchor) {
9+
target.insertBefore(node, anchor);
10+
}
11+
12+
function detachNode(node) {
13+
node.parentNode.removeChild(node);
14+
}
15+
16+
function createElement(name) {
17+
return document.createElement(name);
18+
}
19+
20+
function blankObject() {
21+
return Object.create(null);
22+
}
23+
24+
function destroy(detach) {
25+
this.destroy = noop;
26+
this.fire('destroy');
27+
this.set = noop;
28+
29+
if (detach !== false) this._fragment.u();
30+
this._fragment.d();
31+
this._fragment = null;
32+
this._state = {};
33+
}
34+
35+
function _differs(a, b) {
36+
return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
37+
}
38+
39+
function fire(eventName, data) {
40+
var handlers =
41+
eventName in this._handlers && this._handlers[eventName].slice();
42+
if (!handlers) return;
43+
44+
for (var i = 0; i < handlers.length; i += 1) {
45+
var handler = handlers[i];
46+
47+
if (!handler.__calling) {
48+
handler.__calling = true;
49+
handler.call(this, data);
50+
handler.__calling = false;
51+
}
52+
}
53+
}
54+
55+
function get() {
56+
return this._state;
57+
}
58+
59+
function init(component, options) {
60+
component._handlers = blankObject();
61+
component._bind = options._bind;
62+
63+
component.options = options;
64+
component.root = options.root || component;
65+
component.store = component.root.store || options.store;
66+
}
67+
68+
function on(eventName, handler) {
69+
var handlers = this._handlers[eventName] || (this._handlers[eventName] = []);
70+
handlers.push(handler);
71+
72+
return {
73+
cancel: function() {
74+
var index = handlers.indexOf(handler);
75+
if (~index) handlers.splice(index, 1);
76+
}
77+
};
78+
}
79+
80+
function set(newState) {
81+
this._set(assign({}, newState));
82+
if (this.root._lock) return;
83+
this.root._lock = true;
84+
callAll(this.root._beforecreate);
85+
callAll(this.root._oncreate);
86+
callAll(this.root._aftercreate);
87+
this.root._lock = false;
88+
}
89+
90+
function _set(newState) {
91+
var oldState = this._state,
92+
changed = {},
93+
dirty = false;
94+
95+
for (var key in newState) {
96+
if (this._differs(newState[key], oldState[key])) changed[key] = dirty = true;
97+
}
98+
if (!dirty) return;
99+
100+
this._state = assign(assign({}, oldState), newState);
101+
this._recompute(changed, this._state);
102+
if (this._bind) this._bind(changed, this._state);
103+
104+
if (this._fragment) {
105+
this.fire("state", { changed: changed, current: this._state, previous: oldState });
106+
this._fragment.p(changed, this._state);
107+
this.fire("update", { changed: changed, current: this._state, previous: oldState });
108+
}
109+
}
110+
111+
function callAll(fns) {
112+
while (fns && fns.length) fns.shift()();
113+
}
114+
115+
function _mount(target, anchor) {
116+
this._fragment[this._fragment.i ? 'i' : 'm'](target, anchor || null);
117+
}
118+
119+
function _unmount() {
120+
if (this._fragment) this._fragment.u();
121+
}
122+
123+
var proto = {
124+
destroy,
125+
get,
126+
fire,
127+
on,
128+
set,
129+
_recompute: noop,
130+
_set,
131+
_mount,
132+
_unmount,
133+
_differs
134+
};
135+
136+
/* generated by Svelte vX.Y.Z */
137+
138+
function create_main_fragment(component, ctx) {
139+
var div;
140+
141+
return {
142+
c: function create() {
143+
div = createElement("div");
144+
div.textContent = "some content";
145+
},
146+
147+
m: function mount(target, anchor) {
148+
insertNode(div, target, anchor);
149+
150+
div.width = ctx.width ;
151+
div.height = ctx.height;
152+
},
153+
154+
p: noop,
155+
156+
u: function unmount() {
157+
detachNode(div);
158+
},
159+
160+
d: noop
161+
};
162+
}
163+
164+
function SvelteComponent(options) {
165+
init(this, options);
166+
this._state = assign({}, options.data);
167+
168+
this._fragment = create_main_fragment(this, this._state);
169+
170+
if (options.target) {
171+
this._fragment.c();
172+
this._mount(options.target, options.anchor);
173+
}
174+
}
175+
176+
assign(SvelteComponent.prototype, proto);
177+
178+
export default SvelteComponent;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* generated by Svelte vX.Y.Z */
2+
import { addResizeListener, assign, createElement, detachNode, init, insertNode, noop, proto } from "svelte/shared.js";
3+
4+
function create_main_fragment(component, ctx) {
5+
var div, div_resize_listener;
6+
7+
function div_resize_handler() {
8+
component.set({ w: div.offsetWidth, h: offsetHeight });
9+
}
10+
11+
return {
12+
c: function create() {
13+
div = createElement("div");
14+
div.textContent = "some content";
15+
},
16+
17+
h: function hydrate() {
18+
div_resize_listener = addResizeListener(div, div_resize_handler);
19+
component._beforecreate.push(div_resize_handler);
20+
},
21+
22+
m: function mount(target, anchor) {
23+
insertNode(div, target, anchor);
24+
},
25+
26+
p: noop,
27+
28+
u: function unmount() {
29+
div_resize_listener.cancel();
30+
detachNode(div);
31+
},
32+
33+
d: noop
34+
};
35+
}
36+
37+
function SvelteComponent(options) {
38+
init(this, options);
39+
this._state = assign({}, options.data);
40+
41+
this._fragment = create_main_fragment(this, this._state);
42+
43+
if (options.target) {
44+
this._fragment.c();
45+
this._mount(options.target, options.anchor);
46+
}
47+
}
48+
49+
assign(SvelteComponent.prototype, proto);
50+
export default SvelteComponent;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<div bind:width=w bind:height=h>
2+
some content
3+
</div>

0 commit comments

Comments
 (0)