feat(login): implementar alternância de visibilidade de senha
- Implementa funcionalidade de mostrar/esconder senha na página de login - Integra ícones Eye e EyeOff para o controle de visibilidade - Adiciona animação 'fade-up' à página de login - Define novas classes de estilo para o campo de senha e botão de toggle - Configura a regra '@typescript-eslint/semi' no ESLint - Adiciona keyframes e animação 'fade-up' ao Tailwind CSSmaster
parent
91ec90f810
commit
81499374b6
|
|
@ -25,6 +25,7 @@ export default defineConfig([
|
||||||
'react/jsx-indent': ['error', 4],
|
'react/jsx-indent': ['error', 4],
|
||||||
'react/jsx-indent-props': ['error', 4],
|
'react/jsx-indent-props': ['error', 4],
|
||||||
'semi': ['error', 'always'],
|
'semi': ['error', 'always'],
|
||||||
|
'@typescript-eslint/semi': ['error', 'always'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Layout } from "../components/Layout";
|
import { Layout } from "../components/Layout";
|
||||||
import type { LoginProps } from "../types/Login";
|
import type { LoginProps } from "../types/Login";
|
||||||
|
import { Eye, EyeOff } from "lucide-icons-react";
|
||||||
|
|
||||||
export const Login = () => {
|
export const Login = () => {
|
||||||
const [form, setForm] = useState<LoginProps>({
|
const [form, setForm] = useState<LoginProps>({
|
||||||
email: "",
|
email: "",
|
||||||
password: ""
|
password: ""
|
||||||
});
|
});
|
||||||
|
const [showPassword, setShowPassword] = useState<boolean>(false);
|
||||||
|
|
||||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setForm({
|
setForm({
|
||||||
|
|
@ -15,6 +17,10 @@ export const Login = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleShowPassword = () => {
|
||||||
|
setShowPassword(!showPassword);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log(form);
|
console.log(form);
|
||||||
}, [form]);
|
}, [form]);
|
||||||
|
|
@ -35,10 +41,10 @@ export const Login = () => {
|
||||||
className={Styles.input}
|
className={Styles.input}
|
||||||
onChange={handleChange}/>
|
onChange={handleChange}/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className={Styles.inputWrapper}>
|
||||||
<label htmlFor="password" className={Styles.label}>Password:</label>
|
<label htmlFor="password" className={Styles.label}>Password:</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type={showPassword ? "text" : "password"}
|
||||||
id="password"
|
id="password"
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="********"
|
placeholder="********"
|
||||||
|
|
@ -46,6 +52,14 @@ export const Login = () => {
|
||||||
className={Styles.input}
|
className={Styles.input}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label={showPassword ? "Hide password" : "Show password"}
|
||||||
|
className={Styles.iconButton}
|
||||||
|
onClick={handleShowPassword}
|
||||||
|
>
|
||||||
|
{showPassword ? <Eye size={18} /> : <EyeOff size={18} />}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" className={Styles.button}>Login</button>
|
<button type="submit" className={Styles.button}>Login</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
@ -55,10 +69,12 @@ export const Login = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Styles = {
|
const Styles = {
|
||||||
layout: "h-screen flex flex-col gap-4 justify-center",
|
layout: "h-screen flex flex-col gap-4 justify-center animate-fade-up",
|
||||||
logo: "w-36 h-36 mx-auto mb-4",
|
logo: "w-36 h-36 mx-auto mb-4",
|
||||||
card: "w-96 p-8 shadow-lg rounded-lg border border-cardBorder bg-card",
|
card: "w-96 p-8 shadow-lg rounded-lg border border-cardBorder bg-card",
|
||||||
label: "block mb-2 text-md font-medium text-text",
|
label: "block mb-2 text-md font-medium text-text",
|
||||||
|
inputWrapper: "relative",
|
||||||
input: "bg-gray-50 border border-cardBorder text-text text-md rounded-lg outline-none block w-full p-2.5",
|
input: "bg-gray-50 border border-cardBorder text-text text-md rounded-lg outline-none block w-full p-2.5",
|
||||||
|
iconButton: "absolute right-3 top-1/2 text-text p-1 focus:outline-none",
|
||||||
button: "w-full bg-accent p-2 rounded-md mt-4 text-white font-bold text-lg hover:bg-hover transition duration-150",
|
button: "w-full bg-accent p-2 rounded-md mt-4 text-white font-bold text-lg hover:bg-hover transition duration-150",
|
||||||
};
|
};
|
||||||
|
|
@ -15,6 +15,15 @@ export default {
|
||||||
hover: '#D04A0F',
|
hover: '#D04A0F',
|
||||||
accent: '#E95A1B',
|
accent: '#E95A1B',
|
||||||
},
|
},
|
||||||
|
keyframes: {
|
||||||
|
'fade-up': {
|
||||||
|
'0%': { opacity: '0', transform: 'translateY(8px)' },
|
||||||
|
'100%': { opacity: '1', transform: 'translateY(0)' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
'fade-up': 'fade-up 300ms ease-out forwards',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue