mirror of
https://github.com/Kong/insomnia
synced 2024-11-08 06:39:48 +00:00
More reliable dropdown positioning (Fixes #1113)
This commit is contained in:
parent
2b971aed1e
commit
6f355bc7ba
@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="en-US">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Insomnia</title>
|
<title>Insomnia</title>
|
||||||
@ -8,6 +8,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
<div id="dropdowns-container" style="z-index:1000000;position:relative"></div>
|
||||||
<script src="bundle.js" type="application/javascript"></script>
|
<script src="bundle.js" type="application/javascript"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -10,6 +10,8 @@ import { fuzzyMatch } from '../../../../common/misc';
|
|||||||
import KeydownBinder from '../../keydown-binder';
|
import KeydownBinder from '../../keydown-binder';
|
||||||
import * as hotkeys from '../../../../common/hotkeys';
|
import * as hotkeys from '../../../../common/hotkeys';
|
||||||
|
|
||||||
|
const dropdownsContainer = document.querySelector('#dropdowns-container');
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
class Dropdown extends PureComponent {
|
class Dropdown extends PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -123,13 +125,17 @@ class Dropdown extends PureComponent {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make the dropdown scroll if it drops off screen.
|
// Get dropdown menu
|
||||||
const dropdownRect = this._node.getBoundingClientRect();
|
const dropdownList = this._dropdownList;
|
||||||
|
|
||||||
|
// Compute the size of all the menus
|
||||||
|
const dropdownBtnRect = this._node.getBoundingClientRect();
|
||||||
const bodyRect = document.body.getBoundingClientRect();
|
const bodyRect = document.body.getBoundingClientRect();
|
||||||
|
const dropdownListRect = dropdownList.getBoundingClientRect();
|
||||||
|
|
||||||
// Should it drop up?
|
// Should it drop up?
|
||||||
const bodyHeight = bodyRect.height;
|
const bodyHeight = bodyRect.height;
|
||||||
const dropdownTop = dropdownRect.top;
|
const dropdownTop = dropdownBtnRect.top;
|
||||||
const dropUp = dropdownTop > bodyHeight - 200;
|
const dropUp = dropdownTop > bodyHeight - 200;
|
||||||
|
|
||||||
// Reset all the things so we can start fresh
|
// Reset all the things so we can start fresh
|
||||||
@ -140,45 +146,51 @@ class Dropdown extends PureComponent {
|
|||||||
this._dropdownList.style.minWidth = 'initial';
|
this._dropdownList.style.minWidth = 'initial';
|
||||||
this._dropdownList.style.maxWidth = 'initial';
|
this._dropdownList.style.maxWidth = 'initial';
|
||||||
|
|
||||||
// Make dropdown keep it's shape when filtering
|
if (!dropdownList.hasAttribute('data-fixed-shape')) {
|
||||||
const ul = this._dropdownList.querySelector('ul');
|
this._dropdownList.style.minHeight = `${dropdownListRect.height}px`;
|
||||||
const ulRect = ul.getBoundingClientRect();
|
this._dropdownList.style.minWidth = `${dropdownListRect.width}px`;
|
||||||
if (!ul.hasAttribute('data-fixed-shape')) {
|
this._dropdownList.style.width = '100%';
|
||||||
ul.style.minHeight = `${ulRect.height}px`;
|
this._dropdownList.setAttribute('data-fixed-shape', 'on');
|
||||||
ul.style.minWidth = `${ulRect.width}px`;
|
|
||||||
ul.style.width = '100%';
|
|
||||||
ul.setAttribute('data-fixed-shape', 'on');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const screenMargin = 5;
|
||||||
|
|
||||||
const { right, wide } = this.props;
|
const { right, wide } = this.props;
|
||||||
if (right || wide) {
|
if (right || wide) {
|
||||||
const { right: originalRight } = dropdownRect;
|
const { right: originalRight } = dropdownBtnRect;
|
||||||
|
|
||||||
// Prevent dropdown from squishing against left side of screen
|
// Prevent dropdown from squishing against left side of screen
|
||||||
const right = Math.max(220, originalRight);
|
const right = Math.max(dropdownListRect.width + screenMargin, originalRight);
|
||||||
|
|
||||||
const { beside } = this.props;
|
const { beside } = this.props;
|
||||||
const offset = beside ? dropdownRect.width - dropdownRect.height : 0;
|
const offset = beside ? dropdownBtnRect.width - 40 : 0;
|
||||||
this._dropdownList.style.right = `${bodyRect.width - right + offset}px`;
|
this._dropdownList.style.right = `${bodyRect.width - right + offset}px`;
|
||||||
this._dropdownList.style.maxWidth = `${right + offset}px`;
|
this._dropdownList.style.maxWidth = `${Math.min(dropdownListRect.width, right + offset)}px`;
|
||||||
this._dropdownList.style.minWidth = `${Math.min(right, 200)}px`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!right || wide) {
|
if (!right || wide) {
|
||||||
const { left } = dropdownRect;
|
const { left: originalLeft } = dropdownBtnRect;
|
||||||
|
|
||||||
const { beside } = this.props;
|
const { beside } = this.props;
|
||||||
const offset = beside ? dropdownRect.width - dropdownRect.height : 0;
|
const offset = beside ? dropdownBtnRect.width - 40 : 0;
|
||||||
|
|
||||||
|
// Prevent dropdown from squishing against right side of screen
|
||||||
|
const left =
|
||||||
|
Math.min(bodyRect.width - dropdownListRect.width - screenMargin, originalLeft) - offset;
|
||||||
|
|
||||||
this._dropdownList.style.left = `${left + offset}px`;
|
this._dropdownList.style.left = `${left + offset}px`;
|
||||||
this._dropdownList.style.maxWidth = `${bodyRect.width - left - 5 - offset}px`;
|
this._dropdownList.style.maxWidth = `${Math.min(
|
||||||
this._dropdownList.style.minWidth = `${Math.min(bodyRect.width - left, 200)}px`;
|
dropdownListRect.width,
|
||||||
|
bodyRect.width - left - offset
|
||||||
|
)}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dropUp) {
|
if (dropUp) {
|
||||||
const { top } = dropdownRect;
|
const { top } = dropdownBtnRect;
|
||||||
this._dropdownList.style.bottom = `${bodyRect.height - top}px`;
|
this._dropdownList.style.bottom = `${bodyRect.height - top}px`;
|
||||||
this._dropdownList.style.maxHeight = `${top - 5}px`;
|
this._dropdownList.style.maxHeight = `${top - 5}px`;
|
||||||
} else {
|
} else {
|
||||||
const { bottom } = dropdownRect;
|
const { bottom } = dropdownBtnRect;
|
||||||
this._dropdownList.style.top = `${bottom}px`;
|
this._dropdownList.style.top = `${bottom}px`;
|
||||||
this._dropdownList.style.maxHeight = `${bodyRect.height - bottom - 5}px`;
|
this._dropdownList.style.maxHeight = `${bodyRect.height - bottom - 5}px`;
|
||||||
}
|
}
|
||||||
@ -188,7 +200,7 @@ class Dropdown extends PureComponent {
|
|||||||
this.toggle();
|
this.toggle();
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleMouseDown(e) {
|
static _handleMouseDown(e) {
|
||||||
// Intercept mouse down so that clicks don't trigger things like drag and drop.
|
// Intercept mouse down so that clicks don't trigger things like drag and drop.
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
@ -233,35 +245,6 @@ class Dropdown extends PureComponent {
|
|||||||
this._checkSizeAndPosition();
|
this._checkSizeAndPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
_getContainer() {
|
|
||||||
let container = document.querySelector('#dropdowns-container');
|
|
||||||
if (!container) {
|
|
||||||
container = document.createElement('div');
|
|
||||||
container.id = 'dropdowns-container';
|
|
||||||
container.style.zIndex = '1000000';
|
|
||||||
container.style.position = 'relative';
|
|
||||||
document.body.appendChild(container);
|
|
||||||
}
|
|
||||||
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
// Move the element to the body so we can position absolutely
|
|
||||||
if (this._dropdownMenu) {
|
|
||||||
const el = ReactDOM.findDOMNode(this._dropdownMenu);
|
|
||||||
this._getContainer().appendChild(el);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
// Remove the element from the body
|
|
||||||
if (this._dropdownMenu) {
|
|
||||||
const el = ReactDOM.findDOMNode(this._dropdownMenu);
|
|
||||||
this._getContainer().removeChild(el);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
// Focus the dropdown button after hiding
|
// Focus the dropdown button after hiding
|
||||||
if (this._node) {
|
if (this._node) {
|
||||||
@ -373,6 +356,7 @@ class Dropdown extends PureComponent {
|
|||||||
const noResults = filter && filterItems && filterItems.length === 0;
|
const noResults = filter && filterItems && filterItems.length === 0;
|
||||||
finalChildren = [
|
finalChildren = [
|
||||||
dropdownButtons[0],
|
dropdownButtons[0],
|
||||||
|
ReactDOM.createPortal(
|
||||||
<div key="item" className={menuClasses} ref={this._addDropdownMenuRef}>
|
<div key="item" className={menuClasses} ref={this._addDropdownMenuRef}>
|
||||||
<div className="dropdown__backdrop theme--transparent-overlay" />
|
<div className="dropdown__backdrop theme--transparent-overlay" />
|
||||||
<div
|
<div
|
||||||
@ -394,7 +378,9 @@ class Dropdown extends PureComponent {
|
|||||||
{noResults && <div className="text-center pad warning">No match :(</div>}
|
{noResults && <div className="text-center pad warning">No match :(</div>}
|
||||||
<ul className={classnames({ hide: noResults })}>{dropdownItems}</ul>
|
<ul className={classnames({ hide: noResults })}>{dropdownItems}</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>,
|
||||||
|
dropdownsContainer
|
||||||
|
)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,7 +392,7 @@ class Dropdown extends PureComponent {
|
|||||||
ref={this._setRef}
|
ref={this._setRef}
|
||||||
onClick={this._handleClick}
|
onClick={this._handleClick}
|
||||||
tabIndex="-1"
|
tabIndex="-1"
|
||||||
onMouseDown={this._handleMouseDown}>
|
onMouseDown={Dropdown._handleMouseDown}>
|
||||||
{finalChildren}
|
{finalChildren}
|
||||||
</div>
|
</div>
|
||||||
</KeydownBinder>
|
</KeydownBinder>
|
||||||
|
Loading…
Reference in New Issue
Block a user