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

Commit

Permalink
feat(mdDialog): added openFrom and closeTo properties
Browse files Browse the repository at this point in the history
openFrom specifies the origin of the transition animation
closeTo specifies the target of the transition animation

both except string (query selector), element and Rect object

closes #4228
  • Loading branch information
EladBezalel committed Oct 13, 2015
1 parent 9b918cb commit f04c1d4
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 28 deletions.
30 changes: 30 additions & 0 deletions src/components/dialog/demoOpenFromCloseTo/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<div ng-controller="AppCtrl" layout="row" ng-cloak style="height: 300px">
<div layout="column" layout-align="center center"
style="background-color: #f2f2f2" class="md-padding">
<span id="left">left</span>
</div>
<div layout="column" layout-align="center">
<p class="inset">
A dialog can specify its origin and target with <code>openFrom</code> and
<code>closeTo</code> properties.

The options showFrom and closeTo can be specified as a string [where internally
querySelector( ) is used to perform the DOM element lookup],
element or an Rect object [with top, left, width, height fields].
</p>

<div class="dialog-demo-content" layout="row" layout-wrap>
<md-button class="md-primary md-raised" ng-click="openFromLeft()" flex flex-md="100">
Open From Left Close To Right
</md-button>
<md-button class="md-primary md-raised" ng-click="openOffscreen()" flex flex-md="100">
From Offscreen
</md-button>
</div>
</div>

<div layout="column" layout-align="center center"
style="background-color: #f2f2f2" class="md-padding">
<span id="right">right</span>
</div>
</div>
38 changes: 38 additions & 0 deletions src/components/dialog/demoOpenFromCloseTo/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
angular.module('dialogDemo2', ['ngMaterial'])

