From 1c5c13fd16be5293816487b9316a1153c5d6894b Mon Sep 17 00:00:00 2001 From: Timo Tijhof Date: Mon, 23 Feb 2015 08:44:51 +0000 Subject: [PATCH] mw.ViewPageTarget: Use CSS for toolbar transition The toolbar now uses a CSS transition for its height instead of JavaScript animation through slideDown(). * The animation is on toolbar instead of toolbar-bar so that it contains the padding and borders. Otherwise it slides up until there is the top and left (quite thick) borders stacked on each other which then disappear at once. * Add transform/translateY so that toolbar also transitions when it is in floating state. * Move class addition to attachToolbar() to avoid additional reflow. Bug: T89543 Change-Id: I30a7b69b77b874d220f60ebe7f7e616cd77bcc36 --- .../ve.init.mw.ViewPageTarget-vector.css | 8 +++ .../init/styles/ve.init.mw.ViewPageTarget.css | 15 +++++ .../init/targets/ve.init.mw.ViewPageTarget.js | 57 +++++++++++-------- 3 files changed, 57 insertions(+), 23 deletions(-) diff --git a/modules/ve-mw/init/styles/ve.init.mw.ViewPageTarget-vector.css b/modules/ve-mw/init/styles/ve.init.mw.ViewPageTarget-vector.css index f9b793bd78..7d0657a798 100644 --- a/modules/ve-mw/init/styles/ve.init.mw.ViewPageTarget-vector.css +++ b/modules/ve-mw/init/styles/ve.init.mw.ViewPageTarget-vector.css @@ -34,6 +34,14 @@ .ve-init-mw-viewPageTarget-toolbar { margin: -1em -1em 1em -1em; position: relative; + border-bottom: 1px solid rgba(0,0,0,0.15); +} + +.ve-init-mw-viewPageTarget-toolbar > .oo-ui-toolbar-bar { + /* Upstream OOjs UI has 4px. We split this between 3px here + and 1px on the container to allow a smooth collapse transition + where the bottom pixel line sweeps it up. */ + border-bottom: 3px solid rgba(0,0,0,0.15); } .ve-ui-debugBar { diff --git a/modules/ve-mw/init/styles/ve.init.mw.ViewPageTarget.css b/modules/ve-mw/init/styles/ve.init.mw.ViewPageTarget.css index 6474fbe380..cd651ef037 100644 --- a/modules/ve-mw/init/styles/ve.init.mw.ViewPageTarget.css +++ b/modules/ve-mw/init/styles/ve.init.mw.ViewPageTarget.css @@ -42,6 +42,21 @@ /* Toolbar */ +.ve-activating .ve-init-mw-viewPageTarget-toolbar, +.ve-deactivating .ve-init-mw-viewPageTarget-toolbar { + overflow: hidden; + transition: height 0.4s ease; +} + +.ve-ui-toolbar-floating .oo-ui-toolbar-bar { + transform: translateY(-100%); + transition: transform 0.4s ease; +} + +.ve-active .ve-ui-toolbar-floating .oo-ui-toolbar-bar { + transform: translateY(0); +} + .ve-init-mw-viewPageTarget-toolbar-utilities, .ve-init-mw-viewPageTarget-toolbar-actions { display: inline-block; diff --git a/modules/ve-mw/init/targets/ve.init.mw.ViewPageTarget.js b/modules/ve-mw/init/targets/ve.init.mw.ViewPageTarget.js index a0a137a2fe..fa97f3d776 100644 --- a/modules/ve-mw/init/targets/ve.init.mw.ViewPageTarget.js +++ b/modules/ve-mw/init/targets/ve.init.mw.ViewPageTarget.js @@ -41,6 +41,8 @@ ve.init.mw.ViewPageTarget = function VeInitMwViewPageTarget() { this.deactivating = false; this.edited = false; this.recreating = false; + this.activatingDeferred = null; + this.toolbarSetupDeferred = null; // If this is true then #transformPage / #restorePage will not call pushState // This is to avoid adding a new history entry for the url we just got from onpopstate // (which would mess up with the expected order of Back/Forwards browsing) @@ -173,9 +175,9 @@ ve.init.mw.ViewPageTarget.prototype.verifyPopState = function ( popState ) { * @inheritdoc */ ve.init.mw.ViewPageTarget.prototype.setupToolbar = function ( surface ) { - var initPromise, toolbar, - target = this, - wasSetup = !!this.toolbar; + var toolbar, + wasSetup = !!this.toolbar, + target = this; ve.track( 'trace.setupToolbar.enter' ); @@ -186,20 +188,18 @@ ve.init.mw.ViewPageTarget.prototype.setupToolbar = function ( surface ) { ve.track( 'trace.setupToolbar.exit' ); if ( !wasSetup ) { - // Keep it hidden so that we can slide it down smoothly (avoids sudden - // offset flash when original content is hidden, and replaced in-place with a - // similar-looking surface). - // FIXME: This is not ideal, the parent class creates it and appends - // to target (visibly), only for us to hide it again 0ms later. - // Though we can't hide it by default because it needs visible dimensions - // to compute stuff during setup. - this.getToolbar().$bar.hide(); - initPromise = toolbar.$bar.slideDown( 'fast' ).promise(); - } else { - initPromise = $.Deferred().resolve(); + setTimeout( function () { + var height = toolbar.$bar.outerHeight(); + toolbar.$element.css( 'height', height ); + toolbar.$element.one( 'transitionend', function () { + // Clear to allow growth during use and when resizing window + toolbar.$element.css( 'height', '' ); + target.toolbarSetupDeferred.resolve(); + } ); + } ); } - initPromise.done( function () { + this.toolbarSetupDeferred.done( function () { var surface = target.getSurface(); // Check the surface wasn't torn down while the toolbar was animating if ( surface ) { @@ -218,9 +218,12 @@ ve.init.mw.ViewPageTarget.prototype.setupToolbar = function ( surface ) { ve.init.mw.ViewPageTarget.prototype.attachToolbar = function () { // Move the toolbar to top of target, before heading etc. // Avoid re-attaching as it breaks CSS animations - if ( !this.getToolbar().$element.parent().is( this.$element ) ) { - this.getToolbar().$element.addClass( 've-init-mw-viewPageTarget-toolbar' ); - this.$element.prepend( this.getToolbar().$element ); + if ( !this.toolbar.$element.parent().is( this.$element ) ) { + this.toolbar.$element + // Set 0 before attach (expanded in #setupToolbar) + .css( 'height', '0' ) + .addClass( 've-init-mw-viewPageTarget-toolbar' ); + this.$element.prepend( this.toolbar.$element ); } }; @@ -291,9 +294,10 @@ ve.init.mw.ViewPageTarget.prototype.activate = function () { ve.track( 'trace.activate.enter' ); this.activating = true; this.activatingDeferred = $.Deferred(); + this.toolbarSetupDeferred = $.Deferred(); $( 'html' ).addClass( 've-activating ve-activated' ); - this.activatingDeferred.always( function () { + $.when( this.activatingDeferred, this.toolbarSetupDeferred ).always( function () { $( 'html' ).removeClass( 've-activating' ).addClass( 've-active' ); } ); @@ -1304,11 +1308,18 @@ ve.init.mw.ViewPageTarget.prototype.restoreScrollPosition = function () { * @return {jQuery.Promise} Promise which resolves when toolbar is hidden */ ve.init.mw.ViewPageTarget.prototype.tearDownToolbar = function () { - var target = this; - return this.toolbar.$bar.slideUp( 'fast' ).promise().then( function () { - target.toolbar.destroy(); - target.toolbar = null; + var target = this, + deferred = $.Deferred(); + this.toolbar.$element.css( 'height', this.toolbar.$bar.outerHeight() ); + setTimeout( function () { + target.toolbar.$element.css( 'height', '0' ); + target.toolbar.$element.one( 'transitionend', function () { + target.toolbar.destroy(); + target.toolbar = null; + deferred.resolve(); + } ); } ); + return deferred.promise(); }; /**