diff --git a/next-env.d.ts b/next-env.d.ts index c6643fda1..9bc3dd46b 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,3 +1,6 @@ /// /// /// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/package.json b/package.json index 85d71c286..bb78b5cf1 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,6 @@ "eslint-config-next": "^11.1.2", "immutability-helper": "^3.1.1", "js-cookie": "^2.2.1", - "keen-slider": "^5.5.1", "lodash.debounce": "^4.0.8", "lodash.random": "^3.2.0", "lodash.throttle": "^4.1.1", @@ -44,6 +43,7 @@ "react-dom": "^17.0.2", "react-fast-marquee": "^1.1.4", "react-merge-refs": "^1.1.0", + "react-multi-carousel": "^2.6.5", "react-player": "^2.9.0", "react-use-measure": "^2.0.4", "sass": "^1.38.0", diff --git a/pages/_app.tsx b/pages/_app.tsx index ff8845072..d0d633a58 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -2,7 +2,6 @@ import { ThemeProvider } from 'next-themes'; import type { AppProps } from 'next/app'; import React, { FC, useEffect } from 'react'; import { CustomShapeSvg, Head } from 'src/components/common'; -import 'keen-slider/keen-slider.min.css'; import '../src/styles/main.scss'; diff --git a/pages/index.tsx b/pages/index.tsx index 59dade55f..90b759ea3 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,5 +1,5 @@ import { Layout } from 'src/components/common'; -import { HomeBanner, HomeCategories, HomeCollection, HomeCTA, HomeFeature, HomeRecipe, HomeSubscribe, HomeVideo } from 'src/components/modules/home'; +import { FeaturedProductsCarousel, HomeBanner, HomeCategories, HomeCollection, HomeCTA, HomeFeature, HomeRecipe, HomeSubscribe, HomeVideo } from 'src/components/modules/home'; import HomeSpice from 'src/components/modules/home/HomeSpice/HomeSpice'; export default function Home() { @@ -10,6 +10,7 @@ export default function Home() { + diff --git a/pages/test.tsx b/pages/test.tsx index 769a94e53..9d0cb6129 100644 --- a/pages/test.tsx +++ b/pages/test.tsx @@ -2,6 +2,7 @@ import { FeaturedProductCard, Layout } from 'src/components/common'; +import { HomeBanner } from 'src/components/modules/home'; // import { RecipeListPage } from 'src/components/modules/recipes'; import { OPTION_ALL, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'; import { PRODUCT_DATA_TEST, PRODUCT_DATA_TEST_PAGE } from 'src/utils/demo-data'; @@ -44,8 +45,7 @@ const BRAND = [ { name: 'Chinsu', link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=frozen`, - } -]; + }] const FEATURED = [ @@ -79,6 +79,7 @@ export default function Test() { subTitle="50 first orders within a day" price={data.price} originPrice="$20.00" /> + ) } diff --git a/src/components/common/Banner/Banner.module.scss b/src/components/common/Banner/Banner.module.scss index e69de29bb..22b99a787 100644 --- a/src/components/common/Banner/Banner.module.scss +++ b/src/components/common/Banner/Banner.module.scss @@ -0,0 +1,3 @@ +.bannerWrap { + @apply w-full; +} \ No newline at end of file diff --git a/src/components/common/Banner/Banner.tsx b/src/components/common/Banner/Banner.tsx index 3b0c0fe85..dc95c2146 100644 --- a/src/components/common/Banner/Banner.tsx +++ b/src/components/common/Banner/Banner.tsx @@ -1,16 +1,21 @@ -import { TOptionsEvents } from 'keen-slider' import React, { memo } from 'react' +import { ResponsiveType } from 'react-multi-carousel' import CarouselCommon from '../CarouselCommon/CarouselCommon' import BannerItem, { BannerItemProps } from './BannerItem/BannerItem' +import s from './Banner.module.scss' interface Props { data: BannerItemProps[], } -const option: TOptionsEvents = { - slidesPerView: 1, - mode: 'free', -} +const RESPONSIVE:ResponsiveType = { + desktop: { + breakpoint: { max: 9999, min: 0 }, + items: 1, + slidesToSlide:1 + }, +}; + const Banner = memo(({ data }: Props) => { if (data.length === 1) { const item = data[0] @@ -24,13 +29,15 @@ const Banner = memo(({ data }: Props) => { /> } return ( - - data={data} - itemKey="banner" - Component={BannerItem} - option={option} - isDot={true} - /> +
+ + data={data} + itemKey="banner" + Component={BannerItem} + responsive={RESPONSIVE} + showDots={true} + /> +
) }) diff --git a/src/components/common/Banner/BannerItem/BannerItem.module.scss b/src/components/common/Banner/BannerItem/BannerItem.module.scss index 5a37e252a..1c91e0beb 100644 --- a/src/components/common/Banner/BannerItem/BannerItem.module.scss +++ b/src/components/common/Banner/BannerItem/BannerItem.module.scss @@ -1,8 +1,11 @@ @import "../../../../styles/utilities"; .bannerItem { - @apply bg-primary-light shape-common-lg overflow-hidden; + @apply bg-primary-light shape-common-lg overflow-hidden w-full; padding: 0; + @screen lg { + max-height: 42rem; + } &.large { margin-bottom: 2.8rem; background-image: url("./pattern.svg"); diff --git a/src/components/common/CarouselCommon/CarouselCommon.module.scss b/src/components/common/CarouselCommon/CarouselCommon.module.scss index 4b3c7d537..9e04ecdf8 100644 --- a/src/components/common/CarouselCommon/CarouselCommon.module.scss +++ b/src/components/common/CarouselCommon/CarouselCommon.module.scss @@ -1,59 +1,75 @@ @import '../../../styles/utilities'; .navigationWrapper { @apply relative; + overflow: hidden; min-height: theme('caroucel.arrow-height'); .isPadding { - @apply spacing-horizontal; + // @apply spacing-horizontal; + } + .showDots { + padding-bottom: 2.4rem; + } + :global { + .react-multi-carousel-dot-list { + li { + button{ + outline: none; + &:focus { + outline: none; + } + } + } + } } :global(.customArrow) { width: 64px; height: 64px; - border-radius: .8rem; - backdrop-filter: saturate(180%) blur(10px); + border-radius: 0.8rem; + backdrop-filter: saturate(180%) blur(10px); &: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 - } + @apply hidden left-0; + @screen md { + @apply flex; + } } &:global(.rightArrow) { - @apply hidden right-0; - @screen md { - @apply flex; - } + @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; - } - } + :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; + } + } } diff --git a/src/components/common/CarouselCommon/CarouselCommon.tsx b/src/components/common/CarouselCommon/CarouselCommon.tsx index 29f351ccb..0de1ddd29 100644 --- a/src/components/common/CarouselCommon/CarouselCommon.tsx +++ b/src/components/common/CarouselCommon/CarouselCommon.tsx @@ -1,85 +1,87 @@ -import { useKeenSlider } from 'keen-slider/react' -import React, { useEffect } from 'react' -import 'keen-slider/keen-slider.min.css' +import React, { useEffect, useRef } from 'react' 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 { +import Carousel from 'react-multi-carousel' +import 'react-multi-carousel/lib/styles.css' +import { + ResponsiveType, + CarouselProps, + ButtonGroupProps, +} from 'react-multi-carousel/lib/types' +export interface CarouselCommonProps + extends Omit { data: T[] Component: React.ComponentType - isArrow?: Boolean - isDot?: Boolean itemKey: String - option: TOptionsEvents - keenClassname?: string isPadding?: boolean defaultComponentProps?: object + responsive?: ResponsiveType +} +const RESPONSIVE = { + desktop: { + breakpoint: { max: 3000, min: 1024 }, + items: 3, + slidesToSlide: 3, // optional, default to 1. + }, + tablet: { + breakpoint: { max: 1024, min: 464 }, + items: 2, + slidesToSlide: 2, // optional, default to 1. + }, + mobile: { + breakpoint: { max: 464, min: 0 }, + items: 1, + slidesToSlide: 1, // optional, default to 1. + }, } const CarouselCommon = ({ - data, + data = [], Component, itemKey, - keenClassname, - isPadding = false, - isArrow = true, - isDot = false, defaultComponentProps, - option: { slideChanged,slidesPerView, ...sliderOption }, + responsive = RESPONSIVE, + showDots, + isPadding, + arrows, + ...props }: CarouselCommonProps) => { - const [currentSlide, setCurrentSlide] = React.useState(0) - const [dotArr, setDotArr] = React.useState([]) - const [sliderRef, slider] = useKeenSlider({ - ...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 carousel = useRef(null) const handleRightArrowClick = () => { - slider.next() + carousel.current?.next(carousel.current.props.slidesToSlide||1) } - const handleLeftArrowClick = () => { - slider.prev() - } - - const onDotClick = (index:number) => { - slider.moveToSlideRelative(index) + carousel.current?.previous(carousel.current.props.slidesToSlide||1) } return (
-
} + renderButtonGroupOutside + sliderClass={''} + containerClass={classNames({ + [s.showDots]: showDots, [s.isPadding]: isPadding, })} + responsive={responsive} + arrows={false} + renderDotsOutside={true} + ssr={true} + // customLeftArrow={} + // customRightArrow={} > - {data?.map((props, index) => { - const allProps = defaultComponentProps ? { ...props, ...defaultComponentProps } : props - return ( -
- -
- ) + {data.map((props, index) => { + const allProps = defaultComponentProps + ? { ...props, ...defaultComponentProps } + : props + return })} -
- {slider && isArrow && ( + + {carousel && arrows && ( <> ({ /> )} - {slider && isDot && ( + {/* {slider && isDot && (
{dotArr.map((index) => { return ( @@ -99,9 +101,16 @@ const CarouselCommon = ({ ) })}
- )} + )} */}
) } - +const CarouselButtonGroup = ({ next, previous }: ButtonGroupProps) => { + return ( + <> + + + + ) +} export default CarouselCommon diff --git a/src/components/common/CarouselCommon/CustomArrow/CustomCarouselArrow.tsx b/src/components/common/CarouselCommon/CustomArrow/CustomCarouselArrow.tsx index 853188f4a..f2a9acbc8 100644 --- a/src/components/common/CarouselCommon/CustomArrow/CustomCarouselArrow.tsx +++ b/src/components/common/CarouselCommon/CustomArrow/CustomCarouselArrow.tsx @@ -2,24 +2,30 @@ import classNames from 'classnames' import React from 'react' import ArrowLeft from 'src/components/icons/ArrowLeft' import ArrowRight from 'src/components/icons/ArrowRight' -import "./CustomCarouselArrow.module.scss" - -interface CustomCarouselArrowProps - extends React.ButtonHTMLAttributes { +import './CustomCarouselArrow.module.scss' +import { ArrowProps } from 'react-multi-carousel/lib/types' +interface CustomCarouselArrowProps extends ArrowProps { side: 'left' | 'right' - isDisabled?:Boolean + isDisabled?: Boolean } export const CustomCarouselArrow = ({ - side,isDisabled, - ...props + side, + isDisabled, + onClick, }: CustomCarouselArrowProps) => { + const handleClick = () => { + onClick && onClick() + } return ( + {side === 'left' ? : } + ) } diff --git a/src/components/common/CartDrawer/components/CartRecommendation/CartRecommendation.tsx b/src/components/common/CartDrawer/components/CartRecommendation/CartRecommendation.tsx index b275ceda5..8ae920d7a 100644 --- a/src/components/common/CartDrawer/components/CartRecommendation/CartRecommendation.tsx +++ b/src/components/common/CartDrawer/components/CartRecommendation/CartRecommendation.tsx @@ -1,29 +1,30 @@ -import { TOptionsEvents } from 'keen-slider'; import React from 'react'; +import { ResponsiveType } from 'react-multi-carousel'; import { CarouselCommon, ViewAllItem } from 'src/components/common'; import ProductCard, { ProductCardProps } from 'src/components/common/ProductCard/ProductCard'; import { ROUTE } from 'src/utils/constanst.utils'; import { PRODUCT_DATA_TEST } from 'src/utils/demo-data'; import s from './CartRecommendation.module.scss'; -const option: TOptionsEvents = { - slidesPerView: 1.5, - mode: 'free', - breakpoints: { - '(min-width: 640px)': { - slidesPerView: 1.5, - }, - '(min-width: 768px)': { - slidesPerView: 2.5, - }, - '(min-width: 1008px)': { - slidesPerView: 2.2, - }, - '(min-width: 1440px)': { - slidesPerView: 2.5, - } +const RESPONSIVE:ResponsiveType = { + desktop: { + breakpoint: { max: 99999, min: 1440 }, + items: 2.5, + slidesToSlide: 1 // optional, default to 1. }, -} + lap: { + breakpoint: { max: 1440, min: 1008 }, + items: 2.2, + }, + tablet: { + breakpoint: { max: 1008, min: 768 }, + items: 2.5, + }, + mobile: { + breakpoint: { max: 768, min: 0 }, + items: 1.5, + } +}; const CartRecommendation = () => { return ( @@ -39,7 +40,7 @@ const CartRecommendation = () => { data={PRODUCT_DATA_TEST} Component={ProductCard} itemKey="cart-recommendation" - option={option} + responsive={RESPONSIVE} defaultComponentProps={{ isSingleButton: true }} /> diff --git a/src/components/common/FeaturedProductCard/FeaturedProductCard.module.scss b/src/components/common/FeaturedProductCard/FeaturedProductCard.module.scss index 217b3944c..036e84629 100644 --- a/src/components/common/FeaturedProductCard/FeaturedProductCard.module.scss +++ b/src/components/common/FeaturedProductCard/FeaturedProductCard.module.scss @@ -1,33 +1,63 @@ @import '../../../styles/utilities'; .featuredProductCardWarpper{ - width: 59.8rem; - height: 28.8rem; + // min-width: 30.2rem; + height: 14.4rem; padding: 2.4rem; - @apply bg-primary-light inline-flex justify-start items-center shape-common; + @apply bg-primary-light inline-flex justify-start items-center shape-common ; + @screen md { + // min-width: 598px; + height: 28.8rem; + padding: 1.6rem 3.2rem 1.6rem 1.6rem; + } + @screen 2xl { + // min-width: 598px; + height: 28.8rem; + padding: 2.4rem 4rem 2.4rem 2.4rem; + } .left{ - width: 24rem; - height: 24rem; + width: 10rem; + height: 10rem; + @screen md { + width: 24rem; + height: 24rem; + } } .right{ - padding-left: 2.4rem; - min-width: 27rem; - max-width: 28.6rem; - min-height: 16.8rem; + margin-left: 1.2rem; + // min-width: 27rem; + max-width: 16.6rem; + max-height: 10rem; @apply flex justify-between flex-col; + @screen md { + margin-left: 2.4rem; + max-width: 27.0rem; + min-height: 16.8rem; + } .rightTop{ - min-height: 9.6rem; + min-height: 6rem; + @screen md { + min-height: 9.6rem; + } @apply flex justify-between flex-col; .title{ @apply font-bold; - font-size: 2rem; - line-height: 2.8rem; + font-size: 1.2rem; + line-height: 1.6rem; letter-spacing: -0.01em; color: var(--text-active); + @screen md { + font-size: 2rem; + line-height: 2.8rem; + } } .subTitle{ color: var(--text-base); - font-size: 1.6rem; - line-height: 2.4rem; + font-size: 1.2rem; + line-height: 1.6rem; + @screen md { + font-size: 1.6rem; + line-height: 2.4rem; + } } .priceWrapper{ @apply flex justify-start sm-headline; diff --git a/src/components/common/FeaturedProductCard/FeaturedProductCard.tsx b/src/components/common/FeaturedProductCard/FeaturedProductCard.tsx index 9cd84235a..a82b6857b 100644 --- a/src/components/common/FeaturedProductCard/FeaturedProductCard.tsx +++ b/src/components/common/FeaturedProductCard/FeaturedProductCard.tsx @@ -5,7 +5,7 @@ import { LANGUAGE } from '../../../utils/language.utils' import ButtonIconBuy from '../ButtonIconBuy/ButtonIconBuy' import ButtonCommon from '../ButtonCommon/ButtonCommon' import { ImgWithLink } from '..' -interface FeaturedProductCardProps extends FeaturedProductProps { +export interface FeaturedProductCardProps extends FeaturedProductProps { buttonText?: string } diff --git a/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx b/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx index 869b029cd..74ebc1187 100644 --- a/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx +++ b/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx @@ -6,7 +6,7 @@ import { ButtonCommon } from 'src/components/common' import InputSearch from 'src/components/common/InputSearch/InputSearch' import MenuDropdown from 'src/components/common/MenuDropdown/MenuDropdown' import { IconBuy, IconFilter, IconHeart, IconHistory, IconUser } from 'src/components/icons' -import { ACCOUNT_TAB, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils' +import { ACCOUNT_TAB, FILTER_PAGE, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils' import Logo from '../../../Logo/Logo' import s from './HeaderMenu.module.scss' interface Props { @@ -20,7 +20,6 @@ interface Props { toggleCart: () => void, } -const FILTER_PAGE = [ROUTE.HOME, ROUTE.PRODUCTS] const HeaderMenu = memo(({ isFull, isStickyHeader, visibleFilter, openModalAuthen, openModalInfo, toggleFilter, toggleCart }: Props) => { const router = useRouter() @@ -113,16 +112,6 @@ const HeaderMenu = memo(({ isFull, isStickyHeader, visibleFilter, openModalAuthe - - { - FILTER_PAGE.includes(router.pathname) && ( -
  • - -
  • - ) - } ) diff --git a/src/components/common/ImgWithLink/ImgWithLink.tsx b/src/components/common/ImgWithLink/ImgWithLink.tsx index 212e45583..4f61946e0 100644 --- a/src/components/common/ImgWithLink/ImgWithLink.tsx +++ b/src/components/common/ImgWithLink/ImgWithLink.tsx @@ -17,6 +17,7 @@ const ImgWithLink = ({ src, alt, blurDataURL = BLUR_DATA_IMG }: ImgWithLinkProps className={s.imgWithLink} placeholder="blur" blurDataURL={blurDataURL} + draggable='false' /> ) diff --git a/src/components/common/Layout/Layout.tsx b/src/components/common/Layout/Layout.tsx index 979e55c84..4070c8aa6 100644 --- a/src/components/common/Layout/Layout.tsx +++ b/src/components/common/Layout/Layout.tsx @@ -2,7 +2,7 @@ import { CommerceProvider } from '@framework' import { useRouter } from 'next/router' import { FC } from 'react' import { useModalCommon } from 'src/components/hooks' -import { BRAND, CATEGORY, FEATURED, ROUTE } from 'src/utils/constanst.utils' +import { BRAND, CATEGORY, FEATURED, FILTER_PAGE, ROUTE } from 'src/utils/constanst.utils' import { ScrollToTop } from '..' import Footer from '../Footer/Footer' import Header from '../Header/Header' @@ -16,7 +16,7 @@ interface Props { // note: demo code const Layout: FC = ({ children }) => { - const { locale = 'en-US' } = useRouter() + const { locale = 'en-US', pathname } = useRouter() const { visible: visibleFilter, openModal: openFilter, closeModal: closeFilter } = useModalCommon({ initialValue: false }) const router = useRouter() @@ -42,6 +42,9 @@ const Layout: FC = ({ children }) => { }
    + { + FILTER_PAGE.includes(pathname) && (
    ) + }