diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js index ebf478f5a2..004cb6b763 100644 --- a/src/generators/dom/index.js +++ b/src/generators/dom/index.js @@ -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 );` diff --git a/src/generators/dom/visitors/meta/Window.js b/src/generators/dom/visitors/meta/Window.js index dcda89c247..e1d5cb7b43 100644 --- a/src/generators/dom/visitors/meta/Window.js +++ b/src/generators/dom/visitors/meta/Window.js @@ -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} ); + ` ); }); } }; diff --git a/test/generator/samples/window-binding-resize/_config.js b/test/generator/samples/window-binding-resize/_config.js new file mode 100644 index 0000000000..ee991552a7 --- /dev/null +++ b/test/generator/samples/window-binding-resize/_config.js @@ -0,0 +1,29 @@ +export default { + html: `