Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

fix(position): improve placing around corners #4293

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 82 additions & 54 deletions src/position/position.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,74 @@ angular.module('ui.bootstrap.position', [])
return (getStyle(element, 'position') || 'static' ) === 'static';
}

/**
* Computes the `left` position property of a target element to be
* positioned.
* @param place the placement of the target element relative to the
* host. For instance, place `'bottom'` means the target element
* will be placed below the host.
* @param align the alignment of the element relative to the host.
* For instance, align `'left'` means align the element with the host's
* left edge. `'center'` may be passed to center the element.
* @param hostElPos the host element position, having at least a `width`
* and `left` property.
* @param targetElWidth the width of the target element as a number of
* pixels.
*/
function computeLeftProperty(place, align, hostElPos, targetElWidth) {
if (place === 'left') {
return hostElPos.left - targetElWidth;
}

if (place === 'top' || place === 'bottom') {
if (align === 'left') {
return hostElPos.left;
}
if (align === 'center') {
return (hostElPos.left + hostElPos.width / 2) - (targetElWidth / 2);
}
// align === 'right'
return (hostElPos.left + hostElPos.width) - targetElWidth;
}

// place === 'right'
return hostElPos.left + hostElPos.width;
}

/**
* Computes the `top` position property of a target element to be
* positioned.
* @param place the placement of the target element relative to the
* host. For instance, place `'bottom'` means the target element
* will be placed below the host.
* @param align the alignment of the element relative to the host.
* For instance, align `'left'` means align the element with the host's
* left edge. `'center'` may be passed to center the element.
* @param hostElPos the host element position, having at least a `height`
* and `top` property.
* @param targetElHeight the height of the target element as a number of
* pixels.
*/
function computeTopProperty(place, align, hostElPos, targetElHeight) {
if (place === 'top') {
return hostElPos.top - targetElHeight;
}

if (place === 'right' || place === 'left') {
if (align === 'top') {
return hostElPos.top;
}
if (align === 'center') {
return (hostElPos.top + hostElPos.height / 2) - (targetElHeight / 2);
}
// align === 'bottom'
return (hostElPos.top + hostElPos.height) - targetElHeight;
}

// place === 'bottom'
return hostElPos.top + hostElPos.height;
}

/**
* returns the closest, non-statically positioned parentOffset of a given element
* @param element
Expand Down Expand Up @@ -81,70 +149,30 @@ angular.module('ui.bootstrap.position', [])
*/
positionElements: function(hostEl, targetEl, positionStr, appendToBody) {
var positionStrParts = positionStr.split('-');
var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center';
var place = positionStrParts[0],
align = positionStrParts[1] || 'center',
targetElPos = {};

var hostElPos,
targetElWidth,
targetElHeight,
targetElPos;
targetElHeight;

if (place !== 'top' && place !== 'right' && place !== 'bottom' && place !== 'left') {
place = 'top';
}
if (align !== 'top' && align !== 'right' && align !== 'bottom' && align !== 'left' && align !== 'center') {
align = 'center';
}

hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);

targetElWidth = targetEl.prop('offsetWidth');
targetElHeight = targetEl.prop('offsetHeight');

var shiftWidth = {
center: function() {
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
},
left: function() {
return hostElPos.left;
},
right: function() {
return hostElPos.left + hostElPos.width;
}
};

var shiftHeight = {
center: function() {
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
},
top: function() {
return hostElPos.top;
},
bottom: function() {
return hostElPos.top + hostElPos.height;
}
return {
top: computeTopProperty(place, align, hostElPos, targetElHeight),
left: computeLeftProperty(place, align, hostElPos, targetElWidth)
};

switch (pos0) {
case 'right':
targetElPos = {
top: shiftHeight[pos1](),
left: shiftWidth[pos0]()
};
break;
case 'left':
targetElPos = {
top: shiftHeight[pos1](),
left: hostElPos.left - targetElWidth
};
break;
case 'bottom':
targetElPos = {
top: shiftHeight[pos0](),
left: shiftWidth[pos1]()
};
break;
default:
targetElPos = {
top: hostElPos.top - targetElHeight,
left: shiftWidth[pos1]()
};
break;
}

return targetElPos;
}
};
}]);
10 changes: 5 additions & 5 deletions src/position/test/position.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('position elements', function () {
});

it('should position on top-right', function() {
expect($position.positionElements({}, new TargetElMock(10, 10), 'top-right')).toBePositionedAt(90, 120);
expect($position.positionElements({}, new TargetElMock(10, 10), 'top-right')).toBePositionedAt(90, 110);
});

it('should position elements on bottom-center when "bottom" specified', function() {
Expand All @@ -76,7 +76,7 @@ describe('position elements', function () {
});

it('should position elements on bottom-right', function() {
expect($position.positionElements({}, new TargetElMock(10, 10), 'bottom-right')).toBePositionedAt(120, 120);
expect($position.positionElements({}, new TargetElMock(10, 10), 'bottom-right')).toBePositionedAt(120, 110);
});

it('should position elements on left-center when "left" specified', function() {
Expand All @@ -89,7 +89,7 @@ describe('position elements', function () {
});

it('should position elements on left-bottom when "left-bottom" specified', function() {
expect($position.positionElements({}, new TargetElMock(10, 10), 'left-bottom')).toBePositionedAt(120, 90);
expect($position.positionElements({}, new TargetElMock(10, 10), 'left-bottom')).toBePositionedAt(110, 90);
});

it('should position elements on right-center when "right" specified', function() {
Expand All @@ -101,8 +101,8 @@ describe('position elements', function () {
expect($position.positionElements({}, new TargetElMock(10, 10), 'right-top')).toBePositionedAt(100, 120);
});

it('should position elements on right-top when "right-top" specified', function() {
expect($position.positionElements({}, new TargetElMock(10, 10), 'right-bottom')).toBePositionedAt(120, 120);
it('should position elements on right-bottom when "right-bottom" specified', function() {
expect($position.positionElements({}, new TargetElMock(10, 10), 'right-bottom')).toBePositionedAt(110, 120);
});
});
});