From 783cd383e302f6b864f32cc2199d6151d40ba7f2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 10 Apr 2017 16:08:19 -0400 Subject: [PATCH] window scroll bindings are bidirectional (#404) --- .../dom/visitors/Element/meta/Window.js | 53 +++++++++++++++++-- .../window-bind-scroll-update/_config.js | 12 +++++ .../window-bind-scroll-update/main.html | 3 ++ 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 test/runtime/samples/window-bind-scroll-update/_config.js create mode 100644 test/runtime/samples/window-bind-scroll-update/main.html diff --git a/src/generators/dom/visitors/Element/meta/Window.js b/src/generators/dom/visitors/Element/meta/Window.js index c0f9f3d181..837578ef69 100644 --- a/src/generators/dom/visitors/Element/meta/Window.js +++ b/src/generators/dom/visitors/Element/meta/Window.js @@ -1,5 +1,6 @@ import flattenReference from '../../../../../utils/flattenReference.js'; import deindent from '../../../../../utils/deindent.js'; +import CodeBuilder from '../../../../../utils/CodeBuilder.js'; const associatedEvents = { innerWidth: 'resize', @@ -13,6 +14,7 @@ const associatedEvents = { export default function visitWindow ( generator, block, node ) { const events = {}; + const bindings = {}; node.attributes.forEach( attribute => { if ( attribute.type === 'EventHandler' ) { @@ -51,6 +53,8 @@ export default function visitWindow ( generator, block, node ) { throw new Error( `Bindings on <:Window/> must be to top-level properties, e.g. '${parts.pop()}' rather than '${keypath}'` ); } + bindings[ attribute.name ] = attribute.value.name; + if ( !events[ associatedEvent ] ) events[ associatedEvent ] = []; events[ associatedEvent ].push( `${attribute.value.name}: this.${attribute.name}` ); @@ -61,16 +65,31 @@ export default function visitWindow ( generator, block, node ) { } }); + const lock = block.getUniqueName( `window_updating` ); + Object.keys( events ).forEach( event => { const handlerName = block.getUniqueName( `onwindow${event}` ); - const props = events[ event ].join( ',\n' ); + const handlerBody = new CodeBuilder(); + if ( event === 'scroll' ) { // TODO other bidirectional bindings... + block.builders.create.addLine( `var ${lock} = false;` ); + handlerBody.addLine( `${lock} = true;` ); + } + + handlerBody.addBlock( deindent` + component.set({ + ${props} + }); + ` ); + + if ( event === 'scroll' ) { + handlerBody.addLine( `${lock} = false;` ); + } + block.builders.create.addBlock( deindent` var ${handlerName} = function ( event ) { - component.set({ - ${props} - }); + ${handlerBody} }; window.addEventListener( '${event}', ${handlerName} ); ` ); @@ -79,4 +98,30 @@ export default function visitWindow ( generator, block, node ) { window.removeEventListener( '${event}', ${handlerName} ); ` ); }); + + // special case... might need to abstract this out if we add more special cases + if ( bindings.scrollX && bindings.scrollY ) { + const observerCallback = block.getUniqueName( `scrollobserver` ); + + block.builders.create.addBlock( deindent` + function ${observerCallback} () { + if ( ${lock} ) return; + var x = ${bindings.scrollX ? `component.get( '${bindings.scrollX}' )` : `window.scrollX`}; + var y = ${bindings.scrollY ? `component.get( '${bindings.scrollY}' )` : `window.scrollY`}; + window.scrollTo( x, y ); + }; + ` ); + + if ( bindings.scrollX ) block.builders.create.addLine( `component.observe( '${bindings.scrollX}', ${observerCallback} );` ); + if ( bindings.scrollY ) block.builders.create.addLine( `component.observe( '${bindings.scrollY}', ${observerCallback} );` ); + } else if ( bindings.scrollX || bindings.scrollY ) { + const isX = !!bindings.scrollX; + + block.builders.create.addBlock( deindent` + component.observe( '${bindings.scrollX || bindings.scrollY}', function ( ${isX ? 'x' : 'y'} ) { + if ( ${lock} ) return; + window.scrollTo( ${isX ? 'x, window.scrollY' : 'window.scrollX, y' } ); + }); + ` ); + } } \ No newline at end of file diff --git a/test/runtime/samples/window-bind-scroll-update/_config.js b/test/runtime/samples/window-bind-scroll-update/_config.js new file mode 100644 index 0000000000..3ae13c33ce --- /dev/null +++ b/test/runtime/samples/window-bind-scroll-update/_config.js @@ -0,0 +1,12 @@ +export default { + skip: true, // JSDOM + + test ( assert, component, target, window ) { + assert.equal( window.scrollY, 0 ); + + component.set({ scrollY: 100 }); + assert.equal( window.scrollY, 100 ); + + component.destroy(); + } +}; \ No newline at end of file diff --git a/test/runtime/samples/window-bind-scroll-update/main.html b/test/runtime/samples/window-bind-scroll-update/main.html new file mode 100644 index 0000000000..a95028fb7a --- /dev/null +++ b/test/runtime/samples/window-bind-scroll-update/main.html @@ -0,0 +1,3 @@ +<:Window bind:scrollY/> + +
\ No newline at end of file