Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(74)

Unified Diff: third_party/polymer/components/iron-fit-behavior/iron-fit-behavior.html

Issue 2113853002: Run bower update (Closed) Base URL: https://github.com/catapult-project/catapult@polymer10-migration
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/polymer/components/iron-fit-behavior/iron-fit-behavior.html
diff --git a/third_party/polymer/components/iron-fit-behavior/iron-fit-behavior.html b/third_party/polymer/components/iron-fit-behavior/iron-fit-behavior.html
index 1705f31a20edf1e1685917ec88aa79a974e8f2bb..d51d87dfe44f135dce3cdb01d95536709d68923f 100644
--- a/third_party/polymer/components/iron-fit-behavior/iron-fit-behavior.html
+++ b/third_party/polymer/components/iron-fit-behavior/iron-fit-behavior.html
@@ -11,9 +11,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
<link rel="import" href="../polymer/polymer.html">
<script>
-
/**
-Polymer.IronFitBehavior fits an element in another element using `max-height` and `max-width`, and
+`Polymer.IronFitBehavior` fits an element in another element using `max-height` and `max-width`, and
optionally centers it in the window or another element.
The element will only be sized and/or positioned if it has not already been sized and/or positioned
@@ -24,8 +23,25 @@ CSS properties | Action
`position` set | Element is not centered horizontally or vertically
`top` or `bottom` set | Element is not vertically centered
`left` or `right` set | Element is not horizontally centered
-`max-height` or `height` set | Element respects `max-height` or `height`
-`max-width` or `width` set | Element respects `max-width` or `width`
+`max-height` set | Element respects `max-height`
+`max-width` set | Element respects `max-width`
+
+`Polymer.IronFitBehavior` can position an element into another element using
+`verticalAlign` and `horizontalAlign`. This will override the element's css position.
+
+ <div class="container">
+ <iron-fit-impl vertical-align="top" horizontal-align="auto">
+ Positioned into the container
+ </iron-fit-impl>
+ </div>
+
+Use `noOverlap` to position the element around another element without overlapping it.
+
+ <div class="container">
+ <iron-fit-impl no-overlap vertical-align="auto" horizontal-align="auto">
+ Positioned around the container
+ </iron-fit-impl>
+ </div>
@demo demo/index.html
@polymerBehavior
@@ -57,6 +73,66 @@ CSS properties | Action
},
/**
+ * Will position the element around the positionTarget without overlapping it.
+ */
+ noOverlap: {
+ type: Boolean
+ },
+
+ /**
+ * The element that should be used to position the element. If not set, it will
+ * default to the parent node.
+ * @type {!Element}
+ */
+ positionTarget: {
+ type: Element
+ },
+
+ /**
+ * The orientation against which to align the element horizontally
+ * relative to the `positionTarget`. Possible values are "left", "right", "auto".
+ */
+ horizontalAlign: {
+ type: String
+ },
+
+ /**
+ * The orientation against which to align the element vertically
+ * relative to the `positionTarget`. Possible values are "top", "bottom", "auto".
+ */
+ verticalAlign: {
+ type: String
+ },
+
+ /**
+ * If true, it will use `horizontalAlign` and `verticalAlign` values as preferred alignment
+ * and if there's not enough space, it will pick the values which minimize the cropping.
+ */
+ dynamicAlign: {
+ type: Boolean
+ },
+
+ /**
+ * The same as setting margin-left and margin-right css properties.
+ * @deprecated
+ */
+ horizontalOffset: {
+ type: Number,
+ value: 0,
+ notify: true
+ },
+
+ /**
+ * The same as setting margin-top and margin-bottom css properties.
+ * @deprecated
+ */
+ verticalOffset: {
+ type: Number,
+ value: 0,
+ notify: true
+ },
+
+ /**
* Set to true to auto-fit on attach.
*/
autoFitOnAttach: {
@@ -68,7 +144,6 @@ CSS properties | Action
_fitInfo: {
type: Object
}
-
},
get _fitWidth() {
@@ -111,7 +186,40 @@ CSS properties | Action
return fitTop;
},
+ /**
+ * The element that should be used to position the element,
+ * if no position target is configured.
+ */
+ get _defaultPositionTarget() {
+ var parent = Polymer.dom(this).parentNode;
+
+ if (parent && parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
+ parent = parent.host;
+ }
+
+ return parent;
+ },
+
+ /**
+ * The horizontal align value, accounting for the RTL/LTR text direction.
+ */
+ get _localeHorizontalAlign() {
+ if (this._isRTL) {
+ // In RTL, "left" becomes "right".
+ if (this.horizontalAlign === 'right') {
+ return 'left';
+ }
+ if (this.horizontalAlign === 'left') {
+ return 'right';
+ }
+ }
+ return this.horizontalAlign;
+ },
+
attached: function() {
+ // Memoize this to avoid expensive calculations & relayouts.
+ this._isRTL = window.getComputedStyle(this).direction == 'rtl';
+ this.positionTarget = this.positionTarget || this._defaultPositionTarget;
if (this.autoFitOnAttach) {
if (window.getComputedStyle(this).display === 'none') {
setTimeout(function() {
@@ -124,16 +232,18 @@ CSS properties | Action
},
/**
- * Fits and optionally centers the element into the window, or `fitInfo` if specified.
+ * Positions and fits the element into the `fitInto` element.
*/
fit: function() {
this._discoverInfo();
+ this.position();
this.constrain();
this.center();
},
/**
* Memoize information needed to position and size the target element.
+ * @suppress {deprecated}
*/
_discoverInfo: function() {
if (this._fitInfo) {
@@ -141,21 +251,29 @@ CSS properties | Action
}
var target = window.getComputedStyle(this);
var sizer = window.getComputedStyle(this.sizingTarget);
+
this._fitInfo = {
inlineStyle: {
top: this.style.top || '',
- left: this.style.left || ''
+ left: this.style.left || '',
+ position: this.style.position || ''
+ },
+ sizerInlineStyle: {
+ maxWidth: this.sizingTarget.style.maxWidth || '',
+ maxHeight: this.sizingTarget.style.maxHeight || '',
+ boxSizing: this.sizingTarget.style.boxSizing || ''
},
positionedBy: {
vertically: target.top !== 'auto' ? 'top' : (target.bottom !== 'auto' ?
'bottom' : null),
horizontally: target.left !== 'auto' ? 'left' : (target.right !== 'auto' ?
- 'right' : null),
- css: target.position
+ 'right' : null)
},
sizedBy: {
height: sizer.maxHeight !== 'none',
- width: sizer.maxWidth !== 'none'
+ width: sizer.maxWidth !== 'none',
+ minWidth: parseInt(sizer.minWidth, 10) || 0,
+ minHeight: parseInt(sizer.minHeight, 10) || 0
},
margin: {
top: parseInt(target.marginTop, 10) || 0,
@@ -164,6 +282,20 @@ CSS properties | Action
left: parseInt(target.marginLeft, 10) || 0
}
};
+
+ // Support these properties until they are removed.
+ if (this.verticalOffset) {
+ this._fitInfo.margin.top = this._fitInfo.margin.bottom = this.verticalOffset;
+ this._fitInfo.inlineStyle.marginTop = this.style.marginTop || '';
+ this._fitInfo.inlineStyle.marginBottom = this.style.marginBottom || '';
+ this.style.marginTop = this.style.marginBottom = this.verticalOffset + 'px';
+ }
+ if (this.horizontalOffset) {
+ this._fitInfo.margin.left = this._fitInfo.margin.right = this.horizontalOffset;
+ this._fitInfo.inlineStyle.marginLeft = this.style.marginLeft || '';
+ this._fitInfo.inlineStyle.marginRight = this.style.marginRight || '';
+ this.style.marginLeft = this.style.marginRight = this.horizontalOffset + 'px';
+ }
},
/**
@@ -171,61 +303,138 @@ CSS properties | Action
* the memoized data.
*/
resetFit: function() {
- if (!this._fitInfo || !this._fitInfo.sizedBy.width) {
- this.sizingTarget.style.maxWidth = '';
- }
- if (!this._fitInfo || !this._fitInfo.sizedBy.height) {
- this.sizingTarget.style.maxHeight = '';
+ var info = this._fitInfo || {};
+ for (var property in info.sizerInlineStyle) {
+ this.sizingTarget.style[property] = info.sizerInlineStyle[property];
}
- this.style.top = this._fitInfo ? this._fitInfo.inlineStyle.top : '';
- this.style.left = this._fitInfo ? this._fitInfo.inlineStyle.left : '';
- if (this._fitInfo) {
- this.style.position = this._fitInfo.positionedBy.css;
+ for (var property in info.inlineStyle) {
+ this.style[property] = info.inlineStyle[property];
}
+
this._fitInfo = null;
},
/**
- * Equivalent to calling `resetFit()` and `fit()`. Useful to call this after the element,
- * the window, or the `fitInfo` element has been resized.
+ * Equivalent to calling `resetFit()` and `fit()`. Useful to call this after
+ * the element or the `fitInto` element has been resized, or if any of the
+ * positioning properties (e.g. `horizontalAlign, verticalAlign`) is updated.
+ * It preserves the scroll position of the sizingTarget.
*/
refit: function() {
+ var scrollLeft = this.sizingTarget.scrollLeft;
+ var scrollTop = this.sizingTarget.scrollTop;
this.resetFit();
this.fit();
+ this.sizingTarget.scrollLeft = scrollLeft;
+ this.sizingTarget.scrollTop = scrollTop;
+ },
+
+ /**
+ * Positions the element according to `horizontalAlign, verticalAlign`.
+ */
+ position: function() {
+ if (!this.horizontalAlign && !this.verticalAlign) {
+ // needs to be centered, and it is done after constrain.
+ return;
+ }
+
+ this.style.position = 'fixed';
+ // Need border-box for margin/padding.
+ this.sizingTarget.style.boxSizing = 'border-box';
+ // Set to 0, 0 in order to discover any offset caused by parent stacking contexts.
+ this.style.left = '0px';
+ this.style.top = '0px';
+
+ var rect = this.getBoundingClientRect();
+ var positionRect = this.__getNormalizedRect(this.positionTarget);
+ var fitRect = this.__getNormalizedRect(this.fitInto);
+
+ var margin = this._fitInfo.margin;
+
+ // Consider the margin as part of the size for position calculations.
+ var size = {
+ width: rect.width + margin.left + margin.right,
+ height: rect.height + margin.top + margin.bottom
+ };
+
+ var position = this.__getPosition(this._localeHorizontalAlign, this.verticalAlign, size, positionRect, fitRect);
+
+ var left = position.left + margin.left;
+ var top = position.top + margin.top;
+
+ // Use original size (without margin).
+ var right = Math.min(fitRect.right - margin.right, left + rect.width);
+ var bottom = Math.min(fitRect.bottom - margin.bottom, top + rect.height);
+
+ var minWidth = this._fitInfo.sizedBy.minWidth;
+ var minHeight = this._fitInfo.sizedBy.minHeight;
+ if (left < margin.left) {
+ left = margin.left;
+ if (right - left < minWidth) {
+ left = right - minWidth;
+ }
+ }
+ if (top < margin.top) {
+ top = margin.top;
+ if (bottom - top < minHeight) {
+ top = bottom - minHeight;
+ }
+ }
+
+ this.sizingTarget.style.maxWidth = (right - left) + 'px';
+ this.sizingTarget.style.maxHeight = (bottom - top) + 'px';
+
+ // Remove the offset caused by any stacking context.
+ this.style.left = (left - rect.left) + 'px';
+ this.style.top = (top - rect.top) + 'px';
},
/**
- * Constrains the size of the element to the window or `fitInfo` by setting `max-height`
+ * Constrains the size of the element to `fitInto` by setting `max-height`
* and/or `max-width`.
*/
constrain: function() {
+ if (this.horizontalAlign || this.verticalAlign) {
+ return;
+ }
var info = this._fitInfo;
// position at (0px, 0px) if not already positioned, so we can measure the natural size.
- if (!this._fitInfo.positionedBy.vertically) {
+ if (!info.positionedBy.vertically) {
+ this.style.position = 'fixed';
this.style.top = '0px';
}
- if (!this._fitInfo.positionedBy.horizontally) {
- this.style.left = '0px';
- }
- if (!this._fitInfo.positionedBy.vertically || !this._fitInfo.positionedBy.horizontally) {
- // need position:fixed to properly size the element
+ if (!info.positionedBy.horizontally) {
this.style.position = 'fixed';
+ this.style.left = '0px';
}
+
// need border-box for margin/padding
this.sizingTarget.style.boxSizing = 'border-box';
// constrain the width and height if not already set
var rect = this.getBoundingClientRect();
if (!info.sizedBy.height) {
- this._sizeDimension(rect, info.positionedBy.vertically, 'top', 'bottom', 'Height');
+ this.__sizeDimension(rect, info.positionedBy.vertically, 'top', 'bottom', 'Height');
}
if (!info.sizedBy.width) {
- this._sizeDimension(rect, info.positionedBy.horizontally, 'left', 'right', 'Width');
+ this.__sizeDimension(rect, info.positionedBy.horizontally, 'left', 'right', 'Width');
}
},
+ /**
+ * @protected
+ * @deprecated
+ */
_sizeDimension: function(rect, positionedBy, start, end, extent) {
+ this.__sizeDimension(rect, positionedBy, start, end, extent);
+ },
+
+ /**
+ * @private
+ */
+ __sizeDimension: function(rect, positionedBy, start, end, extent) {
var info = this._fitInfo;
- var max = extent === 'Width' ? this._fitWidth : this._fitHeight;
+ var fitRect = this.__getNormalizedRect(this.fitInto);
+ var max = extent === 'Width' ? fitRect.width : fitRect.height;
var flip = (positionedBy === end);
var offset = flip ? max - rect[end] : rect[start];
var margin = info.margin[flip ? start : end];
@@ -239,6 +448,9 @@ CSS properties | Action
* `position:fixed`.
*/
center: function() {
+ if (this.horizontalAlign || this.verticalAlign) {
+ return;
+ }
var positionedBy = this._fitInfo.positionedBy;
if (positionedBy.vertically && positionedBy.horizontally) {
// Already positioned.
@@ -257,16 +469,126 @@ CSS properties | Action
}
// It will take in consideration margins and transforms
var rect = this.getBoundingClientRect();
+ var fitRect = this.__getNormalizedRect(this.fitInto);
if (!positionedBy.vertically) {
- var top = this._fitTop - rect.top + (this._fitHeight - rect.height) / 2;
+ var top = fitRect.top - rect.top + (fitRect.height - rect.height) / 2;
this.style.top = top + 'px';
}
if (!positionedBy.horizontally) {
- var left = this._fitLeft - rect.left + (this._fitWidth - rect.width) / 2;
+ var left = fitRect.left - rect.left + (fitRect.width - rect.width) / 2;
this.style.left = left + 'px';
}
+ },
+
+ __getNormalizedRect: function(target) {
+ if (target === document.documentElement || target === window) {
+ return {
+ top: 0,
+ left: 0,
+ width: window.innerWidth,
+ height: window.innerHeight,
+ right: window.innerWidth,
+ bottom: window.innerHeight
+ };
+ }
+ return target.getBoundingClientRect();
+ },
+
+ __getCroppedArea: function(position, size, fitRect) {
+ var verticalCrop = Math.min(0, position.top) + Math.min(0, fitRect.bottom - (position.top + size.height));
+ var horizontalCrop = Math.min(0, position.left) + Math.min(0, fitRect.right - (position.left + size.width));
+ return Math.abs(verticalCrop) * size.width + Math.abs(horizontalCrop) * size.height;
+ },
+
+
+ __getPosition: function(hAlign, vAlign, size, positionRect, fitRect) {
+ // All the possible configurations.
+ // Ordered as top-left, top-right, bottom-left, bottom-right.
+ var positions = [{
+ verticalAlign: 'top',
+ horizontalAlign: 'left',
+ top: positionRect.top,
+ left: positionRect.left
+ }, {
+ verticalAlign: 'top',
+ horizontalAlign: 'right',
+ top: positionRect.top,
+ left: positionRect.right - size.width
+ }, {
+ verticalAlign: 'bottom',
+ horizontalAlign: 'left',
+ top: positionRect.bottom - size.height,
+ left: positionRect.left
+ }, {
+ verticalAlign: 'bottom',
+ horizontalAlign: 'right',
+ top: positionRect.bottom - size.height,
+ left: positionRect.right - size.width
+ }];
+
+ if (this.noOverlap) {
+ // Duplicate.
+ for (var i = 0, l = positions.length; i < l; i++) {
+ var copy = {};
+ for (var key in positions[i]) {
+ copy[key] = positions[i][key];
+ }
+ positions.push(copy);
+ }
+ // Horizontal overlap only.
+ positions[0].top = positions[1].top += positionRect.height;
+ positions[2].top = positions[3].top -= positionRect.height;
+ // Vertical overlap only.
+ positions[4].left = positions[6].left += positionRect.width;
+ positions[5].left = positions[7].left -= positionRect.width;
+ }
+
+ // Consider auto as null for coding convenience.
+ vAlign = vAlign === 'auto' ? null : vAlign;
+ hAlign = hAlign === 'auto' ? null : hAlign;
+
+ var position;
+ for (var i = 0; i < positions.length; i++) {
+ var pos = positions[i];
+
+ // If both vAlign and hAlign are defined, return exact match.
+ // For dynamicAlign and noOverlap we'll have more than one candidate, so
+ // we'll have to check the croppedArea to make the best choice.
+ if (!this.dynamicAlign && !this.noOverlap &&
+ pos.verticalAlign === vAlign && pos.horizontalAlign === hAlign) {
+ position = pos;
+ break;
+ }
+
+ // Align is ok if alignment preferences are respected. If no preferences,
+ // it is considered ok.
+ var alignOk = (!vAlign || pos.verticalAlign === vAlign) &&
+ (!hAlign || pos.horizontalAlign === hAlign);
+
+ // Filter out elements that don't match the alignment (if defined).
+ // With dynamicAlign, we need to consider all the positions to find the
+ // one that minimizes the cropped area.
+ if (!this.dynamicAlign && !alignOk) {
+ continue;
+ }
+
+ position = position || pos;
+ pos.croppedArea = this.__getCroppedArea(pos, size, fitRect);
+ var diff = pos.croppedArea - position.croppedArea;
+ // Check which crops less. If it crops equally, check if align is ok.
+ if (diff < 0 || (diff === 0 && alignOk)) {
+ position = pos;
+ }
+ // If not cropped and respects the align requirements, keep it.
+ // This allows to prefer positions overlapping horizontally over the
+ // ones overlapping vertically.
+ if (position.croppedArea === 0 && alignOk) {
+ break;
+ }
+ }
+
+ return position;
}
};
-
</script>

Powered by Google App Engine
This is Rietveld 408576698