@thefittingroom/shop-ui 3.0.0-alpha-4 → 3.0.0-alpha-6

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.
@@ -38,7 +38,7 @@ export declare class SizeRecComponent {
38
38
  private tfrToggleClosedElements;
39
39
  private isCollapsed;
40
40
  private redraw;
41
- constructor(sizeRecMainDivId: string, onSignInClick: () => void, onSignOutClick: () => void, onFitInfoClick: () => void, onTryOnClick: (styleId: number, sizeId: number) => void);
41
+ constructor(sizeRecMainDivId: string, onSignInClick: () => void, onSignOutClick: () => void, onFitInfoClick: () => void, onTryOnClick: (styleId: number, sizeId: number, shouldDisplay: boolean) => Promise<void>);
42
42
  get sku(): string;
43
43
  setSku(sku: string): void;
44
44
  get styleId(): number;
@@ -1,6 +1,8 @@
1
1
  export declare class VtoComponent {
2
2
  private readonly vtoMainDivId;
3
3
  private isInit;
4
+ private currentSliderValue;
5
+ private slider;
4
6
  constructor(vtoMainDivId: string);
5
7
  init(): void;
6
8
  onNewFramesReady(frames: string[]): void;
@@ -1,3 +1,3 @@
1
1
  export declare const InitImageSlider: (sliderID: string, onChange: (slider: HTMLInputElement, imageUrl: string) => void) => {
2
- Load(imageURLs: string[]): Error | (() => void);
2
+ Load(imageURLs: string[], initialValue?: number): Error | (() => void);
3
3
  };
package/dist/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * thefittingroom v3.0.0-alpha-4 (2025-01-20T23:29:36.706Z)
2
+ * thefittingroom v3.0.0-alpha-6 (2025-04-18T16:48:31.249Z)
3
3
  * Copyright 2022-present, TheFittingRoom, Inc. All rights reserved.
4
4
  */
5
5
  function loadImageRecursive(imageURL, imageURLs) {
@@ -23,13 +23,13 @@ const InitImageSlider = (sliderID, onChange) => {
23
23
  throw new Error(`Slider with id ${sliderID} not found`);
24
24
  }
25
25
  return {
26
- Load(imageURLs) {
26
+ Load(imageURLs, initialValue) {
27
27
  if (!Array.isArray(imageURLs) || !imageURLs.length) {
28
28
  console.debug('slider has no images to load');
29
29
  return new Error('slider has no images to load');
30
30
  }
31
31
  loadImages(imageURLs);
32
- const defaultScrollValue = 0;
32
+ const defaultScrollValue = initialValue !== undefined ? initialValue : 0;
33
33
  slider.value = defaultScrollValue.toString();
34
34
  slider.max = (imageURLs.length - 1).toString();
35
35
  const handleSliderChange = () => {
@@ -26591,6 +26591,8 @@ class VtoComponent {
26591
26591
  constructor(vtoMainDivId) {
26592
26592
  this.vtoMainDivId = vtoMainDivId;
26593
26593
  this.isInit = false;
26594
+ this.currentSliderValue = 0;
26595
+ this.slider = null;
26594
26596
  }
26595
26597
  init() {
26596
26598
  if (this.isInit)
@@ -26604,21 +26606,34 @@ class VtoComponent {
26604
26606
  <input type="range" id="tfr-slider" />
26605
26607
  </div>
26606
26608
  `;
26607
- this.isInit = true;
26608
- }
26609
- onNewFramesReady(frames) {
26610
26609
  const tryOnImage = document.getElementById('tfr-tryon-image');
26611
26610
  const onChange = (slider, imageUrl) => {
26612
26611
  console.debug('slider change', slider, imageUrl);
26613
26612
  tryOnImage.src = imageUrl;
26613
+ this.currentSliderValue = parseInt(slider.value);
26614
26614
  };
26615
- const slider = InitImageSlider('tfr-slider', onChange);
26615
+ this.slider = InitImageSlider('tfr-slider', onChange);
26616
+ this.isInit = true;
26617
+ }
26618
+ onNewFramesReady(frames) {
26619
+ if (!this.isInit) {
26620
+ this.init();
26621
+ }
26616
26622
  if (Array.isArray(frames) && frames.length > 0) {
26617
- const e = slider.Load(frames);
26623
+ // Ensure the current slider value is within bounds of the new frames array
26624
+ const boundedValue = Math.min(this.currentSliderValue, frames.length - 1);
26625
+ const e = this.slider.Load(frames, boundedValue);
26618
26626
  if (e instanceof Error) {
26619
26627
  console.error(e);
26620
26628
  return;
26621
26629
  }
26630
+ // Restore previous slider position if it's within bounds
26631
+ const sliderElement = document.getElementById('tfr-slider');
26632
+ if (sliderElement && this.currentSliderValue < frames.length) {
26633
+ sliderElement.value = this.currentSliderValue.toString();
26634
+ const tryOnImage = document.getElementById('tfr-tryon-image');
26635
+ tryOnImage.src = frames[this.currentSliderValue];
26636
+ }
26622
26637
  }
26623
26638
  }
26624
26639
  }
@@ -26697,7 +26712,7 @@ n(css$6,{});
26697
26712
  var css$5 = "@media screen and (max-width:702px){.tfr-modal-title-logo-container{display:flex;flex-direction:column}}@media screen and (min-width:600px){.tfr-modal-content-container{border-radius:10px;height:auto;margin:auto;width:100%}}@media screen and (max-width:599px){#tfr-size-recommendations{width:calc(100vw - 30px)!important}.tfr-mobile-small-text{font-size:12px}.tfr-mobile-hidden{display:none}.tfr-modal-content{overflow:hidden}.tfr-modal-content-container{max-height:none;max-width:100vw;min-height:100vh;min-width:100vw}.trf-logo-title{margin-bottom:10px}.tfr-modal-content-flex{overflow:auto}}@media screen and (max-width:500px){.tfr-fieldset{width:90%}.tfr-how-it-works-item{flex-direction:column}.tfr-try-on-content{margin-left:0;margin-top:20px}}@media screen and (max-height:800px){.tfr-video-responsive{height:280px!important}}";
26698
26713
  n(css$5,{});
26699
26714
 
26700
- var css$4 = "#tfr-size-recommendations{align-items:center;background-color:var(--tfr-main-bg-color);border:var(--tfr-main-border-width) solid var(--tfr-main-border-color);border-radius:var(--tfr-main-border-radius);color:var(--tfr-dark);display:flex;flex-direction:column;font-family:var(--tfr-main-font);justify-content:center;padding:var(--tfr-main-v-padding) var(--tfr-main-h-padding);width:var(--tfr-main-width)}#tfr-size-recommendations-container{align-items:center;display:none;flex-direction:column;justify-content:center;position:relative;width:100%}#tfr-size-rec-select-container{align-items:center;display:none;flex-direction:column;font-size:13px;width:100%}#tfr-size-rec-title{align-items:center;display:flex;font-family:var(--tfr-title-font);margin-bottom:8px}#tfr-size-rec-title-toggle{color:var(--tfr-grey);cursor:pointer;position:absolute;right:0;top:0;transition:all .3s ease}.tfr-chevron-up{transform:rotate(180deg) scaleY(.6)}.tfr-chevron-down{transform:rotate(0deg) scaleY(.6)}#tfr-info-icon{cursor:pointer}#tfr-size-rec-subtitle{border-bottom:2px solid #000;font-size:18px;font-weight:700;text-align:center;width:100%}#tfr-size-rec-title{font-size:14px}#tfr-size-rec-table{display:flex;flex-direction:column;font-size:12px;padding:0 10px;width:100%}.tfr-size-rec-table-row:first-of-type{border-top-width:0}.tfr-size-rec-table-row{align-items:center;border-top:var(--tfr-main-border-width) solid var(--tfr-main-border-color);display:flex;font-family:var(--tfr-row-font);justify-content:center;min-height:40px}.tfr-size-rec-table-cell-left,.tfr-size-rec-table-cell-right{flex:1 1 0px}.tfr-size-rec-table-cell-left{flex-grow:1;padding-left:8px;text-align:right}.tfr-size-rec-table-cell-right{margin-left:16px;padding-right:8px;text-align:left}.tfr-size-rec-table-cell-right.perfect{color:var(--tfr-brand-color)}#tfr-size-rec-size{display:inline-block}#tfr-size-rec-size>.tfr-size-rec-login-cta{font-weight:500;margin-left:10px}.tfr-size-rec-login-cta,.tfr-size-rec-table-cell-right{font-size:12px}.tfr-size-rec-login-cta{color:var(--tfr-muted);display:flex;width:150px}#tfr-size-rec-action-login,#tfr-size-rec-action-logout{display:none;font-family:var(--tfr-cta-font)}#tfr-size-rec-action{cursor:pointer;font-size:16px;text-decoration:underline}#tfr-size-rec-select{background-color:var(--tfr-size-selector-bg-color);border-color:var(--tfr-size-selector-border-color);border-radius:var(--tfr-size-selector-button-radius);border-style:solid;border-width:var(--tfr-size-selector-border-width);box-shadow:var(--tfr-size-selector-button-shadow);color:var(--tfr-size-selector-text-color);display:none;font-size:var(--tfr-size-selector-font-size);font-weight:var(--tfr-size-selector-font-weight);margin-bottom:20px;margin-top:10px}#tfr-size-rec-select,.tfr-size-rec-select-button{align-items:center;height:var(--tfr-size-selector-button-height);justify-content:center}.tfr-size-rec-select-button{cursor:pointer;display:flex;transition:all .15s ease-in;width:80px}.tfr-size-rec-select-button:hover:not(.active):not(.tfr-disabled){background-color:var(--tfr-size-selector-bg-color-hover);opacity:.7}.tfr-size-rec-select-button.active{background-color:var(--tfr-size-selector-bg-color-active);border-color:var(--tfr-size-selector-button-active-border-color);border-style:solid;border-width:var(--tfr-size-selector-button-active-border-width);height:var(--tfr-size-selector-button-active-height)}.tfr-size-rec-select-button.active,.tfr-size-rec-select-button:first-of-type{border-bottom-left-radius:var(--tfr-size-selector-button-radius);border-top-left-radius:var(--tfr-size-selector-button-radius)}.tfr-size-rec-select-button.active,.tfr-size-rec-select-button:last-of-type{border-bottom-right-radius:var(--tfr-size-selector-button-radius);border-top-right-radius:var(--tfr-size-selector-button-radius)}.tfr-powered-by{align-items:center;display:flex;font-size:10px;margin-top:10px}.tfr-powered-by-logo{margin:0 4px}.tfr-powered-by-logo,.tfr-powered-by-logo img,.tfr-powered-by-logo svg{height:24px;width:12px}.tfr-powered-by-text-bold{font-weight:700}#tfr-size-recommendation-error{color:#8d0000;display:none}#tfr-sign-in-nav{margin-bottom:80px}.tfr-disabled{cursor:default}#tfr-login-to-view{cursor:pointer}";
26715
+ var css$4 = "#tfr-size-recommendations{align-items:center;background-color:var(--tfr-main-bg-color);border:var(--tfr-main-border-width) solid var(--tfr-main-border-color);border-radius:var(--tfr-main-border-radius);color:var(--tfr-dark);display:flex;flex-direction:column;font-family:var(--tfr-main-font);justify-content:center;padding:var(--tfr-main-v-padding) var(--tfr-main-h-padding);width:var(--tfr-main-width)}#tfr-size-recommendations-container{align-items:center;display:none;flex-direction:column;justify-content:center;position:relative;width:100%}#tfr-size-rec-select-container{align-items:center;display:none;flex-direction:column;font-size:13px;width:100%}#tfr-size-rec-title{align-items:center;display:flex;font-family:var(--tfr-title-font);margin-bottom:8px}#tfr-size-rec-title-toggle{color:var(--tfr-grey);cursor:pointer;position:absolute;right:0;top:0;transition:all .3s ease}.tfr-chevron-up{transform:rotate(180deg) scaleY(.6)}.tfr-chevron-down{transform:rotate(0deg) scaleY(.6)}#tfr-info-icon{cursor:pointer}#tfr-size-rec-subtitle{border-bottom:2px solid #000;font-size:18px;font-weight:700;text-align:center;width:100%}#tfr-size-rec-title{font-size:14px}#tfr-size-rec-table{display:flex;flex-direction:column;font-size:12px;padding:0 10px;width:100%}.tfr-size-rec-table-row:first-of-type{border-top-width:0}.tfr-size-rec-table-row{align-items:center;border-top:var(--tfr-main-border-width) solid var(--tfr-main-border-color);display:flex;font-family:var(--tfr-row-font);justify-content:center;min-height:40px}.tfr-size-rec-table-cell-left,.tfr-size-rec-table-cell-right{flex:1 1 0px}.tfr-size-rec-table-cell-left{flex-grow:1;padding-left:8px;padding-right:8px;text-align:right}.tfr-size-rec-table-cell-right{flex-grow:1;padding-left:8px;padding-right:8px;text-align:left}.tfr-size-rec-table-cell-right.perfect{color:var(--tfr-brand-color)}#tfr-size-rec-size{display:inline-block}#tfr-size-rec-size>.tfr-size-rec-login-cta{font-weight:500;margin-left:10px}.tfr-size-rec-login-cta,.tfr-size-rec-table-cell-right{font-size:12px}.tfr-size-rec-login-cta{color:var(--tfr-muted);display:flex;width:150px}#tfr-size-rec-action-login,#tfr-size-rec-action-logout{display:none;font-family:var(--tfr-cta-font)}#tfr-size-rec-action{cursor:pointer;font-size:16px;text-decoration:underline}#tfr-size-rec-select{background-color:var(--tfr-size-selector-bg-color);border-color:var(--tfr-size-selector-border-color);border-radius:var(--tfr-size-selector-button-radius);border-style:solid;border-width:var(--tfr-size-selector-border-width);box-shadow:var(--tfr-size-selector-button-shadow);color:var(--tfr-size-selector-text-color);display:none;font-size:var(--tfr-size-selector-font-size);font-weight:var(--tfr-size-selector-font-weight);margin-bottom:20px;margin-top:10px}#tfr-size-rec-select,.tfr-size-rec-select-button{align-items:center;height:var(--tfr-size-selector-button-height);justify-content:center}.tfr-size-rec-select-button{cursor:pointer;display:flex;transition:all .15s ease-in;width:80px}.tfr-size-rec-select-button:hover:not(.active):not(.tfr-disabled){background-color:var(--tfr-size-selector-bg-color-hover);opacity:.7}.tfr-size-rec-select-button.active{background-color:var(--tfr-size-selector-bg-color-active);border-color:var(--tfr-size-selector-button-active-border-color);border-style:solid;border-width:var(--tfr-size-selector-button-active-border-width);height:var(--tfr-size-selector-button-active-height)}.tfr-size-rec-select-button.active,.tfr-size-rec-select-button:first-of-type{border-bottom-left-radius:var(--tfr-size-selector-button-radius);border-top-left-radius:var(--tfr-size-selector-button-radius)}.tfr-size-rec-select-button.active,.tfr-size-rec-select-button:last-of-type{border-bottom-right-radius:var(--tfr-size-selector-button-radius);border-top-right-radius:var(--tfr-size-selector-button-radius)}.tfr-powered-by{align-items:center;display:flex;font-size:10px;margin-top:10px}.tfr-powered-by-logo{margin:0 4px}.tfr-powered-by-logo,.tfr-powered-by-logo img,.tfr-powered-by-logo svg{height:24px;width:12px}.tfr-powered-by-text-bold{font-weight:700}#tfr-size-recommendation-error{color:#8d0000;display:none}#tfr-sign-in-nav{margin-bottom:80px}.tfr-disabled{cursor:default}#tfr-login-to-view{cursor:pointer}.tfr-try-on-button{align-items:center;background-color:var(--tfr-size-selector-bg-color-active);border-color:var(--tfr-size-selector-border-color);border-radius:var(--tfr-size-selector-button-radius);border-style:solid;border-width:var(--tfr-size-selector-border-width);box-shadow:var(--tfr-size-selector-button-shadow);color:var(--tfr-size-selector-text-color);cursor:pointer;display:flex;font-size:var(--tfr-size-selector-font-size);font-weight:var(--tfr-size-selector-font-weight);height:var(--tfr-size-selector-button-height);justify-content:center;margin-bottom:10px;margin-top:20px;max-width:200px;position:relative;transition:all .15s ease-in;width:100%}.tfr-try-on-button.loading,.tfr-try-on-button:disabled{cursor:not-allowed;opacity:.7}.tfr-try-on-button.loading:after{animation:spin 1s linear infinite;border:2px solid var(--tfr-size-selector-text-color);border-radius:50%;border-top:2px solid transparent;content:\"\";height:16px;margin-left:8px;position:absolute;width:16px}@keyframes spin{to{transform:rotate(1turn)}}";
26701
26716
  n(css$4,{});
26702
26717
 
26703
26718
  var css$3 = ".tfr-mt-10{margin-top:10px}.tfr-mt-20{margin-top:20px}.tfr-mt-15{margin-top:15px}.tfr-mt-30{margin-top:30px}.mt-40{margin-top:40px}.tfr-mb-40{margin-bottom:40px}.tfr-mb-20{margin-bottom:20px}.tfr-mr-10{margin-right:10px}.tfr-mr-15{margin-right:15px}.tfr-mt-50{margin-top:50px}.tfr-mt-60{margin-top:60px}.tfr-mb-60{margin-bottom:60px}.tfr-mr-20{margin-right:20px}.tfr-mt-15-p{margin-top:15%}.tfr-mb-13-p{margin-bottom:13%}.tfr-m-h-auto{margin-left:auto;margin-right:auto}.tfr-pt-20{padding-top:20px}.tfr-pb-50{padding-bottom:50px}.tfr-p-20{padding:20px 10px}.tfr-pr-20{padding-right:20px}.tfr-pl-20{padding-left:20px}.tfr-pb-7-p{padding-bottom:7%}";
@@ -27501,6 +27516,9 @@ class SizeRecComponent {
27501
27516
  this.isCollapsed = false;
27502
27517
  this.tfrSizeRecTitleToggle.classList.add('tfr-chevron-up');
27503
27518
  this.tfrSizeRecTitleToggle.classList.remove('tfr-chevron-down');
27519
+ // Ensure the container is visible
27520
+ this.tfrSizeRecSelectContainer.style.display = 'flex';
27521
+ this.tfrSizeRecSelectContainer.style.opacity = '1';
27504
27522
  }
27505
27523
  else {
27506
27524
  this.tfrSizeHowItFits.style.opacity = '0.4';
@@ -27577,6 +27595,48 @@ class SizeRecComponent {
27577
27595
  this.tfrSizeRecTitleToggle.addEventListener('click', this.toggletSizeRecSelectContainer.bind(this));
27578
27596
  this.tfrInfoIcon.addEventListener('click', this.onFitInfoClick);
27579
27597
  this.tfrLoginToView.addEventListener('click', this.onSignInClick);
27598
+ const tryOnButton = document.getElementById('tfr-try-on-button');
27599
+ if (!tryOnButton)
27600
+ return;
27601
+ tryOnButton.addEventListener('click', async () => {
27602
+ // Prevent multiple clicks while loading
27603
+ if (tryOnButton.classList.contains('loading')) {
27604
+ return;
27605
+ }
27606
+ const activeButton = document.querySelector('.tfr-size-rec-select-button.active');
27607
+ if (!activeButton)
27608
+ return;
27609
+ const selectedSizeId = Number(activeButton.getAttribute('data-size-id'));
27610
+ if (Number.isNaN(selectedSizeId))
27611
+ return;
27612
+ // Set loading state
27613
+ tryOnButton.classList.add('loading');
27614
+ const originalText = tryOnButton.textContent;
27615
+ tryOnButton.textContent = ' ';
27616
+ tryOnButton.setAttribute('disabled', 'true');
27617
+ try {
27618
+ // Get all size buttons
27619
+ const allSizeButtons = Array.from(document.querySelectorAll('.tfr-size-rec-select-button'));
27620
+ // Request frames for all sizes concurrently
27621
+ await Promise.all(allSizeButtons.map(async (button) => {
27622
+ const sizeId = Number(button.getAttribute('data-size-id'));
27623
+ if (Number.isNaN(sizeId))
27624
+ return;
27625
+ // Only display the active size, others are just requested
27626
+ const shouldDisplay = button === activeButton;
27627
+ await this.onTryOnClick(this.styleId, sizeId, shouldDisplay);
27628
+ }));
27629
+ }
27630
+ catch (error) {
27631
+ console.error('Error during try-on:', error);
27632
+ }
27633
+ finally {
27634
+ // Reset loading state
27635
+ tryOnButton.classList.remove('loading');
27636
+ tryOnButton.textContent = originalText;
27637
+ tryOnButton.removeAttribute('disabled');
27638
+ }
27639
+ });
27580
27640
  }
27581
27641
  onSizeRecSelectClick(e) {
27582
27642
  const target = e.target;
@@ -27593,7 +27653,7 @@ class SizeRecComponent {
27593
27653
  const selectedSizeId = Number(target.getAttribute('data-size-id'));
27594
27654
  if (Number.isNaN(selectedSizeId))
27595
27655
  return;
27596
- this.onTryOnClick(this.styleId, selectedSizeId);
27656
+ this.onTryOnClick(this.styleId, selectedSizeId, true);
27597
27657
  }
27598
27658
  renderSizeRec(recommended, sizes) {
27599
27659
  this.tfrSizeRecSize.innerHTML = `&nbsp;${recommended}`;
@@ -27601,8 +27661,6 @@ class SizeRecComponent {
27601
27661
  this.redraw = (index) => this.renderSizeRecTable(sizes, index);
27602
27662
  this.redraw(selectedSizeIndex);
27603
27663
  this.renderSizeRecSelect(sizes, selectedSizeIndex);
27604
- const selectedSizeId = sizes[selectedSizeIndex].size_id;
27605
- this.onTryOnClick(this.styleId, selectedSizeId);
27606
27664
  }
27607
27665
  renderSizeRecTable(sizes, index) {
27608
27666
  const { locations } = sizes[index];
@@ -27724,6 +27782,8 @@ class SizeRecComponent {
27724
27782
  </div>
27725
27783
 
27726
27784
  <div id="tfr-size-rec-table"></div>
27785
+
27786
+ <div id="tfr-try-on-button" class="tfr-try-on-button">Try On</div>
27727
27787
  </div>
27728
27788
  </div>
27729
27789
 
@@ -27955,10 +28015,8 @@ class FittingRoom {
27955
28015
  this.tfrModal = new TfrModal(modalDivId, this.signIn.bind(this), this.forgotPassword.bind(this), this.submitTel.bind(this));
27956
28016
  this.tfrShop = initShop(Number(this.shopId), env);
27957
28017
  this.tfrSizeRec = new TfrSizeRec(sizeRecMainDivId, cssVariables, this.tfrShop, this.onSignInClick.bind(this), this.signOut.bind(this), this.onFitInfoClick.bind(this), this.onTryOnClick.bind(this));
27958
- try {
28018
+ if (vtoMainDivId)
27959
28019
  this.vtoComponent = new VtoComponent(vtoMainDivId);
27960
- }
27961
- catch (_a) { }
27962
28020
  }
27963
28021
  get shop() {
27964
28022
  return this.tfrShop;
@@ -28057,10 +28115,19 @@ class FittingRoom {
28057
28115
  onFitInfoClick() {
28058
28116
  this.tfrModal.toFitInfo();
28059
28117
  }
28060
- async onTryOnClick(styleId, sizeId) {
28118
+ async onTryOnClick(styleId, sizeId, shouldDisplay = true) {
28119
+ if (!this.vtoComponent)
28120
+ return console.error('VtoComponent is not initialized');
28061
28121
  const frames = await this.shop.tryOn(styleId, sizeId);
28062
- this.vtoComponent.init();
28063
- this.vtoComponent.onNewFramesReady(frames);
28122
+ if (shouldDisplay) {
28123
+ try {
28124
+ this.vtoComponent.init();
28125
+ this.vtoComponent.onNewFramesReady(frames);
28126
+ }
28127
+ catch (e) {
28128
+ console.error(e);
28129
+ }
28130
+ }
28064
28131
  }
28065
28132
  onUserProfileChange(userProfile) {
28066
28133
  var _a, _b, _c, _d;