@thefittingroom/shop-ui 5.0.32 → 5.0.34

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.
Files changed (2) hide show
  1. package/dist/index.js +261 -36
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -41660,11 +41660,74 @@ async function loadProductData(externalId) {
41660
41660
  throw new Error(`Style not found for externalId: ${externalId}`);
41661
41661
  }
41662
41662
  const sizeFitRecommendation = await getSizeRecommendation(style.id);
41663
+ const container = style.is_container ? {
41664
+ children: style.children ?? [],
41665
+ setSizeMappings: style.set_size_mappings ?? []
41666
+ } : void 0;
41663
41667
  return {
41664
41668
  externalId,
41665
41669
  style,
41666
- sizeFitRecommendation
41667
- };
41670
+ sizeFitRecommendation,
41671
+ container
41672
+ };
41673
+ }
41674
+ function buildVtoWireItems(loaded, parentCsaId, untucked) {
41675
+ const untuckFrag = untucked ? {
41676
+ untucked: true
41677
+ } : {};
41678
+ if (!loaded.container) {
41679
+ return [{
41680
+ colorway_size_asset_id: parentCsaId,
41681
+ ...untuckFrag
41682
+ }];
41683
+ }
41684
+ const childCsaIds = resolveContainerExpansion(loaded, parentCsaId);
41685
+ if (!childCsaIds) {
41686
+ return null;
41687
+ }
41688
+ return childCsaIds.map((csaId) => ({
41689
+ colorway_size_asset_id: csaId,
41690
+ ...untuckFrag
41691
+ }));
41692
+ }
41693
+ function resolveContainerExpansion(loaded, parentCsaId) {
41694
+ if (!loaded.container) {
41695
+ return null;
41696
+ }
41697
+ for (const sz of loaded.sizeFitRecommendation.available_sizes) {
41698
+ const csa = sz.colorway_size_assets.find((c) => c.id === parentCsaId);
41699
+ if (!csa || !csa.colorway_name) {
41700
+ continue;
41701
+ }
41702
+ return resolveContainerChildCSAs(loaded.container, sz.id, csa.colorway_name);
41703
+ }
41704
+ return null;
41705
+ }
41706
+ function resolveContainerChildCSAs(container, parentSizeId, parentColorwayName) {
41707
+ const relevantMappings = container.setSizeMappings.filter((m) => m.parent_size_id === parentSizeId);
41708
+ if (relevantMappings.length === 0) {
41709
+ return null;
41710
+ }
41711
+ const csaIds = [];
41712
+ for (const mapping of relevantMappings) {
41713
+ let matchedCsaId = null;
41714
+ for (const child of container.children) {
41715
+ const size = (child.sizes ?? []).find((s) => s.id === mapping.child_size_id);
41716
+ if (!size) {
41717
+ continue;
41718
+ }
41719
+ const csa = (child.colorway_size_assets ?? []).find((a) => a.size_id === size.id && a.colorway_name === parentColorwayName);
41720
+ if (csa) {
41721
+ matchedCsaId = csa.id;
41722
+ }
41723
+ break;
41724
+ }
41725
+ if (matchedCsaId == null) {
41726
+ return null;
41727
+ }
41728
+ csaIds.push(matchedCsaId);
41729
+ }
41730
+ return csaIds;
41668
41731
  }
