@thefittingroom/shop-ui 5.0.23 → 5.0.25
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/README.md +18 -1
- package/dist/index.js +1209 -383
- package/package.json +19 -3
package/dist/index.js
CHANGED
|
@@ -14101,16 +14101,8 @@ function _init$7() {
|
|
|
14101
14101
|
fittingRoom: items
|
|
14102
14102
|
});
|
|
14103
14103
|
}
|
|
14104
|
-
async function
|
|
14104
|
+
async function addFittingRoomItem(productId, handle, isPdp) {
|
|
14105
14105
|
const state = useMainStore.getState();
|
|
14106
|
-
const isInFittingRoom = state.fittingRoom.some((item) => item.externalId === productId);
|
|
14107
|
-
if (isInFittingRoom) {
|
|
14108
|
-
logger$g.logDebug("{{ts}} - Removing from fitting room", {
|
|
14109
|
-
productId
|
|
14110
|
-
});
|
|
14111
|
-
state.removeFromFittingRoom(productId);
|
|
14112
|
-
return;
|
|
14113
|
-
}
|
|
14114
14106
|
logger$g.logDebug("{{ts}} - Adding to fitting room", {
|
|
14115
14107
|
productId,
|
|
14116
14108
|
handle,
|
|
@@ -14161,6 +14153,25 @@ async function toggleFittingRoomItem(productId, handle, isPdp) {
|
|
|
14161
14153
|
addedAt: Date.now()
|
|
14162
14154
|
});
|
|
14163
14155
|
}
|
|
14156
|
+
async function toggleFittingRoomItem(productId, handle, isPdp) {
|
|
14157
|
+
const state = useMainStore.getState();
|
|
14158
|
+
const isInFittingRoom = state.fittingRoom.some((item) => item.externalId === productId);
|
|
14159
|
+
if (isInFittingRoom) {
|
|
14160
|
+
logger$g.logDebug("{{ts}} - Removing from fitting room", {
|
|
14161
|
+
productId
|
|
14162
|
+
});
|
|
14163
|
+
state.removeFromFittingRoom(productId);
|
|
14164
|
+
return;
|
|
14165
|
+
}
|
|
14166
|
+
await addFittingRoomItem(productId, handle, isPdp);
|
|
14167
|
+
}
|
|
14168
|
+
async function ensureFittingRoomItem(productId, handle, isPdp) {
|
|
14169
|
+
const state = useMainStore.getState();
|
|
14170
|
+
if (state.fittingRoom.some((item) => item.externalId === productId)) {
|
|
14171
|
+
return;
|
|
14172
|
+
}
|
|
14173
|
+
await addFittingRoomItem(productId, handle, isPdp);
|
|
14174
|
+
}
|
|
14164
14175
|
const BROWSER_ALIASES_MAP = {
|
|
14165
14176
|
AmazonBot: "amazonbot",
|
|
14166
14177
|
"Amazon Silk": "amazon_silk",
|
|
@@ -19307,7 +19318,7 @@ const en$1 = {
|
|
|
19307
19318
|
"get-app": { "create_avatar": "Create your avatar" },
|
|
19308
19319
|
"sign-in": { "email": "Email", "password": "Password", "forgot_password": "Forgot password?", "sign_in": "Sign in", "no_account": "Don’t have an account?", "download_app": "Download the app.", "invalid_email": "Please enter a valid email address.", "missing_password": "Please enter your password.", "login_failed": "Incorrect email or password." },
|
|
19309
19320
|
"forgot-password": { "title": "Forgot password", "description": "We’ll send you an email with a link to reset your password.", "send_link": "Send link", "link_sent": "Link sent! Please check your email.", "need_help": "Need help?", "contact_us": "Contact us." },
|
|
19310
|
-
"
|
|
19321
|
+
"quick-view": { "title": "Quick View Try On", "avatar_loading": "Finding your perfect fit...", "slide_to_rotate": "Slide to rotate", "sign_out": "Sign out", "color_label": "Color:", "add_to_cart": "Add to cart", "view_product_details": "View product details", "hide_product_details": "Hide product details", "no_recommendation": "There are currently no well fitting sizes available for this item.", "vto_error": "Couldn't load the try-on. Please try again.", "zoom_in": "Zoom In" },
|
|
19311
19322
|
"size-rec": { "recommended_size": "Recommended Size: {{size}}", "item_fit": "This item is {{fit}}", "select_size": "Select a size to see how it fits", "fitClassification": { "form_fitting": "Form Fitting", "slim_fit": "Slim Fit", "regular_fit": "Regular Fit", "relaxed_fit": "Relaxed Fit", "oversized_fit": "Oversized Fit" }, "measurementLocation": { "neck_base": "Neck", "across_shoulder": "Shoulders", "cb_neck_to_wrist": "Neck to Wrist", "sleeve_length_from_shoulder_point": "Sleeve", "bust": "Bust", "waist": "Waist", "low_waist": "Low Waist", "high_hip": "High Hip", "low_hip": "Low Hip", "thigh": "Thigh", "inseam": "Inseam", "hsp_to_low_hip": "HSP to Low Hip", "hsp_to_crotch": "HSP to Crotch", "low_hip_bottoms": "Low Hip (Bottoms)", "high_hip_bottoms": "High Hip (Bottoms)" }, "fit": { "too_tight": "Too Tight", "tight": "Tight", "slightly_tight": "Slightly Tight", "perfect_fit": "Perfect Fit", "slightly_loose": "Slightly Loose", "loose": "Loose", "oversized": "Oversized", "too_short": "Too Short", "short": "Short", "slightly_short": "Slightly Short", "slightly_long": "Slightly Long", "long": "Long", "too_long": "Too Long" } },
|
|
19312
19323
|
"fit-chart": { "fit_scale": "Fit Scale", "fit": { "poor_fit": "Poor Fit", "acceptable_fit": "Acceptable Fit", "too_tight": "Too Tight", "tight": "Tight or More Fitted", "slightly_tight": "Slightly Tight or Fitted", "perfect_fit": "Perfect Fit", "slightly_loose": "Slightly Loose or Less Fitted", "loose": "Loose or Not Fitted", "oversized": "Oversized" }, "measurement_points": "Measurement Points", "point": { "bust": "Chest/Bust", "waist": "Natural Waist", "pant_waist": "Pant Waist", "high_hip": "High Hip", "low_hip": "Low Hip", "thigh": "Thigh" } }
|
|
19313
19324
|
};
|
|
@@ -19331,11 +19342,11 @@ const fr = {
|
|
|
19331
19342
|
"get-app": { "create_avatar": "Créez votre avatar" },
|
|
19332
19343
|
"sign-in": { "email": "Email", "password": "Mot de passe", "forgot_password": "Mot de passe oublié ?", "sign_in": "Se connecter", "no_account": "Vous n'avez pas de compte ?", "download_app": "Téléchargez l'application.", "invalid_email": "Veuillez entrer une adresse e-mail valide.", "missing_password": "Veuillez entrer votre mot de passe.", "login_failed": "Email ou mot de passe incorrect." },
|
|
19333
19344
|
"forgot-password": { "title": "Mot de passe oublié", "description": "Nous vous enverrons un e-mail avec un lien pour réinitialiser votre mot de passe.", "send_link": "Envoyer le lien", "link_sent": "Lien envoyé ! Veuillez vérifier votre e-mail.", "need_help": "Besoin d'aide ?", "contact_us": "Contactez-nous." },
|
|
19334
|
-
"
|
|
19345
|
+
"quick-view": { "title": "Essayage rapide", "avatar_loading": "Trouver votre ajustement parfait...", "slide_to_rotate": "Faites glisser pour faire pivoter", "sign_out": "Se déconnecter", "color_label": "Couleur :", "add_to_cart": "Ajouter au panier", "view_product_details": "Voir les détails du produit", "hide_product_details": "Masquer les détails du produit", "no_recommendation": "Il n'y a actuellement pas de tailles bien ajustées disponibles pour cet article.", "vto_error": "Impossible de charger l'essayage. Veuillez réessayer.", "zoom_in": "Agrandir" },
|
|
19335
19346
|
"size-rec": { "recommended_size": "Taille recommandée : {{size}}", "item_fit": "Cet article est {{fit}}", "select_size": "Sélectionnez une taille pour voir comment elle s'adapte", "fitClassification": { "form_fitting": "Ajusté", "slim_fit": "Coupe slim", "regular_fit": "Coupe régulière", "relaxed_fit": "Coupe décontractée", "oversized_fit": "Coupe oversize" }, "measurementLocation": { "neck_base": "Cou", "across_shoulder": "Épaules", "cb_neck_to_wrist": "Cou jusqu'au poignet", "sleeve_length_from_shoulder_point": "Manche", "bust": "Buste", "waist": "Taille", "low_waist": "Bas de la taille", "high_hip": "Haut des hanches", "low_hip": "Bas des hanches", "thigh": "Cuisse", "inseam": "Entrejambe", "hsp_to_low_hip": "HSP au bas des hanches", "hsp_to_crotch": "HSP à l'entrejambe", "low_hip_bottoms": "Bas des hanches (bas)", "high_hip_bottoms": "Haut des hanches (bas)" }, "fit": { "too_tight": "Trop serré", "tight": "Serré", "slightly_tight": "Légèrement serré", "perfect_fit": "Parfait", "slightly_loose": "Légèrement ample", "loose": "Ample", "oversized": "Oversize", "too_short": "Trop court", "short": "Court", "slightly_short": "Légèrement court", "slightly_long": "Légèrement long", "long": "Long", "too_long": "Trop long" } },
|
|
19336
19347
|
"fit-chart": { "fit_scale": "Échelle d'ajustement", "fit": { "poor_fit": "Mauvais ajustement", "acceptable_fit": "Ajustement acceptable", "too_tight": "Trop serré", "tight": "Serré ou plus ajusté", "slightly_tight": "Légèrement serré ou ajusté", "perfect_fit": "Parfait", "slightly_loose": "Légèrement ample ou moins ajusté", "loose": "Ample ou non ajusté", "oversized": "Oversize" }, "measurement_points": "Points de mesure", "point": { "bust": "Poitrine/Buste", "waist": "Taille naturelle", "pant_waist": "Taille du pantalon", "high_hip": "Haut des hanches", "low_hip": "Bas des hanches", "thigh": "Cuisse" } }
|
|
19337
19348
|
};
|
|
19338
|
-
instance.use(initReactI18next).init({
|
|
19349
|
+
void instance.use(initReactI18next).init({
|
|
19339
19350
|
lng: "en",
|
|
19340
19351
|
fallbackLng: "en",
|
|
19341
19352
|
resources: {
|
|
@@ -19385,14 +19396,14 @@ function keyframes() {
|
|
|
19385
19396
|
};
|
|
19386
19397
|
}
|
|
19387
19398
|
const themeData = {
|
|
19388
|
-
brand_font_family: "
|
|
19399
|
+
brand_font_family: "'Inter', sans-serif",
|
|
19389
19400
|
brand_link_text_decoration: "underline",
|
|
19390
19401
|
brand_button_background_color: "#FFA273",
|
|
19391
19402
|
brand_button_text_color: "#21201F",
|
|
19392
19403
|
color_danger: "#900B09",
|
|
19393
19404
|
color_fg_text: "#21201F",
|
|
19394
19405
|
color_tfr_800: "#265A64",
|
|
19395
|
-
font_family: "sans-serif"
|
|
19406
|
+
font_family: "'Inter', sans-serif"
|
|
19396
19407
|
};
|
|
19397
19408
|
function _init$6(initThemeData) {
|
|
19398
19409
|
if (initThemeData) {
|
|
@@ -19487,7 +19498,7 @@ const Text = reactExports.forwardRef(({
|
|
|
19487
19498
|
const variantCss = useVariantCss(variant, (theme) => ({
|
|
19488
19499
|
base: {
|
|
19489
19500
|
color: theme.color_fg_text,
|
|
19490
|
-
fontFamily:
|
|
19501
|
+
fontFamily: theme.font_family,
|
|
19491
19502
|
fontSize: "14px"
|
|
19492
19503
|
},
|
|
19493
19504
|
brand: {
|
|
@@ -19497,6 +19508,7 @@ const Text = reactExports.forwardRef(({
|
|
|
19497
19508
|
},
|
|
19498
19509
|
error: {
|
|
19499
19510
|
color: theme.color_danger,
|
|
19511
|
+
fontFamily: theme.font_family,
|
|
19500
19512
|
fontSize: "14px"
|
|
19501
19513
|
}
|
|
19502
19514
|
}));
|
|
@@ -21244,6 +21256,45 @@ function SidecarModalFrame({
|
|
|
21244
21256
|
};
|
|
21245
21257
|
return /* @__PURE__ */ jsx$1(ModalFrame, { isOpen: true, onRequestClose, contentStyle: applyContentStyle, children });
|
|
21246
21258
|
}
|
|
21259
|
+
function Snackbar({
|
|
21260
|
+
messageKey,
|
|
21261
|
+
onDismiss
|
|
21262
|
+
}) {
|
|
21263
|
+
const css2 = useCss((theme) => ({
|
|
21264
|
+
container: {
|
|
21265
|
+
position: "absolute",
|
|
21266
|
+
left: "50%",
|
|
21267
|
+
bottom: "24px",
|
|
21268
|
+
transform: "translateX(-50%)",
|
|
21269
|
+
padding: "12px 20px",
|
|
21270
|
+
borderRadius: "24px",
|
|
21271
|
+
backgroundColor: theme.color_fg_text,
|
|
21272
|
+
fontSize: "13px",
|
|
21273
|
+
display: "flex",
|
|
21274
|
+
alignItems: "center",
|
|
21275
|
+
gap: "12px",
|
|
21276
|
+
zIndex: 10,
|
|
21277
|
+
maxWidth: "calc(100% - 32px)"
|
|
21278
|
+
},
|
|
21279
|
+
// The Text 'base' variant carries a dark color; override it so the copy
|
|
21280
|
+
// is readable on the dark snackbar background.
|
|
21281
|
+
text: {
|
|
21282
|
+
color: "#FFFFFF"
|
|
21283
|
+
},
|
|
21284
|
+
dismiss: {
|
|
21285
|
+
background: "none",
|
|
21286
|
+
border: "none",
|
|
21287
|
+
color: "#FFFFFF",
|
|
21288
|
+
fontSize: "16px",
|
|
21289
|
+
lineHeight: 1,
|
|
21290
|
+
cursor: "pointer"
|
|
21291
|
+
}
|
|
21292
|
+
}));
|
|
21293
|
+
return /* @__PURE__ */ jsxs("div", { css: css2.container, children: [
|
|
21294
|
+
/* @__PURE__ */ jsx$1(TextT, { variant: "base", t: messageKey, css: css2.text }),
|
|
21295
|
+
/* @__PURE__ */ jsx$1("button", { css: css2.dismiss, onClick: onDismiss, "aria-label": "Dismiss", children: "×" })
|
|
21296
|
+
] });
|
|
21297
|
+
}
|
|
21247
21298
|
var dayjs_min$1 = { exports: {} };
|
|
21248
21299
|
var dayjs_min = dayjs_min$1.exports;
|
|
21249
21300
|
var hasRequiredDayjs_min;
|
|
@@ -41000,11 +41051,148 @@ registerAuth(
|
|
|
41000
41051
|
"Browser"
|
|
41001
41052
|
/* ClientPlatform.BROWSER */
|
|
41002
41053
|
);
|
|
41054
|
+
const logger$e = getLogger("firebase-mock");
|
|
41055
|
+
class MockFirestoreManager {
|
|
41056
|
+
constructor(seedDocs = {}) {
|
|
41057
|
+
const cloned = {};
|
|
41058
|
+
for (const [coll, byId] of Object.entries(seedDocs)) {
|
|
41059
|
+
cloned[coll] = {
|
|
41060
|
+
...byId
|
|
41061
|
+
};
|
|
41062
|
+
}
|
|
41063
|
+
this.docs = cloned;
|
|
41064
|
+
}
|
|
41065
|
+
async getDocData(collectionName, docId) {
|
|
41066
|
+
const doc2 = this.docs[collectionName]?.[docId];
|
|
41067
|
+
return doc2 ?? null;
|
|
41068
|
+
}
|
|
41069
|
+
async setDocData(collectionName, docId, data) {
|
|
41070
|
+
if (!this.docs[collectionName]) {
|
|
41071
|
+
this.docs[collectionName] = {};
|
|
41072
|
+
}
|
|
41073
|
+
this.docs[collectionName][docId] = data;
|
|
41074
|
+
}
|
|
41075
|
+
async mergeDocData(collectionName, docId, data) {
|
|
41076
|
+
if (!this.docs[collectionName]) {
|
|
41077
|
+
this.docs[collectionName] = {};
|
|
41078
|
+
}
|
|
41079
|
+
const existing = this.docs[collectionName][docId] ?? {};
|
|
41080
|
+
this.docs[collectionName][docId] = {
|
|
41081
|
+
...existing,
|
|
41082
|
+
...data
|
|
41083
|
+
};
|
|
41084
|
+
}
|
|
41085
|
+
listenToDoc(collectionName, docId, callback) {
|
|
41086
|
+
const doc2 = this.docs[collectionName]?.[docId];
|
|
41087
|
+
callback(doc2 ?? null);
|
|
41088
|
+
return () => {
|
|
41089
|
+
};
|
|
41090
|
+
}
|
|
41091
|
+
async queryDocs(collectionName, _constraints) {
|
|
41092
|
+
const entries = Object.values(this.docs[collectionName] ?? {});
|
|
41093
|
+
const docs = entries.map((data) => ({
|
|
41094
|
+
data: () => data
|
|
41095
|
+
}));
|
|
41096
|
+
const snapshot = {
|
|
41097
|
+
empty: docs.length === 0,
|
|
41098
|
+
docs,
|
|
41099
|
+
forEach: (cb) => docs.forEach(cb)
|
|
41100
|
+
};
|
|
41101
|
+
return snapshot;
|
|
41102
|
+
}
|
|
41103
|
+
}
|
|
41104
|
+
function makeMockAuthUser(seed) {
|
|
41105
|
+
const fake = {
|
|
41106
|
+
uid: seed.uid,
|
|
41107
|
+
email: seed.email,
|
|
41108
|
+
getIdToken: async (_forceRefresh) => seed.idToken
|
|
41109
|
+
};
|
|
41110
|
+
return fake;
|
|
41111
|
+
}
|
|
41112
|
+
class MockAuthManager {
|
|
41113
|
+
constructor(seed, firestore) {
|
|
41114
|
+
this.userProfile = null;
|
|
41115
|
+
this.authStateChangeListeners = /* @__PURE__ */ new Set();
|
|
41116
|
+
this.userProfileChangeListeners = /* @__PURE__ */ new Set();
|
|
41117
|
+
this.profileUnsub = null;
|
|
41118
|
+
this.seed = seed;
|
|
41119
|
+
this.currentUser = seed ? makeMockAuthUser(seed) : null;
|
|
41120
|
+
this.firestore = firestore;
|
|
41121
|
+
this.addAuthStateChangeListener((authUser) => this.handleAuthStateChanged(authUser));
|
|
41122
|
+
}
|
|
41123
|
+
addAuthStateChangeListener(callback) {
|
|
41124
|
+
this.authStateChangeListeners.add(callback);
|
|
41125
|
+
callback(this.currentUser);
|
|
41126
|
+
return () => {
|
|
41127
|
+
this.authStateChangeListeners.delete(callback);
|
|
41128
|
+
};
|
|
41129
|
+
}
|
|
41130
|
+
removeAuthStateChangeListener(callback) {
|
|
41131
|
+
this.authStateChangeListeners.delete(callback);
|
|
41132
|
+
}
|
|
41133
|
+
addUserProfileChangeListener(callback) {
|
|
41134
|
+
this.userProfileChangeListeners.add(callback);
|
|
41135
|
+
callback(this.userProfile);
|
|
41136
|
+
return () => {
|
|
41137
|
+
this.userProfileChangeListeners.delete(callback);
|
|
41138
|
+
};
|
|
41139
|
+
}
|
|
41140
|
+
removeUserProfileChangeListener(callback) {
|
|
41141
|
+
this.userProfileChangeListeners.delete(callback);
|
|
41142
|
+
}
|
|
41143
|
+
getAuthUser() {
|
|
41144
|
+
return this.currentUser;
|
|
41145
|
+
}
|
|
41146
|
+
async getAuthToken(_forceRefresh = false) {
|
|
41147
|
+
if (!this.currentUser || !this.seed) {
|
|
41148
|
+
throw new Error("No authenticated user");
|
|
41149
|
+
}
|
|
41150
|
+
return this.seed.idToken;
|
|
41151
|
+
}
|
|
41152
|
+
async getUserProfile(_forceRefresh = false) {
|
|
41153
|
+
return this.userProfile;
|
|
41154
|
+
}
|
|
41155
|
+
async login(_email, _password) {
|
|
41156
|
+
throw new Error("MockAuthManager.login is not supported — seed the user via InitParams.testHooks.auth");
|
|
41157
|
+
}
|
|
41158
|
+
async logout() {
|
|
41159
|
+
if (this.profileUnsub) {
|
|
41160
|
+
this.profileUnsub();
|
|
41161
|
+
this.profileUnsub = null;
|
|
41162
|
+
}
|
|
41163
|
+
this.currentUser = null;
|
|
41164
|
+
this.userProfile = null;
|
|
41165
|
+
this.authStateChangeListeners.forEach((cb) => cb(null));
|
|
41166
|
+
this.userProfileChangeListeners.forEach((cb) => cb(null));
|
|
41167
|
+
}
|
|
41168
|
+
async sendPasswordResetEmail(_email) {
|
|
41169
|
+
}
|
|
41170
|
+
async confirmPasswordReset(_code, _newPassword) {
|
|
41171
|
+
}
|
|
41172
|
+
handleAuthStateChanged(authUser) {
|
|
41173
|
+
if (this.profileUnsub) {
|
|
41174
|
+
this.profileUnsub();
|
|
41175
|
+
this.profileUnsub = null;
|
|
41176
|
+
}
|
|
41177
|
+
if (authUser) {
|
|
41178
|
+
logger$e.logDebug("Mock user logged in:", {
|
|
41179
|
+
uid: authUser.uid
|
|
41180
|
+
});
|
|
41181
|
+
this.profileUnsub = this.firestore.listenToDoc("users", authUser.uid, (profile) => {
|
|
41182
|
+
this.userProfile = profile;
|
|
41183
|
+
this.userProfileChangeListeners.forEach((cb) => cb(this.userProfile));
|
|
41184
|
+
});
|
|
41185
|
+
} else {
|
|
41186
|
+
this.userProfile = null;
|
|
41187
|
+
this.userProfileChangeListeners.forEach((cb) => cb(null));
|
|
41188
|
+
}
|
|
41189
|
+
}
|
|
41190
|
+
}
|
|
41003
41191
|
const firebaseDateToDayjs = (date) => {
|
|
41004
41192
|
return dayjs(date.seconds * 1e3);
|
|
41005
41193
|
};
|
|
41006
41194
|
const LOGIN_TRACKING_PERIOD_SECONDS = 1800;
|
|
41007
|
-
const logger$
|
|
41195
|
+
const logger$d = getLogger("firebase");
|
|
41008
41196
|
let firebaseApp = null;
|
|
41009
41197
|
class FirestoreManager {
|
|
41010
41198
|
constructor(firestore) {
|
|
@@ -41132,14 +41320,14 @@ class AuthManager {
|
|
|
41132
41320
|
this.listenToUserProfileUnsub = null;
|
|
41133
41321
|
}
|
|
41134
41322
|
if (authUser) {
|
|
41135
|
-
logger$
|
|
41323
|
+
logger$d.logDebug("User logged in:", {
|
|
41136
41324
|
uid: authUser.uid
|
|
41137
41325
|
});
|
|
41138
41326
|
this.listenToUserProfileUnsub = getFirestoreManager().listenToDoc("users", authUser.uid, (doc2) => {
|
|
41139
41327
|
this.userProfile = doc2;
|
|
41140
41328
|
this.userProfileChangeListeners.forEach((callback) => callback(this.userProfile));
|
|
41141
41329
|
});
|
|
41142
|
-
(async () => {
|
|
41330
|
+
void (async () => {
|
|
41143
41331
|
try {
|
|
41144
41332
|
const firestore = getFirestoreManager();
|
|
41145
41333
|
const userLoggingDocId = authUser.uid;
|
|
@@ -41166,7 +41354,7 @@ class AuthManager {
|
|
|
41166
41354
|
await firestore.mergeDocData("user_logging", userLoggingDocId, userLoggingData);
|
|
41167
41355
|
}
|
|
41168
41356
|
} catch (error) {
|
|
41169
|
-
logger$
|
|
41357
|
+
logger$d.logError("Error logging user login activity:", {
|
|
41170
41358
|
error
|
|
41171
41359
|
});
|
|
41172
41360
|
}
|
|
@@ -41187,8 +41375,24 @@ function getAuthManager() {
|
|
|
41187
41375
|
async function _init$4() {
|
|
41188
41376
|
const {
|
|
41189
41377
|
brandId,
|
|
41190
|
-
config
|
|
41378
|
+
config,
|
|
41379
|
+
testHooks
|
|
41191
41380
|
} = getStaticData();
|
|
41381
|
+
if (testHooks !== void 0) {
|
|
41382
|
+
const seedDocs = {
|
|
41383
|
+
...testHooks.firestore?.docs ?? {}
|
|
41384
|
+
};
|
|
41385
|
+
if (testHooks.auth?.profile) {
|
|
41386
|
+
seedDocs.users = {
|
|
41387
|
+
...seedDocs.users ?? {},
|
|
41388
|
+
[testHooks.auth.uid]: testHooks.auth.profile
|
|
41389
|
+
};
|
|
41390
|
+
}
|
|
41391
|
+
firestoreManager = new MockFirestoreManager(seedDocs);
|
|
41392
|
+
authManager = new MockAuthManager(testHooks.auth ?? null, firestoreManager);
|
|
41393
|
+
logger$d.logDebug("Firebase initialized in MOCK mode (testHooks present)");
|
|
41394
|
+
return;
|
|
41395
|
+
}
|
|
41192
41396
|
{
|
|
41193
41397
|
const {
|
|
41194
41398
|
firebase: sdkFirebaseConfig
|
|
@@ -41271,7 +41475,9 @@ async function execApiRequest(params) {
|
|
|
41271
41475
|
try {
|
|
41272
41476
|
response = await fetch(url, options);
|
|
41273
41477
|
} finally {
|
|
41274
|
-
if (timeoutHandle)
|
|
41478
|
+
if (timeoutHandle) {
|
|
41479
|
+
clearTimeout(timeoutHandle);
|
|
41480
|
+
}
|
|
41275
41481
|
}
|
|
41276
41482
|
if (!response.ok) {
|
|
41277
41483
|
let detail = "";
|
|
@@ -41364,7 +41570,7 @@ async function getStyleByExternalId(brandId, externalId) {
|
|
|
41364
41570
|
recordCache[cacheKey] = record;
|
|
41365
41571
|
return record;
|
|
41366
41572
|
}
|
|
41367
|
-
const logger$
|
|
41573
|
+
const logger$c = getLogger("product");
|
|
41368
41574
|
function _init$2() {
|
|
41369
41575
|
useMainStore.subscribe((state, prevState) => {
|
|
41370
41576
|
if (state.userHasAvatar && !prevState.userHasAvatar) {
|
|
@@ -41397,11 +41603,11 @@ function loadProductDataToStore(externalId) {
|
|
|
41397
41603
|
try {
|
|
41398
41604
|
const productData2 = await loadProductData(externalId);
|
|
41399
41605
|
useMainStore.getState().setProductData(productData2.externalId, productData2);
|
|
41400
|
-
logger$
|
|
41606
|
+
logger$c.logDebug(`Loaded product data for externalId: ${externalId}`, {
|
|
41401
41607
|
productData: productData2
|
|
41402
41608
|
});
|
|
41403
41609
|
} catch (error) {
|
|
41404
|
-
logger$
|
|
41610
|
+
logger$c.logError(`Error loading product data for externalId: ${externalId}`, {
|
|
41405
41611
|
error
|
|
41406
41612
|
});
|
|
41407
41613
|
}
|
|
@@ -41414,9 +41620,9 @@ function loadProductDataToStore(externalId) {
|
|
|
41414
41620
|
if (productData[externalId] || !userIsLoggedIn || userHasAvatar === false) {
|
|
41415
41621
|
return;
|
|
41416
41622
|
}
|
|
41417
|
-
loadAndStore();
|
|
41623
|
+
void loadAndStore();
|
|
41418
41624
|
}
|
|
41419
|
-
const logger$
|
|
41625
|
+
const logger$b = getLogger("style-categories");
|
|
41420
41626
|
let cached = null;
|
|
41421
41627
|
let inflight = null;
|
|
41422
41628
|
function buildIndex(categories, groups) {
|
|
@@ -41460,7 +41666,7 @@ async function loadStyleCategoryIndex() {
|
|
|
41460
41666
|
}
|
|
41461
41667
|
inflight = (async () => {
|
|
41462
41668
|
try {
|
|
41463
|
-
logger$
|
|
41669
|
+
logger$b.logDebug("{{ts}} - Loading style-category index");
|
|
41464
41670
|
const [categories, groups] = await Promise.all([getStyleCategories(), getStyleCategoryGroups()]);
|
|
41465
41671
|
cached = buildIndex(categories, groups);
|
|
41466
41672
|
return cached;
|
|
@@ -41473,12 +41679,12 @@ async function loadStyleCategoryIndex() {
|
|
|
41473
41679
|
function peekStyleCategoryIndex() {
|
|
41474
41680
|
return cached;
|
|
41475
41681
|
}
|
|
41476
|
-
const logger$
|
|
41682
|
+
const logger$a = getLogger("fitting-room-data");
|
|
41477
41683
|
async function loadFittingRoomData() {
|
|
41478
41684
|
const state = useMainStore.getState();
|
|
41479
41685
|
const items = state.fittingRoom;
|
|
41480
41686
|
loadStyleCategoryIndex().catch((error) => {
|
|
41481
|
-
logger$
|
|
41687
|
+
logger$a.logError("Failed to load style-category index", {
|
|
41482
41688
|
error
|
|
41483
41689
|
});
|
|
41484
41690
|
});
|
|
@@ -41490,7 +41696,9 @@ async function loadFittingRoomData() {
|
|
|
41490
41696
|
} = getStaticData();
|
|
41491
41697
|
if (!productLookup) {
|
|
41492
41698
|
for (const item of items) {
|
|
41493
|
-
if (state.merchantProductData[item.externalId])
|
|
41699
|
+
if (state.merchantProductData[item.externalId]) {
|
|
41700
|
+
continue;
|
|
41701
|
+
}
|
|
41494
41702
|
state.setMerchantProductData(item.externalId, {
|
|
41495
41703
|
error: new Error("No productLookup callback configured")
|
|
41496
41704
|
});
|
|
@@ -41526,7 +41734,7 @@ async function loadFittingRoomData() {
|
|
|
41526
41734
|
}
|
|
41527
41735
|
}
|
|
41528
41736
|
} catch (error) {
|
|
41529
|
-
logger$
|
|
41737
|
+
logger$a.logError("productLookup batch failed", {
|
|
41530
41738
|
error,
|
|
41531
41739
|
handles
|
|
41532
41740
|
});
|
|
@@ -41561,7 +41769,7 @@ function resolveItem(item, merchantSlot, loadedSlot, index) {
|
|
|
41561
41769
|
const found = loadedProduct.sizeFitRecommendation.available_sizes.some((sz) => sz.colorway_size_assets.some((csa) => csa.id === item.colorwaySizeAssetId));
|
|
41562
41770
|
if (!found) {
|
|
41563
41771
|
needsResize = true;
|
|
41564
|
-
logger$
|
|
41772
|
+
logger$a.logDebug("csa no longer in size rec, marking needsResize", {
|
|
41565
41773
|
externalId: item.externalId,
|
|
41566
41774
|
csa: item.colorwaySizeAssetId
|
|
41567
41775
|
});
|
|
@@ -41611,7 +41819,7 @@ function useResolvedFittingRoom() {
|
|
|
41611
41819
|
}, []);
|
|
41612
41820
|
reactExports.useEffect(() => {
|
|
41613
41821
|
loadFittingRoomData().catch((error) => {
|
|
41614
|
-
logger$
|
|
41822
|
+
logger$a.logError("loadFittingRoomData failed", {
|
|
41615
41823
|
error
|
|
41616
41824
|
});
|
|
41617
41825
|
});
|
|
@@ -41662,12 +41870,14 @@ function buildVtoProductDataFromResolved(item) {
|
|
|
41662
41870
|
merchantProduct,
|
|
41663
41871
|
loadedProduct
|
|
41664
41872
|
} = item;
|
|
41665
|
-
if (!merchantProduct || !loadedProduct)
|
|
41873
|
+
if (!merchantProduct || !loadedProduct) {
|
|
41874
|
+
return null;
|
|
41875
|
+
}
|
|
41666
41876
|
const sizeRec = loadedProduct.sizeFitRecommendation;
|
|
41667
41877
|
const recommendedSizeId = sizeRec.recommended_size.id || null;
|
|
41668
41878
|
const recommendedSizeLabel = getSizeLabelFromSize(sizeRec.recommended_size);
|
|
41669
41879
|
if (recommendedSizeId == null || !recommendedSizeLabel) {
|
|
41670
|
-
logger$
|
|
41880
|
+
logger$a.logWarn("Missing recommended size for item", {
|
|
41671
41881
|
externalId: item.externalId
|
|
41672
41882
|
});
|
|
41673
41883
|
return null;
|
|
@@ -41675,13 +41885,19 @@ function buildVtoProductDataFromResolved(item) {
|
|
|
41675
41885
|
const sizes = [];
|
|
41676
41886
|
for (const sizeRecord of sizeRec.available_sizes) {
|
|
41677
41887
|
const sizeLabel = getSizeLabelFromSize(sizeRecord);
|
|
41678
|
-
if (!sizeLabel)
|
|
41888
|
+
if (!sizeLabel) {
|
|
41889
|
+
continue;
|
|
41890
|
+
}
|
|
41679
41891
|
const fit = sizeRec.fits.find((f) => f.size_id === sizeRecord.id);
|
|
41680
|
-
if (!fit)
|
|
41892
|
+
if (!fit) {
|
|
41893
|
+
continue;
|
|
41894
|
+
}
|
|
41681
41895
|
const colors = [];
|
|
41682
41896
|
for (const csa of sizeRecord.colorway_size_assets) {
|
|
41683
41897
|
const variant = merchantProduct.variants.find((v) => v.sku === csa.sku);
|
|
41684
|
-
if (!variant)
|
|
41898
|
+
if (!variant) {
|
|
41899
|
+
continue;
|
|
41900
|
+
}
|
|
41685
41901
|
colors.push({
|
|
41686
41902
|
colorwaySizeAssetId: csa.id,
|
|
41687
41903
|
colorLabel: variant.color || null,
|
|
@@ -41689,7 +41905,9 @@ function buildVtoProductDataFromResolved(item) {
|
|
|
41689
41905
|
priceFormatted: variant.priceFormatted
|
|
41690
41906
|
});
|
|
41691
41907
|
}
|
|
41692
|
-
if (colors.length === 0)
|
|
41908
|
+
if (colors.length === 0) {
|
|
41909
|
+
continue;
|
|
41910
|
+
}
|
|
41693
41911
|
sizes.push({
|
|
41694
41912
|
sizeId: sizeRecord.id,
|
|
41695
41913
|
sizeLabel,
|
|
@@ -41698,7 +41916,9 @@ function buildVtoProductDataFromResolved(item) {
|
|
|
41698
41916
|
colors
|
|
41699
41917
|
});
|
|
41700
41918
|
}
|
|
41701
|
-
if (sizes.length === 0)
|
|
41919
|
+
if (sizes.length === 0) {
|
|
41920
|
+
return null;
|
|
41921
|
+
}
|
|
41702
41922
|
return {
|
|
41703
41923
|
productName: merchantProduct.productName,
|
|
41704
41924
|
productDescriptionHtml: merchantProduct.productDescriptionHtml,
|
|
@@ -41711,12 +41931,16 @@ function buildVtoProductDataFromResolved(item) {
|
|
|
41711
41931
|
}
|
|
41712
41932
|
function findRecommendedColorSize(data, preferredColor) {
|
|
41713
41933
|
const recommended = data.sizes.find((s) => s.isRecommended);
|
|
41714
|
-
if (!recommended || recommended.colors.length === 0)
|
|
41934
|
+
if (!recommended || recommended.colors.length === 0) {
|
|
41935
|
+
return null;
|
|
41936
|
+
}
|
|
41715
41937
|
return recommended.colors.find((c) => c.colorLabel === preferredColor) ?? recommended.colors[0];
|
|
41716
41938
|
}
|
|
41717
41939
|
function findCsaByLabel(data, sizeLabel, preferredColor) {
|
|
41718
41940
|
const sizeRecord = data.sizes.find((s) => s.sizeLabel === sizeLabel);
|
|
41719
|
-
if (!sizeRecord || sizeRecord.colors.length === 0)
|
|
41941
|
+
if (!sizeRecord || sizeRecord.colors.length === 0) {
|
|
41942
|
+
return null;
|
|
41943
|
+
}
|
|
41720
41944
|
return sizeRecord.colors.find((c) => c.colorLabel === preferredColor) ?? sizeRecord.colors[0];
|
|
41721
41945
|
}
|
|
41722
41946
|
function useMobileSheetSnap(initial = "collapsed") {
|
|
@@ -41726,7 +41950,9 @@ function useMobileSheetSnap(initial = "collapsed") {
|
|
|
41726
41950
|
const initialSnap = snap;
|
|
41727
41951
|
const onTouchMove = (moveEvent) => {
|
|
41728
41952
|
const deltaY = moveEvent.touches[0].clientY - startY;
|
|
41729
|
-
if (Math.abs(deltaY) < 30)
|
|
41953
|
+
if (Math.abs(deltaY) < 30) {
|
|
41954
|
+
return;
|
|
41955
|
+
}
|
|
41730
41956
|
if (deltaY > 0) {
|
|
41731
41957
|
if (initialSnap === "full" || initialSnap === "expanded") {
|
|
41732
41958
|
setSnap("collapsed");
|
|
@@ -41755,11 +41981,16 @@ function useMobileSheetSnap(initial = "collapsed") {
|
|
|
41755
41981
|
}
|
|
41756
41982
|
const MAX_OUTFIT_ITEMS = 4;
|
|
41757
41983
|
function asStringList(value) {
|
|
41758
|
-
if (!Array.isArray(value))
|
|
41984
|
+
if (!Array.isArray(value)) {
|
|
41985
|
+
return [];
|
|
41986
|
+
}
|
|
41759
41987
|
const out = [];
|
|
41760
41988
|
for (const v of value) {
|
|
41761
|
-
if (typeof v === "string")
|
|
41762
|
-
|
|
41989
|
+
if (typeof v === "string") {
|
|
41990
|
+
out.push(v);
|
|
41991
|
+
} else if (v != null) {
|
|
41992
|
+
out.push(String(v));
|
|
41993
|
+
}
|
|
41763
41994
|
}
|
|
41764
41995
|
return out;
|
|
41765
41996
|
}
|
|
@@ -41800,8 +42031,12 @@ function computeAvailability(item, selectedExternalIds, resolved) {
|
|
|
41800
42031
|
}
|
|
41801
42032
|
const itemCat = item.styleCategory;
|
|
41802
42033
|
for (const sel of resolved.items) {
|
|
41803
|
-
if (!selectedExternalIds.has(sel.externalId))
|
|
41804
|
-
|
|
42034
|
+
if (!selectedExternalIds.has(sel.externalId)) {
|
|
42035
|
+
continue;
|
|
42036
|
+
}
|
|
42037
|
+
if (!sel.styleCategory) {
|
|
42038
|
+
continue;
|
|
42039
|
+
}
|
|
41805
42040
|
if (!pairCompatible(sel.styleCategory, itemCat, sel.styleCategoryGroup)) {
|
|
41806
42041
|
return "disabled";
|
|
41807
42042
|
}
|
|
@@ -41809,8 +42044,12 @@ function computeAvailability(item, selectedExternalIds, resolved) {
|
|
|
41809
42044
|
return "available";
|
|
41810
42045
|
}
|
|
41811
42046
|
function makeOutfitItem(r, forceUntuck) {
|
|
41812
|
-
if (!r.styleCategory)
|
|
41813
|
-
|
|
42047
|
+
if (!r.styleCategory) {
|
|
42048
|
+
return null;
|
|
42049
|
+
}
|
|
42050
|
+
if (r.storage.colorwaySizeAssetId == null) {
|
|
42051
|
+
return null;
|
|
42052
|
+
}
|
|
41814
42053
|
const tuckable = !!r.styleCategory.tuckable;
|
|
41815
42054
|
const untucked = forceUntuck && tuckable;
|
|
41816
42055
|
const layerOrder = untucked ? r.styleCategory.layer_order_untucked : r.styleCategory.layer_order;
|
|
@@ -41830,10 +42069,16 @@ function buildOutfit(selectedExternalIds, resolved, forceUntuck, lastAddedExtern
|
|
|
41830
42069
|
const entries = [];
|
|
41831
42070
|
let lastAddedResolved = null;
|
|
41832
42071
|
for (const r of resolved.items) {
|
|
41833
|
-
if (!selectedExternalIds.has(r.externalId))
|
|
41834
|
-
|
|
42072
|
+
if (!selectedExternalIds.has(r.externalId)) {
|
|
42073
|
+
continue;
|
|
42074
|
+
}
|
|
42075
|
+
if (r.externalId === lastAddedExternalId) {
|
|
42076
|
+
lastAddedResolved = r;
|
|
42077
|
+
}
|
|
41835
42078
|
const entry = makeOutfitItem(r, forceUntuck);
|
|
41836
|
-
if (entry)
|
|
42079
|
+
if (entry) {
|
|
42080
|
+
entries.push(entry);
|
|
42081
|
+
}
|
|
41837
42082
|
}
|
|
41838
42083
|
entries.sort((a, b) => a.layerOrder - b.layerOrder);
|
|
41839
42084
|
const items = entries.slice(0, MAX_OUTFIT_ITEMS).map((e) => e.outfitItem);
|
|
@@ -41844,10 +42089,16 @@ function buildOutfit(selectedExternalIds, resolved, forceUntuck, lastAddedExtern
|
|
|
41844
42089
|
};
|
|
41845
42090
|
}
|
|
41846
42091
|
function buildAlternateOutfits(primary, lastAddedResolved) {
|
|
41847
|
-
if (!lastAddedResolved || !lastAddedResolved.loadedProduct)
|
|
42092
|
+
if (!lastAddedResolved || !lastAddedResolved.loadedProduct) {
|
|
42093
|
+
return [];
|
|
42094
|
+
}
|
|
41848
42095
|
const currentCsaId = lastAddedResolved.storage.colorwaySizeAssetId;
|
|
41849
|
-
if (currentCsaId == null)
|
|
41850
|
-
|
|
42096
|
+
if (currentCsaId == null) {
|
|
42097
|
+
return [];
|
|
42098
|
+
}
|
|
42099
|
+
if (!primary.some((i) => i.externalId === lastAddedResolved.externalId)) {
|
|
42100
|
+
return [];
|
|
42101
|
+
}
|
|
41851
42102
|
const sizeRec = lastAddedResolved.loadedProduct.sizeFitRecommendation;
|
|
41852
42103
|
let currentColorwayId = null;
|
|
41853
42104
|
for (const sz of sizeRec.available_sizes) {
|
|
@@ -41860,7 +42111,9 @@ function buildAlternateOutfits(primary, lastAddedResolved) {
|
|
|
41860
42111
|
const out = [];
|
|
41861
42112
|
for (const sz of sizeRec.available_sizes) {
|
|
41862
42113
|
const altCsa = sz.colorway_size_assets.find((c) => c.id !== currentCsaId && (currentColorwayId == null || c.colorway_id === currentColorwayId));
|
|
41863
|
-
if (!altCsa)
|
|
42114
|
+
if (!altCsa) {
|
|
42115
|
+
continue;
|
|
42116
|
+
}
|
|
41864
42117
|
const alternate = primary.map((it) => it.externalId === lastAddedResolved.externalId ? {
|
|
41865
42118
|
...it,
|
|
41866
42119
|
colorwaySizeAssetId: altCsa.id
|
|
@@ -41869,6 +42122,24 @@ function buildAlternateOutfits(primary, lastAddedResolved) {
|
|
|
41869
42122
|
}
|
|
41870
42123
|
return out;
|
|
41871
42124
|
}
|
|
42125
|
+
function pillBaseStyle(theme) {
|
|
42126
|
+
return {
|
|
42127
|
+
display: "inline-flex",
|
|
42128
|
+
alignItems: "center",
|
|
42129
|
+
gap: "8px",
|
|
42130
|
+
padding: "8px 16px",
|
|
42131
|
+
borderRadius: "24px",
|
|
42132
|
+
backgroundColor: "rgba(255, 255, 255, 0.95)",
|
|
42133
|
+
border: `1px solid ${theme.color_fg_text}`,
|
|
42134
|
+
fontSize: "12px",
|
|
42135
|
+
fontWeight: "500",
|
|
42136
|
+
letterSpacing: "0.5px",
|
|
42137
|
+
textTransform: "uppercase",
|
|
42138
|
+
cursor: "pointer",
|
|
42139
|
+
userSelect: "none",
|
|
42140
|
+
WebkitUserSelect: "none"
|
|
42141
|
+
};
|
|
42142
|
+
}
|
|
41872
42143
|
function AvatarControls({
|
|
41873
42144
|
selectedItems,
|
|
41874
42145
|
canTuck,
|
|
@@ -41885,7 +42156,9 @@ function AvatarControls({
|
|
|
41885
42156
|
const [popoverOpen, setPopoverOpen] = reactExports.useState(false);
|
|
41886
42157
|
const popoverWrapperRef = reactExports.useRef(null);
|
|
41887
42158
|
reactExports.useEffect(() => {
|
|
41888
|
-
if (!popoverOpen)
|
|
42159
|
+
if (!popoverOpen) {
|
|
42160
|
+
return;
|
|
42161
|
+
}
|
|
41889
42162
|
const onDocClick = (e) => {
|
|
41890
42163
|
if (popoverWrapperRef.current && !popoverWrapperRef.current.contains(e.target)) {
|
|
41891
42164
|
setPopoverOpen(false);
|
|
@@ -41908,20 +42181,7 @@ function AvatarControls({
|
|
|
41908
42181
|
alignItems: "flex-end"
|
|
41909
42182
|
},
|
|
41910
42183
|
pill: {
|
|
41911
|
-
|
|
41912
|
-
alignItems: "center",
|
|
41913
|
-
gap: "8px",
|
|
41914
|
-
padding: "8px 16px",
|
|
41915
|
-
borderRadius: "24px",
|
|
41916
|
-
backgroundColor: "rgba(255, 255, 255, 0.95)",
|
|
41917
|
-
border: `1px solid ${theme.color_fg_text}`,
|
|
41918
|
-
fontSize: "12px",
|
|
41919
|
-
fontWeight: "500",
|
|
41920
|
-
letterSpacing: "0.5px",
|
|
41921
|
-
textTransform: "uppercase",
|
|
41922
|
-
cursor: "pointer",
|
|
41923
|
-
userSelect: "none",
|
|
41924
|
-
WebkitUserSelect: "none",
|
|
42184
|
+
...pillBaseStyle(theme),
|
|
41925
42185
|
transition: "padding 500ms cubic-bezier(0.22, 1, 0.36, 1), gap 500ms cubic-bezier(0.22, 1, 0.36, 1)"
|
|
41926
42186
|
},
|
|
41927
42187
|
pillCollapsed: {
|
|
@@ -42023,13 +42283,114 @@ function AvatarControls({
|
|
|
42023
42283
|
] })
|
|
42024
42284
|
] });
|
|
42025
42285
|
}
|
|
42286
|
+
function MobileTuckControl({
|
|
42287
|
+
canTuck,
|
|
42288
|
+
forceUntuck,
|
|
42289
|
+
onToggleUntuck
|
|
42290
|
+
}) {
|
|
42291
|
+
const {
|
|
42292
|
+
t
|
|
42293
|
+
} = useTranslation();
|
|
42294
|
+
const css2 = useCss((theme) => ({
|
|
42295
|
+
wrapper: {
|
|
42296
|
+
position: "absolute",
|
|
42297
|
+
bottom: "12px",
|
|
42298
|
+
right: "12px"
|
|
42299
|
+
// No z-index: the pill sits above the avatar image (later sibling than
|
|
42300
|
+
// the frame viewer) but below the product-details sheet, which is a
|
|
42301
|
+
// later sibling in the try-on view — so the sheet hides the pill when
|
|
42302
|
+
// it expands over the image.
|
|
42303
|
+
},
|
|
42304
|
+
pill: pillBaseStyle(theme),
|
|
42305
|
+
pillIcon: {
|
|
42306
|
+
width: "14px",
|
|
42307
|
+
height: "14px",
|
|
42308
|
+
flex: "none"
|
|
42309
|
+
}
|
|
42310
|
+
}));
|
|
42311
|
+
if (!canTuck) {
|
|
42312
|
+
return null;
|
|
42313
|
+
}
|
|
42314
|
+
return /* @__PURE__ */ jsx$1("div", { css: css2.wrapper, children: /* @__PURE__ */ jsxs(Button, { variant: "base", css: css2.pill, onClick: onToggleUntuck, children: [
|
|
42315
|
+
/* @__PURE__ */ jsx$1(SvgIconTuck, { css: css2.pillIcon }),
|
|
42316
|
+
/* @__PURE__ */ jsx$1(Text, { variant: "base", children: t(forceUntuck ? "fitting_room.tuck_in" : "fitting_room.untuck") })
|
|
42317
|
+
] }) });
|
|
42318
|
+
}
|
|
42319
|
+
const DRAG_STEP_PX = 50;
|
|
42320
|
+
function useFrameRotation(frameUrls, setSelectedFrameIndex) {
|
|
42321
|
+
const frameCount = frameUrls?.length ?? 0;
|
|
42322
|
+
const rotateLeft = reactExports.useCallback(() => {
|
|
42323
|
+
setSelectedFrameIndex((prev2) => {
|
|
42324
|
+
if (prev2 == null || frameCount === 0) {
|
|
42325
|
+
return prev2;
|
|
42326
|
+
}
|
|
42327
|
+
return prev2 === 0 ? frameCount - 1 : prev2 - 1;
|
|
42328
|
+
});
|
|
42329
|
+
}, [frameCount, setSelectedFrameIndex]);
|
|
42330
|
+
const rotateRight = reactExports.useCallback(() => {
|
|
42331
|
+
setSelectedFrameIndex((prev2) => {
|
|
42332
|
+
if (prev2 == null || frameCount === 0) {
|
|
42333
|
+
return prev2;
|
|
42334
|
+
}
|
|
42335
|
+
return prev2 === frameCount - 1 ? 0 : prev2 + 1;
|
|
42336
|
+
});
|
|
42337
|
+
}, [frameCount, setSelectedFrameIndex]);
|
|
42338
|
+
const handleMouseDragStart = reactExports.useCallback((e) => {
|
|
42339
|
+
e.preventDefault();
|
|
42340
|
+
let startX = e.clientX;
|
|
42341
|
+
const onMove = (move) => {
|
|
42342
|
+
const deltaX = move.clientX - startX;
|
|
42343
|
+
if (Math.abs(deltaX) >= DRAG_STEP_PX) {
|
|
42344
|
+
if (deltaX > 0) {
|
|
42345
|
+
rotateRight();
|
|
42346
|
+
} else {
|
|
42347
|
+
rotateLeft();
|
|
42348
|
+
}
|
|
42349
|
+
startX = move.clientX;
|
|
42350
|
+
}
|
|
42351
|
+
};
|
|
42352
|
+
const onUp = () => {
|
|
42353
|
+
window.removeEventListener("mousemove", onMove);
|
|
42354
|
+
window.removeEventListener("mouseup", onUp);
|
|
42355
|
+
};
|
|
42356
|
+
window.addEventListener("mousemove", onMove);
|
|
42357
|
+
window.addEventListener("mouseup", onUp);
|
|
42358
|
+
}, [rotateLeft, rotateRight]);
|
|
42359
|
+
const handleTouchDragStart = reactExports.useCallback((e) => {
|
|
42360
|
+
e.preventDefault();
|
|
42361
|
+
let startX = e.touches[0].clientX;
|
|
42362
|
+
const onMove = (move) => {
|
|
42363
|
+
const deltaX = move.touches[0].clientX - startX;
|
|
42364
|
+
if (Math.abs(deltaX) >= DRAG_STEP_PX) {
|
|
42365
|
+
if (deltaX > 0) {
|
|
42366
|
+
rotateRight();
|
|
42367
|
+
} else {
|
|
42368
|
+
rotateLeft();
|
|
42369
|
+
}
|
|
42370
|
+
startX = move.touches[0].clientX;
|
|
42371
|
+
}
|
|
42372
|
+
};
|
|
42373
|
+
const onEnd = () => {
|
|
42374
|
+
window.removeEventListener("touchmove", onMove);
|
|
42375
|
+
window.removeEventListener("touchend", onEnd);
|
|
42376
|
+
};
|
|
42377
|
+
window.addEventListener("touchmove", onMove);
|
|
42378
|
+
window.addEventListener("touchend", onEnd);
|
|
42379
|
+
}, [rotateLeft, rotateRight]);
|
|
42380
|
+
return {
|
|
42381
|
+
rotateLeft,
|
|
42382
|
+
rotateRight,
|
|
42383
|
+
handleMouseDragStart,
|
|
42384
|
+
handleTouchDragStart
|
|
42385
|
+
};
|
|
42386
|
+
}
|
|
42026
42387
|
function AvatarFrameViewer({
|
|
42027
42388
|
frameUrls,
|
|
42028
42389
|
selectedFrameIndex,
|
|
42029
42390
|
setSelectedFrameIndex,
|
|
42030
42391
|
imageContainerStyle,
|
|
42031
42392
|
imageStyle,
|
|
42032
|
-
loadingT = "
|
|
42393
|
+
loadingT = "quick-view.avatar_loading"
|
|
42033
42394
|
}) {
|
|
42034
42395
|
const css2 = useCss((_theme) => ({
|
|
42035
42396
|
imageContainer: {
|
|
@@ -42073,59 +42434,17 @@ function AvatarFrameViewer({
|
|
|
42073
42434
|
}, 500);
|
|
42074
42435
|
return () => clearInterval(intervalId);
|
|
42075
42436
|
}, [frameUrls, selectedFrameIndex, setSelectedFrameIndex]);
|
|
42076
|
-
const
|
|
42077
|
-
|
|
42078
|
-
|
|
42079
|
-
|
|
42080
|
-
|
|
42081
|
-
}
|
|
42082
|
-
const rotateRight = reactExports.useCallback(() => {
|
|
42083
|
-
setSelectedFrameIndex((prevIndex) => {
|
|
42084
|
-
if (prevIndex == null) return null;
|
|
42085
|
-
return prevIndex === (frameUrls ? frameUrls.length - 1 : 0) ? 0 : prevIndex + 1;
|
|
42086
|
-
});
|
|
42087
|
-
}, [frameUrls, setSelectedFrameIndex]);
|
|
42088
|
-
const handleImageMouseDrag = reactExports.useCallback((e) => {
|
|
42089
|
-
e.preventDefault();
|
|
42090
|
-
let startX = e.clientX;
|
|
42091
|
-
const onMouseMove = (moveEvent) => {
|
|
42092
|
-
const deltaX = moveEvent.clientX - startX;
|
|
42093
|
-
if (Math.abs(deltaX) >= 50) {
|
|
42094
|
-
if (deltaX > 0) rotateRight();
|
|
42095
|
-
else rotateLeft();
|
|
42096
|
-
startX = moveEvent.clientX;
|
|
42097
|
-
}
|
|
42098
|
-
};
|
|
42099
|
-
const onMouseUp = () => {
|
|
42100
|
-
window.removeEventListener("mousemove", onMouseMove);
|
|
42101
|
-
window.removeEventListener("mouseup", onMouseUp);
|
|
42102
|
-
};
|
|
42103
|
-
window.addEventListener("mousemove", onMouseMove);
|
|
42104
|
-
window.addEventListener("mouseup", onMouseUp);
|
|
42105
|
-
}, [rotateLeft, rotateRight]);
|
|
42106
|
-
const handleImageTouchDrag = reactExports.useCallback((e) => {
|
|
42107
|
-
e.preventDefault();
|
|
42108
|
-
let startX = e.touches[0].clientX;
|
|
42109
|
-
const onTouchMove = (moveEvent) => {
|
|
42110
|
-
const deltaX = moveEvent.touches[0].clientX - startX;
|
|
42111
|
-
if (Math.abs(deltaX) >= 50) {
|
|
42112
|
-
if (deltaX > 0) rotateRight();
|
|
42113
|
-
else rotateLeft();
|
|
42114
|
-
startX = moveEvent.touches[0].clientX;
|
|
42115
|
-
}
|
|
42116
|
-
};
|
|
42117
|
-
const onTouchEnd = () => {
|
|
42118
|
-
window.removeEventListener("touchmove", onTouchMove);
|
|
42119
|
-
window.removeEventListener("touchend", onTouchEnd);
|
|
42120
|
-
};
|
|
42121
|
-
window.addEventListener("touchmove", onTouchMove);
|
|
42122
|
-
window.addEventListener("touchend", onTouchEnd);
|
|
42123
|
-
}, [rotateLeft, rotateRight]);
|
|
42437
|
+
const {
|
|
42438
|
+
rotateLeft,
|
|
42439
|
+
rotateRight,
|
|
42440
|
+
handleMouseDragStart,
|
|
42441
|
+
handleTouchDragStart
|
|
42442
|
+
} = useFrameRotation(frameUrls, setSelectedFrameIndex);
|
|
42124
42443
|
if (!frameUrls || selectedFrameIndex == null) {
|
|
42125
42444
|
return /* @__PURE__ */ jsx$1(Loading, { t: loadingT });
|
|
42126
42445
|
}
|
|
42127
42446
|
return /* @__PURE__ */ jsxs("div", { css: css2.imageContainer, style: imageContainerStyle, children: [
|
|
42128
|
-
/* @__PURE__ */ jsx$1("img", { src: frameUrls[selectedFrameIndex], css: css2.image, style: imageStyle, onMouseDown:
|
|
42447
|
+
/* @__PURE__ */ jsx$1("img", { src: frameUrls[selectedFrameIndex], css: css2.image, style: imageStyle, onMouseDown: handleMouseDragStart, onTouchStart: handleTouchDragStart }),
|
|
42129
42448
|
/* @__PURE__ */ jsx$1("div", { css: css2.chevronLeftContainer, onClick: rotateLeft, children: /* @__PURE__ */ jsx$1(SvgChevronLeft, { css: css2.chevronIcon }) }),
|
|
42130
42449
|
/* @__PURE__ */ jsx$1("div", { css: css2.chevronRightContainer, onClick: rotateRight, children: /* @__PURE__ */ jsx$1(SvgChevronRight, { css: css2.chevronIcon }) })
|
|
42131
42450
|
] });
|
|
@@ -42134,9 +42453,13 @@ function AvatarPane({
|
|
|
42134
42453
|
frameUrls,
|
|
42135
42454
|
hasSelection,
|
|
42136
42455
|
controls,
|
|
42137
|
-
mobileFullscreen
|
|
42456
|
+
mobileFullscreen,
|
|
42457
|
+
selectedFrameIndex: indexProp,
|
|
42458
|
+
setSelectedFrameIndex: setIndexProp
|
|
42138
42459
|
}) {
|
|
42139
|
-
const [
|
|
42460
|
+
const [localFrameIndex, setLocalFrameIndex] = reactExports.useState(null);
|
|
42461
|
+
const selectedFrameIndex = indexProp !== void 0 ? indexProp : localFrameIndex;
|
|
42462
|
+
const setSelectedFrameIndex = setIndexProp ?? setLocalFrameIndex;
|
|
42140
42463
|
const css2 = useCss((theme) => ({
|
|
42141
42464
|
container: {
|
|
42142
42465
|
width: "100%",
|
|
@@ -42194,12 +42517,14 @@ function AvatarPane({
|
|
|
42194
42517
|
}
|
|
42195
42518
|
}));
|
|
42196
42519
|
if (frameUrls && frameUrls.length > 0) {
|
|
42197
|
-
const viewer = /* @__PURE__ */ jsx$1(AvatarFrameViewer, { frameUrls, selectedFrameIndex, setSelectedFrameIndex, imageContainerStyle: css2.frameContainer, imageStyle: css2.frameImage, loadingT: "
|
|
42520
|
+
const viewer = /* @__PURE__ */ jsx$1(AvatarFrameViewer, { frameUrls, selectedFrameIndex, setSelectedFrameIndex, imageContainerStyle: css2.frameContainer, imageStyle: css2.frameImage, loadingT: "quick-view.avatar_loading" });
|
|
42198
42521
|
if (mobileFullscreen) {
|
|
42199
42522
|
return /* @__PURE__ */ jsxs("div", { css: css2.mobileContainer, children: [
|
|
42200
|
-
/* @__PURE__ */
|
|
42201
|
-
|
|
42202
|
-
|
|
42523
|
+
/* @__PURE__ */ jsxs("div", { css: css2.mobileFrameSlot, children: [
|
|
42524
|
+
viewer,
|
|
42525
|
+
controls
|
|
42526
|
+
] }),
|
|
42527
|
+
/* @__PURE__ */ jsx$1("div", { css: css2.bottomFiller, children: " " })
|
|
42203
42528
|
] });
|
|
42204
42529
|
}
|
|
42205
42530
|
return /* @__PURE__ */ jsxs("div", { css: css2.container, children: [
|
|
@@ -42208,7 +42533,7 @@ function AvatarPane({
|
|
|
42208
42533
|
] });
|
|
42209
42534
|
}
|
|
42210
42535
|
if (hasSelection) {
|
|
42211
|
-
return /* @__PURE__ */ jsx$1("div", { css: css2.container, children: /* @__PURE__ */ jsx$1(Loading, { t: "
|
|
42536
|
+
return /* @__PURE__ */ jsx$1("div", { css: css2.container, children: /* @__PURE__ */ jsx$1(Loading, { t: "quick-view.avatar_loading" }) });
|
|
42212
42537
|
}
|
|
42213
42538
|
return /* @__PURE__ */ jsxs("div", { css: css2.container, children: [
|
|
42214
42539
|
/* @__PURE__ */ jsx$1("div", { css: css2.placeholder, children: /* @__PURE__ */ jsx$1(TextT, { variant: "base", t: "fitting_room.avatar_placeholder_empty" }) }),
|
|
@@ -42313,7 +42638,9 @@ function ProductCard({
|
|
|
42313
42638
|
const disabled = availability === "disabled";
|
|
42314
42639
|
const selected = availability === "selected";
|
|
42315
42640
|
const handleClick = () => {
|
|
42316
|
-
if (disabled)
|
|
42641
|
+
if (disabled) {
|
|
42642
|
+
return;
|
|
42643
|
+
}
|
|
42317
42644
|
onClick();
|
|
42318
42645
|
};
|
|
42319
42646
|
const name2 = item.merchantProduct?.productName ?? item.externalId;
|
|
@@ -42359,9 +42686,13 @@ function CardRail({
|
|
|
42359
42686
|
setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
|
|
42360
42687
|
}, []);
|
|
42361
42688
|
reactExports.useLayoutEffect(() => {
|
|
42362
|
-
if (collapsed)
|
|
42689
|
+
if (collapsed) {
|
|
42690
|
+
return;
|
|
42691
|
+
}
|
|
42363
42692
|
const el = scrollRef.current;
|
|
42364
|
-
if (!el)
|
|
42693
|
+
if (!el) {
|
|
42694
|
+
return;
|
|
42695
|
+
}
|
|
42365
42696
|
updateScrollState();
|
|
42366
42697
|
const observer = new ResizeObserver(updateScrollState);
|
|
42367
42698
|
observer.observe(el);
|
|
@@ -42369,7 +42700,9 @@ function CardRail({
|
|
|
42369
42700
|
}, [collapsed, group.items, updateScrollState]);
|
|
42370
42701
|
const scrollByPage = reactExports.useCallback((dir) => {
|
|
42371
42702
|
const el = scrollRef.current;
|
|
42372
|
-
if (!el)
|
|
42703
|
+
if (!el) {
|
|
42704
|
+
return;
|
|
42705
|
+
}
|
|
42373
42706
|
el.scrollBy({
|
|
42374
42707
|
left: dir * el.clientWidth * 0.8,
|
|
42375
42708
|
behavior: "smooth"
|
|
@@ -42462,7 +42795,36 @@ function CardRail({
|
|
|
42462
42795
|
function AddToCartButton({
|
|
42463
42796
|
onClick
|
|
42464
42797
|
}) {
|
|
42465
|
-
return /* @__PURE__ */ jsx$1(ButtonT, { variant: "brand", t: "
|
|
42798
|
+
return /* @__PURE__ */ jsx$1(ButtonT, { variant: "brand", t: "quick-view.add_to_cart", onClick });
|
|
42799
|
+
}
|
|
42800
|
+
function ColorSelector({
|
|
42801
|
+
availableColorLabels,
|
|
42802
|
+
selectedColorLabel,
|
|
42803
|
+
onChangeColor
|
|
42804
|
+
}) {
|
|
42805
|
+
const css2 = useCss((theme) => ({
|
|
42806
|
+
colorContainer: {},
|
|
42807
|
+
colorLabelText: {
|
|
42808
|
+
fontSize: "12px"
|
|
42809
|
+
},
|
|
42810
|
+
colorSelect: {
|
|
42811
|
+
border: "none",
|
|
42812
|
+
color: theme.color_fg_text,
|
|
42813
|
+
fontFamily: theme.font_family,
|
|
42814
|
+
fontSize: "12px"
|
|
42815
|
+
}
|
|
42816
|
+
}));
|
|
42817
|
+
const handleColorSelectChange = reactExports.useCallback((e) => {
|
|
42818
|
+
const newColorLabel = e.target.value || null;
|
|
42819
|
+
onChangeColor(newColorLabel);
|
|
42820
|
+
}, [onChangeColor]);
|
|
42821
|
+
if (availableColorLabels.length < 2) {
|
|
42822
|
+
return null;
|
|
42823
|
+
}
|
|
42824
|
+
return /* @__PURE__ */ jsx$1("div", { css: css2.colorContainer, children: /* @__PURE__ */ jsxs("label", { children: [
|
|
42825
|
+
/* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.colorLabelText, t: "quick-view.color_label" }),
|
|
42826
|
+
/* @__PURE__ */ jsx$1("select", { value: selectedColorLabel ?? "", onChange: handleColorSelectChange, css: css2.colorSelect, children: availableColorLabels.map((colorLabel) => /* @__PURE__ */ jsx$1("option", { value: colorLabel, children: colorLabel }, colorLabel)) })
|
|
42827
|
+
] }) });
|
|
42466
42828
|
}
|
|
42467
42829
|
function ItemFitDetails({
|
|
42468
42830
|
loadedProductData,
|
|
@@ -42574,14 +42936,19 @@ function DetailAccordionItem({
|
|
|
42574
42936
|
onToggleOpen,
|
|
42575
42937
|
onChangeDetailMode,
|
|
42576
42938
|
onChangeSize,
|
|
42939
|
+
onChangeColor,
|
|
42577
42940
|
onAddToCart,
|
|
42578
42941
|
onToggleUntuck
|
|
42579
42942
|
}) {
|
|
42580
42943
|
const productData = reactExports.useMemo(() => buildVtoProductDataFromResolved(item), [item]);
|
|
42581
42944
|
const selectedSizeLabel = reactExports.useMemo(() => {
|
|
42582
|
-
if (!productData)
|
|
42945
|
+
if (!productData) {
|
|
42946
|
+
return null;
|
|
42947
|
+
}
|
|
42583
42948
|
const csaId = item.storage.colorwaySizeAssetId;
|
|
42584
|
-
if (csaId == null)
|
|
42949
|
+
if (csaId == null) {
|
|
42950
|
+
return null;
|
|
42951
|
+
}
|
|
42585
42952
|
for (const sizeRec of productData.sizes) {
|
|
42586
42953
|
if (sizeRec.colors.some((c) => c.colorwaySizeAssetId === csaId)) {
|
|
42587
42954
|
return sizeRec.sizeLabel;
|
|
@@ -42590,22 +42957,38 @@ function DetailAccordionItem({
|
|
|
42590
42957
|
return null;
|
|
42591
42958
|
}, [productData, item.storage.colorwaySizeAssetId]);
|
|
42592
42959
|
const currentPrice = reactExports.useMemo(() => {
|
|
42593
|
-
if (!productData)
|
|
42960
|
+
if (!productData) {
|
|
42961
|
+
return null;
|
|
42962
|
+
}
|
|
42594
42963
|
const csaId = item.storage.colorwaySizeAssetId;
|
|
42595
|
-
if (csaId == null)
|
|
42964
|
+
if (csaId == null) {
|
|
42965
|
+
return null;
|
|
42966
|
+
}
|
|
42596
42967
|
for (const sizeRec of productData.sizes) {
|
|
42597
42968
|
const c = sizeRec.colors.find((c2) => c2.colorwaySizeAssetId === csaId);
|
|
42598
|
-
if (c)
|
|
42969
|
+
if (c) {
|
|
42970
|
+
return c.priceFormatted;
|
|
42971
|
+
}
|
|
42599
42972
|
}
|
|
42600
42973
|
return null;
|
|
42601
42974
|
}, [productData, item.storage.colorwaySizeAssetId]);
|
|
42975
|
+
const availableColorLabels = reactExports.useMemo(() => {
|
|
42976
|
+
if (!productData || !selectedSizeLabel) {
|
|
42977
|
+
return [];
|
|
42978
|
+
}
|
|
42979
|
+
const sizeRec = productData.sizes.find((s) => s.sizeLabel === selectedSizeLabel);
|
|
42980
|
+
if (!sizeRec) {
|
|
42981
|
+
return [];
|
|
42982
|
+
}
|
|
42983
|
+
return sizeRec.colors.map((c) => c.colorLabel).filter((label) => !!label);
|
|
42984
|
+
}, [productData, selectedSizeLabel]);
|
|
42602
42985
|
const categoryLabel = item.styleCategory?.label_singular ?? item.styleCategory?.label ?? "";
|
|
42603
42986
|
const productName = item.merchantProduct?.productName ?? item.externalId;
|
|
42604
42987
|
const tuckable = !!item.styleCategory?.tuckable && canTuck;
|
|
42605
42988
|
if (platform === "desktop") {
|
|
42606
|
-
return /* @__PURE__ */ jsx$1(DesktopAccordionItem, { isOpen, categoryLabel, productData, currentPrice, selectedSizeLabel, onToggleOpen, onChangeSize, onAddToCart });
|
|
42989
|
+
return /* @__PURE__ */ jsx$1(DesktopAccordionItem, { isOpen, categoryLabel, productData, currentPrice, selectedSizeLabel, availableColorLabels, selectedColorLabel: item.storage.color, onToggleOpen, onChangeSize, onChangeColor, onAddToCart });
|
|
42607
42990
|
}
|
|
42608
|
-
return /* @__PURE__ */ jsx$1(MobileAccordionItem, { isOpen, categoryLabel, productName, productData, selectedSizeLabel, currentPrice, detailMode, isMobileQuickRow, tuckable, forceUntuck, onToggleOpen, onChangeDetailMode, onChangeSize, onAddToCart, onToggleUntuck });
|
|
42991
|
+
return /* @__PURE__ */ jsx$1(MobileAccordionItem, { isOpen, categoryLabel, productName, productData, selectedSizeLabel, availableColorLabels, selectedColorLabel: item.storage.color, currentPrice, detailMode, isMobileQuickRow, tuckable, forceUntuck, onToggleOpen, onChangeDetailMode, onChangeSize, onChangeColor, onAddToCart, onToggleUntuck });
|
|
42609
42992
|
}
|
|
42610
42993
|
function DesktopAccordionItem({
|
|
42611
42994
|
isOpen,
|
|
@@ -42613,8 +42996,11 @@ function DesktopAccordionItem({
|
|
|
42613
42996
|
productData,
|
|
42614
42997
|
currentPrice,
|
|
42615
42998
|
selectedSizeLabel,
|
|
42999
|
+
availableColorLabels,
|
|
43000
|
+
selectedColorLabel,
|
|
42616
43001
|
onToggleOpen,
|
|
42617
43002
|
onChangeSize,
|
|
43003
|
+
onChangeColor,
|
|
42618
43004
|
onAddToCart
|
|
42619
43005
|
}) {
|
|
42620
43006
|
const ACCORDION_SHADE = "#F4F4F4";
|
|
@@ -42660,35 +43046,60 @@ function DesktopAccordionItem({
|
|
|
42660
43046
|
price: {
|
|
42661
43047
|
fontSize: "15px"
|
|
42662
43048
|
},
|
|
43049
|
+
// Padding matches quick-view's sizeRecommendationFrame (32px / 56px) so
|
|
43050
|
+
// the "fit box" feels visually consistent between the two overlays.
|
|
43051
|
+
//
|
|
43052
|
+
// No flex `gap` — the three text lines (recommended size, fit text,
|
|
43053
|
+
// select-a-size prompt) sit tight against each other (matching
|
|
43054
|
+
// quick-view), with explicit marginTop on the size selector + fit
|
|
43055
|
+
// details below them to introduce the larger break.
|
|
42663
43056
|
sizeBox: {
|
|
42664
43057
|
width: "100%",
|
|
42665
43058
|
border: `1px solid ${theme.color_fg_text}`,
|
|
42666
|
-
padding: "
|
|
43059
|
+
padding: "32px 56px",
|
|
42667
43060
|
display: "flex",
|
|
42668
43061
|
flexDirection: "column",
|
|
42669
43062
|
alignItems: "center",
|
|
42670
|
-
gap: "12px",
|
|
42671
43063
|
marginTop: "8px",
|
|
42672
43064
|
textAlign: "center"
|
|
42673
43065
|
},
|
|
43066
|
+
colorSelectorContainer: {
|
|
43067
|
+
width: "100%",
|
|
43068
|
+
marginTop: "8px"
|
|
43069
|
+
},
|
|
43070
|
+
// 14px / line-height 1.5 on these three text lines matches quick-view's
|
|
43071
|
+
// fit-box. Quick-view's first line wraps an InfoIcon button alongside
|
|
43072
|
+
// the recommended-size text, which stretches that line vertically; the
|
|
43073
|
+
// simpler line-height bump here matches the *visual* line spacing
|
|
43074
|
+
// without dragging the icon in. The two lines below it inherit
|
|
43075
|
+
// line-height from their containers in quick-view, which the host page's
|
|
43076
|
+
// body styles tend to set looser than Inter's intrinsic `normal` (~1.21).
|
|
42674
43077
|
recommendedSize: {
|
|
42675
|
-
fontSize: "
|
|
42676
|
-
fontWeight: "600"
|
|
43078
|
+
fontSize: "14px",
|
|
43079
|
+
fontWeight: "600",
|
|
43080
|
+
lineHeight: 1.5
|
|
42677
43081
|
},
|
|
42678
43082
|
selectPrompt: {
|
|
42679
|
-
fontSize: "
|
|
43083
|
+
fontSize: "14px",
|
|
43084
|
+
lineHeight: 1.5
|
|
42680
43085
|
},
|
|
42681
43086
|
fitText: {
|
|
42682
|
-
fontSize: "
|
|
43087
|
+
fontSize: "14px",
|
|
43088
|
+
lineHeight: 1.5,
|
|
43089
|
+
// Tight 8px lift to the recommended-size line above; matches
|
|
43090
|
+
// quick-view's `itemFitContainer` marginTop.
|
|
43091
|
+
marginTop: "8px"
|
|
42683
43092
|
},
|
|
42684
43093
|
fitDetails: {
|
|
42685
|
-
width: "100%"
|
|
43094
|
+
width: "100%",
|
|
43095
|
+
marginTop: "24px"
|
|
42686
43096
|
},
|
|
42687
43097
|
sizeRow: {
|
|
42688
43098
|
display: "flex",
|
|
42689
43099
|
gap: "8px",
|
|
42690
43100
|
alignItems: "center",
|
|
42691
|
-
justifyContent: "center"
|
|
43101
|
+
justifyContent: "center",
|
|
43102
|
+
marginTop: "24px"
|
|
42692
43103
|
},
|
|
42693
43104
|
cartContainer: {
|
|
42694
43105
|
width: "100%",
|
|
@@ -42709,6 +43120,7 @@ function DesktopAccordionItem({
|
|
|
42709
43120
|
!isOpen ? null : /* @__PURE__ */ jsx$1("div", { css: css2.body, children: productData ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
42710
43121
|
/* @__PURE__ */ jsx$1(Text, { variant: "brand", css: css2.productName, children: productData.productName }),
|
|
42711
43122
|
currentPrice ? /* @__PURE__ */ jsx$1(Text, { variant: "base", css: css2.price, children: currentPrice }) : null,
|
|
43123
|
+
/* @__PURE__ */ jsx$1("div", { css: css2.colorSelectorContainer, children: /* @__PURE__ */ jsx$1(ColorSelector, { availableColorLabels, selectedColorLabel, onChangeColor }) }),
|
|
42712
43124
|
/* @__PURE__ */ jsxs("div", { css: css2.sizeBox, children: [
|
|
42713
43125
|
/* @__PURE__ */ jsxs(Text, { variant: "base", css: css2.recommendedSize, children: [
|
|
42714
43126
|
"Recommended Size: ",
|
|
@@ -42732,6 +43144,8 @@ function MobileAccordionItem({
|
|
|
42732
43144
|
productName,
|
|
42733
43145
|
productData,
|
|
42734
43146
|
selectedSizeLabel,
|
|
43147
|
+
availableColorLabels,
|
|
43148
|
+
selectedColorLabel,
|
|
42735
43149
|
currentPrice,
|
|
42736
43150
|
detailMode,
|
|
42737
43151
|
isMobileQuickRow,
|
|
@@ -42740,6 +43154,7 @@ function MobileAccordionItem({
|
|
|
42740
43154
|
onToggleOpen,
|
|
42741
43155
|
onChangeDetailMode,
|
|
42742
43156
|
onChangeSize,
|
|
43157
|
+
onChangeColor,
|
|
42743
43158
|
onAddToCart,
|
|
42744
43159
|
onToggleUntuck
|
|
42745
43160
|
}) {
|
|
@@ -42879,6 +43294,7 @@ function MobileAccordionItem({
|
|
|
42879
43294
|
] }),
|
|
42880
43295
|
isMobileQuickRow ? /* @__PURE__ */ jsx$1("div", { css: css2.content, children: /* @__PURE__ */ jsx$1("div", { css: css2.quickRow, children: productData ? /* @__PURE__ */ jsx$1(SizeSelector, { loadedProductData: productData, selectedSizeLabel, onChangeSize }) : null }) }) : !isOpen ? null : /* @__PURE__ */ jsx$1("div", { css: css2.content, children: /* @__PURE__ */ jsx$1("div", { css: css2.body, children: productData ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
42881
43296
|
/* @__PURE__ */ jsx$1("div", { css: css2.sizeRow, children: /* @__PURE__ */ jsx$1(SizeSelector, { loadedProductData: productData, selectedSizeLabel, onChangeSize }) }),
|
|
43297
|
+
/* @__PURE__ */ jsx$1(ColorSelector, { availableColorLabels, selectedColorLabel, onChangeColor }),
|
|
42882
43298
|
/* @__PURE__ */ jsx$1(ItemFitText, { loadedProductData: productData }),
|
|
42883
43299
|
/* @__PURE__ */ jsx$1("div", { css: css2.fitDetailsContainer, children: /* @__PURE__ */ jsx$1(ItemFitDetails, { loadedProductData: productData, selectedSizeLabel }) }),
|
|
42884
43300
|
/* @__PURE__ */ jsx$1("div", { css: css2.buttonContainer, children: /* @__PURE__ */ jsx$1(AddToCartButton, { onClick: onAddToCart }) }),
|
|
@@ -42905,6 +43321,7 @@ function DetailAccordion({
|
|
|
42905
43321
|
onOpenItem,
|
|
42906
43322
|
onChangeDetailMode,
|
|
42907
43323
|
onChangeSize,
|
|
43324
|
+
onChangeColor,
|
|
42908
43325
|
onAddToCart,
|
|
42909
43326
|
onToggleUntuck
|
|
42910
43327
|
}) {
|
|
@@ -42919,9 +43336,164 @@ function DetailAccordion({
|
|
|
42919
43336
|
gap
|
|
42920
43337
|
}, children: items.map((item) => {
|
|
42921
43338
|
const isOpen = openItemExternalId === item.externalId;
|
|
42922
|
-
return /* @__PURE__ */ jsx$1(DetailAccordionItem, { item, isOpen, platform, detailMode, isMobileQuickRow, forceUntuck, canTuck, onToggleOpen: () => onOpenItem(isOpen ? null : item.externalId), onChangeDetailMode, onChangeSize: (label) => onChangeSize(item.externalId, label), onAddToCart: () => onAddToCart(item.externalId), onToggleUntuck }, item.externalId);
|
|
43339
|
+
return /* @__PURE__ */ jsx$1(DetailAccordionItem, { item, isOpen, platform, detailMode, isMobileQuickRow, forceUntuck, canTuck, onToggleOpen: () => onOpenItem(isOpen ? null : item.externalId), onChangeDetailMode, onChangeSize: (label) => onChangeSize(item.externalId, label), onChangeColor: (label) => onChangeColor(item.externalId, label), onAddToCart: () => onAddToCart(item.externalId), onToggleUntuck }, item.externalId);
|
|
42923
43340
|
}) });
|
|
42924
43341
|
}
|
|
43342
|
+
const AXIS_LOCK_PX = 8;
|
|
43343
|
+
const ROTATE_STEP_PX = 50;
|
|
43344
|
+
function ZoomModal({
|
|
43345
|
+
frameUrls,
|
|
43346
|
+
selectedFrameIndex,
|
|
43347
|
+
setSelectedFrameIndex,
|
|
43348
|
+
onClose
|
|
43349
|
+
}) {
|
|
43350
|
+
reactExports.useEffect(() => {
|
|
43351
|
+
const onKeyDown = (e) => {
|
|
43352
|
+
if (e.key === "Escape") {
|
|
43353
|
+
e.stopPropagation();
|
|
43354
|
+
e.preventDefault();
|
|
43355
|
+
onClose();
|
|
43356
|
+
}
|
|
43357
|
+
};
|
|
43358
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
43359
|
+
return () => document.removeEventListener("keydown", onKeyDown, true);
|
|
43360
|
+
}, [onClose]);
|
|
43361
|
+
const {
|
|
43362
|
+
rotateLeft,
|
|
43363
|
+
rotateRight
|
|
43364
|
+
} = useFrameRotation(frameUrls, setSelectedFrameIndex);
|
|
43365
|
+
const scrollAreaRef = reactExports.useRef(null);
|
|
43366
|
+
const handleImageMouseDown = reactExports.useCallback((e) => {
|
|
43367
|
+
e.preventDefault();
|
|
43368
|
+
const startX = e.clientX;
|
|
43369
|
+
const startY = e.clientY;
|
|
43370
|
+
const scrollArea = scrollAreaRef.current;
|
|
43371
|
+
const startScrollTop = scrollArea?.scrollTop ?? 0;
|
|
43372
|
+
let mode = "unknown";
|
|
43373
|
+
let lastRotateX = startX;
|
|
43374
|
+
const onMove = (move) => {
|
|
43375
|
+
const deltaX = move.clientX - startX;
|
|
43376
|
+
const deltaY = move.clientY - startY;
|
|
43377
|
+
if (mode === "unknown") {
|
|
43378
|
+
const absX = Math.abs(deltaX);
|
|
43379
|
+
const absY = Math.abs(deltaY);
|
|
43380
|
+
if (absX < AXIS_LOCK_PX && absY < AXIS_LOCK_PX) {
|
|
43381
|
+
return;
|
|
43382
|
+
}
|
|
43383
|
+
mode = absY > absX ? "scroll" : "rotate";
|
|
43384
|
+
lastRotateX = move.clientX;
|
|
43385
|
+
}
|
|
43386
|
+
if (mode === "scroll" && scrollArea) {
|
|
43387
|
+
scrollArea.scrollTop = startScrollTop - deltaY;
|
|
43388
|
+
} else if (mode === "rotate") {
|
|
43389
|
+
const rotateDelta = move.clientX - lastRotateX;
|
|
43390
|
+
if (Math.abs(rotateDelta) >= ROTATE_STEP_PX) {
|
|
43391
|
+
if (rotateDelta > 0) {
|
|
43392
|
+
rotateRight();
|
|
43393
|
+
} else {
|
|
43394
|
+
rotateLeft();
|
|
43395
|
+
}
|
|
43396
|
+
lastRotateX = move.clientX;
|
|
43397
|
+
}
|
|
43398
|
+
}
|
|
43399
|
+
};
|
|
43400
|
+
const onUp = () => {
|
|
43401
|
+
window.removeEventListener("mousemove", onMove);
|
|
43402
|
+
window.removeEventListener("mouseup", onUp);
|
|
43403
|
+
};
|
|
43404
|
+
window.addEventListener("mousemove", onMove);
|
|
43405
|
+
window.addEventListener("mouseup", onUp);
|
|
43406
|
+
}, [rotateLeft, rotateRight]);
|
|
43407
|
+
const css2 = useCss((_theme) => ({
|
|
43408
|
+
backdrop: {
|
|
43409
|
+
position: "fixed",
|
|
43410
|
+
inset: 0,
|
|
43411
|
+
backgroundColor: "#F4F4F4",
|
|
43412
|
+
zIndex: 100
|
|
43413
|
+
},
|
|
43414
|
+
scrollArea: {
|
|
43415
|
+
position: "absolute",
|
|
43416
|
+
// 40px padding on every side.
|
|
43417
|
+
inset: "40px",
|
|
43418
|
+
overflow: "auto"
|
|
43419
|
+
},
|
|
43420
|
+
// Centres the frame when it fits; grows past the scroll area when it
|
|
43421
|
+
// doesn't, so the scroll area scrolls instead of shrinking the image.
|
|
43422
|
+
imageWrap: {
|
|
43423
|
+
minWidth: "100%",
|
|
43424
|
+
minHeight: "100%",
|
|
43425
|
+
display: "flex",
|
|
43426
|
+
alignItems: "center",
|
|
43427
|
+
justifyContent: "center"
|
|
43428
|
+
},
|
|
43429
|
+
image: {
|
|
43430
|
+
// No sizing — the frame renders at its natural (full) resolution.
|
|
43431
|
+
display: "block",
|
|
43432
|
+
flex: "none",
|
|
43433
|
+
cursor: "grab"
|
|
43434
|
+
},
|
|
43435
|
+
// Rotation chevrons float at the modal's vertical centre, so they stay
|
|
43436
|
+
// put regardless of how the (possibly larger) frame is scrolled.
|
|
43437
|
+
chevron: {
|
|
43438
|
+
position: "absolute",
|
|
43439
|
+
top: "50%",
|
|
43440
|
+
transform: "translateY(-50%)",
|
|
43441
|
+
display: "flex",
|
|
43442
|
+
cursor: "pointer",
|
|
43443
|
+
zIndex: 1
|
|
43444
|
+
},
|
|
43445
|
+
chevronLeft: {
|
|
43446
|
+
left: "8px"
|
|
43447
|
+
},
|
|
43448
|
+
chevronRight: {
|
|
43449
|
+
right: "8px"
|
|
43450
|
+
},
|
|
43451
|
+
chevronIcon: {
|
|
43452
|
+
width: "48px",
|
|
43453
|
+
height: "48px"
|
|
43454
|
+
},
|
|
43455
|
+
close: {
|
|
43456
|
+
position: "absolute",
|
|
43457
|
+
top: "8px",
|
|
43458
|
+
right: "8px",
|
|
43459
|
+
width: "44px",
|
|
43460
|
+
height: "44px",
|
|
43461
|
+
display: "flex",
|
|
43462
|
+
alignItems: "center",
|
|
43463
|
+
justifyContent: "center",
|
|
43464
|
+
borderRadius: "50%",
|
|
43465
|
+
border: "none",
|
|
43466
|
+
// Reset native button chrome and default padding so the circle renders
|
|
43467
|
+
// exactly as styled.
|
|
43468
|
+
appearance: "none",
|
|
43469
|
+
WebkitAppearance: "none",
|
|
43470
|
+
padding: 0,
|
|
43471
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
43472
|
+
color: "#FFFFFF",
|
|
43473
|
+
fontSize: "32px",
|
|
43474
|
+
lineHeight: 1,
|
|
43475
|
+
cursor: "pointer",
|
|
43476
|
+
// Keep the "×" glyph from being text-selected — a stray selection
|
|
43477
|
+
// paints a dark highlight rectangle behind it.
|
|
43478
|
+
userSelect: "none",
|
|
43479
|
+
WebkitUserSelect: "none",
|
|
43480
|
+
zIndex: 1
|
|
43481
|
+
}
|
|
43482
|
+
}));
|
|
43483
|
+
const imageUrl = frameUrls[selectedFrameIndex ?? 0];
|
|
43484
|
+
return /* @__PURE__ */ jsxs("div", { css: css2.backdrop, children: [
|
|
43485
|
+
/* @__PURE__ */ jsx$1("div", { ref: scrollAreaRef, css: css2.scrollArea, children: /* @__PURE__ */ jsx$1("div", { css: css2.imageWrap, children: /* @__PURE__ */ jsx$1("img", { src: imageUrl, css: css2.image, alt: "", onMouseDown: handleImageMouseDown }) }) }),
|
|
43486
|
+
/* @__PURE__ */ jsx$1("div", { css: /* @__PURE__ */ css$1({
|
|
43487
|
+
...css2.chevron,
|
|
43488
|
+
...css2.chevronLeft
|
|
43489
|
+
}, "", ""), onClick: rotateLeft, children: /* @__PURE__ */ jsx$1(SvgChevronLeft, { css: css2.chevronIcon }) }),
|
|
43490
|
+
/* @__PURE__ */ jsx$1("div", { css: /* @__PURE__ */ css$1({
|
|
43491
|
+
...css2.chevron,
|
|
43492
|
+
...css2.chevronRight
|
|
43493
|
+
}, "", ""), onClick: rotateRight, children: /* @__PURE__ */ jsx$1(SvgChevronRight, { css: css2.chevronIcon }) }),
|
|
43494
|
+
/* @__PURE__ */ jsx$1("button", { css: css2.close, onClick: onClose, "aria-label": "Close zoom", children: "×" })
|
|
43495
|
+
] });
|
|
43496
|
+
}
|
|
42925
43497
|
const AVATAR_ASPECT_RATIO = 2 / 3;
|
|
42926
43498
|
const EDGE_INSET_PX = 16;
|
|
42927
43499
|
const AVATAR_MIN_WIDTH_PX = 240;
|
|
@@ -42937,35 +43509,40 @@ function DesktopLayout$1({
|
|
|
42937
43509
|
detailMode,
|
|
42938
43510
|
forceUntuck,
|
|
42939
43511
|
canTuck,
|
|
42940
|
-
zoomed,
|
|
42941
43512
|
frameUrls,
|
|
42942
43513
|
onSelectItem,
|
|
42943
43514
|
onRemoveItem,
|
|
42944
43515
|
onOpenAccordionItem,
|
|
42945
43516
|
onChangeDetailMode,
|
|
42946
43517
|
onChangeSize,
|
|
43518
|
+
onChangeColor,
|
|
42947
43519
|
onAddToCart,
|
|
42948
43520
|
onToggleUntuck,
|
|
42949
|
-
onToggleZoom,
|
|
42950
43521
|
onSignOut
|
|
42951
43522
|
}) {
|
|
42952
43523
|
const hasSelection = selectedItems.length > 0;
|
|
42953
43524
|
const [avatarHovered, setAvatarHovered] = reactExports.useState(false);
|
|
43525
|
+
const [zoomOpen, setZoomOpen] = reactExports.useState(false);
|
|
43526
|
+
const [selectedFrameIndex, setSelectedFrameIndex] = reactExports.useState(null);
|
|
42954
43527
|
const containerRef = reactExports.useRef(null);
|
|
42955
43528
|
const [avatarWidth, setAvatarWidth] = reactExports.useState(AVATAR_MIN_WIDTH_PX);
|
|
42956
43529
|
reactExports.useLayoutEffect(() => {
|
|
42957
43530
|
const el = containerRef.current;
|
|
42958
|
-
if (!el)
|
|
43531
|
+
if (!el) {
|
|
43532
|
+
return;
|
|
43533
|
+
}
|
|
42959
43534
|
const observer = new ResizeObserver(() => {
|
|
42960
43535
|
const availableHeightPx = el.clientHeight;
|
|
42961
|
-
if (availableHeightPx <= 0)
|
|
43536
|
+
if (availableHeightPx <= 0) {
|
|
43537
|
+
return;
|
|
43538
|
+
}
|
|
42962
43539
|
const target = Math.floor(availableHeightPx * AVATAR_ASPECT_RATIO);
|
|
42963
43540
|
setAvatarWidth(Math.min(AVATAR_MAX_WIDTH_PX, Math.max(AVATAR_MIN_WIDTH_PX, target)));
|
|
42964
43541
|
});
|
|
42965
43542
|
observer.observe(el);
|
|
42966
43543
|
return () => observer.disconnect();
|
|
42967
43544
|
}, []);
|
|
42968
|
-
const gridTemplateColumns =
|
|
43545
|
+
const gridTemplateColumns = hasSelection ? `${avatarWidth}px minmax(${DETAILS_MIN_WIDTH_PX}px, ${DETAILS_FR}fr) ${CARDS_FR}fr` : `${avatarWidth}px 1fr`;
|
|
42969
43546
|
const css2 = useCss((_theme) => ({
|
|
42970
43547
|
container: {
|
|
42971
43548
|
display: "grid",
|
|
@@ -43030,21 +43607,127 @@ function DesktopLayout$1({
|
|
|
43030
43607
|
fontSize: "14px"
|
|
43031
43608
|
}
|
|
43032
43609
|
}));
|
|
43033
|
-
const controls = hasSelection ? /* @__PURE__ */ jsx$1(AvatarControls, { selectedItems, canTuck, forceUntuck, zoomed, expanded: avatarHovered, onToggleUntuck, onToggleZoom, onRemoveItem }) : null;
|
|
43610
|
+
const controls = hasSelection ? /* @__PURE__ */ jsx$1(AvatarControls, { selectedItems, canTuck, forceUntuck, zoomed: zoomOpen, expanded: avatarHovered, onToggleUntuck, onToggleZoom: () => setZoomOpen(true), onRemoveItem }) : null;
|
|
43034
43611
|
return /* @__PURE__ */ jsxs("div", { ref: containerRef, css: css2.container, style: {
|
|
43035
43612
|
gridTemplateColumns
|
|
43036
43613
|
}, children: [
|
|
43037
|
-
/* @__PURE__ */ jsx$1("div", { css: css2.avatarColumn, onMouseEnter: () => setAvatarHovered(true), onMouseLeave: () => setAvatarHovered(false), children: /* @__PURE__ */ jsx$1(AvatarPane, { hasSelection, frameUrls, controls }) }),
|
|
43038
|
-
|
|
43039
|
-
|
|
43614
|
+
/* @__PURE__ */ jsx$1("div", { css: css2.avatarColumn, onMouseEnter: () => setAvatarHovered(true), onMouseLeave: () => setAvatarHovered(false), children: /* @__PURE__ */ jsx$1(AvatarPane, { hasSelection, frameUrls, controls, selectedFrameIndex, setSelectedFrameIndex }) }),
|
|
43615
|
+
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,
|
|
43616
|
+
/* @__PURE__ */ jsxs("div", { css: css2.railsColumn, children: [
|
|
43040
43617
|
/* @__PURE__ */ jsxs("span", { css: css2.signOutWrapper, onClick: onSignOut, children: [
|
|
43041
43618
|
/* @__PURE__ */ jsx$1(SvgTfrIcon, { css: css2.signOutIcon }),
|
|
43042
43619
|
/* @__PURE__ */ jsx$1(LinkT, { variant: "underline", css: css2.signOut, t: "fitting_room.sign_out" })
|
|
43043
43620
|
] }),
|
|
43044
43621
|
resolved.groups.map((group) => /* @__PURE__ */ jsx$1(CardRail, { group, availabilityByExternalId, onSelectItem, onRemoveItem }, group.group.name))
|
|
43045
|
-
] })
|
|
43622
|
+
] }),
|
|
43623
|
+
zoomOpen && frameUrls && frameUrls.length > 0 ? /* @__PURE__ */ jsx$1(ZoomModal, { frameUrls, selectedFrameIndex, setSelectedFrameIndex, onClose: () => setZoomOpen(false) }) : null
|
|
43624
|
+
] });
|
|
43625
|
+
}
|
|
43626
|
+
function MenuIcon({
|
|
43627
|
+
size = 20
|
|
43628
|
+
}) {
|
|
43629
|
+
return /* @__PURE__ */ jsx$1("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx$1("path", { d: "M4 7H20M4 12H20M4 17H20", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) });
|
|
43630
|
+
}
|
|
43631
|
+
function SectionNav({
|
|
43632
|
+
sections,
|
|
43633
|
+
activeName,
|
|
43634
|
+
onSelect
|
|
43635
|
+
}) {
|
|
43636
|
+
const [open, setOpen] = reactExports.useState(false);
|
|
43637
|
+
const wrapperRef = reactExports.useRef(null);
|
|
43638
|
+
reactExports.useEffect(() => {
|
|
43639
|
+
if (!open) {
|
|
43640
|
+
return;
|
|
43641
|
+
}
|
|
43642
|
+
const onDocClick = (e) => {
|
|
43643
|
+
if (wrapperRef.current && !wrapperRef.current.contains(e.target)) {
|
|
43644
|
+
setOpen(false);
|
|
43645
|
+
}
|
|
43646
|
+
};
|
|
43647
|
+
document.addEventListener("mousedown", onDocClick);
|
|
43648
|
+
return () => document.removeEventListener("mousedown", onDocClick);
|
|
43649
|
+
}, [open]);
|
|
43650
|
+
const handleSelect = reactExports.useCallback((name2) => {
|
|
43651
|
+
setOpen(false);
|
|
43652
|
+
onSelect(name2);
|
|
43653
|
+
}, [onSelect]);
|
|
43654
|
+
const css2 = useCss((theme) => ({
|
|
43655
|
+
wrapper: {
|
|
43656
|
+
// Floats over the card rails at the top-right instead of taking its own
|
|
43657
|
+
// row in the browse-view column (BrowseView's container is relative).
|
|
43658
|
+
position: "absolute",
|
|
43659
|
+
top: "12px",
|
|
43660
|
+
right: "16px",
|
|
43661
|
+
// Above the rails so the pill and its drop-down sit over the content.
|
|
43662
|
+
zIndex: 5
|
|
43663
|
+
},
|
|
43664
|
+
bar: {
|
|
43665
|
+
display: "inline-flex",
|
|
43666
|
+
alignItems: "center",
|
|
43667
|
+
gap: "8px",
|
|
43668
|
+
padding: "6px 16px",
|
|
43669
|
+
borderRadius: "999px",
|
|
43670
|
+
backgroundColor: theme.color_fg_text,
|
|
43671
|
+
color: "#FFFFFF",
|
|
43672
|
+
border: "none",
|
|
43673
|
+
cursor: "pointer",
|
|
43674
|
+
fontSize: "13px",
|
|
43675
|
+
fontWeight: "500",
|
|
43676
|
+
letterSpacing: "0.5px",
|
|
43677
|
+
textTransform: "uppercase",
|
|
43678
|
+
whiteSpace: "nowrap"
|
|
43679
|
+
},
|
|
43680
|
+
icon: {
|
|
43681
|
+
display: "inline-flex",
|
|
43682
|
+
alignItems: "center",
|
|
43683
|
+
flex: "none"
|
|
43684
|
+
},
|
|
43685
|
+
dropdown: {
|
|
43686
|
+
position: "absolute",
|
|
43687
|
+
top: "calc(100% + 6px)",
|
|
43688
|
+
right: 0,
|
|
43689
|
+
// At least as wide as the pill, growing to fit the longest label.
|
|
43690
|
+
minWidth: "100%",
|
|
43691
|
+
backgroundColor: theme.color_fg_text,
|
|
43692
|
+
borderRadius: "16px",
|
|
43693
|
+
maxHeight: "60vh",
|
|
43694
|
+
overflowY: "auto",
|
|
43695
|
+
boxShadow: "0 6px 16px rgba(0, 0, 0, 0.25)"
|
|
43696
|
+
},
|
|
43697
|
+
item: {
|
|
43698
|
+
display: "block",
|
|
43699
|
+
width: "100%",
|
|
43700
|
+
textAlign: "left",
|
|
43701
|
+
whiteSpace: "nowrap",
|
|
43702
|
+
padding: "8px 18px",
|
|
43703
|
+
backgroundColor: "transparent",
|
|
43704
|
+
color: "#FFFFFF",
|
|
43705
|
+
border: "none",
|
|
43706
|
+
cursor: "pointer",
|
|
43707
|
+
fontSize: "13px",
|
|
43708
|
+
letterSpacing: "0.5px",
|
|
43709
|
+
textTransform: "uppercase",
|
|
43710
|
+
"&:not(:first-of-type)": {
|
|
43711
|
+
borderTop: "1px solid rgba(255, 255, 255, 0.12)"
|
|
43712
|
+
}
|
|
43713
|
+
},
|
|
43714
|
+
itemActive: {
|
|
43715
|
+
backgroundColor: "rgba(255, 255, 255, 0.16)"
|
|
43716
|
+
}
|
|
43717
|
+
}));
|
|
43718
|
+
const activeLabel = sections.find((s) => s.name === activeName)?.label ?? sections[0]?.label ?? "";
|
|
43719
|
+
return /* @__PURE__ */ jsxs("div", { ref: wrapperRef, css: css2.wrapper, children: [
|
|
43720
|
+
/* @__PURE__ */ jsxs(Button, { variant: "base", css: css2.bar, onClick: () => setOpen((o) => !o), children: [
|
|
43721
|
+
/* @__PURE__ */ jsx$1("span", { children: activeLabel }),
|
|
43722
|
+
/* @__PURE__ */ jsx$1("span", { css: css2.icon, children: open ? /* @__PURE__ */ jsx$1(Chevron, { direction: "up", size: 20 }) : /* @__PURE__ */ jsx$1(MenuIcon, { size: 20 }) })
|
|
43723
|
+
] }),
|
|
43724
|
+
open ? /* @__PURE__ */ jsx$1("div", { css: css2.dropdown, children: sections.map((s) => /* @__PURE__ */ jsx$1(Button, { variant: "base", css: s.name === activeName ? {
|
|
43725
|
+
...css2.item,
|
|
43726
|
+
...css2.itemActive
|
|
43727
|
+
} : css2.item, onClick: () => handleSelect(s.name), children: s.label }, s.name)) }) : null
|
|
43046
43728
|
] });
|
|
43047
43729
|
}
|
|
43730
|
+
const SECTION_SCROLL_TOP_GAP_PX = 50;
|
|
43048
43731
|
function MobileLayout$1({
|
|
43049
43732
|
mode,
|
|
43050
43733
|
resolved,
|
|
@@ -43064,13 +43747,14 @@ function MobileLayout$1({
|
|
|
43064
43747
|
onOpenAccordionItem,
|
|
43065
43748
|
onChangeDetailMode,
|
|
43066
43749
|
onChangeSize,
|
|
43750
|
+
onChangeColor,
|
|
43067
43751
|
onAddToCart,
|
|
43068
43752
|
onToggleUntuck
|
|
43069
43753
|
}) {
|
|
43070
43754
|
if (mode === "browse") {
|
|
43071
43755
|
return /* @__PURE__ */ jsx$1(BrowseView, { resolved, availabilityByExternalId, selectedCount: selectedItems.length, onSelectItem, onRemoveItem, onTryItOn });
|
|
43072
43756
|
}
|
|
43073
|
-
return /* @__PURE__ */ jsx$1(TryOnView, { selectedItems, openAccordionItemId, detailMode, forceUntuck, canTuck, frameUrls, sheetSnap, sheetTouchStart, onBackToBrowse, onOpenAccordionItem, onChangeDetailMode, onChangeSize, onAddToCart, onToggleUntuck });
|
|
43757
|
+
return /* @__PURE__ */ jsx$1(TryOnView, { selectedItems, openAccordionItemId, detailMode, forceUntuck, canTuck, frameUrls, sheetSnap, sheetTouchStart, onBackToBrowse, onOpenAccordionItem, onChangeDetailMode, onChangeSize, onChangeColor, onAddToCart, onToggleUntuck });
|
|
43074
43758
|
}
|
|
43075
43759
|
function BrowseView({
|
|
43076
43760
|
resolved,
|
|
@@ -43080,12 +43764,55 @@ function BrowseView({
|
|
|
43080
43764
|
onRemoveItem,
|
|
43081
43765
|
onTryItOn
|
|
43082
43766
|
}) {
|
|
43767
|
+
const railsAreaRef = reactExports.useRef(null);
|
|
43768
|
+
const sectionRefs = reactExports.useRef(/* @__PURE__ */ new Map());
|
|
43769
|
+
const [activeSectionName, setActiveSectionName] = reactExports.useState(null);
|
|
43770
|
+
const sections = resolved.groups.map((g) => ({
|
|
43771
|
+
name: g.group.name,
|
|
43772
|
+
label: g.group.label
|
|
43773
|
+
}));
|
|
43774
|
+
const recomputeActiveSection = reactExports.useCallback(() => {
|
|
43775
|
+
const container = railsAreaRef.current;
|
|
43776
|
+
if (!container) {
|
|
43777
|
+
return;
|
|
43778
|
+
}
|
|
43779
|
+
const containerTop = container.getBoundingClientRect().top;
|
|
43780
|
+
let active = null;
|
|
43781
|
+
for (const [name2, el] of sectionRefs.current) {
|
|
43782
|
+
if (el.getBoundingClientRect().top >= containerTop - 1) {
|
|
43783
|
+
active = name2;
|
|
43784
|
+
break;
|
|
43785
|
+
}
|
|
43786
|
+
}
|
|
43787
|
+
if (active == null) {
|
|
43788
|
+
const groups = resolved.groups;
|
|
43789
|
+
active = groups.length > 0 ? groups[groups.length - 1].group.name : null;
|
|
43790
|
+
}
|
|
43791
|
+
setActiveSectionName(active);
|
|
43792
|
+
}, [resolved.groups]);
|
|
43793
|
+
reactExports.useLayoutEffect(() => {
|
|
43794
|
+
recomputeActiveSection();
|
|
43795
|
+
}, [recomputeActiveSection, resolved.groups]);
|
|
43796
|
+
const scrollToSection = reactExports.useCallback((name2) => {
|
|
43797
|
+
const container = railsAreaRef.current;
|
|
43798
|
+
const el = sectionRefs.current.get(name2);
|
|
43799
|
+
if (!container || !el) {
|
|
43800
|
+
return;
|
|
43801
|
+
}
|
|
43802
|
+
const delta = el.getBoundingClientRect().top - container.getBoundingClientRect().top;
|
|
43803
|
+
container.scrollBy({
|
|
43804
|
+
top: delta - SECTION_SCROLL_TOP_GAP_PX,
|
|
43805
|
+
behavior: "smooth"
|
|
43806
|
+
});
|
|
43807
|
+
}, []);
|
|
43083
43808
|
const css2 = useCss((_theme) => ({
|
|
43084
43809
|
container: {
|
|
43085
43810
|
display: "flex",
|
|
43086
43811
|
flexDirection: "column",
|
|
43087
43812
|
height: "100%",
|
|
43088
|
-
width: "100%"
|
|
43813
|
+
width: "100%",
|
|
43814
|
+
// Positioning context for the floating SectionNav pill.
|
|
43815
|
+
position: "relative"
|
|
43089
43816
|
},
|
|
43090
43817
|
railsArea: {
|
|
43091
43818
|
flex: 1,
|
|
@@ -43103,7 +43830,14 @@ function BrowseView({
|
|
|
43103
43830
|
}
|
|
43104
43831
|
}));
|
|
43105
43832
|
return /* @__PURE__ */ jsxs("div", { css: css2.container, children: [
|
|
43106
|
-
|
|
43833
|
+
!resolved.isLoading && resolved.groups.length > 0 ? /* @__PURE__ */ jsx$1(SectionNav, { sections, activeName: activeSectionName, onSelect: scrollToSection }) : null,
|
|
43834
|
+
/* @__PURE__ */ jsx$1("div", { ref: railsAreaRef, css: css2.railsArea, onScroll: recomputeActiveSection, children: resolved.groups.map((group) => /* @__PURE__ */ jsx$1("div", { ref: (el) => {
|
|
43835
|
+
if (el) {
|
|
43836
|
+
sectionRefs.current.set(group.group.name, el);
|
|
43837
|
+
} else {
|
|
43838
|
+
sectionRefs.current.delete(group.group.name);
|
|
43839
|
+
}
|
|
43840
|
+
}, children: /* @__PURE__ */ jsx$1(CardRail, { group, availabilityByExternalId, onSelectItem, onRemoveItem }) }, group.group.name)) }),
|
|
43107
43841
|
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
|
|
43108
43842
|
] });
|
|
43109
43843
|
}
|
|
@@ -43120,6 +43854,7 @@ function TryOnView({
|
|
|
43120
43854
|
onOpenAccordionItem,
|
|
43121
43855
|
onChangeDetailMode,
|
|
43122
43856
|
onChangeSize,
|
|
43857
|
+
onChangeColor,
|
|
43123
43858
|
onAddToCart,
|
|
43124
43859
|
onToggleUntuck
|
|
43125
43860
|
}) {
|
|
@@ -43129,8 +43864,14 @@ function TryOnView({
|
|
|
43129
43864
|
reactExports.useEffect(() => {
|
|
43130
43865
|
function refresh() {
|
|
43131
43866
|
const el = innerRef.current;
|
|
43132
|
-
if (!el)
|
|
43133
|
-
|
|
43867
|
+
if (!el) {
|
|
43868
|
+
return;
|
|
43869
|
+
}
|
|
43870
|
+
const parentEl = el.parentElement;
|
|
43871
|
+
if (!parentEl) {
|
|
43872
|
+
return;
|
|
43873
|
+
}
|
|
43874
|
+
const maxHeightPx = Number(window.getComputedStyle(parentEl).getPropertyValue("max-height").replace("px", ""));
|
|
43134
43875
|
const heightPx = Math.min(el.clientHeight, maxHeightPx || el.clientHeight);
|
|
43135
43876
|
setSheetStyle({
|
|
43136
43877
|
height: `${heightPx}px`
|
|
@@ -43166,7 +43907,7 @@ function TryOnView({
|
|
|
43166
43907
|
},
|
|
43167
43908
|
sheetOuter: {
|
|
43168
43909
|
position: "absolute",
|
|
43169
|
-
// 8px gap to either side, matching
|
|
43910
|
+
// 8px gap to either side, matching quick-view's mobile sheet.
|
|
43170
43911
|
left: "8px",
|
|
43171
43912
|
right: "8px",
|
|
43172
43913
|
bottom: 0,
|
|
@@ -43207,18 +43948,18 @@ function TryOnView({
|
|
|
43207
43948
|
}
|
|
43208
43949
|
}));
|
|
43209
43950
|
return /* @__PURE__ */ jsxs("div", { css: css2.container, children: [
|
|
43210
|
-
/* @__PURE__ */ jsx$1(AvatarPane, { hasSelection: selectedItems.length > 0, frameUrls, mobileFullscreen: true }),
|
|
43951
|
+
/* @__PURE__ */ jsx$1(AvatarPane, { hasSelection: selectedItems.length > 0, frameUrls, mobileFullscreen: true, controls: /* @__PURE__ */ jsx$1(MobileTuckControl, { canTuck, forceUntuck, onToggleUntuck }) }),
|
|
43211
43952
|
/* @__PURE__ */ jsx$1(Button, { variant: "base", css: css2.backButton, onClick: onBackToBrowse, "aria-label": "Back to browse", children: /* @__PURE__ */ jsx$1(SvgIconLeftArrow, { css: css2.backArrow }) }),
|
|
43212
43953
|
/* @__PURE__ */ jsx$1("div", { css: css2.sheetOuter, style: sheetStyle, children: /* @__PURE__ */ jsxs("div", { ref: innerRef, css: css2.sheetInner, style: sheetStyle, children: [
|
|
43213
43954
|
/* @__PURE__ */ jsxs("div", { css: css2.sheetHandleRow, onTouchStart: sheetTouchStart, children: [
|
|
43214
43955
|
/* @__PURE__ */ jsx$1(SvgDragHandle, {}),
|
|
43215
43956
|
/* @__PURE__ */ jsx$1(Text, { variant: "base", css: css2.sheetTitle, children: "RECOMMENDED SIZES" })
|
|
43216
43957
|
] }),
|
|
43217
|
-
sheetSnap === "collapsed" ? null : /* @__PURE__ */ jsx$1("div", { css: css2.sheetContent, children: /* @__PURE__ */ jsx$1(DetailAccordion, { items: selectedItems, openItemExternalId: openAccordionItemId, platform: "mobile", detailMode, isMobileQuickRow, forceUntuck, canTuck, onOpenItem: onOpenAccordionItem, onChangeDetailMode, onChangeSize, onAddToCart, onToggleUntuck }) })
|
|
43958
|
+
sheetSnap === "collapsed" ? null : /* @__PURE__ */ jsx$1("div", { css: css2.sheetContent, children: /* @__PURE__ */ jsx$1(DetailAccordion, { items: selectedItems, openItemExternalId: openAccordionItemId, platform: "mobile", detailMode, isMobileQuickRow, forceUntuck, canTuck, onOpenItem: onOpenAccordionItem, onChangeDetailMode, onChangeSize, onChangeColor, onAddToCart, onToggleUntuck }) })
|
|
43218
43959
|
] }) })
|
|
43219
43960
|
] });
|
|
43220
43961
|
}
|
|
43221
|
-
const logger$
|
|
43962
|
+
const logger$9 = getLogger("use-vto-requests");
|
|
43222
43963
|
function outfitKey(items) {
|
|
43223
43964
|
return items.map((i) => `${i.colorway_size_asset_id}:${i.untucked ? 1 : 0}`).sort().join("|");
|
|
43224
43965
|
}
|
|
@@ -43230,18 +43971,22 @@ function useVtoRequests() {
|
|
|
43230
43971
|
const [lastError, setLastError] = reactExports.useState(null);
|
|
43231
43972
|
const clearError = reactExports.useCallback(() => setLastError(null), []);
|
|
43232
43973
|
const request = reactExports.useCallback((items, priority) => {
|
|
43233
|
-
if (items.length === 0)
|
|
43974
|
+
if (items.length === 0) {
|
|
43975
|
+
return;
|
|
43976
|
+
}
|
|
43234
43977
|
const key = outfitKey(items);
|
|
43235
|
-
if (requestedKeysRef.current.has(key))
|
|
43978
|
+
if (requestedKeysRef.current.has(key)) {
|
|
43979
|
+
return;
|
|
43980
|
+
}
|
|
43236
43981
|
const exec = () => {
|
|
43237
43982
|
requestedKeysRef.current.add(key);
|
|
43238
|
-
logger$
|
|
43983
|
+
logger$9.logDebug("Requesting VTO composition", {
|
|
43239
43984
|
key,
|
|
43240
43985
|
items,
|
|
43241
43986
|
priority
|
|
43242
43987
|
});
|
|
43243
43988
|
requestVto(items).then((resp) => {
|
|
43244
|
-
logger$
|
|
43989
|
+
logger$9.logDebug("VTO frames ready", {
|
|
43245
43990
|
key,
|
|
43246
43991
|
count: resp.frames.length
|
|
43247
43992
|
});
|
|
@@ -43250,7 +43995,7 @@ function useVtoRequests() {
|
|
|
43250
43995
|
[key]: resp.frames
|
|
43251
43996
|
}));
|
|
43252
43997
|
}).catch((error) => {
|
|
43253
|
-
logger$
|
|
43998
|
+
logger$9.logError("VTO request failed", {
|
|
43254
43999
|
error,
|
|
43255
44000
|
items,
|
|
43256
44001
|
key
|
|
@@ -43260,7 +44005,9 @@ function useVtoRequests() {
|
|
|
43260
44005
|
});
|
|
43261
44006
|
};
|
|
43262
44007
|
if (priority) {
|
|
43263
|
-
for (const timer of pendingPrefetchTimersRef.current)
|
|
44008
|
+
for (const timer of pendingPrefetchTimersRef.current) {
|
|
44009
|
+
clearTimeout(timer);
|
|
44010
|
+
}
|
|
43264
44011
|
pendingPrefetchTimersRef.current.clear();
|
|
43265
44012
|
lastPriorityTimeRef.current = Date.now();
|
|
43266
44013
|
exec();
|
|
@@ -43271,7 +44018,9 @@ function useVtoRequests() {
|
|
|
43271
44018
|
if (last) {
|
|
43272
44019
|
const now = Date.now();
|
|
43273
44020
|
const minNext = last + getStaticData().config.api.vtoPrefetchDelayMs;
|
|
43274
|
-
if (now < minNext)
|
|
44021
|
+
if (now < minNext) {
|
|
44022
|
+
delay = minNext - now;
|
|
44023
|
+
}
|
|
43275
44024
|
}
|
|
43276
44025
|
if (delay > 0) {
|
|
43277
44026
|
const timer = setTimeout(() => {
|
|
@@ -43286,15 +44035,21 @@ function useVtoRequests() {
|
|
|
43286
44035
|
reactExports.useEffect(() => {
|
|
43287
44036
|
const timers = pendingPrefetchTimersRef.current;
|
|
43288
44037
|
return () => {
|
|
43289
|
-
for (const timer of timers)
|
|
44038
|
+
for (const timer of timers) {
|
|
44039
|
+
clearTimeout(timer);
|
|
44040
|
+
}
|
|
43290
44041
|
timers.clear();
|
|
43291
44042
|
};
|
|
43292
44043
|
}, []);
|
|
43293
44044
|
const framesForOutfit = reactExports.useCallback((items) => {
|
|
43294
|
-
if (items.length === 0)
|
|
44045
|
+
if (items.length === 0) {
|
|
44046
|
+
return null;
|
|
44047
|
+
}
|
|
43295
44048
|
const key = outfitKey(items);
|
|
43296
44049
|
const frames = framesByKey[key];
|
|
43297
|
-
if (!frames || frames.length === 0)
|
|
44050
|
+
if (!frames || frames.length === 0) {
|
|
44051
|
+
return null;
|
|
44052
|
+
}
|
|
43298
44053
|
const baseUrl2 = getStaticData().config.frames.baseUrl;
|
|
43299
44054
|
return frames.map((u) => applyFrameBaseUrl(u, baseUrl2));
|
|
43300
44055
|
}, [framesByKey]);
|
|
@@ -43313,12 +44068,14 @@ function toWireItems(items) {
|
|
|
43313
44068
|
} : {}
|
|
43314
44069
|
}));
|
|
43315
44070
|
}
|
|
43316
|
-
const logger$
|
|
44071
|
+
const logger$8 = getLogger("overlays/fitting-room");
|
|
43317
44072
|
function measureTopOffset() {
|
|
43318
44073
|
const {
|
|
43319
44074
|
getOverlayTopOffset
|
|
43320
44075
|
} = getStaticData();
|
|
43321
|
-
if (!getOverlayTopOffset)
|
|
44076
|
+
if (!getOverlayTopOffset) {
|
|
44077
|
+
return 0;
|
|
44078
|
+
}
|
|
43322
44079
|
try {
|
|
43323
44080
|
const offset = getOverlayTopOffset();
|
|
43324
44081
|
return Number.isFinite(offset) && offset > 0 ? offset : 0;
|
|
@@ -43326,7 +44083,9 @@ function measureTopOffset() {
|
|
|
43326
44083
|
return 0;
|
|
43327
44084
|
}
|
|
43328
44085
|
}
|
|
43329
|
-
function FittingRoomOverlay(
|
|
44086
|
+
function FittingRoomOverlay({
|
|
44087
|
+
preselectExternalId
|
|
44088
|
+
}) {
|
|
43330
44089
|
const deviceLayout = useMainStore((state) => state.deviceLayout);
|
|
43331
44090
|
const userIsLoggedIn = useMainStore((state) => state.userIsLoggedIn);
|
|
43332
44091
|
const userHasAvatar = useMainStore((state) => state.userHasAvatar);
|
|
@@ -43342,7 +44101,6 @@ function FittingRoomOverlay() {
|
|
|
43342
44101
|
const [forceUntuck, setForceUntuck] = reactExports.useState(false);
|
|
43343
44102
|
const [lastAddedExternalId, setLastAddedExternalId] = reactExports.useState(null);
|
|
43344
44103
|
const [mobileMode, setMobileMode] = reactExports.useState("browse");
|
|
43345
|
-
const [zoomed, setZoomed] = reactExports.useState(false);
|
|
43346
44104
|
const {
|
|
43347
44105
|
snap: sheetSnap,
|
|
43348
44106
|
setSnap: setSheetSnap,
|
|
@@ -43357,13 +44115,17 @@ function FittingRoomOverlay() {
|
|
|
43357
44115
|
} = useVtoRequests();
|
|
43358
44116
|
reactExports.useEffect(() => {
|
|
43359
44117
|
const savedScrollY = window.scrollY;
|
|
43360
|
-
if (savedScrollY > 0)
|
|
44118
|
+
if (savedScrollY > 0) {
|
|
44119
|
+
window.scrollTo(0, 0);
|
|
44120
|
+
}
|
|
43361
44121
|
setTopOffset(measureTopOffset());
|
|
43362
44122
|
const onResize = () => setTopOffset(measureTopOffset());
|
|
43363
44123
|
window.addEventListener("resize", onResize);
|
|
43364
44124
|
return () => {
|
|
43365
44125
|
window.removeEventListener("resize", onResize);
|
|
43366
|
-
if (savedScrollY > 0)
|
|
44126
|
+
if (savedScrollY > 0) {
|
|
44127
|
+
window.scrollTo(0, savedScrollY);
|
|
44128
|
+
}
|
|
43367
44129
|
};
|
|
43368
44130
|
}, []);
|
|
43369
44131
|
const selectedItems = reactExports.useMemo(() => {
|
|
@@ -43376,14 +44138,22 @@ function FittingRoomOverlay() {
|
|
|
43376
44138
|
indexed.sort((a, b) => {
|
|
43377
44139
|
const aOrder = a.item.styleCategoryGroup?.display_order ?? Number.MAX_SAFE_INTEGER;
|
|
43378
44140
|
const bOrder = b.item.styleCategoryGroup?.display_order ?? Number.MAX_SAFE_INTEGER;
|
|
43379
|
-
if (aOrder !== bOrder)
|
|
44141
|
+
if (aOrder !== bOrder) {
|
|
44142
|
+
return aOrder - bOrder;
|
|
44143
|
+
}
|
|
43380
44144
|
return a.idx - b.idx;
|
|
43381
44145
|
});
|
|
43382
44146
|
return indexed.map(({
|
|
43383
44147
|
item
|
|
43384
44148
|
}) => item);
|
|
43385
44149
|
}, [resolved.items, selectedExternalIds]);
|
|
43386
|
-
const canTuck = reactExports.useMemo(() => selectedItems.some((top) =>
|
|
44150
|
+
const canTuck = reactExports.useMemo(() => selectedItems.some((top) => {
|
|
44151
|
+
const topCategory = top.styleCategory;
|
|
44152
|
+
if (!topCategory?.tuckable) {
|
|
44153
|
+
return false;
|
|
44154
|
+
}
|
|
44155
|
+
return selectedItems.some((other) => !!other.styleCategory && !other.styleCategory.tuckable && other.styleCategory.layer_order > topCategory.layer_order);
|
|
44156
|
+
}), [selectedItems]);
|
|
43387
44157
|
const availabilityByExternalId = reactExports.useMemo(() => {
|
|
43388
44158
|
const out = {};
|
|
43389
44159
|
for (const item of resolved.items) {
|
|
@@ -43392,11 +44162,17 @@ function FittingRoomOverlay() {
|
|
|
43392
44162
|
return out;
|
|
43393
44163
|
}, [resolved, selectedExternalIds]);
|
|
43394
44164
|
const ensureSizeForItem = reactExports.useCallback((item) => {
|
|
43395
|
-
if (item.storage.colorwaySizeAssetId != null)
|
|
44165
|
+
if (item.storage.colorwaySizeAssetId != null) {
|
|
44166
|
+
return;
|
|
44167
|
+
}
|
|
43396
44168
|
const productData = buildVtoProductDataFromResolved(item);
|
|
43397
|
-
if (!productData)
|
|
44169
|
+
if (!productData) {
|
|
44170
|
+
return;
|
|
44171
|
+
}
|
|
43398
44172
|
const csa = findRecommendedColorSize(productData, item.storage.color);
|
|
43399
|
-
if (!csa)
|
|
44173
|
+
if (!csa) {
|
|
44174
|
+
return;
|
|
44175
|
+
}
|
|
43400
44176
|
const sizeRec = item.loadedProduct?.sizeFitRecommendation;
|
|
43401
44177
|
const sizeLabel = sizeRec ? getSizeLabelFromSize(sizeRec.recommended_size) : productData.recommendedSizeLabel;
|
|
43402
44178
|
updateFittingRoomItem(item.externalId, {
|
|
@@ -43404,7 +44180,7 @@ function FittingRoomOverlay() {
|
|
|
43404
44180
|
size: sizeLabel,
|
|
43405
44181
|
color: csa.colorLabel
|
|
43406
44182
|
});
|
|
43407
|
-
logger$
|
|
44183
|
+
logger$8.logDebug("Auto-picked recommended size", {
|
|
43408
44184
|
externalId: item.externalId,
|
|
43409
44185
|
sizeLabel,
|
|
43410
44186
|
csaId: csa.colorwaySizeAssetId
|
|
@@ -43412,12 +44188,16 @@ function FittingRoomOverlay() {
|
|
|
43412
44188
|
}, [updateFittingRoomItem]);
|
|
43413
44189
|
const handleSelectItem = reactExports.useCallback((externalId) => {
|
|
43414
44190
|
const item = resolved.items.find((i) => i.externalId === externalId);
|
|
43415
|
-
if (!item)
|
|
44191
|
+
if (!item) {
|
|
44192
|
+
return;
|
|
44193
|
+
}
|
|
43416
44194
|
const isSelected = selectedExternalIds.has(externalId);
|
|
43417
44195
|
const nextSelected = new Set(selectedExternalIds);
|
|
43418
44196
|
if (isSelected) {
|
|
43419
44197
|
nextSelected.delete(externalId);
|
|
43420
|
-
if (openAccordionItemId === externalId)
|
|
44198
|
+
if (openAccordionItemId === externalId) {
|
|
44199
|
+
setOpenAccordionItemId(null);
|
|
44200
|
+
}
|
|
43421
44201
|
} else {
|
|
43422
44202
|
nextSelected.add(externalId);
|
|
43423
44203
|
ensureSizeForItem(item);
|
|
@@ -43431,30 +44211,55 @@ function FittingRoomOverlay() {
|
|
|
43431
44211
|
}, [resolved, selectedExternalIds, openAccordionItemId, isMobileLayout, ensureSizeForItem]);
|
|
43432
44212
|
const handleChangeSize = reactExports.useCallback((externalId, sizeLabel) => {
|
|
43433
44213
|
const item = resolved.items.find((i) => i.externalId === externalId);
|
|
43434
|
-
if (!item)
|
|
44214
|
+
if (!item) {
|
|
44215
|
+
return;
|
|
44216
|
+
}
|
|
43435
44217
|
const productData = buildVtoProductDataFromResolved(item);
|
|
43436
|
-
if (!productData)
|
|
44218
|
+
if (!productData) {
|
|
44219
|
+
return;
|
|
44220
|
+
}
|
|
43437
44221
|
const csa = findCsaByLabel(productData, sizeLabel, item.storage.color);
|
|
43438
|
-
if (!csa)
|
|
44222
|
+
if (!csa) {
|
|
44223
|
+
return;
|
|
44224
|
+
}
|
|
43439
44225
|
updateFittingRoomItem(externalId, {
|
|
43440
44226
|
colorwaySizeAssetId: csa.colorwaySizeAssetId,
|
|
43441
44227
|
size: sizeLabel,
|
|
43442
44228
|
color: csa.colorLabel
|
|
43443
44229
|
});
|
|
43444
44230
|
}, [resolved.items, updateFittingRoomItem]);
|
|
44231
|
+
const handleChangeColor = reactExports.useCallback((externalId, colorLabel) => {
|
|
44232
|
+
const item = resolved.items.find((i) => i.externalId === externalId);
|
|
44233
|
+
if (!item || !item.storage.size) {
|
|
44234
|
+
return;
|
|
44235
|
+
}
|
|
44236
|
+
const productData = buildVtoProductDataFromResolved(item);
|
|
44237
|
+
if (!productData) {
|
|
44238
|
+
return;
|
|
44239
|
+
}
|
|
44240
|
+
const csa = findCsaByLabel(productData, item.storage.size, colorLabel);
|
|
44241
|
+
if (!csa) {
|
|
44242
|
+
return;
|
|
44243
|
+
}
|
|
44244
|
+
updateFittingRoomItem(externalId, {
|
|
44245
|
+
colorwaySizeAssetId: csa.colorwaySizeAssetId,
|
|
44246
|
+
size: item.storage.size,
|
|
44247
|
+
color: csa.colorLabel
|
|
44248
|
+
});
|
|
44249
|
+
}, [resolved.items, updateFittingRoomItem]);
|
|
43445
44250
|
const handleAddToCart = reactExports.useCallback(async (externalId) => {
|
|
43446
44251
|
const {
|
|
43447
44252
|
addToCart
|
|
43448
44253
|
} = getStaticData();
|
|
43449
44254
|
if (!addToCart) {
|
|
43450
|
-
logger$
|
|
44255
|
+
logger$8.logWarn("No addToCart callback configured; skipping", {
|
|
43451
44256
|
externalId
|
|
43452
44257
|
});
|
|
43453
44258
|
return;
|
|
43454
44259
|
}
|
|
43455
44260
|
const item = resolved.items.find((i) => i.externalId === externalId);
|
|
43456
44261
|
if (!item || !item.storage.size || !item.storage.color) {
|
|
43457
|
-
logger$
|
|
44262
|
+
logger$8.logWarn("Cannot add to cart: missing size or color", {
|
|
43458
44263
|
externalId,
|
|
43459
44264
|
size: item?.storage.size,
|
|
43460
44265
|
color: item?.storage.color
|
|
@@ -43468,7 +44273,7 @@ function FittingRoomOverlay() {
|
|
|
43468
44273
|
});
|
|
43469
44274
|
closeOverlay();
|
|
43470
44275
|
} catch (error) {
|
|
43471
|
-
logger$
|
|
44276
|
+
logger$8.logError("addToCart failed", {
|
|
43472
44277
|
error,
|
|
43473
44278
|
externalId
|
|
43474
44279
|
});
|
|
@@ -43477,17 +44282,18 @@ function FittingRoomOverlay() {
|
|
|
43477
44282
|
const handleToggleUntuck = reactExports.useCallback(() => {
|
|
43478
44283
|
setForceUntuck((prev2) => !prev2);
|
|
43479
44284
|
}, []);
|
|
43480
|
-
const handleToggleZoom = reactExports.useCallback(() => {
|
|
43481
|
-
setZoomed((prev2) => !prev2);
|
|
43482
|
-
}, []);
|
|
43483
44285
|
const handleRemoveItem = reactExports.useCallback((externalId) => {
|
|
43484
44286
|
setSelectedExternalIds((prev2) => {
|
|
43485
|
-
if (!prev2.has(externalId))
|
|
44287
|
+
if (!prev2.has(externalId)) {
|
|
44288
|
+
return prev2;
|
|
44289
|
+
}
|
|
43486
44290
|
const next2 = new Set(prev2);
|
|
43487
44291
|
next2.delete(externalId);
|
|
43488
44292
|
return next2;
|
|
43489
44293
|
});
|
|
43490
|
-
if (openAccordionItemId === externalId)
|
|
44294
|
+
if (openAccordionItemId === externalId) {
|
|
44295
|
+
setOpenAccordionItemId(null);
|
|
44296
|
+
}
|
|
43491
44297
|
}, [openAccordionItemId]);
|
|
43492
44298
|
const handleTryItOn = reactExports.useCallback(() => {
|
|
43493
44299
|
setMobileMode("try-on");
|
|
@@ -43501,10 +44307,25 @@ function FittingRoomOverlay() {
|
|
|
43501
44307
|
setOpenAccordionItemId(null);
|
|
43502
44308
|
}
|
|
43503
44309
|
}, [openAccordionItemId, selectedExternalIds]);
|
|
44310
|
+
const preselectAppliedRef = reactExports.useRef(false);
|
|
44311
|
+
reactExports.useEffect(() => {
|
|
44312
|
+
if (preselectAppliedRef.current || !preselectExternalId) {
|
|
44313
|
+
return;
|
|
44314
|
+
}
|
|
44315
|
+
if (!resolved.items.some((i) => i.externalId === preselectExternalId)) {
|
|
44316
|
+
return;
|
|
44317
|
+
}
|
|
44318
|
+
preselectAppliedRef.current = true;
|
|
44319
|
+
handleSelectItem(preselectExternalId);
|
|
44320
|
+
}, [preselectExternalId, resolved.items, handleSelectItem]);
|
|
43504
44321
|
const outfit = reactExports.useMemo(() => buildOutfit(selectedExternalIds, resolved, forceUntuck, lastAddedExternalId), [selectedExternalIds, resolved, forceUntuck, lastAddedExternalId]);
|
|
43505
44322
|
reactExports.useEffect(() => {
|
|
43506
|
-
if (!userIsLoggedIn || !userHasAvatar)
|
|
43507
|
-
|
|
44323
|
+
if (!userIsLoggedIn || !userHasAvatar) {
|
|
44324
|
+
return;
|
|
44325
|
+
}
|
|
44326
|
+
if (outfit.items.length === 0) {
|
|
44327
|
+
return;
|
|
44328
|
+
}
|
|
43508
44329
|
requestVtoComposition(toWireItems(outfit.items), true);
|
|
43509
44330
|
if (getStaticData().config.features.vtoPrefetch) {
|
|
43510
44331
|
for (const alt of outfit.alternates) {
|
|
@@ -43515,7 +44336,9 @@ function FittingRoomOverlay() {
|
|
|
43515
44336
|
const frameUrls = reactExports.useMemo(() => {
|
|
43516
44337
|
if (outfit.items.length === 0) {
|
|
43517
44338
|
const bareFrames = userProfile?.avatar_frames;
|
|
43518
|
-
if (!bareFrames || bareFrames.length === 0)
|
|
44339
|
+
if (!bareFrames || bareFrames.length === 0) {
|
|
44340
|
+
return null;
|
|
44341
|
+
}
|
|
43519
44342
|
const baseUrl2 = getStaticData().config.frames.baseUrl;
|
|
43520
44343
|
return bareFrames.map((u) => applyFrameBaseUrl(u, baseUrl2));
|
|
43521
44344
|
}
|
|
@@ -43539,7 +44362,7 @@ function FittingRoomOverlay() {
|
|
|
43539
44362
|
closeOverlay();
|
|
43540
44363
|
const authManager2 = getAuthManager();
|
|
43541
44364
|
authManager2.logout().catch((error) => {
|
|
43542
|
-
logger$
|
|
44365
|
+
logger$8.logError("Error during logout", {
|
|
43543
44366
|
error
|
|
43544
44367
|
});
|
|
43545
44368
|
});
|
|
@@ -43586,29 +44409,6 @@ function FittingRoomOverlay() {
|
|
|
43586
44409
|
fontSize: "13px",
|
|
43587
44410
|
textDecoration: "underline",
|
|
43588
44411
|
marginTop: "8px"
|
|
43589
|
-
},
|
|
43590
|
-
snackbar: {
|
|
43591
|
-
position: "absolute",
|
|
43592
|
-
left: "50%",
|
|
43593
|
-
bottom: "24px",
|
|
43594
|
-
transform: "translateX(-50%)",
|
|
43595
|
-
padding: "12px 20px",
|
|
43596
|
-
borderRadius: "24px",
|
|
43597
|
-
backgroundColor: theme.color_fg_text,
|
|
43598
|
-
color: "#FFFFFF",
|
|
43599
|
-
fontSize: "13px",
|
|
43600
|
-
display: "flex",
|
|
43601
|
-
alignItems: "center",
|
|
43602
|
-
gap: "12px",
|
|
43603
|
-
zIndex: 10,
|
|
43604
|
-
maxWidth: "calc(100% - 32px)"
|
|
43605
|
-
},
|
|
43606
|
-
snackbarDismiss: {
|
|
43607
|
-
background: "none",
|
|
43608
|
-
border: "none",
|
|
43609
|
-
color: "#FFFFFF",
|
|
43610
|
-
fontSize: "16px",
|
|
43611
|
-
cursor: "pointer"
|
|
43612
44412
|
}
|
|
43613
44413
|
}));
|
|
43614
44414
|
const isMobileTryOn = isMobileLayout && mobileMode === "try-on";
|
|
@@ -43639,15 +44439,12 @@ function FittingRoomOverlay() {
|
|
|
43639
44439
|
/* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.emptyTagline, t: "landing.description" }),
|
|
43640
44440
|
/* @__PURE__ */ jsx$1("div", { css: css2.emptyShopNow, children: /* @__PURE__ */ jsx$1(ButtonT, { variant: "primary", t: "fitting_room.shop_now", onClick: handleShopNow }) }),
|
|
43641
44441
|
userIsLoggedIn ? /* @__PURE__ */ jsx$1(LinkT, { variant: "underline", css: css2.emptySignOut, t: "fitting_room.sign_out", onClick: handleSignOut }) : null
|
|
43642
|
-
] }) }) : 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, onAddToCart: handleAddToCart, onToggleUntuck: handleToggleUntuck }) : /* @__PURE__ */ jsx$1(DesktopLayout$1, { resolved, selectedItems, availabilityByExternalId, openAccordionItemId, detailMode, forceUntuck, canTuck,
|
|
43643
|
-
vtoError ? /* @__PURE__ */
|
|
43644
|
-
/* @__PURE__ */ jsx$1(TextT, { variant: "base", t: "fitting_room.vto_error" }),
|
|
43645
|
-
/* @__PURE__ */ jsx$1("button", { css: css2.snackbarDismiss, onClick: clearVtoError, "aria-label": "Dismiss", children: "×" })
|
|
43646
|
-
] }) : null
|
|
44442
|
+
] }) }) : 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 }),
|
|
44443
|
+
vtoError ? /* @__PURE__ */ jsx$1(Snackbar, { messageKey: "fitting_room.vto_error", onDismiss: clearVtoError }) : null
|
|
43647
44444
|
] }) });
|
|
43648
44445
|
}
|
|
43649
44446
|
const CONTACT_US_LINK = "mailto:info@thefittingroom.tech?subject=Forgot%20Password%20Assistance";
|
|
43650
|
-
const logger$
|
|
44447
|
+
const logger$7 = getLogger("forgot-password");
|
|
43651
44448
|
function ForgotPasswordOverlay({
|
|
43652
44449
|
returnToOverlay
|
|
43653
44450
|
}) {
|
|
@@ -43713,7 +44510,7 @@ function ForgotPasswordOverlay({
|
|
|
43713
44510
|
await authManager2.sendPasswordResetEmail(email2);
|
|
43714
44511
|
setLinkSent(true);
|
|
43715
44512
|
} catch (error) {
|
|
43716
|
-
logger$
|
|
44513
|
+
logger$7.logError("Error sending password reset email:", {
|
|
43717
44514
|
error
|
|
43718
44515
|
});
|
|
43719
44516
|
}
|
|
@@ -43731,7 +44528,7 @@ function ForgotPasswordOverlay({
|
|
|
43731
44528
|
return;
|
|
43732
44529
|
}
|
|
43733
44530
|
const email = emailEl.value;
|
|
43734
|
-
resetUserPassword(email);
|
|
44531
|
+
void resetUserPassword(email);
|
|
43735
44532
|
}, [t]);
|
|
43736
44533
|
const handleBackToSignInClick = reactExports.useCallback(() => {
|
|
43737
44534
|
openOverlay(OverlayName.SIGN_IN, {
|
|
@@ -43932,7 +44729,7 @@ function TfrTitle() {
|
|
|
43932
44729
|
}));
|
|
43933
44730
|
return /* @__PURE__ */ jsx$1("div", { css: css2.container, children: /* @__PURE__ */ jsx$1(SvgTfrName, { css: css2.nameIcon }) });
|
|
43934
44731
|
}
|
|
43935
|
-
const logger$
|
|
44732
|
+
const logger$6 = getLogger("sign-in");
|
|
43936
44733
|
function SignInOverlay({
|
|
43937
44734
|
returnToOverlay
|
|
43938
44735
|
}) {
|
|
@@ -44006,7 +44803,7 @@ function SignInOverlay({
|
|
|
44006
44803
|
closeOverlay();
|
|
44007
44804
|
}
|
|
44008
44805
|
} catch (error) {
|
|
44009
|
-
logger$
|
|
44806
|
+
logger$6.logError("Login failed:", {
|
|
44010
44807
|
error
|
|
44011
44808
|
});
|
|
44012
44809
|
setEmailError(" ");
|
|
@@ -44038,7 +44835,7 @@ function SignInOverlay({
|
|
|
44038
44835
|
}
|
|
44039
44836
|
const email = emailEl.value;
|
|
44040
44837
|
const password = passwordEl.value;
|
|
44041
|
-
loginUser(email, password);
|
|
44838
|
+
void loginUser(email, password);
|
|
44042
44839
|
}, [returnToOverlay, closeOverlay, openOverlay, t]);
|
|
44043
44840
|
const handleForgotPasswordClick = reactExports.useCallback(() => {
|
|
44044
44841
|
openOverlay(OverlayName.FORGOT_PASSWORD, {
|
|
@@ -44369,8 +45166,8 @@ function FitChart({
|
|
|
44369
45166
|
const AVATAR_IMAGE_ASPECT_RATIO = 2 / 3;
|
|
44370
45167
|
const AVATAR_GUTTER_HEIGHT_PX = 100;
|
|
44371
45168
|
const CONTENT_AREA_WIDTH_PX = 550;
|
|
44372
|
-
const logger$
|
|
44373
|
-
function
|
|
45169
|
+
const logger$5 = getLogger("overlays/quick-view");
|
|
45170
|
+
function QuickViewOverlay() {
|
|
44374
45171
|
const userIsLoggedIn = useMainStore((state) => state.userIsLoggedIn);
|
|
44375
45172
|
const userHasAvatar = useMainStore((state) => state.userHasAvatar);
|
|
44376
45173
|
const userProfile = useMainStore((state) => state.userProfile);
|
|
@@ -44384,18 +45181,20 @@ function VtoSingleOverlay() {
|
|
|
44384
45181
|
const [modalStyle, setModalStyle] = reactExports.useState({});
|
|
44385
45182
|
const {
|
|
44386
45183
|
request: vtoRequest,
|
|
44387
|
-
framesForOutfit
|
|
45184
|
+
framesForOutfit,
|
|
45185
|
+
lastError: vtoError,
|
|
45186
|
+
clearError: clearVtoError
|
|
44388
45187
|
} = useVtoRequests();
|
|
44389
45188
|
reactExports.useEffect(() => {
|
|
44390
45189
|
if (!userIsLoggedIn) {
|
|
44391
45190
|
openOverlay(OverlayName.LANDING, {
|
|
44392
|
-
returnToOverlay: OverlayName.
|
|
45191
|
+
returnToOverlay: OverlayName.QUICK_VIEW
|
|
44393
45192
|
});
|
|
44394
45193
|
return;
|
|
44395
45194
|
}
|
|
44396
45195
|
if (userIsLoggedIn && userHasAvatar === false) {
|
|
44397
45196
|
openOverlay(OverlayName.GET_APP, {
|
|
44398
|
-
returnToOverlay: OverlayName.
|
|
45197
|
+
returnToOverlay: OverlayName.QUICK_VIEW,
|
|
44399
45198
|
noAvatar: true
|
|
44400
45199
|
});
|
|
44401
45200
|
return;
|
|
@@ -44434,7 +45233,9 @@ function VtoSingleOverlay() {
|
|
|
44434
45233
|
const {
|
|
44435
45234
|
color: selectedColor
|
|
44436
45235
|
} = await currentProduct.getSelectedOptions();
|
|
44437
|
-
const
|
|
45236
|
+
const styleCategoryIndex = await loadStyleCategoryIndex();
|
|
45237
|
+
const styleCategoryGroup = styleCategoryIndex.groupForCategory(storeProduct.style.style_category_name);
|
|
45238
|
+
const styleCategoryLabel = styleCategoryGroup?.label ?? null;
|
|
44438
45239
|
const sizeRecommendationRecord = storeProduct.sizeFitRecommendation;
|
|
44439
45240
|
{
|
|
44440
45241
|
const recommendedSizeId = sizeRecommendationRecord.recommended_size.id || null;
|
|
@@ -44463,7 +45264,7 @@ function VtoSingleOverlay() {
|
|
|
44463
45264
|
const sku = csaRec.sku;
|
|
44464
45265
|
const variant = variants.find((v) => v.sku === sku);
|
|
44465
45266
|
if (!variant) {
|
|
44466
|
-
logger$
|
|
45267
|
+
logger$5.logWarn(`Variant not found for externalId: ${currentProduct.externalId} sizeId: ${sizeId} sku: ${sku}`);
|
|
44467
45268
|
continue;
|
|
44468
45269
|
}
|
|
44469
45270
|
const colorLabel = variant.color || null;
|
|
@@ -44476,7 +45277,7 @@ function VtoSingleOverlay() {
|
|
|
44476
45277
|
});
|
|
44477
45278
|
}
|
|
44478
45279
|
if (!colors.length) {
|
|
44479
|
-
logger$
|
|
45280
|
+
logger$5.logWarn(`No valid colorways found for externalId: ${currentProduct.externalId} sizeId: ${sizeId}`);
|
|
44480
45281
|
continue;
|
|
44481
45282
|
}
|
|
44482
45283
|
sizes.push({
|
|
@@ -44519,7 +45320,7 @@ function VtoSingleOverlay() {
|
|
|
44519
45320
|
setSelectedColorLabel(recommendedColorLabel);
|
|
44520
45321
|
}
|
|
44521
45322
|
} catch (error) {
|
|
44522
|
-
logger$
|
|
45323
|
+
logger$5.logError("Error fetching initial data:", {
|
|
44523
45324
|
error
|
|
44524
45325
|
});
|
|
44525
45326
|
setVtoProductData(false);
|
|
@@ -44530,7 +45331,7 @@ function VtoSingleOverlay() {
|
|
|
44530
45331
|
if (vtoProductData !== null) {
|
|
44531
45332
|
return;
|
|
44532
45333
|
}
|
|
44533
|
-
setupInitialVtoData();
|
|
45334
|
+
void setupInitialVtoData();
|
|
44534
45335
|
}, [storeProductData, vtoProductData, userProfile]);
|
|
44535
45336
|
const {
|
|
44536
45337
|
sizeColorRecord: selectedColorSizeRecord,
|
|
@@ -44592,7 +45393,7 @@ function VtoSingleOverlay() {
|
|
|
44592
45393
|
if (!rewritten) {
|
|
44593
45394
|
return null;
|
|
44594
45395
|
}
|
|
44595
|
-
logger$
|
|
45396
|
+
logger$5.logDebug(`{{ts}} - Displaying VTO for sku: ${selectedColorSizeRecord.sku}`);
|
|
44596
45397
|
rewritten.forEach((url) => {
|
|
44597
45398
|
const img = new Image();
|
|
44598
45399
|
img.src = url;
|
|
@@ -44603,7 +45404,7 @@ function VtoSingleOverlay() {
|
|
|
44603
45404
|
closeOverlay();
|
|
44604
45405
|
const authManager2 = getAuthManager();
|
|
44605
45406
|
authManager2.logout().catch((error) => {
|
|
44606
|
-
logger$
|
|
45407
|
+
logger$5.logError("Error during logout:", {
|
|
44607
45408
|
error
|
|
44608
45409
|
});
|
|
44609
45410
|
});
|
|
@@ -44625,7 +45426,7 @@ function VtoSingleOverlay() {
|
|
|
44625
45426
|
color: selectedColorLabel
|
|
44626
45427
|
});
|
|
44627
45428
|
} catch (error) {
|
|
44628
|
-
logger$
|
|
45429
|
+
logger$5.logError("Error adding to cart:", {
|
|
44629
45430
|
error
|
|
44630
45431
|
});
|
|
44631
45432
|
}
|
|
@@ -44642,7 +45443,10 @@ function VtoSingleOverlay() {
|
|
|
44642
45443
|
} else {
|
|
44643
45444
|
Layout = DesktopLayout;
|
|
44644
45445
|
}
|
|
44645
|
-
return /* @__PURE__ */
|
|
45446
|
+
return /* @__PURE__ */ jsxs(SidecarModalFrame, { onRequestClose: closeOverlay, contentStyle: modalStyle, children: [
|
|
45447
|
+
/* @__PURE__ */ jsx$1(Layout, { loadedProductData: vtoProductData, selectedColorSizeRecord, availableColorLabels, selectedColorLabel, selectedSizeLabel, frameUrls, setModalStyle, onClose: closeOverlay, onChangeColor: setSelectedColorLabel, onChangeSize: setSelectedSizeLabel, onAddToCart: handleAddToCartClick, onSignOut: handleSignOutClick }),
|
|
45448
|
+
vtoError ? /* @__PURE__ */ jsx$1(Snackbar, { messageKey: "quick-view.vto_error", onDismiss: clearVtoError }) : null
|
|
45449
|
+
] });
|
|
44646
45450
|
}
|
|
44647
45451
|
function NoFitLayout({
|
|
44648
45452
|
onClose,
|
|
@@ -44674,14 +45478,59 @@ function NoFitLayout({
|
|
|
44674
45478
|
}
|
|
44675
45479
|
}));
|
|
44676
45480
|
return /* @__PURE__ */ jsxs("div", { css: css2.mainContainer, children: [
|
|
44677
|
-
/* @__PURE__ */ jsx$1("div", { css: css2.titlebarContainer, children: /* @__PURE__ */ jsx$1(ModalTitlebar, { title: t("
|
|
45481
|
+
/* @__PURE__ */ jsx$1("div", { css: css2.titlebarContainer, children: /* @__PURE__ */ jsx$1(ModalTitlebar, { title: t("quick-view.title"), onCloseClick: onClose }) }),
|
|
44678
45482
|
/* @__PURE__ */ jsxs("div", { css: css2.contentContainer, children: [
|
|
44679
45483
|
/* @__PURE__ */ jsx$1("div", { children: " " }),
|
|
44680
|
-
/* @__PURE__ */ jsx$1("div", { css: css2.messageContainer, children: /* @__PURE__ */ jsx$1(TextT, { variant: "base", t: "
|
|
45484
|
+
/* @__PURE__ */ jsx$1("div", { css: css2.messageContainer, children: /* @__PURE__ */ jsx$1(TextT, { variant: "base", t: "quick-view.no_recommendation" }) }),
|
|
44681
45485
|
/* @__PURE__ */ jsx$1("div", { css: css2.footerContainer, children: /* @__PURE__ */ jsx$1(Footer, { onSignOutClick: onSignOut }) })
|
|
44682
45486
|
] })
|
|
44683
45487
|
] });
|
|
44684
45488
|
}
|
|
45489
|
+
function FittingRoomToggleButton() {
|
|
45490
|
+
const {
|
|
45491
|
+
currentProduct
|
|
45492
|
+
} = getStaticData();
|
|
45493
|
+
const productId = currentProduct?.externalId ?? null;
|
|
45494
|
+
const isInFittingRoom = useMainStore((state) => productId == null ? false : state.fittingRoom.some((item) => item.externalId === productId));
|
|
45495
|
+
const css2 = useCss((theme) => ({
|
|
45496
|
+
button: {
|
|
45497
|
+
marginTop: "10px",
|
|
45498
|
+
width: "100%",
|
|
45499
|
+
display: "flex",
|
|
45500
|
+
alignItems: "center",
|
|
45501
|
+
justifyContent: "center",
|
|
45502
|
+
gap: "10px",
|
|
45503
|
+
padding: "13px",
|
|
45504
|
+
backgroundColor: "#FFFFFF",
|
|
45505
|
+
border: `1px solid ${theme.color_fg_text}`,
|
|
45506
|
+
borderRadius: "30px",
|
|
45507
|
+
cursor: "pointer"
|
|
45508
|
+
},
|
|
45509
|
+
icon: {
|
|
45510
|
+
color: theme.color_fg_text,
|
|
45511
|
+
width: "20px",
|
|
45512
|
+
height: "20px"
|
|
45513
|
+
},
|
|
45514
|
+
text: {
|
|
45515
|
+
fontSize: "14px",
|
|
45516
|
+
textTransform: "uppercase"
|
|
45517
|
+
}
|
|
45518
|
+
}));
|
|
45519
|
+
if (productId == null) {
|
|
45520
|
+
return null;
|
|
45521
|
+
}
|
|
45522
|
+
const handleClick = () => {
|
|
45523
|
+
toggleFittingRoomItem(productId, currentProduct?.handle ?? null, true).catch((error) => {
|
|
45524
|
+
logger$5.logError("toggleFittingRoomItem failed", {
|
|
45525
|
+
error
|
|
45526
|
+
});
|
|
45527
|
+
});
|
|
45528
|
+
};
|
|
45529
|
+
return /* @__PURE__ */ jsxs("button", { type: "button", onClick: handleClick, css: css2.button, children: [
|
|
45530
|
+
/* @__PURE__ */ jsx$1(SvgFittingRoomIcon, { css: css2.icon }),
|
|
45531
|
+
/* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.text, t: isInFittingRoom ? "added_to_fitting_room" : "add_to_fitting_room" })
|
|
45532
|
+
] });
|
|
45533
|
+
}
|
|
44685
45534
|
function MobileLayout({
|
|
44686
45535
|
loadedProductData,
|
|
44687
45536
|
selectedColorSizeRecord,
|
|
@@ -44787,7 +45636,11 @@ function MobileLayout({
|
|
|
44787
45636
|
if (!bottomFrameInnerEl) {
|
|
44788
45637
|
return;
|
|
44789
45638
|
}
|
|
44790
|
-
const
|
|
45639
|
+
const parentEl = bottomFrameInnerEl.parentElement;
|
|
45640
|
+
if (!parentEl) {
|
|
45641
|
+
return;
|
|
45642
|
+
}
|
|
45643
|
+
const maxHeightPx = Number(window.getComputedStyle(parentEl).getPropertyValue("max-height").replace("px", ""));
|
|
44791
45644
|
const heightPx = Math.min(bottomFrameInnerEl.clientHeight, maxHeightPx);
|
|
44792
45645
|
const bottomFrameStyle = {
|
|
44793
45646
|
height: `${heightPx}px`
|
|
@@ -44892,8 +45745,11 @@ function MobileContentExpanded({
|
|
|
44892
45745
|
/* @__PURE__ */ jsx$1("div", { css: css2.sizeSelectorContainer, children: /* @__PURE__ */ jsx$1(SizeSelector, { loadedProductData, selectedSizeLabel, onChangeSize }) }),
|
|
44893
45746
|
/* @__PURE__ */ jsx$1("div", { css: css2.itemFitTextContainer, children: /* @__PURE__ */ jsx$1(ItemFitText, { loadedProductData }) }),
|
|
44894
45747
|
/* @__PURE__ */ jsx$1("div", { css: css2.itemFitDetailsContainer, children: /* @__PURE__ */ jsx$1(ItemFitDetails, { loadedProductData, selectedSizeLabel }) }),
|
|
44895
|
-
/* @__PURE__ */
|
|
44896
|
-
|
|
45748
|
+
/* @__PURE__ */ jsxs("div", { css: css2.buttonContainer, children: [
|
|
45749
|
+
/* @__PURE__ */ jsx$1(AddToCartButton, { onClick: onAddToCart }),
|
|
45750
|
+
/* @__PURE__ */ jsx$1(FittingRoomToggleButton, {})
|
|
45751
|
+
] }),
|
|
45752
|
+
/* @__PURE__ */ jsx$1("div", { css: css2.productDetailsContainer, children: /* @__PURE__ */ jsx$1(LinkT, { variant: "base", css: css2.productDetailsText, t: "quick-view.view_product_details", onClick: handleProductDetailsClick }) })
|
|
44897
45753
|
] });
|
|
44898
45754
|
}
|
|
44899
45755
|
function MobileContentFull({
|
|
@@ -44988,8 +45844,11 @@ function MobileContentFull({
|
|
|
44988
45844
|
] }),
|
|
44989
45845
|
fitChartNode,
|
|
44990
45846
|
/* @__PURE__ */ jsx$1("div", { css: css2.colorSelectorContainer, children: /* @__PURE__ */ jsx$1(ColorSelector, { availableColorLabels, selectedColorLabel, onChangeColor }) }),
|
|
44991
|
-
/* @__PURE__ */
|
|
44992
|
-
|
|
45847
|
+
/* @__PURE__ */ jsxs("div", { css: css2.buttonContainer, children: [
|
|
45848
|
+
/* @__PURE__ */ jsx$1(AddToCartButton, { onClick: onAddToCart }),
|
|
45849
|
+
/* @__PURE__ */ jsx$1(FittingRoomToggleButton, {})
|
|
45850
|
+
] }),
|
|
45851
|
+
/* @__PURE__ */ jsx$1("div", { css: css2.productDetailsContainer, children: /* @__PURE__ */ jsx$1(LinkT, { variant: "base", css: css2.productDetailsText, t: "quick-view.hide_product_details", onClick: handleProductDetailsClick }) }),
|
|
44993
45852
|
/* @__PURE__ */ jsx$1("div", { css: css2.priceContainer, children: /* @__PURE__ */ jsx$1(Text, { variant: "base", css: css2.priceText, children: selectedColorSizeRecord.priceFormatted }) }),
|
|
44994
45853
|
/* @__PURE__ */ jsx$1("div", { css: css2.productDescriptionContainer, children: /* @__PURE__ */ jsx$1(ProductDescriptionText, { loadedProductData }) }),
|
|
44995
45854
|
/* @__PURE__ */ jsx$1("div", { css: css2.footerContainer, children: /* @__PURE__ */ jsx$1(Footer, { onSignOutClick: onSignOut }) })
|
|
@@ -45105,7 +45964,7 @@ function DesktopLayout({
|
|
|
45105
45964
|
return /* @__PURE__ */ jsxs("div", { css: css2.mainContainer, children: [
|
|
45106
45965
|
/* @__PURE__ */ jsx$1(Avatar, { frameUrls, setModalStyle }),
|
|
45107
45966
|
/* @__PURE__ */ jsxs("div", { css: css2.rightContainer, children: [
|
|
45108
|
-
/* @__PURE__ */ jsx$1(ModalTitlebar, { title: t("
|
|
45967
|
+
/* @__PURE__ */ jsx$1(ModalTitlebar, { title: t("quick-view.title"), onCloseClick: onClose }),
|
|
45109
45968
|
/* @__PURE__ */ jsxs("div", { css: css2.contentContainer, children: [
|
|
45110
45969
|
/* @__PURE__ */ jsx$1("div", { css: css2.productNameContainer, children: /* @__PURE__ */ jsx$1(Text, { variant: "brand", css: css2.productNameText, children: loadedProductData.productName }) }),
|
|
45111
45970
|
/* @__PURE__ */ jsx$1("div", { css: css2.priceContainer, children: /* @__PURE__ */ jsx$1(Text, { variant: "base", css: css2.priceText, children: selectedColorSizeRecord.priceFormatted }) }),
|
|
@@ -45121,7 +45980,10 @@ function DesktopLayout({
|
|
|
45121
45980
|
/* @__PURE__ */ jsx$1("div", { css: css2.itemFitDetailsContainer, children: /* @__PURE__ */ jsx$1(ItemFitDetails, { loadedProductData, selectedSizeLabel }) })
|
|
45122
45981
|
] }),
|
|
45123
45982
|
fitChartNode,
|
|
45124
|
-
/* @__PURE__ */
|
|
45983
|
+
/* @__PURE__ */ jsxs("div", { css: css2.buttonContainer, children: [
|
|
45984
|
+
/* @__PURE__ */ jsx$1(AddToCartButton, { onClick: onAddToCart }),
|
|
45985
|
+
/* @__PURE__ */ jsx$1(FittingRoomToggleButton, {})
|
|
45986
|
+
] }),
|
|
45125
45987
|
/* @__PURE__ */ jsx$1("div", { css: css2.descriptionContainer, children: /* @__PURE__ */ jsx$1(ProductDescriptionText, { loadedProductData }) })
|
|
45126
45988
|
] }),
|
|
45127
45989
|
/* @__PURE__ */ jsx$1(Footer, { onSignOutClick: onSignOut })
|
|
@@ -45141,10 +46003,37 @@ function Avatar({
|
|
|
45141
46003
|
bottomContainerStyle: {}
|
|
45142
46004
|
});
|
|
45143
46005
|
const [selectedFrameIndex, setSelectedFrameIndex] = reactExports.useState(null);
|
|
46006
|
+
const [zoomOpen, setZoomOpen] = reactExports.useState(false);
|
|
45144
46007
|
const css2 = useCss((theme) => ({
|
|
45145
46008
|
topContainer: {
|
|
45146
46009
|
flex: "none",
|
|
45147
|
-
height: "100%"
|
|
46010
|
+
height: "100%",
|
|
46011
|
+
// Positioning context for the zoom pill overlaid on the avatar.
|
|
46012
|
+
position: "relative"
|
|
46013
|
+
},
|
|
46014
|
+
zoomPill: {
|
|
46015
|
+
position: "absolute",
|
|
46016
|
+
// Bottom-right of the avatar image, clear of the slider gutter below it.
|
|
46017
|
+
bottom: `${AVATAR_GUTTER_HEIGHT_PX + 16}px`,
|
|
46018
|
+
right: "16px",
|
|
46019
|
+
display: "inline-flex",
|
|
46020
|
+
alignItems: "center",
|
|
46021
|
+
gap: "8px",
|
|
46022
|
+
padding: "8px 16px",
|
|
46023
|
+
borderRadius: "24px",
|
|
46024
|
+
backgroundColor: "rgba(255, 255, 255, 0.95)",
|
|
46025
|
+
border: `1px solid ${theme.color_fg_text}`,
|
|
46026
|
+
fontSize: "12px",
|
|
46027
|
+
fontWeight: "500",
|
|
46028
|
+
letterSpacing: "0.5px",
|
|
46029
|
+
textTransform: "uppercase",
|
|
46030
|
+
cursor: "pointer",
|
|
46031
|
+
zIndex: 2
|
|
46032
|
+
},
|
|
46033
|
+
zoomPillIcon: {
|
|
46034
|
+
width: "14px",
|
|
46035
|
+
height: "14px",
|
|
46036
|
+
flex: "none"
|
|
45148
46037
|
},
|
|
45149
46038
|
bottomContainer: {
|
|
45150
46039
|
position: "absolute",
|
|
@@ -45258,10 +46147,15 @@ function Avatar({
|
|
|
45258
46147
|
}, [isMobileLayout]);
|
|
45259
46148
|
const isReady = !!frameUrls && selectedFrameIndex != null;
|
|
45260
46149
|
return /* @__PURE__ */ jsxs("div", { css: css2.topContainer, style: layoutData.topContainerStyle, children: [
|
|
45261
|
-
/* @__PURE__ */ jsx$1(AvatarFrameViewer, { frameUrls, selectedFrameIndex, setSelectedFrameIndex, imageContainerStyle: layoutData.imageContainerStyle, imageStyle: layoutData.imageStyle, loadingT: "
|
|
45262
|
-
isReady ? /* @__PURE__ */
|
|
46150
|
+
/* @__PURE__ */ jsx$1(AvatarFrameViewer, { frameUrls, selectedFrameIndex, setSelectedFrameIndex, imageContainerStyle: layoutData.imageContainerStyle, imageStyle: layoutData.imageStyle, loadingT: "quick-view.avatar_loading" }),
|
|
46151
|
+
isReady && !isMobileLayout ? /* @__PURE__ */ jsxs(Button, { variant: "base", css: css2.zoomPill, onClick: () => setZoomOpen(true), children: [
|
|
46152
|
+
/* @__PURE__ */ jsx$1(SvgIconZoom, { css: css2.zoomPillIcon }),
|
|
46153
|
+
/* @__PURE__ */ jsx$1(TextT, { variant: "base", t: "quick-view.zoom_in" })
|
|
46154
|
+
] }) : null,
|
|
46155
|
+
zoomOpen && frameUrls && frameUrls.length > 0 ? /* @__PURE__ */ jsx$1(ZoomModal, { frameUrls, selectedFrameIndex, setSelectedFrameIndex, onClose: () => setZoomOpen(false) }) : null,
|
|
46156
|
+
frameUrls && selectedFrameIndex != null ? /* @__PURE__ */ jsx$1("div", { css: css2.bottomContainer, style: layoutData.bottomContainerStyle, children: isMobileLayout ? /* @__PURE__ */ jsx$1(Fragment, { children: " " }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
45263
46157
|
/* @__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 }),
|
|
45264
|
-
/* @__PURE__ */ jsx$1(TextT, { variant: "base", t: "
|
|
46158
|
+
/* @__PURE__ */ jsx$1(TextT, { variant: "base", t: "quick-view.slide_to_rotate", css: css2.sliderText })
|
|
45265
46159
|
] }) }) : null
|
|
45266
46160
|
] });
|
|
45267
46161
|
}
|
|
@@ -45291,35 +46185,6 @@ function ProductSummaryRow({
|
|
|
45291
46185
|
/* @__PURE__ */ jsx$1("div", { css: css2.nameContainer, children: /* @__PURE__ */ jsx$1(Text, { variant: "brand", css: css2.nameText, children: loadedProductData.productName }) })
|
|
45292
46186
|
] });
|
|
45293
46187
|
}
|
|
45294
|
-
function ColorSelector({
|
|
45295
|
-
availableColorLabels,
|
|
45296
|
-
selectedColorLabel,
|
|
45297
|
-
onChangeColor
|
|
45298
|
-
}) {
|
|
45299
|
-
const css2 = useCss((theme) => ({
|
|
45300
|
-
colorContainer: {},
|
|
45301
|
-
colorLabelText: {
|
|
45302
|
-
fontSize: "12px"
|
|
45303
|
-
},
|
|
45304
|
-
colorSelect: {
|
|
45305
|
-
border: "none",
|
|
45306
|
-
color: theme.color_fg_text,
|
|
45307
|
-
fontFamily: theme.font_family,
|
|
45308
|
-
fontSize: "12px"
|
|
45309
|
-
}
|
|
45310
|
-
}));
|
|
45311
|
-
const handleColorSelectChange = reactExports.useCallback((e) => {
|
|
45312
|
-
const newColorLabel = e.target.value || null;
|
|
45313
|
-
onChangeColor(newColorLabel);
|
|
45314
|
-
}, []);
|
|
45315
|
-
if (availableColorLabels.length < 2) {
|
|
45316
|
-
return null;
|
|
45317
|
-
}
|
|
45318
|
-
return /* @__PURE__ */ jsx$1("div", { css: css2.colorContainer, children: /* @__PURE__ */ jsxs("label", { children: [
|
|
45319
|
-
/* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.colorLabelText, t: "vto-single.color_label" }),
|
|
45320
|
-
/* @__PURE__ */ jsx$1("select", { value: selectedColorLabel ?? "", onChange: handleColorSelectChange, css: css2.colorSelect, children: availableColorLabels.map((colorLabel) => /* @__PURE__ */ jsx$1("option", { value: colorLabel, children: colorLabel }, colorLabel)) })
|
|
45321
|
-
] }) });
|
|
45322
|
-
}
|
|
45323
46188
|
function RecommendedSizeText({
|
|
45324
46189
|
loadedProductData,
|
|
45325
46190
|
textCss
|
|
@@ -45363,11 +46228,11 @@ function Footer({
|
|
|
45363
46228
|
}
|
|
45364
46229
|
}));
|
|
45365
46230
|
return /* @__PURE__ */ jsxs("div", { css: css2.container, children: [
|
|
45366
|
-
/* @__PURE__ */ jsx$1(LinkT, { variant: "underline", css: css2.signOutLink, onClick: onSignOutClick, t: "
|
|
46231
|
+
/* @__PURE__ */ jsx$1(LinkT, { variant: "underline", css: css2.signOutLink, onClick: onSignOutClick, t: "quick-view.sign_out" }),
|
|
45367
46232
|
/* @__PURE__ */ jsx$1(SvgTfrName, { css: css2.tfrIcon })
|
|
45368
46233
|
] });
|
|
45369
46234
|
}
|
|
45370
|
-
const logger$
|
|
46235
|
+
const logger$4 = getLogger("widgets/add-to-fitting-room-compact");
|
|
45371
46236
|
function AddToFittingRoomCompactWidget({
|
|
45372
46237
|
attributes
|
|
45373
46238
|
}) {
|
|
@@ -45413,7 +46278,7 @@ function AddToFittingRoomCompactWidget({
|
|
|
45413
46278
|
}
|
|
45414
46279
|
const handleClick = () => {
|
|
45415
46280
|
toggleFittingRoomItem(productId, productHandle, isPdp).catch((error) => {
|
|
45416
|
-
logger$
|
|
46281
|
+
logger$4.logError("toggleFittingRoomItem failed", {
|
|
45417
46282
|
error
|
|
45418
46283
|
});
|
|
45419
46284
|
});
|
|
@@ -45421,64 +46286,8 @@ function AddToFittingRoomCompactWidget({
|
|
|
45421
46286
|
const ariaLabel = t(isInFittingRoom ? "added_to_fitting_room" : "add_to_fitting_room");
|
|
45422
46287
|
return /* @__PURE__ */ jsx$1("button", { type: "button", onClick: handleClick, css: [css2.button, isInFittingRoom && css2.buttonAdded, "", ""], "aria-label": ariaLabel, "aria-pressed": isInFittingRoom, children: /* @__PURE__ */ jsx$1(SvgFittingRoomIcon, { css: [css2.icon, isInFittingRoom && css2.iconAdded, "", ""] }) });
|
|
45423
46288
|
}
|
|
45424
|
-
const logger$4 = getLogger("widgets/add-to-fitting-room");
|
|
45425
|
-
function AddToFittingRoomWidget({
|
|
45426
|
-
attributes
|
|
45427
|
-
}) {
|
|
45428
|
-
const {
|
|
45429
|
-
currentProduct
|
|
45430
|
-
} = getStaticData();
|
|
45431
|
-
const attrProductId = attributes["product-id"];
|
|
45432
|
-
const attrProductHandle = attributes["product-handle"];
|
|
45433
|
-
const productId = attrProductId || currentProduct?.externalId || null;
|
|
45434
|
-
const isPdp = productId != null && productId === currentProduct?.externalId;
|
|
45435
|
-
const productHandle = attrProductHandle || (isPdp ? currentProduct?.handle ?? null : null);
|
|
45436
|
-
const isInFittingRoom = useMainStore((state) => productId == null ? false : state.fittingRoom.some((item) => item.externalId === productId));
|
|
45437
|
-
const css2 = useCss((theme) => ({
|
|
45438
|
-
button: {
|
|
45439
|
-
marginTop: "10px",
|
|
45440
|
-
marginBottom: "10px",
|
|
45441
|
-
width: "100%",
|
|
45442
|
-
maxWidth: "440px",
|
|
45443
|
-
display: "flex",
|
|
45444
|
-
alignItems: "center",
|
|
45445
|
-
justifyContent: "center",
|
|
45446
|
-
gap: "10px",
|
|
45447
|
-
padding: "13px",
|
|
45448
|
-
backgroundColor: "white",
|
|
45449
|
-
borderWidth: "1px",
|
|
45450
|
-
borderColor: theme.color_fg_text,
|
|
45451
|
-
borderStyle: "solid",
|
|
45452
|
-
borderRadius: "30px",
|
|
45453
|
-
cursor: "pointer"
|
|
45454
|
-
},
|
|
45455
|
-
icon: {
|
|
45456
|
-
color: theme.color_fg_text,
|
|
45457
|
-
width: "20px",
|
|
45458
|
-
height: "20px"
|
|
45459
|
-
},
|
|
45460
|
-
text: {
|
|
45461
|
-
fontSize: "14px",
|
|
45462
|
-
textTransform: "uppercase"
|
|
45463
|
-
}
|
|
45464
|
-
}));
|
|
45465
|
-
if (productId == null) {
|
|
45466
|
-
return null;
|
|
45467
|
-
}
|
|
45468
|
-
const handleClick = () => {
|
|
45469
|
-
toggleFittingRoomItem(productId, productHandle, isPdp).catch((error) => {
|
|
45470
|
-
logger$4.logError("toggleFittingRoomItem failed", {
|
|
45471
|
-
error
|
|
45472
|
-
});
|
|
45473
|
-
});
|
|
45474
|
-
};
|
|
45475
|
-
return /* @__PURE__ */ jsxs("button", { type: "button", onClick: handleClick, css: css2.button, children: [
|
|
45476
|
-
/* @__PURE__ */ jsx$1(SvgFittingRoomIcon, { css: css2.icon }),
|
|
45477
|
-
/* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.text, t: isInFittingRoom ? "added_to_fitting_room" : "add_to_fitting_room" })
|
|
45478
|
-
] });
|
|
45479
|
-
}
|
|
45480
46289
|
const logger$3 = getLogger("widgets/fitting-room-icon");
|
|
45481
|
-
function FittingRoomIconWidget(
|
|
46290
|
+
function FittingRoomIconWidget(_props) {
|
|
45482
46291
|
const {
|
|
45483
46292
|
t
|
|
45484
46293
|
} = useTranslation();
|
|
@@ -45540,7 +46349,7 @@ function FittingRoomIconWidget({}) {
|
|
|
45540
46349
|
] });
|
|
45541
46350
|
}
|
|
45542
46351
|
const logger$2 = getLogger("widgets/fitting-room");
|
|
45543
|
-
function FittingRoomWidget(
|
|
46352
|
+
function FittingRoomWidget(_props) {
|
|
45544
46353
|
const count = useMainStore((state) => state.fittingRoom.length);
|
|
45545
46354
|
const openOverlay = useMainStore((state) => state.openOverlay);
|
|
45546
46355
|
const css2 = useCss((theme) => ({
|
|
@@ -45597,11 +46406,11 @@ function FittingRoomWidget({}) {
|
|
|
45597
46406
|
] });
|
|
45598
46407
|
}
|
|
45599
46408
|
const logger$1 = getLogger("size-rec");
|
|
45600
|
-
function SizeRecWidget(
|
|
46409
|
+
function SizeRecWidget(_props) {
|
|
45601
46410
|
const openOverlay = useMainStore((state) => state.openOverlay);
|
|
45602
46411
|
const openedOverlays = useMainStore((state) => state.openedOverlays);
|
|
45603
46412
|
const storeProductData = useMainStore((state) => state.productData);
|
|
45604
|
-
const
|
|
46413
|
+
const hasOpenedQuickViewOverlay = openedOverlays.includes(OverlayName.QUICK_VIEW);
|
|
45605
46414
|
const sizeRecommendationRecord = reactExports.useMemo(() => {
|
|
45606
46415
|
const {
|
|
45607
46416
|
currentProduct
|
|
@@ -45625,9 +46434,9 @@ function SizeRecWidget({}) {
|
|
|
45625
46434
|
return productData.sizeFitRecommendation || null;
|
|
45626
46435
|
}, [storeProductData]);
|
|
45627
46436
|
const handleLinkClick = reactExports.useCallback(() => {
|
|
45628
|
-
openOverlay(OverlayName.
|
|
46437
|
+
openOverlay(OverlayName.QUICK_VIEW);
|
|
45629
46438
|
}, [openOverlay]);
|
|
45630
|
-
if (!sizeRecommendationRecord || !
|
|
46439
|
+
if (!sizeRecommendationRecord || !hasOpenedQuickViewOverlay) {
|
|
45631
46440
|
return null;
|
|
45632
46441
|
}
|
|
45633
46442
|
const sizeLabel = getSizeLabelFromSize(sizeRecommendationRecord.recommended_size);
|
|
@@ -45639,8 +46448,11 @@ function SizeRecWidget({}) {
|
|
|
45639
46448
|
} });
|
|
45640
46449
|
}
|
|
45641
46450
|
const logger = getLogger("widgets/vto-button");
|
|
45642
|
-
function VtoButtonWidget(
|
|
46451
|
+
function VtoButtonWidget(_props) {
|
|
45643
46452
|
const openOverlay = useMainStore((state) => state.openOverlay);
|
|
46453
|
+
const currentProduct = getStaticData().currentProduct ?? null;
|
|
46454
|
+
const currentProductId = currentProduct?.externalId ?? null;
|
|
46455
|
+
const hasOtherFittingRoomItems = useMainStore((state) => state.fittingRoom.some((item) => item.externalId !== currentProductId));
|
|
45644
46456
|
const css2 = useCss((theme) => ({
|
|
45645
46457
|
button: {
|
|
45646
46458
|
marginTop: "10px",
|
|
@@ -45667,13 +46479,29 @@ function VtoButtonWidget({}) {
|
|
|
45667
46479
|
textTransform: "uppercase"
|
|
45668
46480
|
}
|
|
45669
46481
|
}));
|
|
45670
|
-
const
|
|
45671
|
-
|
|
45672
|
-
|
|
46482
|
+
const handleClick = () => {
|
|
46483
|
+
if (!hasOtherFittingRoomItems) {
|
|
46484
|
+
logger.logDebug("{{ts}} - Opening quick-view overlay");
|
|
46485
|
+
openOverlay(OverlayName.QUICK_VIEW);
|
|
46486
|
+
return;
|
|
46487
|
+
}
|
|
46488
|
+
logger.logDebug("{{ts}} - Opening fitting-room overlay");
|
|
46489
|
+
if (!currentProductId) {
|
|
46490
|
+
openOverlay(OverlayName.FITTING_ROOM);
|
|
46491
|
+
return;
|
|
46492
|
+
}
|
|
46493
|
+
ensureFittingRoomItem(currentProductId, currentProduct?.handle ?? null, true).catch((error) => {
|
|
46494
|
+
logger.logError("Failed to add current product to fitting room", {
|
|
46495
|
+
error
|
|
46496
|
+
});
|
|
46497
|
+
});
|
|
46498
|
+
openOverlay(OverlayName.FITTING_ROOM, {
|
|
46499
|
+
preselectExternalId: currentProductId
|
|
46500
|
+
});
|
|
45673
46501
|
};
|
|
45674
|
-
return /* @__PURE__ */ jsxs("button", { type: "button", onClick:
|
|
46502
|
+
return /* @__PURE__ */ jsxs("button", { type: "button", onClick: handleClick, css: css2.button, children: [
|
|
45675
46503
|
/* @__PURE__ */ jsx$1(SvgTfrIcon, { css: css2.icon }),
|
|
45676
|
-
/* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.text, t: "try_it_on" })
|
|
46504
|
+
/* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.text, t: hasOtherFittingRoomItems ? "try_it_on" : "quick-view.title" })
|
|
45677
46505
|
] });
|
|
45678
46506
|
}
|
|
45679
46507
|
var DeviceLayout = /* @__PURE__ */ ((DeviceLayout2) => {
|
|
@@ -45738,10 +46566,6 @@ function _init$1() {
|
|
|
45738
46566
|
});
|
|
45739
46567
|
}
|
|
45740
46568
|
const WIDGETS = {
|
|
45741
|
-
[
|
|
45742
|
-
"add-to-fitting-room"
|
|
45743
|
-
/* ADD_TO_FITTING_ROOM */
|
|
45744
|
-
]: AddToFittingRoomWidget,
|
|
45745
46569
|
[
|
|
45746
46570
|
"add-to-fitting-room-compact"
|
|
45747
46571
|
/* ADD_TO_FITTING_ROOM_COMPACT */
|
|
@@ -45769,7 +46593,7 @@ var OverlayName = /* @__PURE__ */ ((OverlayName2) => {
|
|
|
45769
46593
|
OverlayName2["GET_APP"] = "get-app";
|
|
45770
46594
|
OverlayName2["LANDING"] = "landing";
|
|
45771
46595
|
OverlayName2["SIGN_IN"] = "sign-in";
|
|
45772
|
-
OverlayName2["
|
|
46596
|
+
OverlayName2["QUICK_VIEW"] = "quick-view";
|
|
45773
46597
|
return OverlayName2;
|
|
45774
46598
|
})(OverlayName || {});
|
|
45775
46599
|
const OVERLAYS = {
|
|
@@ -45794,9 +46618,9 @@ const OVERLAYS = {
|
|
|
45794
46618
|
/* SIGN_IN */
|
|
45795
46619
|
]: SignInOverlay,
|
|
45796
46620
|
[
|
|
45797
|
-
"
|
|
45798
|
-
/*
|
|
45799
|
-
]:
|
|
46621
|
+
"quick-view"
|
|
46622
|
+
/* QUICK_VIEW */
|
|
46623
|
+
]: QuickViewOverlay
|
|
45800
46624
|
};
|
|
45801
46625
|
let staticData = null;
|
|
45802
46626
|
function _init(initStaticData) {
|
|
@@ -45941,9 +46765,9 @@ const SHARED_CONFIG = {
|
|
|
45941
46765
|
appGooglePlayUrl: "https://play.google.com/store/apps/details?id=com.thefittingroom.marketplace"
|
|
45942
46766
|
},
|
|
45943
46767
|
build: {
|
|
45944
|
-
version: `${"5.0.
|
|
45945
|
-
commitHash: `${"
|
|
45946
|
-
date: `${"2026-05-
|
|
46768
|
+
version: `${"5.0.25"}`,
|
|
46769
|
+
commitHash: `${"101d833"}`,
|
|
46770
|
+
date: `${"2026-05-22T00:57:54.147Z"}`
|
|
45947
46771
|
}
|
|
45948
46772
|
};
|
|
45949
46773
|
const CONFIGS = {
|
|
@@ -46067,7 +46891,7 @@ const CONFIGS = {
|
|
|
46067
46891
|
const getConfig = (envName) => {
|
|
46068
46892
|
return CONFIGS[envName];
|
|
46069
46893
|
};
|
|
46070
|
-
const css = "
|
|
46894
|
+
const css = "\n@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');\n/* Inter — the SDK's own typeface, matching the Figma designs. Loaded here so\n overlay text renders in Inter regardless of the host page's fonts. */\nbody.tfr-modal-open {\n overflow: hidden;\n position: fixed;\n width: 100%;\n}\n";
|
|
46071
46895
|
class TfrWidgetElement extends HTMLElement {
|
|
46072
46896
|
connectedCallback() {
|
|
46073
46897
|
const attributes = this.getAttributeNames().reduce((attrs, name2) => {
|
|
@@ -46090,7 +46914,8 @@ async function init(initParams) {
|
|
|
46090
46914
|
debug,
|
|
46091
46915
|
productLookup,
|
|
46092
46916
|
getOverlayTopOffset,
|
|
46093
|
-
addToCart
|
|
46917
|
+
addToCart,
|
|
46918
|
+
testHooks
|
|
46094
46919
|
} = initParams;
|
|
46095
46920
|
if (!brandId || typeof brandId !== "number" || isNaN(brandId) || brandId <= 0) {
|
|
46096
46921
|
throw new Error(`Invalid brandId "${brandId}"`);
|
|
@@ -46116,7 +46941,8 @@ async function init(initParams) {
|
|
|
46116
46941
|
config,
|
|
46117
46942
|
productLookup: productLookup ?? null,
|
|
46118
46943
|
getOverlayTopOffset: getOverlayTopOffset ?? null,
|
|
46119
|
-
addToCart: addToCart ?? null
|
|
46944
|
+
addToCart: addToCart ?? null,
|
|
46945
|
+
testHooks
|
|
46120
46946
|
});
|
|
46121
46947
|
_init$7();
|
|
46122
46948
|
_init$5();
|