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; }; @@ -345,6 +346,9 @@ input: { paddingLeft: 35, }, + inputPassword: { + paddingRight: 40, + }, row: { marginHorizontal: 24, }, 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,77 @@ +// @flow + +import invariant from 'invariant'; +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 }; + } + + render(): React.Node { + return ( + + + + + ); + } + + wrappedTextInputRef: ( + wrappedTextInput: ?TextInput, + ) => void = wrappedTextInput => { + this.wrappedTextInput = wrappedTextInput; + }; + + focus() { + invariant(this.wrappedTextInput, 'ref should exist'); + this.wrappedTextInput.focus(); + } +} + +const styles = StyleSheet.create({ + button: { + borderRadius: 21, + bottom: 0, + margin: 2, + padding: 8, + position: 'absolute', + right: 10, + }, +}); + +export { PasswordInput }; diff --git a/web/account/log-in-form.react.js b/web/account/log-in-form.react.js --- a/web/account/log-in-form.react.js +++ b/web/account/log-in-form.react.js @@ -26,6 +26,7 @@ import { useSelector } from '../redux/redux-utils'; import { webLogInExtraInfoSelector } from '../selectors/account-selectors'; import css from './log-in-form.css'; +import PasswordInput from './password-input.react'; const loadingStatusSelector = createLoadingStatusSelector(logInActionTypes); function LoginForm(): React.Node { @@ -139,9 +140,7 @@
Password
- ) => mixed, + +onBlur?: (value: SyntheticEvent) => mixed, + +disabled?: boolean, + +label?: string, + +id?: string, +}; + +function PasswordInput(props: Props, ref): React.Node { + const [type, setType] = React.useState('password'); + const onToggleShowPassword: () => void = () => { + setType(oldType => (oldType === 'password' ? 'text' : 'password')); + }; + + const getIcon: () => Icon = () => { + return type === 'password' ? 'eye-open' : 'eye-closed'; + }; + + return ( +
+ +
+
{ + onToggleShowPassword(); + }} + > + +
+
+
+ ); +} + +const ForwardedPasswordInput: React.AbstractComponent< + Props, + HTMLInputElement, +> = React.forwardRef(PasswordInput); + +export default ForwardedPasswordInput; diff --git a/web/modals/input.css b/web/modals/input.css --- a/web/modals/input.css +++ b/web/modals/input.css @@ -20,4 +20,5 @@ line-height: var(--line-height-text); color: var(--fg); border-radius: 4px; + padding-right: 50px; } diff --git a/web/theme.css b/web/theme.css --- a/web/theme.css +++ b/web/theme.css @@ -180,4 +180,5 @@ --compose-subchannel-label-color: var(--shades-black-60); --compose-subchannel-mark-color: var(--violet-light-100); --enum-option-icon-color: var(--violet-dark-100); + --show-password-background-hover: var(--shades-black-70); }