add infrastructure for bindings on <:Window>

pull/391/head
Rich-Harris 8 years ago
parent d7c5dfbb06
commit 314a8b7622

@ -19,6 +19,11 @@ class DomGenerator extends Generator {
this.importedNames = {};
this.aliases = {};
// initial values for e.g. window.innerWidth, if there's a <:Window> meta tag
this.builders = {
metaBindings: new CodeBuilder()
};
this.importedComponents = {};
}
@ -354,6 +359,10 @@ export default function dom ( parsed, source, options, names ) {
`this._state = ${templateProperties.data ? `Object.assign( template.data(), options.data )` : `options.data || {}`};`
);
if ( !generator.builders.metaBindings.isEmpty() ) {
constructorBlock.addBlock( generator.builders.metaBindings );
}
if ( templateProperties.computed ) {
constructorBlock.addLine(
`${generator.alias( 'applyComputations' )}( this._state, this._state, {}, true );`

@ -1,8 +1,20 @@
import flattenReference from '../../../../utils/flattenReference.js';
import deindent from '../../../../utils/deindent.js';
const associatedEvents = {
innerWidth: 'resize',
innerHeight: 'resize',
outerWidth: 'resize',
outerHeight: 'resize',
scrollX: 'scroll',
scrollY: 'scroll'
};
export default {
enter ( generator, node ) {
const events = {};
node.attributes.forEach( attribute => {
if ( attribute.type === 'EventHandler' ) {
// TODO verify that it's a valid callee (i.e. built-in or declared method)
@ -27,6 +39,46 @@ export default {
window.removeEventListener( '${attribute.name}', ${handlerName} );
` );
}
if ( attribute.type === 'Binding' ) {
const associatedEvent = associatedEvents[ attribute.name ];
if ( !associatedEvent ) {
throw new Error( `Cannot bind to ${attribute.name} on <:Window>` );
}
if ( attribute.value.type !== 'Identifier' ) {
const { parts, keypath } = flattenReference( attribute.value );
throw new Error( `Bindings on <:Window/> must be to top-level properties, e.g. '${parts.pop()}' rather than '${keypath}'` );
}
if ( !events[ associatedEvent ] ) events[ associatedEvent ] = [];
events[ associatedEvent ].push( `${attribute.value.name}: this.${attribute.name}` );
// add initial value
generator.builders.metaBindings.addLine(
`this._state.${attribute.value.name} = window.${attribute.name};`
);
}
});
Object.keys( events ).forEach( event => {
const handlerName = generator.current.getUniqueName( `onwindow${event}` );
const props = events[ event ].join( ',\n' );
generator.current.builders.init.addBlock( deindent`
var ${handlerName} = function ( event ) {
component.set({
${props}
});
};
window.addEventListener( '${event}', ${handlerName} );
` );
generator.current.builders.teardown.addBlock( deindent`
window.removeEventListener( '${event}', ${handlerName} );
` );
});
}
};

@ -0,0 +1,29 @@
export default {
html: `<div>1024x768</div>`,
skip: /^v4/.test( process.version ), // node 4 apparently does some dumb stuff
'skip-ssr': true, // there's some kind of weird bug with this test... it compiles with the wrong require.extensions hook for some bizarre reason
test ( assert, component, target, window ) {
const event = new window.Event( 'resize' );
// JSDOM executes window event listeners with `global` rather than
// `window` (bug?) so we need to do this
Object.defineProperties( global, {
innerWidth: {
value: 567,
configurable: true
},
innerHeight: {
value: 456,
configurable: true
}
});
window.dispatchEvent( event );
assert.htmlEqual( target.innerHTML, `
<div>567x456</div>
`);
}
};

@ -0,0 +1,3 @@
<:Window bind:innerWidth='width' bind:innerHeight='height'/>
<div>{{width}}x{{height}}</div>
Loading…
Cancel
Save