diff --git a/res/css/_common.scss b/res/css/_common.scss index 666129af34..0317e89d20 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -17,6 +17,7 @@ limitations under the License. */ @import "./_font-sizes.scss"; +@import "./_font-weights.scss"; $hover-transition: 0.08s cubic-bezier(.46, .03, .52, .96); // quadratic @@ -323,6 +324,7 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { .mx_Dialog_title { font-size: $font-22px; + font-weight: $font-semi-bold; line-height: $font-36px; color: $dialog-title-fg-color; } @@ -348,8 +350,8 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { background-color: $dialog-close-fg-color; cursor: pointer; position: absolute; - top: 4px; - right: 0px; + top: 10px; + right: 0; } .mx_Dialog_content { diff --git a/res/css/views/dialogs/_InviteDialog.scss b/res/css/views/dialogs/_InviteDialog.scss index b9063f46b9..d8ff56663a 100644 --- a/res/css/views/dialogs/_InviteDialog.scss +++ b/res/css/views/dialogs/_InviteDialog.scss @@ -27,37 +27,29 @@ limitations under the License. padding-left: 8px; overflow-x: hidden; overflow-y: auto; + display: flex; + flex-wrap: wrap; .mx_InviteDialog_userTile { + margin: 6px 6px 0 0; display: inline-block; - float: left; - position: relative; - top: 7px; + min-width: max-content; // prevent manipulation by flexbox } - // Using a textarea for this element, to circumvent autofill - // Mostly copied from AddressPickerDialog - textarea, - textarea:focus { - height: 34px; - line-height: $font-34px; + // Mostly copied from AddressPickerDialog; overrides bunch of our default text input styles + > input[type="text"] { + margin: 6px 0 !important; + height: 24px; + line-height: $font-24px; font-size: $font-14px; padding-left: 12px; - margin: 0 !important; border: 0 !important; outline: 0 !important; resize: none; - overflow: hidden; box-sizing: border-box; - word-wrap: nowrap; - - // Roughly fill about 2/5ths of the available space. This is to try and 'fill' the - // 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%; + min-width: 40%; + flex: 1 !important; + color: $primary-fg-color !important; } } @@ -148,6 +140,10 @@ limitations under the License. } } + .mx_InviteDialog_roomTile_nameStack { + display: inline-block; + } + .mx_InviteDialog_roomTile_name { font-weight: 600; font-size: $font-14px; diff --git a/src/components/views/dialogs/InviteDialog.js b/src/components/views/dialogs/InviteDialog.js index fc3245aa18..99878569d3 100644 --- a/src/components/views/dialogs/InviteDialog.js +++ b/src/components/views/dialogs/InviteDialog.js @@ -280,11 +280,17 @@ class DMRoomTile extends React.PureComponent { ); + const caption = this.props.member.isEmail + ? _t("Invite by email") + : this._highlightName(this.props.member.userId); + return (
{stackedAvatar} - {this._highlightName(this.props.member.name)} - {this._highlightName(this.props.member.userId)} + +
{this._highlightName(this.props.member.name)}
+
{caption}
+
{timestamp}
); @@ -663,12 +669,21 @@ export default class InviteDialog extends React.PureComponent { }; _onKeyDown = (e) => { - // 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 - ) { + 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 e.preventDefault(); 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 +826,10 @@ export default class InviteDialog extends React.PureComponent { filterText = ""; // clear the filter when the user accepts a suggestion } this.setState({targets, filterText}); + + if (this._editorRef && this._editorRef.current) { + this._editorRef.current.focus(); + } }; _removeMember = (member: Member) => { @@ -820,6 +839,10 @@ export default class InviteDialog extends React.PureComponent { targets.splice(idx, 1); this.setState({targets}); } + + if (this._editorRef && this._editorRef.current) { + this._editorRef.current.focus(); + } }; _onPaste = async (e) => { @@ -829,7 +852,7 @@ export default class InviteDialog extends React.PureComponent { return; } - // Prevent the text being pasted into the textarea + // Prevent the text being pasted into the input e.preventDefault(); // Process it as a list of addresses to add instead @@ -1024,8 +1047,8 @@ export default class InviteDialog extends React.PureComponent { )); const input = ( -