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

feat(mdDialog): added openFrom and closeTo properties #5075

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very confusing to read... I think a refactoring is needed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you suggest?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will fix.

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

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