forked from M3-Academy/desafio-react-e-typescript
develop #16
147
src/components/Molecules/Accordion/README.md
Normal file
147
src/components/Molecules/Accordion/README.md
Normal file
@ -0,0 +1,147 @@
|
||||
# Componente Accordion
|
||||
|
||||
Esse componente foi criado sem nenhuma biblioteca externa, aqui deixo as maneiras de utilizar esse componente
|
||||
|
||||
## Primeiros Passos
|
||||
|
||||
Caso queira usar o componente, é recomendado, que estude sobre, `useContext`, `createContext`, e `useState`;
|
||||
Caso queira usar e não tem os conhecimentos, veja os exemplo a seguir.
|
||||
|
||||
```jsx
|
||||
import { createContext, useState } from 'react'
|
||||
|
||||
interface fooProps {
|
||||
current: string | number // O current(Atual) pode ser uma string ou number
|
||||
handleSetCurrent: (customkey) => void // O handleSetCurrent é uma função que vc pode criar para fazer as alterações que desejar no funcionamento
|
||||
// O paremetro passado chamado de customKey servi para identificar e 'setar' o Accordion Atual
|
||||
}
|
||||
|
||||
const foo = createContext({
|
||||
current: '', // Aqui demostra o componente que está aberto atualmente
|
||||
} as fooProps) // Aqui estamos o nosso contexto do nosso Accordion
|
||||
|
||||
// Provedor do Contexto
|
||||
interface AccordionProviderItemsProps {
|
||||
children: ReactNode | ReactNode[]
|
||||
value: fooProps
|
||||
}
|
||||
|
||||
export const AccordionProviderItems = ({
|
||||
children,
|
||||
value,
|
||||
}: AccordionProviderItemsProps) => {
|
||||
return (
|
||||
<>
|
||||
<AccordionContextItems.Provider value={value}>
|
||||
{children}
|
||||
</AccordionContextItems.Provider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Estado que deve ser criado
|
||||
const [accordionCurrentIsOpen, setAccordionCurrentIsOpen] = useState<string | number | ''>('')
|
||||
|
||||
// Demostração da função handleSetCurrent
|
||||
function handleSetCurrent(customkey: string | number | '') {
|
||||
if (customkey === accordionCurrentIsOpen) {/*A verificação do customKey com o state*/
|
||||
// Essa veficação fica engarregada de verificar se o usario clicou no mesmo Accordion
|
||||
|
||||
// SE Clicou no mesmo Accordion 'setar' a customKey para 'vazio'
|
||||
// SENÃO 'setar' customKey
|
||||
|
||||
// A customKey é um identificador para cada Accordion da página caso queira, já que vc pode criar em qualquer lugar dá página
|
||||
|
||||
// Ok, se isso é um identificador se eu colocar outro igual ?
|
||||
// Simplemente ele íra abrir e fechar os identificadores iguais e fechar o mesmo quando clicado por um deles
|
||||
|
||||
setAccordionCurrentIsOpen('')
|
||||
} else {
|
||||
setAccordionCurrentIsOpen(customkey)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Como usar ?
|
||||
import { Accordion } from "foo"
|
||||
|
||||
function Exemple() {
|
||||
const [accordionCurrentIsOpen, setAccordionCurrentIsOpen] = useState<string | number | ''>('')
|
||||
|
||||
|
||||
function handleSetCurrent(customkey: string | number | '') {
|
||||
if (customkey === accordionCurrentIsOpen) {
|
||||
setAccordionCurrentIsOpen('')
|
||||
} else {
|
||||
setAccordionCurrentIsOpen(customkey)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AccordionProviderItems
|
||||
value={{
|
||||
current: accordionCurrentIsOpen,
|
||||
handleSetCurrent: handleSetCurrentAccordion,
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<Accordion customKey={"Identificador"}>
|
||||
<Accordion.Header customKey={"Identificador"}>
|
||||
{/*Elemento escolhido para abrir*/}
|
||||
</Accordion.Header>
|
||||
<Accordion.Content>
|
||||
{/*Qualquer Elemento que vai renderizar*/}
|
||||
</Accordion.Content>
|
||||
</Accordion>
|
||||
</div>
|
||||
<div>
|
||||
<Accordion customKey={"Identificador"}>
|
||||
<Accordion.Header customKey={"Identificador"}>
|
||||
{/*Elemento escolhido para abrir*/}
|
||||
</Accordion.Header>
|
||||
<Accordion.Content>
|
||||
{/*Qualquer Elemento que vai renderizar*/}
|
||||
</Accordion.Content>
|
||||
</Accordion>
|
||||
</div>
|
||||
<div>
|
||||
<Accordion customKey={"Identificador"}>
|
||||
<Accordion.Header customKey={"Identificador"}>
|
||||
{/*Elemento escolhido para abrir*/}
|
||||
</Accordion.Header>
|
||||
<Accordion.Content>
|
||||
{/*Qualquer Elemento que vai renderizar*/}
|
||||
</Accordion.Content>
|
||||
</Accordion>
|
||||
</div>
|
||||
</AccordionProviderItems>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```css
|
||||
/*Como Estilizar*/
|
||||
.accordion {
|
||||
/*...*/
|
||||
}
|
||||
|
||||
.accordion.active {
|
||||
/*...*/
|
||||
}
|
||||
|
||||
.accordion-content {
|
||||
/*...*/
|
||||
}
|
||||
|
||||
.accordion-icon {
|
||||
/*...*/
|
||||
}
|
||||
|
||||
/*Alguns Components estão disponiveis para colocar classes HTML criadas por você*/
|
||||
```
|
||||
|
||||
Aviso Componente Criado em um desafio, pode ocorrer problemas com algumas coisas, então devido a colocação em que estão, esse componente não será atualizado
|
@ -91,25 +91,65 @@
|
||||
text-align: center;
|
||||
|
||||
label {
|
||||
text-decoration: underline;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
font-size: 22px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: var(--txt-normal);
|
||||
line-height: 16.41px;
|
||||
|
||||
span {
|
||||
.form-icon-required {
|
||||
display: inline-block;
|
||||
|
||||
text-decoration: none;
|
||||
color: var(--clr-common-red);
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
color: var(--clr-common-black);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 2500px) {
|
||||
line-height: 32.81px;
|
||||
}
|
||||
}
|
||||
|
||||
input[type='checkbox'] {
|
||||
width: 18.64px;
|
||||
height: 18px;
|
||||
label {
|
||||
input {
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
&:checked {
|
||||
border-radius: 3px;
|
||||
border: 1px solid var(--clr-common-black);
|
||||
& ~ .custom-checkbox {
|
||||
width: 18.64px;
|
||||
height: 18px;
|
||||
margin-left: 4.28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid var(--clr-common-black);
|
||||
border-radius: 3px;
|
||||
|
||||
@media screen and (min-width: 2500px) {
|
||||
width: 36.4px;
|
||||
height: 35.15px;
|
||||
}
|
||||
}
|
||||
|
||||
&:checked {
|
||||
& ~ .custom-checkbox:after {
|
||||
content: '';
|
||||
width: 12.64px;
|
||||
height: 12px;
|
||||
border-radius: 3px;
|
||||
background-color: var(--clr-primary-blue-500);
|
||||
@media screen and (min-width: 2500px) {
|
||||
width: 26.4px;
|
||||
height: 26.15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -151,7 +191,8 @@
|
||||
color: var(--clr-common-green);
|
||||
font-size: var(--txt-xs);
|
||||
line-height: 14.06px;
|
||||
margin-top: 12px;
|
||||
margin-top: 0px;
|
||||
height: 0;
|
||||
|
||||
@media screen and (min-width: 2500px) {
|
||||
line-height: 28.13px;
|
||||
@ -160,5 +201,7 @@
|
||||
|
||||
.form__success.form__success-active {
|
||||
opacity: 1;
|
||||
margin-top: 12px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
@ -152,10 +152,13 @@ export function Contact() {
|
||||
|
||||
<div className="form-check">
|
||||
<label htmlFor="terms">
|
||||
<span>*</span>
|
||||
Declaro que li e aceito
|
||||
<span>
|
||||
<span className="form-icon-required">*</span>{' '}
|
||||
<a href="/">Declaro que li e aceito</a>
|
||||
</span>
|
||||
<Field id="terms" name="terms" type="checkbox" required />
|
||||
<span className="custom-checkbox"></span>
|
||||
</label>
|
||||
<Field id="terms" name="terms" type="checkbox" required />
|
||||
</div>
|
||||
|
||||
<button className={styles['submit']} type="submit">
|
||||
|
@ -11,7 +11,11 @@ const dateRegexPattern =
|
||||
/^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]))\1|(?:(?:29|30)(\/|-|\.)(?:0?[13-9]|1[0-2])\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)0?2\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9])|(?:1[0-2]))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$/
|
||||
|
||||
export default Yup.object().shape({
|
||||
fullname: Yup.string().trim().min(7).max(60).required(messages.required),
|
||||
fullname: Yup.string()
|
||||
.trim()
|
||||
.min(7, 'Nome completo muito curto')
|
||||
.max(60, 'Nome completo muito grande')
|
||||
.required(messages.required),
|
||||
email: Yup.string()
|
||||
.matches(emailRegexPattern, { message: 'Email ínvalido' })
|
||||
.required(messages.required)
|
||||
|
@ -6,8 +6,8 @@ export function RouterBreadcrumb() {
|
||||
let list = useMemo(() => [{ name: 'Introduction', href: '/' }], [])
|
||||
|
||||
return (
|
||||
<div className={styles['breadcrumb-container']}>
|
||||
<nav className={styles['breadcrumb-container']}>
|
||||
<Breadcrumb className={styles['breadcrumb']} list={list} />
|
||||
</div>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
@ -20,18 +20,20 @@ export function ScrollFixed() {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<ul className={styles['scroll__fixed']}>
|
||||
<ItemList
|
||||
className={styles['scroll__fixed-whatsapp']}
|
||||
href="https://wa.me/254777123456"
|
||||
>
|
||||
<img src={whatsappImg} alt="" />
|
||||
</ItemList>
|
||||
{isScrollTop && (
|
||||
<ItemList className={styles['scroll__fixed-top']} href={'#root'}>
|
||||
<img src={scrollTopImg} alt="" />
|
||||
<ul>
|
||||
<ul className={styles['scroll__fixed']}>
|
||||
<ItemList
|
||||
className={styles['scroll__fixed-whatsapp']}
|
||||
href="https://wa.me/254777123456"
|
||||
>
|
||||
<img src={whatsappImg} alt="" />
|
||||
</ItemList>
|
||||
)}
|
||||
{isScrollTop && (
|
||||
<ItemList className={styles['scroll__fixed-top']} href={'#root'}>
|
||||
<img src={scrollTopImg} alt="" />
|
||||
</ItemList>
|
||||
)}
|
||||
</ul>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ export function Router() {
|
||||
element={
|
||||
<>
|
||||
<Header />
|
||||
<RouterBreadcrumb />
|
||||
<div className="window-routes">
|
||||
<RouterBreadcrumb />
|
||||
<Outlet />
|
||||
</div>
|
||||
<Footer />
|
||||
@ -34,10 +34,8 @@ export function Router() {
|
||||
<h1 className="main-title">Institutional</h1>
|
||||
<div className="window-initial">
|
||||
<Sidebar />
|
||||
<div>
|
||||
<div className="main-container">
|
||||
<Outlet />
|
||||
</div>
|
||||
<div className="main-container">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
@ -106,7 +106,7 @@ textarea {
|
||||
|
||||
@media screen and (min-width: 1025px) {
|
||||
grid-template-columns:
|
||||
function.fluid(332px, 1080px)
|
||||
function.fluid(302px, 1080px)
|
||||
function.fluid(748px, 1080px);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Field, Form, Formik } from 'formik'
|
||||
import { useEffect, useState } from 'react'
|
||||
import * as Yup from 'yup'
|
||||
|
||||
import styles from '../index.module.scss'
|
||||
@ -8,6 +9,16 @@ const schema = Yup.object({
|
||||
})
|
||||
|
||||
export function Newsletter() {
|
||||
const [isSubmiting, setIsSubmiting] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (isSubmiting) {
|
||||
setTimeout(() => {
|
||||
setIsSubmiting(false)
|
||||
}, 2000)
|
||||
}
|
||||
}, [isSubmiting])
|
||||
|
||||
return (
|
||||
<section className={styles['newsletter']}>
|
||||
<div className={styles['newsletter__container']}>
|
||||
@ -15,7 +26,13 @@ export function Newsletter() {
|
||||
<h3>Assine nossa newsletter</h3>
|
||||
<Formik
|
||||
initialValues={{ email: '' }}
|
||||
onSubmit={(e) => console.log(e)}
|
||||
onSubmit={(values, e) => {
|
||||
e.resetForm({
|
||||
isSubmitting: true,
|
||||
})
|
||||
|
||||
setIsSubmiting(true)
|
||||
}}
|
||||
validationSchema={schema}
|
||||
>
|
||||
<Form className={styles['newsletter__form']}>
|
||||
@ -26,6 +43,13 @@ export function Newsletter() {
|
||||
</div>
|
||||
<button type="submit">Enviar</button>
|
||||
</div>
|
||||
<p
|
||||
className={`form__success ${
|
||||
isSubmiting ? 'form__success-active' : ''
|
||||
}`}
|
||||
>
|
||||
*Formulário enviado com sucesso!
|
||||
</p>
|
||||
</fieldset>
|
||||
</Form>
|
||||
</Formik>
|
||||
|
@ -11,19 +11,19 @@ import css from '../index.module.scss'
|
||||
export function Socials() {
|
||||
return (
|
||||
<ul className={css.socials}>
|
||||
<ItemList href="/">
|
||||
<ItemList href="https://facebook.com">
|
||||
<img src={facebookIcon} alt="" />
|
||||
</ItemList>
|
||||
<ItemList href="/">
|
||||
<ItemList href="https://instagram.com">
|
||||
<img src={instagramIcon} alt="" />
|
||||
</ItemList>
|
||||
<ItemList href="/">
|
||||
<ItemList href="https://twitter.com">
|
||||
<img src={twitterIcon} alt="" />
|
||||
</ItemList>
|
||||
<ItemList href="/">
|
||||
<ItemList href="https://youtube.com">
|
||||
<img src={youtubeIcon} alt="" />
|
||||
</ItemList>
|
||||
<ItemList href="/">
|
||||
<ItemList href="https://linkedin.com">
|
||||
<img src={linkedinIcon} alt="" />
|
||||
</ItemList>
|
||||
</ul>
|
||||
|
@ -114,6 +114,23 @@
|
||||
height: 59px;
|
||||
}
|
||||
}
|
||||
|
||||
.form__success {
|
||||
display: none;
|
||||
transition: 200ms;
|
||||
color: var(--clr-common-green);
|
||||
font-size: var(--txt-xs);
|
||||
line-height: 14.06px;
|
||||
margin-top: 12px;
|
||||
|
||||
@media screen and (min-width: 2500px) {
|
||||
line-height: 28.13px;
|
||||
}
|
||||
}
|
||||
|
||||
.form__success.form__success-active {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.newsletter__form {
|
||||
@ -132,6 +149,13 @@
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.05em;
|
||||
|
||||
transition: 200ms ease-in-out;
|
||||
|
||||
&:hover {
|
||||
color: var(--clr-common-black);
|
||||
background-color: var(--clr-primary-blue-500);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1025px) {
|
||||
width: function.fluid(126px, 474px);
|
||||
font-size: var(--txt-xs);
|
||||
|
@ -17,7 +17,7 @@ export function Sidebar() {
|
||||
]
|
||||
|
||||
return (
|
||||
<aside className={styles['sidebar']}>
|
||||
<nav className={styles['sidebar']}>
|
||||
<div className={styles['sidebar__container']}>
|
||||
<ul className={styles['sidebar__list']}>
|
||||
{paths.map(({ path, name }) => {
|
||||
@ -33,6 +33,6 @@ export function Sidebar() {
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user