diff --git a/native/account/log-in-panel.react.js b/native/account/log-in-panel.react.js --- a/native/account/log-in-panel.react.js +++ b/native/account/log-in-panel.react.js @@ -37,6 +37,7 @@ setNativeCredentials, } from './native-credentials'; import { PanelButton, Panel } from './panel-components.react'; +import PasswordInput from './password-input.react'; export type LogInState = { +usernameInputText: ?string, @@ -59,7 +60,7 @@ }; class LogInPanel extends React.PureComponent { usernameInput: ?TextInput; - passwordInput: ?TextInput; + passwordInput: ?PasswordInput; componentDidMount() { this.attemptToFetchCredentials(); @@ -132,14 +133,14 @@ color="#555" style={styles.icon} /> - void = passwordInput => { + passwordInputRef: (passwordInput: ?PasswordInput) => void = passwordInput => { this.passwordInput = passwordInput; }; diff --git a/native/account/password-input.react.js b/native/account/password-input.react.js new file mode 100644 --- /dev/null +++ b/native/account/password-input.react.js @@ -0,0 +1,76 @@ +// @flow + +import * as React from 'react'; +import { TextInput as BaseTextInput, View, StyleSheet } from 'react-native'; + +import Button from '../components/button.react'; +import SWMansionIcon from '../components/swmansion-icon.react'; +import { TextInput } from './modal-components.react'; + +type Props = React.ElementConfig; + +type State = { + +secureTextEntry: boolean, +}; + +class PasswordInput extends React.PureComponent { + wrappedTextInput: ?TextInput; + + constructor(props: Props) { + super(props); + this.state = { secureTextEntry: true }; + } + + onToggleShowPassword: () => void = () => { + this.setState(state => ({ + secureTextEntry: !state.secureTextEntry, + })); + }; + + render(): React.Node { + const { style, ...rest } = this.props; + return ( + + + + + ); + } + + wrappedTextInputRef: (wrappedTextInput: ?TextInput) => void = ( + wrappedTextInput: ?TextInput, + ) => { + this.wrappedTextInput = wrappedTextInput; + }; + + focus() { + this.wrappedTextInput?.focus(); + } +} + +const styles = StyleSheet.create({ + button: { + borderRadius: 21, + bottom: 0, + margin: 2, + padding: 8, + position: 'absolute', + right: -10, + }, + inputPassword: { + paddingRight: 30, + }, +}); + +export default PasswordInput; diff --git a/web/account/password-input.css b/web/account/password-input.css --- a/web/account/password-input.css +++ b/web/account/password-input.css @@ -2,10 +2,17 @@ padding-right: 50px; } -.button { +.wrapper { position: absolute; right: 0; top: 0; +} + +.wrapper :hover { + background-color: var(--show-password-bg-hover); +} + +.button { margin: 7px; padding: 6px; border: 0; @@ -13,10 +20,7 @@ color: var(--fg); } -.button:hover { - background-color: var(--show-password-bg-hover); -} - -.wrapper { +.fieldset { + all: unset; position: relative; } diff --git a/web/account/password-input.react.js b/web/account/password-input.react.js --- a/web/account/password-input.react.js +++ b/web/account/password-input.react.js @@ -10,13 +10,11 @@ type PasswordInputProps = BaseInputProps; function PasswordInput(props: PasswordInputProps, ref): React.Node { - const [htmlInputType, setHtmlInputType] = React.useState<'password' | 'text'>( - 'password', - ); + const [htmlInputType, setHtmlInputType] = React.useState('password'); const onToggleShowPassword = React.useCallback(() => { setHtmlInputType(oldType => (oldType === 'password' ? 'text' : 'password')); - }, []); + }, [setHtmlInputType]); const icon: Icon = React.useMemo( () => (htmlInputType === 'password' ? 'eye-open' : 'eye-closed'), @@ -24,7 +22,7 @@ ); return ( -
+
- -
+
+ +
+ ); } diff --git a/web/modals/input.react.js b/web/modals/input.react.js --- a/web/modals/input.react.js +++ b/web/modals/input.react.js @@ -1,6 +1,5 @@ // @flow -import classNames from 'classnames'; import * as React from 'react'; import css from './input.css'; @@ -39,13 +38,11 @@ ); } - const inputClassName = classNames(css.input, className); - return ( <> {label}