.controller('AppCtrl', function($scope, $mdDialog) {
$scope.openFromLeft = function() {
$mdDialog.show(
$mdDialog.alert()
.clickOutsideToClose(true)
.title('Opening from the left')
.content('Closing to the right!')
.ariaLabel('Left to right demo')
.ok('Nice!')
// You can specify either sting with query selector
.openFrom('#left')
// or an element
.closeTo(angular.element(document.querySelector('#right')))
);
};

$scope.openOffscreen = function() {
$mdDialog.show(
$mdDialog.alert()
.clickOutsideToClose(true)
.title('Opening from offscreen')
.content('Closing to offscreen')
.ariaLabel('Offscreen Demo')
.ok('Amazing!')
// Or you can specify the rect to do the transition from
.openFrom({
top: -50,
width: 30,
height: 80
})
.closeTo({
left: 1500
})
);
};
});
95 changes: 69 additions & 26 deletions src/components/dialog/dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,12 @@ function MdDialogDirective($$rAF, $mdTheming, $mdDialog) {
* - `targetEvent` - `{DOMClickEvent=}`: A click's event object. When passed in as an option,
* the location of the click will be used as the starting point for the opening animation
* of the the dialog.
* - `openFrom` - `{string|Element|object}`: The query selector, DOM element or the Rect object
* that is used to determine the bounds (top, left, height, width) from which the Dialog will
* originate.
* - `closeTo` - `{string|Element|object}`: The query selector, DOM element or the Rect object
* that is used to determine the bounds (top, left, height, width) to which the Dialog will
* target.
* - `scope` - `{object=}`: the scope to link the template / controller to. If none is specified,
* it will create a new isolate scope.
* This scope will be destroyed when the dialog is removed unless `preserveScope` is set to true.
Expand Down Expand Up @@ -403,7 +409,7 @@ function MdDialogProvider($$interimElementProvider) {

return $$interimElementProvider('$mdDialog')
.setDefaults({
methods: ['disableParentScroll', 'hasBackdrop', 'clickOutsideToClose', 'escapeToClose', 'targetEvent', 'parent'],
methods: ['disableParentScroll', 'hasBackdrop', 'clickOutsideToClose', 'escapeToClose', 'targetEvent', 'closeTo', 'openFrom', 'parent'],
options: dialogDefaultOptions
})
.addPreset('alert', {
Expand Down Expand Up @@ -459,6 +465,8 @@ function MdDialogProvider($$interimElementProvider) {
clickOutsideToClose: false,
escapeToClose: true,
targetEvent: null,
closeTo: null,
openFrom: null,
focusOnOpen: true,
disableParentScroll: true,
transformTemplate: function(template) {
Expand All @@ -482,7 +490,7 @@ function MdDialogProvider($$interimElementProvider) {

wrapSimpleContent();

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

Expand Down Expand Up @@ -572,39 +580,67 @@ function MdDialogProvider($$interimElementProvider) {
}

/**
* Capture originator/trigger element information (if available)
* Capture originator/trigger/from/to element information (if available)
* and the parent container for the dialog; defaults to the $rootElement
* unless overridden in the options.parent
*/
function captureSourceAndParent(element, options) {
function captureParentAndFromToElements(options) {
options.origin = angular.extend({
element: null,
bounds: null,
focus: angular.noop
}, options.origin || {});

var source = angular.element((options.targetEvent || {}).target);
if (source && source.length) {
// Compute and save the target element's bounding rect, so that if the
// element is hidden when the dialog closes, we can shrink the dialog
// back to the same position it expanded from.
options.origin.element = source;
options.origin.bounds = source[0].getBoundingClientRect();
options.origin.focus = function() {
source.focus();
// Saving the target element bounding rect
var saveBoundingClientRect = function (elem, orig) {
var source = angular.element((elem || {}));
if (source && source.length) {
// Compute and save the target element's bounding rect, so that if the
// element is hidden when the dialog closes, we can shrink the dialog
// back to the same position it expanded from.
//
// Checking if the source is a rect object or a DOM element
var isDomElem = source[0].getBoundingClientRect;
var element = isDomElem ? source : undefined;
var defaultBnds = {top:0,left:0,height:0,width:0};
var bounds = isDomElem ?
source[0].getBoundingClientRect() :
angular.extend({}, defaultBnds, source[0]);

return angular.extend(orig || {}, {
element: element,
bounds: bounds,
focus: function () {
source.focus();
}
});
}
}
};

// If the parent specifier is a simple string selector, then query for
// the DOM element.
if ( angular.isString(options.parent) ) {
var simpleSelector = options.parent,
var getDomElement = function (elem, defaultElement) {
// If the specifier is a simple string selector, then query for
// the DOM element.
if (angular.isString(elem)) {
var simpleSelector = elem,
container = $document[0].querySelectorAll(simpleSelector);
options.parent = container.length ? container[0] : null;
elem = container.length ? container[0] : null;
}

// If we have a reference to a raw dom element, always wrap it in jqLite
return angular.element(elem || defaultElement);
};

if(options.targetEvent) {
options.origin = saveBoundingClientRect(options.targetEvent.target, options.origin);
}
// If we have a reference to a raw dom element, always wrap it in jqLite
options.parent = angular.element(options.parent || $rootElement);

options.parent = getDomElement(options.parent, $rootElement);

options.closeTo = getDomElement(options.closeTo);
options.closeTo = saveBoundingClientRect(options.closeTo);

options.openFrom = getDomElement(options.openFrom);
options.openFrom = saveBoundingClientRect(options.openFrom);
}

/**
Expand Down Expand Up @@ -831,19 +867,26 @@ function MdDialogProvider($$interimElementProvider) {
var animator = $mdUtil.dom.animator;
var buildTranslateToOrigin = animator.calculateZoomToOrigin;
var translateOptions = {transitionInClass: 'md-transition-in', transitionOutClass: 'md-transition-out'};
var from = animator.toTransformCss(buildTranslateToOrigin(dialogEl, options.origin));
var from = animator.toTransformCss(buildTranslateToOrigin(dialogEl, options.openFrom || options.origin));
var to = animator.toTransformCss(""); // defaults to center display (or parent or $rootElement)

return animator
.translate3d(dialogEl, from, to, translateOptions)
.then(function(animateReversal) {



// Build a reversal translate function synched to this translation...
options.reverseAnimate = function() {

delete options.reverseAnimate;

if (options.closeTo) {
// Using the opposite classes to create a close animation to the closeTo element
translateOptions = {transitionInClass: 'md-transition-out', transitionOutClass: 'md-transition-in'};
from = to;
to = animator.toTransformCss(buildTranslateToOrigin(dialogEl, options.closeTo));

return animator
.translate3d(dialogEl, from, to,translateOptions);
}

return animateReversal(
animator.toTransformCss(
// in case the origin element has moved or is hidden,
Expand Down
5 changes: 3 additions & 2 deletions src/core/util/animation/animate.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,13 @@ function AnimateDomUtils($mdUtil, $q, $timeout, $mdConstant, $animateCss) {
*/
calculateZoomToOrigin: function (element, originator) {
var origin = originator.element;
var bounds = originator.bounds;
var zoomTemplate = "translate3d( {centerX}px, {centerY}px, 0 ) scale( {scaleX}, {scaleY} )";
var buildZoom = angular.bind(null, $mdUtil.supplant, zoomTemplate);
var zoomStyle = buildZoom({centerX: 0, centerY: 0, scaleX: 0.5, scaleY: 0.5});

if (origin) {
var originBnds = self.clientRect(origin) || self.copyRect(originator.bounds);
if (origin || bounds) {
var originBnds = origin ? self.clientRect(origin) : self.copyRect(bounds);
var dialogRect = self.copyRect(element[0].getBoundingClientRect());
var dialogCenterPt = self.centerPointFor(dialogRect);
var originCenterPt = self.centerPointFor(originBnds);
Expand Down

0 comments on commit f04c1d4

Please sign in to comment.