diff options
-rw-r--r-- | ui/app/components/list_editor/index.js | 24 | ||||
-rw-r--r-- | ui/app/components/login.js | 145 | ||||
-rw-r--r-- | ui/app/components/main_panel/user_password_form.js | 54 | ||||
-rw-r--r-- | ui/app/components/password_field.js | 28 | ||||
-rw-r--r-- | ui/app/components/wizard/provider_select_stage.js | 32 |
5 files changed, 189 insertions, 94 deletions
diff --git a/ui/app/components/list_editor/index.js b/ui/app/components/list_editor/index.js index 58b6ec44..4fff1cab 100644 --- a/ui/app/components/list_editor/index.js +++ b/ui/app/components/list_editor/index.js @@ -20,7 +20,8 @@ class ListEdit extends React.Component { selected: null, // string of the selected item onRemove: null, onAdd: null, - onSelect: null + onSelect: null, + onKeyDown: null }} constructor(props) { @@ -64,6 +65,18 @@ class ListEdit extends React.Component { } } + componentDidMount() { + if (this.props.onKeyDown) { + this.listEditorDiv.addEventListener("keydown", this.props.onKeyDown) + } + } + + componentWillUnmount() { + if (this.props.onKeyDown) { + this.listEditorDiv.removeEventListener("keydown", this.props.onKeyDown) + } + } + render() { let options = null if (this.props.items) { @@ -72,11 +85,16 @@ class ListEdit extends React.Component { }, this) } return( - <div className="list-editor"> + <div + ref={(div) => { this.listEditorDiv = div; }} + className="list-editor" > <FormControl value={this.row(this.props.selected)} className="list-select" - componentClass="select" size="5" onChange={this.click}> + componentClass="select" + size="5" + onChange={this.click} + > {options} </FormControl> <ButtonToolbar className="pull-right list-toolbar"> diff --git a/ui/app/components/login.js b/ui/app/components/login.js index 0a47d58c..2c96e0d5 100644 --- a/ui/app/components/login.js +++ b/ui/app/components/login.js @@ -1,5 +1,4 @@ import React from 'react' -import ReactDOM from 'react-dom' import { FormGroup, ControlLabel, FormControl, HelpBlock, Button, Checkbox, Glyphicon, Overlay, Tooltip, Alert } from 'react-bootstrap' @@ -59,6 +58,7 @@ class Login extends React.Component { this.onPassword2 = this.onPassword2.bind(this) this.onInvite = this.onInvite.bind(this) this.onSubmit = this.onSubmit.bind(this) + this.onKeyPress = this.onKeyPress.bind(this) this.onRemember = this.onRemember.bind(this) } @@ -73,6 +73,19 @@ class Login extends React.Component { } } + componentDidUpdate(prevProps, prevState) { + // If the user changes anything in the domain (which follows the @ sign) + // it gets replaced reverted to the domain name. This moves the cursor to + // before the @ sign when that happens + if (this.props.domain && this.state.username){ + let textarea = this.usernameref + let start = this.state.username.indexOf('@') + if (textarea.selectionStart > start) { + textarea.setSelectionRange(start, start) + } + } + } + render () { let rememberCheck = "" let submitButton = "" @@ -147,10 +160,11 @@ class Login extends React.Component { <FormGroup controlId="loginPassword2" validationState={this.state.password2State}> <ControlLabel>Repeat Password</ControlLabel> <FormControl - type="password" - ref="password" - value={this.state.password2 || ""} - onChange={this.onPassword2} /> + type="password" + inputRef={ref => this.password2ref = ref} + value={this.state.password2 || ""} + onChange={this.onPassword2} + /> {this.state.password2State == 'success' ? null : <FormControl.Feedback/>} {password2Help} </FormGroup> @@ -161,8 +175,10 @@ class Login extends React.Component { <FormGroup controlId="invite" validationState={this.state.inviteState}> <ControlLabel>Invite Code</ControlLabel> <FormControl - value={this.state.invite || ""} - onChange={this.onInvite} /> + value={this.state.invite || ""} + onChange={this.onInvite} + inputRef={ref => this.inviteref = ref} + /> {inviteHelp} </FormGroup> ) @@ -175,64 +191,54 @@ class Login extends React.Component { disabled: !this.maySubmit() } if (this.state.loading) { - submitButton = <Button block {...buttonProps}><Spinner /></Button> + submitButton = <Button block {...buttonProps}><Spinner /></Button> } else { - submitButton = <Button block {...buttonProps}>{buttonText}</Button> + submitButton = <Button block {...buttonProps}>{buttonText}</Button> } - - let usernameref = null let usernameDisabled = false let usernameValue = this.state.username || "" if (this.props.address) { usernameDisabled = true usernameValue = this.props.address - } else if (this.props.domain) { - usernameref = function(c) { - if (c != null) { - let textarea = ReactDOM.findDOMNode(c) - let start = textarea.value.indexOf('@') - if (textarea.selectionStart > start) { - textarea.setSelectionRange(start, start) - } - } - } } + const form = ( + <form onSubmit={this.onSubmit} onKeyPress={this.onKeyPress}> + {message} + <FormGroup style={{marginBottom: '10px' }} controlId="loginUsername" validationState={this.state.usernameState}> + <ControlLabel>Username</ControlLabel> + <FormControl + componentClass="textarea" + style={{resize: "none"}} + rows="1" + inputRef={(ref) => this.usernameref = ref} + autoFocus + value={usernameValue} + disabled={usernameDisabled} + onChange={this.onUsernameChange} + onBlur={this.onUsernameBlur} + /> + {this.state.usernameState == 'success' ? null : <FormControl.Feedback/>} + {usernameHelp} + </FormGroup> - let form = <form onSubmit={this.onSubmit}> - {message} - <FormGroup style={{marginBottom: '10px' }} controlId="loginUsername" validationState={this.state.usernameState}> - <ControlLabel>Username</ControlLabel> - <FormControl - componentClass="textarea" - style={{resize: "none"}} - rows="1" - ref={usernameref} - autoFocus - value={usernameValue} - disabled={usernameDisabled} - onChange={this.onUsernameChange} - onBlur={this.onUsernameBlur} /> - {this.state.usernameState == 'success' ? null : <FormControl.Feedback/>} - {usernameHelp} - </FormGroup> - - <FormGroup controlId="loginPassword" validationState={this.state.passwordState}> - <ControlLabel>Password</ControlLabel> - <FormControl - type="password" - ref="password" - value={this.state.password || ""} - onChange={this.onPassword} /> - {this.state.passwordState == 'success' ? null : <FormControl.Feedback/>} - {passwordHelp} - </FormGroup> - - {password2Elem} - {inviteElem} - {submitButton} - {rememberCheck} - </form> + <FormGroup controlId="loginPassword" validationState={this.state.passwordState}> + <ControlLabel>Password</ControlLabel> + <FormControl + type="password" + inputRef={ref => this.passwordref = ref} + value={this.state.password || ""} + onChange={this.onPassword} + /> + {this.state.passwordState == 'success' ? null : <FormControl.Feedback/>} + {passwordHelp} + </FormGroup> + {password2Elem} + {inviteElem} + {submitButton} + {rememberCheck} + </form> + ) return form } @@ -390,6 +396,33 @@ class Login extends React.Component { } } + /** + * Handle key presses + */ + onKeyPress(e) { + // On "Enter", move the focus to the first mounted but unfilled field, + // or submit the form if there are none. + if (e.key === 'Enter') { + // Ignore the @ and domain when checking the username + if (this.usernameref.value.split('@')[0] === ''){ + this.usernameref.focus() + return + } + + const firstUnfilledField = [ + this.passwordref, + this.password2ref, + this.inviteref, + ].find(ref => ref != null && ref.value == "") + + if (firstUnfilledField) { + firstUnfilledField.focus() + } else { + this.onSubmit(e); + } + } + } + doLogin() { let account = Account.findOrAdd(this.state.username) account.login(this.state.password, this.props.autoAllowed).then( @@ -451,4 +484,4 @@ class Login extends React.Component { } -export default Login
\ No newline at end of file +export default Login diff --git a/ui/app/components/main_panel/user_password_form.js b/ui/app/components/main_panel/user_password_form.js index 5e26b198..33640f0c 100644 --- a/ui/app/components/main_panel/user_password_form.js +++ b/ui/app/components/main_panel/user_password_form.js @@ -26,6 +26,7 @@ export default class UserPasswordForm extends React.Component { repeatPassword: null } this.submit = this.submit.bind(this) + this.onKeyPress = this.onKeyPress.bind(this) this.setNew = this.setNew.bind(this) this.setCurrent = this.setCurrent.bind(this) this.setRepeat = this.setRepeat.bind(this) @@ -79,6 +80,22 @@ export default class UserPasswordForm extends React.Component { ) } + onKeyPress(e) { + if (e.key === 'Enter') { + const firstUnfilledField = [ + this.currentPasswordRef, + this.newPasswordRef, + this.repeatPasswordRef, + ].find(ref => ref != null && ref.value == "") + + if (firstUnfilledField) { + firstUnfilledField.focus() + } else { + this.submit(e); + } + } + } + render () { let submitButton = null let message = null @@ -100,21 +117,30 @@ export default class UserPasswordForm extends React.Component { submitButton = <Button block disabled={!this.maySubmit()} onClick={this.submit}>Change</Button> } return ( - <form onSubmit={this.submit}> + <form onSubmit={this.submit} onKeyPress={this.onKeyPress}> {message} - <PasswordField id={this.props.account.id + "-current-password"} - label="Current Password" - validationMode="none" - onChange={this.setCurrent} /> - <PasswordField id={this.props.account.id + "-new-password"} - label="New Password" - validationMode="crack" - onChange={this.setNew} /> - <PasswordField id={this.props.account.id + "-repeat-password"} - label="Repeat Password" - validationMode="match" - matchText={this.state.newPassword} - onChange={this.setRepeat} /> + <PasswordField + id={this.props.account.id + "-current-password"} + inputRef={ref => this.currentPasswordRef = ref} + label="Current Password" + validationMode="none" + onChange={this.setCurrent} + /> + <PasswordField + id={this.props.account.id + "-new-password"} + inputRef={ref => this.newPasswordRef = ref} + label="New Password" + validationMode="crack" + onChange={this.setNew} + /> + <PasswordField + id={this.props.account.id + "-repeat-password"} + inputRef={ref => this.repeatPasswordRef = ref} + label="Repeat Password" + validationMode="match" + matchText={this.state.newPassword} + onChange={this.setRepeat} + /> {submitButton} </form> ) diff --git a/ui/app/components/password_field.js b/ui/app/components/password_field.js index 8397967f..654b8a57 100644 --- a/ui/app/components/password_field.js +++ b/ui/app/components/password_field.js @@ -8,13 +8,16 @@ import Validate from 'lib/validate' export default class PasswordField extends React.Component { - static get defaultProps() {return{ - id: null, // required. controlId of the element - label: "Password", - onChange: null, // callback passed current password - validationMode: "crack", // one of 'none', 'match', 'crack' - matchText: null, // used if validationMode == 'match' - }} + static get defaultProps() { + return { + id: null, // required. controlId of the element + label: "Password", + onChange: null, // callback passed current password + validationMode: "crack", // one of 'none', 'match', 'crack' + matchText: null, // used if validationMode == 'match' + inputRef: null, // a ref to the input. Used to set focus + } + } constructor(props) { super(props) @@ -43,14 +46,15 @@ export default class PasswordField extends React.Component { <FormGroup controlId={this.props.id} validationState={this.state.passwordState}> <ControlLabel>{this.props.label}</ControlLabel> <FormControl - type="password" - ref="password" - value={this.state.password || ""} - onChange={this.keypress} /> + type="password" + inputRef={this.props.inputRef} + value={this.state.password || ""} + onChange={this.keypress} + /> {this.state.passwordState == 'success' ? null : <FormControl.Feedback/>} {passwordHelp} </FormGroup> - ) + ) } keypress(e) { diff --git a/ui/app/components/wizard/provider_select_stage.js b/ui/app/components/wizard/provider_select_stage.js index 2a342d9b..b19fc8ac 100644 --- a/ui/app/components/wizard/provider_select_stage.js +++ b/ui/app/components/wizard/provider_select_stage.js @@ -33,12 +33,13 @@ export default class ProviderSelectStage extends React.Component { provider: null, // Provider object, if selected error: null // error message } - this.add = this.add.bind(this) - this.remove = this.remove.bind(this) - this.select = this.select.bind(this) - this.close = this.close.bind(this) - this.cancel = this.cancel.bind(this) - this.next = this.next.bind(this) + this.add = this.add.bind(this) + this.remove = this.remove.bind(this) + this.select = this.select.bind(this) + this.close = this.close.bind(this) + this.cancel = this.cancel.bind(this) + this.next = this.next.bind(this) + this.onKeyDown = this.onKeyDown.bind(this) } componentWillMount() { @@ -134,6 +135,13 @@ export default class ProviderSelectStage extends React.Component { }) } + onKeyDown(e) { + // Check for enter key + if (e.keyCode === 13) { + this.next(); + } + } + render() { let modal = null let info = null @@ -171,9 +179,15 @@ export default class ProviderSelectStage extends React.Component { </ButtonToolbar> </div> ) - let editlist = <ListEditor ref="list" items={this.state.domains} - selected={this.state.selected} onRemove={this.remove} onAdd={this.add} - onSelect={this.select} /> + let editlist = <ListEditor + ref="list" + items={this.state.domains} + selected={this.state.selected} + onRemove={this.remove} + onAdd={this.add} + onSelect={this.select} + onKeyDown={this.onKeyDown} + /> return( <StageLayout title={this.props.title} subtitle={this.props.subtitle} buttons={buttons}> <HorizontalLayout equalWidths={true}> |