Invite UX tweaks:

+ re-focus input field after all interactions
+ change textarea to text input
+ fix margins between things
+ improve keyboard usage with enter/space autofilling
pull/21833/head
Michael Telatynski 2020-11-03 13:14:35 +00:00
parent db8cd68d8b
commit 66377eb731
2 changed files with 37 additions and 27 deletions

View File

@ -27,37 +27,29 @@ limitations under the License.
padding-left: 8px; padding-left: 8px;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
display: flex;
flex-wrap: wrap;
.mx_InviteDialog_userTile { .mx_InviteDialog_userTile {
margin: 6px 6px 0 0;
display: inline-block; display: inline-block;
float: left; min-width: max-content; // prevent manipulation by flexbox
position: relative;
top: 7px;
} }
// Using a textarea for this element, to circumvent autofill // Mostly copied from AddressPickerDialog; overrides bunch of our default text input styles
// Mostly copied from AddressPickerDialog input, input:focus {
textarea, margin: 6px 0 !important;
textarea:focus { height: 24px;
height: 34px; line-height: $font-24px;
line-height: $font-34px;
font-size: $font-14px; font-size: $font-14px;
padding-left: 12px; padding-left: 12px;
margin: 0 !important;
border: 0 !important; border: 0 !important;
outline: 0 !important; outline: 0 !important;
resize: none; resize: none;
overflow: hidden;
box-sizing: border-box; box-sizing: border-box;
word-wrap: nowrap; min-width: 40%;
flex: 1 !important;
// Roughly fill about 2/5ths of the available space. This is to try and 'fill' the color: $primary-fg-color !important;
// remaining space after a bunch of pills, but is a bit hacky. Ideally we'd have
// support for "fill remaining width", but traditional tricks don't work with what
// we're pushing into this "field". Flexbox just makes things worse. The theory is
// that users won't need more than about 2/5ths of the input to find the person
// they're looking for.
width: 40%;
} }
} }

View File

@ -663,12 +663,21 @@ export default class InviteDialog extends React.PureComponent {
}; };
_onKeyDown = (e) => { _onKeyDown = (e) => {
if (this.state.busy) return;
const value = e.target.value.trim();
const hasModifiers = e.ctrlKey || e.shiftKey || e.metaKey;
if (!value && this.state.targets.length > 0 && e.key === Key.BACKSPACE && !hasModifiers) {
// when the field is empty and the user hits backspace remove the right-most target // when the field is empty and the user hits backspace remove the right-most target
if (!e.target.value && !this.state.busy && this.state.targets.length > 0 && e.key === Key.BACKSPACE &&
!e.ctrlKey && !e.shiftKey && !e.metaKey
) {
e.preventDefault(); e.preventDefault();
this._removeMember(this.state.targets[this.state.targets.length - 1]); this._removeMember(this.state.targets[this.state.targets.length - 1]);
} else if (value && e.key === Key.ENTER && !hasModifiers) {
// when the user hits enter with something in their field try to convert it
e.preventDefault();
this._convertFilter();
} else if (value && e.key === Key.SPACE && !hasModifiers && value.includes("@") && !value.includes(" ")) {
// when the user hits space and their input looks like an e-mail/MXID then try to convert it
e.preventDefault();
this._convertFilter();
} }
}; };
@ -811,6 +820,10 @@ export default class InviteDialog extends React.PureComponent {
filterText = ""; // clear the filter when the user accepts a suggestion filterText = ""; // clear the filter when the user accepts a suggestion
} }
this.setState({targets, filterText}); this.setState({targets, filterText});
if (this._editorRef && this._editorRef.current) {
this._editorRef.current.focus();
}
}; };
_removeMember = (member: Member) => { _removeMember = (member: Member) => {
@ -820,6 +833,10 @@ export default class InviteDialog extends React.PureComponent {
targets.splice(idx, 1); targets.splice(idx, 1);
this.setState({targets}); this.setState({targets});
} }
if (this._editorRef && this._editorRef.current) {
this._editorRef.current.focus();
}
}; };
_onPaste = async (e) => { _onPaste = async (e) => {
@ -829,7 +846,7 @@ export default class InviteDialog extends React.PureComponent {
return; return;
} }
// Prevent the text being pasted into the textarea // Prevent the text being pasted into the input
e.preventDefault(); e.preventDefault();
// Process it as a list of addresses to add instead // Process it as a list of addresses to add instead
@ -1024,8 +1041,8 @@ export default class InviteDialog extends React.PureComponent {
<DMUserTile member={t} onRemove={!this.state.busy && this._removeMember} key={t.userId} /> <DMUserTile member={t} onRemove={!this.state.busy && this._removeMember} key={t.userId} />
)); ));
const input = ( const input = (
<textarea <input
rows={1} type="text"
onKeyDown={this._onKeyDown} onKeyDown={this._onKeyDown}
onChange={this._updateFilter} onChange={this._updateFilter}
value={this.state.filterText} value={this.state.filterText}
@ -1033,6 +1050,7 @@ export default class InviteDialog extends React.PureComponent {
onPaste={this._onPaste} onPaste={this._onPaste}
autoFocus={true} autoFocus={true}
disabled={this.state.busy} disabled={this.state.busy}
autoComplete="off"
/> />
); );
return ( return (