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

Commit

Permalink
fix a number of sizing/positioning issues; margin now via css; added …
Browse files Browse the repository at this point in the history
…tests.
  • Loading branch information
sorvell committed Sep 10, 2014
1 parent 45670fb commit da063de
Show file tree
Hide file tree
Showing 8 changed files with 584 additions and 76 deletions.
201 changes: 125 additions & 76 deletions core-overlay.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,15 @@ <h2>Dialog</h2>
</core-overlay>
`core-overlay` will automatically size and position itself according to the
following rules. If the target's style.top and style.left are unset, the
target will be centered. The size of the target is constrained to be no larger
than the window dimensions. The `margin` property specifies the extra amount
of space that should be reserved around the overlay. This can be used to ensure
following rules. The overlay's size is constrained such that it does not
overflow the screen. This is done by setting maxHeight/maxWidth on the
`sizingTarget`. If the `sizingTarget` already has a setting for one of these
properties, it will not be overridden. The overlay should
be positioned via css or imperatively using the `core-overlay-position` event.
If the overlay is not positioned vertically via setting `top` or `bottom`, it
will be centered vertically. The same is true horizontally via a setting to
`left` or `right`. In addition, css `margin` can be used to provide some space
around the overlay. This can be used to ensure
that, for example, a drop shadow is always visible around the overlay.
@group Core Elements
Expand All @@ -75,6 +80,16 @@ <h2>Dialog</h2>
@event core-overlay-close-completed
-->
<!--
Fired when the `core-overlay` needs to position itself. Optionally, implement
in order to position an overlay dynamically.
@event core-overlay-position
@param {Object} detail
@param {Object} detail.target the overlay target
@param {Object} detail.sizingTarget the overlay sizing target
@param {Object} detail.opened the opened state
-->
<style>
.core-overlay-backdrop {
position: fixed;
Expand Down Expand Up @@ -173,7 +188,7 @@ <h2>Dialog</h2>
* @type boolean
* @default false
*/
autoFocusDisabled: false,
autoFocusDisabled: false,

/**
* This property specifies an attribute on elements that should
Expand All @@ -197,18 +212,6 @@ <h2>Dialog</h2>
*/
closeSelector: '',

/**
* A `core-overlay` target's size is constrained to the window size.
* The `margin` property specifies a pixel amount around the overlay
* that will be reserved. It's useful for ensuring that, for example,
* a shadow displayed outside the target will always be visible.
*
* @attribute margin
* @type number
* @default 0
*/
margin: 0,

/**
* The transition property specifies a string which identifies a
* <a href="../core-transition/">`core-transition`</a> element that
Expand Down Expand Up @@ -313,19 +316,22 @@ <h2>Dialog</h2>
if (!this.target || this.target.__overlaySetup) {
return;
}
if (!this.sizingTarget) {
this.sizingTarget = this.target;
}
this.target.__overlaySetup = true;
this.target.style.display = '';
var transition = this.getTransition();
if (transition) {
transition.setup(this.target);
}
// TODO(sorvell): bc
var computed = getComputedStyle(this.target);
this.targetStyle = {
position: computed.position === 'static' ? 'fixed' :
computed.position
};
if (!transition) {
this.target.style.position = this.targetStyle.position;
this.target.style.outline = 'none';
}
this.target.style.display = 'none';
Expand All @@ -335,13 +341,12 @@ <h2>Dialog</h2>
this.transitioning = true;
this.ensureTargetSetup();
this.prepareRenderOpened();
// continue styling after delay so display state can change
// without aborting transitions
// note: we wait a full frame so that transition changes executed
// during measuring do not cause transition
// async here to allow overlay layer to become visible.
this.async(function() {
this.target.style.display = '';
this.async('renderOpened');
// force layout to ensure transitions will go
this.target.offsetWidth;
this.renderOpened();
});
this.fire('core-overlay-open', this.opened);
},
Expand All @@ -363,20 +368,14 @@ <h2>Dialog</h2>
'resizeHandler');

if (this.opened) {
// TODO(sorvell): force SD Polyfill to render
forcePolyfillRender(this.target);
if (!this._shouldPosition) {
this.target.style.position = 'absolute';
var computed = getComputedStyle(this.target);
var t = (computed.top === 'auto' && computed.bottom === 'auto');
var l = (computed.left === 'auto' && computed.right === 'auto');
this.target.style.position = this.targetStyle.position;
this._shouldPosition = {top: t, left: l};
}
// force layout so SD Polyfill renders
this.target.offsetHeight;
// TODO(sorvell): bc
this._shouldPosition = {left: true, top: true};
// if we are showing, then take care when measuring
this.prepareMeasure(this.target);
this.prepareMeasure();
this.updateTargetDimensions();
this.finishMeasure(this.target);
this.finishMeasure();
if (this.layered) {
this.layer.addElement(this.target);
this.layer.opened = this.opened;
Expand Down Expand Up @@ -445,16 +444,18 @@ <h2>Dialog</h2>
}
},

prepareMeasure: function(target) {
target.style.transition = target.style.webkitTransition = 'none';
target.style.transform = target.style.webkitTransform = 'none';
target.style.display = '';
prepareMeasure: function() {
this.target.style.transition = this.target.style.webkitTransition = 'none';
this.target.style.transform = this.target.style.webkitTransform = 'none';
this.target.style.display = '';
},

finishMeasure: function(target) {
target.style.display = 'none';
target.style.transform = target.style.webkitTransform = '';
target.style.transition = target.style.webkitTransition = '';
this.target.style.display = 'none';
this.target.style.transform = this.target.style.webkitTransform = '';
// force layout to avoid application of transform
this.target.offsetWidth;
this.target.style.transition = this.target.style.webkitTransition = '';
},

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

updateTargetDimensions: function() {
this.positionTarget();
this.discoverDimensions();
this.sizeTarget();
//
if (this.layered) {
var rect = this.target.getBoundingClientRect();
this.target.style.top = rect.top + 'px';
this.target.style.left = rect.left + 'px';
this.target.style.right = this.target.style.bottom = 'auto';
this.applyDefaultPositioning();
},

positionTarget: function() {
// fire positioning event
this.fire('core-overlay-position', {target: this.target,
sizingTarget: this.sizingTarget, opened: this.opened});
},

discoverDimensions: function() {
if (this._dims) {
return;
}
var pos = this.target.style.position;
// this.target.style.position = 'absolute !important';
var target = getComputedStyle(this.target);
var sizer = getComputedStyle(this.sizingTarget);
this._dims = {
position: {
v: target.top !== 'auto' ? 'top' : (target.bottom !== 'auto' ?
'bottom' : null),
h: target.left !== 'auto' ? 'left' : (target.right !== 'auto' ?
'right' : null),
css: target.position
},
size: {
v: sizer.maxHeight !== 'none',
h: sizer.maxWidth !== 'none'
},
margin: {
top: parseInt(target.marginTop) || 0,
right: parseInt(target.marginRight) || 0,
bottom: parseInt(target.marginBottom) || 0,
left: parseInt(target.marginLeft) || 0
}
};
// size at top/left if unset
if (!this._dims.position.v) {
this.target.style.top = '0px';
}
if (!this._dims.position.h) {
this.target.style.left = '0px';
}
this.target.style.position = pos || '';
},

sizeTarget: function() {
var sizer = this.sizingTarget || this.target;
var rect = sizer.getBoundingClientRect();
var mt = rect.top === this.margin ? this.margin : this.margin * 2;
var ml = rect.left === this.margin ? this.margin : this.margin * 2;
var h = window.innerHeight - rect.top - mt;
var w = window.innerWidth - rect.left - ml;
sizer.style.maxHeight = h + 'px';
sizer.style.maxWidth = w + 'px';
sizer.style.boxSizing = 'border-box';
this.sizingTarget.style.boxSizing = 'border-box';
var rect = this.target.getBoundingClientRect();
if (!this._dims.size.v) {
this.sizeDimension(rect, this._dims.position.v, 'top', 'bottom', 'Height');
}
if (!this._dims.size.h) {
this.sizeDimension(rect, this._dims.position.h, 'left', 'right', 'Width');
}
},

positionTarget: function() {
// vertically and horizontally center if not positioned
if (this._shouldPosition.top) {
var t = Math.max((window.innerHeight -
this.target.offsetHeight - this.margin*2) / 2, this.margin);
sizeDimension: function(rect, positionedBy, start, end, extent) {
var flip = (positionedBy === end);
var m = flip ? start : end;
var ws = window['inner' + extent];
var o = this._dims.margin[m] + (flip ? ws - rect[end] :
rect[start]);
this.sizingTarget.style['max' + extent] = (ws - o) + 'px';
},

// vertically and horizontally center if not positioned
applyDefaultPositioning: function() {
// only center if position fixed.
if (this._dims.position.css !== 'fixed') {
return;
}
if (!this._dims.position.v) {
var t = (window.innerHeight - this.target.offsetHeight) / 2;
t -= this._dims.margin.top;
this.target.style.top = t + 'px';
}
if (this._shouldPosition.left) {
var l = Math.max((window.innerWidth -
this.target.offsetWidth - this.margin*2) / 2, this.margin);

if (!this._dims.position.h) {
var l = (window.innerWidth - this.target.offsetWidth) / 2;
l -= this._dims.margin.left;
this.target.style.left = l + 'px';
}
},

resetTargetDimensions: function() {
this.target.style.top = this.target.style.left = '';
this.target.style.right = this.target.style.bottom = '';
this.target.style.width = this.target.style.height = '';
this._shouldPosition = null;
if (!this._dims.size.v) {
this.sizingTarget.style.maxHeight = '';
}
if (!this._dims.size.h) {
this.sizingTarget.style.maxWidth = '';
}
this._dims = null;
},

tapHandler: function(e) {
Expand Down Expand Up @@ -616,18 +671,12 @@ <h2>Dialog</h2>
if (!this[bound]) {
this[bound] = function(e) {
method.call(self, e);
}
};
}
return this[bound];
},
});

function forcePolyfillRender(target) {
if (window.ShadowDOMPolyfill) {
target.offsetHeight;
}
}

// TODO(sorvell): This should be an element with private state so it can
// be independent of overlay.
// track overlays for z-index and focus managemant
Expand Down
2 changes: 2 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var gulp = require('gulp');
require('gulp-web-component-tester').init(gulp);
Loading

2 comments on commit da063de

@BerndWessels
Copy link

Choose a reason for hiding this comment

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

Whoa, that change breaks the demo and even worse it breaks everything else in my apps that was based on core-overlay.
Will other core elements see such major breaking changes?

@sorvell
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could you be more specific about how it breaks the demo and breaks your app?

Our goal is obviously to improve the elements and we are still in a period where there may be breaking changes. That said, if we can avoid the pain, we definitely will.

Please sign in to comment.