| import type {CartLineUpdateInput} from '@shopify/hydrogen/storefront-api-types'; |
| import type {CartLayout} from '~/components/CartMain'; |
| import {CartForm, Image, type OptimisticCartLine} from '@shopify/hydrogen'; |
| import {useVariantUrl} from '~/lib/variants'; |
| import {Link} from '@remix-run/react'; |
| import {ProductPrice} from './ProductPrice'; |
| import {useAside} from './Aside'; |
| import type {CartApiQueryFragment} from 'storefrontapi.generated'; |
|
|
| type CartLine = OptimisticCartLine<CartApiQueryFragment>; |
|
|
| |
| |
| |
| |
| export function CartLineItem({ |
| layout, |
| line, |
| }: { |
| layout: CartLayout; |
| line: CartLine; |
| }) { |
| const {id, merchandise} = line; |
| const {product, title, image, selectedOptions} = merchandise; |
| const lineItemUrl = useVariantUrl(product.handle, selectedOptions); |
| const {close} = useAside(); |
|
|
| return ( |
| <li key={id} className="cart-line"> |
| {image && ( |
| <Image |
| alt={title} |
| aspectRatio="1/1" |
| data={image} |
| height={100} |
| loading="lazy" |
| width={100} |
| /> |
| )} |
| |
| <div> |
| <Link |
| prefetch="intent" |
| to={lineItemUrl} |
| onClick={() => { |
| if (layout === 'aside') { |
| close(); |
| } |
| }} |
| > |
| <p> |
| <strong>{product.title}</strong> |
| </p> |
| </Link> |
| <ProductPrice price={line?.cost?.totalAmount} /> |
| <ul> |
| {selectedOptions.map((option) => ( |
| <li key={option.name}> |
| <small> |
| {option.name}: {option.value} |
| </small> |
| </li> |
| ))} |
| </ul> |
| <CartLineQuantity line={line} /> |
| </div> |
| </li> |
| ); |
| } |
|
|
| |
| |
| |
| |
| |
| function CartLineQuantity({line}: {line: CartLine}) { |
| if (!line || typeof line?.quantity === 'undefined') return null; |
| const {id: lineId, quantity, isOptimistic} = line; |
| const prevQuantity = Number(Math.max(0, quantity - 1).toFixed(0)); |
| const nextQuantity = Number((quantity + 1).toFixed(0)); |
|
|
| return ( |
| <div className="cart-line-quantity"> |
| <small>Quantity: {quantity} </small> |
| <CartLineUpdateButton lines={[{id: lineId, quantity: prevQuantity}]}> |
| <button |
| aria-label="Decrease quantity" |
| disabled={quantity <= 1 || !!isOptimistic} |
| name="decrease-quantity" |
| value={prevQuantity} |
| > |
| <span>− </span> |
| </button> |
| </CartLineUpdateButton> |
| |
| <CartLineUpdateButton lines={[{id: lineId, quantity: nextQuantity}]}> |
| <button |
| aria-label="Increase quantity" |
| name="increase-quantity" |
| value={nextQuantity} |
| disabled={!!isOptimistic} |
| > |
| <span>+</span> |
| </button> |
| </CartLineUpdateButton> |
| |
| <CartLineRemoveButton lineIds={[lineId]} disabled={!!isOptimistic} /> |
| </div> |
| ); |
| } |
|
|
| |
| |
| |
| |
| |
| function CartLineRemoveButton({ |
| lineIds, |
| disabled, |
| }: { |
| lineIds: string[]; |
| disabled: boolean; |
| }) { |
| return ( |
| <CartForm |
| route="/cart" |
| action={CartForm.ACTIONS.LinesRemove} |
| inputs={{lineIds}} |
| > |
| <button disabled={disabled} type="submit"> |
| Remove |
| </button> |
| </CartForm> |
| ); |
| } |
|
|
| function CartLineUpdateButton({ |
| children, |
| lines, |
| }: { |
| children: React.ReactNode; |
| lines: CartLineUpdateInput[]; |
| }) { |
| return ( |
| <CartForm |
| route="/cart" |
| action={CartForm.ACTIONS.LinesUpdate} |
| inputs={{lines}} |
| > |
| {children} |
| </CartForm> |
| ); |
| } |
|
|