mirror of
https://github.com/vercel/commerce.git
synced 2025-07-05 12:41:21 +00:00
Merge pull request #3 from KieIO/m1-datnguyen
feat: label, quanitty input, caroucel
This commit is contained in:
commit
20fb4b3446
@ -2,10 +2,32 @@
|
||||
import { ButtonCommon, Layout } from 'src/components/common'
|
||||
import { IconBuy } from 'src/components/icons'
|
||||
import { ButonType, ButtonSize } from 'src/utils/constanst.utils'
|
||||
import {CarouselCommon, LabelCommon, QuanittyInput } from 'src/components/common'
|
||||
const dataTest = [{
|
||||
text:1
|
||||
},{
|
||||
text:2
|
||||
},{
|
||||
text:3
|
||||
},{
|
||||
text:4
|
||||
},{
|
||||
text:5
|
||||
},{
|
||||
text:6
|
||||
}]
|
||||
const test = (props:{text:string})=><div className="h-64 bg-yellow-300">{props.text}</div>
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<div>This is home page</div>
|
||||
<CarouselCommon data={dataTest} Component={test} itemKey="test"/>
|
||||
<QuanittyInput size ="default" min={5} max={10} initValue={3}/>
|
||||
<QuanittyInput size ="small" min={3} step={10}/>
|
||||
<LabelCommon type="default" shape="half" >SEEFOOT</LabelCommon>
|
||||
<LabelCommon type="discount" shape="round">-15%</LabelCommon>
|
||||
<LabelCommon type="waiting">Waitting</LabelCommon>
|
||||
<LabelCommon type="delivering" >Delivering</LabelCommon>
|
||||
<LabelCommon type="delivered">Delivered</LabelCommon>
|
||||
<ButtonCommon loading={true}>Button default</ButtonCommon>
|
||||
<ButtonCommon type={ButonType.light} >{ButonType.light} - Button light</ButtonCommon>
|
||||
<ButtonCommon type={ButonType.light} disabled>{ButonType.light} - Button light</ButtonCommon>
|
||||
@ -13,9 +35,6 @@ export default function Home() {
|
||||
<ButtonCommon size={ButtonSize.large} icon={<IconBuy/>}>{ButtonSize.large} - Button default large</ButtonCommon>
|
||||
<ButtonCommon icon={<IconBuy/>} disabled isIconSuffix={true}>Button with icon disabled</ButtonCommon>
|
||||
<ButtonCommon icon={<IconBuy/>} type={ButonType.light}>Button with icon</ButtonCommon>
|
||||
<p>Go to <code>pages/index.tsx</code> to get your hand dirty!</p>
|
||||
<p>Go to <code>src/components</code> to make your awesome component!</p>
|
||||
<p>Go to <code>src/styles</code> to find global styles!</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
.navigation_wrapper{
|
||||
@apply relative;
|
||||
min-height: theme("caroucel.arrow-height") ;
|
||||
|
||||
}
|
57
src/components/common/CarouselCommon/CarouselCommon.tsx
Normal file
57
src/components/common/CarouselCommon/CarouselCommon.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import { useKeenSlider } from 'keen-slider/react'
|
||||
import React 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
|
||||
}
|
||||
|
||||
const CarouselCommon = ({ data, Component,itemKey }: CarouselCommonProps) => {
|
||||
const [currentSlide, setCurrentSlide] = React.useState(0)
|
||||
const [sliderRef, slider] = useKeenSlider<HTMLDivElement>({
|
||||
slidesPerView: 1,
|
||||
initial: 0,
|
||||
slideChanged(s) {
|
||||
setCurrentSlide(s.details().relativeSlide)
|
||||
},
|
||||
})
|
||||
const handleRightArrowClick = () => {
|
||||
slider.next()
|
||||
}
|
||||
|
||||
const handleLeftArrowClick = () => {
|
||||
slider.prev()
|
||||
}
|
||||
return (
|
||||
<div className={s.navigation_wrapper}>
|
||||
<div ref={sliderRef} className="keen-slider">
|
||||
{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}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CarouselCommon
|
@ -0,0 +1,17 @@
|
||||
.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 ;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
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"
|
||||
interface CustomCarouselArrowProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
side: 'left' | 'right'
|
||||
isDisabled:Boolean
|
||||
}
|
||||
|
||||
export const CustomCarouselArrow = ({
|
||||
side,isDisabled,
|
||||
...props
|
||||
}: CustomCarouselArrowProps) => {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
className={classNames(`${s.custom_arrow}`, { [`${s[side]}`]: side,[`${s.isDisabled}`]:isDisabled })}
|
||||
>
|
||||
{side==='left'?(<ArrowLeft/>):(<ArrowRight/>)}
|
||||
</button>
|
||||
)
|
||||
}
|
43
src/components/common/LabelCommon/LabelCommon.module.scss
Normal file
43
src/components/common/LabelCommon/LabelCommon.module.scss
Normal file
@ -0,0 +1,43 @@
|
||||
.labelCommonWarper{
|
||||
display: inline-flex;
|
||||
align-items: flex-start;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.01em;
|
||||
@apply text-white text-right;
|
||||
&.defaultSize{
|
||||
min-height: 2rem;
|
||||
line-height: 2rem;
|
||||
font-size: 1.2rem;
|
||||
padding: 0 0.8rem;
|
||||
}
|
||||
&.largeSize{
|
||||
max-height: 2.4rem;
|
||||
line-height: 2.4rem;
|
||||
font-size: 1.6rem;
|
||||
padding: 0 1.8rem;
|
||||
}
|
||||
&.defaultType{
|
||||
@apply bg-positive-dark;
|
||||
}
|
||||
&.discountType{
|
||||
@apply bg-negative;
|
||||
}
|
||||
&.waitingType{
|
||||
@apply bg-warning;
|
||||
}
|
||||
&.deliveringType{
|
||||
@apply bg-info;
|
||||
}
|
||||
&.deliveredType{
|
||||
@apply bg-positive;
|
||||
}
|
||||
&.defaultShape{
|
||||
border-radius: 0.4rem;
|
||||
}
|
||||
&.halfShape{
|
||||
border-radius: 0px 1.4rem 1.4rem 0px;
|
||||
}
|
||||
&.roundShape{
|
||||
border-radius: 1.4rem;
|
||||
}
|
||||
}
|
30
src/components/common/LabelCommon/LabelCommon.tsx
Normal file
30
src/components/common/LabelCommon/LabelCommon.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
import s from './LabelCommon.module.scss'
|
||||
interface LabelCommonProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
size?: 'default' | 'large'
|
||||
shape?: 'half' | 'round' | 'default'
|
||||
type?: 'default' | 'discount' | 'waiting' | 'delivering' | 'delivered'
|
||||
color?: string
|
||||
}
|
||||
|
||||
const LabelCommon = ({
|
||||
size = 'default',
|
||||
type = 'default',
|
||||
shape = "default",
|
||||
children,
|
||||
}: LabelCommonProps) => {
|
||||
return (
|
||||
<div
|
||||
className={classNames(s.labelCommonWarper, {
|
||||
[s[`${size}Size`]]: size,
|
||||
[s[`${type}Type`]]: type,
|
||||
[s[`${shape}Shape`]]: shape,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LabelCommon
|
@ -0,0 +1,39 @@
|
||||
@import '../../../styles/utilities';
|
||||
.quanittyInputWarper{
|
||||
border-color: theme("textColor.active");
|
||||
@apply border border-solid inline-flex justify-between items-center custom-border-radius;
|
||||
.plusIcon, .minusIcon{
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
&.default{
|
||||
max-width: 18.4rem;
|
||||
min-height: 4rem;
|
||||
.plusIcon, .minusIcon{
|
||||
margin: 0.8rem;
|
||||
width: 2.4rem;
|
||||
height: 2.4rem;
|
||||
}
|
||||
}
|
||||
&.small{
|
||||
max-width: 10rem;
|
||||
min-height: 2.8rem;
|
||||
.plusIcon, .minusIcon{
|
||||
margin: 0 0.6rem;
|
||||
// width: 1rem;
|
||||
// height: 1rem;
|
||||
}
|
||||
}
|
||||
.quanittyInput{
|
||||
@apply bg-background outline-none w-1/2 text-center h-full font-bold;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
color: theme("textColor.active");
|
||||
&::-webkit-inner-spin-button, &::-webkit-inner-spin-button{
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
83
src/components/common/QuanittyInput/QuanittyInput.tsx
Normal file
83
src/components/common/QuanittyInput/QuanittyInput.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
import React, { ChangeEvent, useEffect, useState } from 'react'
|
||||
import s from './QuanittyInput.module.scss'
|
||||
import classNames from 'classnames'
|
||||
import { Minus, Plus } from '@components/icons'
|
||||
interface QuanittyInputProps
|
||||
extends Omit<
|
||||
React.InputHTMLAttributes<HTMLInputElement>,
|
||||
'onChange' | 'min' | 'max' | 'step' | "type" | "size"
|
||||
> {
|
||||
size?: 'default' | 'small'
|
||||
onChange?: (value: number) => void
|
||||
initValue?: number
|
||||
min?: number
|
||||
max?: number
|
||||
step?: number
|
||||
}
|
||||
|
||||
const QuanittyInput = ({
|
||||
size = 'default',
|
||||
onChange,
|
||||
initValue = 0,
|
||||
min,
|
||||
max,
|
||||
step = 1,
|
||||
...props
|
||||
}: QuanittyInputProps) => {
|
||||
const [value, setValue] = useState<number>(0)
|
||||
|
||||
useEffect(() => {
|
||||
onChange && onChange(value)
|
||||
}, [value])
|
||||
|
||||
useEffect(() => {
|
||||
initValue && setValue(initValue)
|
||||
}, [initValue])
|
||||
|
||||
const onPlusClick = () => {
|
||||
if (max && value + step > max) {
|
||||
setValue(max)
|
||||
} else {
|
||||
setValue(value + step)
|
||||
}
|
||||
}
|
||||
|
||||
const onMinusClick = () => {
|
||||
if (min && value - step < min) {
|
||||
setValue(min)
|
||||
} else {
|
||||
setValue(value - step)
|
||||
}
|
||||
}
|
||||
|
||||
const onValueChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
let value = Number(e.target.value) || 0
|
||||
if (min && value < min) {
|
||||
setValue(min)
|
||||
} else if (max && value > max) {
|
||||
setValue(max)
|
||||
} else {
|
||||
setValue(value)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classNames(s.quanittyInputWarper, { [s[size]]: size })}>
|
||||
<div className={s.minusIcon} onClick={onMinusClick}>
|
||||
<Minus />
|
||||
</div>
|
||||
<input
|
||||
{...props}
|
||||
type="number"
|
||||
value={value}
|
||||
onChange={onValueChange}
|
||||
className={s.quanittyInput}
|
||||
/>
|
||||
<div className={s.plusIcon} onClick={onPlusClick}>
|
||||
<Plus />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default QuanittyInput
|
@ -1,5 +1,8 @@
|
||||
export { default as ButtonCommon } from './ButtonCommon/ButtonCommon'
|
||||
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 Head } from './Head/Head'
|
||||
export { default as ViewAllItem} from './ViewAllItem/ViewAllItem'
|
||||
export { default as ItemWishList} from './ItemWishList/ItemWishList'
|
||||
|
18
src/components/icons/ArrowLeft.tsx
Normal file
18
src/components/icons/ArrowLeft.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
const ArrowLeft = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
height="18"
|
||||
viewBox="0 0 12 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M4.81637 8.99983L10.7164 3.09983C11.0268 2.78756 11.201 2.36514 11.201 1.92483C11.201 1.48452 11.0268 1.0621 10.7164 0.749833C10.5614 0.593619 10.3771 0.469628 10.174 0.385014C9.9709 0.300399 9.75306 0.256836 9.53304 0.256836C9.31302 0.256836 9.09517 0.300399 8.89207 0.385014C8.68898 0.469628 8.50464 0.593619 8.3497 0.749833L1.28304 7.8165C1.12682 7.97144 1.00283 8.15577 0.918218 8.35887C0.833603 8.56197 0.790039 8.77981 0.790039 8.99983C0.790039 9.21985 0.833603 9.43769 0.918218 9.64079C1.00283 9.84389 1.12682 10.0282 1.28304 10.1832L8.3497 17.3332C8.50544 17.4876 8.69013 17.6098 8.89319 17.6928C9.09626 17.7757 9.31369 17.8178 9.53304 17.8165C9.75238 17.8178 9.96982 17.7757 10.1729 17.6928C10.3759 17.6098 10.5606 17.4876 10.7164 17.3332C11.0268 17.0209 11.201 16.5985 11.201 16.1582C11.201 15.7179 11.0268 15.2954 10.7164 14.9832L4.81637 8.99983Z"
|
||||
fill="#141414"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default ArrowLeft
|
18
src/components/icons/ArrowRight.tsx
Normal file
18
src/components/icons/ArrowRight.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
const ArrowRight = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
height="18"
|
||||
viewBox="0 0 12 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10.7168 7.8165L3.65015 0.749833C3.49521 0.593619 3.31088 0.469628 3.10778 0.385014C2.90468 0.300399 2.68684 0.256836 2.46682 0.256836C2.2468 0.256836 2.02895 0.300399 1.82585 0.385014C1.62276 0.469628 1.43842 0.593619 1.28348 0.749833C0.973064 1.0621 0.798828 1.48452 0.798828 1.92483C0.798828 2.36514 0.973064 2.78756 1.28348 3.09983L7.18348 8.99983L1.28348 14.8998C0.973064 15.2121 0.798828 15.6345 0.798828 16.0748C0.798828 16.5151 0.973064 16.9376 1.28348 17.2498C1.43922 17.4043 1.62391 17.5265 1.82697 17.6095C2.03003 17.6924 2.24747 17.7344 2.46682 17.7332C2.68616 17.7344 2.9036 17.6924 3.10666 17.6095C3.30972 17.5265 3.49442 17.4043 3.65015 17.2498L10.7168 10.1832C10.873 10.0282 10.997 9.84389 11.0816 9.64079C11.1662 9.4377 11.2098 9.21985 11.2098 8.99983C11.2098 8.77981 11.1662 8.56197 11.0816 8.35887C10.997 8.15577 10.873 7.97144 10.7168 7.8165Z"
|
||||
fill="#141414"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default ArrowRight
|
18
src/components/icons/Minus.tsx
Normal file
18
src/components/icons/Minus.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
const ArrowRight = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10.6667 5.33317H6.66669V1.33317C6.66669 1.15636 6.59645 0.98679 6.47142 0.861766C6.3464 0.736742 6.17683 0.666504 6.00002 0.666504C5.82321 0.666504 5.65364 0.736742 5.52862 0.861766C5.40359 0.98679 5.33335 1.15636 5.33335 1.33317V5.33317H1.33335C1.15654 5.33317 0.986974 5.40341 0.861949 5.52843C0.736925 5.65346 0.666687 5.82303 0.666687 5.99984C0.666687 6.17665 0.736925 6.34622 0.861949 6.47124C0.986974 6.59627 1.15654 6.6665 1.33335 6.6665H5.33335V10.6665C5.33335 10.8433 5.40359 11.0129 5.52862 11.1379C5.65364 11.2629 5.82321 11.3332 6.00002 11.3332C6.17683 11.3332 6.3464 11.2629 6.47142 11.1379C6.59645 11.0129 6.66669 10.8433 6.66669 10.6665V6.6665H10.6667C10.8435 6.6665 11.0131 6.59627 11.1381 6.47124C11.2631 6.34622 11.3334 6.17665 11.3334 5.99984C11.3334 5.82303 11.2631 5.65346 11.1381 5.52843C11.0131 5.40341 10.8435 5.33317 10.6667 5.33317Z"
|
||||
fill="#141414"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default ArrowRight
|
18
src/components/icons/Plus.tsx
Normal file
18
src/components/icons/Plus.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
const ArrowLeft = ({ ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
height="2"
|
||||
viewBox="0 0 12 2"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10.6667 0.333496H1.33335C1.15654 0.333496 0.986974 0.403734 0.861949 0.528758C0.736925 0.653783 0.666687 0.823352 0.666687 1.00016C0.666687 1.17697 0.736925 1.34654 0.861949 1.47157C0.986974 1.59659 1.15654 1.66683 1.33335 1.66683H10.6667C10.8435 1.66683 11.0131 1.59659 11.1381 1.47157C11.2631 1.34654 11.3334 1.17697 11.3334 1.00016C11.3334 0.823352 11.2631 0.653783 11.1381 0.528758C11.0131 0.403734 10.8435 0.333496 10.6667 0.333496Z"
|
||||
fill="#141414"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default ArrowLeft
|
@ -34,9 +34,12 @@
|
||||
--border-line: #ebebeb;
|
||||
--background: #f8f8f8;
|
||||
--white: #fbfbfb;
|
||||
--background-arrow:rgba(20, 20, 20, 0.05);
|
||||
--font-size: 1.6rem;
|
||||
--line-height: 2.4rem;
|
||||
|
||||
--font-size: 16px;
|
||||
--line-height: 24px;
|
||||
// --font-size: 16px;
|
||||
// --line-height: 24px;
|
||||
|
||||
--font-sans: "Nunito", cursive, -apple-system, system-ui, BlinkMacSystemFont, "Helvetica Neue", "Helvetica",
|
||||
sans-serif;
|
||||
|
@ -48,6 +48,9 @@ module.exports = {
|
||||
'line': 'var(--border-line)',
|
||||
'background': 'var(--background)',
|
||||
'white': 'var(--white)',
|
||||
|
||||
'background-arrow':'var(--background-arrow)',
|
||||
|
||||
'disabled': 'var(--text-disabled)',
|
||||
|
||||
// @deprecated (NOT use these variables)
|
||||
@ -113,7 +116,10 @@ module.exports = {
|
||||
|
||||
'2xl': '1536px',
|
||||
// => @media (min-width: 1536px) { ... }
|
||||
}
|
||||
},
|
||||
caroucel:{
|
||||
"arrow-height":"64px"
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require('postcss-import'), require('tailwindcss'), require('autoprefixer')]
|
||||
|
Loading…
x
Reference in New Issue
Block a user