Convert autocomplete stuff to TypeScript

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2020-04-20 19:00:54 +01:00
parent 37bd0f3508
commit fced4ea51e
14 changed files with 251 additions and 142 deletions

View file

@ -15,30 +15,62 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import flatMap from 'lodash/flatMap';
import type {Completion} from '../../../autocomplete/Autocompleter';
import { Room } from 'matrix-js-sdk';
import {ICompletion, ISelectionRange, IProviderCompletions} from '../../../autocomplete/Autocompleter';
import {Room} from 'matrix-js-sdk/src/models/room';
import SettingsStore from "../../../settings/SettingsStore";
import Autocompleter from '../../../autocomplete/Autocompleter';
import {sleep} from "../../../utils/promise";
const COMPOSER_SELECTED = 0;
export const generateCompletionDomId = (number) => `mx_Autocomplete_Completion_${number}`;
export default class Autocomplete extends React.Component {
interface IProps {
query: string;
onConfirm: (ICompletion) => void;
onSelectionChange?: (ICompletion, number) => void;
selection: ISelectionRange;
room: Room;
}
interface IState {
completions: IProviderCompletions[];
completionList: ICompletion[];
selectionOffset: number;
shouldShowCompletions: boolean;
hide: boolean;
forceComplete: boolean;
}
export default class Autocomplete extends React.PureComponent<IProps, IState> {
static propTypes = {
// the query string for which to show autocomplete suggestions
query: PropTypes.string.isRequired,
// method invoked with range and text content when completion is confirmed
onConfirm: PropTypes.func.isRequired,
// method invoked when selected (if any) completion changes
onSelectionChange: PropTypes.func,
// The room in which we're autocompleting
room: PropTypes.instanceOf(Room),
};
autocompleter: Autocompleter;
queryRequested: string;
debounceCompletionsRequest: NodeJS.Timeout;
container: React.RefObject<HTMLDivElement>;
constructor(props) {
super(props);
this.autocompleter = new Autocompleter(props.room);
this.completionPromise = null;
this.hide = this.hide.bind(this);
this.onCompletionClicked = this.onCompletionClicked.bind(this);
this.state = {
// list of completionResults, each containing completions
@ -57,13 +89,15 @@ export default class Autocomplete extends React.Component {
forceComplete: false,
};
this.container = React.createRef();
}
componentDidMount() {
this._applyNewProps();
}
_applyNewProps(oldQuery, oldRoom) {
_applyNewProps(oldQuery?: string, oldRoom?: Room) {
if (oldRoom && this.props.room.roomId !== oldRoom.roomId) {
this.autocompleter.destroy();
this.autocompleter = new Autocompleter(this.props.room);
@ -159,7 +193,7 @@ export default class Autocomplete extends React.Component {
});
}
hasSelection(): bool {
hasSelection(): boolean {
return this.countCompletions() > 0 && this.state.selectionOffset !== 0;
}
@ -168,7 +202,7 @@ export default class Autocomplete extends React.Component {
}
// called from MessageComposerInput
moveSelection(delta): ?Completion {
moveSelection(delta): ICompletion | undefined {
const completionCount = this.countCompletions();
if (completionCount === 0) return; // there are no items to move the selection through
@ -190,9 +224,14 @@ export default class Autocomplete extends React.Component {
this.hide();
}
hide() {
this.setState({hide: true, selectionOffset: 0, completions: [], completionList: []});
}
hide = () => {
this.setState({
hide: true,
selectionOffset: 0,
completions: [],
completionList: [],
});
};
forceComplete() {
return new Promise((resolve) => {
@ -207,7 +246,7 @@ export default class Autocomplete extends React.Component {
});
}
onCompletionClicked(selectionOffset: number): boolean {
onCompletionClicked = (selectionOffset: number): boolean => {
if (this.countCompletions() === 0 || selectionOffset === COMPOSER_SELECTED) {
return false;
}
@ -216,7 +255,7 @@ export default class Autocomplete extends React.Component {
this.hide();
return true;
}
};
setSelection(selectionOffset: number) {
this.setState({selectionOffset, hide: false});
@ -229,20 +268,16 @@ export default class Autocomplete extends React.Component {
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.container) {
if (selectedCompletion && this.container.current) {
const domNode = ReactDOM.findDOMNode(selectedCompletion);
const offsetTop = domNode && domNode.offsetTop;
if (offsetTop > this.container.scrollTop + this.container.offsetHeight ||
offsetTop < this.container.scrollTop) {
this.container.scrollTop = offsetTop - this.container.offsetTop;
if (offsetTop > this.container.current.scrollTop + this.container.current.offsetHeight ||
offsetTop < this.container.current.scrollTop) {
this.container.current.scrollTop = offsetTop - this.container.current.offsetTop;
}
}
}
setState(state, func) {
super.setState(state, func);
}
render() {
let position = 1;
const renderedCompletions = this.state.completions.map((completionResult, i) => {
@ -276,23 +311,9 @@ export default class Autocomplete extends React.Component {
}).filter((completion) => !!completion);
return !this.state.hide && renderedCompletions.length > 0 ? (
<div className="mx_Autocomplete" ref={(e) => this.container = e}>
<div className="mx_Autocomplete" ref={this.container}>
{ renderedCompletions }
</div>
) : null;
}
}
Autocomplete.propTypes = {
// the query string for which to show autocomplete suggestions
query: PropTypes.string.isRequired,
// method invoked with range and text content when completion is confirmed
onConfirm: PropTypes.func.isRequired,
// method invoked when selected (if any) completion changes
onSelectionChange: PropTypes.func,
// The room in which we're autocompleting
room: PropTypes.instanceOf(Room),
};