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

Commit 71e23e5

Browse files
EladBezalelThomasBurleson
authored andcommitted
feat(mdDialog): added openFrom and closeTo properties
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 fixes #4228. closes #5075.
1 parent 2ecbb8f commit 71e23e5

File tree

4 files changed

+147
-38
lines changed

4 files changed

+147
-38
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<div ng-controller="AppCtrl" layout="row" ng-cloak style="height: 300px">
2+
<div layout="column" layout-align="center center"
3+
style="background-color: #f2f2f2" class="md-padding">
4+
<span id="left">left</span>
5+
</div>
6+
<div layout="column" layout-align="center">
7+
<p class="inset">
8+
A dialog can specify its origin and target with <code>openFrom</code> and
9+
<code>closeTo</code> properties.
10+
11+
The options showFrom and closeTo can be specified as a string [where internally
12+
querySelector( ) is used to perform the DOM element lookup],
13+
element or an Rect object [with top, left, width, height fields].
14+
</p>
15+
16+
<div class="dialog-demo-content" layout="row" layout-wrap>
17+
<md-button class="md-primary md-raised" ng-click="openFromLeft()" flex flex-md="100">
18+
Open From Left Close To Right
19+
</md-button>
20+
<md-button class="md-primary md-raised" ng-click="openOffscreen()" flex flex-md="100">
21+
From Offscreen
22+
</md-button>
23+
</div>
24+
</div>
25+
26+
<div layout="column" layout-align="center center"
27+
style="background-color: #f2f2f2" class="md-padding">
28+
<span id="right">right</span>
29+
</div>
30+
</div>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
angular.module('dialogDemo2', ['ngMaterial'])
2+
3+
.controller('AppCtrl', function($scope, $mdDialog) {
4+
$scope.openFromLeft = function() {
5+
$mdDialog.show(
6+
$mdDialog.alert()
7+
.clickOutsideToClose(true)
8+
.title('Opening from the left')
9+
.content('Closing to the right!')
10+
.ariaLabel('Left to right demo')
11+
.ok('Nice!')
12+
// You can specify either sting with query selector
13+
.openFrom('#left')
14+
// or an element
15+
.closeTo(angular.element(document.querySelector('#right')))
16+
);
17+
};
18+
19+
$scope.openOffscreen = function() {
20+
$mdDialog.show(
21+
$mdDialog.alert()
22+
.clickOutsideToClose(true)
23+
.title('Opening from offscreen')
24+
.content('Closing to offscreen')
25+
.ariaLabel('Offscreen Demo')
26+
.ok('Amazing!')
27+
// Or you can specify the rect to do the transition from
28+
.openFrom({
29+
top: -50,
30+
width: 30,
31+
height: 80
32+
})
33+
.closeTo({
34+
left: 1500
35+
})
36+
);
37+
};
38+
});

src/components/dialog/dialog.js

