@primestyleai/tryon 3.18.0 → 3.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -308,6 +308,10 @@ const en = {
|
|
|
308
308
|
"Compare with another size": "Compare with another size",
|
|
309
309
|
"recommended": "recommended",
|
|
310
310
|
"size": "size",
|
|
311
|
+
"Your size in other countries": "Your size in other countries",
|
|
312
|
+
"Showing fit for size": "Showing fit for size",
|
|
313
|
+
"Back to": "Back to",
|
|
314
|
+
"Edit values": "Edit values",
|
|
311
315
|
"Fit Analysis": "Fit Analysis",
|
|
312
316
|
"Show more": "Show more",
|
|
313
317
|
"Done": "Done",
|
package/dist/primestyle-tryon.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage } from "./index-
|
|
2
|
-
import { P, b, T, d, r } from "./index-
|
|
1
|
+
import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage } from "./index-Dr4MxT8W.js";
|
|
2
|
+
import { P, b, T, d, r } from "./index-Dr4MxT8W.js";
|
|
3
3
|
function detectProductImage() {
|
|
4
4
|
const ogImage = document.querySelector(
|
|
5
5
|
'meta[property="og:image"]'
|
package/dist/react/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect, useMemo, useRef, useCallback } from "react";
|
|
4
|
-
import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage, P as PrimeStyleError, L as LOCALE_LABELS, b as SUPPORTED_LOCALES } from "../index-
|
|
4
|
+
import { c as createT, A as ApiClient, S as SseClient, i as isValidImageFile, a as compressImage, P as PrimeStyleError, L as LOCALE_LABELS, b as SUPPORTED_LOCALES } from "../index-Dr4MxT8W.js";
|
|
5
5
|
const HEADER_ALIASES = {
|
|
6
6
|
// ── Size label columns (skipped during field derivation) ──
|
|
7
7
|
size: "__size__",
|
|
@@ -406,6 +406,26 @@ const FALLBACK_FIELDS_MALE = [
|
|
|
406
406
|
{ key: "inseam", label: "Inseam", required: false, unit: "cm", placeholder: "e.g. 81", category: "body" },
|
|
407
407
|
{ key: "footLengthCm", label: "Foot length", required: false, unit: "cm", placeholder: "e.g. 27", category: "shoe" }
|
|
408
408
|
];
|
|
409
|
+
const SIZE_CONVERSIONS = {
|
|
410
|
+
"XXS": { US: "XXS", UK: "4", EU: "30", IT: "36", FR: "32", DE: "30", JP: "3", KR: "40", AU: "4", BR: "PP" },
|
|
411
|
+
"XS": { US: "XS", UK: "6", EU: "32", IT: "38", FR: "34", DE: "32", JP: "5", KR: "44", AU: "6", BR: "PP" },
|
|
412
|
+
"S": { US: "S", UK: "8", EU: "36", IT: "42", FR: "38", DE: "36", JP: "9", KR: "55", AU: "10", BR: "P" },
|
|
413
|
+
"M": { US: "M", UK: "10", EU: "38", IT: "44", FR: "40", DE: "38", JP: "11", KR: "66", AU: "12", BR: "M" },
|
|
414
|
+
"L": { US: "L", UK: "12", EU: "40", IT: "46", FR: "42", DE: "40", JP: "13", KR: "77", AU: "14", BR: "G" },
|
|
415
|
+
"XL": { US: "XL", UK: "14", EU: "42", IT: "48", FR: "44", DE: "42", JP: "15", KR: "88", AU: "16", BR: "GG" },
|
|
416
|
+
"XXL": { US: "XXL", UK: "16", EU: "44", IT: "50", FR: "46", DE: "44", JP: "17", KR: "99", AU: "18", BR: "XG" },
|
|
417
|
+
"XXXL": { US: "XXXL", UK: "18", EU: "46", IT: "52", FR: "48", DE: "46", JP: "19", KR: "100", AU: "20", BR: "EG" },
|
|
418
|
+
// Numeric sizes (EU-based)
|
|
419
|
+
"34": { US: "XS", UK: "6", EU: "34", IT: "38", FR: "34", DE: "34", JP: "5", KR: "44", AU: "6" },
|
|
420
|
+
"36": { US: "S", UK: "8", EU: "36", IT: "40", FR: "36", DE: "36", JP: "7", KR: "55", AU: "8" },
|
|
421
|
+
"38": { US: "M", UK: "10", EU: "38", IT: "42", FR: "38", DE: "38", JP: "9", KR: "66", AU: "10" },
|
|
422
|
+
"40": { US: "L", UK: "12", EU: "40", IT: "44", FR: "40", DE: "40", JP: "11", KR: "77", AU: "12" },
|
|
423
|
+
"42": { US: "XL", UK: "14", EU: "42", IT: "46", FR: "42", DE: "42", JP: "13", KR: "88", AU: "14" },
|
|
424
|
+
"44": { US: "XXL", UK: "16", EU: "44", IT: "48", FR: "44", DE: "44", JP: "15", KR: "99", AU: "16" },
|
|
425
|
+
"46": { US: "XXXL", UK: "18", EU: "46", IT: "50", FR: "46", DE: "46", JP: "17", KR: "100", AU: "18" },
|
|
426
|
+
"48": { US: "XXXL", UK: "20", EU: "48", IT: "52", FR: "48", DE: "48", JP: "19", KR: "105", AU: "20" },
|
|
427
|
+
"50": { US: "XXXL", UK: "22", EU: "50", IT: "54", FR: "50", DE: "50", JP: "21", KR: "110", AU: "22" }
|
|
428
|
+
};
|
|
409
429
|
const STEP_LABELS = ["", "Welcome", "Size", "Your Fit", "Try On"];
|
|
410
430
|
const TOTAL_STEPS = 4;
|
|
411
431
|
function detectLocale() {
|
|
@@ -424,9 +444,6 @@ function detectLocale() {
|
|
|
424
444
|
function isImperial(locale) {
|
|
425
445
|
return locale === "US" || locale === "UK";
|
|
426
446
|
}
|
|
427
|
-
function cmToIn(cm) {
|
|
428
|
-
return +(cm / 2.54).toFixed(1);
|
|
429
|
-
}
|
|
430
447
|
function inToCm(inches) {
|
|
431
448
|
return +(inches * 2.54).toFixed(1);
|
|
432
449
|
}
|
|
@@ -436,6 +453,22 @@ function lbsToKg(lbs) {
|
|
|
436
453
|
function ftInToCm(ft, inch) {
|
|
437
454
|
return +(ft * 30.48 + inch * 2.54).toFixed(1);
|
|
438
455
|
}
|
|
456
|
+
function SvgIcon({ d, size = 18, strokeWidth = 2 }) {
|
|
457
|
+
return /* @__PURE__ */ jsx(
|
|
458
|
+
"svg",
|
|
459
|
+
{
|
|
460
|
+
width: size,
|
|
461
|
+
height: size,
|
|
462
|
+
viewBox: "0 0 24 24",
|
|
463
|
+
fill: "none",
|
|
464
|
+
stroke: "currentColor",
|
|
465
|
+
strokeWidth,
|
|
466
|
+
strokeLinecap: "round",
|
|
467
|
+
strokeLinejoin: "round",
|
|
468
|
+
children: /* @__PURE__ */ jsx("path", { d })
|
|
469
|
+
}
|
|
470
|
+
);
|
|
471
|
+
}
|
|
439
472
|
function CameraIcon({ size = 18 }) {
|
|
440
473
|
return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
441
474
|
/* @__PURE__ */ jsx("path", { d: "M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z" }),
|
|
@@ -826,115 +859,7 @@ function PrimeStyleTryonInner({
|
|
|
826
859
|
}
|
|
827
860
|
return formGender === "female" ? FALLBACK_FIELDS_FEMALE : FALLBACK_FIELDS_MALE;
|
|
828
861
|
}, [sizeGuide, formGender]);
|
|
829
|
-
const computeSizingLocally = useCallback(() => {
|
|
830
|
-
if (sizingMethod !== "exact" || !sizeGuide?.found || !sizeGuide.headers || !sizeGuide.rows) return null;
|
|
831
|
-
const HEADER_MAP = {
|
|
832
|
-
chest: "chest",
|
|
833
|
-
bust: "bust",
|
|
834
|
-
waist: "waist",
|
|
835
|
-
hips: "hips",
|
|
836
|
-
hip: "hips",
|
|
837
|
-
shoulder: "shoulderWidth",
|
|
838
|
-
shoulders: "shoulderWidth",
|
|
839
|
-
"shoulder width": "shoulderWidth",
|
|
840
|
-
sleeve: "sleeveLength",
|
|
841
|
-
"sleeve length": "sleeveLength",
|
|
842
|
-
inseam: "inseam",
|
|
843
|
-
neck: "neckCircumference",
|
|
844
|
-
foot: "footLengthCm",
|
|
845
|
-
"foot length": "footLengthCm"
|
|
846
|
-
};
|
|
847
|
-
const INTL = /* @__PURE__ */ new Set(["eu", "us", "uk", "it", "fr", "de", "jp", "cn", "kr", "au", "br", "eur"]);
|
|
848
|
-
const userMeas = {};
|
|
849
|
-
for (const f of dynamicFields) {
|
|
850
|
-
if (f.unit === "size" || ["shoeEU", "shoeUS", "shoeUK"].includes(f.key)) continue;
|
|
851
|
-
const raw = formRef.current[f.key];
|
|
852
|
-
if (!raw) continue;
|
|
853
|
-
const v = parseFloat(raw);
|
|
854
|
-
if (isNaN(v)) continue;
|
|
855
|
-
userMeas[f.key] = sizingUnit === "in" ? inToCm(v) : v;
|
|
856
|
-
}
|
|
857
|
-
for (const k of ["chest", "bust", "waist", "hips", "shoulderWidth", "sleeveLength", "inseam", "neckCircumference", "footLengthCm"]) {
|
|
858
|
-
if (userMeas[k] || !formRef.current[k]) continue;
|
|
859
|
-
const v = parseFloat(formRef.current[k]);
|
|
860
|
-
if (!isNaN(v)) userMeas[k] = sizingUnit === "in" ? inToCm(v) : v;
|
|
861
|
-
}
|
|
862
|
-
if (Object.keys(userMeas).length === 0) return null;
|
|
863
|
-
let idx = sizeGuide.headers.findIndex((h) => /^size$/i.test(h.trim()));
|
|
864
|
-
if (idx < 0) idx = sizeGuide.headers.findIndex((h) => /size|taglia|größe|taille/i.test(h.trim()));
|
|
865
|
-
if (idx < 0) {
|
|
866
|
-
const firstColVals = sizeGuide.rows.map((r) => r[0]?.trim() || "");
|
|
867
|
-
idx = firstColVals.some((v) => /^(XXS|XS|S|M|L|XL|XXL|XXXL|\d{1,3})$/i.test(v)) ? 0 : 0;
|
|
868
|
-
}
|
|
869
|
-
const colMap = [];
|
|
870
|
-
const intlCols = [];
|
|
871
|
-
sizeGuide.headers.forEach((h, i) => {
|
|
872
|
-
if (i === idx) return;
|
|
873
|
-
const lower = h.toLowerCase().trim().replace(/\s*\(.*\)/, "");
|
|
874
|
-
const clean = lower.replace(/\s*size\s*/gi, "").trim();
|
|
875
|
-
if (INTL.has(clean)) {
|
|
876
|
-
intlCols.push({ hi: i, code: clean.toUpperCase() });
|
|
877
|
-
return;
|
|
878
|
-
}
|
|
879
|
-
let fk = HEADER_MAP[clean];
|
|
880
|
-
if (!fk) {
|
|
881
|
-
const pk = Object.keys(HEADER_MAP).find((k) => clean.includes(k));
|
|
882
|
-
if (pk) fk = HEADER_MAP[pk];
|
|
883
|
-
}
|
|
884
|
-
if (fk && userMeas[fk] !== void 0) colMap.push({ hi: i, formKey: fk, label: h.trim() });
|
|
885
|
-
});
|
|
886
|
-
if (colMap.length === 0) return null;
|
|
887
|
-
const parseRange = (cell) => {
|
|
888
|
-
const nums = cell.replace(/[^\d.\-–]/g, " ").trim().split(/[\s\-–]+/).filter(Boolean).map(Number).filter((n) => !isNaN(n));
|
|
889
|
-
if (nums.length === 0) return null;
|
|
890
|
-
return { min: Math.min(...nums), max: Math.max(...nums) };
|
|
891
|
-
};
|
|
892
|
-
const scores = [];
|
|
893
|
-
for (const row of sizeGuide.rows) {
|
|
894
|
-
const label = row[idx] || "";
|
|
895
|
-
let fitting = 0;
|
|
896
|
-
let total = 0;
|
|
897
|
-
let dist = 0;
|
|
898
|
-
const details = [];
|
|
899
|
-
for (const col of colMap) {
|
|
900
|
-
const range = parseRange(row[col.hi] || "");
|
|
901
|
-
if (!range) continue;
|
|
902
|
-
total++;
|
|
903
|
-
const uv = userMeas[col.formKey];
|
|
904
|
-
const fit = uv >= range.min && uv <= range.max ? "good" : uv < range.min ? "tight" : "loose";
|
|
905
|
-
if (fit === "good") fitting++;
|
|
906
|
-
dist += Math.abs(uv - (range.min + range.max) / 2);
|
|
907
|
-
const dispVal = sizingUnit === "in" ? `${cmToIn(uv)} in` : `${Math.round(uv)} cm`;
|
|
908
|
-
const dispRange = sizingUnit === "in" ? `${cmToIn(range.min)}–${cmToIn(range.max)} in` : `${Math.round(range.min)}–${Math.round(range.max)} cm`;
|
|
909
|
-
details.push({ measurement: col.label, userValue: dispVal, chartRange: dispRange, fit });
|
|
910
|
-
}
|
|
911
|
-
if (total === 0) continue;
|
|
912
|
-
const intl = {};
|
|
913
|
-
for (const ic of intlCols) {
|
|
914
|
-
if (row[ic.hi]) intl[ic.code] = row[ic.hi];
|
|
915
|
-
}
|
|
916
|
-
scores.push({ label, fitting, total, dist, details, intl, row });
|
|
917
|
-
}
|
|
918
|
-
if (scores.length === 0) return null;
|
|
919
|
-
scores.sort((a, b) => b.fitting - a.fitting || a.dist - b.dist);
|
|
920
|
-
const best = scores[0];
|
|
921
|
-
const conf = best.fitting === best.total ? "high" : best.fitting >= best.total * 0.6 ? "medium" : "low";
|
|
922
|
-
return {
|
|
923
|
-
recommendedSize: best.label,
|
|
924
|
-
confidence: conf,
|
|
925
|
-
reasoning: `Based on your measurements, size ${best.label} is the best fit.`,
|
|
926
|
-
internationalSizes: best.intl,
|
|
927
|
-
matchDetails: best.details,
|
|
928
|
-
method: "deterministic"
|
|
929
|
-
};
|
|
930
|
-
}, [sizingMethod, sizeGuide, dynamicFields, sizingUnit]);
|
|
931
862
|
const submitSizing = useCallback(async () => {
|
|
932
|
-
const localResult = computeSizingLocally();
|
|
933
|
-
if (localResult) {
|
|
934
|
-
setSizingResult(localResult);
|
|
935
|
-
setSizingLoading(false);
|
|
936
|
-
return;
|
|
937
|
-
}
|
|
938
863
|
if (!apiRef.current) return;
|
|
939
864
|
const baseUrl = getApiUrl(apiUrl);
|
|
940
865
|
const key = getApiKey();
|
|
@@ -993,7 +918,7 @@ function PrimeStyleTryonInner({
|
|
|
993
918
|
} finally {
|
|
994
919
|
setSizingLoading(false);
|
|
995
920
|
}
|
|
996
|
-
}, [apiUrl, sizingMethod, sizingCountry, heightUnit, weightUnit, sizingUnit, sizeGuide, productTitle, dynamicFields
|
|
921
|
+
}, [apiUrl, sizingMethod, sizingCountry, heightUnit, weightUnit, sizingUnit, sizeGuide, productTitle, dynamicFields]);
|
|
997
922
|
const handleTryOnSubmit = useCallback(async () => {
|
|
998
923
|
if (!selectedFile || !apiRef.current || !sseRef.current) {
|
|
999
924
|
const msg = !apiRef.current ? t("SDK not configured. Please provide an API key.") : t("Something went wrong");
|
|
@@ -1582,65 +1507,71 @@ function PrimeStyleTryonInner({
|
|
|
1582
1507
|
] }, `form-${formGender}-${sizingUnit}-${heightUnit}-${sizingCountry}-${formKey}`);
|
|
1583
1508
|
}
|
|
1584
1509
|
function SizeResultView() {
|
|
1585
|
-
const
|
|
1586
|
-
const
|
|
1587
|
-
const allSizes = useMemo(() => {
|
|
1588
|
-
if (!sizeGuide?.found || !sizeGuide.headers || !sizeGuide.rows) return [];
|
|
1589
|
-
const idx = sizeGuide.headers.findIndex((h) => /^size$/i.test(h.trim()));
|
|
1590
|
-
const col = idx >= 0 ? idx : 0;
|
|
1591
|
-
return sizeGuide.rows.map((r) => r[col]?.trim()).filter(Boolean);
|
|
1592
|
-
}, [sizeGuide]);
|
|
1593
|
-
const [compareSize, setCompareSize] = useState("");
|
|
1510
|
+
const unitLbl = sizingUnit === "cm" ? t("cm") : t("in");
|
|
1511
|
+
const [editing, setEditing] = useState(false);
|
|
1594
1512
|
const [editVals, setEditVals] = useState({});
|
|
1595
|
-
const
|
|
1513
|
+
const [compareSize, setCompareSize] = useState("");
|
|
1514
|
+
const recSize = sizingResult?.recommendedSize || "";
|
|
1515
|
+
const selSize = compareSize || recSize;
|
|
1516
|
+
const sizeColIdx = useMemo(() => {
|
|
1517
|
+
if (!sizeGuide?.headers || !sizeGuide?.rows) return -1;
|
|
1518
|
+
const byName = sizeGuide.headers.findIndex((h) => /size|taglia|größe|taille/i.test(h.trim()));
|
|
1519
|
+
if (byName >= 0) return byName;
|
|
1520
|
+
for (let c = 0; c < sizeGuide.headers.length; c++) {
|
|
1521
|
+
const vals = sizeGuide.rows.map((r) => r[c]?.trim() || "");
|
|
1522
|
+
if (vals.some((v) => /^(XXS|XS|S|M|L|XL|XXL|XXXL|ONE SIZE|\d{1,2})$/i.test(v))) return c;
|
|
1523
|
+
}
|
|
1524
|
+
return 0;
|
|
1525
|
+
}, [sizeGuide]);
|
|
1526
|
+
const allSizes = useMemo(() => {
|
|
1527
|
+
if (sizeColIdx < 0 || !sizeGuide?.rows) return [];
|
|
1528
|
+
return sizeGuide.rows.map((r) => r[sizeColIdx]?.trim()).filter(Boolean);
|
|
1529
|
+
}, [sizeGuide, sizeColIdx]);
|
|
1530
|
+
const pNum = (s) => {
|
|
1596
1531
|
const n = parseFloat(s.replace(/[^\d.]/g, ""));
|
|
1597
1532
|
return isNaN(n) ? 0 : n;
|
|
1598
1533
|
};
|
|
1599
|
-
const
|
|
1600
|
-
const
|
|
1601
|
-
|
|
1602
|
-
return { min: Math.min(...nums), max: Math.max(...nums) };
|
|
1534
|
+
const pRange = (s) => {
|
|
1535
|
+
const ns = s.replace(/[^\d.\-–]/g, " ").trim().split(/[\s\-–]+/).filter(Boolean).map(Number).filter((n) => !isNaN(n));
|
|
1536
|
+
return ns.length ? { min: Math.min(...ns), max: Math.max(...ns) } : { min: 0, max: 0 };
|
|
1603
1537
|
};
|
|
1604
|
-
const
|
|
1605
|
-
if (!sizeGuide?.
|
|
1606
|
-
const
|
|
1607
|
-
|
|
1608
|
-
const measCol = sizeGuide.headers.findIndex((h) => {
|
|
1609
|
-
const a = h.toLowerCase().trim();
|
|
1610
|
-
const b = measurement.toLowerCase().trim();
|
|
1538
|
+
const chartRangeFor = useCallback((measurement, size) => {
|
|
1539
|
+
if (!sizeGuide?.headers || !sizeGuide?.rows || sizeColIdx < 0) return null;
|
|
1540
|
+
const mc = sizeGuide.headers.findIndex((h) => {
|
|
1541
|
+
const a = h.toLowerCase().trim(), b = measurement.toLowerCase().trim();
|
|
1611
1542
|
return a === b || a.includes(b) || b.includes(a);
|
|
1612
1543
|
});
|
|
1613
|
-
if (
|
|
1614
|
-
const row = sizeGuide.rows.find((r) => r[
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
const parsed = parseRangeFromDetail(cell);
|
|
1618
|
-
return { range: cell, ...parsed };
|
|
1619
|
-
}, [sizeGuide]);
|
|
1544
|
+
if (mc < 0) return null;
|
|
1545
|
+
const row = sizeGuide.rows.find((r) => r[sizeColIdx]?.trim() === size);
|
|
1546
|
+
return row?.[mc] ? { range: row[mc], ...pRange(row[mc]) } : null;
|
|
1547
|
+
}, [sizeGuide, sizeColIdx]);
|
|
1620
1548
|
const fitRows = useMemo(() => {
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
const
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
let rangeMax = parseRangeFromDetail(m.chartRange).max;
|
|
1631
|
-
if (compareSize && compareSize !== sizingResult?.recommendedSize) {
|
|
1632
|
-
const alt = getChartRangeForSize(m.measurement, compareSize);
|
|
1549
|
+
if (!sizingResult?.matchDetails?.length) return [];
|
|
1550
|
+
return sizingResult.matchDetails.map((m) => {
|
|
1551
|
+
const origNum = pNum(m.userValue);
|
|
1552
|
+
const edited = editVals[m.measurement];
|
|
1553
|
+
const userNum = edited !== void 0 && edited !== "" ? parseFloat(edited) : origNum;
|
|
1554
|
+
let { min: rMin, max: rMax } = pRange(m.chartRange);
|
|
1555
|
+
let chartLabel = m.chartRange;
|
|
1556
|
+
if (compareSize && compareSize !== sizingResult.recommendedSize) {
|
|
1557
|
+
const alt = chartRangeFor(m.measurement, compareSize);
|
|
1633
1558
|
if (alt) {
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1559
|
+
chartLabel = alt.range;
|
|
1560
|
+
rMin = alt.min;
|
|
1561
|
+
rMax = alt.max;
|
|
1637
1562
|
}
|
|
1638
1563
|
}
|
|
1639
|
-
const fit = userNum >=
|
|
1640
|
-
return {
|
|
1564
|
+
const fit = userNum >= rMin && userNum <= rMax ? "good" : userNum < rMin ? "tight" : "loose";
|
|
1565
|
+
return { area: m.measurement, userNum, chartLabel, fit };
|
|
1641
1566
|
});
|
|
1642
|
-
}, [sizingResult, compareSize, editVals,
|
|
1643
|
-
const
|
|
1567
|
+
}, [sizingResult, compareSize, editVals, chartRangeFor]);
|
|
1568
|
+
const allIntlSizes = useMemo(() => {
|
|
1569
|
+
const backendIntl = sizingResult?.internationalSizes || {};
|
|
1570
|
+
const upper = recSize.toUpperCase().trim();
|
|
1571
|
+
const fromTable = SIZE_CONVERSIONS[upper] || SIZE_CONVERSIONS[recSize] || {};
|
|
1572
|
+
const merged = { ...fromTable, ...backendIntl };
|
|
1573
|
+
return merged;
|
|
1574
|
+
}, [sizingResult, recSize]);
|
|
1644
1575
|
return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr", children: [
|
|
1645
1576
|
sizingLoading && !sizingResult && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-loading", children: [
|
|
1646
1577
|
/* @__PURE__ */ jsx("div", { className: "ps-tryon-size-loading-spinner" }),
|
|
@@ -1648,69 +1579,80 @@ function PrimeStyleTryonInner({
|
|
|
1648
1579
|
] }),
|
|
1649
1580
|
sizingResult && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1650
1581
|
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-hero", children: [
|
|
1651
|
-
/* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-hero-badge", children:
|
|
1582
|
+
recSize && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-hero-badge", children: recSize }),
|
|
1652
1583
|
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-hero-info", children: [
|
|
1653
1584
|
/* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-hero-title", children: t("Your Size") }),
|
|
1654
1585
|
/* @__PURE__ */ jsx("div", { className: `ps-tryon-sr-hero-conf ps-conf-${sizingResult.confidence}`, children: sizingResult.confidence === "high" ? t("High Confidence") : sizingResult.confidence === "medium" ? t("Medium Confidence") : t("Low Confidence") })
|
|
1655
1586
|
] })
|
|
1656
1587
|
] }),
|
|
1657
|
-
Object.keys(
|
|
1658
|
-
/* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-label", children: t("
|
|
1659
|
-
/* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-intl-primary", children: Object.entries(
|
|
1588
|
+
Object.keys(allIntlSizes).length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-intl", children: [
|
|
1589
|
+
/* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-label", children: t("Your size in other countries") }),
|
|
1590
|
+
/* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-intl-primary", children: Object.entries(allIntlSizes).map(([code, val]) => /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-intl-card", children: [
|
|
1660
1591
|
/* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-card-val", children: val }),
|
|
1661
1592
|
/* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-intl-card-code", children: code })
|
|
1662
1593
|
] }, code)) })
|
|
1663
1594
|
] }),
|
|
1664
1595
|
allSizes.length > 1 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-compare", children: [
|
|
1665
1596
|
/* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-label", children: t("Compare with another size") }),
|
|
1666
|
-
/* @__PURE__ */ jsx(
|
|
1667
|
-
|
|
1668
|
-
{
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1597
|
+
/* @__PURE__ */ jsx("select", { className: "ps-tryon-sr-compare-select", value: selSize, onChange: (e) => setCompareSize(e.target.value), children: allSizes.map((s) => /* @__PURE__ */ jsxs("option", { value: s, children: [
|
|
1598
|
+
s,
|
|
1599
|
+
s === recSize ? ` ★ ${t("recommended")}` : ""
|
|
1600
|
+
] }, s)) }),
|
|
1601
|
+
compareSize && compareSize !== recSize && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-compare-note", children: [
|
|
1602
|
+
t("Showing fit for size"),
|
|
1603
|
+
" ",
|
|
1604
|
+
/* @__PURE__ */ jsx("strong", { children: compareSize }),
|
|
1605
|
+
/* @__PURE__ */ jsxs("button", { className: "ps-tryon-sr-compare-reset", onClick: () => setCompareSize(""), children: [
|
|
1606
|
+
t("Back to"),
|
|
1607
|
+
" ",
|
|
1608
|
+
recSize
|
|
1609
|
+
] })
|
|
1610
|
+
] })
|
|
1678
1611
|
] }),
|
|
1679
1612
|
fitRows.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit", children: [
|
|
1680
|
-
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-
|
|
1681
|
-
t("Fit Analysis"),
|
|
1682
|
-
|
|
1683
|
-
"
|
|
1684
|
-
t("
|
|
1685
|
-
" ",
|
|
1686
|
-
compareSize
|
|
1613
|
+
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-header-row", children: [
|
|
1614
|
+
/* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-label", children: t("Fit Analysis") }),
|
|
1615
|
+
/* @__PURE__ */ jsxs("button", { className: "ps-tryon-sr-edit-btn", onClick: () => setEditing(!editing), children: [
|
|
1616
|
+
/* @__PURE__ */ jsx(SvgIcon, { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7", size: 12 }),
|
|
1617
|
+
editing ? t("Done") : t("Edit values")
|
|
1687
1618
|
] })
|
|
1688
1619
|
] }),
|
|
1689
|
-
/* @__PURE__ */ jsxs("div", { className:
|
|
1690
|
-
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-
|
|
1691
|
-
/* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-
|
|
1692
|
-
/* @__PURE__ */ jsx("span", { className:
|
|
1693
|
-
/* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-chart", children: t("Chart") }),
|
|
1694
|
-
/* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-col-fit", children: t("Fit") })
|
|
1620
|
+
fitRows.map((row, i) => /* @__PURE__ */ jsxs("div", { className: `ps-tryon-sr-fit-card ps-fit-${row.fit}`, children: [
|
|
1621
|
+
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-card-top", children: [
|
|
1622
|
+
/* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-area", children: row.area }),
|
|
1623
|
+
/* @__PURE__ */ jsx("span", { className: `ps-tryon-sr-fit-badge ps-fit-${row.fit}`, children: row.fit === "good" ? `✓ ${t("within range")}` : row.fit === "tight" ? `↑ ${t("may be snug")}` : `↓ ${t("may be loose")}` })
|
|
1695
1624
|
] }),
|
|
1696
|
-
|
|
1697
|
-
/* @__PURE__ */
|
|
1698
|
-
|
|
1699
|
-
/* @__PURE__ */
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1625
|
+
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-card-bottom", children: [
|
|
1626
|
+
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-val", children: [
|
|
1627
|
+
/* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-val-label", children: t("You") }),
|
|
1628
|
+
editing ? /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-val-input-wrap", children: [
|
|
1629
|
+
/* @__PURE__ */ jsx(
|
|
1630
|
+
"input",
|
|
1631
|
+
{
|
|
1632
|
+
type: "number",
|
|
1633
|
+
className: "ps-tryon-sr-fit-input",
|
|
1634
|
+
value: editVals[row.area] !== void 0 ? editVals[row.area] : row.userNum,
|
|
1635
|
+
onChange: (e) => setEditVals((prev) => ({ ...prev, [row.area]: e.target.value }))
|
|
1636
|
+
}
|
|
1637
|
+
),
|
|
1638
|
+
/* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-unit", children: unitLbl })
|
|
1639
|
+
] }) : /* @__PURE__ */ jsxs("span", { className: "ps-tryon-sr-fit-val-text", children: [
|
|
1640
|
+
row.userNum,
|
|
1641
|
+
" ",
|
|
1642
|
+
unitLbl
|
|
1643
|
+
] })
|
|
1709
1644
|
] }),
|
|
1710
|
-
/* @__PURE__ */
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1645
|
+
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-fit-val", children: [
|
|
1646
|
+
/* @__PURE__ */ jsxs("span", { className: "ps-tryon-sr-fit-val-label", children: [
|
|
1647
|
+
t("Chart"),
|
|
1648
|
+
" (",
|
|
1649
|
+
selSize,
|
|
1650
|
+
")"
|
|
1651
|
+
] }),
|
|
1652
|
+
/* @__PURE__ */ jsx("span", { className: "ps-tryon-sr-fit-val-text", children: row.chartLabel })
|
|
1653
|
+
] })
|
|
1654
|
+
] })
|
|
1655
|
+
] }, i))
|
|
1714
1656
|
] }),
|
|
1715
1657
|
fitRows.length === 0 && sizingResult.reasoning && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-reasoning", children: /* @__PURE__ */ jsx("p", { children: sizingResult.reasoning }) }),
|
|
1716
1658
|
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-ctas", children: [
|
|
@@ -1995,7 +1937,7 @@ const STYLES = `
|
|
|
1995
1937
|
.ps-tryon-modal {
|
|
1996
1938
|
background: var(--ps-modal-bg, #111211); color: var(--ps-modal-color, #fff);
|
|
1997
1939
|
border-radius: var(--ps-modal-radius, 0.83vw); width: var(--ps-modal-width, 100%);
|
|
1998
|
-
max-width: var(--ps-modal-max-width,
|
|
1940
|
+
max-width: var(--ps-modal-max-width, 32vw); max-height: 92vh; overflow-y: auto;
|
|
1999
1941
|
font-family: var(--ps-modal-font, system-ui, -apple-system, sans-serif);
|
|
2000
1942
|
box-shadow: 0 1.3vw 2.6vw rgba(0,0,0,0.4); animation: ps-slide-up 0.3s ease;
|
|
2001
1943
|
scrollbar-width: thin; scrollbar-color: #333 transparent;
|
|
@@ -2463,44 +2405,60 @@ const STYLES = `
|
|
|
2463
2405
|
.ps-tryon-sr-comparing { font-size: 0.73vw; color: #bb945c; margin-top: 0.42vw; }
|
|
2464
2406
|
.ps-tryon-sr-comparing strong { color: #d6ba7d; }
|
|
2465
2407
|
|
|
2466
|
-
/* Fit analysis
|
|
2467
|
-
.ps-tryon-sr-fit { }
|
|
2468
|
-
.ps-tryon-sr-fit-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
}
|
|
2479
|
-
.ps-tryon-sr-fit-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
.ps-tryon-sr-fit-
|
|
2483
|
-
.ps-tryon-sr-fit-
|
|
2484
|
-
|
|
2485
|
-
.ps-tryon-sr-fit-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2408
|
+
/* Fit analysis cards */
|
|
2409
|
+
.ps-tryon-sr-fit { display: flex; flex-direction: column; gap: 0.52vw; }
|
|
2410
|
+
.ps-tryon-sr-fit-header-row {
|
|
2411
|
+
display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.1vw;
|
|
2412
|
+
}
|
|
2413
|
+
.ps-tryon-sr-edit-btn {
|
|
2414
|
+
display: inline-flex; align-items: center; gap: 0.31vw;
|
|
2415
|
+
padding: 0.26vw 0.63vw; border: 1.5px solid #bb945c; border-radius: 0.36vw;
|
|
2416
|
+
background: transparent; color: #bb945c; font-size: 0.63vw; font-weight: 600;
|
|
2417
|
+
cursor: pointer; font-family: inherit; transition: all 0.2s;
|
|
2418
|
+
}
|
|
2419
|
+
.ps-tryon-sr-edit-btn:hover { background: rgba(187,148,92,0.1); }
|
|
2420
|
+
.ps-tryon-sr-edit-btn svg { stroke: currentColor; }
|
|
2421
|
+
.ps-tryon-sr-fit-card {
|
|
2422
|
+
border: 1.5px solid #333; border-radius: 0.63vw; overflow: hidden; transition: border-color 0.2s;
|
|
2423
|
+
}
|
|
2424
|
+
.ps-tryon-sr-fit-card.ps-fit-good { border-left: 3px solid #4ade80; }
|
|
2425
|
+
.ps-tryon-sr-fit-card.ps-fit-tight { border-left: 3px solid #f59e0b; }
|
|
2426
|
+
.ps-tryon-sr-fit-card.ps-fit-loose { border-left: 3px solid #60a5fa; }
|
|
2427
|
+
.ps-tryon-sr-fit-card-top {
|
|
2428
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
2429
|
+
padding: 0.52vw 0.83vw; background: #1a1b1a;
|
|
2430
|
+
}
|
|
2431
|
+
.ps-tryon-sr-fit-area { font-size: 0.83vw; font-weight: 600; color: #fff; }
|
|
2432
|
+
.ps-tryon-sr-fit-card-bottom {
|
|
2433
|
+
display: flex; gap: 1vw; padding: 0.52vw 0.83vw;
|
|
2434
|
+
}
|
|
2435
|
+
.ps-tryon-sr-fit-val { flex: 1; }
|
|
2436
|
+
.ps-tryon-sr-fit-val-label { font-size: 0.57vw; font-weight: 700; color: #666; text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 0.21vw; display: block; }
|
|
2437
|
+
.ps-tryon-sr-fit-val-input-wrap { display: flex; align-items: center; gap: 0.26vw; }
|
|
2438
|
+
.ps-tryon-sr-fit-val-text { font-size: 0.83vw; font-weight: 600; color: #ccc; }
|
|
2489
2439
|
|
|
2490
2440
|
.ps-tryon-sr-fit-input {
|
|
2491
|
-
width:
|
|
2492
|
-
background: #
|
|
2441
|
+
width: 4vw; padding: 0.31vw 0.42vw; border: 1.5px solid #444; border-radius: 0.36vw;
|
|
2442
|
+
background: #0c0c0d; color: #fff; font-size: 0.83vw; font-weight: 600; font-family: inherit;
|
|
2493
2443
|
outline: none; text-align: center; -moz-appearance: textfield;
|
|
2494
2444
|
}
|
|
2495
2445
|
.ps-tryon-sr-fit-input::-webkit-outer-spin-button,
|
|
2496
2446
|
.ps-tryon-sr-fit-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
|
|
2497
|
-
.ps-tryon-sr-fit-input:focus { border-color: #bb945c; }
|
|
2498
|
-
.ps-tryon-sr-fit-unit { font-size: 0.
|
|
2447
|
+
.ps-tryon-sr-fit-input:focus { border-color: #bb945c; background: #1a1b1a; }
|
|
2448
|
+
.ps-tryon-sr-fit-unit { font-size: 0.68vw; color: #666; }
|
|
2449
|
+
|
|
2450
|
+
.ps-tryon-sr-fit-badge {
|
|
2451
|
+
display: inline-flex; align-items: center; gap: 0.26vw; padding: 0.26vw 0.57vw;
|
|
2452
|
+
border-radius: 0.36vw; font-size: 0.68vw; font-weight: 700; white-space: nowrap;
|
|
2453
|
+
}
|
|
2454
|
+
.ps-tryon-sr-fit-badge.ps-fit-good { background: rgba(74,222,128,0.1); color: #4ade80; }
|
|
2455
|
+
.ps-tryon-sr-fit-badge.ps-fit-tight { background: rgba(245,158,11,0.1); color: #f59e0b; }
|
|
2456
|
+
.ps-tryon-sr-fit-badge.ps-fit-loose { background: rgba(96,165,250,0.1); color: #60a5fa; }
|
|
2499
2457
|
|
|
2500
2458
|
/* Compare dropdown */
|
|
2501
2459
|
.ps-tryon-sr-compare { }
|
|
2502
2460
|
.ps-tryon-sr-compare-select {
|
|
2503
|
-
width: 100%; padding: 0.
|
|
2461
|
+
width: 100%; padding: 0.57vw 2vw 0.57vw 0.83vw; border: 1.5px solid #333; border-radius: 0.57vw;
|
|
2504
2462
|
background: #1a1b1a; color: #fff; font-size: 0.83vw; font-weight: 600; font-family: inherit;
|
|
2505
2463
|
appearance: none; -webkit-appearance: none; cursor: pointer; outline: none;
|
|
2506
2464
|
background-image: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='%23999' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
|
@@ -2508,6 +2466,15 @@ const STYLES = `
|
|
|
2508
2466
|
}
|
|
2509
2467
|
.ps-tryon-sr-compare-select:focus { border-color: #bb945c; }
|
|
2510
2468
|
.ps-tryon-sr-compare-select option { background: #1a1b1a; color: #fff; }
|
|
2469
|
+
.ps-tryon-sr-compare-note {
|
|
2470
|
+
font-size: 0.73vw; color: #bb945c; margin-top: 0.42vw; display: flex; align-items: center; gap: 0.52vw;
|
|
2471
|
+
}
|
|
2472
|
+
.ps-tryon-sr-compare-reset {
|
|
2473
|
+
background: none; border: 1px solid #bb945c; color: #bb945c; padding: 0.16vw 0.52vw;
|
|
2474
|
+
border-radius: 0.31vw; font-size: 0.63vw; font-weight: 600; cursor: pointer;
|
|
2475
|
+
font-family: inherit; transition: all 0.2s;
|
|
2476
|
+
}
|
|
2477
|
+
.ps-tryon-sr-compare-reset:hover { background: rgba(187,148,92,0.1); }
|
|
2511
2478
|
|
|
2512
2479
|
.ps-tryon-sr-fit-badge {
|
|
2513
2480
|
display: inline-flex; align-items: center; gap: 0.26vw; padding: 0.21vw 0.52vw;
|