feat: login form and page with country code part and animation
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
interface CountryCodeAdornmentProps {
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* An animated country code adornment that fades and slides into view.
|
||||
* Its visibility is controlled by the `show` prop.
|
||||
*/
|
||||
export function CountryCodeAdornment({ show }: CountryCodeAdornmentProps) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
// Animate width and opacity based on the 'show' prop
|
||||
width: show ? 'auto' : 0,
|
||||
opacity: show ? 1 : 0,
|
||||
transition: (theme) =>
|
||||
theme.transitions.create(['width', 'opacity'], {
|
||||
duration: theme.transitions.duration.short,
|
||||
}),
|
||||
// Prevent content from wrapping or spilling out during animation
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{/* This inner Box prevents the content from being squeezed during the transition */}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', px: 1, gap: 0.25 }}>
|
||||
<Typography variant="body1" color="text.primary">
|
||||
+41
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
72
src/features/authentication/components/LoginForm.tsx
Normal file
72
src/features/authentication/components/LoginForm.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
InputAdornment,
|
||||
Stack,
|
||||
TextField,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CountryCodeAdornment } from './CountryCodeAdornment';
|
||||
|
||||
const isNumeric = (value: string) => /^\d*$/.test(value);
|
||||
|
||||
export function LoginForm() {
|
||||
const { t } = useTranslation('authentication');
|
||||
const [value, setValue] = useState('');
|
||||
const [inputType, setInputType] = useState<'phone' | 'email'>('phone');
|
||||
|
||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = event.target.value;
|
||||
setValue(newValue);
|
||||
|
||||
// If the new value contains only digits (or is empty), it's a phone number
|
||||
if (isNumeric(newValue)) {
|
||||
setInputType('phone');
|
||||
} else {
|
||||
setInputType('email');
|
||||
}
|
||||
};
|
||||
|
||||
const showAdornment = inputType === 'phone' && value.length > 0;
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Stack spacing={1}>
|
||||
<Typography variant="h5">{t('loginForm.title')}</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{t('loginForm.description')}
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
<TextField
|
||||
label={t('loginForm.emailOrPhoneLabel')}
|
||||
value={value}
|
||||
onChange={handleInputChange}
|
||||
slotProps={{
|
||||
htmlInput: { dir: 'ltr' },
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment
|
||||
position="start"
|
||||
sx={{
|
||||
mr: 0,
|
||||
// transition: (theme) => theme.transitions.create('margin'),
|
||||
}}
|
||||
>
|
||||
<CountryCodeAdornment show={showAdornment} />
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
}}
|
||||
sx={{ my: 4 }}
|
||||
/>
|
||||
|
||||
<Stack spacing={2}>
|
||||
<Button>{t('loginForm.submitButton')}</Button>
|
||||
<Button variant="outlined">{t('loginForm.loginWithGoogle')}</Button>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
0
src/features/authentication/index.ts
Normal file
0
src/features/authentication/index.ts
Normal file
30
src/features/authentication/routes/LoginPage.tsx
Normal file
30
src/features/authentication/routes/LoginPage.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { FlexBox } from '@/components/components/common/FlexBox';
|
||||
import Logo from '@/components/Logo';
|
||||
import { Paper } from '@mui/material';
|
||||
import { LoginForm } from '../components/LoginForm';
|
||||
|
||||
export function LoginPage() {
|
||||
return (
|
||||
<FlexBox
|
||||
direction="column"
|
||||
align="center"
|
||||
justify="center"
|
||||
sx={{
|
||||
minHeight: '100vh',
|
||||
gap: 3,
|
||||
}}
|
||||
>
|
||||
<Logo />
|
||||
<Paper
|
||||
elevation={1}
|
||||
square
|
||||
sx={{
|
||||
p: 6,
|
||||
width: '34.5rem',
|
||||
}}
|
||||
>
|
||||
<LoginForm />
|
||||
</Paper>
|
||||
</FlexBox>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user