blob: af249ab20544bf3df32dcfd1e748b6a5679385b1 [file] [log] [blame]
simon35378692022-10-02 23:25:57 -04001import { IconButtonProps, Stack, TextField, TextFieldProps } from '@mui/material';
simond47ef9e2022-09-28 22:24:28 -04002import { styled } from '@mui/material/styles';
simon35378692022-10-02 23:25:57 -04003import { ChangeEvent, ReactElement, useCallback, useEffect, useState } from 'react';
simon07b4eb02022-09-29 17:50:26 -04004
simon35378692022-10-02 23:25:57 -04005import { InfoButton, ToggleVisibilityButton } from './Button';
6import { CheckedIcon, LockIcon, PenIcon, PersonIcon, RoundSaltireIcon } from './SvgIcon';
idillon-sfl37c18df2022-08-26 18:44:27 -04007
simond47ef9e2022-09-28 22:24:28 -04008const iconsHeight = '16px';
9const StyledCheckedIconSuccess = styled(CheckedIcon)(({ theme }) => ({
10 height: iconsHeight,
11 color: theme.palette.success.main,
12}));
13const StyledRoundSaltireIconError = styled(RoundSaltireIcon)(({ theme }) => ({
14 height: iconsHeight,
15 color: theme.palette.error.main,
16}));
17const StyledPenIconLight = styled(PenIcon)({ height: iconsHeight, color: '#03B9E9' });
18const StyledPenIconDark = styled(PenIcon)(({ theme }) => ({ height: iconsHeight, color: theme.palette.primary.dark }));
19const StyledPersonIconLight = styled(PersonIcon)({ height: iconsHeight, color: '#03B9E9' });
20const StyledLockIcon = styled(LockIcon)({ height: iconsHeight, color: '#03B9E9' });
idillon-sfl37c18df2022-08-26 18:44:27 -040021
simon35378692022-10-02 23:25:57 -040022type InputProps = TextFieldProps & {
23 infoButtonProps?: IconButtonProps;
24 success?: boolean;
25};
26
27export const UsernameInput = ({ infoButtonProps, onChange: _onChange, ...props }: InputProps) => {
simond47ef9e2022-09-28 22:24:28 -040028 const [isSelected, setIsSelected] = useState(false);
29 const [input, setInput] = useState(props.defaultValue);
simon35378692022-10-02 23:25:57 -040030 const [startAdornment, setStartAdornment] = useState<ReactElement | undefined>();
idillon-sfl37c18df2022-08-26 18:44:27 -040031
simond47ef9e2022-09-28 22:24:28 -040032 const onChange = useCallback(
simon35378692022-10-02 23:25:57 -040033 (event: ChangeEvent<HTMLInputElement>) => {
simond47ef9e2022-09-28 22:24:28 -040034 setInput(event.target.value);
simon80b7b3b2022-09-28 17:50:10 -040035 _onChange?.(event);
simond47ef9e2022-09-28 22:24:28 -040036 },
simon80b7b3b2022-09-28 17:50:10 -040037 [_onChange]
simond47ef9e2022-09-28 22:24:28 -040038 );
idillon-sfl37c18df2022-08-26 18:44:27 -040039
simond47ef9e2022-09-28 22:24:28 -040040 useEffect(() => {
41 /* Handle startAdornment */
42 let Icon = StyledPersonIconLight;
43 let visibility = 'visible';
44 if (props.error) {
45 Icon = StyledRoundSaltireIconError;
46 } else if (props.success) {
47 Icon = StyledCheckedIconSuccess;
48 } else if (!isSelected && !input) {
49 visibility = 'hidden'; // keep icon's space so text does not move
idillon-sfl37c18df2022-08-26 18:44:27 -040050 }
simond47ef9e2022-09-28 22:24:28 -040051 setStartAdornment(<Icon sx={{ visibility }} />);
52 }, [props.error, props.success, isSelected, input]);
idillon-sfl37c18df2022-08-26 18:44:27 -040053
simond47ef9e2022-09-28 22:24:28 -040054 return (
55 <TextField
56 {...props}
57 label="Choose an identifier"
58 variant="standard"
59 InputLabelProps={{ shrink: !!(isSelected || input) }}
60 onChange={onChange}
61 InputProps={{
62 startAdornment,
63 endAdornment: <InfoButton {...infoButtonProps} />,
64 }}
65 onFocus={() => setIsSelected(true)}
66 onBlur={() => setIsSelected(false)}
67 />
68 );
69};
idillon-sfl37c18df2022-08-26 18:44:27 -040070
simon35378692022-10-02 23:25:57 -040071export const PasswordInput = ({ infoButtonProps, onChange: _onChange, ...props }: InputProps) => {
simond47ef9e2022-09-28 22:24:28 -040072 const [showPassword, setShowPassword] = useState(false);
73 const [isSelected, setIsSelected] = useState(false);
74 const [input, setInput] = useState(props.defaultValue);
simon35378692022-10-02 23:25:57 -040075 const [startAdornment, setStartAdornment] = useState<ReactElement | undefined>();
idillon-sfl37c18df2022-08-26 18:44:27 -040076
simond47ef9e2022-09-28 22:24:28 -040077 const toggleShowPassword = () => {
78 setShowPassword((showPassword) => !showPassword);
79 };
80
81 const onChange = useCallback(
simon35378692022-10-02 23:25:57 -040082 (event: ChangeEvent<HTMLInputElement>) => {
simond47ef9e2022-09-28 22:24:28 -040083 setInput(event.target.value);
simon80b7b3b2022-09-28 17:50:10 -040084 _onChange?.(event);
simond47ef9e2022-09-28 22:24:28 -040085 },
simon80b7b3b2022-09-28 17:50:10 -040086 [_onChange]
simond47ef9e2022-09-28 22:24:28 -040087 );
88
89 useEffect(() => {
90 /* Handle startAdornment */
91 let Icon = StyledLockIcon;
92 let visibility = 'visible';
93 if (props.error) {
94 Icon = StyledRoundSaltireIconError;
95 } else if (props.success) {
96 Icon = StyledCheckedIconSuccess;
97 } else if (!isSelected && !input) {
98 visibility = 'hidden'; // keep icon's space so text does not move
99 }
100 setStartAdornment(<Icon sx={{ visibility }} />);
101 }, [props.error, props.success, isSelected, input]);
102
103 return (
104 <TextField
105 {...props}
106 label="Password"
107 type={showPassword ? 'text' : 'password'}
108 variant="standard"
109 autoComplete="current-password"
110 InputLabelProps={{ shrink: !!(isSelected || input) }}
111 onChange={onChange}
112 InputProps={{
113 startAdornment,
114 endAdornment: (
115 <Stack direction="row" spacing="14px">
116 <InfoButton {...infoButtonProps} />
117 <ToggleVisibilityButton visible={showPassword} onClick={toggleShowPassword} />
118 </Stack>
119 ),
120 }}
121 onFocus={() => setIsSelected(true)}
122 onBlur={() => setIsSelected(false)}
123 />
124 );
125};
idillon-sfl37c18df2022-08-26 18:44:27 -0400126
simon35378692022-10-02 23:25:57 -0400127export const NickNameInput = ({ onChange: _onChange, ...props }: TextFieldProps) => {
simond47ef9e2022-09-28 22:24:28 -0400128 const [isSelected, setIsSelected] = useState(false);
129 const [input, setInput] = useState(props.defaultValue);
simon35378692022-10-02 23:25:57 -0400130 const [startAdornmentVisibility, setStartAdornmentVisibility] = useState<'visible' | 'hidden'>('hidden');
idillon-sfl37c18df2022-08-26 18:44:27 -0400131
simond47ef9e2022-09-28 22:24:28 -0400132 const onChange = useCallback(
simon35378692022-10-02 23:25:57 -0400133 (event: ChangeEvent<HTMLInputElement>) => {
simond47ef9e2022-09-28 22:24:28 -0400134 setInput(event.target.value);
simon80b7b3b2022-09-28 17:50:10 -0400135 _onChange?.(event);
simond47ef9e2022-09-28 22:24:28 -0400136 },
simon80b7b3b2022-09-28 17:50:10 -0400137 [_onChange]
simond47ef9e2022-09-28 22:24:28 -0400138 );
idillon-sfl37c18df2022-08-26 18:44:27 -0400139
simond47ef9e2022-09-28 22:24:28 -0400140 useEffect(() => {
141 setStartAdornmentVisibility(isSelected || input ? 'visible' : 'hidden');
142 }, [isSelected, input]);
idillon-sfl37c18df2022-08-26 18:44:27 -0400143
simond47ef9e2022-09-28 22:24:28 -0400144 return (
145 <TextField
146 {...props}
147 label="Nickname, surname..."
148 variant="standard"
149 InputLabelProps={{ shrink: !!(isSelected || input) }}
150 onChange={onChange}
151 InputProps={{
152 startAdornment: <StyledPenIconLight sx={{ visibility: startAdornmentVisibility }} />,
153 }}
154 onFocus={() => setIsSelected(true)}
155 onBlur={() => setIsSelected(false)}
156 />
157 );
158};
idillon-sfl37c18df2022-08-26 18:44:27 -0400159
simon35378692022-10-02 23:25:57 -0400160export const RegularInput = ({ onChange: _onChange, ...props }: TextFieldProps) => {
simond47ef9e2022-09-28 22:24:28 -0400161 const [isSelected, setIsSelected] = useState(false);
162 const [input, setInput] = useState(props.defaultValue);
simon35378692022-10-02 23:25:57 -0400163 const [startAdornmentVisibility, setStartAdornmentVisibility] = useState<'visible' | 'hidden'>('hidden');
164 const [endAdornmentVisibility, setEndAdornmentVisibility] = useState<'visible' | 'hidden'>('visible');
idillon-sfl37c18df2022-08-26 18:44:27 -0400165
simond47ef9e2022-09-28 22:24:28 -0400166 const onChange = useCallback(
simon35378692022-10-02 23:25:57 -0400167 (event: ChangeEvent<HTMLInputElement>) => {
simond47ef9e2022-09-28 22:24:28 -0400168 setInput(event.target.value);
simon80b7b3b2022-09-28 17:50:10 -0400169 _onChange?.(event);
simond47ef9e2022-09-28 22:24:28 -0400170 },
simon80b7b3b2022-09-28 17:50:10 -0400171 [_onChange]
simond47ef9e2022-09-28 22:24:28 -0400172 );
idillon-sfl37c18df2022-08-26 18:44:27 -0400173
simond47ef9e2022-09-28 22:24:28 -0400174 useEffect(() => {
175 setStartAdornmentVisibility(isSelected || input ? 'visible' : 'hidden');
176 setEndAdornmentVisibility(isSelected || input ? 'hidden' : 'visible');
177 }, [isSelected, input]);
idillon-sfl37c18df2022-08-26 18:44:27 -0400178
simond47ef9e2022-09-28 22:24:28 -0400179 return (
180 <TextField
181 {...props}
182 variant="standard"
183 InputLabelProps={{ shrink: !!(isSelected || input) }}
184 onChange={onChange}
185 InputProps={{
186 startAdornment: <StyledPenIconLight sx={{ visibility: startAdornmentVisibility }} />,
187 endAdornment: <StyledPenIconDark sx={{ visibility: endAdornmentVisibility }} />,
188 }}
189 onFocus={() => setIsSelected(true)}
190 onBlur={() => setIsSelected(false)}
191 />
192 );
193};