Autocomplete: use scrollIntoView for auto-scroll instead of broken manual scrollTop calculation

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
pull/21833/head
Michael Telatynski 2020-05-29 21:42:33 +01:00
parent 3f76b73b50
commit 8087b521e6
2 changed files with 29 additions and 18 deletions

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React, {createRef} from 'react';
import classNames from 'classnames';
/* These were earlier stateless functional components but had to be converted
@ -30,7 +30,11 @@ interface ITextualCompletionProps {
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() {
const {
title,
@ -40,7 +44,11 @@ export class TextualCompletion extends React.PureComponent<ITextualCompletionPro
...restProps
} = this.props;
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_subtitle">{ subtitle }</span>
<span className="mx_Autocomplete_Completion_description">{ description }</span>
@ -57,7 +65,7 @@ interface IPillCompletionProps {
className?: string;
}
export class PillCompletion extends React.PureComponent<IPillCompletionProps> {
export class PillCompletion extends Completion<IPillCompletionProps> {
render() {
const {
title,
@ -68,7 +76,11 @@ export class PillCompletion extends React.PureComponent<IPillCompletionProps> {
...restProps
} = this.props;
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 }
<span className="mx_Autocomplete_Completion_title">{ title }</span>
<span className="mx_Autocomplete_Completion_subtitle">{ subtitle }</span>

View File

@ -15,8 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import React, {createRef} from 'react';
import classNames from 'classnames';
import flatMap from 'lodash/flatMap';
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 Autocompleter from '../../../autocomplete/Autocompleter';
import { Completion } from '../../../autocomplete/Components';
const COMPOSER_SELECTED = 0;
@ -54,7 +54,7 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
autocompleter: Autocompleter;
queryRequested: string;
debounceCompletionsRequest: NodeJS.Timeout;
containerRef: React.RefObject<HTMLDivElement>;
private containerRef = createRef<HTMLDivElement>();
constructor(props) {
super(props);
@ -78,8 +78,6 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
forceComplete: false,
};
this.containerRef = React.createRef();
}
componentDidMount() {
@ -256,14 +254,15 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
componentDidUpdate(prevProps: IProps) {
this.applyNewProps(prevProps.query, prevProps.room);
// this is the selected completion, so scroll it into view if needed
const selectedCompletion = this.refs[`completion${this.state.selectionOffset}`];
if (selectedCompletion && this.containerRef.current) {
const domNode = ReactDOM.findDOMNode(selectedCompletion);
const offsetTop = domNode && (domNode as HTMLElement).offsetTop;
if (offsetTop > this.containerRef.current.scrollTop + this.containerRef.current.offsetHeight ||
offsetTop < this.containerRef.current.scrollTop) {
this.containerRef.current.scrollTop = offsetTop - this.containerRef.current.offsetTop;
}
const selectedCompletion = this.refs[`completion${this.state.selectionOffset}`] as Completion<any>;
if (selectedCompletion && selectedCompletion.nodeRef.current) {
selectedCompletion.nodeRef.current.scrollIntoView({
behavior: "auto",
block: "nearest",
});
} else {
this.containerRef.current.scrollTo({ top: 0 });
}
}