@salla.sa/twilight-components
Version:
Salla Web Component
417 lines (416 loc) • 18.2 kB
JavaScript
/*!
* Crafted with ❤ by Salla
*/
import { Host, h } from "@stencil/core";
import Helper from "../../Helpers/Helper";
//todo:: extends this component from salla-products-list or the opposite
export class SallaProductsSlider {
constructor() {
/**
* Custom Card Component for the Salla Products Slider.
*
* This component allows you to customize the appearance of individual product cards within a Salla Products Slider.
*
* @example
* <salla-products-slider product-card-component="my-custom-card-style1" ...
* <salla-products-slider product-card-component="my-custom-card-style2" ...
*/
this.productCardComponent = 'custom-salla-product-card';
this.apiUrl = '';
}
componentWillLoad() {
return salla.onReady()
.then(() => {
//TODO:: check why `this.includes` not working!!
this.includes = Helper.parseJson(this.includes || this.host.getAttribute('includes'));
if (!Array.isArray(this.includes)) {
this.includes = null;
}
Helper.setIncludes(this.includes);
this.sourceValueIsValid = !!(this.getSourceValue() || this.isSourceWithoutValue());
if (!this.sourceValueIsValid) {
salla.logger.warn(`source-value prop is required for source [${this.getSource()}]`);
return;
}
this.hasCustomComponent = !!customElements.get(this.productCardComponent);
if (this.source === 'json') {
this.productsData = this.getSourceValue();
this.isReady = true;
return;
}
if ((this.getSource() == 'related' && !salla.config.get('store.settings.product.related_products_enabled'))) {
this.isReady = false;
return;
}
return salla.api.withoutNotifier(() => salla.product.api.fetch({
source: Helper.getApiSource(this.getSource()),
source_value: this.getSourceValue(),
limit: this.limit
}))
.then(response => Helper.injectExtraFieldsToResponse(response))
.then(response => {
this.productsData = response.data;
this.isReady = true;
response.source = this.getSource();
response.sourceValue = this.getSourceValue();
salla.event.emit('salla-products-slider::products.fetched', response.data);
});
});
}
componentDidRender() {
let processedCount = 0;
const intervalId = setInterval(() => {
this.host.querySelectorAll('[loading="lazy"]')?.forEach(img => img.removeAttribute('loading'));
processedCount++;
if (processedCount >= 10) {
clearInterval(intervalId);
}
}, 1000);
}
async componentDidLoad() {
await Salla.hooks.registerComponent('salla-products-slider', this);
}
isSourceWithoutValue() {
return ['offers', 'latest', 'sales', 'top-rated'].includes(this.getSource());
}
getItemHTML(product) {
//as a request they don't want to let the user to open the product details
//todo:: find a better way to handle this request
const isLandingPage = this.getSource() === 'landing-page';
isLandingPage && (product.url = '');
const handleClick = isLandingPage ? undefined : () => {
Helper.saveProductSource(this.getSource());
};
if (this.hasCustomComponent && this.productCardComponent.toLowerCase() == 'custom-salla-product-card') {
return h("div", { class: "s-products-slider-card", onClick: handleClick }, h("custom-salla-product-card", { product: product, source: this.getSource(), "source-value": this.getSourceValue() }));
}
if (this.hasCustomComponent) {
const customElem = document.createElement(this.productCardComponent);
customElem.setAttribute('product', JSON.stringify(product));
customElem.setAttribute('source', this.getSource());
customElem.setAttribute('source-value', this.getSourceValue());
return h("div", { class: "s-products-slider-card", onClick: handleClick, innerHTML: customElem.outerHTML });
}
return h("div", { class: "s-products-slider-card", onClick: handleClick }, h("salla-product-card", { "show-quantity": isLandingPage, "hide-add-btn": isLandingPage, "shadow-on-hover": true, product: product }));
}
canRender() {
return this.sourceValueIsValid && this.isReady;
}
getSource() {
return Helper.getProductsSource(this.source);
}
getSourceValue() {
return Helper.getProductsSourceValue(this.source, this.sourceValue);
}
render() {
if (!this.canRender()) {
return;
}
return (h(Host, { class: "s-products-slider-wrapper" }, h("salla-slider", { class: "s-products-slider-slider", id: this.sliderId || `s-products-slider-${Math.random().toString(36).substr(2, 9)}`, ...(this.sliderProps || {}), "auto-play": this.autoplay, type: "carousel", "block-title": this.blockTitle, "block-subTitle": this.subTitle, "display-all-url": this.displayAllUrl, sliderConfig: this.sliderConfig ? this.sliderConfig : null }, h("div", { slot: "items" }, this.productsData?.map(product => this.getItemHTML(product))))));
}
static get is() { return "salla-products-slider"; }
static get originalStyleUrls() {
return {
"$": ["salla-products-slider.scss"]
};
}
static get styleUrls() {
return {
"$": ["salla-products-slider.css"]
};
}
static get properties() {
return {
"blockTitle": {
"type": "string",
"attribute": "block-title",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "type",
"text": "{string}"
}, {
"name": "default",
"text": "''"
}],
"text": "Title of the block - works only if slider is true"
},
"getter": false,
"setter": false,
"reflect": false
},
"subTitle": {
"type": "string",
"attribute": "sub-title",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "type",
"text": "{string}"
}, {
"name": "default",
"text": "''"
}],
"text": "Sub title of the block - works only if slider is true"
},
"getter": false,
"setter": false,
"reflect": false
},
"sliderId": {
"type": "string",
"attribute": "slider-id",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "type",
"text": "{string}"
}, {
"name": "default",
"text": "''"
}],
"text": "Slider Id, if not provided will be generated automatically"
},
"getter": false,
"setter": false,
"reflect": false
},
"displayAllUrl": {
"type": "string",
"attribute": "display-all-url",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "type",
"text": "{string}"
}, {
"name": "default",
"text": "''"
}],
"text": "Display 'ALL' URL"
},
"getter": false,
"setter": false,
"reflect": false
},
"autoplay": {
"type": "boolean",
"attribute": "autoplay",
"mutable": true,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "autoplay option for products slider"
},
"getter": false,
"setter": false,
"reflect": false
},
"source": {
"type": "string",
"attribute": "source",
"mutable": true,
"complexType": {
"original": "'categories' | 'latest' | 'related' | 'brands' | 'json' | 'tags' | 'selected' | 'offers' | 'landing-page' | 'wishlist' | 'top-rated' | 'recently'",
"resolved": "\"brands\" | \"categories\" | \"json\" | \"landing-page\" | \"latest\" | \"offers\" | \"recently\" | \"related\" | \"selected\" | \"tags\" | \"top-rated\" | \"wishlist\"",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "type",
"text": "{string}"
}, {
"name": "default",
"text": "''"
}],
"text": "Source of the products, if api will get the products from the API, if json will get the products from the products prop"
},
"getter": false,
"setter": false,
"reflect": true
},
"sourceValue": {
"type": "string",
"attribute": "source-value",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "type",
"text": "{string}"
}],
"text": "The source value, cloud be different values as following:\n- array of ids when `source` in ['categories', 'brands', 'tags', 'selected']\n- products payload when `source` = 'json'\n- product_id when `source` = 'related'"
},
"getter": false,
"setter": false,
"reflect": false
},
"limit": {
"type": "number",
"attribute": "limit",
"mutable": true,
"complexType": {
"original": "number",
"resolved": "number",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Limit for number of products in the list."
},
"getter": false,
"setter": false,
"reflect": false
},
"sliderConfig": {
"type": "any",
"attribute": "slider-config",
"mutable": false,
"complexType": {
"original": "any",
"resolved": "any",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "example",
"text": "let productsSlider = document.querySelector('salla-products-slider');\n productsSlider.sliderConfig = {\n slidesPerView: 1,\n spaceBetween : 30,\n lazy: true,\n}"
}],
"text": "Slider Configs refer to https://swiperjs.com/swiper-api#parameters and pass the entire config object"
},
"getter": false,
"setter": false,
"reflect": true
},
"sliderProps": {
"type": "unknown",
"attribute": "slider-props",
"mutable": false,
"complexType": {
"original": "Partial<SallaSliderProps>",
"resolved": "{ autoPlay?: boolean; loop?: boolean; type?: \"\" | \"default\" | \"blog\" | \"carousel\" | \"fullscreen\" | \"thumbs\" | \"hero\" | \"testimonials\" | \"fullwidth\"; direction?: string; vertical?: boolean; blockTitle?: string; listenToThumbnailsOption?: boolean; blockSubtitle?: string; displayAllUrl?: string; arrowsCentered?: boolean; verticalThumbs?: boolean; gridThumbs?: boolean; autoHeight?: boolean; showControls?: boolean; controlsOuter?: boolean; showThumbsControls?: boolean; staticWhenSingle?: boolean; slidesPerView?: string; pagination?: boolean; centered?: boolean; sliderConfig?: any; thumbsConfig?: any; }",
"references": {
"Partial": {
"location": "global",
"id": "global::Partial"
},
"SallaSliderProps": {
"location": "import",
"path": "../salla-slider/salla-slider",
"id": "src/components/salla-slider/salla-slider.tsx::SallaSliderProps"
}
}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "example",
"text": "productsSlider.sliderProps = { loop: true, pagination: true };"
}],
"text": "Additional props to pass through to the inner salla-slider component.\nUse this to set any salla-slider props (e.g. loop, pagination, showControls, enableSingleSlideMode)."
},
"getter": false,
"setter": false
},
"productCardComponent": {
"type": "string",
"attribute": "product-card-component",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [{
"name": "example",
"text": "<salla-products-slider product-card-component=\"my-custom-card-style1\" ...\n<salla-products-slider product-card-component=\"my-custom-card-style2\" ..."
}],
"text": "Custom Card Component for the Salla Products Slider.\n\nThis component allows you to customize the appearance of individual product cards within a Salla Products Slider."
},
"getter": false,
"setter": false,
"reflect": false,
"defaultValue": "'custom-salla-product-card'"
},
"includes": {
"type": "unknown",
"attribute": "includes",
"mutable": true,
"complexType": {
"original": "string[]",
"resolved": "string[]",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Specifies additional data to be included in the product fetch response.\nThe value can be an array of strings or a JSON string.\n\nExample:\n- As an array: includes=[\"options\"]\n- As a JSON string: includes='[\"options\"]'\n\nThe array or JSON string can include:\n- \"options\": Include product options in the response.\n- \"images\": Include product images in the response."
},
"getter": false,
"setter": false
}
};
}
static get states() {
return {
"productsData": {},
"isReady": {},
"sourceValueIsValid": {},
"hasCustomComponent": {},
"apiUrl": {},
"parsedSourceValue": {}
};
}
static get elementRef() { return "host"; }
}