@wewear/virtual-try-on 1.4.26 → 1.4.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3,11 +3,12 @@ export interface VirtualTryOnConfig {
3
3
  baseUrl: string;
4
4
  productPageSelector: string[];
5
5
  gallerySelector: string;
6
+ galleryElement?: HTMLElement;
6
7
  productImageSelector: string;
7
8
  buttonPosition: ButtonPosition;
8
9
  modelTier: string;
9
- greenSuit: string;
10
10
  outfitID: string;
11
+ singleton?: boolean;
11
12
  }
12
13
  export interface VirtualTryOnResult {
13
14
  imageUrl: string | null;
@@ -16,5 +17,5 @@ export interface VirtualTryOnResult {
16
17
  type: "error" | "warning";
17
18
  };
18
19
  }
19
- export { getWidgetInstance, initVirtualTryOn, } from "./installer.js";
20
+ export { getWidgetInstance, getWidgetInstances, initVirtualTryOn, } from "./installer.js";
20
21
  export { VirtualTryOnWidget } from "./widget.js";
package/dist/index.esm.js CHANGED
@@ -20,12 +20,6 @@ function getPositionStyles(position) {
20
20
  return "right: 20px; bottom: 20px;";
21
21
  }
22
22
  }
23
- function removeElements(selector) {
24
- const elements = document.querySelectorAll(selector);
25
- elements.forEach((element) => {
26
- element.remove();
27
- });
28
- }
29
23
 
