@thefittingroom/shop-ui 5.0.26 → 5.0.28
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.
- package/dist/index.js +442 -138
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14100,6 +14100,40 @@ function _init$7() {
|
|
|
14100
14100
|
useMainStore.setState({
|
|
14101
14101
|
fittingRoom: items
|
|
14102
14102
|
});
|
|
14103
|
+
if (typeof window !== "undefined") {
|
|
14104
|
+
window.addEventListener("storage", (e) => {
|
|
14105
|
+
if (e.key !== STORAGE_KEY) {
|
|
14106
|
+
return;
|
|
14107
|
+
}
|
|
14108
|
+
useMainStore.setState({
|
|
14109
|
+
fittingRoom: readFittingRoom(brandId)
|
|
14110
|
+
});
|
|
14111
|
+
});
|
|
14112
|
+
}
|
|
14113
|
+
}
|
|
14114
|
+
function resolveCurrentProductCsaId(productId, size, color) {
|
|
14115
|
+
if (size == null) {
|
|
14116
|
+
return null;
|
|
14117
|
+
}
|
|
14118
|
+
const {
|
|
14119
|
+
currentProduct
|
|
14120
|
+
} = getStaticData();
|
|
14121
|
+
if (!currentProduct) {
|
|
14122
|
+
return null;
|
|
14123
|
+
}
|
|
14124
|
+
const stored = useMainStore.getState().productData[productId];
|
|
14125
|
+
if (!stored || "error" in stored) {
|
|
14126
|
+
return null;
|
|
14127
|
+
}
|
|
14128
|
+
const sizeRec = stored.sizeFitRecommendation.available_sizes.find((s) => getSizeLabelFromSize(s) === size);
|
|
14129
|
+
if (!sizeRec) {
|
|
14130
|
+
return null;
|
|
14131
|
+
}
|
|
14132
|
+
const csa = sizeRec.colorway_size_assets.find((c) => {
|
|
14133
|
+
const variant = currentProduct.variants.find((v) => v.sku === c.sku);
|
|
14134
|
+
return variant?.color === color;
|
|
14135
|
+
});
|
|
14136
|
+
return csa?.id ?? null;
|
|
14103
14137
|
}
|
|
14104
14138
|
async function addFittingRoomItem(productId, handle, isPdp) {
|
|
14105
14139
|
const state = useMainStore.getState();
|
|
@@ -14108,9 +14142,7 @@ async function addFittingRoomItem(productId, handle, isPdp) {
|
|
|
14108
14142
|
handle,
|
|
14109
14143
|
isPdp
|
|
14110
14144
|
});
|
|
14111
|
-
let size = null;
|
|
14112
14145
|
let color = null;
|
|
14113
|
-
let colorwaySizeAssetId = null;
|
|
14114
14146
|
let resolvedHandle = handle;
|
|
14115
14147
|
if (isPdp) {
|
|
14116
14148
|
const {
|
|
@@ -14122,37 +14154,59 @@ async function addFittingRoomItem(productId, handle, isPdp) {
|
|
|
14122
14154
|
}
|
|
14123
14155
|
try {
|
|
14124
14156
|
const selection = await currentProduct.getSelectedOptions();
|
|
14125
|
-
size = selection.size || null;
|
|
14126
14157
|
color = selection.color;
|
|
14127
14158
|
} catch (error) {
|
|
14128
14159
|
logger$g.logWarn("Failed to read selected options from currentProduct", {
|
|
14129
14160
|
error
|
|
14130
14161
|
});
|
|
14131
14162
|
}
|
|
14132
|
-
const stored = state.productData[productId];
|
|
14133
|
-
if (stored && !("error" in stored) && size != null) {
|
|
14134
|
-
const sizeRec = stored.sizeFitRecommendation.available_sizes.find((s) => getSizeLabelFromSize(s) === size);
|
|
14135
|
-
if (sizeRec) {
|
|
14136
|
-
const csa = sizeRec.colorway_size_assets.find((c) => {
|
|
14137
|
-
const variant = currentProduct.variants.find((v) => v.sku === c.sku);
|
|
14138
|
-
return variant?.color === color;
|
|
14139
|
-
});
|
|
14140
|
-
if (csa) {
|
|
14141
|
-
colorwaySizeAssetId = csa.id;
|
|
14142
|
-
}
|
|
14143
|
-
}
|
|
14144
|
-
}
|
|
14145
14163
|
}
|
|
14146
14164
|
}
|
|
14147
14165
|
state.addToFittingRoom({
|
|
14148
14166
|
externalId: productId,
|
|
14149
14167
|
handle: resolvedHandle,
|
|
14150
|
-
size,
|
|
14168
|
+
size: null,
|
|
14151
14169
|
color,
|
|
14152
|
-
colorwaySizeAssetId,
|
|
14170
|
+
colorwaySizeAssetId: null,
|
|
14153
14171
|
addedAt: Date.now()
|
|
14154
14172
|
});
|
|
14155
14173
|
}
|
|
14174
|
+
async function syncCurrentProductSelection() {
|
|
14175
|
+
const {
|
|
14176
|
+
currentProduct
|
|
14177
|
+
} = getStaticData();
|
|
14178
|
+
if (!currentProduct) {
|
|
14179
|
+
return;
|
|
14180
|
+
}
|
|
14181
|
+
const state = useMainStore.getState();
|
|
14182
|
+
const existing = state.fittingRoom.find((item) => item.externalId === currentProduct.externalId);
|
|
14183
|
+
if (!existing) {
|
|
14184
|
+
return;
|
|
14185
|
+
}
|
|
14186
|
+
let color = null;
|
|
14187
|
+
try {
|
|
14188
|
+
const selection = await currentProduct.getSelectedOptions();
|
|
14189
|
+
color = selection.color;
|
|
14190
|
+
} catch (error) {
|
|
14191
|
+
logger$g.logWarn("syncCurrentProductSelection: failed to read selected options", {
|
|
14192
|
+
error
|
|
14193
|
+
});
|
|
14194
|
+
return;
|
|
14195
|
+
}
|
|
14196
|
+
if (color === existing.color) {
|
|
14197
|
+
return;
|
|
14198
|
+
}
|
|
14199
|
+
const colorwaySizeAssetId = resolveCurrentProductCsaId(currentProduct.externalId, existing.size, color);
|
|
14200
|
+
state.updateFittingRoomItem(currentProduct.externalId, {
|
|
14201
|
+
color,
|
|
14202
|
+
colorwaySizeAssetId
|
|
14203
|
+
});
|
|
14204
|
+
logger$g.logDebug("{{ts}} - Synced PDP color into fitting-room", {
|
|
14205
|
+
externalId: currentProduct.externalId,
|
|
14206
|
+
color,
|
|
14207
|
+
colorwaySizeAssetId
|
|
14208
|
+
});
|
|
14209
|
+
}
|
|
14156
14210
|
async function toggleFittingRoomItem(productId, handle, isPdp) {
|
|
14157
14211
|
const state = useMainStore.getState();
|
|
14158
14212
|
const isInFittingRoom = state.fittingRoom.some((item) => item.externalId === productId);
|
|
@@ -19304,7 +19358,7 @@ const loading$1 = "Loading...";
|
|
|
19304
19358
|
const add_to_fitting_room$1 = "Add to Fitting Room";
|
|
19305
19359
|
const added_to_fitting_room$1 = "In Fitting Room";
|
|
19306
19360
|
const view_fitting_room$1 = "Fitting Room";
|
|
19307
|
-
const fitting_room$1 = { "title": "Fitting Room", "empty": "Your fitting room is empty.", "size_not_chosen": "Size not chosen — pick one before trying on.", "remove": "Remove", "ungrouped": "Other", "try_it_on": "Try it on", "view_product_details": "View product details", "hide_product_details": "Hide product details", "add_to_cart": "Add to cart", "recommended_sizes": "Recommended sizes", "shop_now": "Shop now", "sign_out": "Sign out", "try_it_tucked_in": "Try it tucked in", "try_it_untucked": "Try it untucked", "avatar_placeholder_empty": "Add clothes to try on by selecting items in the rails.", "avatar_placeholder_selected": "Avatar render will appear here once VTO is wired up.", "see_selected_items": "See Selected Items", "no_selection": "Nothing selected yet.", "tuck_in": "Tuck In", "untuck": "Untuck", "zoom_in": "Zoom In", "zoom_out": "Zoom Out", "vto_error": "Couldn't load the try-on. Please try again." };
|
|
19361
|
+
const fitting_room$1 = { "title": "Fitting Room", "empty": "Your fitting room is empty.", "size_not_chosen": "Size not chosen — pick one before trying on.", "remove": "Remove", "ungrouped": "Other", "try_it_on": "Try it on", "view_product_details": "View product details", "hide_product_details": "Hide product details", "add_to_cart": "Add to cart", "recommended_sizes": "Recommended sizes", "shop_now": "Shop now", "sign_out": "Sign out", "clear_all": "Clear All", "try_it_tucked_in": "Try it tucked in", "try_it_untucked": "Try it untucked", "avatar_placeholder_empty": "Add clothes to try on by selecting items in the rails.", "avatar_placeholder_selected": "Avatar render will appear here once VTO is wired up.", "see_selected_items": "See Selected Items", "no_selection": "Nothing selected yet.", "tuck_in": "Tuck In", "untuck": "Untuck", "zoom_in": "Zoom In", "zoom_out": "Zoom Out", "vto_error": "Couldn't load the try-on. Please try again." };
|
|
19308
19362
|
const landing$1 = { "header": "Meet your mini me!", "description": "Loose on your waist but tight on your hips? We’ll show you.", "get_the_app": "Get the app", "already_have_account": "Already have an account?", "sign_in": "Sign in." };
|
|
19309
19363
|
const en$1 = {
|
|
19310
19364
|
try_it_on: try_it_on$1,
|
|
@@ -41378,7 +41432,7 @@ async function _init$4() {
|
|
|
41378
41432
|
config,
|
|
41379
41433
|
testHooks
|
|
41380
41434
|
} = getStaticData();
|
|
41381
|
-
if (testHooks !== void 0) {
|
|
41435
|
+
if (testHooks && (testHooks.auth !== void 0 || testHooks.firestore !== void 0)) {
|
|
41382
41436
|
const seedDocs = {
|
|
41383
41437
|
...testHooks.firestore?.docs ?? {}
|
|
41384
41438
|
};
|
|
@@ -42201,7 +42255,12 @@ function AvatarControls({
|
|
|
42201
42255
|
// clip rendered the text at fractional opacity while it reflowed each
|
|
42202
42256
|
// frame, which subpixel-shimmered. The max-width clip alone reveals
|
|
42203
42257
|
// the label cleanly at full opacity.
|
|
42204
|
-
transition: "max-width 500ms cubic-bezier(0.22, 1, 0.36, 1)"
|
|
42258
|
+
transition: "max-width 500ms cubic-bezier(0.22, 1, 0.36, 1)",
|
|
42259
|
+
// Belt-and-suspenders with pillBaseStyle on the parent — rapid clicks
|
|
42260
|
+
// on the avatar's rotation chevrons can otherwise extend a triple-
|
|
42261
|
+
// click selection into these labels.
|
|
42262
|
+
userSelect: "none",
|
|
42263
|
+
WebkitUserSelect: "none"
|
|
42205
42264
|
},
|
|
42206
42265
|
pillLabelCollapsed: {
|
|
42207
42266
|
maxWidth: 0
|
|
@@ -42317,37 +42376,43 @@ function MobileTuckControl({
|
|
|
42317
42376
|
] }) });
|
|
42318
42377
|
}
|
|
42319
42378
|
const DRAG_STEP_PX = 50;
|
|
42320
|
-
|
|
42379
|
+
const AXIS_LOCK_PX$1 = 8;
|
|
42380
|
+
function applyDragSteps(deltaX, rotateLeft, rotateRight) {
|
|
42381
|
+
const steps = Math.floor(Math.abs(deltaX) / DRAG_STEP_PX);
|
|
42382
|
+
if (steps === 0) {
|
|
42383
|
+
return 0;
|
|
42384
|
+
}
|
|
42385
|
+
const fn = deltaX > 0 ? rotateRight : rotateLeft;
|
|
42386
|
+
for (let i = 0; i < steps; i++) {
|
|
42387
|
+
fn();
|
|
42388
|
+
}
|
|
42389
|
+
return (deltaX > 0 ? 1 : -1) * steps * DRAG_STEP_PX;
|
|
42390
|
+
}
|
|
42391
|
+
function useFrameRotation(frameUrls, setSelectedFrameIndex, onUserInteract) {
|
|
42321
42392
|
const frameCount = frameUrls?.length ?? 0;
|
|
42322
42393
|
const rotateLeft = reactExports.useCallback(() => {
|
|
42394
|
+
onUserInteract?.();
|
|
42323
42395
|
setSelectedFrameIndex((prev2) => {
|
|
42324
42396
|
if (prev2 == null || frameCount === 0) {
|
|
42325
42397
|
return prev2;
|
|
42326
42398
|
}
|
|
42327
42399
|
return prev2 === 0 ? frameCount - 1 : prev2 - 1;
|
|
42328
42400
|
});
|
|
42329
|
-
}, [frameCount, setSelectedFrameIndex]);
|
|
42401
|
+
}, [frameCount, onUserInteract, setSelectedFrameIndex]);
|
|
42330
42402
|
const rotateRight = reactExports.useCallback(() => {
|
|
42403
|
+
onUserInteract?.();
|
|
42331
42404
|
setSelectedFrameIndex((prev2) => {
|
|
42332
42405
|
if (prev2 == null || frameCount === 0) {
|
|
42333
42406
|
return prev2;
|
|
42334
42407
|
}
|
|
42335
42408
|
return prev2 === frameCount - 1 ? 0 : prev2 + 1;
|
|
42336
42409
|
});
|
|
42337
|
-
}, [frameCount, setSelectedFrameIndex]);
|
|
42410
|
+
}, [frameCount, onUserInteract, setSelectedFrameIndex]);
|
|
42338
42411
|
const handleMouseDragStart = reactExports.useCallback((e) => {
|
|
42339
42412
|
e.preventDefault();
|
|
42340
42413
|
let startX = e.clientX;
|
|
42341
42414
|
const onMove = (move) => {
|
|
42342
|
-
|
|
42343
|
-
if (Math.abs(deltaX) >= DRAG_STEP_PX) {
|
|
42344
|
-
if (deltaX > 0) {
|
|
42345
|
-
rotateRight();
|
|
42346
|
-
} else {
|
|
42347
|
-
rotateLeft();
|
|
42348
|
-
}
|
|
42349
|
-
startX = move.clientX;
|
|
42350
|
-
}
|
|
42415
|
+
startX += applyDragSteps(move.clientX - startX, rotateLeft, rotateRight);
|
|
42351
42416
|
};
|
|
42352
42417
|
const onUp = () => {
|
|
42353
42418
|
window.removeEventListener("mousemove", onMove);
|
|
@@ -42357,25 +42422,37 @@ function useFrameRotation(frameUrls, setSelectedFrameIndex) {
|
|
|
42357
42422
|
window.addEventListener("mouseup", onUp);
|
|
42358
42423
|
}, [rotateLeft, rotateRight]);
|
|
42359
42424
|
const handleTouchDragStart = reactExports.useCallback((e) => {
|
|
42360
|
-
e.preventDefault();
|
|
42361
42425
|
let startX = e.touches[0].clientX;
|
|
42426
|
+
const startY = e.touches[0].clientY;
|
|
42427
|
+
let mode = "unknown";
|
|
42362
42428
|
const onMove = (move) => {
|
|
42363
|
-
const
|
|
42364
|
-
|
|
42365
|
-
|
|
42366
|
-
|
|
42367
|
-
|
|
42368
|
-
|
|
42429
|
+
const currentX = move.touches[0].clientX;
|
|
42430
|
+
const currentY = move.touches[0].clientY;
|
|
42431
|
+
if (mode === "unknown") {
|
|
42432
|
+
const absDx = Math.abs(currentX - startX);
|
|
42433
|
+
const absDy = Math.abs(currentY - startY);
|
|
42434
|
+
if (absDx < AXIS_LOCK_PX$1 && absDy < AXIS_LOCK_PX$1) {
|
|
42435
|
+
return;
|
|
42369
42436
|
}
|
|
42370
|
-
|
|
42437
|
+
mode = absDx >= absDy ? "horizontal" : "vertical";
|
|
42438
|
+
startX = currentX;
|
|
42439
|
+
}
|
|
42440
|
+
if (mode !== "horizontal") {
|
|
42441
|
+
return;
|
|
42371
42442
|
}
|
|
42443
|
+
move.preventDefault();
|
|
42444
|
+
startX += applyDragSteps(currentX - startX, rotateLeft, rotateRight);
|
|
42372
42445
|
};
|
|
42373
42446
|
const onEnd = () => {
|
|
42374
42447
|
window.removeEventListener("touchmove", onMove);
|
|
42375
42448
|
window.removeEventListener("touchend", onEnd);
|
|
42449
|
+
window.removeEventListener("touchcancel", onEnd);
|
|
42376
42450
|
};
|
|
42377
|
-
window.addEventListener("touchmove", onMove
|
|
42451
|
+
window.addEventListener("touchmove", onMove, {
|
|
42452
|
+
passive: false
|
|
42453
|
+
});
|
|
42378
42454
|
window.addEventListener("touchend", onEnd);
|
|
42455
|
+
window.addEventListener("touchcancel", onEnd);
|
|
42379
42456
|
}, [rotateLeft, rotateRight]);
|
|
42380
42457
|
return {
|
|
42381
42458
|
rotateLeft,
|
|
@@ -42390,7 +42467,8 @@ function AvatarFrameViewer({
|
|
|
42390
42467
|
setSelectedFrameIndex,
|
|
42391
42468
|
imageContainerStyle,
|
|
42392
42469
|
imageStyle,
|
|
42393
|
-
loadingT = "quick-view.avatar_loading"
|
|
42470
|
+
loadingT = "quick-view.avatar_loading",
|
|
42471
|
+
onUserInteract
|
|
42394
42472
|
}) {
|
|
42395
42473
|
const css2 = useCss((_theme) => ({
|
|
42396
42474
|
imageContainer: {
|
|
@@ -42398,21 +42476,31 @@ function AvatarFrameViewer({
|
|
|
42398
42476
|
},
|
|
42399
42477
|
image: {
|
|
42400
42478
|
objectFit: "contain",
|
|
42401
|
-
cursor: "grab"
|
|
42479
|
+
cursor: "grab",
|
|
42480
|
+
// Reserve horizontal touch gestures for our rotation handler — the
|
|
42481
|
+
// browser can still pan vertically natively. Without this, the
|
|
42482
|
+
// browser may start consuming a horizontal swipe as a scroll/zoom
|
|
42483
|
+
// before our touchmove listener can preventDefault, which produced
|
|
42484
|
+
// the "janky first swipe" reports.
|
|
42485
|
+
touchAction: "pan-y"
|
|
42402
42486
|
},
|
|
42403
42487
|
chevronLeftContainer: {
|
|
42404
42488
|
position: "absolute",
|
|
42405
42489
|
top: "50%",
|
|
42406
42490
|
left: "0",
|
|
42407
42491
|
transform: "translateY(-50%)",
|
|
42408
|
-
cursor: "pointer"
|
|
42492
|
+
cursor: "pointer",
|
|
42493
|
+
userSelect: "none",
|
|
42494
|
+
WebkitUserSelect: "none"
|
|
42409
42495
|
},
|
|
42410
42496
|
chevronRightContainer: {
|
|
42411
42497
|
position: "absolute",
|
|
42412
42498
|
top: "50%",
|
|
42413
42499
|
right: "0",
|
|
42414
42500
|
transform: "translateY(-50%)",
|
|
42415
|
-
cursor: "pointer"
|
|
42501
|
+
cursor: "pointer",
|
|
42502
|
+
userSelect: "none",
|
|
42503
|
+
WebkitUserSelect: "none"
|
|
42416
42504
|
},
|
|
42417
42505
|
chevronIcon: {
|
|
42418
42506
|
width: "48px",
|
|
@@ -42420,46 +42508,83 @@ function AvatarFrameViewer({
|
|
|
42420
42508
|
}
|
|
42421
42509
|
}));
|
|
42422
42510
|
reactExports.useEffect(() => {
|
|
42423
|
-
if (
|
|
42424
|
-
|
|
42511
|
+
if (frameUrls && frameUrls.length > 0 && selectedFrameIndex == null) {
|
|
42512
|
+
setSelectedFrameIndex(0);
|
|
42425
42513
|
}
|
|
42426
|
-
let currentFrameIndex = 0;
|
|
42427
|
-
setSelectedFrameIndex(currentFrameIndex);
|
|
42428
|
-
const intervalId = setInterval(() => {
|
|
42429
|
-
currentFrameIndex = (currentFrameIndex + 1) % frameUrls.length;
|
|
42430
|
-
setSelectedFrameIndex(currentFrameIndex);
|
|
42431
|
-
if (currentFrameIndex === 0) {
|
|
42432
|
-
clearInterval(intervalId);
|
|
42433
|
-
}
|
|
42434
|
-
}, 500);
|
|
42435
|
-
return () => clearInterval(intervalId);
|
|
42436
42514
|
}, [frameUrls, selectedFrameIndex, setSelectedFrameIndex]);
|
|
42437
42515
|
const {
|
|
42438
42516
|
rotateLeft,
|
|
42439
42517
|
rotateRight,
|
|
42440
42518
|
handleMouseDragStart,
|
|
42441
42519
|
handleTouchDragStart
|
|
42442
|
-
} = useFrameRotation(frameUrls, setSelectedFrameIndex);
|
|
42520
|
+
} = useFrameRotation(frameUrls, setSelectedFrameIndex, onUserInteract);
|
|
42443
42521
|
if (!frameUrls || selectedFrameIndex == null) {
|
|
42444
42522
|
return /* @__PURE__ */ jsx$1(Loading, { t: loadingT });
|
|
42445
42523
|
}
|
|
42446
42524
|
return /* @__PURE__ */ jsxs("div", { css: css2.imageContainer, style: imageContainerStyle, children: [
|
|
42447
42525
|
/* @__PURE__ */ jsx$1("img", { src: frameUrls[selectedFrameIndex], css: css2.image, style: imageStyle, onMouseDown: handleMouseDragStart, onTouchStart: handleTouchDragStart }),
|
|
42448
|
-
/* @__PURE__ */ jsx$1("div", { css: css2.chevronLeftContainer, onClick: rotateLeft, children: /* @__PURE__ */ jsx$1(SvgChevronLeft, { css: css2.chevronIcon }) }),
|
|
42449
|
-
/* @__PURE__ */ jsx$1("div", { css: css2.chevronRightContainer, onClick: rotateRight, children: /* @__PURE__ */ jsx$1(SvgChevronRight, { css: css2.chevronIcon }) })
|
|
42526
|
+
/* @__PURE__ */ jsx$1("div", { role: "button", "aria-label": "Rotate left", css: css2.chevronLeftContainer, onMouseDown: (e) => e.preventDefault(), onClick: rotateLeft, children: /* @__PURE__ */ jsx$1(SvgChevronLeft, { css: css2.chevronIcon }) }),
|
|
42527
|
+
/* @__PURE__ */ jsx$1("div", { role: "button", "aria-label": "Rotate right", css: css2.chevronRightContainer, onMouseDown: (e) => e.preventDefault(), onClick: rotateRight, children: /* @__PURE__ */ jsx$1(SvgChevronRight, { css: css2.chevronIcon }) })
|
|
42450
42528
|
] });
|
|
42451
42529
|
}
|
|
42530
|
+
const AUTO_ROTATE_DURATION_MS = 4e3;
|
|
42531
|
+
function useAutoRotate(trigger, frameUrls, selectedFrameIndex, setSelectedFrameIndex) {
|
|
42532
|
+
const lastFiredRef = reactExports.useRef(void 0);
|
|
42533
|
+
const intervalIdRef = reactExports.useRef(null);
|
|
42534
|
+
const indexRef = reactExports.useRef(selectedFrameIndex);
|
|
42535
|
+
reactExports.useEffect(() => {
|
|
42536
|
+
indexRef.current = selectedFrameIndex;
|
|
42537
|
+
}, [selectedFrameIndex]);
|
|
42538
|
+
const cancelAutoRotate = reactExports.useCallback(() => {
|
|
42539
|
+
if (intervalIdRef.current !== null) {
|
|
42540
|
+
clearInterval(intervalIdRef.current);
|
|
42541
|
+
intervalIdRef.current = null;
|
|
42542
|
+
}
|
|
42543
|
+
}, []);
|
|
42544
|
+
const frameCount = frameUrls?.length ?? 0;
|
|
42545
|
+
reactExports.useEffect(() => {
|
|
42546
|
+
if (trigger === void 0) {
|
|
42547
|
+
return;
|
|
42548
|
+
}
|
|
42549
|
+
if (frameCount === 0) {
|
|
42550
|
+
return;
|
|
42551
|
+
}
|
|
42552
|
+
if (trigger === lastFiredRef.current) {
|
|
42553
|
+
return;
|
|
42554
|
+
}
|
|
42555
|
+
const tickMs = Math.round(AUTO_ROTATE_DURATION_MS / frameCount);
|
|
42556
|
+
const startFrameIndex = (indexRef.current ?? 0) % frameCount;
|
|
42557
|
+
let currentFrameIndex = startFrameIndex;
|
|
42558
|
+
let didFirstTick = false;
|
|
42559
|
+
intervalIdRef.current = setInterval(() => {
|
|
42560
|
+
if (!didFirstTick) {
|
|
42561
|
+
didFirstTick = true;
|
|
42562
|
+
lastFiredRef.current = trigger;
|
|
42563
|
+
}
|
|
42564
|
+
const nextFrameIndex = (currentFrameIndex + 1) % frameCount;
|
|
42565
|
+
currentFrameIndex = nextFrameIndex;
|
|
42566
|
+
setSelectedFrameIndex(nextFrameIndex);
|
|
42567
|
+
if (nextFrameIndex === startFrameIndex) {
|
|
42568
|
+
cancelAutoRotate();
|
|
42569
|
+
}
|
|
42570
|
+
}, tickMs);
|
|
42571
|
+
return cancelAutoRotate;
|
|
42572
|
+
}, [trigger, frameCount, setSelectedFrameIndex, cancelAutoRotate]);
|
|
42573
|
+
return cancelAutoRotate;
|
|
42574
|
+
}
|
|
42452
42575
|
function AvatarPane({
|
|
42453
42576
|
frameUrls,
|
|
42454
42577
|
hasSelection,
|
|
42455
42578
|
controls,
|
|
42456
42579
|
mobileFullscreen,
|
|
42457
42580
|
selectedFrameIndex: indexProp,
|
|
42458
|
-
setSelectedFrameIndex: setIndexProp
|
|
42581
|
+
setSelectedFrameIndex: setIndexProp,
|
|
42582
|
+
autoRotateTrigger
|
|
42459
42583
|
}) {
|
|
42460
42584
|
const [localFrameIndex, setLocalFrameIndex] = reactExports.useState(null);
|
|
42461
42585
|
const selectedFrameIndex = indexProp !== void 0 ? indexProp : localFrameIndex;
|
|
42462
42586
|
const setSelectedFrameIndex = setIndexProp ?? setLocalFrameIndex;
|
|
42587
|
+
const cancelAutoRotate = useAutoRotate(autoRotateTrigger, frameUrls, selectedFrameIndex, setSelectedFrameIndex);
|
|
42463
42588
|
const css2 = useCss((theme) => ({
|
|
42464
42589
|
container: {
|
|
42465
42590
|
width: "100%",
|
|
@@ -42517,7 +42642,7 @@ function AvatarPane({
|
|
|
42517
42642
|
}
|
|
42518
42643
|
}));
|
|
42519
42644
|
if (frameUrls && frameUrls.length > 0) {
|
|
42520
|
-
const viewer = /* @__PURE__ */ jsx$1(AvatarFrameViewer, { frameUrls, selectedFrameIndex, setSelectedFrameIndex, imageContainerStyle: css2.frameContainer, imageStyle: css2.frameImage, loadingT: "quick-view.avatar_loading" });
|
|
42645
|
+
const viewer = /* @__PURE__ */ jsx$1(AvatarFrameViewer, { frameUrls, selectedFrameIndex, setSelectedFrameIndex, imageContainerStyle: css2.frameContainer, imageStyle: css2.frameImage, loadingT: "quick-view.avatar_loading", onUserInteract: cancelAutoRotate });
|
|
42521
42646
|
if (mobileFullscreen) {
|
|
42522
42647
|
return /* @__PURE__ */ jsxs("div", { css: css2.mobileContainer, children: [
|
|
42523
42648
|
/* @__PURE__ */ jsxs("div", { css: css2.mobileFrameSlot, children: [
|
|
@@ -42644,8 +42769,19 @@ function ProductCard({
|
|
|
42644
42769
|
onClick();
|
|
42645
42770
|
};
|
|
42646
42771
|
const name2 = item.merchantProduct?.productName ?? item.externalId;
|
|
42647
|
-
|
|
42648
|
-
|
|
42772
|
+
let effectiveColor = item.storage.color;
|
|
42773
|
+
if (!effectiveColor && item.loadedProduct) {
|
|
42774
|
+
const sizeRec = item.loadedProduct.sizeFitRecommendation;
|
|
42775
|
+
const recommendedSize = sizeRec.available_sizes.find((s) => s.id === sizeRec.recommended_size.id);
|
|
42776
|
+
const firstCsa = recommendedSize?.colorway_size_assets[0];
|
|
42777
|
+
if (firstCsa) {
|
|
42778
|
+
const variant = item.merchantProduct?.variants.find((v) => v.sku === firstCsa.sku);
|
|
42779
|
+
effectiveColor = variant?.color ?? null;
|
|
42780
|
+
}
|
|
42781
|
+
}
|
|
42782
|
+
const selectedVariant = item.merchantProduct?.variants.find((v) => v.color === effectiveColor && (!item.storage.size || v.size === item.storage.size));
|
|
42783
|
+
const imageUrl = selectedVariant?.imageUrl ?? item.merchantProduct?.imageUrl ?? null;
|
|
42784
|
+
const price = selectedVariant?.priceFormatted ?? item.merchantProduct?.variants[0]?.priceFormatted ?? null;
|
|
42649
42785
|
return /* @__PURE__ */ jsxs("div", { css: /* @__PURE__ */ css$1({
|
|
42650
42786
|
...css2.container,
|
|
42651
42787
|
...selected && css2.containerSelected,
|
|
@@ -43353,7 +43489,6 @@ function DetailAccordion({
|
|
|
43353
43489
|
}) });
|
|
43354
43490
|
}
|
|
43355
43491
|
const AXIS_LOCK_PX = 8;
|
|
43356
|
-
const ROTATE_STEP_PX = 50;
|
|
43357
43492
|
function ZoomModal({
|
|
43358
43493
|
frameUrls,
|
|
43359
43494
|
selectedFrameIndex,
|
|
@@ -43399,15 +43534,7 @@ function ZoomModal({
|
|
|
43399
43534
|
if (mode === "scroll" && scrollArea) {
|
|
43400
43535
|
scrollArea.scrollTop = startScrollTop - deltaY;
|
|
43401
43536
|
} else if (mode === "rotate") {
|
|
43402
|
-
|
|
43403
|
-
if (Math.abs(rotateDelta) >= ROTATE_STEP_PX) {
|
|
43404
|
-
if (rotateDelta > 0) {
|
|
43405
|
-
rotateRight();
|
|
43406
|
-
} else {
|
|
43407
|
-
rotateLeft();
|
|
43408
|
-
}
|
|
43409
|
-
lastRotateX = move.clientX;
|
|
43410
|
-
}
|
|
43537
|
+
lastRotateX += applyDragSteps(move.clientX - lastRotateX, rotateLeft, rotateRight);
|
|
43411
43538
|
}
|
|
43412
43539
|
};
|
|
43413
43540
|
const onUp = () => {
|
|
@@ -43453,7 +43580,11 @@ function ZoomModal({
|
|
|
43453
43580
|
transform: "translateY(-50%)",
|
|
43454
43581
|
display: "flex",
|
|
43455
43582
|
cursor: "pointer",
|
|
43456
|
-
zIndex: 1
|
|
43583
|
+
zIndex: 1,
|
|
43584
|
+
// Rapid clicks shouldn't initiate a text-selection drag into the
|
|
43585
|
+
// close-button glyph or any other overlay text.
|
|
43586
|
+
userSelect: "none",
|
|
43587
|
+
WebkitUserSelect: "none"
|
|
43457
43588
|
},
|
|
43458
43589
|
chevronLeft: {
|
|
43459
43590
|
left: "8px"
|
|
@@ -43499,11 +43630,11 @@ function ZoomModal({
|
|
|
43499
43630
|
/* @__PURE__ */ jsx$1("div", { css: /* @__PURE__ */ css$1({
|
|
43500
43631
|
...css2.chevron,
|
|
43501
43632
|
...css2.chevronLeft
|
|
43502
|
-
}, "", ""), onClick: rotateLeft, children: /* @__PURE__ */ jsx$1(SvgChevronLeft, { css: css2.chevronIcon }) }),
|
|
43633
|
+
}, "", ""), onMouseDown: (e) => e.preventDefault(), onClick: rotateLeft, children: /* @__PURE__ */ jsx$1(SvgChevronLeft, { css: css2.chevronIcon }) }),
|
|
43503
43634
|
/* @__PURE__ */ jsx$1("div", { css: /* @__PURE__ */ css$1({
|
|
43504
43635
|
...css2.chevron,
|
|
43505
43636
|
...css2.chevronRight
|
|
43506
|
-
}, "", ""), onClick: rotateRight, children: /* @__PURE__ */ jsx$1(SvgChevronRight, { css: css2.chevronIcon }) }),
|
|
43637
|
+
}, "", ""), onMouseDown: (e) => e.preventDefault(), onClick: rotateRight, children: /* @__PURE__ */ jsx$1(SvgChevronRight, { css: css2.chevronIcon }) }),
|
|
43507
43638
|
/* @__PURE__ */ jsx$1("button", { css: css2.close, onClick: onClose, "aria-label": "Close zoom", children: "×" })
|
|
43508
43639
|
] });
|
|
43509
43640
|
}
|
|
@@ -43523,6 +43654,7 @@ function DesktopLayout$1({
|
|
|
43523
43654
|
forceUntuck,
|
|
43524
43655
|
canTuck,
|
|
43525
43656
|
frameUrls,
|
|
43657
|
+
autoRotateTrigger,
|
|
43526
43658
|
onSelectItem,
|
|
43527
43659
|
onRemoveItem,
|
|
43528
43660
|
onOpenAccordionItem,
|
|
@@ -43531,7 +43663,8 @@ function DesktopLayout$1({
|
|
|
43531
43663
|
onChangeColor,
|
|
43532
43664
|
onAddToCart,
|
|
43533
43665
|
onToggleUntuck,
|
|
43534
|
-
onSignOut
|
|
43666
|
+
onSignOut,
|
|
43667
|
+
onClearAll
|
|
43535
43668
|
}) {
|
|
43536
43669
|
const hasSelection = selectedItems.length > 0;
|
|
43537
43670
|
const [avatarHovered, setAvatarHovered] = reactExports.useState(false);
|
|
@@ -43592,46 +43725,65 @@ function DesktopLayout$1({
|
|
|
43592
43725
|
gap: "24px",
|
|
43593
43726
|
padding: `26px ${EDGE_INSET_PX}px ${EDGE_INSET_PX}px 8px`
|
|
43594
43727
|
},
|
|
43595
|
-
//
|
|
43596
|
-
//
|
|
43597
|
-
//
|
|
43598
|
-
|
|
43599
|
-
signOutWrapper: {
|
|
43600
|
-
position: "absolute",
|
|
43601
|
-
// Near the overlay top, partially overlapping the first card-rail
|
|
43602
|
-
// header row below it.
|
|
43603
|
-
top: "15px",
|
|
43604
|
-
right: "24px",
|
|
43605
|
-
zIndex: 3,
|
|
43728
|
+
// Shared icon+link visual for the rails column's two utility actions
|
|
43729
|
+
// (Clear All in the top-right corner, Sign Out at the bottom). Callers
|
|
43730
|
+
// add their own positioning on top.
|
|
43731
|
+
utilityLink: {
|
|
43606
43732
|
display: "inline-flex",
|
|
43607
43733
|
alignItems: "center",
|
|
43608
43734
|
gap: "8px",
|
|
43609
43735
|
cursor: "pointer",
|
|
43610
43736
|
color: _theme.color_tfr_800
|
|
43611
43737
|
},
|
|
43612
|
-
|
|
43738
|
+
utilityIcon: {
|
|
43613
43739
|
width: "12px",
|
|
43614
43740
|
height: "22px",
|
|
43615
43741
|
fill: _theme.color_tfr_800,
|
|
43616
43742
|
flex: "none"
|
|
43617
43743
|
},
|
|
43618
|
-
|
|
43744
|
+
utilityText: {
|
|
43619
43745
|
color: _theme.color_tfr_800,
|
|
43620
43746
|
fontSize: "14px"
|
|
43747
|
+
},
|
|
43748
|
+
// Clear All is overlaid on the top-right corner of the rails column so it
|
|
43749
|
+
// shares a row with the first card-rail header rather than reserving its
|
|
43750
|
+
// own row. It scrolls away with the content, which is fine — it only
|
|
43751
|
+
// needs to overlap that first header.
|
|
43752
|
+
clearAllWrapper: {
|
|
43753
|
+
position: "absolute",
|
|
43754
|
+
top: "15px",
|
|
43755
|
+
right: "24px",
|
|
43756
|
+
zIndex: 3
|
|
43757
|
+
},
|
|
43758
|
+
// Sign Out sits at the bottom of the rails column, after the last card
|
|
43759
|
+
// rail. It scrolls with the content — visible once the shopper reaches
|
|
43760
|
+
// the end of their items. marginTop: auto in a flex column would only
|
|
43761
|
+
// help with a non-scrolling parent; here the scroll context makes a
|
|
43762
|
+
// top margin enough to separate it from the last rail above.
|
|
43763
|
+
signOutWrapper: {
|
|
43764
|
+
marginTop: "8px",
|
|
43765
|
+
justifyContent: "center"
|
|
43621
43766
|
}
|
|
43622
43767
|
}));
|
|
43623
43768
|
const controls = hasSelection ? /* @__PURE__ */ jsx$1(AvatarControls, { selectedItems, canTuck, forceUntuck, zoomed: zoomOpen, expanded: avatarHovered, onToggleUntuck, onToggleZoom: () => setZoomOpen(true), onRemoveItem }) : null;
|
|
43624
43769
|
return /* @__PURE__ */ jsxs("div", { ref: containerRef, css: css2.container, style: {
|
|
43625
43770
|
gridTemplateColumns
|
|
43626
43771
|
}, children: [
|
|
43627
|
-
/* @__PURE__ */ jsx$1("div", { css: css2.avatarColumn, onMouseEnter: () => setAvatarHovered(true), onMouseLeave: () => setAvatarHovered(false), children: /* @__PURE__ */ jsx$1(AvatarPane, { hasSelection, frameUrls, controls, selectedFrameIndex, setSelectedFrameIndex }) }),
|
|
43772
|
+
/* @__PURE__ */ jsx$1("div", { css: css2.avatarColumn, onMouseEnter: () => setAvatarHovered(true), onMouseLeave: () => setAvatarHovered(false), children: /* @__PURE__ */ jsx$1(AvatarPane, { hasSelection, frameUrls, controls, selectedFrameIndex, setSelectedFrameIndex, autoRotateTrigger }) }),
|
|
43628
43773
|
hasSelection ? /* @__PURE__ */ jsx$1("div", { css: css2.detailColumn, children: /* @__PURE__ */ jsx$1(DetailAccordion, { items: selectedItems, openItemExternalId: openAccordionItemId, platform: "desktop", detailMode, isMobileQuickRow: false, forceUntuck, canTuck, onOpenItem: onOpenAccordionItem, onChangeDetailMode, onChangeSize, onChangeColor, onAddToCart, onToggleUntuck }) }) : null,
|
|
43629
43774
|
/* @__PURE__ */ jsxs("div", { css: css2.railsColumn, children: [
|
|
43630
|
-
/* @__PURE__ */
|
|
43631
|
-
|
|
43632
|
-
|
|
43633
|
-
|
|
43634
|
-
resolved.groups.map((group) => /* @__PURE__ */ jsx$1(CardRail, { group, availabilityByExternalId, onSelectItem, onRemoveItem }, group.group.name))
|
|
43775
|
+
/* @__PURE__ */ jsx$1("span", { css: /* @__PURE__ */ css$1({
|
|
43776
|
+
...css2.utilityLink,
|
|
43777
|
+
...css2.clearAllWrapper
|
|
43778
|
+
}, "", ""), onClick: onClearAll, children: /* @__PURE__ */ jsx$1(LinkT, { variant: "underline", css: css2.utilityText, t: "fitting_room.clear_all" }) }),
|
|
43779
|
+
resolved.groups.map((group) => /* @__PURE__ */ jsx$1(CardRail, { group, availabilityByExternalId, onSelectItem, onRemoveItem }, group.group.name)),
|
|
43780
|
+
/* @__PURE__ */ jsxs("span", { css: /* @__PURE__ */ css$1({
|
|
43781
|
+
...css2.utilityLink,
|
|
43782
|
+
...css2.signOutWrapper
|
|
43783
|
+
}, "", ""), onClick: onSignOut, children: [
|
|
43784
|
+
/* @__PURE__ */ jsx$1(SvgTfrIcon, { css: css2.utilityIcon }),
|
|
43785
|
+
/* @__PURE__ */ jsx$1(LinkT, { variant: "underline", css: css2.utilityText, t: "fitting_room.sign_out" })
|
|
43786
|
+
] })
|
|
43635
43787
|
] }),
|
|
43636
43788
|
zoomOpen && frameUrls && frameUrls.length > 0 ? /* @__PURE__ */ jsx$1(ZoomModal, { frameUrls, selectedFrameIndex, setSelectedFrameIndex, onClose: () => setZoomOpen(false) }) : null
|
|
43637
43789
|
] });
|
|
@@ -43751,6 +43903,7 @@ function MobileLayout$1({
|
|
|
43751
43903
|
forceUntuck,
|
|
43752
43904
|
canTuck,
|
|
43753
43905
|
frameUrls,
|
|
43906
|
+
autoRotateTrigger,
|
|
43754
43907
|
sheetSnap,
|
|
43755
43908
|
sheetTouchStart,
|
|
43756
43909
|
onSelectItem,
|
|
@@ -43762,12 +43915,14 @@ function MobileLayout$1({
|
|
|
43762
43915
|
onChangeSize,
|
|
43763
43916
|
onChangeColor,
|
|
43764
43917
|
onAddToCart,
|
|
43765
|
-
onToggleUntuck
|
|
43918
|
+
onToggleUntuck,
|
|
43919
|
+
onSignOut,
|
|
43920
|
+
onClearAll
|
|
43766
43921
|
}) {
|
|
43767
43922
|
if (mode === "browse") {
|
|
43768
|
-
return /* @__PURE__ */ jsx$1(BrowseView, { resolved, availabilityByExternalId, selectedCount: selectedItems.length, onSelectItem, onRemoveItem, onTryItOn });
|
|
43923
|
+
return /* @__PURE__ */ jsx$1(BrowseView, { resolved, availabilityByExternalId, selectedCount: selectedItems.length, onSelectItem, onRemoveItem, onTryItOn, onSignOut, onClearAll });
|
|
43769
43924
|
}
|
|
43770
|
-
return /* @__PURE__ */ jsx$1(TryOnView, { selectedItems, openAccordionItemId, detailMode, forceUntuck, canTuck, frameUrls, sheetSnap, sheetTouchStart, onBackToBrowse, onOpenAccordionItem, onChangeDetailMode, onChangeSize, onChangeColor, onAddToCart, onToggleUntuck });
|
|
43925
|
+
return /* @__PURE__ */ jsx$1(TryOnView, { selectedItems, openAccordionItemId, detailMode, forceUntuck, canTuck, frameUrls, autoRotateTrigger, sheetSnap, sheetTouchStart, onBackToBrowse, onOpenAccordionItem, onChangeDetailMode, onChangeSize, onChangeColor, onAddToCart, onToggleUntuck });
|
|
43771
43926
|
}
|
|
43772
43927
|
function BrowseView({
|
|
43773
43928
|
resolved,
|
|
@@ -43775,7 +43930,9 @@ function BrowseView({
|
|
|
43775
43930
|
selectedCount,
|
|
43776
43931
|
onSelectItem,
|
|
43777
43932
|
onRemoveItem,
|
|
43778
|
-
onTryItOn
|
|
43933
|
+
onTryItOn,
|
|
43934
|
+
onSignOut,
|
|
43935
|
+
onClearAll
|
|
43779
43936
|
}) {
|
|
43780
43937
|
const railsAreaRef = reactExports.useRef(null);
|
|
43781
43938
|
const sectionRefs = reactExports.useRef(/* @__PURE__ */ new Map());
|
|
@@ -43818,13 +43975,13 @@ function BrowseView({
|
|
|
43818
43975
|
behavior: "smooth"
|
|
43819
43976
|
});
|
|
43820
43977
|
}, []);
|
|
43821
|
-
const css2 = useCss((
|
|
43978
|
+
const css2 = useCss((theme) => ({
|
|
43822
43979
|
container: {
|
|
43823
43980
|
display: "flex",
|
|
43824
43981
|
flexDirection: "column",
|
|
43825
43982
|
height: "100%",
|
|
43826
43983
|
width: "100%",
|
|
43827
|
-
// Positioning context for the floating SectionNav
|
|
43984
|
+
// Positioning context for the floating SectionNav + Clear All pills.
|
|
43828
43985
|
position: "relative"
|
|
43829
43986
|
},
|
|
43830
43987
|
railsArea: {
|
|
@@ -43835,6 +43992,62 @@ function BrowseView({
|
|
|
43835
43992
|
flexDirection: "column",
|
|
43836
43993
|
gap: "24px"
|
|
43837
43994
|
},
|
|
43995
|
+
// Sign Out lives at the very bottom of the scrollable card list — same
|
|
43996
|
+
// pattern as desktop. Centered icon+link in TFR teal. Scrolls into view
|
|
43997
|
+
// once the shopper reaches the end of their items.
|
|
43998
|
+
signOutWrapper: {
|
|
43999
|
+
display: "inline-flex",
|
|
44000
|
+
alignItems: "center",
|
|
44001
|
+
justifyContent: "center",
|
|
44002
|
+
gap: "8px",
|
|
44003
|
+
cursor: "pointer",
|
|
44004
|
+
color: theme.color_tfr_800,
|
|
44005
|
+
marginTop: "8px"
|
|
44006
|
+
},
|
|
44007
|
+
signOutIcon: {
|
|
44008
|
+
width: "12px",
|
|
44009
|
+
height: "22px",
|
|
44010
|
+
fill: theme.color_tfr_800,
|
|
44011
|
+
flex: "none"
|
|
44012
|
+
},
|
|
44013
|
+
signOutText: {
|
|
44014
|
+
color: theme.color_tfr_800,
|
|
44015
|
+
fontSize: "14px"
|
|
44016
|
+
},
|
|
44017
|
+
// Clear All — white pill with thin black border, black text. Positioned
|
|
44018
|
+
// absolute over the BrowseView container, bottom-right, offset to clear
|
|
44019
|
+
// the Try It On CTA bar below it. Pill shape matches the SectionNav
|
|
44020
|
+
// pill at the top-right, but inverted on color for visual contrast.
|
|
44021
|
+
clearAllPill: {
|
|
44022
|
+
position: "absolute",
|
|
44023
|
+
bottom: "96px",
|
|
44024
|
+
right: "16px",
|
|
44025
|
+
zIndex: 5,
|
|
44026
|
+
display: "inline-flex",
|
|
44027
|
+
alignItems: "center",
|
|
44028
|
+
gap: "8px",
|
|
44029
|
+
padding: "6px 16px",
|
|
44030
|
+
borderRadius: "999px",
|
|
44031
|
+
backgroundColor: "#FFFFFF",
|
|
44032
|
+
color: theme.color_fg_text,
|
|
44033
|
+
border: `1px solid ${theme.color_fg_text}`,
|
|
44034
|
+
cursor: "pointer",
|
|
44035
|
+
fontSize: "13px",
|
|
44036
|
+
fontWeight: "500",
|
|
44037
|
+
letterSpacing: "0.5px",
|
|
44038
|
+
textTransform: "uppercase",
|
|
44039
|
+
whiteSpace: "nowrap"
|
|
44040
|
+
},
|
|
44041
|
+
clearAllText: {
|
|
44042
|
+
color: theme.color_fg_text
|
|
44043
|
+
},
|
|
44044
|
+
clearAllIcon: {
|
|
44045
|
+
width: "12px",
|
|
44046
|
+
height: "12px",
|
|
44047
|
+
flex: "none"
|
|
44048
|
+
// close-icon.svg's <path>s have a hardcoded dark fill — already matches
|
|
44049
|
+
// the new black-on-white pill, no override needed.
|
|
44050
|
+
},
|
|
43838
44051
|
bottomBar: {
|
|
43839
44052
|
flex: "none",
|
|
43840
44053
|
padding: "16px",
|
|
@@ -43844,13 +44057,23 @@ function BrowseView({
|
|
|
43844
44057
|
}));
|
|
43845
44058
|
return /* @__PURE__ */ jsxs("div", { css: css2.container, children: [
|
|
43846
44059
|
!resolved.isLoading && resolved.groups.length > 0 ? /* @__PURE__ */ jsx$1(SectionNav, { sections, activeName: activeSectionName, onSelect: scrollToSection }) : null,
|
|
43847
|
-
/* @__PURE__ */
|
|
43848
|
-
|
|
43849
|
-
|
|
43850
|
-
|
|
43851
|
-
|
|
43852
|
-
|
|
43853
|
-
|
|
44060
|
+
/* @__PURE__ */ jsxs("div", { ref: railsAreaRef, css: css2.railsArea, onScroll: recomputeActiveSection, children: [
|
|
44061
|
+
resolved.groups.map((group) => /* @__PURE__ */ jsx$1("div", { ref: (el) => {
|
|
44062
|
+
if (el) {
|
|
44063
|
+
sectionRefs.current.set(group.group.name, el);
|
|
44064
|
+
} else {
|
|
44065
|
+
sectionRefs.current.delete(group.group.name);
|
|
44066
|
+
}
|
|
44067
|
+
}, children: /* @__PURE__ */ jsx$1(CardRail, { group, availabilityByExternalId, onSelectItem, onRemoveItem }) }, group.group.name)),
|
|
44068
|
+
/* @__PURE__ */ jsxs("span", { css: css2.signOutWrapper, onClick: onSignOut, children: [
|
|
44069
|
+
/* @__PURE__ */ jsx$1(SvgTfrIcon, { css: css2.signOutIcon }),
|
|
44070
|
+
/* @__PURE__ */ jsx$1(LinkT, { variant: "underline", css: css2.signOutText, t: "fitting_room.sign_out" })
|
|
44071
|
+
] })
|
|
44072
|
+
] }),
|
|
44073
|
+
/* @__PURE__ */ jsxs(Button, { variant: "base", css: css2.clearAllPill, onClick: onClearAll, children: [
|
|
44074
|
+
/* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.clearAllText, t: "fitting_room.clear_all" }),
|
|
44075
|
+
/* @__PURE__ */ jsx$1(SvgCloseIcon, { css: css2.clearAllIcon })
|
|
44076
|
+
] }),
|
|
43854
44077
|
resolved.groups.length > 0 ? /* @__PURE__ */ jsx$1("div", { css: css2.bottomBar, children: /* @__PURE__ */ jsx$1(ButtonT, { variant: "brand", t: "fitting_room.try_it_on", onClick: onTryItOn, disabled: selectedCount === 0 }) }) : null
|
|
43855
44078
|
] });
|
|
43856
44079
|
}
|
|
@@ -43861,6 +44084,7 @@ function TryOnView({
|
|
|
43861
44084
|
forceUntuck,
|
|
43862
44085
|
canTuck,
|
|
43863
44086
|
frameUrls,
|
|
44087
|
+
autoRotateTrigger,
|
|
43864
44088
|
sheetSnap,
|
|
43865
44089
|
sheetTouchStart,
|
|
43866
44090
|
onBackToBrowse,
|
|
@@ -43961,7 +44185,7 @@ function TryOnView({
|
|
|
43961
44185
|
}
|
|
43962
44186
|
}));
|
|
43963
44187
|
return /* @__PURE__ */ jsxs("div", { css: css2.container, children: [
|
|
43964
|
-
/* @__PURE__ */ jsx$1(AvatarPane, { hasSelection: selectedItems.length > 0, frameUrls, mobileFullscreen: true, controls: /* @__PURE__ */ jsx$1(MobileTuckControl, { canTuck, forceUntuck, onToggleUntuck }) }),
|
|
44188
|
+
/* @__PURE__ */ jsx$1(AvatarPane, { hasSelection: selectedItems.length > 0, frameUrls, autoRotateTrigger, mobileFullscreen: true, controls: /* @__PURE__ */ jsx$1(MobileTuckControl, { canTuck, forceUntuck, onToggleUntuck }) }),
|
|
43965
44189
|
/* @__PURE__ */ jsx$1(Button, { variant: "base", css: css2.backButton, onClick: onBackToBrowse, "aria-label": "Back to browse", children: /* @__PURE__ */ jsx$1(SvgIconLeftArrow, { css: css2.backArrow }) }),
|
|
43966
44190
|
/* @__PURE__ */ jsx$1("div", { css: css2.sheetOuter, style: sheetStyle, children: /* @__PURE__ */ jsxs("div", { ref: innerRef, css: css2.sheetInner, style: sheetStyle, children: [
|
|
43967
44191
|
/* @__PURE__ */ jsxs("div", { css: css2.sheetHandleRow, onTouchStart: sheetTouchStart, children: [
|
|
@@ -44106,9 +44330,12 @@ function FittingRoomOverlay({
|
|
|
44106
44330
|
const closeOverlay = useMainStore((state) => state.closeOverlay);
|
|
44107
44331
|
const openOverlay = useMainStore((state) => state.openOverlay);
|
|
44108
44332
|
const updateFittingRoomItem = useMainStore((state) => state.updateFittingRoomItem);
|
|
44333
|
+
const removeFromFittingRoom = useMainStore((state) => state.removeFromFittingRoom);
|
|
44334
|
+
const clearFittingRoom = useMainStore((state) => state.clearFittingRoom);
|
|
44109
44335
|
const resolved = useResolvedFittingRoom();
|
|
44110
44336
|
const [topOffset, setTopOffset] = reactExports.useState(0);
|
|
44111
44337
|
const [selectedExternalIds, setSelectedExternalIds] = reactExports.useState(() => /* @__PURE__ */ new Set());
|
|
44338
|
+
const [autoRotateTrigger, setAutoRotateTrigger] = reactExports.useState(void 0);
|
|
44112
44339
|
const [openAccordionItemId, setOpenAccordionItemId] = reactExports.useState(null);
|
|
44113
44340
|
const [detailMode, setDetailMode] = reactExports.useState("compact");
|
|
44114
44341
|
const [forceUntuck, setForceUntuck] = reactExports.useState(false);
|
|
@@ -44215,6 +44442,7 @@ function FittingRoomOverlay({
|
|
|
44215
44442
|
nextSelected.add(externalId);
|
|
44216
44443
|
ensureSizeForItem(item);
|
|
44217
44444
|
setLastAddedExternalId(externalId);
|
|
44445
|
+
setAutoRotateTrigger((n) => (n ?? 0) + 1);
|
|
44218
44446
|
if (!isMobileLayout) {
|
|
44219
44447
|
setOpenAccordionItemId(externalId);
|
|
44220
44448
|
setDetailMode("compact");
|
|
@@ -44307,7 +44535,8 @@ function FittingRoomOverlay({
|
|
|
44307
44535
|
if (openAccordionItemId === externalId) {
|
|
44308
44536
|
setOpenAccordionItemId(null);
|
|
44309
44537
|
}
|
|
44310
|
-
|
|
44538
|
+
removeFromFittingRoom(externalId);
|
|
44539
|
+
}, [openAccordionItemId, removeFromFittingRoom]);
|
|
44311
44540
|
const handleTryItOn = reactExports.useCallback(() => {
|
|
44312
44541
|
setMobileMode("try-on");
|
|
44313
44542
|
setSheetSnap("collapsed");
|
|
@@ -44325,7 +44554,8 @@ function FittingRoomOverlay({
|
|
|
44325
44554
|
if (preselectAppliedRef.current || !preselectExternalId) {
|
|
44326
44555
|
return;
|
|
44327
44556
|
}
|
|
44328
|
-
|
|
44557
|
+
const item = resolved.items.find((i) => i.externalId === preselectExternalId);
|
|
44558
|
+
if (!item || !item.merchantProduct || !item.loadedProduct) {
|
|
44329
44559
|
return;
|
|
44330
44560
|
}
|
|
44331
44561
|
preselectAppliedRef.current = true;
|
|
@@ -44380,6 +44610,11 @@ function FittingRoomOverlay({
|
|
|
44380
44610
|
});
|
|
44381
44611
|
});
|
|
44382
44612
|
}, [closeOverlay]);
|
|
44613
|
+
const handleClearAll = reactExports.useCallback(() => {
|
|
44614
|
+
clearFittingRoom();
|
|
44615
|
+
setSelectedExternalIds(/* @__PURE__ */ new Set());
|
|
44616
|
+
setOpenAccordionItemId(null);
|
|
44617
|
+
}, [clearFittingRoom]);
|
|
44383
44618
|
const handleShopNow = reactExports.useCallback(() => {
|
|
44384
44619
|
closeOverlay();
|
|
44385
44620
|
}, [closeOverlay]);
|
|
@@ -44452,7 +44687,7 @@ function FittingRoomOverlay({
|
|
|
44452
44687
|
/* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.emptyTagline, t: "landing.description" }),
|
|
44453
44688
|
/* @__PURE__ */ jsx$1("div", { css: css2.emptyShopNow, children: /* @__PURE__ */ jsx$1(ButtonT, { variant: "primary", t: "fitting_room.shop_now", onClick: handleShopNow }) }),
|
|
44454
44689
|
userIsLoggedIn ? /* @__PURE__ */ jsx$1(LinkT, { variant: "underline", css: css2.emptySignOut, t: "fitting_room.sign_out", onClick: handleSignOut }) : null
|
|
44455
|
-
] }) }) : isMobileLayout ? /* @__PURE__ */ jsx$1(MobileLayout$1, { mode: mobileMode, resolved, selectedItems, availabilityByExternalId, openAccordionItemId, detailMode, forceUntuck, canTuck, frameUrls, sheetSnap, sheetTouchStart, onSelectItem: handleSelectItem, onRemoveItem: handleRemoveItem, onTryItOn: handleTryItOn, onBackToBrowse: handleBackToBrowse, onOpenAccordionItem: setOpenAccordionItemId, onChangeDetailMode: setDetailMode, onChangeSize: handleChangeSize, onChangeColor: handleChangeColor, onAddToCart: handleAddToCart, onToggleUntuck: handleToggleUntuck }) : /* @__PURE__ */ jsx$1(DesktopLayout$1, { resolved, selectedItems, availabilityByExternalId, openAccordionItemId, detailMode, forceUntuck, canTuck, frameUrls, onSelectItem: handleSelectItem, onRemoveItem: handleRemoveItem, onOpenAccordionItem: setOpenAccordionItemId, onChangeDetailMode: setDetailMode, onChangeSize: handleChangeSize, onChangeColor: handleChangeColor, onAddToCart: handleAddToCart, onToggleUntuck: handleToggleUntuck, onSignOut: handleSignOut }),
|
|
44690
|
+
] }) }) : isMobileLayout ? /* @__PURE__ */ jsx$1(MobileLayout$1, { mode: mobileMode, resolved, selectedItems, availabilityByExternalId, openAccordionItemId, detailMode, forceUntuck, canTuck, frameUrls, autoRotateTrigger, sheetSnap, sheetTouchStart, onSelectItem: handleSelectItem, onRemoveItem: handleRemoveItem, onTryItOn: handleTryItOn, onBackToBrowse: handleBackToBrowse, onOpenAccordionItem: setOpenAccordionItemId, onChangeDetailMode: setDetailMode, onChangeSize: handleChangeSize, onChangeColor: handleChangeColor, onAddToCart: handleAddToCart, onToggleUntuck: handleToggleUntuck, onSignOut: handleSignOut, onClearAll: handleClearAll }) : /* @__PURE__ */ jsx$1(DesktopLayout$1, { resolved, selectedItems, availabilityByExternalId, openAccordionItemId, detailMode, forceUntuck, canTuck, frameUrls, autoRotateTrigger, onSelectItem: handleSelectItem, onRemoveItem: handleRemoveItem, onOpenAccordionItem: setOpenAccordionItemId, onChangeDetailMode: setDetailMode, onChangeSize: handleChangeSize, onChangeColor: handleChangeColor, onAddToCart: handleAddToCart, onToggleUntuck: handleToggleUntuck, onSignOut: handleSignOut, onClearAll: handleClearAll }),
|
|
44456
44691
|
vtoError ? /* @__PURE__ */ jsx$1(Snackbar, { messageKey: "fitting_room.vto_error", onDismiss: clearVtoError }) : null
|
|
44457
44692
|
] }) });
|
|
44458
44693
|
}
|
|
@@ -45179,6 +45414,8 @@ function FitChart({
|
|
|
45179
45414
|
const AVATAR_IMAGE_ASPECT_RATIO = 2 / 3;
|
|
45180
45415
|
const AVATAR_GUTTER_HEIGHT_PX = 100;
|
|
45181
45416
|
const CONTENT_AREA_WIDTH_PX = 550;
|
|
45417
|
+
const SHOW_ROTATION_SLIDER = false;
|
|
45418
|
+
const DESKTOP_AVATAR_GUTTER_HEIGHT_PX = 0;
|
|
45182
45419
|
const logger$5 = getLogger("overlays/quick-view");
|
|
45183
45420
|
function QuickViewOverlay() {
|
|
45184
45421
|
const userIsLoggedIn = useMainStore((state) => state.userIsLoggedIn);
|
|
@@ -45188,6 +45425,8 @@ function QuickViewOverlay() {
|
|
|
45188
45425
|
const deviceLayout = useMainStore((state) => state.deviceLayout);
|
|
45189
45426
|
const openOverlay = useMainStore((state) => state.openOverlay);
|
|
45190
45427
|
const closeOverlay = useMainStore((state) => state.closeOverlay);
|
|
45428
|
+
const updateFittingRoomItem = useMainStore((state) => state.updateFittingRoomItem);
|
|
45429
|
+
const fittingRoomItems = useMainStore((state) => state.fittingRoom);
|
|
45191
45430
|
const [vtoProductData, setVtoProductData] = reactExports.useState(null);
|
|
45192
45431
|
const [selectedSizeLabel, setSelectedSizeLabel] = reactExports.useState(null);
|
|
45193
45432
|
const [selectedColorLabel, setSelectedColorLabel] = reactExports.useState(null);
|
|
@@ -45422,6 +45661,27 @@ function QuickViewOverlay() {
|
|
|
45422
45661
|
});
|
|
45423
45662
|
});
|
|
45424
45663
|
}, [closeOverlay]);
|
|
45664
|
+
const handleChangeColor = reactExports.useCallback((newColorLabel) => {
|
|
45665
|
+
setSelectedColorLabel(newColorLabel);
|
|
45666
|
+
const currentProduct = getStaticData().currentProduct;
|
|
45667
|
+
if (!currentProduct) {
|
|
45668
|
+
return;
|
|
45669
|
+
}
|
|
45670
|
+
const inRoom = fittingRoomItems.some((item) => item.externalId === currentProduct.externalId);
|
|
45671
|
+
if (!inRoom || !vtoProductData || !selectedSizeLabel) {
|
|
45672
|
+
return;
|
|
45673
|
+
}
|
|
45674
|
+
const sizeRec = vtoProductData.sizes.find((s) => s.sizeLabel === selectedSizeLabel);
|
|
45675
|
+
const csa = sizeRec?.colors.find((c) => c.colorLabel === newColorLabel);
|
|
45676
|
+
if (!csa) {
|
|
45677
|
+
return;
|
|
45678
|
+
}
|
|
45679
|
+
updateFittingRoomItem(currentProduct.externalId, {
|
|
45680
|
+
colorwaySizeAssetId: csa.colorwaySizeAssetId,
|
|
45681
|
+
size: selectedSizeLabel,
|
|
45682
|
+
color: csa.colorLabel
|
|
45683
|
+
});
|
|
45684
|
+
}, [fittingRoomItems, selectedSizeLabel, updateFittingRoomItem, vtoProductData]);
|
|
45425
45685
|
const handleAddToCartClick = reactExports.useCallback(async () => {
|
|
45426
45686
|
try {
|
|
45427
45687
|
if (!selectedSizeLabel) {
|
|
@@ -45457,7 +45717,7 @@ function QuickViewOverlay() {
|
|
|
45457
45717
|
Layout = DesktopLayout;
|
|
45458
45718
|
}
|
|
45459
45719
|
return /* @__PURE__ */ jsxs(SidecarModalFrame, { onRequestClose: closeOverlay, contentStyle: modalStyle, children: [
|
|
45460
|
-
/* @__PURE__ */ jsx$1(Layout, { loadedProductData: vtoProductData, selectedColorSizeRecord, availableColorLabels, selectedColorLabel, selectedSizeLabel, frameUrls, setModalStyle, onClose: closeOverlay, onChangeColor:
|
|
45720
|
+
/* @__PURE__ */ jsx$1(Layout, { loadedProductData: vtoProductData, selectedColorSizeRecord, availableColorLabels, selectedColorLabel, selectedSizeLabel, frameUrls, setModalStyle, onClose: closeOverlay, onChangeColor: handleChangeColor, onChangeSize: setSelectedSizeLabel, onAddToCart: handleAddToCartClick, onSignOut: handleSignOutClick }),
|
|
45461
45721
|
vtoError ? /* @__PURE__ */ jsx$1(Snackbar, { messageKey: "quick-view.vto_error", onDismiss: clearVtoError }) : null
|
|
45462
45722
|
] });
|
|
45463
45723
|
}
|
|
@@ -45695,7 +45955,10 @@ function MobileLayout({
|
|
|
45695
45955
|
}
|
|
45696
45956
|
function MobileContentCollapsed({
|
|
45697
45957
|
loadedProductData,
|
|
45958
|
+
availableColorLabels,
|
|
45959
|
+
selectedColorLabel,
|
|
45698
45960
|
selectedSizeLabel,
|
|
45961
|
+
onChangeColor,
|
|
45699
45962
|
onChangeSize
|
|
45700
45963
|
}) {
|
|
45701
45964
|
const css2 = useCss((_theme) => ({
|
|
@@ -45705,17 +45968,24 @@ function MobileContentCollapsed({
|
|
|
45705
45968
|
selectSizeLabelText: {},
|
|
45706
45969
|
sizeSelectorContainer: {
|
|
45707
45970
|
marginTop: "8px"
|
|
45971
|
+
},
|
|
45972
|
+
colorSelectorContainer: {
|
|
45973
|
+
marginTop: "8px"
|
|
45708
45974
|
}
|
|
45709
45975
|
}));
|
|
45710
45976
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
45711
45977
|
/* @__PURE__ */ jsx$1("div", { css: css2.selectSizeLabelContainer, children: /* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.selectSizeLabelText, t: "size-rec.select_size" }) }),
|
|
45712
|
-
/* @__PURE__ */ jsx$1("div", { css: css2.sizeSelectorContainer, children: /* @__PURE__ */ jsx$1(SizeSelector, { loadedProductData, selectedSizeLabel, onChangeSize }) })
|
|
45978
|
+
/* @__PURE__ */ jsx$1("div", { css: css2.sizeSelectorContainer, children: /* @__PURE__ */ jsx$1(SizeSelector, { loadedProductData, selectedSizeLabel, onChangeSize }) }),
|
|
45979
|
+
/* @__PURE__ */ jsx$1("div", { css: css2.colorSelectorContainer, children: /* @__PURE__ */ jsx$1(ColorSelector, { availableColorLabels, selectedColorLabel, onChangeColor }) })
|
|
45713
45980
|
] });
|
|
45714
45981
|
}
|
|
45715
45982
|
function MobileContentExpanded({
|
|
45716
45983
|
loadedProductData,
|
|
45984
|
+
availableColorLabels,
|
|
45985
|
+
selectedColorLabel,
|
|
45717
45986
|
selectedSizeLabel,
|
|
45718
45987
|
onChangeContentView,
|
|
45988
|
+
onChangeColor,
|
|
45719
45989
|
onChangeSize,
|
|
45720
45990
|
onAddToCart
|
|
45721
45991
|
}) {
|
|
@@ -45727,6 +45997,9 @@ function MobileContentExpanded({
|
|
|
45727
45997
|
sizeSelectorContainer: {
|
|
45728
45998
|
marginTop: "8px"
|
|
45729
45999
|
},
|
|
46000
|
+
colorSelectorContainer: {
|
|
46001
|
+
marginTop: "8px"
|
|
46002
|
+
},
|
|
45730
46003
|
itemFitTextContainer: {
|
|
45731
46004
|
marginTop: "8px"
|
|
45732
46005
|
},
|
|
@@ -45756,6 +46029,7 @@ function MobileContentExpanded({
|
|
|
45756
46029
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
45757
46030
|
/* @__PURE__ */ jsx$1("div", { css: css2.selectSizeLabelContainer, children: /* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.selectSizeLabelText, t: "size-rec.select_size" }) }),
|
|
45758
46031
|
/* @__PURE__ */ jsx$1("div", { css: css2.sizeSelectorContainer, children: /* @__PURE__ */ jsx$1(SizeSelector, { loadedProductData, selectedSizeLabel, onChangeSize }) }),
|
|
46032
|
+
/* @__PURE__ */ jsx$1("div", { css: css2.colorSelectorContainer, children: /* @__PURE__ */ jsx$1(ColorSelector, { availableColorLabels, selectedColorLabel, onChangeColor }) }),
|
|
45759
46033
|
/* @__PURE__ */ jsx$1("div", { css: css2.itemFitTextContainer, children: /* @__PURE__ */ jsx$1(ItemFitText, { loadedProductData }) }),
|
|
45760
46034
|
/* @__PURE__ */ jsx$1("div", { css: css2.itemFitDetailsContainer, children: /* @__PURE__ */ jsx$1(ItemFitDetails, { loadedProductData, selectedSizeLabel }) }),
|
|
45761
46035
|
/* @__PURE__ */ jsxs("div", { css: css2.buttonContainer, children: [
|
|
@@ -45909,7 +46183,7 @@ function DesktopLayout({
|
|
|
45909
46183
|
productNameContainer: {},
|
|
45910
46184
|
productNameText: {
|
|
45911
46185
|
fontFamily: "'Inter', sans-serif",
|
|
45912
|
-
fontSize: "
|
|
46186
|
+
fontSize: "24px",
|
|
45913
46187
|
fontWeight: 300
|
|
45914
46188
|
},
|
|
45915
46189
|
priceContainer: {
|
|
@@ -46029,6 +46303,7 @@ function Avatar({
|
|
|
46029
46303
|
});
|
|
46030
46304
|
const [selectedFrameIndex, setSelectedFrameIndex] = reactExports.useState(null);
|
|
46031
46305
|
const [zoomOpen, setZoomOpen] = reactExports.useState(false);
|
|
46306
|
+
const cancelAutoRotate = useAutoRotate(1, frameUrls, selectedFrameIndex, setSelectedFrameIndex);
|
|
46032
46307
|
const css2 = useCss((theme) => ({
|
|
46033
46308
|
topContainer: {
|
|
46034
46309
|
flex: "none",
|
|
@@ -46038,8 +46313,11 @@ function Avatar({
|
|
|
46038
46313
|
},
|
|
46039
46314
|
zoomPill: {
|
|
46040
46315
|
position: "absolute",
|
|
46041
|
-
// Bottom-right of the avatar image,
|
|
46042
|
-
|
|
46316
|
+
// Bottom-right of the avatar image, 16px above the image's bottom edge.
|
|
46317
|
+
// When the rotation slider is on, that's also `clear of the gutter` —
|
|
46318
|
+
// when the slider is hidden, the gutter collapses to 0 and the 16px
|
|
46319
|
+
// offset alone keeps the pill inset from the image's true bottom.
|
|
46320
|
+
bottom: `${DESKTOP_AVATAR_GUTTER_HEIGHT_PX + 16}px`,
|
|
46043
46321
|
right: "16px",
|
|
46044
46322
|
display: "inline-flex",
|
|
46045
46323
|
alignItems: "center",
|
|
@@ -46053,6 +46331,10 @@ function Avatar({
|
|
|
46053
46331
|
letterSpacing: "0.5px",
|
|
46054
46332
|
textTransform: "uppercase",
|
|
46055
46333
|
cursor: "pointer",
|
|
46334
|
+
// Rapid clicks on the rotation chevrons can otherwise spill a triple-
|
|
46335
|
+
// click selection into this label.
|
|
46336
|
+
userSelect: "none",
|
|
46337
|
+
WebkitUserSelect: "none",
|
|
46056
46338
|
zIndex: 2
|
|
46057
46339
|
},
|
|
46058
46340
|
zoomPillIcon: {
|
|
@@ -46129,7 +46411,7 @@ function Avatar({
|
|
|
46129
46411
|
}
|
|
46130
46412
|
} else {
|
|
46131
46413
|
const screenHeightPx = window.innerHeight;
|
|
46132
|
-
const bottomContainerHeightPx =
|
|
46414
|
+
const bottomContainerHeightPx = DESKTOP_AVATAR_GUTTER_HEIGHT_PX;
|
|
46133
46415
|
const imageHeightPx = screenHeightPx - bottomContainerHeightPx;
|
|
46134
46416
|
const imageWidthPx = Math.floor(imageHeightPx * AVATAR_IMAGE_ASPECT_RATIO);
|
|
46135
46417
|
const modalWidthPx = imageWidthPx + CONTENT_AREA_WIDTH_PX;
|
|
@@ -46172,13 +46454,13 @@ function Avatar({
|
|
|
46172
46454
|
}, [isMobileLayout, setModalStyle]);
|
|
46173
46455
|
const isReady = !!frameUrls && selectedFrameIndex != null;
|
|
46174
46456
|
return /* @__PURE__ */ jsxs("div", { css: css2.topContainer, style: layoutData.topContainerStyle, children: [
|
|
46175
|
-
/* @__PURE__ */ jsx$1(AvatarFrameViewer, { frameUrls, selectedFrameIndex, setSelectedFrameIndex, imageContainerStyle: layoutData.imageContainerStyle, imageStyle: layoutData.imageStyle, loadingT: "quick-view.avatar_loading" }),
|
|
46457
|
+
/* @__PURE__ */ jsx$1(AvatarFrameViewer, { frameUrls, selectedFrameIndex, setSelectedFrameIndex, imageContainerStyle: layoutData.imageContainerStyle, imageStyle: layoutData.imageStyle, loadingT: "quick-view.avatar_loading", onUserInteract: cancelAutoRotate }),
|
|
46176
46458
|
isReady && !isMobileLayout ? /* @__PURE__ */ jsxs(Button, { variant: "base", css: css2.zoomPill, onClick: () => setZoomOpen(true), children: [
|
|
46177
46459
|
/* @__PURE__ */ jsx$1(SvgIconZoom, { css: css2.zoomPillIcon }),
|
|
46178
46460
|
/* @__PURE__ */ jsx$1(TextT, { variant: "base", t: "quick-view.zoom_in" })
|
|
46179
46461
|
] }) : null,
|
|
46180
46462
|
zoomOpen && frameUrls && frameUrls.length > 0 ? /* @__PURE__ */ jsx$1(ZoomModal, { frameUrls, selectedFrameIndex, setSelectedFrameIndex, onClose: () => setZoomOpen(false) }) : null,
|
|
46181
|
-
frameUrls && selectedFrameIndex != null ? /* @__PURE__ */ jsx$1("div", { css: css2.bottomContainer, style: layoutData.bottomContainerStyle, children: isMobileLayout ? /* @__PURE__ */ jsx$1(Fragment, { children: " " }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
46463
|
+
frameUrls && selectedFrameIndex != null && (isMobileLayout || SHOW_ROTATION_SLIDER) ? /* @__PURE__ */ jsx$1("div", { css: css2.bottomContainer, style: layoutData.bottomContainerStyle, children: isMobileLayout ? /* @__PURE__ */ jsx$1(Fragment, { children: " " }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
46182
46464
|
/* @__PURE__ */ jsx$1("input", { type: "range", min: 0, max: frameUrls.length - 1, step: 1, value: selectedFrameIndex, onChange: (e) => setSelectedFrameIndex(Number(e.target.value)), css: css2.sliderInput }),
|
|
46183
46465
|
/* @__PURE__ */ jsx$1(TextT, { variant: "base", t: "quick-view.slide_to_rotate", css: css2.sliderText })
|
|
46184
46466
|
] }) }) : null
|
|
@@ -46543,6 +46825,13 @@ var DeviceLayout = /* @__PURE__ */ ((DeviceLayout2) => {
|
|
|
46543
46825
|
})(DeviceLayout || {});
|
|
46544
46826
|
function _init$1() {
|
|
46545
46827
|
function getDeviceData() {
|
|
46828
|
+
const testDevice = getStaticData().testHooks?.device;
|
|
46829
|
+
if (testDevice) {
|
|
46830
|
+
return {
|
|
46831
|
+
isMobileDevice: testDevice.isMobileDevice ?? testDevice.layout.startsWith("mobile"),
|
|
46832
|
+
deviceLayout: testDevice.layout
|
|
46833
|
+
};
|
|
46834
|
+
}
|
|
46546
46835
|
const bowserParser = Bowser.getParser(window.navigator.userAgent);
|
|
46547
46836
|
const {
|
|
46548
46837
|
width,
|
|
@@ -46706,28 +46995,41 @@ const useMainStore = create((set) => ({
|
|
|
46706
46995
|
}
|
|
46707
46996
|
})),
|
|
46708
46997
|
// Fitting room:
|
|
46998
|
+
//
|
|
46999
|
+
// Each mutation reads the latest localStorage state before applying the
|
|
47000
|
+
// change, so two tabs adding different products at the same time merge
|
|
47001
|
+
// instead of last-write-wins. The in-memory Zustand value is just a
|
|
47002
|
+
// cache of "what was in localStorage the last time we touched it".
|
|
47003
|
+
// Cross-tab UI freshness (Tab B's open fitting-room sees Tab A's add
|
|
47004
|
+
// without a mutation of its own) is handled by the `storage` event
|
|
47005
|
+
// listener registered in fitting-room-storage.ts::_init.
|
|
46709
47006
|
fittingRoom: [],
|
|
46710
|
-
addToFittingRoom: (item) => set((
|
|
46711
|
-
const
|
|
46712
|
-
const
|
|
46713
|
-
|
|
47007
|
+
addToFittingRoom: (item) => set(() => {
|
|
47008
|
+
const brandId = getStaticData().brandId;
|
|
47009
|
+
const fresh = readFittingRoom(brandId);
|
|
47010
|
+
const next2 = [...fresh.filter((existing) => existing.externalId !== item.externalId), item];
|
|
47011
|
+
writeFittingRoom(brandId, next2);
|
|
46714
47012
|
return {
|
|
46715
47013
|
fittingRoom: next2
|
|
46716
47014
|
};
|
|
46717
47015
|
}),
|
|
46718
|
-
removeFromFittingRoom: (externalId) => set((
|
|
46719
|
-
const
|
|
46720
|
-
|
|
47016
|
+
removeFromFittingRoom: (externalId) => set(() => {
|
|
47017
|
+
const brandId = getStaticData().brandId;
|
|
47018
|
+
const fresh = readFittingRoom(brandId);
|
|
47019
|
+
const next2 = fresh.filter((existing) => existing.externalId !== externalId);
|
|
47020
|
+
writeFittingRoom(brandId, next2);
|
|
46721
47021
|
return {
|
|
46722
47022
|
fittingRoom: next2
|
|
46723
47023
|
};
|
|
46724
47024
|
}),
|
|
46725
|
-
updateFittingRoomItem: (externalId, patch) => set((
|
|
46726
|
-
const
|
|
47025
|
+
updateFittingRoomItem: (externalId, patch) => set(() => {
|
|
47026
|
+
const brandId = getStaticData().brandId;
|
|
47027
|
+
const fresh = readFittingRoom(brandId);
|
|
47028
|
+
const next2 = fresh.map((existing) => existing.externalId === externalId ? {
|
|
46727
47029
|
...existing,
|
|
46728
47030
|
...patch
|
|
46729
47031
|
} : existing);
|
|
46730
|
-
writeFittingRoom(
|
|
47032
|
+
writeFittingRoom(brandId, next2);
|
|
46731
47033
|
return {
|
|
46732
47034
|
fittingRoom: next2
|
|
46733
47035
|
};
|
|
@@ -46794,9 +47096,9 @@ const SHARED_CONFIG = {
|
|
|
46794
47096
|
appGooglePlayUrl: "https://play.google.com/store/apps/details?id=com.thefittingroom.marketplace"
|
|
46795
47097
|
},
|
|
46796
47098
|
build: {
|
|
46797
|
-
version: `${"5.0.
|
|
46798
|
-
commitHash: `${"
|
|
46799
|
-
date: `${"2026-05-
|
|
47099
|
+
version: `${"5.0.28"}`,
|
|
47100
|
+
commitHash: `${"1e1de65"}`,
|
|
47101
|
+
date: `${"2026-05-25T00:50:20.004Z"}`
|
|
46800
47102
|
}
|
|
46801
47103
|
};
|
|
46802
47104
|
const CONFIGS = {
|
|
@@ -47016,10 +47318,12 @@ async function logout() {
|
|
|
47016
47318
|
}
|
|
47017
47319
|
const TFR = {
|
|
47018
47320
|
init,
|
|
47019
|
-
logout
|
|
47321
|
+
logout,
|
|
47322
|
+
syncCurrentProductSelection
|
|
47020
47323
|
};
|
|
47021
47324
|
export {
|
|
47022
47325
|
TFR as default,
|
|
47023
47326
|
init,
|
|
47024
|
-
logout
|
|
47327
|
+
logout,
|
|
47328
|
+
syncCurrentProductSelection
|
|
47025
47329
|
};
|