mirror of https://github.com/rancher/ui.git
use new pos calc to ensure correct above and below pos
rancher/rancher#13535
This commit is contained in:
parent
3c8e5d601b
commit
8838158d1d
|
|
@ -2,6 +2,7 @@ import Component from '@ember/component';
|
|||
import layout from './template';
|
||||
import { inject as service } from '@ember/service'
|
||||
import { computed, get } from '@ember/object';
|
||||
import calculatePosition from 'shared/utils/calculate-position';
|
||||
|
||||
|
||||
export default Component.extend({
|
||||
|
|
@ -40,6 +41,8 @@ export default Component.extend({
|
|||
get(this, 'tooltipService').hide();
|
||||
},
|
||||
|
||||
calculatePosition: calculatePosition,
|
||||
|
||||
},
|
||||
|
||||
sizeClass: computed('size', function() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{{#basic-dropdown
|
||||
horizontalPosition="right"
|
||||
verticalPosition="below"
|
||||
calculatePosition=(action "calculatePosition" )
|
||||
onOpen=(action 'actionsOpen')
|
||||
onClose=(action 'actionsClosed')
|
||||
as |dd|
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import layout from './template';
|
|||
import { cancel, later } from '@ember/runloop';
|
||||
import { oneWay } from '@ember/object/computed';
|
||||
import { get, set } from '@ember/object';
|
||||
import calculatePosition from 'shared/utils/calculate-position';
|
||||
|
||||
export default Component.extend({
|
||||
layout,
|
||||
|
|
@ -66,181 +67,6 @@ export default Component.extend({
|
|||
return false;
|
||||
},
|
||||
|
||||
calculatePosition(trigger, content, destination, { horizontalPosition, verticalPosition, matchTriggerWidth, previousHorizontalPosition, previousVerticalPosition }) {
|
||||
// Re-implement calculateWormholePosition
|
||||
// https://git.io/vhf7p
|
||||
// See WJW comment below
|
||||
let scroll = { left: window.pageXOffset, top: window.pageYOffset };
|
||||
let {
|
||||
left: triggerLeft,
|
||||
top: triggerTop,
|
||||
width: triggerWidth,
|
||||
height: triggerHeight
|
||||
} = trigger.getBoundingClientRect();
|
||||
let {
|
||||
height: dropdownHeight,
|
||||
width: dropdownWidth
|
||||
} = content.getBoundingClientRect();
|
||||
let viewportWidth = document.body.clientWidth || window.innerWidth;
|
||||
let style = {};
|
||||
|
||||
// Apply containers' offset
|
||||
let anchorElement = destination.parentNode;
|
||||
let anchorPosition = window.getComputedStyle(anchorElement).position;
|
||||
|
||||
while (anchorPosition !== 'relative' && anchorPosition !== 'absolute' && anchorElement.tagName.toUpperCase() !== 'BODY') {
|
||||
anchorElement = anchorElement.parentNode;
|
||||
anchorPosition = window.getComputedStyle(anchorElement).position;
|
||||
}
|
||||
|
||||
if (anchorPosition === 'relative' || anchorPosition === 'absolute') {
|
||||
|
||||
let rect = anchorElement.getBoundingClientRect();
|
||||
triggerLeft = triggerLeft - rect.left;
|
||||
triggerTop = triggerTop - rect.top;
|
||||
let { offsetParent } = anchorElement;
|
||||
|
||||
if (offsetParent) {
|
||||
triggerLeft -= anchorElement.offsetParent.scrollLeft;
|
||||
triggerTop -= anchorElement.offsetParent.scrollTop;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Calculate drop down width
|
||||
dropdownWidth = matchTriggerWidth ? triggerWidth : dropdownWidth;
|
||||
|
||||
if (matchTriggerWidth) {
|
||||
style.width = dropdownWidth;
|
||||
}
|
||||
|
||||
// Calculate horizontal position
|
||||
let triggerLeftWithScroll = triggerLeft + scroll.left;
|
||||
|
||||
if (horizontalPosition === 'auto' || horizontalPosition === 'auto-left') {
|
||||
|
||||
// Calculate the number of visible horizontal pixels if we were to place the
|
||||
// dropdown on the left and right
|
||||
let leftVisible = Math.min(viewportWidth, triggerLeft + dropdownWidth) - Math.max(0, triggerLeft);
|
||||
let rightVisible = Math.min(viewportWidth, triggerLeft + triggerWidth) - Math.max(0, triggerLeft + triggerWidth - dropdownWidth);
|
||||
|
||||
if (dropdownWidth > leftVisible && rightVisible > leftVisible) {
|
||||
|
||||
// If the drop down won't fit left-aligned, and there is more space on the
|
||||
// right than on the left, then force right-aligned
|
||||
horizontalPosition = 'right';
|
||||
|
||||
} else if (dropdownWidth > rightVisible && leftVisible > rightVisible) {
|
||||
|
||||
// If the drop down won't fit right-aligned, and there is more space on
|
||||
// the left than on the right, then force left-aligned
|
||||
horizontalPosition = 'left';
|
||||
|
||||
} else {
|
||||
|
||||
// Keep same position as previous
|
||||
horizontalPosition = previousHorizontalPosition || 'left';
|
||||
|
||||
}
|
||||
} else if (horizontalPosition === 'auto-right') {
|
||||
|
||||
// Calculate the number of visible horizontal pixels if we were to place the
|
||||
// dropdown on the left and right
|
||||
let leftVisible = Math.min(viewportWidth, triggerLeft + dropdownWidth) - Math.max(0, triggerLeft);
|
||||
let rightVisible = Math.min(viewportWidth, triggerLeft + triggerWidth) - Math.max(0, triggerLeft + triggerWidth - dropdownWidth);
|
||||
|
||||
if (dropdownWidth > rightVisible && leftVisible > rightVisible) {
|
||||
|
||||
// If the drop down won't fit right-aligned, and there is more space on the
|
||||
// left than on the right, then force left-aligned
|
||||
horizontalPosition = 'left';
|
||||
|
||||
} else if (dropdownWidth > leftVisible && rightVisible > leftVisible) {
|
||||
|
||||
// If the drop down won't fit left-aligned, and there is more space on
|
||||
// the right than on the left, then force right-aligned
|
||||
horizontalPosition = 'right';
|
||||
|
||||
} else {
|
||||
|
||||
// Keep same position as previous
|
||||
horizontalPosition = previousHorizontalPosition || 'right';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (horizontalPosition === 'right') {
|
||||
|
||||
style.right = viewportWidth - (triggerLeftWithScroll + triggerWidth);
|
||||
|
||||
} else if (horizontalPosition === 'center') {
|
||||
|
||||
style.left = triggerLeftWithScroll + (triggerWidth - dropdownWidth) / 2;
|
||||
|
||||
} else {
|
||||
|
||||
style.left = triggerLeftWithScroll;
|
||||
|
||||
}
|
||||
|
||||
// Calculate vertical position
|
||||
let triggerTopWithScroll = triggerTop;
|
||||
|
||||
/**
|
||||
* Fixes bug where the dropdown always stays on the same position on the screen when
|
||||
* the <body> is relatively positioned
|
||||
*/
|
||||
let isBodyPositionRelative = window.getComputedStyle(document.body).getPropertyValue('position') === 'relative';
|
||||
|
||||
if (!isBodyPositionRelative) {
|
||||
triggerTopWithScroll += scroll.top;
|
||||
}
|
||||
|
||||
if (verticalPosition === 'above') {
|
||||
|
||||
style.top = triggerTopWithScroll - dropdownHeight;
|
||||
|
||||
} else if (verticalPosition === 'below') {
|
||||
|
||||
style.top = triggerTopWithScroll + triggerHeight;
|
||||
|
||||
} else {
|
||||
|
||||
let viewportBottom = scroll.top + window.innerHeight;
|
||||
let enoughRoomBelow = triggerTopWithScroll + triggerHeight + dropdownHeight < viewportBottom;
|
||||
let enoughRoomAbove = triggerTop > dropdownHeight;
|
||||
|
||||
if (previousVerticalPosition === 'below' && !enoughRoomBelow && enoughRoomAbove) {
|
||||
|
||||
verticalPosition = 'above';
|
||||
|
||||
} else if (previousVerticalPosition === 'above' && !enoughRoomAbove && enoughRoomBelow) {
|
||||
|
||||
verticalPosition = 'below';
|
||||
|
||||
} else if (!previousVerticalPosition) {
|
||||
|
||||
// WJW - Library code always opts for above if we can't go either way
|
||||
// I used all the lib code and opted for always going below in the event that
|
||||
// we can't go above or below.
|
||||
// other wise the lib positioning works as it does normally
|
||||
if (!enoughRoomAbove && !enoughRoomBelow) {
|
||||
verticalPosition = 'below';
|
||||
} else {
|
||||
verticalPosition = enoughRoomBelow ? 'below' : 'above';
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
verticalPosition = previousVerticalPosition;
|
||||
|
||||
}
|
||||
|
||||
style.top = triggerTopWithScroll + (verticalPosition === 'below' ? triggerHeight : -dropdownHeight);
|
||||
|
||||
}
|
||||
|
||||
return { horizontalPosition, verticalPosition, style };
|
||||
},
|
||||
calculatePosition: calculatePosition,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,176 @@
|
|||
export default function calculatePosition(trigger, content, destination, { horizontalPosition, verticalPosition, matchTriggerWidth, previousHorizontalPosition, previousVerticalPosition }) {
|
||||
// Re-implement calculateWormholePosition
|
||||
// https://git.io/vhf7p
|
||||
// See WJW comment below
|
||||
let scroll = { left: window.pageXOffset, top: window.pageYOffset };
|
||||
let {
|
||||
left: triggerLeft,
|
||||
top: triggerTop,
|
||||
width: triggerWidth,
|
||||
height: triggerHeight
|
||||
} = trigger.getBoundingClientRect();
|
||||
let {
|
||||
height: dropdownHeight,
|
||||
width: dropdownWidth
|
||||
} = content.getBoundingClientRect();
|
||||
let viewportWidth = document.body.clientWidth || window.innerWidth;
|
||||
let style = {};
|
||||
|
||||
// Apply containers' offset
|
||||
let anchorElement = destination.parentNode;
|
||||
let anchorPosition = window.getComputedStyle(anchorElement).position;
|
||||
|
||||
while (anchorPosition !== 'relative' && anchorPosition !== 'absolute' && anchorElement.tagName.toUpperCase() !== 'BODY') {
|
||||
anchorElement = anchorElement.parentNode;
|
||||
anchorPosition = window.getComputedStyle(anchorElement).position;
|
||||
}
|
||||
|
||||
if (anchorPosition === 'relative' || anchorPosition === 'absolute') {
|
||||
|
||||
let rect = anchorElement.getBoundingClientRect();
|
||||
triggerLeft = triggerLeft - rect.left;
|
||||
triggerTop = triggerTop - rect.top;
|
||||
let { offsetParent } = anchorElement;
|
||||
|
||||
if (offsetParent) {
|
||||
triggerLeft -= anchorElement.offsetParent.scrollLeft;
|
||||
triggerTop -= anchorElement.offsetParent.scrollTop;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Calculate drop down width
|
||||
dropdownWidth = matchTriggerWidth ? triggerWidth : dropdownWidth;
|
||||
|
||||
if (matchTriggerWidth) {
|
||||
style.width = dropdownWidth;
|
||||
}
|
||||
|
||||
// Calculate horizontal position
|
||||
let triggerLeftWithScroll = triggerLeft + scroll.left;
|
||||
|
||||
if (horizontalPosition === 'auto' || horizontalPosition === 'auto-left') {
|
||||
|
||||
// Calculate the number of visible horizontal pixels if we were to place the
|
||||
// dropdown on the left and right
|
||||
let leftVisible = Math.min(viewportWidth, triggerLeft + dropdownWidth) - Math.max(0, triggerLeft);
|
||||
let rightVisible = Math.min(viewportWidth, triggerLeft + triggerWidth) - Math.max(0, triggerLeft + triggerWidth - dropdownWidth);
|
||||
|
||||
if (dropdownWidth > leftVisible && rightVisible > leftVisible) {
|
||||
|
||||
// If the drop down won't fit left-aligned, and there is more space on the
|
||||
// right than on the left, then force right-aligned
|
||||
horizontalPosition = 'right';
|
||||
|
||||
} else if (dropdownWidth > rightVisible && leftVisible > rightVisible) {
|
||||
|
||||
// If the drop down won't fit right-aligned, and there is more space on
|
||||
// the left than on the right, then force left-aligned
|
||||
horizontalPosition = 'left';
|
||||
|
||||
} else {
|
||||
|
||||
// Keep same position as previous
|
||||
horizontalPosition = previousHorizontalPosition || 'left';
|
||||
|
||||
}
|
||||
} else if (horizontalPosition === 'auto-right') {
|
||||
|
||||
// Calculate the number of visible horizontal pixels if we were to place the
|
||||
// dropdown on the left and right
|
||||
let leftVisible = Math.min(viewportWidth, triggerLeft + dropdownWidth) - Math.max(0, triggerLeft);
|
||||
let rightVisible = Math.min(viewportWidth, triggerLeft + triggerWidth) - Math.max(0, triggerLeft + triggerWidth - dropdownWidth);
|
||||
|
||||
if (dropdownWidth > rightVisible && leftVisible > rightVisible) {
|
||||
|
||||
// If the drop down won't fit right-aligned, and there is more space on the
|
||||
// left than on the right, then force left-aligned
|
||||
horizontalPosition = 'left';
|
||||
|
||||
} else if (dropdownWidth > leftVisible && rightVisible > leftVisible) {
|
||||
|
||||
// If the drop down won't fit left-aligned, and there is more space on
|
||||
// the right than on the left, then force right-aligned
|
||||
horizontalPosition = 'right';
|
||||
|
||||
} else {
|
||||
|
||||
// Keep same position as previous
|
||||
horizontalPosition = previousHorizontalPosition || 'right';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (horizontalPosition === 'right') {
|
||||
|
||||
style.right = viewportWidth - (triggerLeftWithScroll + triggerWidth);
|
||||
|
||||
} else if (horizontalPosition === 'center') {
|
||||
|
||||
style.left = triggerLeftWithScroll + (triggerWidth - dropdownWidth) / 2;
|
||||
|
||||
} else {
|
||||
|
||||
style.left = triggerLeftWithScroll;
|
||||
|
||||
}
|
||||
|
||||
// Calculate vertical position
|
||||
let triggerTopWithScroll = triggerTop;
|
||||
|
||||
/**
|
||||
* Fixes bug where the dropdown always stays on the same position on the screen when
|
||||
* the <body> is relatively positioned
|
||||
*/
|
||||
let isBodyPositionRelative = window.getComputedStyle(document.body).getPropertyValue('position') === 'relative';
|
||||
|
||||
if (!isBodyPositionRelative) {
|
||||
triggerTopWithScroll += scroll.top;
|
||||
}
|
||||
|
||||
if (verticalPosition === 'above') {
|
||||
|
||||
style.top = triggerTopWithScroll - dropdownHeight;
|
||||
|
||||
} else if (verticalPosition === 'below') {
|
||||
|
||||
style.top = triggerTopWithScroll + triggerHeight;
|
||||
|
||||
} else {
|
||||
|
||||
let viewportBottom = scroll.top + window.innerHeight;
|
||||
let enoughRoomBelow = triggerTopWithScroll + triggerHeight + dropdownHeight < viewportBottom;
|
||||
let enoughRoomAbove = triggerTop > dropdownHeight;
|
||||
|
||||
if (previousVerticalPosition === 'below' && !enoughRoomBelow && enoughRoomAbove) {
|
||||
|
||||
verticalPosition = 'above';
|
||||
|
||||
} else if (previousVerticalPosition === 'above' && !enoughRoomAbove && enoughRoomBelow) {
|
||||
|
||||
verticalPosition = 'below';
|
||||
|
||||
} else if (!previousVerticalPosition) {
|
||||
|
||||
// WJW - Library code always opts for above if we can't go either way
|
||||
// I used all the lib code and opted for always going below in the event that
|
||||
// we can't go above or below.
|
||||
// other wise the lib positioning works as it does normally
|
||||
if (!enoughRoomAbove && !enoughRoomBelow) {
|
||||
verticalPosition = 'below';
|
||||
} else {
|
||||
verticalPosition = enoughRoomBelow ? 'below' : 'above';
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
verticalPosition = previousVerticalPosition;
|
||||
|
||||
}
|
||||
|
||||
style.top = triggerTopWithScroll + (verticalPosition === 'below' ? triggerHeight : -dropdownHeight);
|
||||
|
||||
}
|
||||
|
||||
return { horizontalPosition, verticalPosition, style };
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from 'shared/calculate-position/util';
|
||||
Loading…
Reference in New Issue