30
24
  function createButtonContainer(buttonPosition, hasVirtualTryOn = false, onCameraClick, onRefreshClick, onToggleClick, isShowingVirtualTryOn = false) {
31
25
  const container = document.createElement("div");
@@ -395,7 +389,9 @@ class VirtualTryOnWidget {
395
389
  this.isShowingVirtualTryOn = false;
396
390
  this.lastModelImage = null;
397
391
  this.cameraButton = null;
392
+ this.modalElement = null;
398
393
  this.iframeMessageListener = null;
394
+ this.instanceId = ++VirtualTryOnWidget.instanceCounter;
399
395
  this.config = {
400
396
  baseUrl: config.baseUrl,
401
397
  productPageSelector: config.productPageSelector,
@@ -403,10 +399,19 @@ class VirtualTryOnWidget {
403
399
  productImageSelector: config.productImageSelector,
404
400
  buttonPosition: config.buttonPosition,
405
401
  modelTier: config.modelTier,
406
- greenSuit: config.greenSuit,
407
402
  outfitID: config.outfitID,
403
+ singleton: config.singleton,
404
+ galleryElement: config.galleryElement,
408
405
  };
409
406
  }
407
+ getContainer() {
408
+ const configContainer = this.config.galleryElement;
409
+ if (configContainer && configContainer.isConnected) {
410
+ return configContainer;
411
+ }
412
+ const queriedContainer = document.querySelector(this.config.gallerySelector);
413
+ return queriedContainer instanceof HTMLElement ? queriedContainer : null;
414
+ }
410
415
  async init() {
411
416
  try {
412
417
  const selectors = this.config.productPageSelector;
@@ -415,8 +420,8 @@ class VirtualTryOnWidget {
415
420
  if (!matches) {
416
421
  return;
417
422
  }
418
- const container = document.querySelector(this.config.gallerySelector);
419
- if (!container || !(container instanceof HTMLElement)) {
423
+ const container = this.getContainer();
424
+ if (!container) {
420
425
  console.warn("[WeWear VTO] Gallery container not found:", this.config.gallerySelector);
421
426
  return;
422
427
  }
@@ -463,7 +468,10 @@ class VirtualTryOnWidget {
463
468
  }
464
469
  }
465
470
  getAllProductImages() {
466
- const productImageElements = document.querySelectorAll(this.config.productImageSelector);
471
+ const container = this.getContainer();
472
+ const scopedElements = container === null || container === void 0 ? void 0 : container.querySelectorAll(this.config.productImageSelector);
473
+ const fallbackElements = document.querySelectorAll(this.config.productImageSelector);
474
+ const productImageElements = ((scopedElements === null || scopedElements === void 0 ? void 0 : scopedElements.length) ? scopedElements : fallbackElements);
467
475
  const images = [];
468
476
  productImageElements.forEach((img) => {
469
477
  const imageUrl = img.src ||
@@ -479,7 +487,8 @@ class VirtualTryOnWidget {
479
487
  return images;
480
488
  }
481
489
  showPhotoUploadModal(url) {
482
- removeElements(`.${CSS_CLASSES.MODAL}`);
490
+ this.closeModal();
491
+ VirtualTryOnWidget.activeWidgetId = this.instanceId;
483
492
  const modal = document.createElement("div");
484
493
  modal.className = CSS_CLASSES.MODAL;
485
494
  modal.style.cssText = `
@@ -573,7 +582,7 @@ class VirtualTryOnWidget {
573
582
  modal.style.opacity = "0";
574
583
  iframeContainer.style.transform = "scale(0.95)";
575
584
  iframeContainer.style.opacity = "0";
576
- setTimeout(() => modal.remove(), 300);
585
+ setTimeout(() => this.closeModal(), 300);
577
586
  };
578
587
  iframeContainer.appendChild(iframe);
579
588
  iframeContainer.appendChild(closeButton);
@@ -584,12 +593,25 @@ class VirtualTryOnWidget {
584
593
  closeButton.click();
585
594
  }
586
595
  };
596
+ this.modalElement = modal;
597
+ }
598
+ closeModal() {
599
+ if (this.modalElement) {
600
+ this.modalElement.remove();
601
+ this.modalElement = null;
602
+ }
603
+ if (VirtualTryOnWidget.activeWidgetId === this.instanceId) {
604
+ VirtualTryOnWidget.activeWidgetId = null;
605
+ }
587
606
  }
588
607
  setupIframeListener() {
589
608
  if (this.iframeMessageListener) {
590
609
  window.removeEventListener("message", this.iframeMessageListener);
591
610
  }
592
611
  this.iframeMessageListener = (event) => {
612
+ if (VirtualTryOnWidget.activeWidgetId !== this.instanceId) {
613
+ return;
614
+ }
593
615
  if (event.origin !== new URL(this.config.baseUrl).origin) {
594
616
  return;
595
617
  }
@@ -601,7 +623,7 @@ class VirtualTryOnWidget {
601
623
  }
602
624
  break;
603
625
  case "CLOSE_MODAL":
604
- removeElements(`.${CSS_CLASSES.MODAL}`);
626
+ this.closeModal();
605
627
  break;
606
628
  }
607
629
  };
@@ -626,58 +648,18 @@ class VirtualTryOnWidget {
626
648
  if (modal && modal instanceof HTMLElement) {
627
649
  modal.style.display = "none";
628
650
  }
629
- const container = document.querySelector(this.config.gallerySelector);
630
- if (container instanceof HTMLElement) {
651
+ const container = this.getContainer();
652
+ if (container) {
631
653
  showProductLoading(container, "Preparing your personalized look");
632
654
  }
633
- let hasPreview = false;
634
655
  try {
635
656
  const submitResponse = await this.callVtoApi(this.lastModelImage, this.originalProductImages);
636
657
  let status = await this.fetchJobStatus(submitResponse.job_id);
637
- let lastPreviewCount = 0;
638
658
  while (status.status !== "COMPLETED" && status.status !== "FAILED") {
639
- if (!hasPreview && container instanceof HTMLElement) {
640
- let statusMessage = "Preparing your personalized look";
641
- if (status.current_iteration === 1) {
642
- statusMessage = "Refining your look";
643
- }
644
- else if (status.current_iteration && status.current_iteration > 1) {
645
- statusMessage = "Finalizing your look";
646
- }
659
+ if (container instanceof HTMLElement) {
660
+ const statusMessage = "Preparing your personalized look";
647
661
  showProductLoading(container, statusMessage);
648
662
  }
649
- else if (hasPreview && container instanceof HTMLElement) {
650
- let statusMessage = "Refining your look";
651
- if (status.current_iteration && status.current_iteration > 1) {
652
- statusMessage = "Finalizing your look";
653
- }
654
- showStatusBadge(container, statusMessage);
655
- this.hideButtonContainer(container);
656
- }
657
- if (status.preview_count > lastPreviewCount) {
658
- try {
659
- const previewUrl = await this.fetchJobImage(submitResponse.job_id);
660
- if (!hasPreview && container instanceof HTMLElement) {
661
- this.replaceProductImage(previewUrl);
662
- let statusMessage = "Refining your look";
663
- if (status.current_iteration && status.current_iteration > 1) {
664
- statusMessage = "Finalizing your look";
665
- }
666
- showStatusBadge(container, statusMessage);
667
- this.hideButtonContainer(container);
668
- removeProductLoading(container);
669
- hasPreview = true;
670
- }
671
- else if (hasPreview) {
672
- this.replaceProductImage(previewUrl);
673
- }
674
- lastPreviewCount = status.preview_count;
675
- }
676
- catch (e) {
677
- if (!(e instanceof Error && e.message === "202_PROCESSING"))
678
- throw e;
679
- }
680
- }
681
663
  await new Promise((r) => setTimeout(r, 3000));
682
664
  status = await this.fetchJobStatus(submitResponse.job_id);
683
665
  }
@@ -694,12 +676,7 @@ class VirtualTryOnWidget {
694
676
  if (status.status === "FAILED") {
695
677
  console.error("[WeWear VTO] VTO process failed:", status.message);
696
678
  if (container instanceof HTMLElement) {
697
- if (!hasPreview) {
698
- showProductLoading(container, "We are experiencing some technical issues and apologise for any inconvenience caused. Please come back later again");
699
- }
700
- else {
701
- showStatusBadge(container, "We are experiencing some technical issues and apologise for any inconvenience caused. Please come back later again");
702
- }
679
+ showStatusBadge(container, "We are experiencing some technical issues and apologise for any inconvenience caused. Please come back later again");
703
680
  }
704
681
  await new Promise((r) => setTimeout(r, 5000));
705
682
  }
@@ -707,12 +684,7 @@ class VirtualTryOnWidget {
707
684
  catch (error) {
708
685
  console.error("[WeWear VTO] Error during virtual try-on process:", error);
709
686
  if (container instanceof HTMLElement) {
710
- if (!hasPreview) {
711
- showProductLoading(container, "We are experiencing technical issues and apologise for any inconvenience caused. Please come back later again");
712
- }
713
- else {
714
- showStatusBadge(container, "We are experiencing technical issues. Please try again later.");
715
- }
687
+ showProductLoading(container, "We are experiencing technical issues and apologise for any inconvenience caused. Please come back later again");
716
688
  }
717
689
  await new Promise((r) => setTimeout(r, 5000));
718
690
  }
@@ -722,7 +694,7 @@ class VirtualTryOnWidget {
722
694
  removeStatusBadge(container);
723
695
  this.showButtonContainer(container);
724
696
  }
725
- removeElements(`.${CSS_CLASSES.MODAL}`);
697
+ this.closeModal();
726
698
  }
727
699
  }
728
700
  async callVtoApi(modelImage, productImages) {
@@ -732,7 +704,6 @@ class VirtualTryOnWidget {
732
704
  formData.append("product_image_urls", JSON.stringify(productImages));
733
705
  formData.append("page_url", window.location.href);
734
706
  formData.append("model_tier", this.config.modelTier);
735
- formData.append("green_suit", this.config.greenSuit);
736
707
  const outfitID = (_a = this.config.outfitID) === null || _a === void 0 ? void 0 : _a.trim();
737
708
  if (outfitID)
738
709
  formData.append("outfit_id", outfitID);
@@ -769,8 +740,8 @@ class VirtualTryOnWidget {
769
740
  replaceProductImage(imageUrl) {
770
741
  try {
771
742
  this.virtualTryOnImageUrl = imageUrl;
772
- const container = document.querySelector(this.config.gallerySelector);
773
- if (!container || !(container instanceof HTMLElement)) {
743
+ const container = this.getContainer();
744
+ if (!container) {
774
745
  console.warn("[WeWear VTO] Gallery container not found for image replacement:", this.config.gallerySelector);
775
746
  return;
776
747
  }
@@ -807,12 +778,6 @@ class VirtualTryOnWidget {
807
778
  console.error("[WeWear VTO] Error replacing product image:", error);
808
779
  }
809
780
  }
810
- hideButtonContainer(container) {
811
- const buttonContainer = container.querySelector(`.${CSS_CLASSES.BUTTON_CONTAINER}`);
812
- if (buttonContainer) {
813
- buttonContainer.style.display = "none";
814
- }
815
- }
816
781
  showButtonContainer(container) {
817
782
  const buttonContainer = container.querySelector(`.${CSS_CLASSES.BUTTON_CONTAINER}`);
818
783
  if (buttonContainer) {
@@ -901,8 +866,12 @@ class VirtualTryOnWidget {
901
866
  }
902
867
  destroy() {
903
868
  try {
904
- removeElements(`.${CSS_CLASSES.BUTTON_CONTAINER}`);
905
- removeElements(`.${CSS_CLASSES.MODAL}`);
869
+ const container = this.getContainer();
870
+ if (container) {
871
+ const existingButtons = container.querySelectorAll(`.${CSS_CLASSES.BUTTON_CONTAINER}`);
872
+ existingButtons.forEach((btn) => btn.remove());
873
+ }
874
+ this.closeModal();
906
875
  this.cameraButton = null;
907
876
  if (this.iframeMessageListener) {
908
877
  window.removeEventListener("message", this.iframeMessageListener);
@@ -920,30 +889,45 @@ class VirtualTryOnWidget {
920
889
  }
921
890
  }
922
891
  }
892
+ VirtualTryOnWidget.instanceCounter = 0;
893
+ VirtualTryOnWidget.activeWidgetId = null;
923
894
 
924
- let widgetInstance = null;
895
+ let widgetInstances = [];
925
896
  function initVirtualTryOn(config) {
926
897
  try {
927
- if (widgetInstance) {
928
- widgetInstance.destroy();
929
- widgetInstance = null;
930
- }
898
+ widgetInstances.forEach((instance) => instance.destroy());
899
+ widgetInstances = [];
931
900
  if (!config) {
932
901
  console.log("[WeWear VTO] Missing configuration. Widget not initialized.");
933
902
  return;
934
903
  }
935
- widgetInstance = new VirtualTryOnWidget(config);
936
- if (document.readyState === "loading") {
937
- document.addEventListener("DOMContentLoaded", () => {
938
- widgetInstance === null || widgetInstance === void 0 ? void 0 : widgetInstance.init().catch((error) => {
939
- console.error("[WeWear VTO] Failed to initialize after DOM ready:", error);
940
- });
904
+ const allContainers = Array.from(document.querySelectorAll(config.gallerySelector)).filter((container) => container instanceof HTMLElement);
905
+ const shouldUseSingleton = config.singleton !== false;
906
+ if (allContainers.length > 1 && !shouldUseSingleton) {
907
+ widgetInstances = allContainers.map((galleryElement) => {
908
+ return new VirtualTryOnWidget(Object.assign(Object.assign({}, config), { galleryElement }));
941
909
  });
942
910
  }
943
911
  else {
944
- widgetInstance.init().catch((error) => {
945
- console.error("[WeWear VTO] Failed to initialize:", error);
912
+ const firstContainer = allContainers[0];
913
+ widgetInstances = [
914
+ new VirtualTryOnWidget(Object.assign(Object.assign({}, config), { galleryElement: firstContainer })),
915
+ ];
916
+ }
917
+ const initializeWidgets = () => {
918
+ widgetInstances.forEach((widget) => {
919
+ widget.init().catch((error) => {
920
+ console.error("[WeWear VTO] Failed to initialize:", error);
921
+ });
946
922
  });
923
+ };
924
+ if (document.readyState === "loading") {
925
+ document.addEventListener("DOMContentLoaded", initializeWidgets, {
926
+ once: true,
927
+ });
928
+ }
929
+ else {
930
+ initializeWidgets();
947
931
  }
948
932
  }
949
933
  catch (error) {
@@ -951,11 +935,15 @@ function initVirtualTryOn(config) {
951
935
  }
952
936
  }
953
937
  function getWidgetInstance() {
954
- return widgetInstance;
938
+ var _a;
939
+ return (_a = widgetInstances[0]) !== null && _a !== void 0 ? _a : null;
940
+ }
941
+ function getWidgetInstances() {
942
+ return [...widgetInstances];
955
943
  }
956
944
  if (typeof window !== "undefined") {
957
945
  initVirtualTryOn();
958
946
  }
959
947
 
960
- export { VirtualTryOnWidget, getWidgetInstance, initVirtualTryOn };
948
+ export { VirtualTryOnWidget, getWidgetInstance, getWidgetInstances, initVirtualTryOn };
961
949
  //# sourceMappingURL=index.esm.js.map