Skip to content
This repository has been archived by the owner on Dec 19, 2024. It is now read-only.

Commit

Permalink
horizontalAlign = center, verticalAlign = middle (#78)
Browse files Browse the repository at this point in the history
* horizontalAlign = center, verticalAlign = middle

* rename variables & functions
  • Loading branch information
valdrinkoshi authored Oct 26, 2017
1 parent 8603fa1 commit 68aa26b
Show file tree
Hide file tree
Showing 3 changed files with 263 additions and 28 deletions.
2 changes: 2 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,14 @@ <h3>
<h2>Align</h2>
<p>
<button on-tap="updateAlign" vertical-align="top">top</button>
<button on-tap="updateAlign" vertical-align="middle">middle</button>
<button on-tap="updateAlign" vertical-align="bottom">bottom</button>
<button on-tap="updateAlign" vertical-align="auto">auto</button>
<button on-tap="updateAlign" vertical-align>null</button>
</p>
<p>
<button on-tap="updateAlign" horizontal-align="left">left</button>
<button on-tap="updateAlign" horizontal-align="center">center</button>
<button on-tap="updateAlign" horizontal-align="right">right</button>
<button on-tap="updateAlign" horizontal-align="auto">auto</button>
<button on-tap="updateAlign" horizontal-align>null</button>
Expand Down
97 changes: 69 additions & 28 deletions iron-fit-behavior.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,15 @@

/**
* The orientation against which to align the element horizontally
* relative to the `positionTarget`. Possible values are "left", "right", "auto".
* relative to the `positionTarget`. Possible values are "left", "right", "center", "auto".
*/
horizontalAlign: {
type: String
},

/**
* The orientation against which to align the element vertically
* relative to the `positionTarget`. Possible values are "top", "bottom", "auto".
* relative to the `positionTarget`. Possible values are "top", "bottom", "middle", "auto".
*/
verticalAlign: {
type: String
Expand All @@ -128,8 +128,8 @@
* of it as increasing or decreasing the distance to the side of the
* screen given by `horizontalAlign`.
*
* If `horizontalAlign` is "left", this offset will increase or decrease
* the distance to the left side of the screen: a negative offset will
* If `horizontalAlign` is "left" or "center", this offset will increase or
* decrease the distance to the left side of the screen: a negative offset will
* move the dropdown to the left; a positive one, to the right.
*
* Conversely if `horizontalAlign` is "right", this offset will increase
Expand All @@ -148,8 +148,8 @@
* of it as increasing or decreasing the distance to the side of the
* screen given by `verticalAlign`.
*
* If `verticalAlign` is "top", this offset will increase or decrease
* the distance to the top side of the screen: a negative offset will
* If `verticalAlign` is "top" or "middle", this offset will increase or
* decrease the distance to the top side of the screen: a negative offset will
* move the dropdown upwards; a positive one, downwards.
*
* Conversely if `verticalAlign` is "bottom", this offset will increase
Expand Down Expand Up @@ -246,6 +246,15 @@
return this.horizontalAlign;
},

/**
* True if the element should be positioned instead of centered.
* @private
*/
get __shouldPosition() {
return (this.horizontalAlign || this.verticalAlign) &&
(this.horizontalAlign !== 'center' || this.verticalAlign !== 'middle');
},

attached: function() {
// Memoize this to avoid expensive calculations & relayouts.
// Make sure we do it only once
Expand Down Expand Up @@ -362,7 +371,7 @@
* Positions the element according to `horizontalAlign, verticalAlign`.
*/
position: function() {
if (!this.horizontalAlign && !this.verticalAlign) {
if (!this.__shouldPosition) {
// needs to be centered, and it is done after constrain.
return;
}
Expand All @@ -387,7 +396,7 @@
height: rect.height + margin.top + margin.bottom
};

var position = this.__getPosition(this._localeHorizontalAlign, this.verticalAlign, size, positionRect,
var position = this.__getPosition(this._localeHorizontalAlign, this.verticalAlign, size, rect, positionRect,
fitRect);

var left = position.left + margin.left;
Expand Down Expand Up @@ -418,7 +427,7 @@
* and/or `max-width`.
*/
constrain: function() {
if (this.horizontalAlign || this.verticalAlign) {
if (this.__shouldPosition) {
return;
}
this._discoverInfo();
Expand Down Expand Up @@ -474,7 +483,7 @@
* `position:fixed`.
*/
center: function() {
if (this.horizontalAlign || this.verticalAlign) {
if (this.__shouldPosition) {
return;
}
this._discoverInfo();
Expand Down Expand Up @@ -522,14 +531,14 @@
return target.getBoundingClientRect();
},

__getCroppedArea: function(position, size, fitRect) {
__getOffscreenArea: 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) {
__getPosition: function(hAlign, vAlign, size, sizeNoMargins, positionRect, fitRect) {
// All the possible configurations.
// Ordered as top-left, top-right, bottom-left, bottom-right.
var positions = [{
Expand Down Expand Up @@ -575,23 +584,53 @@
vAlign = vAlign === 'auto' ? null : vAlign;
hAlign = hAlign === 'auto' ? null : hAlign;

if (!hAlign || hAlign === 'center') {
positions.push({
verticalAlign: 'top',
horizontalAlign: 'center',
top: positionRect.top + this.verticalOffset + (this.noOverlap ? positionRect.height : 0),
left: positionRect.left - sizeNoMargins.width / 2 + positionRect.width / 2 + this.horizontalOffset
});
positions.push({
verticalAlign: 'bottom',
horizontalAlign: 'center',
top: positionRect.bottom - size.height - this.verticalOffset - (this.noOverlap ? positionRect.height : 0),
left: positionRect.left - sizeNoMargins.width / 2 + positionRect.width / 2 + this.horizontalOffset
});
}

if (!vAlign || vAlign === 'middle') {
positions.push({
verticalAlign: 'middle',
horizontalAlign: 'left',
top: positionRect.top - sizeNoMargins.height / 2 + positionRect.height / 2 + this.verticalOffset,
left: positionRect.left + this.horizontalOffset + (this.noOverlap ? positionRect.width : 0)
});
positions.push({
verticalAlign: 'middle',
horizontalAlign: 'right',
top: positionRect.top - sizeNoMargins.height / 2 + positionRect.height / 2 + this.verticalOffset,
left: positionRect.right - size.width - this.horizontalOffset - (this.noOverlap ? positionRect.width : 0)
});
}

var position;
for (var i = 0; i < positions.length; i++) {
var pos = positions[i];
var candidate = positions[i];
var vAlignOk = candidate.verticalAlign === vAlign;
var hAlignOk = candidate.horizontalAlign === hAlign;

// 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;
// we'll have to check the offscreenArea to make the best choice.
if (!this.dynamicAlign && !this.noOverlap && vAlignOk && hAlignOk) {
position = candidate;
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);
var alignOk = (!vAlign || vAlignOk) && (!hAlign || hAlignOk);

// Filter out elements that don't match the alignment (if defined).
// With dynamicAlign, we need to consider all the positions to find the
Expand All @@ -600,23 +639,25 @@
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;
}
candidate.offscreenArea = this.__getOffscreenArea(candidate, size, fitRect);
// 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) {
if (candidate.offscreenArea === 0 && alignOk) {
position = candidate;
break;
}
position = position || candidate;
var diff = candidate.offscreenArea - position.offscreenArea;
// Check which crops less. If it crops equally, check if at least one
// align setting is ok.
if (diff < 0 || (diff === 0 && (vAlignOk || hAlignOk))) {
position = candidate;
}
}

return position;
}

};
</script>
</script>
Loading

0 comments on commit 68aa26b

Please sign in to comment.