Untitled

mail@pastecode.io avatarunknown
javascript
a month ago
9.8 kB
4
Indexable
Never
import Popup from '../lib/popup';
import { fetchSection } from '../lib/utils';

class ShopifyWishlist extends HTMLElement {
  constructor() {
    super();

    window.shopifyWishlist = this;

    this.settings = JSON.parse(this.querySelector('#settings').textContent);
    this.hash = null;
    this.items = [];

    this.selectors = {
      form: '[data-shopify-wishlist-form]',
      button: '[data-shopify-wishlist-button]',
      count: '[data-wishlist-count]',
      products: '[data-wishlist-products]',
      product: '[data-product-card]',
      removeAll: '[data-remove-all]'
    };

    this.initialise();
  }

  async initialise() {
    document.addEventListener('submit', this.onFormSubmit.bind(this));

    document.addEventListener('wishlist-hero-wishlist-sdk-ready', async () => {
      await this.initWishlist();

      await this.getWishlistItems();

      this.initButtons();

      this.initWishlistPage();
    });

    if (typeof window.WishListHero_SDK !== 'undefined') {
      // If wishlist hero SDK is already loaded, trigger the event manually to leave the rest of the code intact
      document.dispatchEvent(new Event('wishlist-hero-wishlist-sdk-ready'));
    }
  }

  onRemoveAllClick(e) {
    if (e.target.closest(this.selectors.removeAll) == null) return;

    this.removeAllItems();
  }

  initWishlistPage(container = document) {
    if (document.querySelector(this.selectors.products) === null) return;

    document.addEventListener('click', this.onRemoveAllClick.bind(this));

    const productContainer = document.querySelector(this.selectors.products);

    if (this.items.length > 0) {
      const productRequests = this.items.map((item) =>
        fetchSection(
          `/products/${item.ProductHandle}?section_id=wishlist-product-card&variant=${item.ProductVariantId}`
        ).then((section) => section)
      );

      Promise.all(productRequests)
        .then((products) => {
          productContainer.innerHTML = `
          ${products.map((product) => product.innerHTML).join('')}
        `;
        })
        .then(() => {
          document
            .querySelector(this.selectors.removeAll)
            .classList.add('active');

          productContainer
            .querySelectorAll('[data-product-handle]')
            .forEach((el, index) => {
              el.setAttribute('data-sal-delay', `${250 * index}`);
            });

          this.initButtons(productContainer);

          window.sal.update();
        });
    } else {
      productContainer.innerHTML = this.emptyContent();

      window.sal.update();
    }

    /**
     * When a product is added to wishlist
     */
    document.addEventListener('wishlistItemAdded', (e) => {
      if (this.count === 1) container.innerHTML = '';

      container.insertAdjacentElement('afterbegin', e.detail.product);

      this.initButtons(container);
    });

    /**
     * When a product is removed from wishlist
     */
    document.addEventListener('wishlistItemRemoved', (e) => {
      if (e.detail.handle) {
        productContainer
          .querySelector(`[data-product-handle="${e.detail.handle}"]`)
          .remove();
      }

      if (this.count === 0) {
        productContainer.innerHTML = this.emptyContent();

        window.sal.update();

        document
          .querySelector(this.selectors.removeAll)
          .classList.remove('active');
      }
    });
  }

  async onFormSubmit(e) {
    if (!e.target.matches(this.selectors.form)) return;

    e.preventDefault();

    document.querySelectorAll(`#${e.target.id}`).forEach((form) => {
      form.querySelector(this.selectors.button).classList.add('loading');
    });

    if (
      e.target.querySelector(this.selectors.button).classList.contains('active')
    ) {
      await this.removeWishlistItem(e.target);
    } else {
      await this.addWishlistItem(e.target);
    }

    document.querySelectorAll(`#${e.target.id}`).forEach((form) => {
      form.querySelector(this.selectors.button).classList.remove('loading');
    });
  }

  initButtons(container = document) {
    container.querySelectorAll(this.selectors.form).forEach((form) => {
      form.querySelector(this.selectors.button).classList.remove('loading');
      form.querySelector(this.selectors.button).disabled = false;
    });

    if (this.items && this.items.length > 0) {
      const ids = this.items.map((item) => item.ProductId);

      ids.forEach((id) => {
        document.querySelectorAll(`#wishlist_form_${id}`).forEach((form) => {
          form.querySelector(this.selectors.button).classList.add('active');
        });
      });
    }
  }

  async initWishlist() {
    const hash = await window.WishListHero_SDK.GetWishListHash(true);

    this.hash = hash;
  }

  async getWishlistItems() {
    const items = await window.WishListHero_SDK.GetWishListItems();

    this.items = items;
    this.count = items.length;
  }

