Convert files in themes/ services/ contexts/ to TS
Convert all Javascript files in `client/src/themes`, `client/src/service`, `client/src/contexts` to Typescript.
Convert SvgIcon, Input, ConversationView, Button components to Typescript
Gitlab #30
Change-Id: I0d0505c28e21c771906edf8d5e7b8ce36126fa64
diff --git a/client/src/components/Input.tsx b/client/src/components/Input.tsx
new file mode 100644
index 0000000..af249ab
--- /dev/null
+++ b/client/src/components/Input.tsx
@@ -0,0 +1,193 @@
+import { IconButtonProps, Stack, TextField, TextFieldProps } from '@mui/material';
+import { styled } from '@mui/material/styles';
+import { ChangeEvent, ReactElement, useCallback, useEffect, useState } from 'react';
+
+import { InfoButton, ToggleVisibilityButton } from './Button';
+import { CheckedIcon, LockIcon, PenIcon, PersonIcon, RoundSaltireIcon } from './SvgIcon';
+
+const iconsHeight = '16px';
+const StyledCheckedIconSuccess = styled(CheckedIcon)(({ theme }) => ({
+ height: iconsHeight,
+ color: theme.palette.success.main,
+}));
+const StyledRoundSaltireIconError = styled(RoundSaltireIcon)(({ theme }) => ({
+ height: iconsHeight,
+ color: theme.palette.error.main,
+}));
+const StyledPenIconLight = styled(PenIcon)({ height: iconsHeight, color: '#03B9E9' });
+const StyledPenIconDark = styled(PenIcon)(({ theme }) => ({ height: iconsHeight, color: theme.palette.primary.dark }));
+const StyledPersonIconLight = styled(PersonIcon)({ height: iconsHeight, color: '#03B9E9' });
+const StyledLockIcon = styled(LockIcon)({ height: iconsHeight, color: '#03B9E9' });
+
+type InputProps = TextFieldProps & {
+ infoButtonProps?: IconButtonProps;
+ success?: boolean;
+};
+
+export const UsernameInput = ({ infoButtonProps, onChange: _onChange, ...props }: InputProps) => {
+ const [isSelected, setIsSelected] = useState(false);
+ const [input, setInput] = useState(props.defaultValue);
+ const [startAdornment, setStartAdornment] = useState<ReactElement | undefined>();
+
+ const onChange = useCallback(
+ (event: ChangeEvent<HTMLInputElement>) => {
+ setInput(event.target.value);
+ _onChange?.(event);
+ },
+ [_onChange]
+ );
+
+ useEffect(() => {
+ /* Handle startAdornment */
+ let Icon = StyledPersonIconLight;
+ let visibility = 'visible';
+ if (props.error) {
+ Icon = StyledRoundSaltireIconError;
+ } else if (props.success) {
+ Icon = StyledCheckedIconSuccess;
+ } else if (!isSelected && !input) {
+ visibility = 'hidden'; // keep icon's space so text does not move
+ }
+ setStartAdornment(<Icon sx={{ visibility }} />);
+ }, [props.error, props.success, isSelected, input]);
+
+ return (
+ <TextField
+ {...props}
+ label="Choose an identifier"
+ variant="standard"
+ InputLabelProps={{ shrink: !!(isSelected || input) }}
+ onChange={onChange}
+ InputProps={{
+ startAdornment,
+ endAdornment: <InfoButton {...infoButtonProps} />,
+ }}
+ onFocus={() => setIsSelected(true)}
+ onBlur={() => setIsSelected(false)}
+ />
+ );
+};
+
+export const PasswordInput = ({ infoButtonProps, onChange: _onChange, ...props }: InputProps) => {
+ const [showPassword, setShowPassword] = useState(false);
+ const [isSelected, setIsSelected] = useState(false);
+ const [input, setInput] = useState(props.defaultValue);
+ const [startAdornment, setStartAdornment] = useState<ReactElement | undefined>();
+
+ const toggleShowPassword = () => {
+ setShowPassword((showPassword) => !showPassword);
+ };
+
+ const onChange = useCallback(
+ (event: ChangeEvent<HTMLInputElement>) => {
+ setInput(event.target.value);
+ _onChange?.(event);
+ },
+ [_onChange]
+ );
+
+ useEffect(() => {
+ /* Handle startAdornment */
+ let Icon = StyledLockIcon;
+ let visibility = 'visible';
+ if (props.error) {
+ Icon = StyledRoundSaltireIconError;
+ } else if (props.success) {
+ Icon = StyledCheckedIconSuccess;
+ } else if (!isSelected && !input) {
+ visibility = 'hidden'; // keep icon's space so text does not move
+ }
+ setStartAdornment(<Icon sx={{ visibility }} />);
+ }, [props.error, props.success, isSelected, input]);
+
+ return (
+ <TextField
+ {...props}
+ label="Password"
+ type={showPassword ? 'text' : 'password'}
+ variant="standard"
+ autoComplete="current-password"
+ InputLabelProps={{ shrink: !!(isSelected || input) }}
+ onChange={onChange}
+ InputProps={{
+ startAdornment,
+ endAdornment: (
+ <Stack direction="row" spacing="14px">
+ <InfoButton {...infoButtonProps} />
+ <ToggleVisibilityButton visible={showPassword} onClick={toggleShowPassword} />
+ </Stack>
+ ),
+ }}
+ onFocus={() => setIsSelected(true)}
+ onBlur={() => setIsSelected(false)}
+ />
+ );
+};
+
+export const NickNameInput = ({ onChange: _onChange, ...props }: TextFieldProps) => {
+ const [isSelected, setIsSelected] = useState(false);
+ const [input, setInput] = useState(props.defaultValue);
+ const [startAdornmentVisibility, setStartAdornmentVisibility] = useState<'visible' | 'hidden'>('hidden');
+
+ const onChange = useCallback(
+ (event: ChangeEvent<HTMLInputElement>) => {
+ setInput(event.target.value);
+ _onChange?.(event);
+ },
+ [_onChange]
+ );
+
+ useEffect(() => {
+ setStartAdornmentVisibility(isSelected || input ? 'visible' : 'hidden');
+ }, [isSelected, input]);
+
+ return (
+ <TextField
+ {...props}
+ label="Nickname, surname..."
+ variant="standard"
+ InputLabelProps={{ shrink: !!(isSelected || input) }}
+ onChange={onChange}
+ InputProps={{
+ startAdornment: <StyledPenIconLight sx={{ visibility: startAdornmentVisibility }} />,
+ }}
+ onFocus={() => setIsSelected(true)}
+ onBlur={() => setIsSelected(false)}
+ />
+ );
+};
+
+export const RegularInput = ({ onChange: _onChange, ...props }: TextFieldProps) => {
+ const [isSelected, setIsSelected] = useState(false);
+ const [input, setInput] = useState(props.defaultValue);
+ const [startAdornmentVisibility, setStartAdornmentVisibility] = useState<'visible' | 'hidden'>('hidden');
+ const [endAdornmentVisibility, setEndAdornmentVisibility] = useState<'visible' | 'hidden'>('visible');
+
+ const onChange = useCallback(
+ (event: ChangeEvent<HTMLInputElement>) => {
+ setInput(event.target.value);
+ _onChange?.(event);
+ },
+ [_onChange]
+ );
+
+ useEffect(() => {
+ setStartAdornmentVisibility(isSelected || input ? 'visible' : 'hidden');
+ setEndAdornmentVisibility(isSelected || input ? 'hidden' : 'visible');
+ }, [isSelected, input]);
+
+ return (
+ <TextField
+ {...props}
+ variant="standard"
+ InputLabelProps={{ shrink: !!(isSelected || input) }}
+ onChange={onChange}
+ InputProps={{
+ startAdornment: <StyledPenIconLight sx={{ visibility: startAdornmentVisibility }} />,
+ endAdornment: <StyledPenIconDark sx={{ visibility: endAdornmentVisibility }} />,
+ }}
+ onFocus={() => setIsSelected(true)}
+ onBlur={() => setIsSelected(false)}
+ />
+ );
+};