@@ -48,10 +48,15 @@ <h2>Dialog</h2>
48
48
</core-overlay>
49
49
50
50
`core-overlay` will automatically size and position itself according to the
51
- following rules. If the target's style.top and style.left are unset, the
52
- target will be centered. The size of the target is constrained to be no larger
53
- than the window dimensions. The `margin` property specifies the extra amount
54
- of space that should be reserved around the overlay. This can be used to ensure
51
+ following rules. The overlay's size is constrained such that it does not
52
+ overflow the screen. This is done by setting maxHeight/maxWidth on the
53
+ `sizingTarget`. If the `sizingTarget` already has a setting for one of these
54
+ properties, it will not be overridden. The overlay should
55
+ be positioned via css or imperatively using the `core-overlay-position` event.
56
+ If the overlay is not positioned vertically via setting `top` or `bottom`, it
57
+ will be centered vertically. The same is true horizontally via a setting to
58
+ `left` or `right`. In addition, css `margin` can be used to provide some space
59
+ around the overlay. This can be used to ensure
55
60
that, for example, a drop shadow is always visible around the overlay.
56
61
57
62
@group Core Elements
@@ -75,6 +80,16 @@ <h2>Dialog</h2>
75
80
76
81
@event core-overlay-close-completed
77
82
-->
83
+ <!--
84
+ Fired when the `core-overlay` needs to position itself. Optionally, implement
85
+ in order to position an overlay dynamically.
86
+
87
+ @event core-overlay-position
88
+ @param {Object} detail
89
+ @param {Object} detail.target the overlay target
90
+ @param {Object} detail.sizingTarget the overlay sizing target
91
+ @param {Object} detail.opened the opened state
92
+ -->
78
93
< style >
79
94
.core-overlay-backdrop {
80
95
position : fixed;
@@ -173,7 +188,7 @@ <h2>Dialog</h2>
173
188
* @type boolean
174
189
* @default false
175
190
*/
176
- autoFocusDisabled : false ,
191
+ autoFocusDisabled : false ,
177
192
178
193
/**
179
194
* This property specifies an attribute on elements that should
@@ -197,18 +212,6 @@ <h2>Dialog</h2>
197
212
*/
198
213
closeSelector : '' ,
199
214
200
- /**
201
- * A `core-overlay` target's size is constrained to the window size.
202
- * The `margin` property specifies a pixel amount around the overlay
203
- * that will be reserved. It's useful for ensuring that, for example,
204
- * a shadow displayed outside the target will always be visible.
205
- *
206
- * @attribute margin
207
- * @type number
208
- * @default 0
209
- */
210
- margin : 0 ,
211
-
212
215
/**
213
216
* The transition property specifies a string which identifies a
214
217
* <a href="../core-transition/">`core-transition`</a> element that
@@ -313,19 +316,22 @@ <h2>Dialog</h2>
313
316
if ( ! this . target || this . target . __overlaySetup ) {
314
317
return ;
315
318
}
319
+ if ( ! this . sizingTarget ) {
320
+ this . sizingTarget = this . target ;
321
+ }
316
322
this . target . __overlaySetup = true ;
317
323
this . target . style . display = '' ;
318
324
var transition = this . getTransition ( ) ;
319
325
if ( transition ) {
320
326
transition . setup ( this . target ) ;
321
327
}
328
+ // TODO(sorvell): bc
322
329
var computed = getComputedStyle ( this . target ) ;
323
330
this . targetStyle = {
324
331
position : computed . position === 'static' ? 'fixed' :
325
332
computed . position
326
333
} ;
327
334
if ( ! transition ) {
328
- this . target . style . position = this . targetStyle . position ;
329
335
this . target . style . outline = 'none' ;
330
336
}
331
337
this . target . style . display = 'none' ;
@@ -335,13 +341,12 @@ <h2>Dialog</h2>
335
341
this . transitioning = true ;
336
342
this . ensureTargetSetup ( ) ;
337
343
this . prepareRenderOpened ( ) ;
338
- // continue styling after delay so display state can change
339
- // without aborting transitions
340
- // note: we wait a full frame so that transition changes executed
341
- // during measuring do not cause transition
344
+ // async here to allow overlay layer to become visible.
342
345
this . async ( function ( ) {
343
346
this . target . style . display = '' ;
344
- this . async ( 'renderOpened' ) ;
347
+ // force layout to ensure transitions will go
348
+ this . target . offsetWidth ;
349
+ this . renderOpened ( ) ;
345
350
} ) ;
346
351
this . fire ( 'core-overlay-open' , this . opened ) ;
347
352
} ,
@@ -363,20 +368,14 @@ <h2>Dialog</h2>
363
368
'resizeHandler' ) ;
364
369
365
370
if ( this . opened ) {
366
- // TODO(sorvell): force SD Polyfill to render
367
- forcePolyfillRender ( this . target ) ;
368
- if ( ! this . _shouldPosition ) {
369
- this . target . style . position = 'absolute' ;
370
- var computed = getComputedStyle ( this . target ) ;
371
- var t = ( computed . top === 'auto' && computed . bottom === 'auto' ) ;
372
- var l = ( computed . left === 'auto' && computed . right === 'auto' ) ;
373
- this . target . style . position = this . targetStyle . position ;
374
- this . _shouldPosition = { top : t , left : l } ;
375
- }
371
+ // force layout so SD Polyfill renders
372
+ this . target . offsetHeight ;
373
+ // TODO(sorvell): bc
374
+ this . _shouldPosition = { left : true , top : true } ;
376
375
// if we are showing, then take care when measuring
377
- this . prepareMeasure ( this . target ) ;
376
+ this . prepareMeasure ( ) ;
378
377
this . updateTargetDimensions ( ) ;
379
- this . finishMeasure ( this . target ) ;
378
+ this . finishMeasure ( ) ;
380
379
if ( this . layered ) {
381
380
this . layer . addElement ( this . target ) ;
382
381
this . layer . opened = this . opened ;
@@ -445,16 +444,18 @@ <h2>Dialog</h2>
445
444
}
446
445
} ,
447
446
448
- prepareMeasure : function ( target ) {
449
- target . style . transition = target . style . webkitTransition = 'none' ;
450
- target . style . transform = target . style . webkitTransform = 'none' ;
451
- target . style . display = '' ;
447
+ prepareMeasure : function ( ) {
448
+ this . target . style . transition = this . target . style . webkitTransition = 'none' ;
449
+ this . target . style . transform = this . target . style . webkitTransform = 'none' ;
450
+ this . target . style . display = '' ;
452
451
} ,
453
452
454
453
finishMeasure : function ( target ) {
455
- target . style . display = 'none' ;
456
- target . style . transform = target . style . webkitTransform = '' ;
457
- target . style . transition = target . style . webkitTransition = '' ;
454
+ this . target . style . display = 'none' ;
455
+ this . target . style . transform = this . target . style . webkitTransform = '' ;
456
+ // force layout to avoid application of transform
457
+ this . target . offsetWidth ;
458
+ this . target . style . transition = this . target . style . webkitTransition = '' ;
458
459
} ,
459
460
460
461
getTransition : function ( name ) {
@@ -483,47 +484,101 @@ <h2>Dialog</h2>
483
484
484
485
updateTargetDimensions : function ( ) {
485
486
this . positionTarget ( ) ;
487
+ this . discoverDimensions ( ) ;
486
488
this . sizeTarget ( ) ;
487
- //
488
- if ( this . layered ) {
489
- var rect = this . target . getBoundingClientRect ( ) ;
490
- this . target . style . top = rect . top + 'px' ;
491
- this . target . style . left = rect . left + 'px' ;
492
- this . target . style . right = this . target . style . bottom = 'auto' ;
489
+ this . applyDefaultPositioning ( ) ;
490
+ } ,
491
+
492
+ positionTarget : function ( ) {
493
+ // fire positioning event
494
+ this . fire ( 'core-overlay-position' , { target : this . target ,
495
+ sizingTarget : this . sizingTarget , opened : this . opened } ) ;
496
+ } ,
497
+
498
+ discoverDimensions : function ( ) {
499
+ if ( this . _dims ) {
500
+ return ;
501
+ }
502
+ var pos = this . target . style . position ;
503
+ // this.target.style.position = 'absolute !important';
504
+ var target = getComputedStyle ( this . target ) ;
505
+ var sizer = getComputedStyle ( this . sizingTarget ) ;
506
+ this . _dims = {
507
+ position : {
508
+ v : target . top !== 'auto' ? 'top' : ( target . bottom !== 'auto' ?
509
+ 'bottom' : null ) ,
510
+ h : target . left !== 'auto' ? 'left' : ( target . right !== 'auto' ?
511
+ 'right' : null ) ,
512
+ css : target . position
513
+ } ,
514
+ size : {
515
+ v : sizer . maxHeight !== 'none' ,
516
+ h : sizer . maxWidth !== 'none'
517
+ } ,
518
+ margin : {
519
+ top : parseInt ( target . marginTop ) || 0 ,
520
+ right : parseInt ( target . marginRight ) || 0 ,
521
+ bottom : parseInt ( target . marginBottom ) || 0 ,
522
+ left : parseInt ( target . marginLeft ) || 0
523
+ }
524
+ } ;
525
+ // size at top/left if unset
526
+ if ( ! this . _dims . position . v ) {
527
+ this . target . style . top = '0px' ;
528
+ }
529
+ if ( ! this . _dims . position . h ) {
530
+ this . target . style . left = '0px' ;
493
531
}
532
+ this . target . style . position = pos || '' ;
494
533
} ,
495
534
496
535
sizeTarget : function ( ) {
497
- var sizer = this . sizingTarget || this . target ;
498
- var rect = sizer . getBoundingClientRect ( ) ;
499
- var mt = rect . top === this . margin ? this . margin : this . margin * 2 ;
500
- var ml = rect . left === this . margin ? this . margin : this . margin * 2 ;
501
- var h = window . innerHeight - rect . top - mt ;
502
- var w = window . innerWidth - rect . left - ml ;
503
- sizer . style . maxHeight = h + 'px' ;
504
- sizer . style . maxWidth = w + 'px' ;
505
- sizer . style . boxSizing = 'border-box' ;
536
+ this . sizingTarget . style . boxSizing = 'border-box' ;
537
+ var rect = this . target . getBoundingClientRect ( ) ;
538
+ if ( ! this . _dims . size . v ) {
539
+ this . sizeDimension ( rect , this . _dims . position . v , 'top' , 'bottom' , 'Height' ) ;
540
+ }
541
+ if ( ! this . _dims . size . h ) {
542
+ this . sizeDimension ( rect , this . _dims . position . h , 'left' , 'right' , 'Width' ) ;
543
+ }
506
544
} ,
507
545
508
- positionTarget : function ( ) {
509
- // vertically and horizontally center if not positioned
510
- if ( this . _shouldPosition . top ) {
511
- var t = Math . max ( ( window . innerHeight -
512
- this . target . offsetHeight - this . margin * 2 ) / 2 , this . margin ) ;
546
+ sizeDimension : function ( rect , positionedBy , start , end , extent ) {
547
+ var flip = ( positionedBy === end ) ;
548
+ var m = flip ? start : end ;
549
+ var ws = window [ 'inner' + extent ] ;
550
+ var o = this . _dims . margin [ m ] + ( flip ? ws - rect [ end ] :
551
+ rect [ start ] ) ;
552
+ this . sizingTarget . style [ 'max' + extent ] = ( ws - o ) + 'px' ;
553
+ } ,
554
+
555
+ // vertically and horizontally center if not positioned
556
+ applyDefaultPositioning : function ( ) {
557
+ // only center if position fixed.
558
+ if ( this . _dims . position . css !== 'fixed' ) {
559
+ return ;
560
+ }
561
+ if ( ! this . _dims . position . v ) {
562
+ var t = ( window . innerHeight - this . target . offsetHeight ) / 2 ;
563
+ t -= this . _dims . margin . top ;
513
564
this . target . style . top = t + 'px' ;
514
565
}
515
- if ( this . _shouldPosition . left ) {
516
- var l = Math . max ( ( window . innerWidth -
517
- this . target . offsetWidth - this . margin * 2 ) / 2 , this . margin ) ;
566
+
567
+ if ( ! this . _dims . position . h ) {
568
+ var l = ( window . innerWidth - this . target . offsetWidth ) / 2 ;
569
+ l -= this . _dims . margin . left ;
518
570
this . target . style . left = l + 'px' ;
519
571
}
520
572
} ,
521
573
522
574
resetTargetDimensions : function ( ) {
523
- this . target . style . top = this . target . style . left = '' ;
524
- this . target . style . right = this . target . style . bottom = '' ;
525
- this . target . style . width = this . target . style . height = '' ;
526
- this . _shouldPosition = null ;
575
+ if ( ! this . _dims . size . v ) {
576
+ this . sizingTarget . style . maxHeight = '' ;
577
+ }
578
+ if ( ! this . _dims . size . h ) {
579
+ this . sizingTarget . style . maxWidth = '' ;
580
+ }
581
+ this . _dims = null ;
527
582
} ,
528
583
529
584
tapHandler : function ( e ) {
@@ -616,18 +671,12 @@ <h2>Dialog</h2>
616
671
if ( ! this [ bound ] ) {
617
672
this [ bound ] = function ( e ) {
618
673
method . call ( self , e ) ;
619
- }
674
+ } ;
620
675
}
621
676
return this [ bound ] ;
622
677
} ,
623
678
} ) ;
624
679
625
- function forcePolyfillRender ( target ) {
626
- if ( window . ShadowDOMPolyfill ) {
627
- target . offsetHeight ;
628
- }
629
- }
630
-
631
680
// TODO(sorvell): This should be an element with private state so it can
632
681
// be independent of overlay.
633
682
// track overlays for z-index and focus managemant
0 commit comments