Skip to content
This repository has been archived by the owner on Sep 5, 2024. It is now read-only.

Commit

Permalink
fix(backdrop, dialog, css): improve position and animations of backdrop
Browse files Browse the repository at this point in the history
improve backdrop animations and css to account for scroll and parenting:
-  backdrop
  - css uses transition and ng-enter/ng-leav
  - css transitions opacity instead of background-color
  - css no longer uses key frames
  - postLink uses $animate.pin if available
  - postLink logs a warning if the backdrop parent has a style 'position:static'
-  util
  - disableElementScroll uses specified element or body; used by dialog to disable dialog parent scrolling when parent is explicitly specified
  - refactor(util): centralize use of backdrop $compile
-  dialog
  - css for 'md-dialog-container' is now 'position:absolute'; 'fixed' is deprecated
  - hide backdrop now runs in parallel with hide dialog
  - basic demo #1 now uses element '#popupContainer' as parent; instead of document.body
  - basic demo #1 has #popupContainer 'position:relative' styling

Fixes #3826, Fixes #3828, Fixes #1967, Fixes #1106. Closes #3841.
  • Loading branch information
ThomasBurleson committed Jul 21, 2015
1 parent c5c148d commit 623496e
Show file tree
Hide file tree
Showing 17 changed files with 89 additions and 59 deletions.
2 changes: 1 addition & 1 deletion config/build.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ var fs = require('fs');
var versionFile = __dirname + '/../dist/commit';

module.exports = {
ngVersion: '1.3.15',
ngVersion: '1.4.3',
version: pkg.version,
repository: pkg.repository.url
.replace(/^git/,'https')
Expand Down
40 changes: 32 additions & 8 deletions src/components/backdrop/backdrop.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,35 @@
*
*/

angular.module('material.components.backdrop', [
'material.core'
])
.directive('mdBackdrop', BackdropDirective);

function BackdropDirective($mdTheming) {
return $mdTheming;
}
angular
.module('material.components.backdrop', ['material.core'])
.directive('mdBackdrop', function BackdropDirective($mdTheming, $animate, $rootElement, $window, $log, $$rAF) {

return {
restrict: 'E',
link: postLink
};

function postLink(scope, element, attrs) {
// backdrop may be outside the $rootElement, tell ngAnimate to animate regardless
if( $animate.pin ) $animate.pin(element,$rootElement);

$$rAF(function(){
// Often $animate.enter() is used to append the backDrop element
// so let's wait until $animate is done...

var parent = element.parent()[0];
if ( parent ) {
var position = $window.getComputedStyle(parent).getPropertyValue('position');
if (position == 'static') {
// backdrop uses position:absolute and will not work properly with parent position:static (default)
var positionError = "<md-backdrop> may not work properly in a scrolled, static-positioned parent container.";
$log.warn( positionError );
}
}

$mdTheming.inherit(element, element.parent());
});

};
});
25 changes: 14 additions & 11 deletions src/components/backdrop/backdrop.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ md-backdrop {
z-index: $z-index-sidenav - 1;
}

opacity: 1;
transition: opacity 600ms $swift-ease-in-timing-function;

background-color: rgba(0,0,0,0);

position: absolute;
Expand All @@ -25,27 +28,27 @@ md-backdrop {
right: 0;

&.md-click-catcher {
top: 0;
position: fixed;
}

&.ng-enter {
animation: $swift-ease-out-timing-function mdBackdropFadeIn 0.5s both;
opacity: 0;
&.ng-enter-active {
opacity: 1;
}
}

&.ng-leave {
animation: $swift-ease-in-timing-function mdBackdropFadeOut 0.4s both;
opacity: 1;
transition: opacity 400ms linear;

&.ng-leave-active {
opacity: 0;
}
}

&.md-opaque {
background-color: rgba(0,0,0,0.42);
}
}

@keyframes mdBackdropFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes mdBackdropFadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
4 changes: 2 additions & 2 deletions src/components/bottomSheet/bottomSheet.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ function MdBottomSheetProvider($$interimElementProvider) {
});

