add visual bell when no replacements are available
also add try/catch in _tabCompleteName so errors don't get swallowed
This commit is contained in:
parent
c44fbb73d0
commit
85efb71a23
3 changed files with 53 additions and 18 deletions
|
@ -27,6 +27,15 @@ limitations under the License.
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes visualbell {
|
||||||
|
from { background-color: #faa; }
|
||||||
|
to { background-color: $primary-bg-color; }
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mx_BasicMessageComposer_input_error {
|
||||||
|
animation: 0.2s visualbell;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_BasicMessageComposer_input {
|
.mx_BasicMessageComposer_input {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
|
|
@ -14,6 +14,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import EditorModel from '../../../editor/model';
|
import EditorModel from '../../../editor/model';
|
||||||
|
@ -271,7 +273,7 @@ export default class BasicMessageEditor extends React.Component {
|
||||||
return; // don't preventDefault on anything else
|
return; // don't preventDefault on anything else
|
||||||
}
|
}
|
||||||
} else if (event.key === "Tab") {
|
} else if (event.key === "Tab") {
|
||||||
this._tabCompleteName(event);
|
this._tabCompleteName();
|
||||||
handled = true;
|
handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -281,7 +283,9 @@ export default class BasicMessageEditor extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _tabCompleteName(event) {
|
async _tabCompleteName() {
|
||||||
|
try {
|
||||||
|
await new Promise(resolve => this.setState({showVisualBell: false}, resolve));
|
||||||
const {model} = this.props;
|
const {model} = this.props;
|
||||||
const caret = this.getCaret();
|
const caret = this.getCaret();
|
||||||
const position = model.positionForOffset(caret.offset, caret.atNodeEnd);
|
const position = model.positionForOffset(caret.offset, caret.atNodeEnd);
|
||||||
|
@ -290,11 +294,19 @@ export default class BasicMessageEditor extends React.Component {
|
||||||
return part.text[offset] !== " " && (part.type === "plain" || part.type === "pill-candidate");
|
return part.text[offset] !== " " && (part.type === "plain" || part.type === "pill-candidate");
|
||||||
});
|
});
|
||||||
const {partCreator} = model;
|
const {partCreator} = model;
|
||||||
|
// await for auto-complete to be open
|
||||||
await model.transform(() => {
|
await model.transform(() => {
|
||||||
const addedLen = range.replace([partCreator.pillCandidate(range.text)]);
|
const addedLen = range.replace([partCreator.pillCandidate(range.text)]);
|
||||||
return model.positionForOffset(caret.offset + addedLen, true);
|
return model.positionForOffset(caret.offset + addedLen, true);
|
||||||
});
|
});
|
||||||
await model.autoComplete.onTab();
|
await model.autoComplete.onTab();
|
||||||
|
if (!model.autoComplete.hasSelection()) {
|
||||||
|
this.setState({showVisualBell: true});
|
||||||
|
model.autoComplete.close();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isModified() {
|
isModified() {
|
||||||
|
@ -324,7 +336,14 @@ export default class BasicMessageEditor extends React.Component {
|
||||||
// not really, but we could not serialize the parts, and just change the autoCompleter
|
// not really, but we could not serialize the parts, and just change the autoCompleter
|
||||||
partCreator.setAutoCompleteCreator(autoCompleteCreator(
|
partCreator.setAutoCompleteCreator(autoCompleteCreator(
|
||||||
() => this._autocompleteRef,
|
() => this._autocompleteRef,
|
||||||
query => new Promise(resolve => this.setState({query}, resolve)),
|
query => {
|
||||||
|
return new Promise(resolve => this.setState({query}, resolve));
|
||||||
|
// if setState
|
||||||
|
// if (this.state.query === query) {
|
||||||
|
// return Promise.resolve();
|
||||||
|
// } else {
|
||||||
|
// }
|
||||||
|
},
|
||||||
));
|
));
|
||||||
this.historyManager = new HistoryManager(partCreator);
|
this.historyManager = new HistoryManager(partCreator);
|
||||||
// initial render of model
|
// initial render of model
|
||||||
|
@ -365,7 +384,10 @@ export default class BasicMessageEditor extends React.Component {
|
||||||
/>
|
/>
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
return (<div className="mx_BasicMessageComposer">
|
const classes = classNames("mx_BasicMessageComposer", {
|
||||||
|
"mx_BasicMessageComposer_input_error": this.state.showVisualBell,
|
||||||
|
});
|
||||||
|
return (<div className={classes}>
|
||||||
{ autoComplete }
|
{ autoComplete }
|
||||||
<div
|
<div
|
||||||
className="mx_BasicMessageComposer_input"
|
className="mx_BasicMessageComposer_input"
|
||||||
|
|
|
@ -33,6 +33,10 @@ export default class AutocompleteWrapperModel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this._updateCallback({close: true});
|
||||||
|
}
|
||||||
|
|
||||||
hasSelection() {
|
hasSelection() {
|
||||||
return this._getAutocompleterComponent().hasSelection();
|
return this._getAutocompleterComponent().hasSelection();
|
||||||
}
|
}
|
||||||
|
@ -67,7 +71,7 @@ export default class AutocompleteWrapperModel {
|
||||||
// so we can restore it in onComponentSelectionChange when the value is undefined (meaning it should be the typed text)
|
// so we can restore it in onComponentSelectionChange when the value is undefined (meaning it should be the typed text)
|
||||||
this._queryPart = part;
|
this._queryPart = part;
|
||||||
this._queryOffset = offset;
|
this._queryOffset = offset;
|
||||||
this._updateQuery(part.text);
|
return this._updateQuery(part.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
onComponentSelectionChange(completion) {
|
onComponentSelectionChange(completion) {
|
||||||
|
|
Loading…
Reference in a new issue