41669
41732
  function loadProductDataToStore(externalId) {
41670
41733
  async function loadAndStore() {
@@ -41748,6 +41811,9 @@ function peekStyleCategoryIndex() {
41748
41811
  return cached;
41749
41812
  }
41750
41813
  const logger$a = getLogger("fitting-room-data");
41814
+ function isItemTuckable(item) {
41815
+ return item.effective.some((e) => !!e.category.tuckable);
41816
+ }
41751
41817
  async function loadFittingRoomData() {
41752
41818
  const state = useMainStore.getState();
41753
41819
  const items = state.fittingRoom;
@@ -41831,10 +41897,28 @@ function resolveItem(item, merchantSlot, loadedSlot, index) {
41831
41897
  const loadedError = loadedSlot && isLoadedError(loadedSlot) ? loadedSlot.error : null;
41832
41898
  let styleCategory = null;
41833
41899
  let styleCategoryGroup = null;
41900
+ const effective = [];
41834
41901
  if (loadedProduct && index) {
41835
41902
  const categoryName = String(loadedProduct.style.style_category_name);
41836
41903
  styleCategory = index.byName(categoryName);
41837
41904
  styleCategoryGroup = index.groupForCategory(categoryName);
41905
+ if (loadedProduct.container) {
41906
+ for (const child of loadedProduct.container.children) {
41907
+ const childName = String(child.style_category_name);
41908
+ const childCat = index.byName(childName);
41909
+ if (childCat) {
41910
+ effective.push({
41911
+ category: childCat,
41912
+ group: index.groupForCategory(childName)
41913
+ });
41914
+ }
41915
+ }
41916
+ } else if (styleCategory) {
41917
+ effective.push({
41918
+ category: styleCategory,
41919
+ group: styleCategoryGroup
41920
+ });
41921
+ }
41838
41922
  }
41839
41923
  let needsResize = item.colorwaySizeAssetId == null;
41840
41924
  if (!needsResize && loadedProduct && item.colorwaySizeAssetId != null) {
@@ -41852,6 +41936,16 @@ function resolveItem(item, merchantSlot, loadedSlot, index) {
41852
41936
  needsResize = true;
41853
41937
  }
41854
41938
  }
41939
+ if (!needsResize && loadedProduct.container && item.colorwaySizeAssetId != null) {
41940
+ const expanded = resolveContainerExpansion(loadedProduct, item.colorwaySizeAssetId);
41941
+ if (!expanded) {
41942
+ needsResize = true;
41943
+ logger$a.logDebug("container mapping resolved to empty, marking needsResize", {
41944
+ externalId: item.externalId,
41945
+ csa: item.colorwaySizeAssetId
41946
+ });
41947
+ }
41948
+ }
41855
41949
  }
41856
41950
  const isReady = !!merchantProduct && !!loadedProduct && !!styleCategory && !needsResize;
41857
41951
  return {
@@ -41863,6 +41957,7 @@ function resolveItem(item, merchantSlot, loadedSlot, index) {
41863
41957
  loadedError,
41864
41958
  styleCategory,
41865
41959
  styleCategoryGroup,
41960
+ effective,
41866
41961
  isReady,
41867
41962
  needsResize
41868
41963
  };
@@ -42052,6 +42147,12 @@ function useMobileSheetSnap(initial = "collapsed") {
42052
42147
  };
42053
42148
  }
42054
42149
  const MAX_OUTFIT_ITEMS = 4;
42150
+ function getGarmentCount(item) {
42151
+ if (item.effective.length > 0) {
42152
+ return item.effective.length;
42153
+ }
42154
+ return item.styleCategory ? 1 : 0;
42155
+ }
42055
42156
  function asStringList(value) {
42056
42157
  if (!Array.isArray(value)) {
42057
42158
  return [];
@@ -42069,6 +42170,19 @@ function asStringList(value) {
42069
42170
  function catName(c) {
42070
42171
  return String(c.name);
42071
42172
  }
42173
+ function itemsCompatible(a, b) {
42174
+ if (a.effective.length === 0 || b.effective.length === 0) {
42175
+ return true;
42176
+ }
42177
+ for (const ae of a.effective) {
42178
+ for (const be of b.effective) {
42179
+ if (!pairCompatible(ae.category, be.category, ae.group)) {
42180
+ return false;
42181
+ }
42182
+ }
42183
+ }
42184
+ return true;
42185
+ }
42072
42186
  function pairCompatible(a, b, group) {
42073
42187
  const aName = catName(a);
42074
42188
  const bName = catName(b);
@@ -42121,11 +42235,7 @@ function computeAvailability(item, selectedExternalIds, resolved) {
42121
42235
  return "disabled";
42122
42236
  }
42123
42237
  const sameCategoryEvictions = new Set(getSameCategoryConflicts(item, selectedExternalIds, resolved));
42124
- const effectiveSize = selectedExternalIds.size - sameCategoryEvictions.size + 1;
42125
- if (effectiveSize > MAX_OUTFIT_ITEMS) {
42126
- return "disabled";
42127
- }
42128
- const itemCat = item.styleCategory;
42238
+ let effectiveGarments = getGarmentCount(item);
42129
42239
  for (const sel of resolved.items) {
42130
42240
  if (!selectedExternalIds.has(sel.externalId)) {
42131
42241
  continue;
@@ -42133,10 +42243,19 @@ function computeAvailability(item, selectedExternalIds, resolved) {
42133
42243
  if (sameCategoryEvictions.has(sel.externalId)) {
42134
42244
  continue;
42135
42245
  }
42136
- if (!sel.styleCategory) {
42246
+ effectiveGarments += getGarmentCount(sel);
42247
+ }
42248
+ if (effectiveGarments > MAX_OUTFIT_ITEMS) {
42249
+ return "disabled";
42250
+ }
42251
+ for (const sel of resolved.items) {
42252
+ if (!selectedExternalIds.has(sel.externalId)) {
42253
+ continue;
42254
+ }
42255
+ if (sameCategoryEvictions.has(sel.externalId)) {
42137
42256
  continue;
42138
42257
  }
42139
- if (!pairCompatible(sel.styleCategory, itemCat, sel.styleCategoryGroup)) {
42258
+ if (!itemsCompatible(sel, item)) {
42140
42259
  return "disabled";
42141
42260
  }
42142
42261
  }
@@ -42149,19 +42268,31 @@ function makeOutfitItem(r, forceUntuck) {
42149
42268
  if (r.storage.colorwaySizeAssetId == null) {
42150
42269
  return null;
42151
42270
  }
42152
- const tuckable = !!r.styleCategory.tuckable;
42271
+ const tuckable = isItemTuckable(r);
42153
42272
  const untucked = forceUntuck && tuckable;
42154
42273
  const layerOrder = untucked ? r.styleCategory.layer_order_untucked : r.styleCategory.layer_order;
42274
+ let childCsaIds;
42275
+ if (r.loadedProduct?.container) {
42276
+ const resolved = resolveContainerExpansion(r.loadedProduct, r.storage.colorwaySizeAssetId);
42277
+ if (!resolved) {
42278
+ return null;
42279
+ }
42280
+ childCsaIds = resolved;
42281
+ }
42155
42282
  const outfitItem = {
42156
42283
  externalId: r.externalId,
42157
42284
  colorwaySizeAssetId: r.storage.colorwaySizeAssetId,
42158
42285
  ...untucked ? {
42159
42286
  untucked: true
42287
+ } : {},
42288
+ ...childCsaIds ? {
42289
+ childCsaIds
42160
42290
  } : {}
42161
42291
  };
42162
42292
  return {
42163
42293
  outfitItem,
42164
- layerOrder
42294
+ layerOrder,
42295
+ garmentCount: childCsaIds?.length ?? 1
42165
42296
  };
42166
42297
  }
42167
42298
  function buildOutfit(selectedExternalIds, resolved, forceUntuck, lastAddedExternalId) {
@@ -42180,7 +42311,15 @@ function buildOutfit(selectedExternalIds, resolved, forceUntuck, lastAddedExtern
42180
42311
  }
42181
42312
  }
42182
42313
  entries.sort((a, b) => a.layerOrder - b.layerOrder);
42183
- const items = entries.slice(0, MAX_OUTFIT_ITEMS).map((e) => e.outfitItem);
42314
+ const items = [];
42315
+ let cumulativeGarments = 0;
42316
+ for (const entry of entries) {
42317
+ if (cumulativeGarments + entry.garmentCount > MAX_OUTFIT_ITEMS) {
42318
+ break;
42319
+ }
42320
+ items.push(entry.outfitItem);
42321
+ cumulativeGarments += entry.garmentCount;
42322
+ }
42184
42323
  const alternates = buildAlternateOutfits(items, lastAddedResolved);
42185
42324
  return {
42186
42325
  items,
@@ -42213,9 +42352,20 @@ function buildAlternateOutfits(primary, lastAddedResolved) {
42213
42352
  if (!altCsa) {
42214
42353
  continue;
42215
42354
  }
42355
+ let altChildCsaIds;
42356
+ if (lastAddedResolved.loadedProduct.container) {
42357
+ const resolved = resolveContainerExpansion(lastAddedResolved.loadedProduct, altCsa.id);
42358
+ if (!resolved) {
42359
+ continue;
42360
+ }
42361
+ altChildCsaIds = resolved;
42362
+ }
42216
42363
  const alternate = primary.map((it) => it.externalId === lastAddedResolved.externalId ? {
42217
42364
  ...it,
42218
- colorwaySizeAssetId: altCsa.id
42365
+ colorwaySizeAssetId: altCsa.id,
42366
+ ...altChildCsaIds ? {
42367
+ childCsaIds: altChildCsaIds
42368
+ } : {}
42219
42369
  } : it);
42220
42370
  out.push(alternate);
42221
42371
  }
@@ -43339,7 +43489,7 @@ function DetailAccordionItem({
43339
43489
  }, [productData, selectedSizeLabel]);
43340
43490
  const categoryLabel = item.styleCategory?.label_singular ?? item.styleCategory?.label ?? "";
43341
43491
  const productName = item.merchantProduct?.productName ?? item.externalId;
43342
- const tuckable = !!item.styleCategory?.tuckable && canTuck;
43492
+ const tuckable = isItemTuckable(item) && canTuck;
43343
43493
  if (platform === "desktop") {
43344
43494
  return /* @__PURE__ */ jsx$1(DesktopAccordionItem, { isOpen, categoryLabel, productData, currentPrice, selectedSizeLabel, availableColorLabels, selectedColorLabel: item.storage.color, onToggleOpen, onChangeSize, onChangeColor, onAddToCart });
43345
43495
  }
@@ -44518,12 +44668,21 @@ function useVtoRequests() {
44518
44668
  };
44519
44669
  }
44520
44670
  function toWireItems(items) {
44521
- return items.map((i) => ({
44522
- colorway_size_asset_id: i.colorwaySizeAssetId,
44523
- ...i.untucked ? {
44671
+ return items.flatMap((i) => {
44672
+ const untuckFrag = i.untucked ? {
44524
44673
  untucked: true
44525
- } : {}
44526
- }));
44674
+ } : {};
44675
+ if (i.childCsaIds && i.childCsaIds.length > 0) {
44676
+ return i.childCsaIds.map((csaId) => ({
44677
+ colorway_size_asset_id: csaId,
44678
+ ...untuckFrag
44679
+ }));
44680
+ }
44681
+ return [{
44682
+ colorway_size_asset_id: i.colorwaySizeAssetId,
44683
+ ...untuckFrag
44684
+ }];
44685
+ });
44527
44686
  }
44528
44687
  const logger$8 = getLogger("overlays/fitting-room");
44529
44688
  function measureTopOffset() {
@@ -45671,6 +45830,7 @@ function QuickViewOverlay() {
45671
45830
  const [selectedSizeLabel, setSelectedSizeLabel] = reactExports.useState(null);
45672
45831
  const [selectedColorLabel, setSelectedColorLabel] = reactExports.useState(null);
45673
45832
  const [modalStyle, setModalStyle] = reactExports.useState({});
45833
+ const [containerUntucked, setContainerUntucked] = reactExports.useState(false);
45674
45834
  const {
45675
45835
  request: vtoRequest,
45676
45836
  framesForOutfit,
@@ -45849,12 +46009,46 @@ function QuickViewOverlay() {
45849
46009
  availableColorLabels: availableColorLabels2
45850
46010
  };
45851
46011
  }, [vtoProductData, selectedSizeLabel, selectedColorLabel]);
46012
+ const loadedProduct = reactExports.useMemo(() => {
46013
+ const {
46014
+ currentProduct
46015
+ } = getStaticData();
46016
+ if (!currentProduct) {
46017
+ return null;
46018
+ }
46019
+ const entry = storeProductData[currentProduct.externalId];
46020
+ if (!entry || "error" in entry) {
46021
+ return null;
46022
+ }
46023
+ return entry;
46024
+ }, [storeProductData]);
46025
+ const containerHasTuckableChild = reactExports.useMemo(() => {
46026
+ if (!loadedProduct?.container) {
46027
+ return false;
46028
+ }
46029
+ const index = peekStyleCategoryIndex();
46030
+ if (!index) {
46031
+ return false;
46032
+ }
46033
+ return loadedProduct.container.children.some((c) => {
46034
+ const cat = index.byName(String(c.style_category_name));
46035
+ return !!cat?.tuckable;
46036
+ });
46037
+ }, [loadedProduct]);
46038
+ const handleToggleContainerUntucked = reactExports.useCallback(() => {
46039
+ setContainerUntucked((prev2) => !prev2);
46040
+ }, []);
45852
46041
  const requestVto2 = reactExports.useCallback((sizeColorRecord, priority) => {
45853
- vtoRequest([{
45854
- colorway_size_asset_id: sizeColorRecord.colorwaySizeAssetId,
45855
- untucked: false
45856
- }], priority);
45857
- }, [vtoRequest]);
46042
+ if (!loadedProduct) {
46043
+ return;
46044
+ }
46045
+ const untucked = loadedProduct.container ? containerUntucked : false;
46046
+ const wireItems = buildVtoWireItems(loadedProduct, sizeColorRecord.colorwaySizeAssetId, untucked);
46047
+ if (!wireItems) {
46048
+ return;
46049
+ }
46050
+ vtoRequest(wireItems, priority);
46051
+ }, [vtoRequest, loadedProduct, containerUntucked]);
45858
46052
  reactExports.useEffect(() => {
45859
46053
  if (selectedColorSizeRecord) {
45860
46054
  requestVto2(selectedColorSizeRecord, true);
@@ -45875,13 +46069,15 @@ function QuickViewOverlay() {
45875
46069
  }
45876
46070
  }, [requestVto2, vtoProductData, selectedColorLabel]);
45877
46071
  const frameUrls = reactExports.useMemo(() => {
45878
- if (!selectedColorSizeRecord) {
46072
+ if (!selectedColorSizeRecord || !loadedProduct) {
45879
46073
  return null;
45880
46074
  }
45881
- const rewritten = framesForOutfit([{
45882
- colorway_size_asset_id: selectedColorSizeRecord.colorwaySizeAssetId,
45883
- untucked: false
45884
- }]);
46075
+ const untucked = loadedProduct.container ? containerUntucked : false;
46076
+ const wireItems = buildVtoWireItems(loadedProduct, selectedColorSizeRecord.colorwaySizeAssetId, untucked);
46077
+ if (!wireItems) {
46078
+ return null;
46079
+ }
46080
+ const rewritten = framesForOutfit(wireItems);
45885
46081
  if (!rewritten) {
45886
46082
  return null;
45887
46083
  }
@@ -45891,7 +46087,7 @@ function QuickViewOverlay() {
45891
46087
  img.src = url;
45892
46088
  });
45893
46089
  return rewritten;
45894
- }, [selectedColorSizeRecord, framesForOutfit]);
46090
+ }, [selectedColorSizeRecord, framesForOutfit, loadedProduct, containerUntucked]);
45895
46091
  const handleSignOutClick = reactExports.useCallback(() => {
45896
46092
  closeOverlay();
45897
46093
  const authManager2 = getAuthManager();
@@ -45957,7 +46153,7 @@ function QuickViewOverlay() {
45957
46153
  Layout = DesktopLayout;
45958
46154
  }
45959
46155
  return /* @__PURE__ */ jsxs(SidecarModalFrame, { onRequestClose: closeOverlay, contentStyle: modalStyle, children: [
45960
- /* @__PURE__ */ jsx$1(Layout, { loadedProductData: vtoProductData, selectedColorSizeRecord, availableColorLabels, selectedColorLabel, selectedSizeLabel, frameUrls, setModalStyle, onClose: closeOverlay, onChangeColor: handleChangeColor, onChangeSize: setSelectedSizeLabel, onAddToCart: handleAddToCartClick, onSignOut: handleSignOutClick }),
46156
+ /* @__PURE__ */ jsx$1(Layout, { loadedProductData: vtoProductData, selectedColorSizeRecord, availableColorLabels, selectedColorLabel, selectedSizeLabel, frameUrls, setModalStyle, onClose: closeOverlay, onChangeColor: handleChangeColor, onChangeSize: setSelectedSizeLabel, onAddToCart: handleAddToCartClick, onSignOut: handleSignOutClick, containerTuckable: containerHasTuckableChild, containerUntucked, onToggleUntuck: handleToggleContainerUntucked }),
45961
46157
  vtoError ? /* @__PURE__ */ jsx$1(Snackbar, { messageKey: "quick-view.vto_error", onDismiss: clearVtoError }) : null
45962
46158
  ] });
45963
46159
  }
@@ -46044,6 +46240,31 @@ function FittingRoomToggleButton() {
46044
46240
  /* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.text, t: isInFittingRoom ? "added_to_fitting_room" : "add_to_fitting_room" })
46045
46241
  ] });
46046
46242
  }
46243
+ function ContainerTuckToggleButton({
46244
+ untucked,
46245
+ onToggle
46246
+ }) {
46247
+ const css2 = useCss((theme) => ({
46248
+ button: {
46249
+ marginTop: "10px",
46250
+ width: "100%",
46251
+ display: "flex",
46252
+ alignItems: "center",
46253
+ justifyContent: "center",
46254
+ gap: "10px",
46255
+ padding: "13px",
46256
+ backgroundColor: "#FFFFFF",
46257
+ border: `1px solid ${theme.color_fg_text}`,
46258
+ borderRadius: "30px",
46259
+ cursor: "pointer"
46260
+ },
46261
+ text: {
46262
+ fontSize: "14px",
46263
+ textTransform: "uppercase"
46264
+ }
46265
+ }));
46266
+ return /* @__PURE__ */ jsx$1("button", { type: "button", onClick: onToggle, css: css2.button, children: /* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.text, t: untucked ? "fitting_room.try_it_tucked_in" : "fitting_room.try_it_untucked" }) });
46267
+ }
46047
46268
  function MobileLayout({
46048
46269
  loadedProductData,
46049
46270
  selectedColorSizeRecord,
@@ -46393,7 +46614,10 @@ function DesktopLayout({
46393
46614
  onChangeColor,
46394
46615
  onChangeSize,
46395
46616
  onAddToCart,
46396
- onSignOut
46617
+ onSignOut,
46618
+ containerTuckable,
46619
+ containerUntucked,
46620
+ onToggleUntuck
46397
46621
  }) {
46398
46622
  const {
46399
46623
  t
@@ -46521,7 +46745,8 @@ function DesktopLayout({
46521
46745
  fitChartNode,
46522
46746
  /* @__PURE__ */ jsxs("div", { css: css2.buttonContainer, children: [
46523
46747
  /* @__PURE__ */ jsx$1(AddToCartButton, { onClick: onAddToCart }),
46524
- /* @__PURE__ */ jsx$1(FittingRoomToggleButton, {})
46748
+ /* @__PURE__ */ jsx$1(FittingRoomToggleButton, {}),
46749
+ containerTuckable ? /* @__PURE__ */ jsx$1(ContainerTuckToggleButton, { untucked: containerUntucked, onToggle: onToggleUntuck }) : null
46525
46750
  ] }),
46526
46751
  /* @__PURE__ */ jsx$1("div", { css: css2.descriptionContainer, children: /* @__PURE__ */ jsx$1(ProductDescriptionText, { loadedProductData }) })
46527
46752
  ] }),
@@ -47796,9 +48021,9 @@ const SHARED_CONFIG = {
47796
48021
  appGooglePlayUrl: "https://play.google.com/store/apps/details?id=com.thefittingroom.marketplace"
47797
48022
  },
47798
48023
  build: {
47799
- version: `${"5.0.32"}`,
47800
- commitHash: `${"1e960c2"}`,
47801
- date: `${"2026-06-28T19:41:53.726Z"}`
48024
+ version: `${"5.0.34"}`,
48025
+ commitHash: `${"5b52e20"}`,
48026
+ date: `${"2026-07-13T13:49:10.971Z"}`
47802
48027
  }
47803
48028
  };
47804
48029
  const CONFIGS = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thefittingroom/shop-ui",
3
- "version": "5.0.32",
3
+ "version": "5.0.34",
4
4
  "description": "the fitting room UI library",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",