mirror of
https://github.com/vercel/commerce.git
synced 2025-05-18 07:26:59 +00:00
Adding Quantity Component
This commit is contained in:
parent
fe324696d7
commit
f8f8cb3494
@ -6,23 +6,6 @@
|
|||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
|
||||||
@apply flex p-1 border-accent-2 border items-center justify-center w-12 text-accent-7;
|
|
||||||
transition-property: border-color, background, color, transform, box-shadow;
|
|
||||||
transition-duration: 0.15s;
|
|
||||||
transition-timing-function: ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions:hover {
|
|
||||||
@apply bg-accent-1 border-accent-3 text-accent-9;
|
|
||||||
transition: border-color;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions:focus {
|
|
||||||
@apply bg-accent-2 outline-none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quantity {
|
.quantity {
|
||||||
appearance: textfield;
|
appearance: textfield;
|
||||||
@apply w-8 border-accent-2 border mx-3 rounded text-center text-sm text-black;
|
@apply w-8 border-accent-2 border mx-3 rounded text-center text-sm text-black;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ChangeEvent, useEffect, useState } from 'react'
|
import { ChangeEvent, FocusEventHandler, useEffect, useState } from 'react'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
@ -9,6 +9,7 @@ import type { LineItem } from '@commerce/types/cart'
|
|||||||
import usePrice from '@framework/product/use-price'
|
import usePrice from '@framework/product/use-price'
|
||||||
import useUpdateItem from '@framework/cart/use-update-item'
|
import useUpdateItem from '@framework/cart/use-update-item'
|
||||||
import useRemoveItem from '@framework/cart/use-remove-item'
|
import useRemoveItem from '@framework/cart/use-remove-item'
|
||||||
|
import Quantity from '@components/ui/Quantity'
|
||||||
|
|
||||||
type ItemOption = {
|
type ItemOption = {
|
||||||
name: string
|
name: string
|
||||||
@ -28,6 +29,10 @@ const CartItem = ({
|
|||||||
currencyCode: string
|
currencyCode: string
|
||||||
}) => {
|
}) => {
|
||||||
const { closeSidebarIfPresent } = useUI()
|
const { closeSidebarIfPresent } = useUI()
|
||||||
|
const [removing, setRemoving] = useState(false)
|
||||||
|
const [quantity, setQuantity] = useState<number>(item.quantity)
|
||||||
|
const removeItem = useRemoveItem()
|
||||||
|
const updateItem = useUpdateItem({ item })
|
||||||
|
|
||||||
const { price } = usePrice({
|
const { price } = usePrice({
|
||||||
amount: item.variant.price * item.quantity,
|
amount: item.variant.price * item.quantity,
|
||||||
@ -35,43 +40,22 @@ const CartItem = ({
|
|||||||
currencyCode,
|
currencyCode,
|
||||||
})
|
})
|
||||||
|
|
||||||
const updateItem = useUpdateItem({ item })
|
const handleChange = async ({
|
||||||
const removeItem = useRemoveItem()
|
target: { value },
|
||||||
const [quantity, setQuantity] = useState<number | ''>(item.quantity)
|
}: ChangeEvent<HTMLInputElement>) => {
|
||||||
const [removing, setRemoving] = useState(false)
|
setQuantity(Number(value))
|
||||||
|
await updateItem({ quantity: Number(value) })
|
||||||
const updateQuantity = async (val: number) => {
|
|
||||||
await updateItem({ quantity: val })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleQuantity = (e: ChangeEvent<HTMLInputElement>) => {
|
const increaseQuantity = async (n = 1) => {
|
||||||
const val = !e.target.value ? '' : Number(e.target.value)
|
|
||||||
|
|
||||||
if (!val || (Number.isInteger(val) && val >= 0)) {
|
|
||||||
setQuantity(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleBlur = () => {
|
|
||||||
const val = Number(quantity)
|
|
||||||
if (val !== item.quantity) {
|
|
||||||
updateQuantity(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const increaseQuantity = (n = 1) => {
|
|
||||||
const val = Number(quantity) + n
|
const val = Number(quantity) + n
|
||||||
if (Number.isInteger(val) && val >= 0) {
|
setQuantity(val)
|
||||||
setQuantity(val)
|
await updateItem({ quantity: val })
|
||||||
updateQuantity(val)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRemove = async () => {
|
const handleRemove = async () => {
|
||||||
setRemoving(true)
|
setRemoving(true)
|
||||||
try {
|
try {
|
||||||
// If this action succeeds then there's no need to do `setRemoving(true)`
|
|
||||||
// because the component will be removed from the view
|
|
||||||
await removeItem(item)
|
await removeItem(item)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setRemoving(false)
|
setRemoving(false)
|
||||||
@ -152,38 +136,13 @@ const CartItem = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{variant === 'default' && (
|
{variant === 'default' && (
|
||||||
<div className="flex flex-row h-9">
|
<Quantity
|
||||||
<button className={s.actions} onClick={handleRemove}>
|
value={quantity}
|
||||||
<Cross width={20} height={20} />
|
handleRemove={handleRemove}
|
||||||
</button>
|
handleChange={handleChange}
|
||||||
<label className="w-full border-accent-2 border ml-2">
|
increase={() => increaseQuantity(1)}
|
||||||
<input
|
decrease={() => increaseQuantity(-1)}
|
||||||
type="number"
|
/>
|
||||||
max={99}
|
|
||||||
min={0}
|
|
||||||
className="bg-transparent px-4 w-full h-full focus:outline-none"
|
|
||||||
value={quantity}
|
|
||||||
onChange={handleQuantity}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => increaseQuantity(-1)}
|
|
||||||
className={s.actions}
|
|
||||||
style={{ marginLeft: '-1px' }}
|
|
||||||
>
|
|
||||||
<Minus width={18} height={18} />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => increaseQuantity(1)}
|
|
||||||
className={cn(s.actions)}
|
|
||||||
style={{ marginLeft: '-1px' }}
|
|
||||||
>
|
|
||||||
<Plus width={18} height={18} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
.root {
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
import React, { FC } from 'react'
|
|
||||||
import s from './Quantifier.module.css'
|
|
||||||
|
|
||||||
export interface QuantifierProps {}
|
|
||||||
|
|
||||||
const Quantifier: FC<QuantifierProps> = ({}) => {
|
|
||||||
return <div></div>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Quantifier
|
|
@ -1,2 +0,0 @@
|
|||||||
export { default } from './Quantifier'
|
|
||||||
export * from './Quantifier'
|
|
22
components/ui/Quantity/Quantity.module.css
Normal file
22
components/ui/Quantity/Quantity.module.css
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
.actions {
|
||||||
|
@apply flex p-1 border-accent-2 border items-center justify-center w-12 text-accent-7;
|
||||||
|
transition-property: border-color, background, color, transform, box-shadow;
|
||||||
|
transition-duration: 0.15s;
|
||||||
|
transition-timing-function: ease;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions:hover {
|
||||||
|
@apply border bg-accent-1 border-accent-3 text-accent-9;
|
||||||
|
transition: border-color;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions:focus {
|
||||||
|
@apply outline-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
@apply bg-transparent px-4 w-full h-full focus:outline-none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
62
components/ui/Quantity/Quantity.tsx
Normal file
62
components/ui/Quantity/Quantity.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import React, { FC } from 'react'
|
||||||
|
import s from './Quantity.module.css'
|
||||||
|
import { Cross, Plus, Minus } from '@components/icons'
|
||||||
|
import cn from 'classnames'
|
||||||
|
export interface QuantityProps {
|
||||||
|
value: number
|
||||||
|
increase: () => any
|
||||||
|
decrease: () => any
|
||||||
|
handleRemove: React.MouseEventHandler<HTMLButtonElement>
|
||||||
|
handleChange: React.ChangeEventHandler<HTMLInputElement>
|
||||||
|
max?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const Quantity: FC<QuantityProps> = ({
|
||||||
|
value,
|
||||||
|
increase,
|
||||||
|
decrease,
|
||||||
|
handleChange,
|
||||||
|
handleRemove,
|
||||||
|
max = 6,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-row h-9">
|
||||||
|
<button className={s.actions} onClick={handleRemove}>
|
||||||
|
<Cross width={20} height={20} />
|
||||||
|
</button>
|
||||||
|
<label className="w-full border-accent-2 border ml-2">
|
||||||
|
<input
|
||||||
|
className={s.input}
|
||||||
|
onChange={(e) =>
|
||||||
|
Number(e.target.value) < max + 1 ? handleChange(e) : () => {}
|
||||||
|
}
|
||||||
|
value={value}
|
||||||
|
type="number"
|
||||||
|
max={max}
|
||||||
|
min="0"
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={decrease}
|
||||||
|
className={s.actions}
|
||||||
|
style={{ marginLeft: '-1px' }}
|
||||||
|
disabled={value <= 1}
|
||||||
|
>
|
||||||
|
<Minus width={18} height={18} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={increase}
|
||||||
|
className={cn(s.actions)}
|
||||||
|
style={{ marginLeft: '-1px' }}
|
||||||
|
disabled={value < 1 || value >= max}
|
||||||
|
>
|
||||||
|
<Plus width={18} height={18} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Quantity
|
2
components/ui/Quantity/index.ts
Normal file
2
components/ui/Quantity/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { default } from './Quantity'
|
||||||
|
export * from './Quantity'
|
@ -11,4 +11,5 @@ export { default as Modal } from './Modal'
|
|||||||
export { default as Text } from './Text'
|
export { default as Text } from './Text'
|
||||||
export { default as Input } from './Input'
|
export { default as Input } from './Input'
|
||||||
export { default as Collapse } from './Collapse'
|
export { default as Collapse } from './Collapse'
|
||||||
|
export { default as Quantity } from './Quantity'
|
||||||
export { useUI } from './context'
|
export { useUI } from './context'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user