Product.jsx
3.1 kB · jsx · 74 lines
1import { useRef, useState } from "react";2import { SHOP } from "../config/shop.ts";3import { Breadcrumbs } from "../components/Breadcrumbs.jsx";4import { Carousel, FullGrid, Mockups, mockupSlides } from "../components/Gallery.jsx";5import { MoreVariations } from "../components/MoreVariations.jsx";6import { ProductDetails } from "../components/ProductDetails.jsx";7import { ProductGrid } from "../components/ProductGrid.jsx";8import { Strip } from "../components/Strip.jsx";9import { pickPrimary, useNow, usePrimary } from "../lib/hooks.ts";10import { left } from "../lib/life.ts";1112export function Product({ trail, product, family, matching = [], alike = [], printful, shop, tiles, buyPrefix, review, preview = false, now: built }) {13 const primary = usePrimary("light");14 const one = product[primary] ?? product.light;15 const now = useNow(built);16 const [index, setIndex] = useState(0);17 const [size, setSize] = useState("");18 const box = useRef(null);19 const hold = useRef(null);20 const slides = mockupSlides(one.images, product.title);21 const at = index < slides.length ? index : 0;22 const variant = one.variants.find((each) => each.size === size) ?? one.variants[0];23 const dead = Boolean(one.released) && left(one.released, now) <= 0;24 const jump = (i) => {25 setIndex(i);26 if (box.current?.open) box.current.close();27 else hold.current?.scrollIntoView({ block: "start", behavior: "smooth" });28 };29 return (30 <div className="wrap">31 <Breadcrumbs trail={trail} />32 <article className="product" data-primary={primary}>33 <div className="left">34 <Carousel slides={slides} index={at} onIndex={setIndex} keys full label="Image" hold={hold} onFull={() => box.current?.showModal()} />35 <Mockups images={one.images} title={product.title} onJump={jump} />36 </div>37 <div className="side">38 <ProductDetails39 product={product}40 one={one}41 primary={primary}42 onPrimary={pickPrimary}43 variant={variant}44 size={variant ? variant.size : ""}45 onSize={setSize}46 image={slides[at] ? slides[at].full : ""}47 buyPrefix={buyPrefix}48 printful={printful}49 shop={shop}50 tiles={tiles}51 now={built}52 review={review}53 preview={preview}54 dead={dead}55 />56 <MoreVariations family={family} current={product.design} title={product.title} shop={shop} />57 </div>58 </article>59 {matching.length ? (60 <section className="alike" aria-labelledby="matching">61 <Strip id="matching" title="Matching Products" href={`${SHOP}?design=${product.design}`} more={`All ${product.design}`} />62 <ProductGrid products={matching} now={built} />63 </section>64 ) : null}65 {alike.length ? (66 <section className="alike" aria-labelledby="alike">67 <Strip id="alike" title="You May Also Like" href={SHOP} more="All variations" />68 <ProductGrid products={alike} now={built} />69 </section>70 ) : null}71 <FullGrid images={one.images} title={product.title} box={box} onJump={jump} />72 </div>73 );74}