@thefittingroom/shop-ui 5.0.24 → 5.0.26
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 +385 -90
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -18,13 +18,30 @@ npm ci
|
|
|
18
18
|
| Script | What it does |
|
|
19
19
|
|---|---|
|
|
20
20
|
| `npm run build` | Clean + production build into `dist/` |
|
|
21
|
-
| `npm run check` | Type-check (`tsc --noEmit`) |
|
|
21
|
+
| `npm run check` | Type-check (`tsc --noEmit`) + ESLint + Prettier |
|
|
22
22
|
| `npm run watch` | Vite build in watch mode |
|
|
23
23
|
| `npm run serve` | Static-serve the repo on `:5173` with CORS |
|
|
24
24
|
| `npm run watch-serve` | Both `watch` and `serve` together; Ctrl+C stops both |
|
|
25
|
+
| `npm run test` / `npm run test:e2e` | Playwright e2e suite (Chromium) against the built bundle |
|
|
26
|
+
| `npm run test:e2e:ui` | Playwright UI runner for local debugging |
|
|
25
27
|
| `npm run gen-types` | Regenerate `src/api/gen/*.ts` from `tfr-backend` Go types |
|
|
26
28
|
| `npm run promote-latest [version]` | Move npm dist-tag `latest` onto a published version (defaults to current `package.json` version) |
|
|
27
29
|
|
|
30
|
+
## Running e2e tests
|
|
31
|
+
|
|
32
|
+
First-time setup: `npm install && npx playwright install chromium` (the
|
|
33
|
+
second command downloads the Chromium binary once; cached afterwards). Then
|
|
34
|
+
`npm run test:e2e` runs the suite. Specs live under `tests/e2e/`; they boot
|
|
35
|
+
the built bundle in headless Chromium against a static fixture page
|
|
36
|
+
(`tests/e2e/fixtures/host.html`) that mimics the minimal contract the Shopify
|
|
37
|
+
theme provides. Firebase is mocked via `InitParams.testHooks`
|
|
38
|
+
(see `src/lib/firebase-mock.ts`); REST endpoints are mocked via
|
|
39
|
+
`page.route()`. See AGENTS.md for the architecture and constraints.
|
|
40
|
+
|
|
41
|
+
For an interactive debug loop, `npm run watch-serve &` in one terminal and
|
|
42
|
+
`npm run test:e2e:ui` in another — Playwright reuses the live-rebuilt
|
|
43
|
+
`:5173`.
|
|
44
|
+
|
|
28
45
|
## Release process
|
|
29
46
|
|
|
30
47
|
Releases are explicit, human-initiated steps via a single GitHub Actions
|
package/dist/index.js
CHANGED
|
@@ -14054,7 +14054,7 @@ function applyFrameBaseUrl(url, baseUrl2) {
|
|
|
14054
14054
|
return `${cleanBase}${url.startsWith("/") ? "" : "/"}${url}`;
|
|
14055
14055
|
}
|
|
14056
14056
|
const STORAGE_KEY = "tfr:fitting-room:v1";
|
|
14057
|
-
const logger$
|
|
14057
|
+
const logger$g = getLogger("fitting-room-storage");
|
|
14058
14058
|
function readAll() {
|
|
14059
14059
|
try {
|
|
14060
14060
|
const raw = window.localStorage.getItem(STORAGE_KEY);
|
|
@@ -14067,7 +14067,7 @@ function readAll() {
|
|
|
14067
14067
|
}
|
|
14068
14068
|
return parsed;
|
|
14069
14069
|
} catch (error) {
|
|
14070
|
-
logger$
|
|
14070
|
+
logger$g.logWarn("Failed to read fitting room from localStorage", {
|
|
14071
14071
|
error
|
|
14072
14072
|
});
|
|
14073
14073
|
return {};
|
|
@@ -14077,7 +14077,7 @@ function writeAll(all) {
|
|
|
14077
14077
|
try {
|
|
14078
14078
|
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(all));
|
|
14079
14079
|
} catch (error) {
|
|
14080
|
-
logger$
|
|
14080
|
+
logger$g.logWarn("Failed to write fitting room to localStorage", {
|
|
14081
14081
|
error
|
|
14082
14082
|
});
|
|
14083
14083
|
}
|
|
@@ -14103,7 +14103,7 @@ function _init$7() {
|
|
|
14103
14103
|
}
|
|
14104
14104
|
async function addFittingRoomItem(productId, handle, isPdp) {
|
|
14105
14105
|
const state = useMainStore.getState();
|
|
14106
|
-
logger$
|
|
14106
|
+
logger$g.logDebug("{{ts}} - Adding to fitting room", {
|
|
14107
14107
|
productId,
|
|
14108
14108
|
handle,
|
|
14109
14109
|
isPdp
|
|
@@ -14125,7 +14125,7 @@ async function addFittingRoomItem(productId, handle, isPdp) {
|
|
|
14125
14125
|
size = selection.size || null;
|
|
14126
14126
|
color = selection.color;
|
|
14127
14127
|
} catch (error) {
|
|
14128
|
-
logger$
|
|
14128
|
+
logger$g.logWarn("Failed to read selected options from currentProduct", {
|
|
14129
14129
|
error
|
|
14130
14130
|
});
|
|
14131
14131
|
}
|
|
@@ -14157,7 +14157,7 @@ async function toggleFittingRoomItem(productId, handle, isPdp) {
|
|
|
14157
14157
|
const state = useMainStore.getState();
|
|
14158
14158
|
const isInFittingRoom = state.fittingRoom.some((item) => item.externalId === productId);
|
|
14159
14159
|
if (isInFittingRoom) {
|
|
14160
|
-
logger$
|
|
14160
|
+
logger$g.logDebug("{{ts}} - Removing from fitting room", {
|
|
14161
14161
|
productId
|
|
14162
14162
|
});
|
|
14163
14163
|
state.removeFromFittingRoom(productId);
|
|
@@ -23058,7 +23058,7 @@ function isVersionServiceProvider(provider) {
|
|
|
23058
23058
|
}
|
|
23059
23059
|
const name$q = "@firebase/app";
|
|
23060
23060
|
const version$1$1 = "0.14.5";
|
|
23061
|
-
const logger$
|
|
23061
|
+
const logger$f = new Logger3("@firebase/app");
|
|
23062
23062
|
const name$p = "@firebase/app-compat";
|
|
23063
23063
|
const name$o = "@firebase/analytics-compat";
|
|
23064
23064
|
const name$n = "@firebase/analytics";
|
|
@@ -23125,13 +23125,13 @@ function _addComponent(app, component) {
|
|
|
23125
23125
|
try {
|
|
23126
23126
|
app.container.addComponent(component);
|
|
23127
23127
|
} catch (e) {
|
|
23128
|
-
logger$
|
|
23128
|
+
logger$f.debug(`Component ${component.name} failed to register with FirebaseApp ${app.name}`, e);
|
|
23129
23129
|
}
|
|
23130
23130
|
}
|
|
23131
23131
|
function _registerComponent(component) {
|
|
23132
23132
|
const componentName = component.name;
|
|
23133
23133
|
if (_components.has(componentName)) {
|
|
23134
|
-
logger$
|
|
23134
|
+
logger$f.debug(`There were multiple attempts to register component ${componentName}.`);
|
|
23135
23135
|
return false;
|
|
23136
23136
|
}
|
|
23137
23137
|
_components.set(componentName, component);
|
|
@@ -23340,7 +23340,7 @@ function registerVersion(libraryKeyOrName, version2, variant) {
|
|
|
23340
23340
|
if (versionMismatch) {
|
|
23341
23341
|
warning.push(`version name "${version2}" contains illegal characters (whitespace or "/")`);
|
|
23342
23342
|
}
|
|
23343
|
-
logger$
|
|
23343
|
+
logger$f.warn(warning.join(" "));
|
|
23344
23344
|
return;
|
|
23345
23345
|
}
|
|
23346
23346
|
_registerComponent(new Component(
|
|
@@ -23384,12 +23384,12 @@ async function readHeartbeatsFromIndexedDB(app) {
|
|
|
23384
23384
|
return result;
|
|
23385
23385
|
} catch (e) {
|
|
23386
23386
|
if (e instanceof FirebaseError) {
|
|
23387
|
-
logger$
|
|
23387
|
+
logger$f.warn(e.message);
|
|
23388
23388
|
} else {
|
|
23389
23389
|
const idbGetError = ERROR_FACTORY.create("idb-get", {
|
|
23390
23390
|
originalErrorMessage: e?.message
|
|
23391
23391
|
});
|
|
23392
|
-
logger$
|
|
23392
|
+
logger$f.warn(idbGetError.message);
|
|
23393
23393
|
}
|
|
23394
23394
|
}
|
|
23395
23395
|
}
|
|
@@ -23402,12 +23402,12 @@ async function writeHeartbeatsToIndexedDB(app, heartbeatObject) {
|
|
|
23402
23402
|
await tx.done;
|
|
23403
23403
|
} catch (e) {
|
|
23404
23404
|
if (e instanceof FirebaseError) {
|
|
23405
|
-
logger$
|
|
23405
|
+
logger$f.warn(e.message);
|
|
23406
23406
|
} else {
|
|
23407
23407
|
const idbGetError = ERROR_FACTORY.create("idb-set", {
|
|
23408
23408
|
originalErrorMessage: e?.message
|
|
23409
23409
|
});
|
|
23410
|
-
logger$
|
|
23410
|
+
logger$f.warn(idbGetError.message);
|
|
23411
23411
|
}
|
|
23412
23412
|
}
|
|
23413
23413
|
}
|
|
@@ -23456,7 +23456,7 @@ class HeartbeatServiceImpl {
|
|
|
23456
23456
|
}
|
|
23457
23457
|
return this._storage.overwrite(this._heartbeatsCache);
|
|
23458
23458
|
} catch (e) {
|
|
23459
|
-
logger$
|
|
23459
|
+
logger$f.warn(e);
|
|
23460
23460
|
}
|
|
23461
23461
|
}
|
|
23462
23462
|
/**
|
|
@@ -23487,7 +23487,7 @@ class HeartbeatServiceImpl {
|
|
|
23487
23487
|
}
|
|
23488
23488
|
return headerString;
|
|
23489
23489
|
} catch (e) {
|
|
23490
|
-
logger$
|
|
23490
|
+
logger$f.warn(e);
|
|
23491
23491
|
return "";
|
|
23492
23492
|
}
|
|
23493
23493
|
}
|
|
@@ -41051,6 +41051,143 @@ registerAuth(
|
|
|
41051
41051
|
"Browser"
|
|
41052
41052
|
/* ClientPlatform.BROWSER */
|
|
41053
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
|
+
}
|
|
41054
41191
|
const firebaseDateToDayjs = (date) => {
|
|
41055
41192
|
return dayjs(date.seconds * 1e3);
|
|
41056
41193
|
};
|
|
@@ -41238,8 +41375,24 @@ function getAuthManager() {
|
|
|
41238
41375
|
async function _init$4() {
|
|
41239
41376
|
const {
|
|
41240
41377
|
brandId,
|
|
41241
|
-
config
|
|
41378
|
+
config,
|
|
41379
|
+
testHooks
|
|
41242
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
|
+
}
|
|
41243
41396
|
{
|
|
41244
41397
|
const {
|
|
41245
41398
|
firebase: sdkFirebaseConfig
|
|
@@ -42644,6 +42797,35 @@ function AddToCartButton({
|
|
|
42644
42797
|
}) {
|
|
42645
42798
|
return /* @__PURE__ */ jsx$1(ButtonT, { variant: "brand", t: "quick-view.add_to_cart", onClick });
|
|
42646
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
|
+
] }) });
|
|
42828
|
+
}
|
|
42647
42829
|
function ItemFitDetails({
|
|
42648
42830
|
loadedProductData,
|
|
42649
42831
|
selectedSizeLabel
|
|
@@ -42693,7 +42875,7 @@ function ItemFitDetails({
|
|
|
42693
42875
|
] }) : fitLabel })
|
|
42694
42876
|
] }, index);
|
|
42695
42877
|
});
|
|
42696
|
-
}, [loadedProductData, selectedSizeLabel]);
|
|
42878
|
+
}, [loadedProductData, selectedSizeLabel, t, css2.detailCell, css2.firstLine, css2.line]);
|
|
42697
42879
|
return /* @__PURE__ */ jsx$1("div", { css: css2.container, children: fitLineNodeList });
|
|
42698
42880
|
}
|
|
42699
42881
|
function ItemFitText({
|
|
@@ -42740,7 +42922,7 @@ function SizeSelector({
|
|
|
42740
42922
|
}
|
|
42741
42923
|
onChangeSize(sizeRecord.sizeLabel);
|
|
42742
42924
|
}, children: sizeRecord.sizeLabel }, sizeRecord.sizeLabel);
|
|
42743
|
-
}), [loadedProductData.sizes, selectedSizeLabel, onChangeSize]);
|
|
42925
|
+
}), [loadedProductData.sizes, selectedSizeLabel, onChangeSize, css2.button, css2.selectedButton]);
|
|
42744
42926
|
return /* @__PURE__ */ jsx$1("div", { css: css2.container, children: sizeSelectorNodeList });
|
|
42745
42927
|
}
|
|
42746
42928
|
function DetailAccordionItem({
|
|
@@ -42754,6 +42936,7 @@ function DetailAccordionItem({
|
|
|
42754
42936
|
onToggleOpen,
|
|
42755
42937
|
onChangeDetailMode,
|
|
42756
42938
|
onChangeSize,
|
|
42939
|
+
onChangeColor,
|
|
42757
42940
|
onAddToCart,
|
|
42758
42941
|
onToggleUntuck
|
|
42759
42942
|
}) {
|
|
@@ -42789,13 +42972,23 @@ function DetailAccordionItem({
|
|
|
42789
42972
|
}
|
|
42790
42973
|
return null;
|
|
42791
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]);
|
|
42792
42985
|
const categoryLabel = item.styleCategory?.label_singular ?? item.styleCategory?.label ?? "";
|
|
42793
42986
|
const productName = item.merchantProduct?.productName ?? item.externalId;
|
|
42794
42987
|
const tuckable = !!item.styleCategory?.tuckable && canTuck;
|
|
42795
42988
|
if (platform === "desktop") {
|
|
42796
|
-
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 });
|
|
42797
42990
|
}
|
|
42798
|
-
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 });
|
|
42799
42992
|
}
|
|
42800
42993
|
function DesktopAccordionItem({
|
|
42801
42994
|
isOpen,
|
|
@@ -42803,8 +42996,11 @@ function DesktopAccordionItem({
|
|
|
42803
42996
|
productData,
|
|
42804
42997
|
currentPrice,
|
|
42805
42998
|
selectedSizeLabel,
|
|
42999
|
+
availableColorLabels,
|
|
43000
|
+
selectedColorLabel,
|
|
42806
43001
|
onToggleOpen,
|
|
42807
43002
|
onChangeSize,
|
|
43003
|
+
onChangeColor,
|
|
42808
43004
|
onAddToCart
|
|
42809
43005
|
}) {
|
|
42810
43006
|
const ACCORDION_SHADE = "#F4F4F4";
|
|
@@ -42823,8 +43019,10 @@ function DesktopAccordionItem({
|
|
|
42823
43019
|
backgroundColor: ACCORDION_SHADE
|
|
42824
43020
|
},
|
|
42825
43021
|
categoryLabel: {
|
|
42826
|
-
|
|
42827
|
-
|
|
43022
|
+
fontFamily: "'Times New Roman', serif",
|
|
43023
|
+
fontSize: "20px",
|
|
43024
|
+
fontWeight: "400",
|
|
43025
|
+
letterSpacing: "0.04em"
|
|
42828
43026
|
},
|
|
42829
43027
|
chevron: {
|
|
42830
43028
|
display: "inline-flex",
|
|
@@ -42845,40 +43043,72 @@ function DesktopAccordionItem({
|
|
|
42845
43043
|
},
|
|
42846
43044
|
productName: {
|
|
42847
43045
|
fontSize: "24px",
|
|
43046
|
+
fontWeight: "300",
|
|
42848
43047
|
lineHeight: "1.2"
|
|
42849
43048
|
},
|
|
42850
43049
|
price: {
|
|
42851
43050
|
fontSize: "15px"
|
|
42852
43051
|
},
|
|
43052
|
+
// Padding matches quick-view's sizeRecommendationFrame (32px / 56px) so
|
|
43053
|
+
// the "fit box" feels visually consistent between the two overlays.
|
|
43054
|
+
//
|
|
43055
|
+
// No flex `gap` — the three text lines (recommended size, fit text,
|
|
43056
|
+
// select-a-size prompt) sit tight against each other (matching
|
|
43057
|
+
// quick-view), with explicit marginTop on the size selector + fit
|
|
43058
|
+
// details below them to introduce the larger break.
|
|
42853
43059
|
sizeBox: {
|
|
42854
43060
|
width: "100%",
|
|
42855
43061
|
border: `1px solid ${theme.color_fg_text}`,
|
|
42856
|
-
padding: "
|
|
43062
|
+
padding: "32px 56px",
|
|
42857
43063
|
display: "flex",
|
|
42858
43064
|
flexDirection: "column",
|
|
42859
43065
|
alignItems: "center",
|
|
42860
|
-
gap: "12px",
|
|
42861
43066
|
marginTop: "8px",
|
|
42862
43067
|
textAlign: "center"
|
|
42863
43068
|
},
|
|
43069
|
+
colorSelectorContainer: {
|
|
43070
|
+
width: "100%",
|
|
43071
|
+
marginTop: "8px"
|
|
43072
|
+
},
|
|
43073
|
+
// 14px / line-height 1.5 on these three text lines matches quick-view's
|
|
43074
|
+
// fit-box. Quick-view's first line wraps an InfoIcon button alongside
|
|
43075
|
+
// the recommended-size text, which stretches that line vertically; the
|
|
43076
|
+
// simpler line-height bump here matches the *visual* line spacing
|
|
43077
|
+
// without dragging the icon in. The two lines below it inherit
|
|
43078
|
+
// line-height from their containers in quick-view, which the host page's
|
|
43079
|
+
// body styles tend to set looser than Inter's intrinsic `normal` (~1.21).
|
|
42864
43080
|
recommendedSize: {
|
|
42865
|
-
fontSize: "
|
|
42866
|
-
fontWeight: "600"
|
|
43081
|
+
fontSize: "14px",
|
|
43082
|
+
fontWeight: "600",
|
|
43083
|
+
lineHeight: 1.5
|
|
42867
43084
|
},
|
|
42868
43085
|
selectPrompt: {
|
|
42869
|
-
fontSize: "
|
|
43086
|
+
fontSize: "14px",
|
|
43087
|
+
fontWeight: "300",
|
|
43088
|
+
lineHeight: 1.5
|
|
42870
43089
|
},
|
|
42871
43090
|
fitText: {
|
|
42872
|
-
fontSize: "
|
|
43091
|
+
fontSize: "14px",
|
|
43092
|
+
fontWeight: "300",
|
|
43093
|
+
lineHeight: 1.5,
|
|
43094
|
+
// Tight 8px lift to the recommended-size line above; matches
|
|
43095
|
+
// quick-view's `itemFitContainer` marginTop.
|
|
43096
|
+
marginTop: "8px"
|
|
42873
43097
|
},
|
|
42874
43098
|
fitDetails: {
|
|
42875
|
-
width: "100%"
|
|
43099
|
+
width: "100%",
|
|
43100
|
+
// Cascades to the text inside <ItemFitDetails>; the size-selector
|
|
43101
|
+
// buttons and the bold recommended-size line above stay at their
|
|
43102
|
+
// own weights.
|
|
43103
|
+
fontWeight: 300,
|
|
43104
|
+
marginTop: "24px"
|
|
42876
43105
|
},
|
|
42877
43106
|
sizeRow: {
|
|
42878
43107
|
display: "flex",
|
|
42879
43108
|
gap: "8px",
|
|
42880
43109
|
alignItems: "center",
|
|
42881
|
-
justifyContent: "center"
|
|
43110
|
+
justifyContent: "center",
|
|
43111
|
+
marginTop: "24px"
|
|
42882
43112
|
},
|
|
42883
43113
|
cartContainer: {
|
|
42884
43114
|
width: "100%",
|
|
@@ -42899,6 +43129,7 @@ function DesktopAccordionItem({
|
|
|
42899
43129
|
!isOpen ? null : /* @__PURE__ */ jsx$1("div", { css: css2.body, children: productData ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
42900
43130
|
/* @__PURE__ */ jsx$1(Text, { variant: "brand", css: css2.productName, children: productData.productName }),
|
|
42901
43131
|
currentPrice ? /* @__PURE__ */ jsx$1(Text, { variant: "base", css: css2.price, children: currentPrice }) : null,
|
|
43132
|
+
/* @__PURE__ */ jsx$1("div", { css: css2.colorSelectorContainer, children: /* @__PURE__ */ jsx$1(ColorSelector, { availableColorLabels, selectedColorLabel, onChangeColor }) }),
|
|
42902
43133
|
/* @__PURE__ */ jsxs("div", { css: css2.sizeBox, children: [
|
|
42903
43134
|
/* @__PURE__ */ jsxs(Text, { variant: "base", css: css2.recommendedSize, children: [
|
|
42904
43135
|
"Recommended Size: ",
|
|
@@ -42922,6 +43153,8 @@ function MobileAccordionItem({
|
|
|
42922
43153
|
productName,
|
|
42923
43154
|
productData,
|
|
42924
43155
|
selectedSizeLabel,
|
|
43156
|
+
availableColorLabels,
|
|
43157
|
+
selectedColorLabel,
|
|
42925
43158
|
currentPrice,
|
|
42926
43159
|
detailMode,
|
|
42927
43160
|
isMobileQuickRow,
|
|
@@ -42930,6 +43163,7 @@ function MobileAccordionItem({
|
|
|
42930
43163
|
onToggleOpen,
|
|
42931
43164
|
onChangeDetailMode,
|
|
42932
43165
|
onChangeSize,
|
|
43166
|
+
onChangeColor,
|
|
42933
43167
|
onAddToCart,
|
|
42934
43168
|
onToggleUntuck
|
|
42935
43169
|
}) {
|
|
@@ -42962,12 +43196,16 @@ function MobileAccordionItem({
|
|
|
42962
43196
|
minWidth: 0
|
|
42963
43197
|
},
|
|
42964
43198
|
categoryLabel: {
|
|
42965
|
-
|
|
43199
|
+
fontFamily: "'Times New Roman', serif",
|
|
43200
|
+
fontSize: "16px",
|
|
42966
43201
|
fontWeight: "400",
|
|
43202
|
+
letterSpacing: "0.04em",
|
|
42967
43203
|
flex: "none"
|
|
42968
43204
|
},
|
|
42969
43205
|
productName: {
|
|
42970
|
-
|
|
43206
|
+
fontFamily: "'Times New Roman', serif",
|
|
43207
|
+
fontSize: "16px",
|
|
43208
|
+
letterSpacing: "0.04em",
|
|
42971
43209
|
color: "#8A8A8A",
|
|
42972
43210
|
overflow: "hidden",
|
|
42973
43211
|
textOverflow: "ellipsis",
|
|
@@ -43069,6 +43307,7 @@ function MobileAccordionItem({
|
|
|
43069
43307
|
] }),
|
|
43070
43308
|
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: [
|
|
43071
43309
|
/* @__PURE__ */ jsx$1("div", { css: css2.sizeRow, children: /* @__PURE__ */ jsx$1(SizeSelector, { loadedProductData: productData, selectedSizeLabel, onChangeSize }) }),
|
|
43310
|
+
/* @__PURE__ */ jsx$1(ColorSelector, { availableColorLabels, selectedColorLabel, onChangeColor }),
|
|
43072
43311
|
/* @__PURE__ */ jsx$1(ItemFitText, { loadedProductData: productData }),
|
|
43073
43312
|
/* @__PURE__ */ jsx$1("div", { css: css2.fitDetailsContainer, children: /* @__PURE__ */ jsx$1(ItemFitDetails, { loadedProductData: productData, selectedSizeLabel }) }),
|
|
43074
43313
|
/* @__PURE__ */ jsx$1("div", { css: css2.buttonContainer, children: /* @__PURE__ */ jsx$1(AddToCartButton, { onClick: onAddToCart }) }),
|
|
@@ -43095,6 +43334,7 @@ function DetailAccordion({
|
|
|
43095
43334
|
onOpenItem,
|
|
43096
43335
|
onChangeDetailMode,
|
|
43097
43336
|
onChangeSize,
|
|
43337
|
+
onChangeColor,
|
|
43098
43338
|
onAddToCart,
|
|
43099
43339
|
onToggleUntuck
|
|
43100
43340
|
}) {
|
|
@@ -43109,9 +43349,11 @@ function DetailAccordion({
|
|
|
43109
43349
|
gap
|
|
43110
43350
|
}, children: items.map((item) => {
|
|
43111
43351
|
const isOpen = openItemExternalId === item.externalId;
|
|
43112
|
-
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);
|
|
43352
|
+
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);
|
|
43113
43353
|
}) });
|
|
43114
43354
|
}
|
|
43355
|
+
const AXIS_LOCK_PX = 8;
|
|
43356
|
+
const ROTATE_STEP_PX = 50;
|
|
43115
43357
|
function ZoomModal({
|
|
43116
43358
|
frameUrls,
|
|
43117
43359
|
selectedFrameIndex,
|
|
@@ -43131,10 +43373,50 @@ function ZoomModal({
|
|
|
43131
43373
|
}, [onClose]);
|
|
43132
43374
|
const {
|
|
43133
43375
|
rotateLeft,
|
|
43134
|
-
rotateRight
|
|
43135
|
-
handleMouseDragStart,
|
|
43136
|
-
handleTouchDragStart
|
|
43376
|
+
rotateRight
|
|
43137
43377
|
} = useFrameRotation(frameUrls, setSelectedFrameIndex);
|
|
43378
|
+
const scrollAreaRef = reactExports.useRef(null);
|
|
43379
|
+
const handleImageMouseDown = reactExports.useCallback((e) => {
|
|
43380
|
+
e.preventDefault();
|
|
43381
|
+
const startX = e.clientX;
|
|
43382
|
+
const startY = e.clientY;
|
|
43383
|
+
const scrollArea = scrollAreaRef.current;
|
|
43384
|
+
const startScrollTop = scrollArea?.scrollTop ?? 0;
|
|
43385
|
+
let mode = "unknown";
|
|
43386
|
+
let lastRotateX = startX;
|
|
43387
|
+
const onMove = (move) => {
|
|
43388
|
+
const deltaX = move.clientX - startX;
|
|
43389
|
+
const deltaY = move.clientY - startY;
|
|
43390
|
+
if (mode === "unknown") {
|
|
43391
|
+
const absX = Math.abs(deltaX);
|
|
43392
|
+
const absY = Math.abs(deltaY);
|
|
43393
|
+
if (absX < AXIS_LOCK_PX && absY < AXIS_LOCK_PX) {
|
|
43394
|
+
return;
|
|
43395
|
+
}
|
|
43396
|
+
mode = absY > absX ? "scroll" : "rotate";
|
|
43397
|
+
lastRotateX = move.clientX;
|
|
43398
|
+
}
|
|
43399
|
+
if (mode === "scroll" && scrollArea) {
|
|
43400
|
+
scrollArea.scrollTop = startScrollTop - deltaY;
|
|
43401
|
+
} else if (mode === "rotate") {
|
|
43402
|
+
const rotateDelta = move.clientX - lastRotateX;
|
|
43403
|
+
if (Math.abs(rotateDelta) >= ROTATE_STEP_PX) {
|
|
43404
|
+
if (rotateDelta > 0) {
|
|
43405
|
+
rotateRight();
|
|
43406
|
+
} else {
|
|
43407
|
+
rotateLeft();
|
|
43408
|
+
}
|
|
43409
|
+
lastRotateX = move.clientX;
|
|
43410
|
+
}
|
|
43411
|
+
}
|
|
43412
|
+
};
|
|
43413
|
+
const onUp = () => {
|
|
43414
|
+
window.removeEventListener("mousemove", onMove);
|
|
43415
|
+
window.removeEventListener("mouseup", onUp);
|
|
43416
|
+
};
|
|
43417
|
+
window.addEventListener("mousemove", onMove);
|
|
43418
|
+
window.addEventListener("mouseup", onUp);
|
|
43419
|
+
}, [rotateLeft, rotateRight]);
|
|
43138
43420
|
const css2 = useCss((_theme) => ({
|
|
43139
43421
|
backdrop: {
|
|
43140
43422
|
position: "fixed",
|
|
@@ -43213,7 +43495,7 @@ function ZoomModal({
|
|
|
43213
43495
|
}));
|
|
43214
43496
|
const imageUrl = frameUrls[selectedFrameIndex ?? 0];
|
|
43215
43497
|
return /* @__PURE__ */ jsxs("div", { css: css2.backdrop, children: [
|
|
43216
|
-
/* @__PURE__ */ jsx$1("div", { css: css2.scrollArea, children: /* @__PURE__ */ jsx$1("div", { css: css2.imageWrap, children: /* @__PURE__ */ jsx$1("img", { src: imageUrl, css: css2.image, alt: "", onMouseDown:
|
|
43498
|
+
/* @__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 }) }) }),
|
|
43217
43499
|
/* @__PURE__ */ jsx$1("div", { css: /* @__PURE__ */ css$1({
|
|
43218
43500
|
...css2.chevron,
|
|
43219
43501
|
...css2.chevronLeft
|
|
@@ -43246,6 +43528,7 @@ function DesktopLayout$1({
|
|
|
43246
43528
|
onOpenAccordionItem,
|
|
43247
43529
|
onChangeDetailMode,
|
|
43248
43530
|
onChangeSize,
|
|
43531
|
+
onChangeColor,
|
|
43249
43532
|
onAddToCart,
|
|
43250
43533
|
onToggleUntuck,
|
|
43251
43534
|
onSignOut
|
|
@@ -43342,7 +43625,7 @@ function DesktopLayout$1({
|
|
|
43342
43625
|
gridTemplateColumns
|
|
43343
43626
|
}, children: [
|
|
43344
43627
|
/* @__PURE__ */ jsx$1("div", { css: css2.avatarColumn, onMouseEnter: () => setAvatarHovered(true), onMouseLeave: () => setAvatarHovered(false), children: /* @__PURE__ */ jsx$1(AvatarPane, { hasSelection, frameUrls, controls, selectedFrameIndex, setSelectedFrameIndex }) }),
|
|
43345
|
-
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, onAddToCart, onToggleUntuck }) }) : null,
|
|
43628
|
+
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,
|
|
43346
43629
|
/* @__PURE__ */ jsxs("div", { css: css2.railsColumn, children: [
|
|
43347
43630
|
/* @__PURE__ */ jsxs("span", { css: css2.signOutWrapper, onClick: onSignOut, children: [
|
|
43348
43631
|
/* @__PURE__ */ jsx$1(SvgTfrIcon, { css: css2.signOutIcon }),
|
|
@@ -43477,13 +43760,14 @@ function MobileLayout$1({
|
|
|
43477
43760
|
onOpenAccordionItem,
|
|
43478
43761
|
onChangeDetailMode,
|
|
43479
43762
|
onChangeSize,
|
|
43763
|
+
onChangeColor,
|
|
43480
43764
|
onAddToCart,
|
|
43481
43765
|
onToggleUntuck
|
|
43482
43766
|
}) {
|
|
43483
43767
|
if (mode === "browse") {
|
|
43484
43768
|
return /* @__PURE__ */ jsx$1(BrowseView, { resolved, availabilityByExternalId, selectedCount: selectedItems.length, onSelectItem, onRemoveItem, onTryItOn });
|
|
43485
43769
|
}
|
|
43486
|
-
return /* @__PURE__ */ jsx$1(TryOnView, { selectedItems, openAccordionItemId, detailMode, forceUntuck, canTuck, frameUrls, sheetSnap, sheetTouchStart, onBackToBrowse, onOpenAccordionItem, onChangeDetailMode, onChangeSize, onAddToCart, onToggleUntuck });
|
|
43770
|
+
return /* @__PURE__ */ jsx$1(TryOnView, { selectedItems, openAccordionItemId, detailMode, forceUntuck, canTuck, frameUrls, sheetSnap, sheetTouchStart, onBackToBrowse, onOpenAccordionItem, onChangeDetailMode, onChangeSize, onChangeColor, onAddToCart, onToggleUntuck });
|
|
43487
43771
|
}
|
|
43488
43772
|
function BrowseView({
|
|
43489
43773
|
resolved,
|
|
@@ -43583,6 +43867,7 @@ function TryOnView({
|
|
|
43583
43867
|
onOpenAccordionItem,
|
|
43584
43868
|
onChangeDetailMode,
|
|
43585
43869
|
onChangeSize,
|
|
43870
|
+
onChangeColor,
|
|
43586
43871
|
onAddToCart,
|
|
43587
43872
|
onToggleUntuck
|
|
43588
43873
|
}) {
|
|
@@ -43683,7 +43968,7 @@ function TryOnView({
|
|
|
43683
43968
|
/* @__PURE__ */ jsx$1(SvgDragHandle, {}),
|
|
43684
43969
|
/* @__PURE__ */ jsx$1(Text, { variant: "base", css: css2.sheetTitle, children: "RECOMMENDED SIZES" })
|
|
43685
43970
|
] }),
|
|
43686
|
-
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 }) })
|
|
43971
|
+
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 }) })
|
|
43687
43972
|
] }) })
|
|
43688
43973
|
] });
|
|
43689
43974
|
}
|
|
@@ -43956,6 +44241,25 @@ function FittingRoomOverlay({
|
|
|
43956
44241
|
color: csa.colorLabel
|
|
43957
44242
|
});
|
|
43958
44243
|
}, [resolved.items, updateFittingRoomItem]);
|
|
44244
|
+
const handleChangeColor = reactExports.useCallback((externalId, colorLabel) => {
|
|
44245
|
+
const item = resolved.items.find((i) => i.externalId === externalId);
|
|
44246
|
+
if (!item || !item.storage.size) {
|
|
44247
|
+
return;
|
|
44248
|
+
}
|
|
44249
|
+
const productData = buildVtoProductDataFromResolved(item);
|
|
44250
|
+
if (!productData) {
|
|
44251
|
+
return;
|
|
44252
|
+
}
|
|
44253
|
+
const csa = findCsaByLabel(productData, item.storage.size, colorLabel);
|
|
44254
|
+
if (!csa) {
|
|
44255
|
+
return;
|
|
44256
|
+
}
|
|
44257
|
+
updateFittingRoomItem(externalId, {
|
|
44258
|
+
colorwaySizeAssetId: csa.colorwaySizeAssetId,
|
|
44259
|
+
size: item.storage.size,
|
|
44260
|
+
color: csa.colorLabel
|
|
44261
|
+
});
|
|
44262
|
+
}, [resolved.items, updateFittingRoomItem]);
|
|
43959
44263
|
const handleAddToCart = reactExports.useCallback(async (externalId) => {
|
|
43960
44264
|
const {
|
|
43961
44265
|
addToCart
|
|
@@ -44148,7 +44452,7 @@ function FittingRoomOverlay({
|
|
|
44148
44452
|
/* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.emptyTagline, t: "landing.description" }),
|
|
44149
44453
|
/* @__PURE__ */ jsx$1("div", { css: css2.emptyShopNow, children: /* @__PURE__ */ jsx$1(ButtonT, { variant: "primary", t: "fitting_room.shop_now", onClick: handleShopNow }) }),
|
|
44150
44454
|
userIsLoggedIn ? /* @__PURE__ */ jsx$1(LinkT, { variant: "underline", css: css2.emptySignOut, t: "fitting_room.sign_out", onClick: handleSignOut }) : null
|
|
44151
|
-
] }) }) : 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, frameUrls, onSelectItem: handleSelectItem, onRemoveItem: handleRemoveItem, onOpenAccordionItem: setOpenAccordionItemId, onChangeDetailMode: setDetailMode, onChangeSize: handleChangeSize, onAddToCart: handleAddToCart, onToggleUntuck: handleToggleUntuck, onSignOut: handleSignOut }),
|
|
44455
|
+
] }) }) : isMobileLayout ? /* @__PURE__ */ jsx$1(MobileLayout$1, { mode: mobileMode, resolved, selectedItems, availabilityByExternalId, openAccordionItemId, detailMode, forceUntuck, canTuck, frameUrls, sheetSnap, sheetTouchStart, onSelectItem: handleSelectItem, onRemoveItem: handleRemoveItem, onTryItOn: handleTryItOn, onBackToBrowse: handleBackToBrowse, onOpenAccordionItem: setOpenAccordionItemId, onChangeDetailMode: setDetailMode, onChangeSize: handleChangeSize, onChangeColor: handleChangeColor, onAddToCart: handleAddToCart, onToggleUntuck: handleToggleUntuck }) : /* @__PURE__ */ jsx$1(DesktopLayout$1, { resolved, selectedItems, availabilityByExternalId, openAccordionItemId, detailMode, forceUntuck, canTuck, frameUrls, onSelectItem: handleSelectItem, onRemoveItem: handleRemoveItem, onOpenAccordionItem: setOpenAccordionItemId, onChangeDetailMode: setDetailMode, onChangeSize: handleChangeSize, onChangeColor: handleChangeColor, onAddToCart: handleAddToCart, onToggleUntuck: handleToggleUntuck, onSignOut: handleSignOut }),
|
|
44152
44456
|
vtoError ? /* @__PURE__ */ jsx$1(Snackbar, { messageKey: "fitting_room.vto_error", onDismiss: clearVtoError }) : null
|
|
44153
44457
|
] }) });
|
|
44154
44458
|
}
|
|
@@ -44338,7 +44642,7 @@ function GetAppOverlay({
|
|
|
44338
44642
|
openOverlay(OverlayName.SIGN_IN, {
|
|
44339
44643
|
returnToOverlay
|
|
44340
44644
|
});
|
|
44341
|
-
}, [returnToOverlay]);
|
|
44645
|
+
}, [openOverlay, returnToOverlay]);
|
|
44342
44646
|
const handleGetAppAppleClick = reactExports.useCallback(() => {
|
|
44343
44647
|
const url = getStaticData().config.links.appAppleStoreUrl;
|
|
44344
44648
|
window.open(url, "_blank");
|
|
@@ -44405,12 +44709,12 @@ function LandingOverlay({
|
|
|
44405
44709
|
openOverlay(OverlayName.GET_APP, {
|
|
44406
44710
|
returnToOverlay
|
|
44407
44711
|
});
|
|
44408
|
-
}, [returnToOverlay]);
|
|
44712
|
+
}, [openOverlay, returnToOverlay]);
|
|
44409
44713
|
const handleSignInClick = reactExports.useCallback(() => {
|
|
44410
44714
|
openOverlay(OverlayName.SIGN_IN, {
|
|
44411
44715
|
returnToOverlay
|
|
44412
44716
|
});
|
|
44413
|
-
}, [returnToOverlay]);
|
|
44717
|
+
}, [openOverlay, returnToOverlay]);
|
|
44414
44718
|
return /* @__PURE__ */ jsxs(ContentModal, { onRequestClose: closeOverlay, title: /* @__PURE__ */ jsx$1(TextT, { variant: "brand", css: css2.titleText, t: "try_it_on" }), children: [
|
|
44415
44719
|
/* @__PURE__ */ jsx$1("div", { css: css2.headerText, children: /* @__PURE__ */ jsx$1(TextT, { variant: "brand", css: css2.headerText, t: t("landing.header") }) }),
|
|
44416
44720
|
/* @__PURE__ */ jsx$1("div", { css: css2.description, children: /* @__PURE__ */ jsx$1(TextT, { variant: "base", t: t("landing.description") }) }),
|
|
@@ -44550,12 +44854,12 @@ function SignInOverlay({
|
|
|
44550
44854
|
openOverlay(OverlayName.FORGOT_PASSWORD, {
|
|
44551
44855
|
returnToOverlay
|
|
44552
44856
|
});
|
|
44553
|
-
}, [returnToOverlay]);
|
|
44857
|
+
}, [openOverlay, returnToOverlay]);
|
|
44554
44858
|
const handleGetAppClick = reactExports.useCallback(() => {
|
|
44555
44859
|
openOverlay(OverlayName.GET_APP, {
|
|
44556
44860
|
returnToOverlay
|
|
44557
44861
|
});
|
|
44558
|
-
}, [returnToOverlay]);
|
|
44862
|
+
}, [openOverlay, returnToOverlay]);
|
|
44559
44863
|
return /* @__PURE__ */ jsx$1(ContentModal, { onRequestClose: closeOverlay, title: /* @__PURE__ */ jsx$1(TfrTitle, {}), children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, css: css2.form, children: [
|
|
44560
44864
|
/* @__PURE__ */ jsx$1("div", { css: css2.emailContainer, children: /* @__PURE__ */ jsx$1("input", { name: "email", type: "email", placeholder: t("sign-in.email"), required: true, css: /* @__PURE__ */ css$1({
|
|
44561
44865
|
...css2.input,
|
|
@@ -44942,7 +45246,9 @@ function QuickViewOverlay() {
|
|
|
44942
45246
|
const {
|
|
44943
45247
|
color: selectedColor
|
|
44944
45248
|
} = await currentProduct.getSelectedOptions();
|
|
44945
|
-
const
|
|
45249
|
+
const styleCategoryIndex = await loadStyleCategoryIndex();
|
|
45250
|
+
const styleCategoryRecord = styleCategoryIndex.byName(storeProduct.style.style_category_name);
|
|
45251
|
+
const styleCategoryLabel = styleCategoryRecord?.label_singular ?? styleCategoryRecord?.label ?? null;
|
|
44946
45252
|
const sizeRecommendationRecord = storeProduct.sizeFitRecommendation;
|
|
44947
45253
|
{
|
|
44948
45254
|
const recommendedSizeId = sizeRecommendationRecord.recommended_size.id || null;
|
|
@@ -45115,7 +45421,7 @@ function QuickViewOverlay() {
|
|
|
45115
45421
|
error
|
|
45116
45422
|
});
|
|
45117
45423
|
});
|
|
45118
|
-
}, [closeOverlay
|
|
45424
|
+
}, [closeOverlay]);
|
|
45119
45425
|
const handleAddToCartClick = reactExports.useCallback(async () => {
|
|
45120
45426
|
try {
|
|
45121
45427
|
if (!selectedSizeLabel) {
|
|
@@ -45137,7 +45443,7 @@ function QuickViewOverlay() {
|
|
|
45137
45443
|
error
|
|
45138
45444
|
});
|
|
45139
45445
|
}
|
|
45140
|
-
}, [selectedColorLabel, selectedSizeLabel]);
|
|
45446
|
+
}, [closeOverlay, selectedColorLabel, selectedSizeLabel]);
|
|
45141
45447
|
if (vtoProductData === false) {
|
|
45142
45448
|
return /* @__PURE__ */ jsx$1(SidecarModalFrame, { onRequestClose: closeOverlay, children: /* @__PURE__ */ jsx$1(NoFitLayout, { onClose: closeOverlay, onSignOut: handleSignOutClick }) });
|
|
45143
45449
|
}
|
|
@@ -45602,7 +45908,9 @@ function DesktopLayout({
|
|
|
45602
45908
|
},
|
|
45603
45909
|
productNameContainer: {},
|
|
45604
45910
|
productNameText: {
|
|
45605
|
-
|
|
45911
|
+
fontFamily: "'Inter', sans-serif",
|
|
45912
|
+
fontSize: "32px",
|
|
45913
|
+
fontWeight: 300
|
|
45606
45914
|
},
|
|
45607
45915
|
priceContainer: {
|
|
45608
45916
|
marginTop: "8px"
|
|
@@ -45636,17 +45944,27 @@ function DesktopLayout({
|
|
|
45636
45944
|
marginTop: "8px",
|
|
45637
45945
|
lineHeight: "normal"
|
|
45638
45946
|
},
|
|
45639
|
-
itemFitText: {
|
|
45947
|
+
itemFitText: {
|
|
45948
|
+
fontFamily: "'Inter', sans-serif",
|
|
45949
|
+
fontWeight: 300
|
|
45950
|
+
},
|
|
45640
45951
|
selectSizeLabelContainer: {
|
|
45641
45952
|
lineHeight: "normal"
|
|
45642
45953
|
},
|
|
45643
|
-
selectSizeLabelText: {
|
|
45954
|
+
selectSizeLabelText: {
|
|
45955
|
+
fontFamily: "'Inter', sans-serif",
|
|
45956
|
+
fontWeight: 300
|
|
45957
|
+
},
|
|
45644
45958
|
sizeSelectorContainer: {
|
|
45645
45959
|
marginTop: "24px"
|
|
45646
45960
|
},
|
|
45647
45961
|
itemFitDetailsContainer: {
|
|
45648
45962
|
marginTop: "24px",
|
|
45649
|
-
width: "100%"
|
|
45963
|
+
width: "100%",
|
|
45964
|
+
// Cascades into <ItemFitDetails>; the bold recommended-size line above
|
|
45965
|
+
// and the size-selector buttons stay at their own weights.
|
|
45966
|
+
fontFamily: "'Inter', sans-serif",
|
|
45967
|
+
fontWeight: 300
|
|
45650
45968
|
},
|
|
45651
45969
|
fitChartContainer: {
|
|
45652
45970
|
marginTop: "16px"
|
|
@@ -45851,7 +46169,7 @@ function Avatar({
|
|
|
45851
46169
|
return () => {
|
|
45852
46170
|
window.removeEventListener("resize", refreshLayoutData);
|
|
45853
46171
|
};
|
|
45854
|
-
}, [isMobileLayout]);
|
|
46172
|
+
}, [isMobileLayout, setModalStyle]);
|
|
45855
46173
|
const isReady = !!frameUrls && selectedFrameIndex != null;
|
|
45856
46174
|
return /* @__PURE__ */ jsxs("div", { css: css2.topContainer, style: layoutData.topContainerStyle, children: [
|
|
45857
46175
|
/* @__PURE__ */ jsx$1(AvatarFrameViewer, { frameUrls, selectedFrameIndex, setSelectedFrameIndex, imageContainerStyle: layoutData.imageContainerStyle, imageStyle: layoutData.imageStyle, loadingT: "quick-view.avatar_loading" }),
|
|
@@ -45880,10 +46198,14 @@ function ProductSummaryRow({
|
|
|
45880
46198
|
},
|
|
45881
46199
|
labelContainer: {},
|
|
45882
46200
|
labelText: {
|
|
46201
|
+
fontFamily: "'Times New Roman', serif",
|
|
46202
|
+
fontSize: "16px",
|
|
45883
46203
|
color: "#1A1A1A"
|
|
45884
46204
|
},
|
|
45885
46205
|
nameContainer: {},
|
|
45886
46206
|
nameText: {
|
|
46207
|
+
fontFamily: "'Times New Roman', serif",
|
|
46208
|
+
fontSize: "16px",
|
|
45887
46209
|
color: "#9F9F9F"
|
|
45888
46210
|
}
|
|
45889
46211
|
}));
|
|
@@ -45892,35 +46214,6 @@ function ProductSummaryRow({
|
|
|
45892
46214
|
/* @__PURE__ */ jsx$1("div", { css: css2.nameContainer, children: /* @__PURE__ */ jsx$1(Text, { variant: "brand", css: css2.nameText, children: loadedProductData.productName }) })
|
|
45893
46215
|
] });
|
|
45894
46216
|
}
|
|
45895
|
-
function ColorSelector({
|
|
45896
|
-
availableColorLabels,
|
|
45897
|
-
selectedColorLabel,
|
|
45898
|
-
onChangeColor
|
|
45899
|
-
}) {
|
|
45900
|
-
const css2 = useCss((theme) => ({
|
|
45901
|
-
colorContainer: {},
|
|
45902
|
-
colorLabelText: {
|
|
45903
|
-
fontSize: "12px"
|
|
45904
|
-
},
|
|
45905
|
-
colorSelect: {
|
|
45906
|
-
border: "none",
|
|
45907
|
-
color: theme.color_fg_text,
|
|
45908
|
-
fontFamily: theme.font_family,
|
|
45909
|
-
fontSize: "12px"
|
|
45910
|
-
}
|
|
45911
|
-
}));
|
|
45912
|
-
const handleColorSelectChange = reactExports.useCallback((e) => {
|
|
45913
|
-
const newColorLabel = e.target.value || null;
|
|
45914
|
-
onChangeColor(newColorLabel);
|
|
45915
|
-
}, []);
|
|
45916
|
-
if (availableColorLabels.length < 2) {
|
|
45917
|
-
return null;
|
|
45918
|
-
}
|
|
45919
|
-
return /* @__PURE__ */ jsx$1("div", { css: css2.colorContainer, children: /* @__PURE__ */ jsxs("label", { children: [
|
|
45920
|
-
/* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.colorLabelText, t: "quick-view.color_label" }),
|
|
45921
|
-
/* @__PURE__ */ jsx$1("select", { value: selectedColorLabel ?? "", onChange: handleColorSelectChange, css: css2.colorSelect, children: availableColorLabels.map((colorLabel) => /* @__PURE__ */ jsx$1("option", { value: colorLabel, children: colorLabel }, colorLabel)) })
|
|
45922
|
-
] }) });
|
|
45923
|
-
}
|
|
45924
46217
|
function RecommendedSizeText({
|
|
45925
46218
|
loadedProductData,
|
|
45926
46219
|
textCss
|
|
@@ -46501,9 +46794,9 @@ const SHARED_CONFIG = {
|
|
|
46501
46794
|
appGooglePlayUrl: "https://play.google.com/store/apps/details?id=com.thefittingroom.marketplace"
|
|
46502
46795
|
},
|
|
46503
46796
|
build: {
|
|
46504
|
-
version: `${"5.0.
|
|
46505
|
-
commitHash: `${"
|
|
46506
|
-
date: `${"2026-05-
|
|
46797
|
+
version: `${"5.0.26"}`,
|
|
46798
|
+
commitHash: `${"357d81c"}`,
|
|
46799
|
+
date: `${"2026-05-23T18:08:09.620Z"}`
|
|
46507
46800
|
}
|
|
46508
46801
|
};
|
|
46509
46802
|
const CONFIGS = {
|
|
@@ -46627,7 +46920,7 @@ const CONFIGS = {
|
|
|
46627
46920
|
const getConfig = (envName) => {
|
|
46628
46921
|
return CONFIGS[envName];
|
|
46629
46922
|
};
|
|
46630
|
-
const css = "\n@import url('https://fonts.googleapis.com/css2?family=Inter:wght@
|
|
46923
|
+
const css = "\n@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100..900&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. The\n variable-axis URL serves every integer weight from 100 to 900 in one file\n so any `font-weight: <n>` value renders accurately without falling back to\n the nearest pre-loaded weight. */\nbody.tfr-modal-open {\n overflow: hidden;\n position: fixed;\n width: 100%;\n}\n";
|
|
46631
46924
|
class TfrWidgetElement extends HTMLElement {
|
|
46632
46925
|
connectedCallback() {
|
|
46633
46926
|
const attributes = this.getAttributeNames().reduce((attrs, name2) => {
|
|
@@ -46650,7 +46943,8 @@ async function init(initParams) {
|
|
|
46650
46943
|
debug,
|
|
46651
46944
|
productLookup,
|
|
46652
46945
|
getOverlayTopOffset,
|
|
46653
|
-
addToCart
|
|
46946
|
+
addToCart,
|
|
46947
|
+
testHooks
|
|
46654
46948
|
} = initParams;
|
|
46655
46949
|
if (!brandId || typeof brandId !== "number" || isNaN(brandId) || brandId <= 0) {
|
|
46656
46950
|
throw new Error(`Invalid brandId "${brandId}"`);
|
|
@@ -46676,7 +46970,8 @@ async function init(initParams) {
|
|
|
46676
46970
|
config,
|
|
46677
46971
|
productLookup: productLookup ?? null,
|
|
46678
46972
|
getOverlayTopOffset: getOverlayTopOffset ?? null,
|
|
46679
|
-
addToCart: addToCart ?? null
|
|
46973
|
+
addToCart: addToCart ?? null,
|
|
46974
|
+
testHooks
|
|
46680
46975
|
});
|
|
46681
46976
|
_init$7();
|
|
46682
46977
|
_init$5();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thefittingroom/shop-ui",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.26",
|
|
4
4
|
"description": "the fitting room UI library",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,8 +22,12 @@
|
|
|
22
22
|
"promote-latest": "sh scripts/promote-latest.sh",
|
|
23
23
|
"lint": "eslint .",
|
|
24
24
|
"lint:fix": "eslint . --fix",
|
|
25
|
-
"format": "prettier --write src",
|
|
26
|
-
"format:check": "prettier --check src"
|
|
25
|
+
"format": "prettier --write src tests playwright.config.ts",
|
|
26
|
+
"format:check": "prettier --check src tests playwright.config.ts",
|
|
27
|
+
"test": "npm run test:e2e",
|
|
28
|
+
"test:e2e": "playwright test",
|
|
29
|
+
"test:e2e:debug": "PWDEBUG=1 playwright test",
|
|
30
|
+
"test:e2e:ui": "playwright test --ui"
|
|
27
31
|
},
|
|
28
32
|
"engines": {
|
|
29
33
|
"node": ">=22"
|
|
@@ -33,6 +37,7 @@
|
|
|
33
37
|
},
|
|
34
38
|
"devDependencies": {
|
|
35
39
|
"@eslint/js": "^9.39.4",
|
|
40
|
+
"@playwright/test": "^1.60.0",
|
|
36
41
|
"@types/react": "^19.2.7",
|
|
37
42
|
"@types/react-dom": "^19.2.3",
|
|
38
43
|
"@types/react-modal": "^3.16.3",
|