Autocomplete: use scrollIntoView for auto-scroll instead of broken manual scrollTop calculation
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
3f76b73b50
commit
8087b521e6
2 changed files with 29 additions and 18 deletions
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, {createRef} from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
/* These were earlier stateless functional components but had to be converted
|
/* These were earlier stateless functional components but had to be converted
|
||||||
|
@ -30,7 +30,11 @@ interface ITextualCompletionProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TextualCompletion extends React.PureComponent<ITextualCompletionProps> {
|
export abstract class Completion<T> extends React.PureComponent<T> {
|
||||||
|
nodeRef = createRef<HTMLDivElement>();
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TextualCompletion extends Completion<ITextualCompletionProps> {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
|
@ -40,7 +44,11 @@ export class TextualCompletion extends React.PureComponent<ITextualCompletionPro
|
||||||
...restProps
|
...restProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<div className={classNames('mx_Autocomplete_Completion_block', className)} role="option" {...restProps}>
|
<div {...restProps}
|
||||||
|
className={classNames('mx_Autocomplete_Completion_block', className)}
|
||||||
|
role="option"
|
||||||
|
ref={this.nodeRef}
|
||||||
|
>
|
||||||
<span className="mx_Autocomplete_Completion_title">{ title }</span>
|
<span className="mx_Autocomplete_Completion_title">{ title }</span>
|
||||||
<span className="mx_Autocomplete_Completion_subtitle">{ subtitle }</span>
|
<span className="mx_Autocomplete_Completion_subtitle">{ subtitle }</span>
|
||||||
<span className="mx_Autocomplete_Completion_description">{ description }</span>
|
<span className="mx_Autocomplete_Completion_description">{ description }</span>
|
||||||
|
@ -57,7 +65,7 @@ interface IPillCompletionProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PillCompletion extends React.PureComponent<IPillCompletionProps> {
|
export class PillCompletion extends Completion<IPillCompletionProps> {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
|
@ -68,7 +76,11 @@ export class PillCompletion extends React.PureComponent<IPillCompletionProps> {
|
||||||
...restProps
|
...restProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<div className={classNames('mx_Autocomplete_Completion_pill', className)} role="option" {...restProps}>
|
<div {...restProps}
|
||||||
|
className={classNames('mx_Autocomplete_Completion_pill', className)}
|
||||||
|
role="option"
|
||||||
|
ref={this.nodeRef}
|
||||||
|
>
|
||||||
{ initialComponent }
|
{ initialComponent }
|
||||||
<span className="mx_Autocomplete_Completion_title">{ title }</span>
|
<span className="mx_Autocomplete_Completion_title">{ title }</span>
|
||||||
<span className="mx_Autocomplete_Completion_subtitle">{ subtitle }</span>
|
<span className="mx_Autocomplete_Completion_subtitle">{ subtitle }</span>
|
||||||
|
|
|
@ -15,8 +15,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, {createRef} from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import flatMap from 'lodash/flatMap';
|
import flatMap from 'lodash/flatMap';
|
||||||
import {ICompletion, ISelectionRange, IProviderCompletions} from '../../../autocomplete/Autocompleter';
|
import {ICompletion, ISelectionRange, IProviderCompletions} from '../../../autocomplete/Autocompleter';
|
||||||
|
@ -24,6 +23,7 @@ import {Room} from 'matrix-js-sdk/src/models/room';
|
||||||
|
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import Autocompleter from '../../../autocomplete/Autocompleter';
|
import Autocompleter from '../../../autocomplete/Autocompleter';
|
||||||
|
import { Completion } from '../../../autocomplete/Components';
|
||||||
|
|
||||||
const COMPOSER_SELECTED = 0;
|
const COMPOSER_SELECTED = 0;
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
|
||||||
autocompleter: Autocompleter;
|
autocompleter: Autocompleter;
|
||||||
queryRequested: string;
|
queryRequested: string;
|
||||||
debounceCompletionsRequest: NodeJS.Timeout;
|
debounceCompletionsRequest: NodeJS.Timeout;
|
||||||
containerRef: React.RefObject<HTMLDivElement>;
|
private containerRef = createRef<HTMLDivElement>();
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -78,8 +78,6 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
|
||||||
|
|
||||||
forceComplete: false,
|
forceComplete: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.containerRef = React.createRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -256,14 +254,15 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
|
||||||
componentDidUpdate(prevProps: IProps) {
|
componentDidUpdate(prevProps: IProps) {
|
||||||
this.applyNewProps(prevProps.query, prevProps.room);
|
this.applyNewProps(prevProps.query, prevProps.room);
|
||||||
// this is the selected completion, so scroll it into view if needed
|
// this is the selected completion, so scroll it into view if needed
|
||||||
const selectedCompletion = this.refs[`completion${this.state.selectionOffset}`];
|
const selectedCompletion = this.refs[`completion${this.state.selectionOffset}`] as Completion<any>;
|
||||||
if (selectedCompletion && this.containerRef.current) {
|
|
||||||
const domNode = ReactDOM.findDOMNode(selectedCompletion);
|
if (selectedCompletion && selectedCompletion.nodeRef.current) {
|
||||||
const offsetTop = domNode && (domNode as HTMLElement).offsetTop;
|
selectedCompletion.nodeRef.current.scrollIntoView({
|
||||||
if (offsetTop > this.containerRef.current.scrollTop + this.containerRef.current.offsetHeight ||
|
behavior: "auto",
|
||||||
offsetTop < this.containerRef.current.scrollTop) {
|
block: "nearest",
|
||||||
this.containerRef.current.scrollTop = offsetTop - this.containerRef.current.offsetTop;
|
});
|
||||||
}
|
} else {
|
||||||
|
this.containerRef.current.scrollTo({ top: 0 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue