Render autocomplete suggestions in a Portal
parent
83dd1c93b7
commit
80bbb584db
|
@ -9,6 +9,7 @@ import { AUTOCOMPLETE } from '../identifiers.js';
|
||||||
import InjectChip from '../chip/Chip.js';
|
import InjectChip from '../chip/Chip.js';
|
||||||
import InjectInput from '../input/Input.js';
|
import InjectInput from '../input/Input.js';
|
||||||
import events from '../utils/events.js';
|
import events from '../utils/events.js';
|
||||||
|
import Portal from '../hoc/Portal';
|
||||||
|
|
||||||
const POSITION = {
|
const POSITION = {
|
||||||
AUTO: 'auto',
|
AUTO: 'auto',
|
||||||
|
@ -77,7 +78,6 @@ const factory = (Chip, Input) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
direction: this.props.direction,
|
|
||||||
focus: false,
|
focus: false,
|
||||||
showAllSuggestions: this.props.showSuggestionsWhenValueIsSet,
|
showAllSuggestions: this.props.showSuggestionsWhenValueIsSet,
|
||||||
query: this.props.query ? this.props.query : this.query(this.props.value),
|
query: this.props.query ? this.props.query : this.query(this.props.value),
|
||||||
|
@ -92,11 +92,8 @@ const factory = (Chip, Input) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
if (!this.state.focus && nextState.focus && this.props.direction === POSITION.AUTO) {
|
if (!this.state.focus && nextState.focus) {
|
||||||
const direction = this.calculateDirection();
|
this.calculateDirection();
|
||||||
if (this.state.direction !== direction) {
|
|
||||||
this.setState({ direction });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -178,13 +175,26 @@ const factory = (Chip, Input) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
calculateDirection() {
|
calculateDirection() {
|
||||||
|
const client = ReactDOM.findDOMNode(this.inputNode).getBoundingClientRect();
|
||||||
|
const screen_height = window.innerHeight || document.documentElement.offsetHeight;
|
||||||
|
let direction = this.props.direction;
|
||||||
if (this.props.direction === 'auto') {
|
if (this.props.direction === 'auto') {
|
||||||
const client = ReactDOM.findDOMNode(this.inputNode).getBoundingClientRect();
|
|
||||||
const screen_height = window.innerHeight || document.documentElement.offsetHeight;
|
|
||||||
const up = client.top > ((screen_height / 2) + client.height);
|
const up = client.top > ((screen_height / 2) + client.height);
|
||||||
return up ? 'up' : 'down';
|
direction = up ? 'up' : 'down';
|
||||||
|
}
|
||||||
|
const top = direction == 'down' ? (client.top+client.height)+'px' : client.top+'px';
|
||||||
|
const bottom = direction == 'up' ? '0px' : undefined;
|
||||||
|
const left = client.left+'px';
|
||||||
|
const width = client.width+'px';
|
||||||
|
let maxHeight = direction == 'down' ? (screen_height-client.top-client.height) : client.top;
|
||||||
|
if (maxHeight > screen_height*0.45) {
|
||||||
|
maxHeight = Math.floor(screen_height*0.45);
|
||||||
|
}
|
||||||
|
if (this.state.top !== top || this.state.left !== left ||
|
||||||
|
this.state.width !== width || this.state.bottom !== bottom ||
|
||||||
|
this.state.maxHeight !== maxHeight) {
|
||||||
|
this.setState({ top, bottom, left, width, maxHeight });
|
||||||
}
|
}
|
||||||
return this.props.direction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
query(key) {
|
query(key) {
|
||||||
|
@ -383,13 +393,13 @@ const factory = (Chip, Input) => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
const { top, bottom, maxHeight, left, width } = this.state;
|
||||||
<ul
|
return (<div style={{position: 'absolute', top, left, width}}>
|
||||||
className={classnames(theme.suggestions, { [theme.up]: this.state.direction === 'up' })}
|
<ul style={{bottom, maxHeight}}
|
||||||
>
|
className={theme.suggestions}>
|
||||||
{suggestions}
|
{suggestions}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
</div>);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -425,7 +435,9 @@ const factory = (Chip, Input) => {
|
||||||
themeNamespace="input"
|
themeNamespace="input"
|
||||||
value={this.state.query}
|
value={this.state.query}
|
||||||
/>
|
/>
|
||||||
{this.renderSuggestions()}
|
<Portal>
|
||||||
|
{this.state.focus ? this.renderSuggestions() : null}
|
||||||
|
</Portal>
|
||||||
{this.props.selectedPosition === 'below' ? this.renderSelected() : null}
|
{this.props.selectedPosition === 'below' ? this.renderSelected() : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,15 +8,6 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
@apply --reset;
|
@apply --reset;
|
||||||
|
|
||||||
&.focus {
|
|
||||||
& .suggestions {
|
|
||||||
box-shadow: var(--zdepth-shadow-1);
|
|
||||||
max-height: var(--autocomplete-overflow-max-height);
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.clear {
|
.clear {
|
||||||
|
@ -46,27 +37,21 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.suggestions {
|
.suggestions {
|
||||||
|
position: absolute;
|
||||||
background-color: var(--autocomplete-suggestions-background);
|
background-color: var(--autocomplete-suggestions-background);
|
||||||
list-style: none;
|
list-style: none;
|
||||||
max-height: 0;
|
max-height: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
position: absolute;
|
|
||||||
transition-duration: var(--animation-duration);
|
transition-duration: var(--animation-duration);
|
||||||
transition-property: max-height, box-shadow;
|
transition-property: max-height, box-shadow;
|
||||||
transition-timing-function: var(--animation-curve-default);
|
transition-timing-function: var(--animation-curve-default);
|
||||||
visibility: hidden;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: var(--z-index-high);
|
z-index: var(--z-index-highest);
|
||||||
|
box-shadow: var(--zdepth-shadow-1);
|
||||||
&:not(.up) {
|
max-height: var(--autocomplete-overflow-max-height);
|
||||||
margin-top: calc(-1 * var(--input-padding));
|
-ms-overflow-style: none;
|
||||||
}
|
|
||||||
|
|
||||||
&.up {
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|
Loading…
Reference in New Issue