initial hacky hookup of Autocomplete menu in MessageEditor
This commit is contained in:
parent
1330b438d6
commit
317e88bef2
2 changed files with 85 additions and 7 deletions
|
@ -64,4 +64,9 @@ limitations under the License.
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_MessageEditor_AutoCompleteWrapper {
|
||||||
|
position: relative;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,9 @@ import dis from '../../../dispatcher';
|
||||||
import EditorModel from '../../../editor/model';
|
import EditorModel from '../../../editor/model';
|
||||||
import {getCaretOffset, setCaretPosition} from '../../../editor/caret';
|
import {getCaretOffset, setCaretPosition} from '../../../editor/caret';
|
||||||
import parseEvent from '../../../editor/parse-event';
|
import parseEvent from '../../../editor/parse-event';
|
||||||
|
import Autocomplete from '../rooms/Autocomplete';
|
||||||
|
// import AutocompleteModel from '../../../editor/autocomplete';
|
||||||
|
import {PartCreator} from '../../../editor/parts';
|
||||||
import {renderModel, rerenderModel} from '../../../editor/render';
|
import {renderModel, rerenderModel} from '../../../editor/render';
|
||||||
import {MatrixEvent, MatrixClient} from 'matrix-js-sdk';
|
import {MatrixEvent, MatrixClient} from 'matrix-js-sdk';
|
||||||
|
|
||||||
|
@ -28,7 +31,6 @@ export default class MessageEditor extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
// the latest event in this chain of replies
|
// the latest event in this chain of replies
|
||||||
event: PropTypes.instanceOf(MatrixEvent).isRequired,
|
event: PropTypes.instanceOf(MatrixEvent).isRequired,
|
||||||
// called when the ReplyThread contents has changed, including EventTiles thereof
|
|
||||||
// onHeightChanged: PropTypes.func.isRequired,
|
// onHeightChanged: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,9 +40,18 @@ export default class MessageEditor extends React.Component {
|
||||||
|
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.model = new EditorModel(parseEvent(this.props.event));
|
const partCreator = new PartCreator(
|
||||||
this.state = {};
|
() => this._autocompleteRef,
|
||||||
|
query => this.setState({query}),
|
||||||
|
);
|
||||||
|
this.model = new EditorModel(parseEvent(this.props.event), partCreator);
|
||||||
|
const room = this.context.matrixClient.getRoom(this.props.event.getRoomId());
|
||||||
|
this.state = {
|
||||||
|
autoComplete: null,
|
||||||
|
room,
|
||||||
|
};
|
||||||
this._editorRef = null;
|
this._editorRef = null;
|
||||||
|
this._autocompleteRef = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onInput = (event) => {
|
_onInput = (event) => {
|
||||||
|
@ -55,8 +66,33 @@ export default class MessageEditor extends React.Component {
|
||||||
}
|
}
|
||||||
setCaretPosition(this._editorRef, caret);
|
setCaretPosition(this._editorRef, caret);
|
||||||
|
|
||||||
const modelOutput = this._editorRef.parentElement.querySelector(".model");
|
this.setState({autoComplete: this.model.autoComplete});
|
||||||
modelOutput.textContent = JSON.stringify(this.model.serializeParts(), undefined, 2);
|
this._updateModelOutput();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onKeyDown = (event) => {
|
||||||
|
if (event.metaKey || event.altKey || event.shiftKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.model.autoComplete) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const autoComplete = this.model.autoComplete;
|
||||||
|
switch (event.key) {
|
||||||
|
case "Enter":
|
||||||
|
autoComplete.onEnter(event); break;
|
||||||
|
case "ArrowUp":
|
||||||
|
autoComplete.onUpArrow(event); break;
|
||||||
|
case "ArrowDown":
|
||||||
|
autoComplete.onDownArrow(event); break;
|
||||||
|
case "Tab":
|
||||||
|
autoComplete.onTab(event); break;
|
||||||
|
case "Escape":
|
||||||
|
autoComplete.onEscape(event); break;
|
||||||
|
default:
|
||||||
|
return; // don't preventDefault on anything else
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onCancelClicked = () => {
|
_onCancelClicked = () => {
|
||||||
|
@ -67,11 +103,31 @@ export default class MessageEditor extends React.Component {
|
||||||
this._editorRef = ref;
|
this._editorRef = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_collectAutocompleteRef = (ref) => {
|
||||||
|
this._autocompleteRef = ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
_onAutoCompleteConfirm = (completion) => {
|
||||||
|
this.model.autoComplete.onComponentConfirm(completion);
|
||||||
|
renderModel(this._editorRef, this.model);
|
||||||
|
this._updateModelOutput();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onAutoCompleteSelectionChange = (completion) => {
|
||||||
|
this.model.autoComplete.onComponentSelectionChange(completion);
|
||||||
|
renderModel(this._editorRef, this.model);
|
||||||
|
this._updateModelOutput();
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateModelOutput() {
|
||||||
|
const modelOutput = this._editorRef.parentElement.querySelector(".model");
|
||||||
|
modelOutput.textContent = JSON.stringify(this.model.serializeParts(), undefined, 2);
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const editor = this._editorRef;
|
const editor = this._editorRef;
|
||||||
rerenderModel(editor, this.model);
|
rerenderModel(editor, this.model);
|
||||||
const modelOutput = this._editorRef.parentElement.querySelector(".model");
|
this._updateModelOutput();
|
||||||
modelOutput.textContent = JSON.stringify(this.model.serializeParts(), undefined, 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -84,14 +140,31 @@ export default class MessageEditor extends React.Component {
|
||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
// const modelOutput = JSON.stringify(this.state.parts, undefined, 2);
|
// const modelOutput = JSON.stringify(this.state.parts, undefined, 2);
|
||||||
|
let autoComplete;
|
||||||
|
if (this.state.autoComplete) {
|
||||||
|
const query = this.state.query;
|
||||||
|
const queryLen = query.length;
|
||||||
|
autoComplete = <div className="mx_MessageEditor_AutoCompleteWrapper">
|
||||||
|
<Autocomplete
|
||||||
|
ref={this._collectAutocompleteRef}
|
||||||
|
query={query}
|
||||||
|
onConfirm={this._onAutoCompleteConfirm}
|
||||||
|
onSelectionChange={this._onAutoCompleteSelectionChange}
|
||||||
|
selection={{beginning: true, end: queryLen, start: queryLen}}
|
||||||
|
room={this.state.room}
|
||||||
|
/>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
return <div className="mx_MessageEditor">
|
return <div className="mx_MessageEditor">
|
||||||
|
{ autoComplete }
|
||||||
<div
|
<div
|
||||||
className="editor"
|
className="editor"
|
||||||
contentEditable="true"
|
contentEditable="true"
|
||||||
tabIndex="1"
|
tabIndex="1"
|
||||||
// suppressContentEditableWarning={true}
|
// suppressContentEditableWarning={true}
|
||||||
onInput={this._onInput}
|
onInput={this._onInput}
|
||||||
|
onKeyDown={this._onKeyDown}
|
||||||
ref={this._collectEditorRef}
|
ref={this._collectEditorRef}
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue