From 2595adc3055dd0b370b717c15c934b84b442f693 Mon Sep 17 00:00:00 2001 From: Andymick Date: Sat, 18 Jul 2026 12:19:35 +0100 Subject: [PATCH] Add reference size input for custom garment dimensions - Add Reference Size (cm) section with Width and Height inputs - Rulers now use reference size instead of product print area dimensions - Measurements scale to garment size specified by user - Add to bag button disabled until reference sizes are provided - Rulers span full canvas and show custom scale (e.g., 0-40cm x 0-60cm) - Reference size required for personalization checkout Co-Authored-By: Claude Haiku 4.5 --- src/components/DesignCanvas.tsx | 93 ++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 26 deletions(-) diff --git a/src/components/DesignCanvas.tsx b/src/components/DesignCanvas.tsx index cd37e32..7368b4a 100644 --- a/src/components/DesignCanvas.tsx +++ b/src/components/DesignCanvas.tsx @@ -168,6 +168,8 @@ function PrintSurface({ onNodeReady, scale, product, + referenceWidthCm, + referenceHeightCm, }: { photo: React.ReactNode; printArea: PrintArea; @@ -183,6 +185,8 @@ function PrintSurface({ onNodeReady: () => void; scale: number; product: ProductDTO; + referenceWidthCm: number | null; + referenceHeightCm: number | null; }) { const stageW = printArea.wPct * DISPLAY; const stageH = printArea.hPct * DISPLAY; @@ -204,13 +208,13 @@ function PrintSurface({ {/* Horizontal Ruler */} - {product.printAreaWidthMm > 0 && stageW > 0 && ( + {referenceWidthCm && referenceWidthCm > 0 && (
{(() => { const marks = []; - const cmPerStageW = product.printAreaWidthMm / 10; // total cm width of stage - const pxPerCm = stageW / cmPerStageW; + const pxPerCm = DISPLAY / referenceWidthCm; - for (let cm = 0; cm <= Math.ceil(cmPerStageW); cm += 2) { + for (let cm = 0; cm <= Math.ceil(referenceWidthCm); cm += 2) { const px = cm * pxPerCm; - if (px > stageW) break; + if (px > DISPLAY) break; marks.push(
0 && stageH > 0 && ( + {referenceHeightCm && referenceHeightCm > 0 && (
{(() => { const marks = []; - const cmPerStageH = product.printAreaHeightMm / 10; // total cm height of stage - const pxPerCm = stageH / cmPerStageH; + const pxPerCm = DISPLAY / referenceHeightCm; - for (let cm = 0; cm <= Math.ceil(cmPerStageH); cm += 2) { + for (let cm = 0; cm <= Math.ceil(referenceHeightCm); cm += 2) { const px = cm * pxPerCm; - if (px > stageH) break; + if (px > DISPLAY) break; marks.push(
(null); + const [referenceHeightCm, setReferenceHeightCm] = useState(null); const stageFrontRef = useRef(null); const stageBackRef = useRef(null); @@ -439,14 +443,12 @@ export default function DesignCanvas({ product }: { product: ProductDTO }) { const activeStageW = activePrintArea.wPct * DISPLAY; const activeStageH = activePrintArea.hPct * DISPLAY; - // Calculate cm per pixel for display - const printAreaWidthPx = activePrintArea.wPct * DISPLAY; - // printAreaWidthMm is in mm, divide by 10 to get cm - // printAreaWidthPx is in pixels - // cmPerPx = cm per pixel - const mmPerPx = printAreaWidthPx > 0 ? product.printAreaWidthMm / printAreaWidthPx : 0; - const cmPerPx = mmPerPx > 0 ? mmPerPx / 10 : 0; + // Calculate cm per pixel for display using reference size + // Reference size maps to full canvas width/height (560px) + const cmPerPx = referenceWidthCm && referenceWidthCm > 0 ? referenceWidthCm / DISPLAY : 0; + const cmPerPxHeight = referenceHeightCm && referenceHeightCm > 0 ? referenceHeightCm / DISPLAY : 0; const pxToCm = (px: number) => { + if (!cmPerPx || cmPerPx <= 0) return '—'; const cm = px * cmPerPx; return isNaN(cm) ? '?' : cm.toFixed(2); }; @@ -748,6 +750,8 @@ export default function DesignCanvas({ product }: { product: ProductDTO }) { onNodeReady={() => setNodeReadyTick((t) => t + 1)} scale={canvasScale} product={product} + referenceWidthCm={referenceWidthCm} + referenceHeightCm={referenceHeightCm} /> {hasBack && ( @@ -766,6 +770,8 @@ export default function DesignCanvas({ product }: { product: ProductDTO }) { onNodeReady={() => setNodeReadyTick((t) => t + 1)} scale={canvasScale} product={product} + referenceWidthCm={referenceWidthCm} + referenceHeightCm={referenceHeightCm} /> )} @@ -855,6 +861,38 @@ export default function DesignCanvas({ product }: { product: ProductDTO }) {
)} + {canPersonalize && ( +
+

Reference Size (cm)

+
+
+ + setReferenceWidthCm(e.target.value ? Number(e.target.value) : null)} + placeholder="e.g. 40" + className="w-full border border-line bg-paper px-3 py-2 text-sm placeholder:text-muted" + /> +
+
+ + setReferenceHeightCm(e.target.value ? Number(e.target.value) : null)} + placeholder="e.g. 60" + className="w-full border border-line bg-paper px-3 py-2 text-sm placeholder:text-muted" + /> +
+
+
+ )} + {canPersonalize && (

@@ -1002,9 +1040,9 @@ export default function DesignCanvas({ product }: { product: ProductDTO }) {

- {elementsFront.length === 0 && elementsBack.length === 0 && ( + {canPersonalize && (!referenceWidthCm || !referenceHeightCm) && ( +

Please enter the reference size (width and height) before adding to bag

+ )} + {canPersonalize && (elementsFront.length === 0 && elementsBack.length === 0) && (referenceWidthCm && referenceHeightCm) && (

Add text or images to personalize your design before adding to bag

)} {justAdded &&

Added to your bag.

}