Merge pull request #17 from KieIO/m3-lytran

M3: home page, modal login, register
This commit is contained in:
lytrankieio123
2021-08-27 18:31:24 +07:00
committed by GitHub
27 changed files with 467 additions and 74 deletions

View File

@@ -5,7 +5,10 @@
display: flex;
justify-content: center;
align-items: center;
padding: 0.8rem 3.2rem;
padding: 1rem 2rem;
@screen md {
padding: 0.8rem 3.2rem;
}
&:disabled {
filter: brightness(0.9);
cursor: not-allowed;
@@ -76,9 +79,15 @@
}
&.large {
padding: 1.6rem 4.8rem;
padding: 1rem 1.5rem;
&.onlyIcon {
padding: 1.6rem;
padding: 1rem;
}
@screen md {
padding: 1.6rem 4.8rem;
&.onlyIcon {
padding: 1.6rem;
}
}
&.loading {
&::before {
@@ -97,10 +106,6 @@
.icon {
margin: 0 1.6rem 0 0;
}
.label,
.icon {
svg path {
fill: currentColor;
}

View File

@@ -1,19 +1,18 @@
import classNames from 'classnames'
import React, { memo, useEffect, useState } from 'react'
import { useModalCommon } from 'src/components/hooks/useModalCommon'
import { isMobile } from 'src/utils/funtion.utils'
import ModalAuthenticate from '../ModalAuthenticate/ModalAuthenticate'
import HeaderHighLight from './components/HeaderHighLight/HeaderHighLight'
import HeaderMenu from './components/HeaderMenu/HeaderMenu'
import HeaderSubMenu from './components/HeaderSubMenu/HeaderSubMenu'
import HeaderSubMenuMobile from './components/HeaderSubMenuMobile/HeaderSubMenuMobile'
import s from './Header.module.scss'
interface Props {
className?: string
children?: any
}
const Header = memo(({ }: Props) => {
const Header = memo(() => {
const [isFullHeader, setIsFullHeader] = useState<boolean>(true)
const { visible: visibleModalAuthen, closeModal: closeModalAuthen, openModal: openModalAuthen } = useModalCommon({ initialValue: true })
useEffect(() => {
window.addEventListener('scroll', handleScroll)
@@ -36,11 +35,12 @@ const Header = memo(({ }: Props) => {
<header className={classNames({ [s.header]: true, [s.full]: isFullHeader })}>
<HeaderHighLight isShow={isFullHeader} />
<div className={s.menu}>
<HeaderMenu isFull={isFullHeader} />
<HeaderMenu isFull={isFullHeader} openModalAuthen={openModalAuthen} />
<HeaderSubMenu isShow={isFullHeader} />
</div>
</header>
<HeaderSubMenuMobile />
<ModalAuthenticate visible={visibleModalAuthen} closeModal={closeModalAuthen} />
</>
)
})

View File

@@ -1,30 +1,35 @@
import classNames from 'classnames'
import Link from 'next/link'
import { memo } from 'react'
import { memo, useMemo } from 'react'
import InputSearch from 'src/components/common/InputSearch/InputSearch'
import MenuDropdown from 'src/components/common/MenuDropdown/MenuDropdown'
import { IconBuy, IconHeart, IconHistory, IconUser } from 'src/components/icons'
import { ACCOUNT_TAB, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'
import s from './HeaderMenu.module.scss'
const OPTION_MENU = [
{
link: ROUTE.ACCOUNT,
name: 'Account',
},
{
link: '/',
name: 'Logout',
},
]
interface Props {
children?: any,
isFull: boolean,
openModalAuthen: () => void,
}
const HeaderMenu = memo(({ isFull }: Props) => {
const HeaderMenu = memo(({ isFull, openModalAuthen }: Props) => {
const optionMenu = useMemo(() => [
{
onClick: openModalAuthen,
name: 'Login (Demo)',
},
{
link: ROUTE.ACCOUNT,
name: 'Account',
},
{
link: '/',
name: 'Logout',
},
], [openModalAuthen])
return (
<section className={classNames({ [s.headerMenu]: true, [s.full]: isFull })}>
<div className={s.left}>
@@ -54,7 +59,7 @@ const HeaderMenu = memo(({ isFull }: Props) => {
</Link>
</li>
<li>
<MenuDropdown options={OPTION_MENU} isHasArrow={false}><IconUser /></MenuDropdown>
<MenuDropdown options={optionMenu} isHasArrow={false}><IconUser /></MenuDropdown>
</li>
<li>
<button>

View File

@@ -7,6 +7,7 @@
padding: 2rem 1rem;
border-top: 1px solid var(--border-line);
box-shadow: -5px 6px 10px rgba(0, 0, 0, 0.2);
z-index: 9999;
.menu {
@apply grid grid-cols-4;
li {

View File

@@ -10,7 +10,7 @@ interface Props {
children?: React.ReactNode,
value?: string | number,
placeholder?: string,
type?: 'text' | 'number' | 'email',
type?: 'text' | 'number' | 'email' | 'password',
styleType?: 'default' | 'custom',
backgroundTransparent?: boolean,
icon?: React.ReactNode,

View File

@@ -62,17 +62,25 @@
.menuIner {
@apply rounded list-none bg-white;
border: 1px solid var(--text-active);
margin-top: .4rem;
margin-top: 0.4rem;
li {
@apply block w-full transition-all duration-200 cursor-pointer text-active;
padding: 0.8rem 1.6rem;
&:hover {
@apply bg-primary-lightest;
color: var(--primary);
button {
all: unset;
color: currentColor;
@apply text-left w-full;
}
button,
a {
padding: 0.8rem 1.6rem;
}
a {
@apply block;
}
&:hover {
@apply bg-primary-lightest;
color: var(--primary);
}
}
}
}

View File

@@ -5,7 +5,7 @@ import s from './MenuDropdown.module.scss';
interface Props {
children?: React.ReactNode,
options: { link: string, name: string }[],
options: { link?: string, name: string, onClick?: () => void }[],
isHasArrow?: boolean,
align?: 'left'
}
@@ -26,11 +26,16 @@ const MenuDropdown = ({ options, children, isHasArrow = true, align }: Props) =>
<ul className={s.menuIner}>
{
options.map(item => <li key={item.name}>
<Link href={item.link}>
<a >
{item.onClick ?
<button onClick={item.onClick}>
{item.name}
</a>
</Link>
</button>
:
<Link href={item.link || ''}>
<a >
{item.name}
</a>
</Link>}
</li>)
}
</ul>

View File

@@ -0,0 +1,10 @@
.formAuthenticate {
@apply overflow-hidden;
.inner {
@apply grid grid-cols-2 overflow-hidden transition-all duration-200;
width: 200%;
&.register {
transform: translateX(-50%);
}
}
}

View File

@@ -0,0 +1,36 @@
import classNames from 'classnames'
import React, { useState } from 'react'
import ModalCommon from '../ModalCommon/ModalCommon'
import FormLogin from './components/FormLogin/FormLogin'
import FormRegister from './components/FormRegister/FormRegister'
import s from './ModalAuthenticate.module.scss'
interface Props {
visible: boolean,
closeModal: () => void,
}
const ModalAuthenticate = ({ visible, closeModal }: Props) => {
const [isLogin, setIsLogin] = useState<boolean>(true)
const onSwitch = () => {
setIsLogin(!isLogin)
}
return (
<ModalCommon visible={visible} onClose={closeModal} title={isLogin ? 'Sign In' : 'Create Account'}>
<section className={s.formAuthenticate}>
<div className={classNames({
[s.inner]: true,
[s.register]: !isLogin,
})}>
<FormLogin isHide={!isLogin} onSwitch={onSwitch} />
<FormRegister isHide={isLogin} onSwitch={onSwitch} />
</div>
</section>
</ModalCommon>
)
}
export default ModalAuthenticate

View File

@@ -0,0 +1,35 @@
.formAuthen {
@apply bg-white w-full;
.inner {
@screen md {
max-width: 52rem;
margin: auto;
}
.body {
> div {
&:not(:last-child) {
margin-bottom: 1.6rem;
}
}
}
.others {
@apply font-bold text-center;
margin-top: 4rem;
span {
@apply text-active;
margin-right: 0.8rem;
}
button {
all: unset;
@apply text-primary cursor-pointer;
&:focus-visible {
outline: 2px solid #000;
}
&:focus {
outline: none;
}
}
}
}
}

View File

@@ -0,0 +1,9 @@
.bottom {
@apply flex justify-between items-center;
margin: 4rem auto;
.forgotPassword {
@apply font-bold;
color: var(--primary);
}
}

View File

@@ -0,0 +1,53 @@
import Link from 'next/link'
import React, { useRef, useEffect } from 'react'
import { Inputcommon, ButtonCommon } from 'src/components/common'
import { ROUTE } from 'src/utils/constanst.utils'
import SocialAuthen from '../SocialAuthen/SocialAuthen'
import s from '../FormAuthen.module.scss'
import styles from './FormLogin.module.scss'
import classNames from 'classnames'
import { CustomInputCommon } from 'src/utils/type.utils'
interface Props {
isHide: boolean,
onSwitch: () => void
}
const FormLogin = ({ onSwitch, isHide }: Props) => {
const emailRef = useRef<CustomInputCommon>(null)
useEffect(() => {
if (!isHide) {
emailRef.current?.focus()
}
}, [isHide])
return (
<section className={classNames({
[s.formAuthen]: true,
// [styles.hide]: isHide
})}>
<div className={s.inner}>
<div className={s.body}>
<Inputcommon placeholder='Email Address' type='email' ref={emailRef} />
<Inputcommon placeholder='Password' type='password' />
</div>
<div className={styles.bottom}>
<Link href={ROUTE.FORGOT_PASSWORD}>
<a href="" className={styles.forgotPassword}>
Forgot Password?
</a>
</Link>
<ButtonCommon size='large'>Sign in</ButtonCommon>
</div>
<SocialAuthen />
<div className={s.others}>
<span>Don't have an account?</span>
<button onClick={onSwitch}>Create Account</button>
</div>
</div>
</section>
)
}
export default FormLogin

View File

@@ -0,0 +1,15 @@
@import "../../../../../styles/utilities";
.formRegister {
.passwordNote {
@apply text-center caption text-label;
margin-top: 0.8rem;
}
.bottom {
@apply flex justify-between items-center w-full;
margin: 4rem auto;
button {
@apply w-full;
}
}
}

View File

@@ -0,0 +1,50 @@
import React, { useEffect, useRef } from 'react'
import { ButtonCommon, Inputcommon } from 'src/components/common'
import s from '../FormAuthen.module.scss'
import styles from './FormRegister.module.scss'
import SocialAuthen from '../SocialAuthen/SocialAuthen'
import classNames from 'classnames'
import { CustomInputCommon } from 'src/utils/type.utils'
interface Props {
isHide: boolean,
onSwitch: () => void
}
const FormRegister = ({ onSwitch, isHide }: Props) => {
const emailRef = useRef<CustomInputCommon>(null)
useEffect(() => {
if (!isHide) {
emailRef.current?.focus()
}
}, [isHide])
return (
<section className={classNames({
[s.formAuthen]: true,
[styles.formRegister]: true,
// [styles.hide]: isHide
})}>
<div className={s.inner}>
<div className={s.body}>
<Inputcommon placeholder='Email Address' type='email' ref={emailRef}/>
<Inputcommon placeholder='Password' type='password' />
<div className={styles.passwordNote}>
Must contain 8 characters with at least 1 uppercase and 1 lowercase letter and either 1 number or 1 special character.
</div>
</div>
<div className={styles.bottom}>
<ButtonCommon size='large'>Create Account</ButtonCommon>
</div>
<SocialAuthen />
<div className={s.others}>
<span>Already an account?</span>
<button onClick={onSwitch}>Sign In</button>
</div>
</div>
</section>
)
}
export default FormRegister

View File

@@ -0,0 +1,36 @@
@import "../../../../../styles/utilities";
.socialAuthen {
.captionText {
@apply relative text-center;
margin-bottom: 4rem;
span {
@apply relative bg-white uppercase text-label caption;
padding: 0 0.8rem;
z-index: 10;
}
&::after {
@apply absolute bg-line;
content: "";
width: 100%;
height: 1px;
top: 50%;
left: 0;
transform: translateY(-50%);
}
}
.btns {
@apply grid grid-cols-3;
grid-gap: 1.6rem;
.buttonWithIcon {
@apply flex items-center;
.label {
@apply hidden;
@screen md {
@apply inline-block;
margin-left: 0.8rem;
}
}
}
}
}

View File

@@ -0,0 +1,37 @@
import React from 'react'
import ButtonCommon from 'src/components/common/ButtonCommon/ButtonCommon'
import { IconApple, IconFacebookColor, IconGoogleColor } from 'src/components/icons'
import s from './SocialAuthen.module.scss'
const SocialAuthen = () => {
return (
<section className={s.socialAuthen}>
<div className={s.captionText}>
<span>
OR CONTINUE WITH
</span>
</div>
<div className={s.btns}>
<ButtonCommon type='light' size='large'>
<span className={s.buttonWithIcon}>
<IconFacebookColor /><span className={s.label}>Facebook</span>
</span>
</ButtonCommon>
<ButtonCommon type='light' size='large'>
<span className={s.buttonWithIcon}>
<IconApple />
<span className={s.label}>Apple</span>
</span>
</ButtonCommon>
<ButtonCommon type='light' size='large'>
<span className={s.buttonWithIcon}>
<IconGoogleColor />
<span className={s.label}>Google</span>
</span>
</ButtonCommon>
</div>
</section>
)
}
export default SocialAuthen

View File

@@ -1,3 +1,5 @@
@import '../../../styles/utilities';
.background{
@apply fixed inset-0 overflow-y-auto;
background: rgba(20, 20, 20, 0.65);
@@ -10,10 +12,12 @@
padding: 3.2rem;
box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.24);
border-radius: 1.2rem;
.top {
margin-bottom: 4rem;
}
.title{
@apply font-heading heading-3;
padding: 0 0.8rem 0 0.8rem;
font-size: 3.2rem;
line-height: 4rem;
}
.close{
@apply absolute;