@@ -336,6 +336,12 @@ function MdDialogDirective($$rAF, $mdTheming, $mdDialog) {
336
336
* - `targetEvent` - `{DOMClickEvent=}`: A click's event object. When passed in as an option,
337
337
* the location of the click will be used as the starting point for the opening animation
338
338
* 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.
339
345
* - `scope` - `{object=}`: the scope to link the template / controller to. If none is specified,
340
346
* it will create a new isolate scope.
341
347
* This scope will be destroyed when the dialog is removed unless `preserveScope` is set to true.
@@ -403,7 +409,7 @@ function MdDialogProvider($$interimElementProvider) {
403
409
404
410
return $$interimElementProvider ( '$mdDialog' )
405
411
. setDefaults ( {
406
- methods : [ 'disableParentScroll' , 'hasBackdrop' , 'clickOutsideToClose' , 'escapeToClose' , 'targetEvent' , 'parent' ] ,
412
+ methods : [ 'disableParentScroll' , 'hasBackdrop' , 'clickOutsideToClose' , 'escapeToClose' , 'targetEvent' , 'closeTo' , 'openFrom' , ' parent'] ,
407
413
options : dialogDefaultOptions
408
414
} )
409
415
. addPreset ( 'alert' , {
@@ -459,6 +465,8 @@ function MdDialogProvider($$interimElementProvider) {
459
465
clickOutsideToClose : false ,
460
466
escapeToClose : true ,
461
467
targetEvent : null ,
468
+ closeTo : null ,
469
+ openFrom : null ,
462
470
focusOnOpen : true ,
463
471
disableParentScroll : true ,
464
472
transformTemplate : function ( template ) {
@@ -482,7 +490,7 @@ function MdDialogProvider($$interimElementProvider) {
482
490
483
491
wrapSimpleContent ( ) ;
484
492
485
- captureSourceAndParent ( element , options ) ;
493
+ captureParentAndFromToElements ( options ) ;
486
494
configureAria ( element . find ( 'md-dialog' ) , options ) ;
487
495
showBackdrop ( scope , element , options ) ;
488
496
@@ -572,40 +580,65 @@ function MdDialogProvider($$interimElementProvider) {
572
580
}
573
581
574
582
/**
575
- * Capture originator/trigger element information (if available)
583
+ * Capture originator/trigger/from/to element information (if available)
576
584
* and the parent container for the dialog; defaults to the $rootElement
577
585
* unless overridden in the options.parent
578
586
*/
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
+ }
597
601
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
+ }
607
639
608
- }
640
+
641
+ }
609
642
610
643
/**
611
644
* Listen for escape keys and outside clicks to auto close
@@ -831,19 +864,26 @@ function MdDialogProvider($$interimElementProvider) {
831
864
var animator = $mdUtil . dom . animator ;
832
865
var buildTranslateToOrigin = animator . calculateZoomToOrigin ;
833
866
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 ) ) ;
835
868
var to = animator . toTransformCss ( "" ) ; // defaults to center display (or parent or $rootElement)
836
869
837
870
return animator
838
871
. translate3d ( dialogEl , from , to , translateOptions )
839
872
. then ( function ( animateReversal ) {
840
-
841
-
842
-
843
873
// Build a reversal translate function synched to this translation...
844
874
options . reverseAnimate = function ( ) {
845
-
846
875
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
+
847
887
return animateReversal (
848
888
animator . toTransformCss (
849
889
// in case the origin element has moved or is hidden,
0 commit comments