Skip to content
This repository was archived by the owner on Mar 13, 2018. It is now read-only.

Commit da063de

Browse files
committed
fix a number of sizing/positioning issues; margin now via css; added tests.
1 parent 45670fb commit da063de

8 files changed

+584
-76
lines changed

core-overlay.html

+125-76
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,15 @@ <h2>Dialog</h2>
4848
</core-overlay>
4949
5050
`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
5560
that, for example, a drop shadow is always visible around the overlay.
5661
5762
@group Core Elements
@@ -75,6 +80,16 @@ <h2>Dialog</h2>
7580
7681
@event core-overlay-close-completed
7782
-->
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+
-->
7893
<style>
7994
.core-overlay-backdrop {
8095
position: fixed;
@@ -173,7 +188,7 @@ <h2>Dialog</h2>
173188
* @type boolean
174189
* @default false
175190
*/
176-
autoFocusDisabled: false,
191+
autoFocusDisabled: false,
177192

178193
/**
179194
* This property specifies an attribute on elements that should
@@ -197,18 +212,6 @@ <h2>Dialog</h2>
197212
*/
198213
closeSelector: '',
199214

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-
212215
/**
213216
* The transition property specifies a string which identifies a
214217
* <a href="../core-transition/">`core-transition`</a> element that
@@ -313,19 +316,22 @@ <h2>Dialog</h2>
313316
if (!this.target || this.target.__overlaySetup) {
314317
return;
315318
}
319+
if (!this.sizingTarget) {
320+
this.sizingTarget = this.target;
321+
}
316322
this.target.__overlaySetup = true;
317323
this.target.style.display = '';
318324
var transition = this.getTransition();
319325
if (transition) {
320326
transition.setup(this.target);
321327
}
328+
// TODO(sorvell): bc
322329
var computed = getComputedStyle(this.target);
323330
this.targetStyle = {
324331
position: computed.position === 'static' ? 'fixed' :
325332
computed.position
326333
};
327334
if (!transition) {
328-
this.target.style.position = this.targetStyle.position;
329335
this.target.style.outline = 'none';
330336
}
331337
this.target.style.display = 'none';
@@ -335,13 +341,12 @@ <h2>Dialog</h2>
335341
this.transitioning = true;
336342
this.ensureTargetSetup();
337343
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.
342345
this.async(function() {
343346
this.target.style.display = '';
344-
this.async('renderOpened');
347+
// force layout to ensure transitions will go
348+
this.target.offsetWidth;
349+
this.renderOpened();
345350
});
346351
this.fire('core-overlay-open', this.opened);
347352
},
@@ -363,20 +368,14 @@ <h2>Dialog</h2>
363368
'resizeHandler');
364369

365370
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};
376375
// if we are showing, then take care when measuring
377-
this.prepareMeasure(this.target);
376+
this.prepareMeasure();
378377
this.updateTargetDimensions();
379-
this.finishMeasure(this.target);
378+
this.finishMeasure();
380379
if (this.layered) {
381380
this.layer.addElement(this.target);
382381
this.layer.opened = this.opened;
@@ -445,16 +444,18 @@ <h2>Dialog</h2>
445444
}
446445
},
447446

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 = '';
452451
},
453452

454453
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 = '';
458459
},
459460

460461
getTransition: function(name) {
@@ -483,47 +484,101 @@ <h2>Dialog</h2>
483484

484485
updateTargetDimensions: function() {
485486
this.positionTarget();
487+
this.discoverDimensions();
486488
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';
493531
}
532+
this.target.style.position = pos || '';
494533
},
495534

496535
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+
}
506544
},
507545

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;
513564
this.target.style.top = t + 'px';
514565
}
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;
518570
this.target.style.left = l + 'px';
519571
}
520572
},
521573

522574
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;
527582
},
528583

529584
tapHandler: function(e) {
@@ -616,18 +671,12 @@ <h2>Dialog</h2>
616671
if (!this[bound]) {
617672
this[bound] = function(e) {
618673
method.call(self, e);
619-
}
674+
};
620675
}
621676
return this[bound];
622677
},
623678
});
624679

625-
function forcePolyfillRender(target) {
626-
if (window.ShadowDOMPolyfill) {
627-
target.offsetHeight;
628-
}
629-
}
630-
631680
// TODO(sorvell): This should be an element with private state so it can
632681
// be independent of overlay.
633682
// track overlays for z-index and focus managemant

gulpfile.js

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
var gulp = require('gulp');
2+
require('gulp-web-component-tester').init(gulp);

0 commit comments

Comments
 (0)