/* @ngInject */
function bottomSheetDefaults($animate, $mdConstant, $mdUtil, $compile, $mdTheming, $mdBottomSheet, $rootElement, $mdGesture) {
function bottomSheetDefaults($animate, $mdConstant, $mdUtil, $mdTheming, $mdBottomSheet, $rootElement, $mdGesture) {
var backdrop;

return {
Expand All @@ -143,7 +143,7 @@ function MdBottomSheetProvider($$interimElementProvider) {
element = $mdUtil.extractElementByName(element, 'md-bottom-sheet');

// Add a backdrop that will close on click
backdrop = $compile('<md-backdrop class="md-opaque md-bottom-sheet-backdrop">')(scope);
backdrop = $mdUtil.createBackdrop(scope, "md-bottom-sheet-backdrop md-opaque");
backdrop.on('click', function() {
$mdUtil.nextTick($mdBottomSheet.cancel,true);
});
Expand Down
4 changes: 2 additions & 2 deletions src/components/dialog/demoBasicUsage/index.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<div ng-controller="AppCtrl" class="md-padding">
<div ng-controller="AppCtrl" class="md-padding" id="popupContainer">
<p class="inset">
Open a dialog over the app's content. Press escape or click outside to close the dialog and
send focus back to the triggering button.
</p>

<div class="dialog-demo-content" layout="row" layout-wrap>
<div class="dialog-demo-content" layout="row" layout-wrap >
<md-button class="md-primary md-raised" ng-click="showAlert($event)" flex flex-md="100">
Alert Dialog
</md-button>
Expand Down
3 changes: 1 addition & 2 deletions src/components/dialog/demoBasicUsage/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ angular.module('dialogDemo1', ['ngMaterial'])
// to prevent interaction outside of dialog
$mdDialog.show(
$mdDialog.alert()
.parent(angular.element(document.body))
.parent(angular.element(document.querySelector('#popupContainer')))
.clickOutsideToClose(true)
.title('This is an alert title')
.content('You can specify some description text in here.')
Expand All @@ -22,7 +22,6 @@ angular.module('dialogDemo1', ['ngMaterial'])
$scope.showConfirm = function(ev) {
// Appending dialog to document.body to cover sidenav in docs app
var confirm = $mdDialog.confirm()
.parent(angular.element(document.body))
.title('Would you like to delete your debt?')
.content('All of the banks have agreed to forgive you your debts.')
.ariaLabel('Lucky day')
Expand Down
3 changes: 3 additions & 0 deletions src/components/dialog/demoBasicUsage/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#popupContainer {
position:relative;
}
16 changes: 7 additions & 9 deletions src/components/dialog/dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,7 @@ function MdDialogProvider($$interimElementProvider) {
}

/* @ngInject */
function dialogDefaultOptions($mdAria, $document, $mdUtil, $mdConstant, $mdTheming, $mdDialog, $animate, $q ) {

function dialogDefaultOptions($mdDialog, $mdAria, $mdUtil, $mdConstant, $animate, $document) {
return {
hasBackdrop: true,
isolateScope: true,
Expand All @@ -456,7 +455,7 @@ function MdDialogProvider($$interimElementProvider) {

captureSourceAndParent(element, options);
configureAria(element.find('md-dialog'), options);
showBackdrop(element, options);
showBackdrop(scope, element, options);

return dialogPopIn(element, options)
.then(function () {
Expand Down Expand Up @@ -492,10 +491,11 @@ function MdDialogProvider($$interimElementProvider) {
options.deactivateListeners();
options.unlockScreenReader();

options.hideBackdrop();

return dialogPopOut(element, options)
.finally(function () {
angular.element($document[0].body).removeClass('md-dialog-is-showing');
options.hideBackdrop();
element.remove();

options.origin.focus();
Expand All @@ -522,7 +522,7 @@ function MdDialogProvider($$interimElementProvider) {
options.parent = angular.element(options.parent);

if (options.disableParentScroll) {
options.restoreScroll = $mdUtil.disableScrollAround(element);
options.restoreScroll = $mdUtil.disableScrollAround(element,options.parent);
}
}

Expand Down Expand Up @@ -594,7 +594,7 @@ function MdDialogProvider($$interimElementProvider) {
/**
* Show modal backdrop element...
*/
function showBackdrop(element, options) {
function showBackdrop(scope, element, options) {

if (options.hasBackdrop) {
// Fix for IE 10
Expand All @@ -605,9 +605,7 @@ function MdDialogProvider($$interimElementProvider) {

element.css('top', parentOffset + 'px');

options.backdrop = angular.element('<md-backdrop class="md-dialog-backdrop md-opaque">');
options.backdrop.css('top', parentOffset + 'px');
$mdTheming.inherit(options.backdrop, options.parent);
options.backdrop = $mdUtil.createBackdrop(scope, "md-dialog-backdrop md-opaque");

$animate.enter(options.backdrop, options.parent);
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/dialog/dialog.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ $dialog-padding: $baseline-grid * 3;
display: flex;
justify-content: center;
align-items: center;
position: fixed;
position: absolute;
top: 0;
left: 0;
width: 100%;
Expand Down
1 change: 0 additions & 1 deletion src/components/menu/_menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ function MenuDirective($mdMenu) {
triggerElement = triggerElement.querySelector('[ng-click]');
}
triggerElement && triggerElement.setAttribute('aria-haspopup', 'true');
triggerElement.setAttribute('type', 'button');
if (templateElement.children().length != 2) {
throw Error('Invalid HTML for md-menu. Expected two children elements.');
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/menu/menu-interim-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ function MenuProvider($$interimElementProvider) {
target: angular.element(opts.target), //make sure it's not a naked dom node
parent: angular.element(opts.parent),
menuContentEl: angular.element(element[0].querySelector('md-menu-content')),
backdrop: opts.hasBackdrop && angular.element('<md-backdrop class="md-menu-backdrop md-click-catcher">')
backdrop: opts.hasBackdrop && $mdUtil.createBackdrop(scope, "md-menu-backdrop md-click-catcher")
});
}

Expand Down
5 changes: 0 additions & 5 deletions src/components/menu/menu.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,6 @@ describe('md-menu directive', function () {
expect(menu.firstElementChild.nodeName).toBe('BUTTON');
});

it('specifies button type', inject(function($compile, $rootScope) {
var menu = setup()[0];
expect(menu.firstElementChild.getAttribute('type')).toBe('button');
}));

it('opens on click', function () {
var menu = setup();
openMenu(menu);
Expand Down
6 changes: 2 additions & 4 deletions src/components/select/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,7 @@ function SelectProvider($$interimElementProvider) {
});

/* @ngInject */
function selectDefaultOptions($mdSelect, $mdConstant, $$rAF, $mdUtil, $mdTheming, $window, $q, $log ) {
function selectDefaultOptions($mdSelect, $mdConstant, $$rAF, $mdUtil, $mdTheming, $window, $q, $compile ) {
var animator = $mdUtil.dom.animator;

return {
Expand All @@ -774,7 +774,7 @@ function SelectProvider($$interimElementProvider) {
parent: angular.element(opts.parent),
selectEl: element.find('md-select-menu'),
contentEl: element.find('md-content'),
backdrop: opts.hasBackdrop && angular.element('<md-backdrop class="md-select-backdrop md-click-catcher">')
backdrop: opts.hasBackdrop && $mdUtil.createBackdrop(scope, "md-select-backdrop md-click-catcher")
});

opts.resizeFn = function() {
Expand Down Expand Up @@ -847,8 +847,6 @@ function SelectProvider($$interimElementProvider) {
element.addClass('md-clickable');

opts.backdrop && opts.backdrop.on('click', function(e) {
$log.debug("backdrop click");

e.preventDefault();
e.stopPropagation();
opts.restoreFocus = false;
Expand Down
6 changes: 2 additions & 4 deletions src/components/sidenav/sidenav.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ function SidenavFocusDirective() {
* - `<md-sidenav md-is-locked-open="$mdMedia('min-width: 1000px')"></md-sidenav>`
* - `<md-sidenav md-is-locked-open="$mdMedia('sm')"></md-sidenav>` (locks open on small screens)
*/
function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate, $parse, $log, $compile, $q, $document) {
function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate, $compile, $parse, $log, $q, $document) {
return {
restrict: 'E',
scope: {
Expand Down Expand Up @@ -240,9 +240,7 @@ function SidenavDirective($mdMedia, $mdUtil, $mdConstant, $mdTheming, $animate,
$mdMedia: $mdMedia
});
};
var backdrop = $compile(
'<md-backdrop class="md-sidenav-backdrop md-opaque ng-enter">'
)(scope);
var backdrop = $mdUtil.createBackdrop(scope, "md-sidenav-backdrop md-opaque ng-enter");

element.on('$destroy', sidenavCtrl.destroy);
$mdTheming.inherit(backdrop, element);
Expand Down
3 changes: 2 additions & 1 deletion src/core/services/compiler/compiler.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
angular.module('material.core')
angular
.module('material.core')
.service('$mdCompiler', mdCompilerService);

function mdCompilerService($q, $http, $injector, $compile, $controller, $templateCache) {
Expand Down
7 changes: 5 additions & 2 deletions src/core/services/interimElement/interimElement.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ describe('$$interimElement service', function() {

beforeEach(module('material.core'));

var $rootScope, $animate, $timeout;
var $rootScope, $animate, $q, $timeout;
var $compilerSpy, $themingSpy;

describe('provider', function() {
Expand Down Expand Up @@ -250,7 +250,10 @@ describe('$$interimElement service', function() {

beforeEach(function() {
setup();
inject(function($$interimElement) {
inject(function($$interimElement, _$q_, _$timeout_) {
$q = _$q_;
$timeout = _$timeout_;

Service = $$interimElement();

Service.show = tailHook(Service.show, flush);
Expand Down
19 changes: 14 additions & 5 deletions src/core/util/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
var nextUniqueId = 0;

angular.module('material.core')
.factory('$mdUtil', function ($cacheFactory, $document, $timeout, $q, $window, $mdConstant, $$rAF, $rootScope, $$mdAnimate) {
.factory('$mdUtil', function ($cacheFactory, $document, $timeout, $q, $compile, $window, $mdConstant, $$rAF, $rootScope, $$mdAnimate) {
var $mdUtil = {
dom : { },
now: window.performance ?
Expand Down Expand Up @@ -47,14 +47,14 @@ angular.module('material.core')
},

// Disables scroll around the passed element.
disableScrollAround: function (element) {
disableScrollAround: function (element, parent) {
$mdUtil.disableScrollAround._count = $mdUtil.disableScrollAround._count || 0;
++$mdUtil.disableScrollAround._count;
if ($mdUtil.disableScrollAround._enableScrolling) return $mdUtil.disableScrollAround._enableScrolling;
element = angular.element(element);
var body = $document[0].body,
restoreBody = disableBodyScroll(),
restoreElement = disableElementScroll();
restoreElement = disableElementScroll(parent);

return $mdUtil.disableScrollAround._enableScrolling = function () {
if (!--$mdUtil.disableScrollAround._count) {
Expand All @@ -65,13 +65,14 @@ angular.module('material.core')
};

// Creates a virtual scrolling mask to absorb touchmove, keyboard, scrollbar clicking, and wheel events
function disableElementScroll() {
function disableElementScroll(element) {
element = angular.element(element || body)[0];
var zIndex = 50;
var scrollMask = angular.element(
'<div class="md-scroll-mask" style="z-index: ' + zIndex + '">' +
' <div class="md-scroll-mask-bar"></div>' +
'</div>');
body.appendChild(scrollMask[0]);
element.appendChild(scrollMask[0]);

scrollMask.on('wheel', preventDefault);
scrollMask.on('touchmove', preventDefault);
Expand Down Expand Up @@ -174,6 +175,14 @@ angular.module('material.core')
node.dispatchEvent(newEvent);
},

/**
* facade to build md-backdrop element with desired styles
* NOTE: Use $compile to trigger backdrop postLink function
*/
createBackdrop : function(scope, addClass){
return $compile( $mdUtil.supplant('<md-backdrop class="{0}">',[addClass]) )(scope);
},

/**
* supplant() method from Crockford's `Remedial Javascript`
* Equivalent to use of $interpolate; without dependency on
Expand Down

0 comments on commit 623496e

Please sign in to comment.