commercetools-8.x-1.2-alpha1/modules/commercetools_decoupled/js/molecules/ProductDetails.js
modules/commercetools_decoupled/js/molecules/ProductDetails.js
class ProductDetails extends HTMLElement {
constructor() {
super();
// It will be used on loading products.
this.placeholderProduct = {
name: 'Placeholder for title',
masterVariant: {
images: [{ url: '' }],
price: { localizedPrice: '$1000' },
availability: {
noChannel: {
availableQuantity:
window.drupalSettings.commercetoolsDecoupled.unavailableDataText,
},
},
attributes: [
{ name: 'designer', value: { label: 'Placeholder designer' } },
{ name: 'color', value: { label: { en: 'Placeholder color' } } },
{ name: 'style', value: { label: 'Placeholder style' } },
],
},
isLoading: true,
};
this.handleSkuChange = this.handleSkuChange.bind(this);
}
connectedCallback() {
this.renderComponent();
}
renderComponent() {
const product =
this.product.isLoading !== true ? this.product : this.placeholderProduct;
const currentVariant =
product?.variants?.find(
(variant) => variant.sku === this.product.currentSku,
) || product.masterVariant;
this.name = product.name;
this.images = currentVariant?.images;
this.price = currentVariant.price;
this.availability =
currentVariant.availability.noChannel?.availableQuantity || 0;
this.isLoading = product.isLoading;
this.innerHTML = /* html */ `
<article class="product row my-4${this.isLoading ? ' placeholderify' : ''}">
<div class="col-12 col-md-6">
<div class="card-img-wrapper text-center" style="overflow:hidden; height: 100%;">
${
this.isLoading
? `
<img src="${this.image}" alt="${
this.name
}" style="max-height: 24rem; width: 100%; object-fit: contain; height: 100%;" />
`
: ''
}
</div>
</div>
<div class="col-12 col-md-6 mt-4 mt-md-0">
<div class="card-body">
<p class="product-price fs-4">
${
this.price.discounted
? `
<del>${this.price.localizedPrice}</del>
<span class="discount text-danger">${this.price.discounted.localizedPrice}</span>
`
: this.price.localizedPrice
}
</p>
<div class="product-availability mt-2 mb-2"><span>${this.availability} ${Drupal.t('available')}</span></div>
<div class="ct-add-to-cart-form"></div>
</div>
</div>
</article>
`;
if (this.images && !this.isLoading) {
// Build full-screen image popup.
const imageCarousel = document.createElement('ct-carousel');
imageCarousel.items = this.images.map((image) => {
const imgWr = document.createElement('div');
imgWr.innerHTML = `<img src="${image.url}" alt="${image.label || this.name}" style=" width: 100%; object-fit: contain; height: 100%;" />`;
return imgWr;
});
const modal = document.createElement('ct-gallery-modal');
modal.title = this.name;
modal.content = imageCarousel;
document.body.appendChild(modal);
// Build main product image carousel.
const modalSelector = modal.getSelector();
const carousel = document.createElement('ct-carousel');
carousel.items = this.images.map((image, k) => {
const button = document.createElement('button');
button.className = 'btn btn-link';
button.setAttribute('data-bs-toggle', 'modal');
button.setAttribute('data-bs-target', `#${modalSelector}`);
button.innerHTML = `<img src="${image.url}" alt="${image.label || this.name}" style="max-height: 24rem; width: 100%; object-fit: contain; height: 100%;"/>`;
// Open a full-screen pop-up with a carousel of product images.
button.addEventListener('click', (e) => {
e.preventDefault();
imageCarousel.setActiveSlider(k);
});
return button;
});
this.querySelector('.card-img-wrapper').append(carousel);
}
if (this.product.isLoading !== true) {
const addToCartForm = document.createElement('ct-add-to-cart-form');
addToCartForm.product = this.product;
addToCartForm.handleSkuChange = this.handleSkuChange;
this.querySelector('.ct-add-to-cart-form').appendChild(addToCartForm);
}
}
async handleSkuChange() {
this.renderComponent();
}
}
customElements.define('ct-product-details', ProductDetails);
