mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 20:26:49 +00:00
fix: fix conflic
This commit is contained in:
@@ -1,52 +0,0 @@
|
||||
@import "../../../styles/utilities";
|
||||
|
||||
.banner {
|
||||
@apply bg-primary-light custom-border-radius-lg overflow-hidden;
|
||||
@screen md {
|
||||
border: 1px solid var(--primary);
|
||||
}
|
||||
&.large {
|
||||
margin-bottom: 2.8rem;
|
||||
.inner {
|
||||
@screen xl {
|
||||
@apply bg-right-bottom;
|
||||
background-size: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
.inner {
|
||||
@apply bg-no-repeat;
|
||||
background-size: 90%;
|
||||
background-position: right -500% bottom 0%;
|
||||
.content {
|
||||
background-image: linear-gradient(
|
||||
to right,
|
||||
rgb(227, 242, 233, 0.9),
|
||||
rgb(227, 242, 233, 0.5) 80%,
|
||||
rgb(227, 242, 233, 0)
|
||||
);
|
||||
padding: 1.6rem;
|
||||
max-width: 37rem;
|
||||
@screen md {
|
||||
max-width: 49.6rem;
|
||||
padding: 4.8rem;
|
||||
}
|
||||
.top {
|
||||
.heading {
|
||||
@apply heading-1 font-heading;
|
||||
margin-bottom: 1.6rem;
|
||||
}
|
||||
.subHeading {
|
||||
@apply sub-headline;
|
||||
@screen md {
|
||||
@apply caption;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
margin-top: 4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,47 +1,24 @@
|
||||
import classNames from 'classnames'
|
||||
import Link from 'next/link'
|
||||
import React, { memo } from 'react'
|
||||
import { IconArrowRight } from 'src/components/icons'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
import { LANGUAGE } from 'src/utils/language.utils'
|
||||
import ButtonCommon from '../ButtonCommon/ButtonCommon'
|
||||
import s from './Banner.module.scss'
|
||||
import CarouselCommon from '../CarouselCommon/CarouselCommon'
|
||||
import BannerItem, { BannerItemProps } from './BannerItem/BannerItem'
|
||||
|
||||
interface Props {
|
||||
imgLink: string,
|
||||
title: string,
|
||||
subtitle: string,
|
||||
buttonLabel?: string,
|
||||
linkButton?: string,
|
||||
size?: 'small' | 'large',
|
||||
data: BannerItemProps[],
|
||||
}
|
||||
|
||||
const Banner = memo(({ imgLink, title, subtitle, buttonLabel = LANGUAGE.BUTTON_LABEL.SHOP_NOW, linkButton = ROUTE.HOME, size = 'large' }: Props) => {
|
||||
const option = {
|
||||
slidesPerView: 1,
|
||||
breakpoints: {}
|
||||
}
|
||||
const Banner = memo(({ data }: Props) => {
|
||||
return (
|
||||
<div className={classNames({
|
||||
[s.banner]: true,
|
||||
[s[size]]: true,
|
||||
})}>
|
||||
<div className={s.inner} style={{ backgroundImage: `url(${imgLink})` }}>
|
||||
<div className={s.content}>
|
||||
<div className={s.top}>
|
||||
<h1 className={s.heading}>
|
||||
{title}
|
||||
</h1>
|
||||
<div className={s.subHeading}>
|
||||
{subtitle}
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.bottom}>
|
||||
<Link href={linkButton}>
|
||||
<a>
|
||||
<ButtonCommon icon={<IconArrowRight />} isIconSuffix={true}>{buttonLabel}</ButtonCommon>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<CarouselCommon<BannerItemProps>
|
||||
data={data}
|
||||
itemKey="banner"
|
||||
Component={BannerItem}
|
||||
option={option}
|
||||
isDot = {true}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
|
@@ -0,0 +1,52 @@
|
||||
@import "../../../../styles/utilities";
|
||||
|
||||
.bannerItem {
|
||||
@apply bg-primary-light custom-border-radius-lg overflow-hidden;
|
||||
@screen md {
|
||||
border: 1px solid var(--primary);
|
||||
}
|
||||
&.large {
|
||||
margin-bottom: 2.8rem;
|
||||
.inner {
|
||||
@screen xl {
|
||||
@apply bg-right-bottom;
|
||||
background-size: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
.inner {
|
||||
@apply bg-no-repeat;
|
||||
background-size: 90%;
|
||||
background-position: right -500% bottom 0%;
|
||||
.content {
|
||||
background-image: linear-gradient(
|
||||
to right,
|
||||
rgb(227, 242, 233, 0.9),
|
||||
rgb(227, 242, 233, 0.5) 80%,
|
||||
rgb(227, 242, 233, 0)
|
||||
);
|
||||
padding: 1.6rem;
|
||||
max-width: 37rem;
|
||||
@screen md {
|
||||
max-width: 49.6rem;
|
||||
padding: 4.8rem;
|
||||
}
|
||||
.top {
|
||||
.heading {
|
||||
@apply heading-1 font-heading;
|
||||
margin-bottom: 1.6rem;
|
||||
}
|
||||
.subHeading {
|
||||
@apply sub-headline;
|
||||
@screen md {
|
||||
@apply caption;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
margin-top: 4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
48
src/components/common/Banner/BannerItem/BannerItem.tsx
Normal file
48
src/components/common/Banner/BannerItem/BannerItem.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import classNames from 'classnames'
|
||||
import Link from 'next/link'
|
||||
import React, { memo } from 'react'
|
||||
import { IconArrowRight } from 'src/components/icons'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
import { LANGUAGE } from 'src/utils/language.utils'
|
||||
import ButtonCommon from '../../ButtonCommon/ButtonCommon'
|
||||
import s from './BannerItem.module.scss'
|
||||
|
||||
export interface BannerItemProps {
|
||||
imgLink: string,
|
||||
title: string,
|
||||
subtitle: string,
|
||||
buttonLabel?: string,
|
||||
linkButton?: string,
|
||||
size?: 'small' | 'large',
|
||||
}
|
||||
|
||||
const BannerItem = memo(({ imgLink, title, subtitle, buttonLabel = LANGUAGE.BUTTON_LABEL.SHOP_NOW, linkButton = ROUTE.HOME, size = 'large' }: BannerItemProps) => {
|
||||
return (
|
||||
<div className={classNames({
|
||||
[s.bannerItem]: true,
|
||||
[s[size]]: true,
|
||||
})}>
|
||||
<div className={s.inner} style={{ backgroundImage: `url(${imgLink})` }}>
|
||||
<div className={s.content}>
|
||||
<div className={s.top}>
|
||||
<h1 className={s.heading}>
|
||||
{title}
|
||||
</h1>
|
||||
<div className={s.subHeading}>
|
||||
{subtitle}
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.bottom}>
|
||||
<Link href={linkButton}>
|
||||
<a>
|
||||
<ButtonCommon icon={<IconArrowRight />} isIconSuffix={true}>{buttonLabel}</ButtonCommon>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export default BannerItem
|
@@ -2,4 +2,10 @@
|
||||
|
||||
.breadcrumbCommon {
|
||||
color: var(--text-base);
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
.currentItem {
|
||||
cursor: default;
|
||||
}
|
||||
>>>>>>> a9f9f06eb9dee2a1ddefe907ff804237a78c5210
|
||||
}
|
||||
|
@@ -1,65 +1,36 @@
|
||||
import React from 'react'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
import s from './BreadcrumbCommon.module.scss'
|
||||
|
||||
import BreadcrumbItem from './components/BreadcrumbItem/BreadcrumbItem'
|
||||
import BreadcrumbSeparator from './components/BreadcrumbSeparator/BreadcrumbSeparator'
|
||||
|
||||
interface BreadcrumbCommonProps {
|
||||
crumbs: { link:string, name:string }[];
|
||||
crumbs: { link: string, name: string }[];
|
||||
showHomePage?: boolean;
|
||||
}
|
||||
|
||||
const BreadcrumbCommon = ({ crumbs, showHomePage=true } : BreadcrumbCommonProps) => {
|
||||
console.log(crumbs);
|
||||
const BreadcrumbCommon = ({ crumbs, showHomePage = true }: BreadcrumbCommonProps) => {
|
||||
return (
|
||||
<section className={s.breadcrumbCommon}>
|
||||
{
|
||||
showHomePage && crumbs[0].link==="/" && crumbs.map((crumb, i) => {
|
||||
if (i === 0) {
|
||||
return (
|
||||
<BreadcrumbItem key={crumb.name} text={crumb.name} href={crumb.link} />
|
||||
)
|
||||
}
|
||||
if (i === crumbs.length-1) {
|
||||
return (
|
||||
<BreadcrumbSeparator key={crumb.name}>
|
||||
<span>{crumb.name}</span>
|
||||
</BreadcrumbSeparator>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<BreadcrumbSeparator key={crumb.name}>
|
||||
<BreadcrumbItem text={crumb.name} href={crumb.link} />
|
||||
</BreadcrumbSeparator>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
!showHomePage && crumbs.map((crumb, i) => {
|
||||
if (i === 0) {
|
||||
return
|
||||
}
|
||||
if (i === 1) {
|
||||
return (
|
||||
<BreadcrumbItem key={crumb.name} text={crumb.name} href={crumb.link} />
|
||||
)
|
||||
}
|
||||
if (i === crumbs.length-1) {
|
||||
return (
|
||||
<BreadcrumbSeparator key={crumb.name}>
|
||||
<span>{crumb.name}</span>
|
||||
</BreadcrumbSeparator>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<BreadcrumbSeparator key={crumb.name}>
|
||||
<BreadcrumbItem text={crumb.name} href={crumb.link} />
|
||||
</BreadcrumbSeparator>
|
||||
)
|
||||
})
|
||||
showHomePage && <BreadcrumbItem key='Home' text='Home' href={ROUTE.HOME} />
|
||||
}
|
||||
</section>
|
||||
{
|
||||
crumbs.length > 0 && <>
|
||||
|
||||
{
|
||||
crumbs.slice(0, crumbs.length - 1).map((crumb) => (
|
||||
< BreadcrumbSeparator key={crumb.name}>
|
||||
<BreadcrumbItem text={crumb.name} href={crumb.link} />
|
||||
</BreadcrumbSeparator>
|
||||
))}
|
||||
< BreadcrumbSeparator>
|
||||
<span className={s.currentItem}>{crumbs[crumbs.length - 1].name}</span>
|
||||
</BreadcrumbSeparator>
|
||||
</>
|
||||
}
|
||||
</section >
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,5 @@
|
||||
.breadcrumbItem {
|
||||
&:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import s from './BreadcrumbItem.module.scss'
|
||||
|
||||
interface BreadcrumbItemProps {
|
||||
text: string;
|
||||
@@ -9,7 +10,7 @@ interface BreadcrumbItemProps {
|
||||
const BreadcrumbItem = ({ text, href }: BreadcrumbItemProps) => {
|
||||
return (
|
||||
<Link href={href}>
|
||||
<a>{text}</a>
|
||||
<a className={s.breadcrumbItem}>{text}</a>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
@@ -5,7 +5,13 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 1.2rem 3.2rem;
|
||||
padding: 1rem 2rem;
|
||||
@screen md {
|
||||
padding: 0.8rem 1.6rem;
|
||||
}
|
||||
@screen lg {
|
||||
padding: 0.8rem 3.2rem;
|
||||
}
|
||||
&:disabled {
|
||||
filter: brightness(0.9);
|
||||
cursor: not-allowed;
|
||||
@@ -63,7 +69,7 @@
|
||||
border: 1px solid var(--primary);
|
||||
&.loading {
|
||||
&::before {
|
||||
border-top-color: var(--primary);
|
||||
border-top-color: var(--text-active);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,9 +82,18 @@
|
||||
}
|
||||
|
||||
&.large {
|
||||
padding: 1.6rem 4.8rem;
|
||||
padding: 1rem 1.5rem;
|
||||
&.onlyIcon {
|
||||
padding: 1.6rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
@screen md {
|
||||
padding: 1.6rem 3.2rem;
|
||||
&.onlyIcon {
|
||||
padding: 1.6rem;
|
||||
}
|
||||
}
|
||||
@screen lg {
|
||||
padding: 1.6rem 4.8rem;
|
||||
}
|
||||
&.loading {
|
||||
&::before {
|
||||
@@ -97,10 +112,6 @@
|
||||
|
||||
.icon {
|
||||
margin: 0 1.6rem 0 0;
|
||||
}
|
||||
|
||||
.label,
|
||||
.icon {
|
||||
svg path {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
@@ -1,5 +0,0 @@
|
||||
.navigation_wrapper{
|
||||
@apply relative;
|
||||
min-height: theme("caroucel.arrow-height") ;
|
||||
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
@import '../../../styles/utilities';
|
||||
.navigationWrapper {
|
||||
@apply relative;
|
||||
min-height: theme('caroucel.arrow-height');
|
||||
.isPadding {
|
||||
@apply spacing-horizontal;
|
||||
}
|
||||
:global(.customArrow) {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
@apply absolute top-1/2 bg-background-arrow transform -translate-y-1/2 flex justify-center items-center transition duration-100;
|
||||
&:global(.leftArrow) {
|
||||
@apply hidden left-0;
|
||||
@screen md {
|
||||
@apply flex
|
||||
}
|
||||
}
|
||||
&:global(.rightArrow) {
|
||||
@apply hidden right-0;
|
||||
@screen md {
|
||||
@apply flex;
|
||||
}
|
||||
}
|
||||
&:global(.isDisabledArrow) {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
:global {
|
||||
.dots {
|
||||
display: flex;
|
||||
padding: 1rem 0;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.dot {
|
||||
border: none;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
background: #c5c5c5;
|
||||
border-radius: 50%;
|
||||
margin: 0 0.5rem;
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dot:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.dot.active {
|
||||
background: #000;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,25 +1,55 @@
|
||||
import { useKeenSlider } from 'keen-slider/react'
|
||||
import React from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import 'keen-slider/keen-slider.min.css'
|
||||
import { CustomCarouselArrow } from './CustomArrow/CustomCarouselArrow';
|
||||
import s from "./CaroucelCommon.module.scss"
|
||||
interface CarouselCommonProps {
|
||||
children?: React.ReactNode
|
||||
data?: any[]
|
||||
Component: React.ComponentType<any>
|
||||
isArrow?:Boolean
|
||||
itemKey:String
|
||||
import { CustomCarouselArrow } from './CustomArrow/CustomCarouselArrow'
|
||||
import s from './CarouselCommon.module.scss'
|
||||
import { TOptionsEvents } from 'keen-slider'
|
||||
import classNames from 'classnames'
|
||||
import CustomDot from './CustomDot/CustomDot'
|
||||
export interface CarouselCommonProps<T> {
|
||||
data: T[]
|
||||
Component: React.ComponentType<T>
|
||||
isArrow?: Boolean
|
||||
isDot?: Boolean
|
||||
itemKey: String
|
||||
option: TOptionsEvents
|
||||
keenClassname?: string
|
||||
isPadding?: boolean
|
||||
}
|
||||
|
||||
const CarouselCommon = ({ data, Component,itemKey }: CarouselCommonProps) => {
|
||||
const CarouselCommon = <T,>({
|
||||
data,
|
||||
Component,
|
||||
itemKey,
|
||||
keenClassname,
|
||||
isPadding = false,
|
||||
isArrow = true,
|
||||
isDot = false,
|
||||
option: { slideChanged,slidesPerView, ...sliderOption },
|
||||
}: CarouselCommonProps<T>) => {
|
||||
const [currentSlide, setCurrentSlide] = React.useState(0)
|
||||
// const [dotActive, setDotActive] = React.useState<number>(0)
|
||||
const [dotArr, setDotArr] = React.useState<number[]>([])
|
||||
const [sliderRef, slider] = useKeenSlider<HTMLDivElement>({
|
||||
slidesPerView: 1,
|
||||
initial: 0,
|
||||
...sliderOption,
|
||||
slidesPerView,
|
||||
slideChanged(s) {
|
||||
setCurrentSlide(s.details().relativeSlide)
|
||||
},
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if(isDot && slider && data){
|
||||
let array:number[]
|
||||
let number = data.length - Math.floor(slider.details().slidesPerView - 1)
|
||||
if(number<1){
|
||||
number = 1
|
||||
}
|
||||
array = [...Array(number).keys()]
|
||||
setDotArr(array)
|
||||
}
|
||||
}, [isDot,slider,data])
|
||||
|
||||
const handleRightArrowClick = () => {
|
||||
slider.next()
|
||||
}
|
||||
@@ -27,29 +57,45 @@ const CarouselCommon = ({ data, Component,itemKey }: CarouselCommonProps) => {
|
||||
const handleLeftArrowClick = () => {
|
||||
slider.prev()
|
||||
}
|
||||
|
||||
const onDotClick = (index:number) => {
|
||||
slider.moveToSlideRelative(index)
|
||||
}
|
||||
return (
|
||||
<div className={s.navigation_wrapper}>
|
||||
<div ref={sliderRef} className="keen-slider">
|
||||
{data?.map((props,index) => (
|
||||
<div className={s.navigationWrapper}>
|
||||
<div
|
||||
ref={sliderRef}
|
||||
className={classNames('keen-slider', keenClassname, {
|
||||
[s.isPadding]: isPadding,
|
||||
})}
|
||||
>
|
||||
{data?.map((props, index) => (
|
||||
<div className="keen-slider__slide" key={`${itemKey}-${index}`}>
|
||||
<Component {...props} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{slider && (
|
||||
<>
|
||||
<CustomCarouselArrow
|
||||
side="right"
|
||||
onClick={handleRightArrowClick}
|
||||
isDisabled={currentSlide === slider.details().size - 1}
|
||||
/>
|
||||
<CustomCarouselArrow
|
||||
side="left"
|
||||
onClick={handleLeftArrowClick}
|
||||
isDisabled={currentSlide === 0}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{slider && isArrow && (
|
||||
<>
|
||||
<CustomCarouselArrow
|
||||
side="right"
|
||||
onClick={handleRightArrowClick}
|
||||
/>
|
||||
<CustomCarouselArrow
|
||||
side="left"
|
||||
onClick={handleLeftArrowClick}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{slider && isDot && (
|
||||
<div className="dots">
|
||||
{dotArr.map((index) => {
|
||||
return (
|
||||
<CustomDot key={`dot-${index}`} index={index} dotActive={currentSlide} onClick={onDotClick}/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@@ -1,17 +1,2 @@
|
||||
.custom_arrow{
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
&:focus{
|
||||
outline: none;
|
||||
}
|
||||
@apply absolute top-1/2 bg-background-arrow transform -translate-y-1/2 flex justify-center items-center transition duration-100;
|
||||
&.left{
|
||||
@apply left-0;
|
||||
}
|
||||
&.right{
|
||||
@apply right-0;
|
||||
}
|
||||
&.isDisabled{
|
||||
@apply hidden ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -2,11 +2,12 @@ import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
import ArrowLeft from 'src/components/icons/ArrowLeft'
|
||||
import ArrowRight from 'src/components/icons/ArrowRight'
|
||||
import s from "./CustomCarouselArrow.module.scss"
|
||||
import "./CustomCarouselArrow.module.scss"
|
||||
|
||||
interface CustomCarouselArrowProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
side: 'left' | 'right'
|
||||
isDisabled:Boolean
|
||||
isDisabled?:Boolean
|
||||
}
|
||||
|
||||
export const CustomCarouselArrow = ({
|
||||
@@ -16,7 +17,7 @@ export const CustomCarouselArrow = ({
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
className={classNames(`${s.custom_arrow}`, { [`${s[side]}`]: side,[`${s.isDisabled}`]:isDisabled })}
|
||||
className={classNames("customArrow", { [`${side}Arrow`]: side,"isDisabledArrow":isDisabled})}
|
||||
>
|
||||
{side==='left'?(<ArrowLeft/>):(<ArrowRight/>)}
|
||||
</button>
|
||||
|
21
src/components/common/CarouselCommon/CustomDot/CustomDot.tsx
Normal file
21
src/components/common/CarouselCommon/CustomDot/CustomDot.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react'
|
||||
|
||||
interface Props {
|
||||
index: number
|
||||
dotActive:number
|
||||
onClick: (index: number) => void
|
||||
}
|
||||
|
||||
const CustomDot = ({ index, onClick, dotActive }: Props) => {
|
||||
const handleOnClick = () => {
|
||||
onClick && onClick(index)
|
||||
}
|
||||
return (
|
||||
<button
|
||||
onClick={handleOnClick}
|
||||
className={'dot' + (dotActive === index ? ' active' : '')}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default CustomDot
|
@@ -1,3 +1,5 @@
|
||||
.subtitle {
|
||||
font-size: var(--font-size);
|
||||
line-height: var(--line-height);
|
||||
margin-top: .4rem;
|
||||
}
|
@@ -2,7 +2,7 @@ import React from 'react'
|
||||
import s from './CollectionHeading.module.scss'
|
||||
import HeadingCommon from '../HeadingCommon/HeadingCommon'
|
||||
|
||||
interface CollectionHeadingProps {
|
||||
export interface CollectionHeadingProps {
|
||||
type?: 'default' | 'highlight' | 'light';
|
||||
title: string;
|
||||
subtitle: string;
|
||||
|
@@ -0,0 +1,61 @@
|
||||
@import '../../../styles/utilities';
|
||||
.featuredProductCardWarpper{
|
||||
width: 59.8rem;
|
||||
height: 28.8rem;
|
||||
padding: 2.4rem;
|
||||
@apply bg-primary-light inline-flex justify-start items-center custom-border-radius ;
|
||||
.left{
|
||||
width: 24rem;
|
||||
height: 24rem;
|
||||
}
|
||||
.right{
|
||||
padding-left: 2.4rem;
|
||||
min-width: 27rem;
|
||||
max-width: 28.6rem;
|
||||
min-height: 16.8rem;
|
||||
@apply flex justify-between flex-col;
|
||||
.rightTop{
|
||||
min-height: 9.6rem;
|
||||
@apply flex justify-between flex-col;
|
||||
.title{
|
||||
@apply font-bold;
|
||||
font-size: 2rem;
|
||||
line-height: 2.8rem;
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--text-active);
|
||||
}
|
||||
.subTitle{
|
||||
color: var(--text-base);
|
||||
font-size: 1.6rem;
|
||||
line-height: 2.4rem;
|
||||
}
|
||||
.priceWrapper{
|
||||
@apply flex justify-start;
|
||||
.price{
|
||||
@apply font-bold;
|
||||
font-size: 2rem;
|
||||
line-height: 2.8rem;
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--text-active);
|
||||
}
|
||||
.originPrice{
|
||||
margin-left: 0.8rem;
|
||||
font-size: 2rem;
|
||||
line-height: 2.8rem;
|
||||
color: var(--text-label);
|
||||
text-decoration-line: line-through;
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonWarpper{
|
||||
@apply flex;
|
||||
.icon{
|
||||
width: 5.6rem;
|
||||
}
|
||||
.button{
|
||||
margin-left: 0.8rem;
|
||||
width: 20.6rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
import React from 'react'
|
||||
import { FeaturedProductProps } from 'src/utils/types.utils'
|
||||
import s from './FeaturedProductCard.module.scss'
|
||||
import { LANGUAGE } from '../../../utils/language.utils'
|
||||
import ButtonIconBuy from '../ButtonIconBuy/ButtonIconBuy'
|
||||
import ButtonCommon from '../ButtonCommon/ButtonCommon'
|
||||
interface FeaturedProductCardProps extends FeaturedProductProps {
|
||||
buttonText?: string
|
||||
}
|
||||
|
||||
const FeaturedProductCard = ({
|
||||
imageSrc,
|
||||
title,
|
||||
subTitle,
|
||||
price,
|
||||
originPrice,
|
||||
buttonText = LANGUAGE.BUTTON_LABEL.BUY_NOW,
|
||||
}: FeaturedProductCardProps) => {
|
||||
return (
|
||||
<div className={s.featuredProductCardWarpper}>
|
||||
<div className={s.left}>
|
||||
<img src={imageSrc} alt="image" />
|
||||
</div>
|
||||
<div className={s.right}>
|
||||
<div className={s.rightTop}>
|
||||
<div className={s.title}>{title}</div>
|
||||
<div className={s.subTitle}>{subTitle}</div>
|
||||
<div className={s.priceWrapper}>
|
||||
<div className={s.price}>{price} </div>
|
||||
<div className={s.originPrice}>{originPrice} </div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.buttonWarpper}>
|
||||
<div className={s.icon}>
|
||||
<ButtonIconBuy />
|
||||
</div>
|
||||
<div className={s.button}>
|
||||
<ButtonCommon>{buttonText}</ButtonCommon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FeaturedProductCard
|
@@ -1,19 +1,20 @@
|
||||
import classNames from 'classnames'
|
||||
import React, { memo, useEffect, useState } from 'react'
|
||||
import { useModalCommon } from 'src/components/hooks'
|
||||
import { isMobile } from 'src/utils/funtion.utils'
|
||||
import ModalAuthenticate from '../ModalAuthenticate/ModalAuthenticate'
|
||||
import ModalCreateUserInfo from '../ModalCreateUserInfo/ModalCreateUserInfo'
|
||||
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: false })
|
||||
const { visible: visibleModalInfo, closeModal: closeModalInfo, openModal: openModalInfo } = useModalCommon({ initialValue: false })
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
@@ -36,11 +37,15 @@ 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}
|
||||
openModalInfo={openModalInfo} />
|
||||
<HeaderSubMenu isShow={isFullHeader} />
|
||||
</div>
|
||||
</header>
|
||||
<HeaderSubMenuMobile />
|
||||
<ModalAuthenticate visible={visibleModalAuthen} closeModal={closeModalAuthen} />
|
||||
<ModalCreateUserInfo demoVisible={visibleModalInfo} demoCloseModal={closeModalInfo}/>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
@@ -1,30 +1,40 @@
|
||||
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,
|
||||
openModalInfo: () => void,
|
||||
}
|
||||
|
||||
const HeaderMenu = memo(({ isFull }: Props) => {
|
||||
const HeaderMenu = memo(({ isFull, openModalAuthen, openModalInfo }: Props) => {
|
||||
const optionMenu = useMemo(() => [
|
||||
{
|
||||
onClick: openModalAuthen,
|
||||
name: 'Login (Demo)',
|
||||
},
|
||||
{
|
||||
onClick: openModalInfo,
|
||||
name: 'Create User Info (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 +64,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>
|
||||
|
@@ -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 {
|
||||
|
@@ -11,5 +11,7 @@
|
||||
}
|
||||
&.center {
|
||||
@apply text-center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,4 @@
|
||||
.imgWithLink {
|
||||
@apply w-full h-full;
|
||||
object-fit: cover;
|
||||
}
|
16
src/components/common/ImgWithLink/ImgWithLink.tsx
Normal file
16
src/components/common/ImgWithLink/ImgWithLink.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from 'react'
|
||||
import s from './ImgWithLink.module.scss'
|
||||
|
||||
export interface ImgWithLinkProps {
|
||||
src: string,
|
||||
alt?: string,
|
||||
}
|
||||
|
||||
const ImgWithLink = ({ src, alt }: ImgWithLinkProps) => {
|
||||
return (
|
||||
<img className={s.imgWithLink} src={src} alt={alt} />
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default ImgWithLink
|
@@ -1,51 +1,94 @@
|
||||
@import "../../../styles/utilities";
|
||||
|
||||
.inputWrap {
|
||||
@apply flex items-center relative;
|
||||
.icon {
|
||||
@apply absolute;
|
||||
content: "";
|
||||
left: 1.6rem;
|
||||
margin-right: 1.6rem;
|
||||
svg path {
|
||||
fill: currentColor;
|
||||
.inputInner {
|
||||
@apply flex items-center relative;
|
||||
.icon {
|
||||
@apply absolute flex justify-center items-center;
|
||||
content: "";
|
||||
left: 1.6rem;
|
||||
margin-right: 1.6rem;
|
||||
svg path {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
.icon + .inputCommon {
|
||||
padding-left: 4.8rem;
|
||||
}
|
||||
.inputCommon {
|
||||
@apply block w-full transition-all duration-200 rounded;
|
||||
padding: 1.2rem 1.6rem;
|
||||
border: 1px solid var(--border-line);
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: none;
|
||||
border: 1px solid var(--primary);
|
||||
@apply shadow-md;
|
||||
.icon + .inputCommon {
|
||||
padding-left: 4.8rem;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
@apply text-label;
|
||||
}
|
||||
|
||||
&.custom {
|
||||
@apply custom-border-radius;
|
||||
border: 1px solid transparent;
|
||||
background: var(--gray);
|
||||
.inputCommon {
|
||||
@apply block w-full transition-all duration-200 rounded;
|
||||
padding: 1.2rem 1.6rem;
|
||||
border: 1px solid var(--border-line);
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: none;
|
||||
border: 1px solid var(--primary);
|
||||
@apply shadow-md;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
@apply text-label;
|
||||
}
|
||||
|
||||
&.custom {
|
||||
@apply custom-border-radius;
|
||||
border: 1px solid transparent;
|
||||
background: var(--gray);
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
border: 1px solid var(--primary);
|
||||
}
|
||||
}
|
||||
&.bgTransparent {
|
||||
background: rgb(227, 242, 233, 0.3);
|
||||
color: var(--white);
|
||||
&::placeholder {
|
||||
color: var(--white);
|
||||
}
|
||||
}
|
||||
}
|
||||
&.bgTransparent {
|
||||
background: rgb(227, 242, 233, 0.3);
|
||||
color: var(--white);
|
||||
&::placeholder {
|
||||
color: var(--white);
|
||||
|
||||
&.preserve {
|
||||
@apply flex-row-reverse;
|
||||
.icon {
|
||||
left: unset;
|
||||
right: 1.6rem;
|
||||
margin-left: 1.6rem;
|
||||
margin-right: 0;
|
||||
svg path {
|
||||
fill: var(--text-label);
|
||||
}
|
||||
}
|
||||
.icon + .inputCommon {
|
||||
padding-left: 1.6rem;
|
||||
padding-right: 4.8rem;
|
||||
}
|
||||
}
|
||||
&.success {
|
||||
.icon {
|
||||
svg path {
|
||||
fill: var(--primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.error {
|
||||
.icon {
|
||||
svg path {
|
||||
fill: var(--negative);
|
||||
}
|
||||
}
|
||||
input {
|
||||
border-color: var(--negative) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.errorMessage {
|
||||
@apply caption;
|
||||
color: var(--negative);
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import classNames from 'classnames';
|
||||
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
|
||||
import React, { forwardRef, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
||||
import { IconCheck, IconError, IconPassword, IconPasswordCross } from 'src/components/icons';
|
||||
import { KEY } from 'src/utils/constanst.utils';
|
||||
import s from './InputCommon.module.scss';
|
||||
|
||||
@@ -10,18 +11,33 @@ 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,
|
||||
isIconSuffix?: boolean,
|
||||
isShowIconSuccess?: boolean,
|
||||
error?: string,
|
||||
onChange?: (value: string | number) => void,
|
||||
onEnter?: (value: string | number) => void,
|
||||
}
|
||||
|
||||
const InputCommon = forwardRef<Ref, Props>(({ value, placeholder, type, styleType = 'default', icon, backgroundTransparent = false,
|
||||
isIconSuffix, isShowIconSuccess, error,
|
||||
onChange, onEnter }: Props, ref) => {
|
||||
const inputElementRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const iconElement = useMemo(() => {
|
||||
if (error) {
|
||||
return <span className={s.icon}><IconError /> </span>
|
||||
} else if (isShowIconSuccess) {
|
||||
return <span className={s.icon}><IconCheck /> </span>
|
||||
} else if (icon) {
|
||||
return <span className={s.icon}>{icon} </span>
|
||||
}
|
||||
return <></>
|
||||
}, [icon, error, isShowIconSuccess])
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
focus: () => {
|
||||
inputElementRef.current?.focus();
|
||||
@@ -44,23 +60,33 @@ const InputCommon = forwardRef<Ref, Props>(({ value, placeholder, type, styleTyp
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={s.inputWrap}>
|
||||
<div className={classNames({
|
||||
[s.inputWrap]: true,
|
||||
})}>
|
||||
<div className={classNames({
|
||||
[s.inputInner]: true,
|
||||
[s.preserve]: isIconSuffix,
|
||||
[s.success]: isShowIconSuccess,
|
||||
[s.error]: !!error,
|
||||
})}>
|
||||
{iconElement}
|
||||
<input
|
||||
ref={inputElementRef}
|
||||
value={value}
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
className={classNames({
|
||||
[s.inputCommon]: true,
|
||||
[s[styleType]]: true,
|
||||
[s.bgTransparent]: backgroundTransparent
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
icon && <span className={s.icon}>{icon}</span>
|
||||
error && <div className={s.errorMessage}>{error}</div>
|
||||
}
|
||||
<input
|
||||
ref={inputElementRef}
|
||||
value={value}
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
className={classNames({
|
||||
[s.inputCommon]: true,
|
||||
[s[styleType]]: true,
|
||||
[s.bgTransparent]: backgroundTransparent
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
|
@@ -0,0 +1,10 @@
|
||||
.iconPassword {
|
||||
all: unset;
|
||||
cursor: pointer;
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--text-active);
|
||||
}
|
||||
}
|
40
src/components/common/InputPassword/InputPassword.tsx
Normal file
40
src/components/common/InputPassword/InputPassword.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import React, { useState } from 'react';
|
||||
import { IconPassword, IconPasswordCross } from 'src/components/icons';
|
||||
import { Inputcommon } from '..';
|
||||
import s from './InputPassword.module.scss';
|
||||
|
||||
interface Props {
|
||||
children?: React.ReactNode,
|
||||
value?: string | number,
|
||||
placeholder?: string,
|
||||
styleType?: 'default' | 'custom',
|
||||
error?: string,
|
||||
onChange?: (value: string | number) => void,
|
||||
onEnter?: (value: string | number) => void,
|
||||
}
|
||||
|
||||
const InputPassword = ({ value, placeholder, styleType = 'default', error,
|
||||
onChange, onEnter }: Props) => {
|
||||
const [isShowPassword, setIsShowPassword] = useState<boolean>(false)
|
||||
const toggleShowPassword = () => {
|
||||
setIsShowPassword(!isShowPassword)
|
||||
}
|
||||
|
||||
return (
|
||||
<Inputcommon
|
||||
value={value}
|
||||
type={isShowPassword ? 'text' : 'password'}
|
||||
styleType={styleType}
|
||||
error={error}
|
||||
placeholder={placeholder}
|
||||
icon={<button className={s.iconPassword} onClick={toggleShowPassword}>
|
||||
{isShowPassword ? <IconPassword /> : <IconPasswordCross />}
|
||||
</button>}
|
||||
isIconSuffix={true}
|
||||
onChange={onChange}
|
||||
onEnter={onEnter}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default InputPassword
|
@@ -0,0 +1,23 @@
|
||||
@import "../../../../styles/utilities";
|
||||
|
||||
.infoProducts {
|
||||
@apply flex justify-between items-center spacing-horizontal;
|
||||
|
||||
.top {
|
||||
.sub {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@screen lg {
|
||||
@apply block;
|
||||
margin-right: 4rem;
|
||||
padding: 0;
|
||||
.top {
|
||||
margin-bottom: 3.2rem;
|
||||
.sub {
|
||||
display: block;
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import { ROUTE } from 'src/utils/constanst.utils';
|
||||
import HeadingCommon from '../../HeadingCommon/HeadingCommon';
|
||||
import ViewAllItem from '../../ViewAllItem/ViewAllItem';
|
||||
import s from './InfoProducts.module.scss'
|
||||
interface Props {
|
||||
title: string,
|
||||
subtitle?: string,
|
||||
}
|
||||
|
||||
const InfoProducts = ({ title, subtitle }: Props) => {
|
||||
return (
|
||||
<div className={s.infoProducts}>
|
||||
<div className={s.top}>
|
||||
<HeadingCommon>{title}</HeadingCommon>
|
||||
<div className={s.sub}>
|
||||
{subtitle}
|
||||
</div>
|
||||
</div>
|
||||
<ViewAllItem link={ROUTE.PRODUCTS} />
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InfoProducts;
|
@@ -0,0 +1,48 @@
|
||||
@import "../../../styles/utilities";
|
||||
|
||||
.listProductWithInfo {
|
||||
background-color: var(--background);
|
||||
border-top: 1rem solid var(--gray);
|
||||
border-bottom: 1rem solid var(--gray);
|
||||
padding-top: 6rem;
|
||||
padding-bottom: 6rem;
|
||||
@screen lg {
|
||||
@apply flex spacing-horizontal-left;
|
||||
padding-top: 5.6rem;
|
||||
padding-bottom: 5.6rem;
|
||||
border: none;
|
||||
background-color: #f5f4f2;
|
||||
}
|
||||
.productsWrap {
|
||||
@apply spacing-horizontal-left;
|
||||
@screen lg {
|
||||
max-width: 75%;
|
||||
@apply custom-border-radius-lg bg-white;
|
||||
padding: 4rem .8rem;
|
||||
:global(.customArrow) {
|
||||
@screen lg {
|
||||
&:global(.leftArrow) {
|
||||
left: calc(-6.4rem + 3rem);
|
||||
}
|
||||
&:global(.rightArrow) {
|
||||
right: calc(-6.4rem + 3rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@screen xl {
|
||||
padding: 4rem 2.4rem;
|
||||
max-width: 80%;
|
||||
:global(.customArrow) {
|
||||
@screen lg {
|
||||
&:global(.leftArrow) {
|
||||
left: calc(-6.4rem + 1rem);
|
||||
}
|
||||
&:global(.rightArrow) {
|
||||
right: calc(-6.4rem + 1rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
import { TOptionsEvents } from 'keen-slider';
|
||||
import React from 'react';
|
||||
import CarouselCommon from '../CarouselCommon/CarouselCommon';
|
||||
import ProductCard, { ProductCardProps } from '../ProductCard/ProductCard';
|
||||
import InfoProducts from './InfoProducts/InfoProducts';
|
||||
import s from './ListProductWithInfo.module.scss';
|
||||
|
||||
interface Props {
|
||||
data: ProductCardProps[],
|
||||
title: string,
|
||||
subtitle?: string,
|
||||
}
|
||||
const OPTION_DEFAULT: TOptionsEvents = {
|
||||
slidesPerView: 2,
|
||||
mode: 'free',
|
||||
breakpoints: {
|
||||
'(min-width: 640px)': {
|
||||
slidesPerView: 3,
|
||||
},
|
||||
'(min-width: 768px)': {
|
||||
slidesPerView: 4,
|
||||
},
|
||||
'(min-width: 1024px)': {
|
||||
slidesPerView: 3,
|
||||
},
|
||||
'(min-width: 1280px)': {
|
||||
slidesPerView: 4.5,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const ListProductWithInfo = ({ data, title, subtitle }: Props) => {
|
||||
return (
|
||||
<div className={s.listProductWithInfo}>
|
||||
<InfoProducts
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
/>
|
||||
<div className={s.productsWrap}>
|
||||
<CarouselCommon<ProductCardProps>
|
||||
data={data}
|
||||
Component={ProductCard}
|
||||
itemKey={title}
|
||||
option={OPTION_DEFAULT}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListProductWithInfo;
|
@@ -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-gray;
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -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%);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
@@ -0,0 +1,30 @@
|
||||
@import '../../../../styles/utilities';
|
||||
|
||||
.formAuthen {
|
||||
@apply bg-white w-full u-form;
|
||||
.inner {
|
||||
@screen md {
|
||||
width: 60rem;
|
||||
margin: auto;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
.bottom {
|
||||
@apply flex justify-between items-center;
|
||||
margin: 4rem auto;
|
||||
.forgotPassword {
|
||||
@apply font-bold;
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,52 @@
|
||||
import Link from 'next/link'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { ButtonCommon, Inputcommon } from 'src/components/common'
|
||||
import InputPassword from 'src/components/common/InputPassword/InputPassword'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
import { CustomInputCommon } from 'src/utils/type.utils'
|
||||
import s from '../FormAuthen.module.scss'
|
||||
import SocialAuthen from '../SocialAuthen/SocialAuthen'
|
||||
import styles from './FormLogin.module.scss'
|
||||
|
||||
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={s.formAuthen}>
|
||||
<div className={s.inner}>
|
||||
<div className={s.body}>
|
||||
<Inputcommon placeholder='Email Address' type='email' ref={emailRef} />
|
||||
{/* <Inputcommon placeholder='Email Address' type='email' ref={emailRef}
|
||||
isShowIconSuccess={true} isIconSuffix={true} /> */}
|
||||
<InputPassword placeholder='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
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
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,
|
||||
})}>
|
||||
<div className={s.inner}>
|
||||
<div className={s.body}>
|
||||
<Inputcommon placeholder='Email Address' type='email' ref={emailRef}/>
|
||||
<InputPassword placeholder='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
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
32
src/components/common/ModalCommon/ModalCommon.module.scss
Normal file
32
src/components/common/ModalCommon/ModalCommon.module.scss
Normal file
@@ -0,0 +1,32 @@
|
||||
@import '../../../styles/utilities';
|
||||
|
||||
.background{
|
||||
@apply fixed inset-0 overflow-y-auto;
|
||||
background: rgba(20, 20, 20, 0.65);
|
||||
z-index: 10000;
|
||||
.warpper{
|
||||
@apply flex justify-center items-center min-h-screen;
|
||||
.modal{
|
||||
@apply inline-block align-bottom bg-white relative;
|
||||
max-width: 66.4rem;
|
||||
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 1.6rem 0 0.8rem;
|
||||
}
|
||||
.close{
|
||||
@apply absolute;
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
}
|
||||
top:4.4rem;
|
||||
right: 4.4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
40
src/components/common/ModalCommon/ModalCommon.tsx
Normal file
40
src/components/common/ModalCommon/ModalCommon.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import React, { useRef } from 'react'
|
||||
import { Close } from 'src/components/icons'
|
||||
import { useOnClickOutside } from 'src/utils/useClickOutSide'
|
||||
import s from './ModalCommon.module.scss'
|
||||
export interface ModalCommonProps {
|
||||
onClose: () => void
|
||||
visible: boolean
|
||||
children: React.ReactNode
|
||||
title?: string
|
||||
maxWidth?:string
|
||||
}
|
||||
|
||||
const ModalCommon = ({ onClose, visible, children, title="Modal",maxWidth }: ModalCommonProps) => {
|
||||
const modalRef = useRef<HTMLDivElement>(null)
|
||||
const clickOutSide = () => {
|
||||
onClose && onClose()
|
||||
}
|
||||
useOnClickOutside(modalRef, clickOutSide)
|
||||
return (
|
||||
<>
|
||||
{visible && (
|
||||
<div className={s.background}>
|
||||
<div className={s.warpper}>
|
||||
<div className={s.modal} ref={modalRef} style={{maxWidth}}>
|
||||
<div className={s.top}>
|
||||
<div className={s.title}>{title}</div>
|
||||
<div className={s.close} onClick={clickOutSide}>
|
||||
<Close />
|
||||
</div>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModalCommon
|
@@ -0,0 +1,4 @@
|
||||
.footer{
|
||||
margin-top: 4rem;
|
||||
@apply flex justify-end items-center;
|
||||
}
|
34
src/components/common/ModalConfirm/ModalConfirm.tsx
Normal file
34
src/components/common/ModalConfirm/ModalConfirm.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React from 'react'
|
||||
import ButtonCommon from '../ButtonCommon/ButtonCommon'
|
||||
import ModalCommon, { ModalCommonProps } from '../ModalCommon/ModalCommon'
|
||||
import s from './ModalConfirm.module.scss'
|
||||
interface ModalConfirmProps extends ModalCommonProps {
|
||||
okText?: String
|
||||
cancelText?: String
|
||||
onOk?: () => void
|
||||
onCancel?: () => void
|
||||
}
|
||||
|
||||
const ModalConfirm = ({
|
||||
okText = 'Ok',
|
||||
cancelText = 'cancel',
|
||||
onOk,
|
||||
onCancel,
|
||||
children,
|
||||
title = 'Confirm',
|
||||
...props
|
||||
}: ModalConfirmProps) => {
|
||||
return (
|
||||
<ModalCommon {...props} title={title}>
|
||||
{children}
|
||||
<div className={s.footer}>
|
||||
<div className="mr-4">
|
||||
<ButtonCommon onClick={onCancel} type="light"> {cancelText}</ButtonCommon>
|
||||
</div>
|
||||
<ButtonCommon onClick={onOk}>{okText}</ButtonCommon>
|
||||
</div>
|
||||
</ModalCommon>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModalConfirm
|
@@ -0,0 +1,19 @@
|
||||
@import "../../../styles/utilities";
|
||||
|
||||
.formUserInfo {
|
||||
@apply u-form;
|
||||
.inner {
|
||||
@screen md {
|
||||
width: 60rem;
|
||||
margin: auto;
|
||||
}
|
||||
.bottom {
|
||||
@apply grid grid-cols-2;
|
||||
margin-top: 4rem;
|
||||
grid-gap: 1.6rem;
|
||||
> button {
|
||||
@apply w-full;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
import classNames from 'classnames';
|
||||
import Link from 'next/link';
|
||||
import React, { useRef } from 'react';
|
||||
import { useModalCommon } from 'src/components/hooks/useModalCommon';
|
||||
import { CustomInputCommon } from 'src/utils/type.utils';
|
||||
import { Inputcommon } from '..';
|
||||
import ButtonCommon from '../ButtonCommon/ButtonCommon';
|
||||
import ModalCommon from '../ModalCommon/ModalCommon';
|
||||
import s from './ModalCreateUserInfo.module.scss';
|
||||
|
||||
// todo: remove
|
||||
interface Props {
|
||||
demoVisible: boolean,
|
||||
demoCloseModal: () => void,
|
||||
}
|
||||
|
||||
|
||||
const ModalCreateUserInfo = ({ demoVisible: visible, demoCloseModal: closeModal }: Props) => {
|
||||
// const { visible, closeModal } = useModalCommon({ initialValue: false})
|
||||
const firstInputRef = useRef<CustomInputCommon>(null)
|
||||
|
||||
return (
|
||||
<ModalCommon visible={visible} onClose={closeModal} title='Enter your Information'>
|
||||
<div className={s.formUserInfo}>
|
||||
<div className={s.inner}>
|
||||
<div className={s.body}>
|
||||
<Inputcommon placeholder='Street Address' ref={firstInputRef} />
|
||||
<Inputcommon placeholder='City' />
|
||||
<div className={s.line}>
|
||||
{/* todo: select, not input */}
|
||||
<Inputcommon placeholder='State' />
|
||||
<Inputcommon placeholder='Zip code' />
|
||||
</div>
|
||||
<Inputcommon placeholder='Phone (delivery contact)' />
|
||||
</div>
|
||||
<div className={s.bottom}>
|
||||
<ButtonCommon size='large' onClick={closeModal} type='light'>Skip</ButtonCommon>
|
||||
<ButtonCommon size='large'>Submit</ButtonCommon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalCommon>
|
||||
);
|
||||
}
|
||||
|
||||
export default ModalCreateUserInfo;
|
4
src/components/common/ModalInfo/ModalInfo.module.scss
Normal file
4
src/components/common/ModalInfo/ModalInfo.module.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
.footer{
|
||||
margin-top: 4rem;
|
||||
@apply flex justify-end items-center;
|
||||
}
|
27
src/components/common/ModalInfo/ModalInfo.tsx
Normal file
27
src/components/common/ModalInfo/ModalInfo.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import React from 'react'
|
||||
import ButtonCommon from '../ButtonCommon/ButtonCommon'
|
||||
import ModalCommon, { ModalCommonProps } from '../ModalCommon/ModalCommon'
|
||||
import s from './ModalInfo.module.scss'
|
||||
interface ModalInfoProps extends ModalCommonProps {
|
||||
okText?: String
|
||||
onOk?: () => void
|
||||
}
|
||||
|
||||
const ModalInfo = ({
|
||||
okText = 'Ok',
|
||||
onOk,
|
||||
children,
|
||||
title = 'Confirm',
|
||||
...props
|
||||
}: ModalInfoProps) => {
|
||||
return (
|
||||
<ModalCommon {...props} title={title}>
|
||||
{children}
|
||||
<div className={s.footer}>
|
||||
<ButtonCommon onClick={onOk}>{okText}</ButtonCommon>
|
||||
</div>
|
||||
</ModalCommon>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModalInfo
|
65
src/components/common/ProductCard/ProductCard.module.scss
Normal file
65
src/components/common/ProductCard/ProductCard.module.scss
Normal file
@@ -0,0 +1,65 @@
|
||||
.productCardWarpper{
|
||||
max-width: 20.8rem;
|
||||
min-height: 31.8rem;
|
||||
padding: 1.2rem 1.2rem 0 1.2rem;
|
||||
margin: auto;
|
||||
margin-bottom: 1px;
|
||||
@apply flex flex-col justify-between;
|
||||
&.notSell {
|
||||
@apply justify-center;
|
||||
}
|
||||
.cardTop{
|
||||
@apply relative;
|
||||
height: 13.8rem;
|
||||
width: 100%;
|
||||
.productImage{
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
@apply flex justify-center items-center;
|
||||
img{
|
||||
@apply inline;
|
||||
}
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.productLabel{
|
||||
@apply absolute left-0 bottom-0;
|
||||
}
|
||||
}
|
||||
.cardMid{
|
||||
min-height: 10.4rem;
|
||||
@apply flex flex-col justify-between;
|
||||
.cardMidTop{
|
||||
.productname{
|
||||
font-weight: bold;
|
||||
color: var(--text-active);
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.productWeight{
|
||||
font-size: 1.2rem;
|
||||
line-height: 2rem;
|
||||
letter-spacing: 0.01em;
|
||||
color: var(--text-base);
|
||||
}
|
||||
}
|
||||
.cardMidBot{
|
||||
padding-top: 0.8rem;
|
||||
@apply flex justify-between items-center border-t border-solid border-line;
|
||||
.productPrice{
|
||||
@apply font-bold;
|
||||
font-size: 2rem;
|
||||
line-height: 2.8rem;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cardBot{
|
||||
min-height: 4rem;
|
||||
@apply flex justify-between items-center;
|
||||
.cardButton{
|
||||
}
|
||||
}
|
||||
}
|
69
src/components/common/ProductCard/ProductCard.tsx
Normal file
69
src/components/common/ProductCard/ProductCard.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import { ProductProps } from 'src/utils/types.utils'
|
||||
import ButtonCommon from '../ButtonCommon/ButtonCommon'
|
||||
import ButtonIconBuy from '../ButtonIconBuy/ButtonIconBuy'
|
||||
import ItemWishList from '../ItemWishList/ItemWishList'
|
||||
import LabelCommon from '../LabelCommon/LabelCommon'
|
||||
import s from './ProductCard.module.scss'
|
||||
import ProductNotSell from './ProductNotSell/ProductNotSell'
|
||||
|
||||
export interface ProductCardProps extends ProductProps {
|
||||
buttonText?: string
|
||||
}
|
||||
|
||||
const ProductCard = ({
|
||||
category,
|
||||
name,
|
||||
weight,
|
||||
price,
|
||||
buttonText = 'Buy Now',
|
||||
imageSrc,
|
||||
isNotSell,
|
||||
}: ProductCardProps) => {
|
||||
if (isNotSell) {
|
||||
return <div className={`${s.productCardWarpper} ${s.notSell}`}>
|
||||
<ProductNotSell name={name} imageSrc={imageSrc} />
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={s.productCardWarpper}>
|
||||
<div className={s.cardTop}>
|
||||
<Link href="#">
|
||||
<div className={s.productImage}>
|
||||
<img src={imageSrc} alt="image" />
|
||||
</div>
|
||||
</Link>
|
||||
<div className={s.productLabel}>
|
||||
<LabelCommon shape="half">{category}</LabelCommon>
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.cardMid}>
|
||||
<div className={s.cardMidTop}>
|
||||
<Link href="#">
|
||||
<div className={s.productname}>{name} </div>
|
||||
</Link>
|
||||
<div className={s.productWeight}>{weight}</div>
|
||||
</div>
|
||||
<div className={s.cardMidBot}>
|
||||
<div className={s.productPrice}>{price}</div>
|
||||
<div className={s.wishList}>
|
||||
<ItemWishList />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.cardBot}>
|
||||
<div className={s.cardIcon}>
|
||||
<ButtonIconBuy />
|
||||
</div>
|
||||
<div className={s.cardButton}>
|
||||
<ButtonCommon type="light">{buttonText}</ButtonCommon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductCard
|
@@ -0,0 +1,27 @@
|
||||
@import "../../../../styles/utilities";
|
||||
|
||||
.imgWrap {
|
||||
img {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
@apply text-label cursor-default font-bold;
|
||||
}
|
||||
|
||||
.info {
|
||||
@apply flex justify-center items-center custom-border-radius bg-info-light text-center;
|
||||
padding: .8rem 1.6rem;
|
||||
margin-top: 1.6rem;
|
||||
color: var(--info);
|
||||
svg {
|
||||
@apply u-icon;
|
||||
path {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
.text {
|
||||
margin-left: 0.8rem;
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import { IconInfo } from 'src/components/icons';
|
||||
import ImgWithLink from '../../ImgWithLink/ImgWithLink';
|
||||
import s from './ProductNotSell.module.scss';
|
||||
|
||||
export interface Props {
|
||||
name: string,
|
||||
imageSrc: string,
|
||||
}
|
||||
|
||||
const ProductNotSell = ({ name, imageSrc }: Props) => {
|
||||
return (
|
||||
<>
|
||||
<div className={s.imgWrap}>
|
||||
<ImgWithLink src={imageSrc} alt={name} />
|
||||
</div>
|
||||
<div className={s.name}>{name}</div>
|
||||
<div className={s.info}>
|
||||
<IconInfo />
|
||||
<div className={s.text}>
|
||||
Not Sell
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProductNotSell;
|
@@ -0,0 +1,16 @@
|
||||
@import '../../../styles/utilities';
|
||||
.productCardWarpper {
|
||||
@apply spacing-horizontal;
|
||||
@screen xl {
|
||||
:global(.customArrow) {
|
||||
@screen lg {
|
||||
&:global(.leftArrow) {
|
||||
left: calc(-6.4rem - 2rem);
|
||||
}
|
||||
&:global(.rightArrow) {
|
||||
right: calc(-6.4rem - 2rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
44
src/components/common/ProductCarousel/ProductCarousel.tsx
Normal file
44
src/components/common/ProductCarousel/ProductCarousel.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { TOptionsEvents } from 'keen-slider'
|
||||
import React from 'react'
|
||||
import CarouselCommon, {
|
||||
CarouselCommonProps,
|
||||
} from '../CarouselCommon/CarouselCommon'
|
||||
import ProductCard, { ProductCardProps } from '../ProductCard/ProductCard'
|
||||
import s from "./ProductCarousel.module.scss"
|
||||
|
||||
interface ProductCarouselProps
|
||||
extends Omit<CarouselCommonProps<ProductCardProps>, 'Component'|"option"> {
|
||||
option?:TOptionsEvents
|
||||
}
|
||||
|
||||
const OPTION_DEFAULT: TOptionsEvents = {
|
||||
slidesPerView: 2,
|
||||
mode: 'free',
|
||||
breakpoints: {
|
||||
'(min-width: 640px)': {
|
||||
slidesPerView: 3,
|
||||
},
|
||||
'(min-width: 768px)': {
|
||||
slidesPerView: 4,
|
||||
},
|
||||
'(min-width: 1024px)': {
|
||||
slidesPerView: 4.5,
|
||||
},'(min-width: 1280px)': {
|
||||
slidesPerView: 5.5,
|
||||
},
|
||||
},
|
||||
}
|
||||
const ProductCarousel = ({ option, data, ...props }: ProductCarouselProps) => {
|
||||
return (
|
||||
<div className={s.productCardWarpper}>
|
||||
<CarouselCommon<ProductCardProps>
|
||||
data={data}
|
||||
Component={ProductCard}
|
||||
{...props}
|
||||
option={{ ...OPTION_DEFAULT, ...option }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProductCarousel
|
34
src/components/common/RecipeCard/RecipeCard.module.scss
Normal file
34
src/components/common/RecipeCard/RecipeCard.module.scss
Normal file
@@ -0,0 +1,34 @@
|
||||
.recipeCardWarpper{
|
||||
max-width: 39.2rem;
|
||||
min-height: 34rem;
|
||||
@apply inline-flex flex-col justify-start;
|
||||
.image{
|
||||
width: 100%;
|
||||
max-height: 22rem;
|
||||
border-radius: 2.4rem;
|
||||
img {
|
||||
border-radius: 2.4rem;
|
||||
}
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.title{
|
||||
padding: 1.6rem 0.8rem 0.4rem 0.8rem;
|
||||
@apply font-bold;
|
||||
font-size: 2rem;
|
||||
line-height: 2.8rem;
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--text-active);
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.description{
|
||||
padding: 0 0.8rem;
|
||||
@apply overflow-hidden overflow-ellipsis ;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3; /* number of lines to show */
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
}
|
23
src/components/common/RecipeCard/RecipeCard.tsx
Normal file
23
src/components/common/RecipeCard/RecipeCard.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import { RecipeProps } from 'src/utils/types.utils'
|
||||
import s from './RecipeCard.module.scss'
|
||||
export interface RecipeCardProps extends RecipeProps {}
|
||||
|
||||
const RecipeCard = ({ imageSrc, title, description }: RecipeCardProps) => {
|
||||
return (
|
||||
<div className={s.recipeCardWarpper}>
|
||||
<Link href="#">
|
||||
<div className={s.image}>
|
||||
<img src={imageSrc} alt="image recipe" />
|
||||
</div>
|
||||
</Link>
|
||||
<Link href="#">
|
||||
<div className={s.title}>{title}</div>
|
||||
</Link>
|
||||
<div className={s.description}>{description}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RecipeCard
|
@@ -0,0 +1,16 @@
|
||||
@import '../../../styles/utilities';
|
||||
.recipeCardWarpper {
|
||||
@apply spacing-horizontal;
|
||||
@screen xl {
|
||||
:global(.customArrow) {
|
||||
@screen lg {
|
||||
&:global(.leftArrow) {
|
||||
left: calc(-6.4rem - 2rem);
|
||||
}
|
||||
&:global(.rightArrow) {
|
||||
right: calc(-6.4rem - 2rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
46
src/components/common/RecipeCarousel/RecipeCarousel.tsx
Normal file
46
src/components/common/RecipeCarousel/RecipeCarousel.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { TOptionsEvents } from 'keen-slider'
|
||||
import React from 'react'
|
||||
import CarouselCommon, {
|
||||
CarouselCommonProps,
|
||||
} from '../CarouselCommon/CarouselCommon'
|
||||
import RecipeCard, { RecipeCardProps } from '../RecipeCard/RecipeCard'
|
||||
import s from "./RecipeCarousel.module.scss"
|
||||
|
||||
interface RecipeCarouselProps
|
||||
extends Omit<CarouselCommonProps<RecipeCardProps>, 'Component'|"option"> {
|
||||
option?:TOptionsEvents
|
||||
}
|
||||
|
||||
const OPTION_DEFAULT: TOptionsEvents = {
|
||||
slidesPerView: 1.25,
|
||||
mode: 'free',
|
||||
spacing:24,
|
||||
breakpoints: {
|
||||
'(min-width: 640px)': {
|
||||
slidesPerView: 2,
|
||||
},
|
||||
'(min-width: 1024px)': {
|
||||
slidesPerView: 2.5,
|
||||
},
|
||||
'(min-width: 1440px)': {
|
||||
slidesPerView: 3,
|
||||
},
|
||||
'(min-width: 1536px)': {
|
||||
slidesPerView: 3.5,
|
||||
},
|
||||
},
|
||||
}
|
||||
const RecipeCarousel = ({ option, data, ...props }: RecipeCarouselProps) => {
|
||||
return (
|
||||
<div className={s.recipeCardWarpper}>
|
||||
<CarouselCommon<RecipeCardProps>
|
||||
data={data}
|
||||
Component={RecipeCard}
|
||||
{...props}
|
||||
option={{ ...OPTION_DEFAULT, ...option }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RecipeCarousel
|
23
src/components/common/RecipeDetail/RecipeDetail.tsx
Normal file
23
src/components/common/RecipeDetail/RecipeDetail.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from 'react'
|
||||
import { ProductCardProps } from '../ProductCard/ProductCard'
|
||||
import RecipeDetailInfo from './components/RecipeDetailInfo/RecipeDetailInfo'
|
||||
import RecipeIngredient from './components/RecipeIngredient/RecipeIngredient'
|
||||
import s from './RecipeDetail.module.scss'
|
||||
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
children?: any,
|
||||
ingredients: ProductCardProps[],
|
||||
}
|
||||
|
||||
const RecipeDetail = ({ ingredients }: Props) => {
|
||||
return (
|
||||
<section className={s.recipeDetail}>
|
||||
<RecipeDetailInfo />
|
||||
<RecipeIngredient data={ingredients} />
|
||||
</section >
|
||||
)
|
||||
}
|
||||
|
||||
export default RecipeDetail
|
@@ -0,0 +1,19 @@
|
||||
.recipeBriefInfo {
|
||||
@apply flex;
|
||||
.item {
|
||||
@apply flex;
|
||||
&:not(:last-child) {
|
||||
margin-right: 2.4rem;
|
||||
}
|
||||
svg {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
path {
|
||||
fill: var(--text-label);
|
||||
}
|
||||
}
|
||||
.content {
|
||||
margin-left: 0.8rem;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
import React from 'react'
|
||||
import { IconLocation, IconPeople, IconTime } from 'src/components/icons'
|
||||
import s from './RecipeBriefInfo.module.scss'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
children?: any,
|
||||
}
|
||||
|
||||
const RecipeBriefInfo = ({ }: Props) => {
|
||||
return (
|
||||
<section className={s.recipeBriefInfo}>
|
||||
<div className={s.item}>
|
||||
<IconTime />
|
||||
<div className={s.content}>15 minutes</div>
|
||||
</div>
|
||||
<div className={s.item}>
|
||||
<IconPeople />
|
||||
<div className={s.content}>4 People</div>
|
||||
</div>
|
||||
<div className={s.item}>
|
||||
<IconLocation />
|
||||
<div className={s.content}>15 minutes</div>
|
||||
</div>
|
||||
</section >
|
||||
)
|
||||
}
|
||||
|
||||
export default RecipeBriefInfo
|
@@ -0,0 +1,61 @@
|
||||
@import "../../../../../styles/utilities";
|
||||
|
||||
.recipeDetailInfo {
|
||||
@apply spacing-horizontal;
|
||||
margin: 5.6rem auto;
|
||||
@screen md {
|
||||
@apply flex;
|
||||
}
|
||||
.img {
|
||||
width: fit-content;
|
||||
margin-top: 0;
|
||||
|
||||
@screen sm-only {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
@screen lg {
|
||||
max-width: 60rem;
|
||||
}
|
||||
img {
|
||||
@apply w-full;
|
||||
object-fit: contain;
|
||||
max-height: 64rem;
|
||||
border-radius: 2.4rem;
|
||||
@screen md {
|
||||
max-height: 90rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.recipeInfo {
|
||||
@screen md {
|
||||
max-width: 39rem;
|
||||
margin-left: 4.8rem;
|
||||
}
|
||||
@screen lg {
|
||||
margin-left: 11.2rem;
|
||||
}
|
||||
.top {
|
||||
margin-bottom: 4.8rem;
|
||||
.name {
|
||||
@apply heading-1 font-heading;
|
||||
margin-bottom: 1.6rem;
|
||||
}
|
||||
}
|
||||
.detail {
|
||||
.item {
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 2.4rem;
|
||||
}
|
||||
.heading {
|
||||
@apply heading-3 font-heading;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
.content {
|
||||
list-style: disc;
|
||||
margin-left: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
import React from 'react'
|
||||
import RecipeBriefInfo from '../RecipeBriefInfo/RecipeBriefInfo'
|
||||
import s from './RecipeDetailInfo.module.scss'
|
||||
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
children?: any
|
||||
}
|
||||
|
||||
const RecipeDetailInfo = ({ }: Props) => {
|
||||
return (
|
||||
<section className={s.recipeDetailInfo}>
|
||||
<div className={s.img}>
|
||||
<img src="https://user-images.githubusercontent.com/76729908/131634880-8ae1437b-d3f8-421e-a546-d5a4f9a28e5f.png" alt="Recipe" />
|
||||
</div>
|
||||
<div className={s.recipeInfo}>
|
||||
<div className={s.top}>
|
||||
<h1 className={s.name}>
|
||||
Crispy Fried Calamari
|
||||
</h1>
|
||||
<RecipeBriefInfo />
|
||||
</div>
|
||||
<div className={s.detail}>
|
||||
<div className={s.item}>
|
||||
<h3 className={s.heading}>Ingredients</h3>
|
||||
<ul className={s.content}>
|
||||
<li>Canola oil for frying</li>
|
||||
<li>1 pound clean squid bodies cut in 1/4 inch rings and dried with a paper towel</li>
|
||||
<li>2 cups flour</li>
|
||||
<li>1/2 teaspoon kosher salt</li>
|
||||
<li>1/2 teaspoon garlic powder</li>
|
||||
<li>1/8 teaspoon coarse ground black pepper</li>
|
||||
<li>1 lemon cut into wedges</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className={s.item}>
|
||||
<h3 className={s.heading}>Preparation</h3>
|
||||
<ul className={s.content}>
|
||||
<li>1In a large pot or dutch oven add three inches of oil and bring to 350 degrees.</li>
|
||||
<li>Add the flour, salt, garlic powder and pepper to a large bowl and stir to combine.</li>
|
||||
<li>Toss the squid pieces in the flour then into the hot oil.</li>
|
||||
<li>Fry the squid for 1-2 minutes. You want the color to stay pale like in the pictures.</li>
|
||||
<li>Remove to a cookie sheet to drain (do not add paper towels as it will steam the calamari and make it soft.)</li>
|
||||
<li>Serve with lemon wedges.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className={s.item}>
|
||||
<h3 className={s.heading}>Link</h3>
|
||||
<a href="https://dinnerthendessert.com/crispy-fried-calamari" target="_blank" rel="noopener noreferrer">https://dinnerthendessert.com/crispy-fried-calamari</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section >
|
||||
)
|
||||
}
|
||||
|
||||
export default RecipeDetailInfo
|
@@ -0,0 +1,21 @@
|
||||
@import "../../../../../styles/utilities";
|
||||
|
||||
.recipeIngredient {
|
||||
padding: 6rem 0;
|
||||
@screen md {
|
||||
padding: 5.6rem 0;
|
||||
}
|
||||
.top {
|
||||
@apply flex justify-between items-center spacing-horizontal;
|
||||
}
|
||||
.bottom {
|
||||
@apply flex justify-center items-center spacing-horizontal;
|
||||
margin-top: 4rem;
|
||||
button {
|
||||
width: 100%;
|
||||
@screen md {
|
||||
width: 39rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
import React from 'react'
|
||||
import ButtonCommon from 'src/components/common/ButtonCommon/ButtonCommon'
|
||||
import HeadingCommon from 'src/components/common/HeadingCommon/HeadingCommon'
|
||||
import { ProductCardProps } from 'src/components/common/ProductCard/ProductCard'
|
||||
import ProductCarousel from 'src/components/common/ProductCarousel/ProductCarousel'
|
||||
import ViewAllItem from 'src/components/common/ViewAllItem/ViewAllItem'
|
||||
import { ROUTE } from 'src/utils/constanst.utils'
|
||||
import s from './RecipeIngredient.module.scss'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
children?: any,
|
||||
data: ProductCardProps[],
|
||||
}
|
||||
|
||||
const RecipeIngredient = ({ data }: Props) => {
|
||||
return (
|
||||
<section className={s.recipeIngredient}>
|
||||
<div className={s.top}>
|
||||
<HeadingCommon>Ingredients</HeadingCommon>
|
||||
<div>
|
||||
<ViewAllItem link={ROUTE.PRODUCTS} />
|
||||
</div>
|
||||
</div>
|
||||
<ProductCarousel data={data} itemKey="recipe-ingredient" />
|
||||
<div className={s.bottom}>
|
||||
<ButtonCommon type='ghost' size='large'>Buy all</ButtonCommon>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default RecipeIngredient
|
@@ -1,15 +0,0 @@
|
||||
import React, { MutableRefObject } from 'react'
|
||||
|
||||
interface ScrollTargetProps {
|
||||
refScrollUp: MutableRefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
const ScrollTarget = ({ refScrollUp } : ScrollTargetProps) => {
|
||||
|
||||
return (
|
||||
<div ref={refScrollUp}></div>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default ScrollTarget
|
@@ -5,11 +5,10 @@ import s from './ScrollToTop.module.scss'
|
||||
import ArrowUp from '../../icons/IconArrowUp'
|
||||
|
||||
interface ScrollToTopProps {
|
||||
target: MutableRefObject<HTMLDivElement>;
|
||||
visibilityHeight?: number;
|
||||
}
|
||||
|
||||
const ScrollToTop = ({ target, visibilityHeight=450 }: ScrollToTopProps) => {
|
||||
const ScrollToTop = ({ visibilityHeight=450 }: ScrollToTopProps) => {
|
||||
|
||||
const [scrollPosition, setSrollPosition] = useState(0);
|
||||
const [showScrollToTop, setShowScrollToTop] = useState("hide");
|
||||
@@ -26,7 +25,7 @@ const ScrollToTop = ({ target, visibilityHeight=450 }: ScrollToTopProps) => {
|
||||
};
|
||||
|
||||
function handleScrollUp() {
|
||||
target.current.scrollIntoView({ behavior: "smooth" });
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
function addEventScroll() {
|
||||
@@ -34,7 +33,7 @@ const ScrollToTop = ({ target, visibilityHeight=450 }: ScrollToTopProps) => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
addEventScroll()
|
||||
addEventScroll();
|
||||
});
|
||||
|
||||
return (
|
||||
|
@@ -1,32 +1,69 @@
|
||||
@import "../../../styles/utilities";
|
||||
|
||||
.select{
|
||||
@apply rounded-lg border-solid;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 1.2rem 1.6rem;
|
||||
background-color: var(--white);
|
||||
&.base{
|
||||
width: 18.6rem;
|
||||
height: 4.8rem;
|
||||
width: 20.6rem;
|
||||
.selectTrigger{
|
||||
width: 20.6rem;
|
||||
padding: 1.2rem 1.6rem;
|
||||
}
|
||||
}
|
||||
&.large{
|
||||
width: 34.25rem;
|
||||
height: 5.6rem;
|
||||
.selectTrigger{
|
||||
width: 34.25rem;
|
||||
padding: 1.6rem 1.6rem;
|
||||
}
|
||||
}
|
||||
&.default{
|
||||
@apply border;
|
||||
.selectTrigger{
|
||||
@apply border-solid border border-current;
|
||||
}
|
||||
}
|
||||
&.custom{
|
||||
.selectTrigger{
|
||||
@apply border-2;
|
||||
border-color: var(--border-line);
|
||||
color: var(--text-label);
|
||||
}
|
||||
}
|
||||
&.isActive{
|
||||
.selectOptionWrapper{
|
||||
@apply block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.selectTrigger{
|
||||
@apply outline-none flex justify-between;
|
||||
color: var(--text-active);
|
||||
border-radius: 0.8rem;
|
||||
|
||||
}
|
||||
.selectOptionWrapper{
|
||||
@apply outline-none hidden z-10 absolute;
|
||||
border-radius: 0.8rem;
|
||||
background-color: var(--white);
|
||||
padding: 0.4rem 0rem 0.4rem 0rem;
|
||||
margin-top: 0.6rem;
|
||||
&.base{
|
||||
width: 20.6rem;
|
||||
}
|
||||
&.large{
|
||||
width: 34.25rem;
|
||||
}
|
||||
&.default{
|
||||
@apply border-solid border border-current;
|
||||
}
|
||||
&.custom{
|
||||
@apply border-2;
|
||||
border-color: var(--border-line);
|
||||
color: var(--text-label);
|
||||
}
|
||||
.option{
|
||||
&:hover{
|
||||
background-color: black;
|
||||
}
|
||||
&.active{
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@@ -1,26 +1,75 @@
|
||||
import s from './SelectCommon.module.scss'
|
||||
import classNames from 'classnames'
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import { IconVectorDown } from 'src/components/icons'
|
||||
import SelectOption from './SelectOption/SelectOption'
|
||||
|
||||
interface Props {
|
||||
placeHolder? : string,
|
||||
children? : React.ReactNode,
|
||||
size?: 'base' | 'large',
|
||||
type?: 'default' | 'custom',
|
||||
option: {name: string}[],
|
||||
}
|
||||
|
||||
const SelectCommon = ({ type = 'default', size = 'base', option, placeHolder }: Props) => {
|
||||
return(
|
||||
<select className={classNames({
|
||||
[s.select] : true,
|
||||
[s[type]]: !!type,
|
||||
[s[size]]: !!size,
|
||||
})}
|
||||
>
|
||||
<option disabled selected hidden>{placeHolder}</option>
|
||||
{
|
||||
option.map(item => <option className={s.option} value={item.name}> {item.name} </option>)
|
||||
const SelectCommon = ({ type = 'default', size = 'base', option, children }: Props) => {
|
||||
const [isActive, setActive] = useState(false)
|
||||
const [selectedName, setSelectedName] = useState(children)
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const handleClick = (event: MouseEvent) => {
|
||||
const { target } = event;
|
||||
if (!ref?.current || ref?.current.contains(target as Node)) {
|
||||
return
|
||||
}
|
||||
</select>
|
||||
else{
|
||||
setActive(false)
|
||||
}
|
||||
}
|
||||
document.addEventListener('click', handleClick)
|
||||
return () => {
|
||||
document.removeEventListener('click', handleClick)
|
||||
}
|
||||
}, [ref])
|
||||
|
||||
const changeActiveStatus = () => {
|
||||
setActive(!isActive)
|
||||
}
|
||||
|
||||
const changeSelectedName = (item:string) => {
|
||||
setSelectedName(item)
|
||||
}
|
||||
return(
|
||||
<>
|
||||
<div className={classNames({
|
||||
[s.select] : true,
|
||||
[s[size]] : !!size,
|
||||
[s[type]] : !!type,
|
||||
[s.isActive] : isActive,
|
||||
})}
|
||||
onClick = { changeActiveStatus }
|
||||
ref = {ref}
|
||||
>
|
||||
<div className={classNames({
|
||||
[s.selectTrigger] : true,
|
||||
|
||||
})}
|
||||
>{selectedName}<IconVectorDown /></div>
|
||||
|
||||
<div className={classNames({
|
||||
[s.selectOptionWrapper] : true,
|
||||
[s[type]] : !!type,
|
||||
[s[size]] : !!size,
|
||||
})}
|
||||
>
|
||||
{
|
||||
option.map(item =>
|
||||
<SelectOption itemName={item.name} onClick={changeSelectedName} size={size} />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,17 @@
|
||||
@import "../../../../styles/utilities";
|
||||
|
||||
.selectOption {
|
||||
@apply outline-none;
|
||||
background-color: var(--white);
|
||||
&.base{
|
||||
width: 20.4rem;
|
||||
padding: 0.8rem 1.6rem;
|
||||
}
|
||||
&.large{
|
||||
width: 33.75rem;
|
||||
padding: 0.8rem 1.6rem;
|
||||
}
|
||||
&:hover{
|
||||
background-color: var(--gray);
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
import s from './SelectOption.module.scss'
|
||||
import classNames from 'classnames'
|
||||
|
||||
interface Props{
|
||||
onClick: (value: string) => void,
|
||||
itemName: string,
|
||||
size: 'base' | 'large',
|
||||
}
|
||||
|
||||
const SelectOption = ({onClick, itemName, size}: Props) => {
|
||||
|
||||
const changeName = () => {
|
||||
onClick(itemName)
|
||||
}
|
||||
return(
|
||||
<div className={classNames({
|
||||
[s.selectOption] : true,
|
||||
[s[size]] : !!size,
|
||||
})}
|
||||
onClick = {changeName}
|
||||
>{itemName}</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SelectOption
|
@@ -4,7 +4,7 @@
|
||||
display: flex;
|
||||
.content {
|
||||
color: var(--primary);
|
||||
margin: 0.8rem 0.8rem 0.8rem 1.6rem;
|
||||
margin: 0.8rem 0.8rem 0.8rem 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
.vector {
|
||||
|
@@ -3,18 +3,22 @@ export { default as Layout } from './Layout/Layout'
|
||||
export { default as CarouselCommon } from './CarouselCommon/CarouselCommon'
|
||||
export { default as QuanittyInput } from './QuanittyInput/QuanittyInput'
|
||||
export { default as LabelCommon } from './LabelCommon/LabelCommon'
|
||||
export { default as ProductCard } from './ProductCard/ProductCard'
|
||||
export { default as ProductCarousel } from './ProductCarousel/ProductCarousel'
|
||||
export { default as FeaturedProductCard } from './FeaturedProductCard/FeaturedProductCard'
|
||||
export { default as RecipeCard } from './RecipeCard/RecipeCard'
|
||||
export { default as Head } from './Head/Head'
|
||||
export { default as ViewAllItem} from './ViewAllItem/ViewAllItem'
|
||||
export { default as ItemWishList} from './ItemWishList/ItemWishList'
|
||||
export { default as Logo} from './Logo/Logo'
|
||||
export { default as Inputcommon} from './InputCommon/InputCommon'
|
||||
export { default as InputPassword} from './InputPassword/InputPassword'
|
||||
export { default as CheckboxCommon} from './CheckboxCommon/CheckboxCommon'
|
||||
export { default as Author} from './Author/Author'
|
||||
export { default as DateTime} from './DateTime/DateTime'
|
||||
export { default as HeadingCommon } from './HeadingCommon/HeadingCommon'
|
||||
export { default as CollectionHeading } from './CollectionHeading/CollectionHeading'
|
||||
export { default as ScrollToTop } from './ScrollToTop/ScrollToTop'
|
||||
export { default as ScrollTarget } from './ScrollToTop/ScrollTarget'
|
||||
export { default as InputSearch} from './InputSearch/InputSearch'
|
||||
export { default as ButtonIconBuy} from './ButtonIconBuy/ButtonIconBuy'
|
||||
export { default as Banner} from './Banner/Banner'
|
||||
@@ -23,5 +27,9 @@ export { default as MenuDropdown} from './MenuDropdown/MenuDropdown'
|
||||
export { default as NotiMessage} from './NotiMessage/NotiMessage'
|
||||
export { default as VideoPlayer} from './VideoPlayer/VideoPlayer'
|
||||
export { default as SelectCommon} from './SelectCommon/SelectCommon'
|
||||
export { default as MenuNavigation} from './MenuNavigation/MenuNavigation'
|
||||
export { default as MenuFilter} from './MenuFilter/MenuFilter'
|
||||
export { default as ModalCommon} from './ModalCommon/ModalCommon'
|
||||
export { default as ModalConfirm} from "./ModalConfirm/ModalConfirm"
|
||||
export { default as ModalInfo} from "./ModalInfo/ModalInfo"
|
||||
export { default as ModalCreateUserInfo} from './ModalCreateUserInfo/ModalCreateUserInfo'
|
||||
export { default as ImgWithLink} from './ImgWithLink/ImgWithLink'
|
||||
export { default as RecipeDetail} from './RecipeDetail/RecipeDetail'
|
||||
|
1
src/components/hooks/index.ts
Normal file
1
src/components/hooks/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as useModalCommon } from './useModalCommon'
|
25
src/components/hooks/useModalCommon.tsx
Normal file
25
src/components/hooks/useModalCommon.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
interface Props {
|
||||
initialValue?: boolean,
|
||||
}
|
||||
|
||||
const useModalCommon = ({ initialValue = false }: Props) => {
|
||||
const [visible, setVisible] = useState<boolean>(initialValue)
|
||||
|
||||
const openModal = (e?: any) => {
|
||||
e && e.stopPropagation()
|
||||
setVisible(true)
|
||||
}
|
||||
|
||||
const closeModal = (e?: any) => {
|
||||
e && e.stopPropagation()
|
||||
setVisible(false)
|
||||
}
|
||||
|
||||
return {
|
||||
visible, openModal, closeModal
|
||||
}
|
||||
};
|
||||
|
||||
export default useModalCommon
|
22
src/components/icons/Close.tsx
Normal file
22
src/components/icons/Close.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from 'react'
|
||||
|
||||
interface Props {}
|
||||
|
||||
const Close = (props: Props) => {
|
||||
return (
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10.4099 9.00019L16.7099 2.71019C16.8982 2.52188 17.004 2.26649 17.004 2.00019C17.004 1.73388 16.8982 1.47849 16.7099 1.29019C16.5216 1.10188 16.2662 0.996094 15.9999 0.996094C15.7336 0.996094 15.4782 1.10188 15.2899 1.29019L8.99994 7.59019L2.70994 1.29019C2.52164 1.10188 2.26624 0.996094 1.99994 0.996094C1.73364 0.996094 1.47824 1.10188 1.28994 1.29019C1.10164 1.47849 0.995847 1.73388 0.995847 2.00019C0.995847 2.26649 1.10164 2.52188 1.28994 2.71019L7.58994 9.00019L1.28994 15.2902C1.19621 15.3831 1.12182 15.4937 1.07105 15.6156C1.02028 15.7375 0.994141 15.8682 0.994141 16.0002C0.994141 16.1322 1.02028 16.2629 1.07105 16.3848C1.12182 16.5066 1.19621 16.6172 1.28994 16.7102C1.3829 16.8039 1.4935 16.8783 1.61536 16.9291C1.73722 16.9798 1.86793 17.006 1.99994 17.006C2.13195 17.006 2.26266 16.9798 2.38452 16.9291C2.50638 16.8783 2.61698 16.8039 2.70994 16.7102L8.99994 10.4102L15.2899 16.7102C15.3829 16.8039 15.4935 16.8783 15.6154 16.9291C15.7372 16.9798 15.8679 17.006 15.9999 17.006C16.132 17.006 16.2627 16.9798 16.3845 16.9291C16.5064 16.8783 16.617 16.8039 16.7099 16.7102C16.8037 16.6172 16.8781 16.5066 16.9288 16.3848C16.9796 16.2629 17.0057 16.1322 17.0057 16.0002C17.0057 15.8682 16.9796 15.7375 16.9288 15.6156C16.8781 15.4937 16.8037 15.3831 16.7099 15.2902L10.4099 9.00019Z"
|
||||
fill="#141414"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default Close
|
18
src/components/icons/IconApple.tsx
Normal file
18
src/components/icons/IconApple.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconApple = () => {
|
||||
return (
|
||||
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clipPath="url(#clip0)">
|
||||
<path d="M22.292 18.7033C21.929 19.5418 21.4994 20.3136 21.0016 21.0232C20.3231 21.9906 19.7676 22.6602 19.3395 23.032C18.6758 23.6424 17.9647 23.955 17.2032 23.9728C16.6566 23.9728 15.9973 23.8172 15.23 23.5017C14.4601 23.1876 13.7525 23.032 13.1056 23.032C12.4271 23.032 11.6994 23.1876 10.9211 23.5017C10.1415 23.8172 9.51355 23.9817 9.03342 23.9979C8.30322 24.0291 7.57539 23.7076 6.8489 23.032C6.38521 22.6276 5.80523 21.9343 5.11043 20.9521C4.36498 19.9033 3.75211 18.687 3.27198 17.3004C2.75777 15.8026 2.5 14.3523 2.5 12.9482C2.5 11.3398 2.84754 9.95259 3.54367 8.79011C4.09076 7.85636 4.81859 7.11979 5.72953 6.57906C6.64046 6.03834 7.62473 5.76279 8.68469 5.74516C9.26467 5.74516 10.0252 5.92457 10.9704 6.27715C11.9129 6.63091 12.5181 6.81032 12.7834 6.81032C12.9817 6.81032 13.654 6.60054 14.7937 6.18233C15.8714 5.79449 16.781 5.63391 17.5262 5.69716C19.5454 5.86012 21.0624 6.6561 22.0712 8.09013C20.2654 9.18432 19.3721 10.7169 19.3898 12.6829C19.4061 14.2142 19.9617 15.4886 21.0535 16.5004C21.5483 16.97 22.1009 17.333 22.7156 17.5907C22.5823 17.9774 22.4416 18.3477 22.292 18.7033ZM17.661 0.480137C17.661 1.68041 17.2225 2.8011 16.3484 3.8384C15.2937 5.07155 14.0179 5.78412 12.6343 5.67168C12.6167 5.52769 12.6065 5.37614 12.6065 5.21688C12.6065 4.06462 13.1081 2.83147 13.9989 1.82321C14.4436 1.3127 15.0092 0.888228 15.6951 0.549615C16.3796 0.216055 17.0269 0.031589 17.6358 0C17.6536 0.160458 17.661 0.320926 17.661 0.480121V0.480137Z" fill="black" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="24" height="24" fill="white" transform="translate(0.5)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconApple
|
11
src/components/icons/IconCheck.tsx
Normal file
11
src/components/icons/IconCheck.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconCheck = () => {
|
||||
return (
|
||||
<svg width="16" height="12" viewBox="0 0 16 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.7104 1.20986C14.6175 1.11613 14.5069 1.04174 14.385 0.990969C14.2632 0.940201 14.1324 0.914062 14.0004 0.914062C13.8684 0.914062 13.7377 0.940201 13.6159 0.990969C13.494 1.04174 13.3834 1.11613 13.2904 1.20986L5.84044 8.66986L2.71044 5.52986C2.61392 5.43662 2.49998 5.36331 2.37512 5.3141C2.25026 5.2649 2.11694 5.24077 1.98276 5.24309C1.84858 5.24541 1.71617 5.27414 1.59309 5.32763C1.47001 5.38113 1.35868 5.45834 1.26544 5.55486C1.1722 5.65138 1.09889 5.76532 1.04968 5.89018C1.00048 6.01503 0.976347 6.14836 0.978669 6.28254C0.98099 6.41672 1.00972 6.54913 1.06321 6.67221C1.1167 6.79529 1.19392 6.90662 1.29044 6.99986L5.13044 10.8399C5.2234 10.9336 5.334 11.008 5.45586 11.0588C5.57772 11.1095 5.70843 11.1357 5.84044 11.1357C5.97245 11.1357 6.10316 11.1095 6.22502 11.0588C6.34687 11.008 6.45748 10.9336 6.55044 10.8399L14.7104 2.67986C14.8119 2.58622 14.893 2.47257 14.9484 2.34607C15.0038 2.21957 15.0324 2.08296 15.0324 1.94486C15.0324 1.80676 15.0038 1.67015 14.9484 1.54365C14.893 1.41715 14.8119 1.3035 14.7104 1.20986Z" fill="#4EA674" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconCheck
|
11
src/components/icons/IconError.tsx
Normal file
11
src/components/icons/IconError.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconError = () => {
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 5C9.73479 5 9.48043 5.10536 9.2929 5.29289C9.10536 5.48043 9 5.73478 9 6V10C9 10.2652 9.10536 10.5196 9.2929 10.7071C9.48043 10.8946 9.73479 11 10 11C10.2652 11 10.5196 10.8946 10.7071 10.7071C10.8946 10.5196 11 10.2652 11 10V6C11 5.73478 10.8946 5.48043 10.7071 5.29289C10.5196 5.10536 10.2652 5 10 5ZM10.92 13.62C10.8981 13.5563 10.8679 13.4957 10.83 13.44L10.71 13.29C10.5694 13.1512 10.3908 13.0572 10.1968 13.0199C10.0028 12.9825 9.80212 13.0034 9.62 13.08C9.49882 13.1306 9.38721 13.2017 9.29 13.29C9.19732 13.3834 9.124 13.4943 9.07423 13.6161C9.02447 13.7379 8.99924 13.8684 9 14C9.00159 14.1307 9.02876 14.2598 9.08 14.38C9.12492 14.5041 9.19657 14.6168 9.28989 14.7101C9.38321 14.8034 9.49591 14.8751 9.62 14.92C9.73971 14.9729 9.86913 15.0002 10 15.0002C10.1309 15.0002 10.2603 14.9729 10.38 14.92C10.5041 14.8751 10.6168 14.8034 10.7101 14.7101C10.8034 14.6168 10.8751 14.5041 10.92 14.38C10.9712 14.2598 10.9984 14.1307 11 14C11.0049 13.9334 11.0049 13.8666 11 13.8C10.9828 13.7362 10.9558 13.6755 10.92 13.62ZM10 0C8.02219 0 6.08879 0.58649 4.4443 1.6853C2.79981 2.78412 1.51809 4.3459 0.761209 6.17317C0.00433284 8.00043 -0.193701 10.0111 0.192152 11.9509C0.578004 13.8907 1.53041 15.6725 2.92894 17.0711C4.32746 18.4696 6.10929 19.422 8.0491 19.8079C9.98891 20.1937 11.9996 19.9957 13.8268 19.2388C15.6541 18.4819 17.2159 17.2002 18.3147 15.5557C19.4135 13.9112 20 11.9778 20 10C20 8.68678 19.7413 7.38642 19.2388 6.17317C18.7363 4.95991 17.9997 3.85752 17.0711 2.92893C16.1425 2.00035 15.0401 1.26375 13.8268 0.761205C12.6136 0.258658 11.3132 0 10 0ZM10 18C8.41775 18 6.87104 17.5308 5.55544 16.6518C4.23985 15.7727 3.21447 14.5233 2.60897 13.0615C2.00347 11.5997 1.84504 9.99113 2.15372 8.43928C2.4624 6.88743 3.22433 5.46197 4.34315 4.34315C5.46197 3.22433 6.88743 2.4624 8.43928 2.15372C9.99113 1.84504 11.5997 2.00346 13.0615 2.60896C14.5233 3.21447 15.7727 4.23984 16.6518 5.55544C17.5308 6.87103 18 8.41775 18 10C18 12.1217 17.1572 14.1566 15.6569 15.6569C14.1566 17.1571 12.1217 18 10 18Z" fill="#D1644D" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconError
|
19
src/components/icons/IconFacebookColor.tsx
Normal file
19
src/components/icons/IconFacebookColor.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconFacebookColor = () => {
|
||||
return (
|
||||
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clipPath="url(#clip0)">
|
||||
<path d="M24.5 12C24.5 5.37258 19.1274 0 12.5 0C5.87258 0 0.5 5.37258 0.5 12C0.5 17.9895 4.8882 22.954 10.625 23.8542V15.4688H7.57812V12H10.625V9.35625C10.625 6.34875 12.4166 4.6875 15.1576 4.6875C16.4701 4.6875 17.8438 4.92188 17.8438 4.92188V7.875H16.3306C14.84 7.875 14.375 8.80008 14.375 9.75V12H17.7031L17.1711 15.4688H14.375V23.8542C20.1118 22.954 24.5 17.9895 24.5 12Z" fill="#1877F2" />
|
||||
<path d="M17.1711 15.4688L17.7031 12H14.375V9.75C14.375 8.80102 14.84 7.875 16.3306 7.875H17.8438V4.92188C17.8438 4.92188 16.4705 4.6875 15.1576 4.6875C12.4166 4.6875 10.625 6.34875 10.625 9.35625V12H7.57812V15.4688H10.625V23.8542C11.8674 24.0486 13.1326 24.0486 14.375 23.8542V15.4688H17.1711Z" fill="white" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="24" height="24" fill="white" transform="translate(0.5)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconFacebookColor
|
21
src/components/icons/IconGoogleColor.tsx
Normal file
21
src/components/icons/IconGoogleColor.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconGoogleColor = () => {
|
||||
return (
|
||||
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clipPath="url(#clip0)">
|
||||
<path d="M24.2663 12.2767C24.2663 11.461 24.2001 10.6409 24.059 9.83838H12.7402V14.4594H19.222C18.953 15.9497 18.0888 17.2681 16.8233 18.1059V21.1042H20.6903C22.9611 19.0142 24.2663 15.9277 24.2663 12.2767Z" fill="#4285F4" />
|
||||
<path d="M12.7391 24.0008C15.9756 24.0008 18.705 22.9382 20.6936 21.1039L16.8266 18.1055C15.7507 18.8375 14.3618 19.252 12.7435 19.252C9.61291 19.252 6.95849 17.1399 6.00607 14.3003H2.01562V17.3912C4.05274 21.4434 8.20192 24.0008 12.7391 24.0008Z" fill="#34A853" />
|
||||
<path d="M6.00277 14.3002C5.50011 12.8099 5.50011 11.196 6.00277 9.70569V6.61475H2.01674C0.314734 10.0055 0.314734 14.0004 2.01674 17.3912L6.00277 14.3002Z" fill="#FBBC04" />
|
||||
<path d="M12.7391 4.74966C14.4499 4.7232 16.1034 5.36697 17.3425 6.54867L20.7685 3.12262C18.5991 1.0855 15.7198 -0.034466 12.7391 0.000808666C8.20192 0.000808666 4.05274 2.55822 2.01562 6.61481L6.00166 9.70575C6.94967 6.86173 9.6085 4.74966 12.7391 4.74966Z" fill="#EA4335" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="24" height="24" fill="white" transform="translate(0.5)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconGoogleColor
|
11
src/components/icons/IconLocation.tsx
Normal file
11
src/components/icons/IconLocation.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconLocation = () => {
|
||||
return (
|
||||
<svg width="18" height="22" viewBox="0 0 18 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.9999 3.47991C13.4086 1.88861 11.2504 0.994629 8.99993 0.994629C6.74949 0.994629 4.59123 1.88861 2.99993 3.47991C1.40863 5.07121 0.514648 7.22947 0.514648 9.47991C0.514648 11.7303 1.40863 13.8886 2.99993 15.4799L8.26993 20.7599C8.36289 20.8536 8.47349 20.928 8.59535 20.9788C8.71721 21.0296 8.84792 21.0557 8.97993 21.0557C9.11194 21.0557 9.24265 21.0296 9.36451 20.9788C9.48637 20.928 9.59697 20.8536 9.68993 20.7599L14.9999 15.4299C16.5846 13.8452 17.4748 11.696 17.4748 9.45491C17.4748 7.21386 16.5846 5.06459 14.9999 3.47991ZM13.5699 13.9999L8.99993 18.5899L4.42993 13.9999C3.52707 13.0962 2.91241 11.9452 2.66362 10.6922C2.41484 9.43925 2.54312 8.14066 3.03223 6.96059C3.52135 5.78052 4.34935 4.77196 5.41156 4.06239C6.47377 3.35281 7.72251 2.97409 8.99993 2.97409C10.2773 2.97409 11.5261 3.35281 12.5883 4.06239C13.6505 4.77196 14.4785 5.78052 14.9676 6.96059C15.4567 8.14066 15.585 9.43925 15.3362 10.6922C15.0875 11.9452 14.4728 13.0962 13.5699 13.9999ZM5.99993 6.40991C5.19264 7.21968 4.73932 8.31648 4.73932 9.45991C4.73932 10.6033 5.19264 11.7001 5.99993 12.5099C6.59969 13.1107 7.36352 13.521 8.19558 13.6893C9.02764 13.8576 9.89088 13.7765 10.677 13.4561C11.4631 13.1356 12.1371 12.5902 12.6144 11.8882C13.0917 11.1861 13.3511 10.3588 13.3599 9.50991C13.3644 8.94311 13.2553 8.38117 13.0388 7.8573C12.8224 7.33343 12.5032 6.85827 12.0999 6.45991C11.7036 6.05449 11.231 5.73145 10.7094 5.50938C10.1877 5.2873 9.62727 5.17059 9.06033 5.16594C8.49339 5.16129 7.93113 5.26881 7.4059 5.4823C6.88067 5.69579 6.40285 6.01104 5.99993 6.40991ZM10.6899 11.0899C10.311 11.4747 9.81014 11.7158 9.27306 11.7722C8.73597 11.8285 8.19599 11.6966 7.74544 11.3988C7.29488 11.1011 6.96173 10.6562 6.80294 10.14C6.64415 9.62384 6.66958 9.06855 6.87489 8.56907C7.0802 8.06958 7.45263 7.65693 7.92851 7.40165C8.4044 7.14637 8.95418 7.06432 9.48387 7.16953C10.0135 7.27474 10.4902 7.56067 10.8324 7.97844C11.1746 8.39621 11.3611 8.91988 11.3599 9.45991C11.3454 10.0772 11.0864 10.6634 10.6399 11.0899H10.6899Z" fill="#8F8F8F" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconLocation
|
11
src/components/icons/IconPassword.tsx
Normal file
11
src/components/icons/IconPassword.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconPassword = () => {
|
||||
return (
|
||||
<svg width="22" height="16" viewBox="0 0 22 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.9196 7.6C18.8996 2.91 15.0996 0 10.9996 0C6.89958 0 3.09958 2.91 1.07958 7.6C1.02452 7.72617 0.996094 7.86234 0.996094 8C0.996094 8.13766 1.02452 8.27383 1.07958 8.4C3.09958 13.09 6.89958 16 10.9996 16C15.0996 16 18.8996 13.09 20.9196 8.4C20.9746 8.27383 21.0031 8.13766 21.0031 8C21.0031 7.86234 20.9746 7.72617 20.9196 7.6ZM10.9996 14C7.82958 14 4.82958 11.71 3.09958 8C4.82958 4.29 7.82958 2 10.9996 2C14.1696 2 17.1696 4.29 18.8996 8C17.1696 11.71 14.1696 14 10.9996 14ZM10.9996 4C10.2085 4 9.43509 4.2346 8.7773 4.67412C8.1195 5.11365 7.60681 5.73836 7.30406 6.46927C7.00131 7.20017 6.9221 8.00444 7.07644 8.78036C7.23078 9.55628 7.61174 10.269 8.17115 10.8284C8.73056 11.3878 9.44329 11.7688 10.2192 11.9231C10.9951 12.0775 11.7994 11.9983 12.5303 11.6955C13.2612 11.3928 13.8859 10.8801 14.3255 10.2223C14.765 9.56448 14.9996 8.79113 14.9996 8C14.9996 6.93913 14.5782 5.92172 13.828 5.17157C13.0779 4.42143 12.0604 4 10.9996 4ZM10.9996 10C10.604 10 10.2173 9.8827 9.88844 9.66294C9.55954 9.44318 9.30319 9.13082 9.15182 8.76537C9.00044 8.39991 8.96084 7.99778 9.03801 7.60982C9.11518 7.22186 9.30566 6.86549 9.58537 6.58579C9.86507 6.30608 10.2214 6.1156 10.6094 6.03843C10.9974 5.96126 11.3995 6.00087 11.7649 6.15224C12.1304 6.30362 12.4428 6.55996 12.6625 6.88886C12.8823 7.21776 12.9996 7.60444 12.9996 8C12.9996 8.53043 12.7889 9.03914 12.4138 9.41421C12.0387 9.78929 11.53 10 10.9996 10Z" fill="#8F8F8F" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconPassword
|
11
src/components/icons/IconPasswordCross.tsx
Normal file
11
src/components/icons/IconPasswordCross.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconPasswordCross = () => {
|
||||
return (
|
||||
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.94409 4.08409C9.29485 4.03032 9.64923 4.00358 10.0041 4.00409C13.1841 4.00409 16.1741 6.29409 17.9141 10.0041C17.648 10.5687 17.3474 11.1164 17.0141 11.6441C16.9083 11.8079 16.8527 11.9991 16.8541 12.1941C16.8563 12.4123 16.9299 12.6238 17.0636 12.7964C17.1972 12.9689 17.3837 13.0929 17.5944 13.1496C17.8052 13.2063 18.0287 13.1924 18.2309 13.1102C18.433 13.0279 18.6027 12.8818 18.7141 12.6941C19.18 11.962 19.5847 11.1927 19.9241 10.3941C19.9777 10.2693 20.0054 10.1349 20.0054 9.99909C20.0054 9.86327 19.9777 9.72887 19.9241 9.60409C17.9041 4.91409 14.1041 2.00409 10.0041 2.00409C9.53476 2.00173 9.06618 2.04189 8.60409 2.12409C8.47277 2.14642 8.34713 2.19439 8.23435 2.26527C8.12157 2.33615 8.02385 2.42855 7.94678 2.53719C7.8697 2.64584 7.81479 2.7686 7.78516 2.89847C7.75553 3.02834 7.75177 3.16277 7.77409 3.29409C7.79642 3.42541 7.84439 3.55105 7.91527 3.66383C7.98615 3.77662 8.07855 3.87433 8.18719 3.95141C8.29584 4.02848 8.4186 4.0834 8.54847 4.11303C8.67834 4.14266 8.81277 4.14642 8.94409 4.12409V4.08409ZM1.71409 0.294092C1.62085 0.200853 1.51016 0.126892 1.38834 0.0764319C1.26652 0.0259715 1.13595 0 1.00409 0C0.872232 0 0.741664 0.0259715 0.619842 0.0764319C0.49802 0.126892 0.38733 0.200853 0.294092 0.294092C0.105788 0.482395 0 0.73779 0 1.00409C0 1.27039 0.105788 1.52579 0.294092 1.71409L3.39409 4.80409C1.97966 6.16562 0.853948 7.79808 0.0840915 9.60409C0.0290282 9.73026 0.000606775 9.86643 0.000606775 10.0041C0.000606775 10.1417 0.0290282 10.2779 0.0840915 10.4041C2.10409 15.0941 5.90409 18.0041 10.0041 18.0041C11.8012 17.9917 13.5559 17.4566 15.0541 16.4641L18.2941 19.7141C18.3871 19.8078 18.4977 19.8822 18.6195 19.933C18.7414 19.9838 18.8721 20.0099 19.0041 20.0099C19.1361 20.0099 19.2668 19.9838 19.3887 19.933C19.5105 19.8822 19.6211 19.8078 19.7141 19.7141C19.8078 19.6211 19.8822 19.5105 19.933 19.3887C19.9838 19.2668 20.0099 19.1361 20.0099 19.0041C20.0099 18.8721 19.9838 18.7414 19.933 18.6195C19.8822 18.4977 19.8078 18.3871 19.7141 18.2941L1.71409 0.294092ZM8.07409 9.48409L10.5241 11.9341C10.3551 11.9826 10.1799 12.0061 10.0041 12.0041C9.47366 12.0041 8.96495 11.7934 8.58988 11.4183C8.21481 11.0432 8.00409 10.5345 8.00409 10.0041C8.00204 9.82828 8.02562 9.6531 8.07409 9.48409ZM10.0041 16.0041C6.82409 16.0041 3.83409 13.7141 2.10409 10.0041C2.75018 8.57784 3.66716 7.29067 4.80409 6.21409L6.57409 8.00409C6.15834 8.76289 5.99983 9.63604 6.12235 10.4925C6.24487 11.3491 6.64181 12.1428 7.25362 12.7546C7.86543 13.3664 8.65912 13.7633 9.51563 13.8858C10.3721 14.0084 11.2453 13.8498 12.0041 13.4341L13.5941 15.0041C12.5052 15.645 11.2675 15.9897 10.0041 16.0041Z" fill="#8F8F8F" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconPasswordCross
|
11
src/components/icons/IconPeople.tsx
Normal file
11
src/components/icons/IconPeople.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconPeople = () => {
|
||||
return (
|
||||
<svg width="22" height="18" viewBox="0 0 22 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.3 9.22C11.8336 8.75813 12.2616 8.18688 12.5549 7.54502C12.8482 6.90316 13 6.20571 13 5.5C13 4.17392 12.4732 2.90215 11.5355 1.96447C10.5979 1.02678 9.32608 0.5 8 0.5C6.67392 0.5 5.40215 1.02678 4.46447 1.96447C3.52678 2.90215 3 4.17392 3 5.5C2.99999 6.20571 3.1518 6.90316 3.44513 7.54502C3.73845 8.18688 4.16642 8.75813 4.7 9.22C3.30014 9.85388 2.11247 10.8775 1.27898 12.1685C0.445495 13.4596 0.00147185 14.9633 0 16.5C0 16.7652 0.105357 17.0196 0.292893 17.2071C0.48043 17.3946 0.734784 17.5 1 17.5C1.26522 17.5 1.51957 17.3946 1.70711 17.2071C1.89464 17.0196 2 16.7652 2 16.5C2 14.9087 2.63214 13.3826 3.75736 12.2574C4.88258 11.1321 6.4087 10.5 8 10.5C9.5913 10.5 11.1174 11.1321 12.2426 12.2574C13.3679 13.3826 14 14.9087 14 16.5C14 16.7652 14.1054 17.0196 14.2929 17.2071C14.4804 17.3946 14.7348 17.5 15 17.5C15.2652 17.5 15.5196 17.3946 15.7071 17.2071C15.8946 17.0196 16 16.7652 16 16.5C15.9985 14.9633 15.5545 13.4596 14.721 12.1685C13.8875 10.8775 12.6999 9.85388 11.3 9.22ZM8 8.5C7.40666 8.5 6.82664 8.32405 6.33329 7.99441C5.83994 7.66476 5.45542 7.19623 5.22836 6.64805C5.0013 6.09987 4.94189 5.49667 5.05764 4.91473C5.1734 4.33279 5.45912 3.79824 5.87868 3.37868C6.29824 2.95912 6.83279 2.6734 7.41473 2.55764C7.99667 2.44189 8.59987 2.5013 9.14805 2.72836C9.69623 2.95542 10.1648 3.33994 10.4944 3.83329C10.8241 4.32664 11 4.90666 11 5.5C11 6.29565 10.6839 7.05871 10.1213 7.62132C9.55871 8.18393 8.79565 8.5 8 8.5ZM17.74 8.82C18.38 8.09933 18.798 7.20905 18.9438 6.25634C19.0896 5.30362 18.9569 4.32907 18.5618 3.45C18.1666 2.57093 17.5258 1.8248 16.7165 1.30142C15.9071 0.77805 14.9638 0.499742 14 0.5C13.7348 0.5 13.4804 0.605357 13.2929 0.792893C13.1054 0.98043 13 1.23478 13 1.5C13 1.76522 13.1054 2.01957 13.2929 2.20711C13.4804 2.39464 13.7348 2.5 14 2.5C14.7956 2.5 15.5587 2.81607 16.1213 3.37868C16.6839 3.94129 17 4.70435 17 5.5C16.9986 6.02524 16.8593 6.5409 16.5961 6.99542C16.3328 7.44994 15.9549 7.82738 15.5 8.09C15.3517 8.17552 15.2279 8.29766 15.1404 8.44474C15.0528 8.59182 15.0045 8.7589 15 8.93C14.9958 9.09976 15.0349 9.2678 15.1137 9.41826C15.1924 9.56872 15.3081 9.69665 15.45 9.79L15.84 10.05L15.97 10.12C17.1754 10.6917 18.1923 11.596 18.901 12.7263C19.6096 13.8566 19.9805 15.1659 19.97 16.5C19.97 16.7652 20.0754 17.0196 20.2629 17.2071C20.4504 17.3946 20.7048 17.5 20.97 17.5C21.2352 17.5 21.4896 17.3946 21.6771 17.2071C21.8646 17.0196 21.97 16.7652 21.97 16.5C21.9782 14.9654 21.5938 13.4543 20.8535 12.1101C20.1131 10.7659 19.0413 9.63331 17.74 8.82Z" fill="#8F8F8F" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconPeople
|
11
src/components/icons/IconTime.tsx
Normal file
11
src/components/icons/IconTime.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
const IconTime = () => {
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 0C8.02219 0 6.08879 0.58649 4.4443 1.6853C2.79981 2.78412 1.51809 4.3459 0.761209 6.17317C0.00433284 8.00043 -0.193701 10.0111 0.192152 11.9509C0.578004 13.8907 1.53041 15.6725 2.92894 17.0711C4.32746 18.4696 6.10929 19.422 8.0491 19.8079C9.98891 20.1937 11.9996 19.9957 13.8268 19.2388C15.6541 18.4819 17.2159 17.2002 18.3147 15.5557C19.4135 13.9112 20 11.9778 20 10C20 8.68678 19.7413 7.38642 19.2388 6.17317C18.7363 4.95991 17.9997 3.85752 17.0711 2.92893C16.1425 2.00035 15.0401 1.26375 13.8268 0.761205C12.6136 0.258658 11.3132 0 10 0ZM10 18C8.41775 18 6.87104 17.5308 5.55544 16.6518C4.23985 15.7727 3.21447 14.5233 2.60897 13.0615C2.00347 11.5997 1.84504 9.99113 2.15372 8.43928C2.4624 6.88743 3.22433 5.46197 4.34315 4.34315C5.46197 3.22433 6.88743 2.4624 8.43928 2.15372C9.99113 1.84504 11.5997 2.00346 13.0615 2.60896C14.5233 3.21447 15.7727 4.23984 16.6518 5.55544C17.5308 6.87103 18 8.41775 18 10C18 12.1217 17.1572 14.1566 15.6569 15.6569C14.1566 17.1571 12.1217 18 10 18ZM10 4C9.73479 4 9.48043 4.10536 9.2929 4.29289C9.10536 4.48043 9 4.73478 9 5V9.42L6.9 10.63C6.70736 10.7392 6.5564 10.9092 6.47078 11.1134C6.38517 11.3176 6.36975 11.5444 6.42695 11.7583C6.48414 11.9722 6.61072 12.1611 6.78682 12.2953C6.96292 12.4296 7.17859 12.5015 7.4 12.5C7.57518 12.5012 7.7476 12.4564 7.9 12.37L10.5 10.87L10.59 10.78L10.75 10.65C10.7891 10.6005 10.8226 10.5468 10.85 10.49C10.8826 10.4363 10.9094 10.3793 10.93 10.32C10.9572 10.2564 10.9741 10.1889 10.98 10.12L11 10V5C11 4.73478 10.8946 4.48043 10.7071 4.29289C10.5196 4.10536 10.2652 4 10 4Z" fill="#8F8F8F" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconTime
|
21
src/components/icons/IconVectorDown.tsx
Normal file
21
src/components/icons/IconVectorDown.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
|
||||
const IconVectorDown = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="-6 -9 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M10.9999 1.16994C10.8126 0.983692 10.5591 0.87915 10.2949 0.87915C10.0308 0.87915 9.77731 0.983692 9.58995 1.16994L5.99995 4.70994L2.45995 1.16994C2.27259 0.983692 2.01913 0.87915 1.75495 0.87915C1.49076 0.87915 1.23731 0.983692 1.04995 1.16994C0.95622 1.26291 0.881826 1.37351 0.831057 1.49537C0.780288 1.61723 0.75415 1.74793 0.75415 1.87994C0.75415 2.01195 0.780288 2.14266 0.831057 2.26452C0.881826 2.38638 0.95622 2.49698 1.04995 2.58994L5.28995 6.82994C5.38291 6.92367 5.49351 6.99806 5.61537 7.04883C5.73723 7.0996 5.86794 7.12574 5.99995 7.12574C6.13196 7.12574 6.26267 7.0996 6.38453 7.04883C6.50638 6.99806 6.61699 6.92367 6.70995 6.82994L10.9999 2.58994C11.0937 2.49698 11.1681 2.38638 11.2188 2.26452C11.2696 2.14266 11.2957 2.01195 11.2957 1.87994C11.2957 1.74793 11.2696 1.61723 11.2188 1.49537C11.1681 1.37351 11.0937 1.26291 10.9999 1.16994Z"
|
||||
fill="#141414"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default IconVectorDown
|
@@ -9,6 +9,17 @@ export { default as IconHome } from './IconHome'
|
||||
export { default as IconShopping } from './IconShopping'
|
||||
export { default as IconHeart } from './IconHeart'
|
||||
export { default as IconVector } from './IconVector'
|
||||
export { default as IconHide } from './IconHide'
|
||||
export { default as ArrowLeftSmall } from './ArrowLeftSmall'
|
||||
export { default as ArrowRightSmall } from './ArrowRightSmall'
|
||||
export { default as IconVectorDown } from './IconVectorDown'
|
||||
export { default as IconFacebookColor } from './IconFacebookColor'
|
||||
export { default as IconGoogleColor } from './IconGoogleColor'
|
||||
export { default as IconApple } from './IconApple'
|
||||
export { default as ArrowLeft } from './ArrowLeft'
|
||||
export { default as ArrowRight } from './ArrowRight'
|
||||
export { default as Close } from './Close'
|
||||
export { default as IconPassword } from './IconPassword'
|
||||
export { default as IconPasswordCross } from './IconPasswordCross'
|
||||
export { default as IconError } from './IconError'
|
||||
export { default as IconCheck } from './IconCheck'
|
||||
export { default as IconTime } from './IconTime'
|
||||
export { default as IconPeople } from './IconPeople'
|
||||
export { default as IconLocation } from './IconLocation'
|
||||
|
@@ -0,0 +1,14 @@
|
||||
@import '../../../../styles/utilities';
|
||||
.colectionCarcoucelWarpper {
|
||||
padding-top: 5.6rem;
|
||||
padding-bottom: 6.5rem;
|
||||
@apply flex flex-col;
|
||||
.top {
|
||||
@apply spacing-horizontal flex w-full justify-between;
|
||||
@screen xl {
|
||||
.right {
|
||||
margin-right: 2.476rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
CollectionHeading,
|
||||
ProductCarousel,
|
||||
ViewAllItem,
|
||||
} from 'src/components/common'
|
||||
import { CollectionHeadingProps } from 'src/components/common/CollectionHeading/CollectionHeading'
|
||||
import { ProductCardProps } from 'src/components/common/ProductCard/ProductCard'
|
||||
import { QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'
|
||||
import s from './CollectionCarcousel.module.scss'
|
||||
interface ColectionCarcouselProps extends CollectionHeadingProps {
|
||||
data: ProductCardProps[]
|
||||
itemKey: string
|
||||
viewAllLink?: string,
|
||||
category:string
|
||||
}
|
||||
|
||||
const ColectionCarcousel = ({
|
||||
data,
|
||||
itemKey,
|
||||
title,
|
||||
subtitle,
|
||||
type,
|
||||
category
|
||||
}: ColectionCarcouselProps) => {
|
||||
return (
|
||||
<div className={s.colectionCarcoucelWarpper}>
|
||||
<div className={s.top}>
|
||||
<div className={s.left}>
|
||||
<CollectionHeading
|
||||
type={type}
|
||||
subtitle={subtitle}
|
||||
title={title}
|
||||
></CollectionHeading>
|
||||
</div>
|
||||
<div className={s.right}>
|
||||
<ViewAllItem link={`${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=${category}`}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={s.bot}>
|
||||
<ProductCarousel data={data} itemKey={itemKey} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ColectionCarcousel
|
@@ -5,6 +5,7 @@
|
||||
margin-bottom: 2.8rem;
|
||||
.left {
|
||||
@apply hidden;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
@screen xl {
|
||||
@apply grid;
|
||||
|
@@ -2,6 +2,7 @@ import React from 'react'
|
||||
import { Banner } from 'src/components/common'
|
||||
import s from './HomeBanner.module.scss'
|
||||
import BannerImgRight from './assets/banner_full.png'
|
||||
import BannerImgRight2 from './assets/banner_product.png'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
@@ -13,14 +14,25 @@ const HomeBanner = ({ }: Props) => {
|
||||
<div className={s.homeBanner}>
|
||||
<section className={s.left}>
|
||||
<div className={s.text}>
|
||||
Freshness<br/>guaranteed
|
||||
Freshness<br />guaranteed
|
||||
</div>
|
||||
</section >
|
||||
<Banner
|
||||
title="Save 15% on your first order"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
imgLink={BannerImgRight.src}
|
||||
size="small"
|
||||
data={
|
||||
[{
|
||||
title: "Save 15% on your first order",
|
||||
subtitle: "Last call! Shop deep deals on 100+ bulk picks while you can.",
|
||||
imgLink: BannerImgRight.src,
|
||||
size: "small",
|
||||
},
|
||||
{
|
||||
title: "Save 15% on your first order 2",
|
||||
subtitle: "Last call! Shop deep deals on 100+ bulk picks while you can.",
|
||||
imgLink: BannerImgRight2.src,
|
||||
size: "small",
|
||||
}
|
||||
]
|
||||
}
|
||||
/>
|
||||
</div >
|
||||
)
|
||||
|
BIN
src/components/modules/home/HomeBanner/assets/banner_product.png
Normal file
BIN
src/components/modules/home/HomeBanner/assets/banner_product.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 212 KiB |
145
src/components/modules/home/HomeCollection/HomeCollection.tsx
Normal file
145
src/components/modules/home/HomeCollection/HomeCollection.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
import React from 'react'
|
||||
import { CollectionCarcousel } from '..'
|
||||
import image5 from '../../../../../public/assets/images/image5.png'
|
||||
import image6 from '../../../../../public/assets/images/image6.png'
|
||||
import image7 from '../../../../../public/assets/images/image7.png'
|
||||
import image8 from '../../../../../public/assets/images/image8.png'
|
||||
interface HomeCollectionProps {}
|
||||
const dataTest = [
|
||||
{
|
||||
name: 'Tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image5.src,
|
||||
},
|
||||
{
|
||||
name: 'Cucumber',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image6.src,
|
||||
},
|
||||
{
|
||||
name: 'Carrot',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image7.src,
|
||||
},
|
||||
{
|
||||
name: 'Salad',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image8.src,
|
||||
},
|
||||
{
|
||||
name: 'Tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image5.src,
|
||||
},
|
||||
{
|
||||
name: 'Cucumber',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image6.src,
|
||||
},
|
||||
{
|
||||
name: 'Tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image5.src,
|
||||
},
|
||||
{
|
||||
name: 'Cucumber',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image6.src,
|
||||
},
|
||||
{
|
||||
name: 'Carrot',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image7.src,
|
||||
},
|
||||
{
|
||||
name: 'Salad',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image8.src,
|
||||
},
|
||||
{
|
||||
name: 'Tomato',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image5.src,
|
||||
},
|
||||
{
|
||||
name: 'Cucumber',
|
||||
weight: '250g',
|
||||
category: 'VEGGIE',
|
||||
price: 'Rp 27.500',
|
||||
imageSrc: image6.src,
|
||||
},
|
||||
]
|
||||
|
||||
const HomeCollection = (props: HomeCollectionProps) => {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<CollectionCarcousel
|
||||
type="highlight"
|
||||
data={dataTest}
|
||||
itemKey="product-1"
|
||||
title="Fresh Products Today"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={dataTest}
|
||||
itemKey="product-2"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={dataTest}
|
||||
itemKey="product-3"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={dataTest}
|
||||
itemKey="product-4"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={dataTest}
|
||||
itemKey="product-5"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
<CollectionCarcousel
|
||||
data={dataTest}
|
||||
itemKey="product-6"
|
||||
title="VEGGIE"
|
||||
subtitle="Last call! Shop deep deals on 100+ bulk picks while you can."
|
||||
category={"veggie"}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HomeCollection
|
@@ -0,0 +1,16 @@
|
||||
@import "../../../../styles/utilities";
|
||||
|
||||
.homeFeature {
|
||||
@apply spacing-horizontal;
|
||||
background-color: #ffffff;
|
||||
height: fit-content;
|
||||
margin: 3.2rem auto;
|
||||
@screen md {
|
||||
@apply grid grid-cols-3;
|
||||
grid-gap: 2.4rem;
|
||||
margin: 6.4rem auto;
|
||||
}
|
||||
@screen md {
|
||||
grid-gap: 4rem;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user