Lines changed: 76 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,12 @@ function MdDialogDirective($$rAF, $mdTheming, $mdDialog) {
336336
* - `targetEvent` - `{DOMClickEvent=}`: A click's event object. When passed in as an option,
337337
* the location of the click will be used as the starting point for the opening animation
338338
* of the the dialog.
339+
* - `openFrom` - `{string|Element|object}`: The query selector, DOM element or the Rect object
340+
* that is used to determine the bounds (top, left, height, width) from which the Dialog will
341+
* originate.
342+
* - `closeTo` - `{string|Element|object}`: The query selector, DOM element or the Rect object
343+
* that is used to determine the bounds (top, left, height, width) to which the Dialog will
344+
* target.
339345
* - `scope` - `{object=}`: the scope to link the template / controller to. If none is specified,
340346
* it will create a new isolate scope.
341347
* This scope will be destroyed when the dialog is removed unless `preserveScope` is set to true.
@@ -403,7 +409,7 @@ function MdDialogProvider($$interimElementProvider) {
403409

404410
return $$interimElementProvider('$mdDialog')
405411
.setDefaults({
406-
methods: ['disableParentScroll', 'hasBackdrop', 'clickOutsideToClose', 'escapeToClose', 'targetEvent', 'parent'],
412+
methods: ['disableParentScroll', 'hasBackdrop', 'clickOutsideToClose', 'escapeToClose', 'targetEvent', 'closeTo', 'openFrom', 'parent'],
407413
options: dialogDefaultOptions
408414
})
409415
.addPreset('alert', {
@@ -459,6 +465,8 @@ function MdDialogProvider($$interimElementProvider) {
459465
clickOutsideToClose: false,
460466
escapeToClose: true,
461467
targetEvent: null,
468+
closeTo: null,
469+
openFrom: null,
462470
focusOnOpen: true,
463471
disableParentScroll: true,
464472
transformTemplate: function(template) {
@@ -482,7 +490,7 @@ function MdDialogProvider($$interimElementProvider) {
482490

483491
wrapSimpleContent();
484492

485-
captureSourceAndParent(element, options);
493+
captureParentAndFromToElements(options);
486494
configureAria(element.find('md-dialog'), options);
487495
showBackdrop(scope, element, options);
488496

@@ -572,40 +580,65 @@ function MdDialogProvider($$interimElementProvider) {
572580
}
573581

574582
/**
575-
* Capture originator/trigger element information (if available)
583+
* Capture originator/trigger/from/to element information (if available)
576584
* and the parent container for the dialog; defaults to the $rootElement
577585
* unless overridden in the options.parent
578586
*/
579-
function captureSourceAndParent(element, options) {
580-
options.origin = angular.extend({
581-
element: null,
582-
bounds: null,
583-
focus: angular.noop
584-
}, options.origin || {});
585-
586-
var source = angular.element((options.targetEvent || {}).target);
587-
if (source && source.length) {
588-
// Compute and save the target element's bounding rect, so that if the
589-
// element is hidden when the dialog closes, we can shrink the dialog
590-
// back to the same position it expanded from.
591-
options.origin.element = source;
592-
options.origin.bounds = source[0].getBoundingClientRect();
593-
options.origin.focus = function() {
594-
source.focus();
595-
}
596-
}
587+
function captureParentAndFromToElements(options) {
588+
options.origin = angular.extend({
589+
element: null,
590+
bounds: null,
591+
focus: angular.noop
592+
}, options.origin || {});
593+
594+
options.parent = getDomElement(options.parent, $rootElement);
595+
options.closeTo = getBoundingClientRect(getDomElement(options.closeTo));
596+
options.openFrom = getBoundingClientRect(getDomElement(options.openFrom));
597+
598+
if ( options.targetEvent ) {
599+
options.origin = getBoundingClientRect(options.targetEvent.target, options.origin);
600+
}
597601

598-
// If the parent specifier is a simple string selector, then query for
599-
// the DOM element.
600-
if ( angular.isString(options.parent) ) {
601-
var simpleSelector = options.parent,
602-
container = $document[0].querySelectorAll(simpleSelector);
603-
options.parent = container.length ? container[0] : null;
604-
}
605-
// If we have a reference to a raw dom element, always wrap it in jqLite
606-
options.parent = angular.element(options.parent || $rootElement);
602+
/**
603+
* Identify the bounding RECT for the target element
604+
*
605+
*/
606+
function getBoundingClientRect (element, orig) {
607+
var source = angular.element((element || {}));
608+
if (source && source.length) {
609+
// Compute and save the target element's bounding rect, so that if the
610+
// element is hidden when the dialog closes, we can shrink the dialog
611+
// back to the same position it expanded from.
612+
//
613+
// Checking if the source is a rect object or a DOM element
614+
var bounds = {top:0,left:0,height:0,width:0};
615+
var hasFn = angular.isFunction(source[0].getBoundingClientRect);
616+
617+
return angular.extend(orig || {}, {
618+
element : hasFn ? source : undefined,
619+
bounds : hasFn ? source[0].getBoundingClientRect() : angular.extend({}, bounds, source[0]),
620+
focus : angular.bind(source, source.focus),
621+
});
622+
}
623+
}
624+
625+
/**
626+
* If the specifier is a simple string selector, then query for
627+
* the DOM element.
628+
*/
629+
function getDomElement(element, defaultElement) {
630+
if (angular.isString(element)) {
631+
var simpleSelector = element,
632+
container = $document[0].querySelectorAll(simpleSelector);
633+
element = container.length ? container[0] : null;
634+
}
635+
636+
// If we have a reference to a raw dom element, always wrap it in jqLite
637+
return angular.element(element || defaultElement);
638+
}
607639

608-
}
640+
641+
}
609642

610643
/**
611644
* Listen for escape keys and outside clicks to auto close
@@ -831,19 +864,26 @@ function MdDialogProvider($$interimElementProvider) {
831864
var animator = $mdUtil.dom.animator;
832865
var buildTranslateToOrigin = animator.calculateZoomToOrigin;
833866
var translateOptions = {transitionInClass: 'md-transition-in', transitionOutClass: 'md-transition-out'};
834-
var from = animator.toTransformCss(buildTranslateToOrigin(dialogEl, options.origin));
867+
var from = animator.toTransformCss(buildTranslateToOrigin(dialogEl, options.openFrom || options.origin));
835868
var to = animator.toTransformCss(""); // defaults to center display (or parent or $rootElement)
836869

837870
return animator
838871
.translate3d(dialogEl, from, to, translateOptions)
839872
.then(function(animateReversal) {
840-
841-
842-
843873
// Build a reversal translate function synched to this translation...
844874
options.reverseAnimate = function() {
845-
846875
delete options.reverseAnimate;
876+
877+
if (options.closeTo) {
878+
// Using the opposite classes to create a close animation to the closeTo element
879+
translateOptions = {transitionInClass: 'md-transition-out', transitionOutClass: 'md-transition-in'};
880+
from = to;
881+
to = animator.toTransformCss(buildTranslateToOrigin(dialogEl, options.closeTo));
882+
883+
return animator
884+
.translate3d(dialogEl, from, to,translateOptions);
885+
}
886+
847887
return animateReversal(
848888
animator.toTransformCss(
849889
// in case the origin element has moved or is hidden,

src/core/util/animation/animate.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,13 @@ function AnimateDomUtils($mdUtil, $q, $timeout, $mdConstant, $animateCss) {
8787
*/
8888
calculateZoomToOrigin: function (element, originator) {
8989
var origin = originator.element;
90+
var bounds = originator.bounds;
9091
var zoomTemplate = "translate3d( {centerX}px, {centerY}px, 0 ) scale( {scaleX}, {scaleY} )";
9192
var buildZoom = angular.bind(null, $mdUtil.supplant, zoomTemplate);
9293
var zoomStyle = buildZoom({centerX: 0, centerY: 0, scaleX: 0.5, scaleY: 0.5});
9394

94-
if (origin) {
95-
var originBnds = self.clientRect(origin) || self.copyRect(originator.bounds);
95+
if (origin || bounds) {
96+
var originBnds = origin ? self.clientRect(origin) : self.copyRect(bounds);
9697
var dialogRect = self.copyRect(element[0].getBoundingClientRect());
9798
var dialogCenterPt = self.centerPointFor(dialogRect);
9899
var originCenterPt = self.centerPointFor(originBnds);

0 commit comments

Comments
 (0)