  async removeAllItems() {
    window.WishListHero_SDK.DeleteAllWishListItems({
      WishlistHash: this.hash
    }).then((response) => {
      if (!response.ok) {
        throw response;
      }

      const productContainer = document.querySelector(this.selectors.products);

      productContainer.innerHTML = this.emptyContent();

      window.sal.update();

      this.count = 0;

      document
        .querySelector(this.selectors.removeAll)
        .classList.remove('active');
    });
  }

  async addWishlistItem(form) {
    const formData = new FormData(form);

    if (form.hasAttribute('data-save-for-later')) {
      form.dispatchEvent(new Event('savedForLater', { bubbles: true }));
    }

    const data = {
      ProductId: Number(formData.get('ProductId')),
      ProductVariantId: Number(formData.get('ProductVariantId')),
      ProductName: formData.get('ProductName'),
      ProductImage: formData.get('ProductImage'),
      ProductLink: formData.get('ProductLink'),
      ProductHandle: formData.get('ProductHandle'),
      ProductPrice: Number(formData.get('ProductPrice')),
      ProductTitle: formData.get('ProductTitle'),
      WishlistHash: this.hash
    };

    return window.WishListHero_SDK.AddWishListItem(data)
      .then((response) => {
        if (!response.ok) {
          throw response.error;
        }

        if (response.ok) {
          document
            .querySelectorAll(`#wishlist_form_${formData.get('ProductId')}`)
            .forEach((formEl) => {
              formEl
                .querySelector(this.selectors.button)
                .classList.add('active');
            });

          this.getWishlistItems();

          document.dispatchEvent(new CustomEvent('savedForLater'));

          /**
           * Dispatch event for Wishlist page
           */
          document.dispatchEvent(
            new CustomEvent('wishlistItemAdded', {
              detail: {
                product:
                  form.closest(this.selectors.product) !== null
                    ? form.closest(this.selectors.product).cloneNode(true)
                    : null
              },
              bubbles: true
            })
          );
        }
      })
      .catch((error) => {
        this.handleWishlistError(error);
      });
  }

  async removeWishlistItem(form) {
    const formData = new FormData(form);

    document
      .querySelectorAll(`#wishlist_form_${formData.get('ProductId')}`)
      .forEach((formEl) => {
        formEl.querySelector(this.selectors.button).classList.add('loading');
      });

    const data = {
      ProductVariantId: Number(formData.get('ProductVariantId')),
      WishlistHash: this.hash
    };

    return window.WishListHero_SDK.DeleteWishListItem(data)
      .then((response) => {
        if (!response.ok) {
          throw response;
        }

        document
          .querySelectorAll(`#wishlist_form_${formData.get('ProductId')}`)
          .forEach((formEl) => {
            formEl
              .querySelector(this.selectors.button)
              .classList.remove('active');
          });

        this.getWishlistItems();

        /**
         * Dispatch event for Wishlist page
         */
        document.dispatchEvent(
          new CustomEvent('wishlistItemRemoved', {
            detail: {
              handle: formData.get('ProductHandle')
            },
            bubbles: true
          })
        );
      })
      .catch(() => {
        this.handleWishlistError();
      });
  }

  handleWishlistError() {
    new Popup(
      `
        <div class="p-8">
          <h3 class="mb-4 text-xl md:text-2xl">Wishlist error</h3>

          <p class="text-gray-500 mb-6">There was an error when trying to update your wishlist, please try again later.</p>

          <button class="self-start btn-black" data-popup-close>Close</button>
        </div>
      `
    );
  }

  get count() {
    return this._count;
  }

  set count(val) {
    this._count = val;

    document.querySelectorAll(this.selectors.count).forEach((el) => {
      el.textContent = val;
    });

    if (document.querySelector('[data-wishlist-page-count') !== null) {
      document.querySelector(
        '[data-wishlist-page-count'
      ).textContent = `${val} ${val === 1 ? 'item' : 'items'}`;
    }
  }

  emptyContent() {
    return `
      <div class="flex flex-col h-full items-center justify-center col-span-1 md:col-span-3 lg:col-span-4 py-20" data-sal="slide-down" data-sal-duration="500">
        <div class="max-w-[500px] w-full mx-auto">
          <h2 class="text-[18px] md:text-[32px] leading-none text-center">${this.settings.empty_subtitle}</h2>
          <h3 class="text-[40px] md:text-[52px] leading-none text-center mb-6">${this.settings.empty_title}</h3>
        </div>
      </div>
    `;
  }
}

if (!customElements.get('shopify-wishlist')) {
  customElements.define('shopify-wishlist', ShopifyWishlist);
}