UNPKG

@salla.sa/twilight-components

Version:
854 lines (835 loc) 84.8 kB
/*! * Crafted with ❤ by Salla */ 'use strict'; var index = require('./index-uoA36zqH.js'); var check = require('./check-BnVBPQNp.js'); var anime_es = require('./anime.es-BqW8JHZi.js'); var Helper = require('./Helper-CU4Xuiki.js'); var star2 = require('./star2-R146a27p.js'); var arrowLeft = require('./arrow-left-xSEmRpp6.js'); var shoppingBag = require('./shopping-bag-C1gvTVHr.js'); const sallaCommentFormCss = ":host{display:block}"; const SallaCommentForm = class { constructor(hostRef) { index.registerInstance(this, hostRef); this.commentAdded = index.createEvent(this, "commentAdded"); this.placeholder = salla.lang.get('blocks.comments.placeholder'); this.submitText = salla.lang.get('blocks.comments.submit'); salla.lang.onLoaded(() => { this.placeholder = salla.lang.get('blocks.comments.placeholder'); this.submitText = salla.lang.get('blocks.comments.submit'); }); salla.onReady(() => { this.canComment = salla.config.get('user.can_comment'); this.itemId = salla.config.get('page.id'); this.type = salla.url.is_page('page-single') ? 'page' : salla.url.is_page('blog.single') ? 'blog' : 'product'; }); } submit() { if (!this.commentForm.reportValidity()) { salla.log('CommentForm:: validation error!'); return; } const commentText = this.commentField.value; this.submitBtn.load() .then(() => salla.comment.add({ id: this.itemId, comment: commentText, type: this.type })) .then(() => { this.commentAdded.emit({ name: salla.config.get('user.name'), avatar: salla.config.get('user.avatar'), content: commentText, is_pending: true, created_at: { date: new Date().toISOString().replace('T', ' ').substring(0, 19) }, images: [], }); this.commentField.value = ''; }) .finally(() => this.submitBtn.stop()); } render() { return (index.h(index.Host, { key: '0ec765c8dd7cf28d23c4305c234cec5efd3daae1' }, !!this.canComment ? index.h("form", { ref: frm => this.commentForm = frm }, index.h("div", { class: "s-comment-form-wrapper" }, this.showAvatar ? index.h("img", { class: "s-comment-form-avatar", src: salla.config.get('user.avatar'), alt: "user avatar" }) : '', index.h("div", { class: "s-comment-form-content" }, index.h("textarea", { cols: 30, rows: 5, minlength: "4", maxlength: "500", ref: field => this.commentField = field, placeholder: this.placeholder, class: "s-comment-form-input", required: true }), index.h("br", null), index.h("div", { class: "s-comment-form-action" }, index.h("salla-button", { ref: btn => this.submitBtn = btn, "loader-position": 'center', onClick: () => this.submit() }, this.submitText))))) : '')); } }; SallaCommentForm.style = sallaCommentFormCss; var Reply = `<!-- Generated by IcoMoon.io --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <title>reply</title> <path d="M20 14.667h-12.609l9.401-6.927c0.592-0.436 0.72-1.271 0.283-1.863-0.439-0.595-1.273-0.72-1.864-0.283l-12.667 9.333c-0.343 0.251-0.544 0.649-0.544 1.072s0.201 0.821 0.543 1.073l12.667 9.333c0.237 0.176 0.515 0.26 0.789 0.26 0.409 0 0.813-0.188 1.075-0.543 0.437-0.592 0.309-1.427-0.283-1.863l-9.4-6.928h12.609c4.412 0 8 3.588 8 8 0 0.737 0.597 1.333 1.333 1.333s1.333-0.596 1.333-1.333c0-5.881-4.785-10.667-10.667-10.667z"></path> </svg> `; var ThumbsUp = `<!-- Generated by IcoMoon.io --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <title>thumbs-up</title> <path d="M26.667 10.667h-8.261l1.279-3.38c0.723-1.911 0.087-4.051-1.549-5.203-0.909-0.639-2.004-0.881-3.085-0.684-1.101 0.203-2.061 0.837-2.703 1.787l-5.452 8.067c-0.148 0.22-0.228 0.48-0.228 0.747v12c0 3.676 2.991 6.667 6.667 6.667h8.688c2.535 0 4.817-1.407 5.955-3.671l2.548-5.063c0.093-0.187 0.143-0.392 0.143-0.6v-6.667c0-2.205-1.795-4-4-4zM28 21.017l-2.405 4.78c-0.683 1.359-2.052 2.203-3.573 2.203h-8.688c-2.205 0-4-1.795-4-4v-11.592l5.223-7.728c0.237-0.352 0.584-0.585 0.975-0.657 0.367-0.067 0.749 0.017 1.068 0.241 0.631 0.444 0.879 1.317 0.591 2.079l-1.963 5.185c-0.156 0.409-0.099 0.869 0.149 1.229s0.66 0.576 1.099 0.576h10.192c0.735 0 1.333 0.599 1.333 1.333zM2.667 10.667c-0.736 0-1.333 0.597-1.333 1.333v16c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v-16c0-0.736-0.597-1.333-1.333-1.333z"></path> </svg> `; const sallaCommentsCss$1 = ":host{display:block}"; const SallaCommentItem = class { constructor(hostRef) { index.registerInstance(this, hostRef); // Translations this.locked_trans = salla.lang.get('blocks.comments.locked'); this.has_bought_trans = salla.lang.get('blocks.comments.has_bought'); this.rated_trans = salla.lang.get('pages.rating.rated'); this.waiting_approval_trans = salla.lang.get('blocks.comments.waiting_approval'); this.has_order_trans = salla.lang.get('blocks.comments.has_order'); this.allowLikes = salla.config.get('store.settings.rating.allow_likes'); this.allowAttachImages = salla.config.get('store.settings.rating.allow_attach_images'); this.helpfulLabel = salla.lang.getWithDefault('blocks.comments.helpful', 'مفيد'); this.likesCount = 0; this.likedComments = []; salla.lang.onLoaded(() => { const setNestedAsync = (lang, key, value) => { return new Promise((resolve) => { salla.helpers.setNested(salla.lang.messages[lang], key, value); resolve(true); }); }; const setTranslations = async () => { await setNestedAsync('ar.trans', 'blocks.comments.helpful', 'مفيد'); await setNestedAsync('en.trans', 'blocks.comments.helpful', 'Helpful'); await setNestedAsync('ar.trans', 'blocks.comments.locked', 'الرد غير ظاهر لباقي العملاء'); await setNestedAsync('en.trans', 'blocks.comments.locked', 'The reply is not visible to other customers'); this.helpfulLabel = salla.lang.getWithDefault('blocks.comments.helpful', 'مفيد'); this.locked_trans = salla.lang.getWithDefault('blocks.comments.locked', 'الرد غير ظاهر لباقي العملاء'); }; this.locked_trans = salla.lang.get('blocks.comments.locked'); this.has_bought_trans = salla.lang.get('blocks.comments.has_bought'); this.rated_trans = salla.lang.get('pages.rating.rated'); this.waiting_approval_trans = salla.lang.get('blocks.comments.waiting_approval'); this.has_order_trans = salla.lang.get('blocks.comments.has_order'); setTranslations(); }); } componentDidLoad() { if (!this.comment) return; this.likesCount = this.comment.likes_count; try { this.likedComments = JSON.parse(localStorage.getItem('liked_comments') || '[]'); if (this.likedComments.includes(this.comment.id)) { this.likeBtn.classList.add('liked'); this.likeBtn.fill = "solid"; } } catch { salla.log('Bad json for liked_comments'); } } getReplies() { return Array.isArray(this.comment.replies) ? this.comment.replies : [this.comment.replies]; } getDate(dateString) { const [datePart] = dateString.split(' '); const [year, month, day] = datePart.split('-'); const formattedDate = `${parseInt(day, 10)}/${parseInt(month, 10)}/${parseInt(year, 10)}`; return formattedDate; } getTime(dateString) { const [, timePart] = dateString.split(' '); const [hours, minutes] = timePart.split(':'); const formattedTime = `${parseInt(hours, 10)}:${parseInt(minutes, 10)}`; return formattedTime; } async toggleLike() { if (salla.config.isGuest()) { return salla.notify.error(salla.lang.get('common.messages.must_login')); } this.likedComments = JSON.parse(localStorage.getItem('liked_comments') || '[]'); const isLiked = this.likedComments.includes(this.comment.id); try { const endpoint = isLiked ? `rating/${this.comment.id}/unlike` : `rating/${this.comment.id}/like`; const res = await salla.api.request(endpoint, '', 'put'); salla.log(res.message); if (isLiked) { this.likeBtn.classList.remove('liked'); this.likeBtn.fill = 'outline'; this.updateLikedComments(this.comment.id, false); this.likesCount--; } else { this.likeBtn.classList.add('liked'); this.likeBtn.fill = 'solid'; this.updateLikedComments(this.comment.id, true); this.likesCount++; } } catch (e) { if (e.response.status == 409) { if (this.likeBtn.classList.contains('liked')) { this.likeBtn.fill = 'outline'; this.likeBtn.classList.remove('liked'); this.updateLikedComments(this.comment.id, false); this.likesCount--; } else { this.likeBtn.fill = 'solid'; this.likeBtn.classList.add('liked'); salla.logger.warn('Like already exists'); this.updateLikedComments(this.comment.id, true); } } } } updateLikedComments(commentId, add) { this.likedComments = JSON.parse(localStorage.getItem('liked_comments') || '[]'); if (add) { if (!this.likedComments.includes(commentId)) { this.likedComments.push(commentId); } } else { this.likedComments = this.likedComments.filter(id => id !== commentId); } localStorage.setItem('liked_comments', JSON.stringify(this.likedComments)); } isPrivateComment(comment) { return comment.status === 'hidden'; } PinnedIcon() { return (index.h("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", width: "16", height: "16", color: "#555555", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }, index.h("path", { d: "M12 16V21" }), index.h("path", { d: "M8 5.2918C8 5.02079 8 4.88529 8.01312 4.77132C8.1194 3.84789 8.84789 3.1194 9.77133 3.01312C9.88529 3 10.0208 3 10.2918 3H13.7082C13.9792 3 14.1147 3 14.2287 3.01312C15.1521 3.1194 15.8806 3.84789 15.9869 4.77132C16 4.88529 16 5.02079 16 5.2918C16 5.37885 16 5.42237 15.9967 5.46264C15.9708 5.78281 15.7927 6.07104 15.5179 6.2374C15.4834 6.25832 15.4444 6.27779 15.3666 6.31672L15.1055 6.44726C14.7021 6.64897 14.5003 6.74983 14.3681 6.90564C14.26 7.03286 14.1856 7.18509 14.1515 7.34846C14.1097 7.54854 14.1539 7.76968 14.2424 8.21197L15 12H15.3333C15.9533 12 16.2633 12 16.5176 12.0681C17.2078 12.2531 17.7469 12.7922 17.9319 13.4824C18 13.7367 18 14.0467 18 14.6667C18 14.9767 18 15.1317 17.9659 15.2588C17.8735 15.6039 17.6039 15.8735 17.2588 15.9659C17.1317 16 16.9767 16 16.6667 16H7.33333C7.02334 16 6.86835 16 6.74118 15.9659C6.39609 15.8735 6.12654 15.6039 6.03407 15.2588C6 15.1317 6 14.9767 6 14.6667C6 14.0467 6 13.7367 6.06815 13.4824C6.25308 12.7922 6.79218 12.2531 7.48236 12.0681C7.73669 12 8.04669 12 8.66667 12H9L9.75761 8.21197C9.84606 7.76968 9.89029 7.54854 9.84852 7.34846C9.81441 7.18509 9.73995 7.03286 9.63194 6.90564C9.49965 6.74983 9.29794 6.64897 8.89452 6.44726L8.63344 6.31672C8.55558 6.27779 8.51665 6.25832 8.48208 6.2374C8.20731 6.07104 8.02917 5.78281 8.00326 5.46264C8 5.42237 8 5.37885 8 5.2918Z" }))); } LockedIcon() { return (index.h("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, index.h("path", { d: "M3.5 4.66683V4.0835C3.5 2.1505 5.067 0.583496 7 0.583496C8.93299 0.583496 10.5 2.1505 10.5 4.0835V4.66683H11.6667C11.9888 4.66683 12.25 4.928 12.25 5.25016V12.2502C12.25 12.5723 11.9888 12.8335 11.6667 12.8335H2.33333C2.01117 12.8335 1.75 12.5723 1.75 12.2502V5.25016C1.75 4.928 2.01117 4.66683 2.33333 4.66683H3.5ZM11.0833 5.8335H2.91667V11.6668H11.0833V5.8335ZM6.41667 9.1774C6.06795 8.97568 5.83333 8.59867 5.83333 8.16683C5.83333 7.52248 6.35565 7.00016 7 7.00016C7.64435 7.00016 8.16667 7.52248 8.16667 8.16683C8.16667 8.59867 7.93205 8.97568 7.58333 9.1774V10.5002H6.41667V9.1774ZM4.66667 4.66683H9.33333V4.0835C9.33333 2.79483 8.28864 1.75016 7 1.75016C5.71134 1.75016 4.66667 2.79483 4.66667 4.0835V4.66683Z", fill: "#555555" }))); } render() { if (!this.comment) { return null; } let isAdmin = this.comment.type == 'admin'; return (index.h(index.Host, { class: isAdmin ? 's-comments-item-admin' : 's-comments-item' }, index.h("div", { class: { "s-comments-item-wrapper": !isAdmin, "s-comments-item-admin-wrapper": isAdmin }, id: `s-comments-item-${this.comment.id}` }, index.h("div", { class: "s-comments-item-inner s-comments-flex-1" }, isAdmin && index.h("span", { class: "s-comments-item-reply-icon", innerHTML: Reply }), index.h("div", { class: "s-comments-item-avatar" }, index.h("img", { "data-src": this.comment?.avatar, alt: this.comment?.name, src: this.comment?.avatar, class: "s-comments-item-avatar-img lazy" })), index.h("div", { class: "s-comments-flex-1" }, index.h("div", { class: "s-comments-item-user-wrapper" }, index.h("div", { class: "s-comments-item-user-info" }, index.h("h3", { class: this.comment.is_pinned ? "s-comments-item-user-info-name" : "s-comments-item-user-info-name-with-margin" }, this.comment?.name), this.comment.is_pinned ? index.h("div", { class: "s-comments-item-pinned-icon-with-margin" }, this.PinnedIcon()) : null, (this.comment.has_order || !!this.comment.rating) && !this.comment.is_pending && !this.hideBought ? index.h("div", { class: "s-comments-flex" }, this.comment.has_order ? [index.h("span", { class: "s-comments-item-has-order-check-icon", innerHTML: check.SICheck }), index.h("span", { class: "s-comments-item-has-order-check-text" }, this.has_bought_trans, " ", this.comment.rating ? ', ' : '')] : null, !!this.comment.rating ? index.h("span", { class: "s-comments-item-rated-widget" }, this.rated_trans) : null) : null), index.h("p", { class: "s-comments-item-timestamp s-ltr" }, this.getDate(this.comment.created_at?.date), index.h("span", { class: "s-comments-item-time" }, " - ", this.getTime(this.comment.created_at?.date))), !!this.comment.rating || !!this.comment.stars ? index.h("salla-rating-stars", { size: "mini", class: "s-comments-item-stars", value: this.comment.rating || this.comment.stars }) : null), index.h("div", { class: "s-comments-item-content" }, index.h("div", { class: "s-comments-item-content-container" }, this.isPrivateComment(this.comment) ? (index.h("div", { id: `s-comments-item-${this.comment.id}-locked-icon`, class: "s-comments-item-pinned-icon" }, this.LockedIcon())) : null, index.h("p", { innerHTML: this.comment.content })), this.isPrivateComment(this.comment) ? (index.h("salla-tooltip", { text: this.locked_trans, targetId: `s-comments-item-${this.comment.id}-locked-icon` })) : null, this.allowAttachImages && index.h("div", { class: "s-comments-item-images" }, this.comment.images.map((image, index$1) => (index.h("img", { key: index$1, src: image, alt: "", onClick: () => this.modal.open() }))), index.h("salla-modal", { ref: modal => this.modal = modal, width: "sm" }, index.h("salla-slider", { id: `s-comments-item-${this.comment.id}-images`, class: "s-comments-item-images-slider", type: "thumbs", "auto-height": true, showControls: this.comment.images.length > 1 ? true : false, "show-thumbs-controls": "false" }, index.h("div", { slot: 'items' }, this.comment.images.map((image, index$1) => (index.h("img", { key: index$1, src: image, alt: "" })))), index.h("div", { slot: "thumbs" }, this.comment.images.map((image, index$1) => (index.h("div", { class: "s-comments-item-images-slider-thumb" }, index.h("img", { key: index$1, src: image, alt: "" })))))))), this.allowLikes && !isAdmin && !!this.comment.id && salla.url.is_page('product.single') ? index.h("salla-button", { ref: el => this.likeBtn = el, class: `s-comments-item-like-btn ${this.likedComments.includes(this.comment.id) ? 'liked' : ''}`, loaderPosition: 'center', fill: 'outline', size: 'small', onClick: () => this.toggleLike() }, index.h("span", null, this.helpfulLabel, " ", this.likesCount > 0 ? `(${this.likesCount})` : ''), index.h("span", { innerHTML: ThumbsUp })) : '', this.comment.is_pending ? index.h("span", { class: "s-comments-item-pending-text" }, this.waiting_approval_trans) : null))), !!this.getReplies().length && !isAdmin ? this.getReplies().map((reply) => { return index.h("div", null, index.h("salla-comment-item", { comment: reply })); }) : null))); } get host() { return index.getElement(this); } }; SallaCommentItem.style = sallaCommentsCss$1; var CommentType; (function (CommentType) { CommentType["PAGE"] = "page"; CommentType["PRODUCT"] = "product"; CommentType["BLOG"] = "blog"; })(CommentType || (CommentType = {})); var ChatBubbles = `<!-- Generated by IcoMoon.io --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <title>chat-bubbles</title> <path d="M15.333 17.333c4.779 0 8.667-3.888 8.667-8.667s-3.888-8.667-8.667-8.667h-6.667c-4.779 0-8.667 3.888-8.667 8.667 0 2.985 1.513 5.712 4 7.297v4.703c0 0.497 0.277 0.953 0.717 1.183 0.195 0.1 0.405 0.151 0.616 0.151 0.269 0 0.536-0.081 0.764-0.241l6.323-4.425zM11.236 14.908l-4.569 3.199v-2.913c0-0.5-0.28-0.959-0.725-1.187-2.020-1.035-3.275-3.080-3.275-5.34 0-3.308 2.692-6 6-6h6.667c3.308 0 6 2.692 6 6s-2.692 6-6 6h-3.333c-0.273 0-0.54 0.084-0.764 0.241zM29.196 12.964c-0.543-0.5-1.388-0.464-1.884 0.077-0.5 0.541-0.465 1.385 0.077 1.884 1.253 1.156 1.944 2.72 1.944 4.408 0 2.26-1.255 4.305-3.275 5.339-0.445 0.228-0.725 0.687-0.725 1.188v2.572l-5.441-2.939c-0.195-0.104-0.412-0.16-0.633-0.16h-2.592c-1.688 0-3.309-0.724-4.451-1.988-0.492-0.545-1.335-0.591-1.883-0.096-0.547 0.493-0.589 1.336-0.096 1.883 1.644 1.823 3.988 2.868 6.429 2.868h2.255l7.111 3.84c0.199 0.107 0.417 0.16 0.635 0.16 0.236 0 0.472-0.063 0.683-0.188 0.404-0.241 0.651-0.676 0.651-1.145v-4.036c2.487-1.585 4-4.311 4-7.297 0-2.407-1.023-4.727-2.804-6.369z"></path> </svg> `; const sallaCommentsCss = ":host{display:block}"; const SallaComments = class { constructor(hostRef) { index.registerInstance(this, hostRef); /** * Comment Type */ this.type = CommentType.PAGE; /** * Show or hide avatar */ this.showFormAvatar = false; /** * Hide Bought */ this.hideBought = false; /** * Determines if the comments are testimonials */ this.testimonials = false; this.customerId = null; // Translations this.noComments = salla.lang.get('blocks.comments.no_comments'); this.comment_title = salla.lang.get('blocks.comments.title'); this.comment_name = salla.lang.get('blocks.comments.comment'); this.showRatingSummary = salla.config.get('store.settings.rating.show_rating_summary'); this.allowLikes = salla.config.get('store.settings.rating.allow_likes'); salla.onReady(() => { this.allowLikes = salla.config.get('store.settings.rating.allow_likes'); this.showRatingSummary = salla.config.get('store.settings.rating.show_rating_summary'); }); salla.lang.onLoaded(() => { this.comment_title = salla.lang.get('blocks.comments.title'); this.comment_name = salla.lang.get('blocks.comments.comment'); this.noComments = salla.lang.get('pages.rating.no_ratings'); const setNestedAsync = (lang, key, value) => { return new Promise((resolve) => { salla.helpers.setNested(salla.lang.messages[lang], key, value); resolve(true); }); }; const setTranslations = async () => { await setNestedAsync('ar.trans', 'blocks.comments.most_helpful', 'الأكثر إفادة'); await setNestedAsync('en.trans', 'blocks.comments.most_helpful', 'Most helpful'); this.mostHelpfulLabel = salla.lang.get('blocks.comments.most_helpful'); this.comment_title = salla.lang.get('blocks.comments.title'); this.comment_name = salla.lang.get('blocks.comments.comment'); this.noComments = salla.lang.get('pages.rating.no_ratings'); }; setTranslations(); }); } // TOOD: it's a good idea to move this into lang.js // Pluralize a string based on the count pluralize(phrases, count) { const options = phrases.split('|'); const conditions = [ { condition: count === 0, index: 0 }, { condition: count === 1, index: 1 }, { condition: count === 2, index: 2 }, { condition: count > 2 && count <= 10, index: 3 }, { condition: count >= 11, index: 4 } ]; const { index } = conditions.find(({ condition }) => condition) || { index: options.length - 1 }; const selectedOption = options[index]; return selectedOption.replace(':count', salla.helpers.number(count.toString())) .replace(/\{[0-9]+\}/g, '') .replace(/\[\d+,\d+\]|\[11,\*\]/g, ''); } wrapConsoleError() { if (Salla.infiniteScroll.errorWrapped) { return; } (() => { const orig = console.error.bind(console); console.error = (...args) => { const msg = args[0]; // only rewrite the noisy one if (typeof msg === 'string' && msg.toLowerCase().replace(/\s/g, '').includes('infinitescroll')) { return console.log(...args); // downgrade to log } return orig(...args); // keep real errors }; })(); Salla.infiniteScroll.errorWrapped = true; } // Preserve sort and private_comments params in next page URL for infinite scroll patchNextPageUrl() { if (!this.nextPage) return; try { const url = new URL(this.nextPage, window.location.origin); if (this.sort && !url.searchParams.get('sort')) { url.searchParams.set('sort', this.sort); } if (this.customerId && !url.searchParams.get('private_comments')) { url.searchParams.set('private_comments', this.customerId); } this.nextPage = url.toString(); } catch (_e) { // fallback for relative next links const hasQuery = this.nextPage.includes('?'); if (this.sort && !/[?&]sort=/.test(this.nextPage)) { this.nextPage = this.nextPage + (hasQuery ? '&' : '?') + `sort=${this.sort}`; } if (this.customerId && !/[?&]private_comments=/.test(this.nextPage)) { const sep = this.nextPage.includes('?') ? '&' : '?'; this.nextPage = this.nextPage + sep + `private_comments=${this.customerId}`; } } } // Initiate infinite scroll initiateInfiniteScroll() { if (!this.wrapper) { console.error('Wrapper is undefined. Cannot initiate infinite scroll.'); return; } this.wrapConsoleError(); this.infiniteScroll = salla.infiniteScroll.initiate(this.wrapper, this.wrapper, { path: () => this.nextPage, history: false, nextPage: this.nextPage, scrollThreshold: false, }, true); this.infiniteScroll?.on('request', _response => { this.loading(); }); this.infiniteScroll?.on('load', response => { this.pagination = response.pagination; this.nextPage = typeof response.pagination.links === 'object' && !!response.pagination.links.next ? response.pagination.links.next : null; this.patchNextPageUrl(); for (const card of this.handleResponse(response)) { this.wrapper.append(card); } const items = this.host.querySelectorAll('salla-comment-item:not(.animated):not(.s-comments-item-admin)'); this.animateItems(items); this.loading(false); }); this.infiniteScroll?.on('error', (e) => { salla.console.error('Error loading more comments:', e); }); } // Show/hide loading loading(isLoading = true) { const btnText = this.status?.querySelector('.s-button-text'); if (btnText) { Helper.Helper.toggleElementClassIf(btnText, 's-button-hide', 's-button-show', () => isLoading); this.btnLoader.style.display = isLoading ? 'inherit' : 'none'; } } // Animate newly added items animateItems(items) { anime_es.anime({ targets: items, opacity: [0, 1], duration: 1200, translateY: [20, 0], delay: (_el, i) => i * 100, easing: 'easeOutExpo', complete: (_anim) => { for (const item of items) { item.classList.add('animated'); } } }); } /** * Reloads the comments data from the server */ async reload() { this.showPlaceholder = false; if (this.wrapper) { this.wrapper.innerHTML = ""; const loading = document.createElement('salla-loading'); this.wrapper.append(loading); } this.nextPage = null; this.loadInitialData(); } // Get comment item HTML getCommentHTML(comment) { const commentItem = document.createElement('salla-comment-item'); commentItem.comment = comment; commentItem.hideBought = this.hideBought; return commentItem; } // Parse response and return an array of comment items to be appended to the wrapper handleResponse(response) { return response.data?.map(comment => this.getCommentHTML(comment)) || []; } componentWillLoad() { return salla.onReady() .then(() => { this.showRatingSummary = salla.config.get('store.settings.rating.show_rating_summary'); }) .then(() => this.loading()) .then(() => { this.hideTitle = this.hideTitle || this.testimonials; this.hideForm = this.hideForm || this.testimonials; return this.loadInitialData(); }); } /** * Catch the commentAdded event emitted by salla-comment-form and instantly render it */ onCommentAdded(event) { const comment = event.detail; if (!comment) return; // If comments were empty, clear the placeholder and initialize the list if (this.showPlaceholder || !this.total) { this.showPlaceholder = false; this.total = (this.total || 0) + 1; this.comments = this.comments || []; } else { this.total = (this.total || 0) + 1; } // Wait for next render tick so wrapper is available if placeholder was just removed setTimeout(() => { if (!this.wrapper) return; const commentItem = this.getCommentHTML(comment); this.wrapper.prepend(commentItem); this.animateItems([commentItem]); }, 0); } // Load initial data async loadInitialData() { try { let resp = { data: [], pagination: {} }; const searchParams = new URLSearchParams(window.location.search); if (searchParams.has('sort')) { this.sort = searchParams.get('sort'); } if (this.testimonials) { const params = { sort: this.sort, type: "store" }; resp = await salla.api.request('reviews', { params }, 'get'); } else { const showPrivateComments = searchParams.get('private_comments'); this.customerId = (showPrivateComments && Salla.config.get('user.type') === 'user' && Salla.config.get('user.id')) ? Salla.config.get('user.id') : null; resp = await salla.api.comment.getComments(this.type, this.itemId, 1, 5, this.sort, this.customerId); } if (!resp.data || !resp.data.length) { this.showPlaceholder = false; this.loading(false); return; } if (this.wrapper) { this.wrapper.innerHTML = ""; } this.comments = resp.data; this.pagination = resp.pagination; this.total = resp.pagination.total; this.nextPage = typeof resp.pagination.links === 'object' && !!resp.pagination.links.next ? resp.pagination.links.next : null; this.patchNextPageUrl(); setTimeout(() => { for (const card of this.handleResponse(resp)) { this.wrapper.append(card); } this.initiateInfiniteScroll(); // Initiate infinite scroll after the initial data is loaded const items = this.wrapper.querySelectorAll('salla-comment-item:not(.animated)'); this.animateItems(items); }, 100); } catch (error) { console.error('Error loading initial data:', error); this.showPlaceholder = true; this.loading(false); } } // Get next page async loadMore() { this.infiniteScroll?.loadNextPage(); } render() { // We should show a different placeholder for pages and products (WIP) if (this.showPlaceholder) { return (index.h("div", null, !!this.total && !this.hideTitle ? index.h("h2", { class: "s-comments-title" }, this.blockTitle ? this.blockTitle : this.comment_title) : '', !this.hideForm && !this.testimonials ? index.h("salla-comment-form", { showAvatar: this.showFormAvatar, type: this.type, "item-id": this.itemId }) : '', index.h("div", { class: "s-comments-placeholder" }, index.h("span", { innerHTML: ChatBubbles }), index.h("p", null, this.noComments)))); } return (index.h("div", { class: `s-comments s-comments-${this.testimonials ? 'testimonials' : this.type}` }, index.h("div", { class: `${this.type === CommentType.PAGE ? "s-comments-page-container" : "s-comments-container"}` }, !!this.total && !this.hideTitle ? index.h("h2", { class: "s-comments-title" }, this.blockTitle ? this.blockTitle : this.comment_title) : '', !this.hideForm && index.h("salla-comment-form", { showAvatar: this.showFormAvatar, type: this.type, "item-id": this.itemId }), salla.url.is_page('product.single') ? index.h("salla-reviews-summary", { itemId: this.itemId }) : '', index.h("div", { class: `s-comments-header ${this.total ? "has-total" : ""}` }, !!this.total && index.h("span", { class: "s-comments-count-label", innerHTML: this.pluralize(this.comment_name, this.total) }), !!this.total && !this.testimonials && this.type !== CommentType.BLOG ? index.h("div", { class: "s-comments-filter-wrapper" }, index.h("label", { class: "s-comments-filter-label", htmlFor: "comments-filter" }, salla.lang.get('pages.categories.sorting')), index.h("select", { id: "comments-filter", "aria-label": salla.lang.get('pages.categories.sorting'), class: "s-form-control s-comments-sort-input", onChange: (e) => { this.sort = e.target.value; this.reload(); } }, index.h("option", { value: "latest", selected: true }, salla.lang.get("pages.testimonials.sort_by_date_desc")), index.h("option", { value: "oldest" }, salla.lang.get("pages.testimonials.sort_by_date_asc")), this.allowLikes && index.h("option", { value: "most_helpful" }, this.mostHelpfulLabel))) : ''), index.h("div", { ref: wrapper => { this.wrapper = wrapper; } }), this.nextPage && (index.h("div", { class: "s-infinite-scroll-wrapper", ref: status => { this.status = status; } }, index.h("button", { onClick: () => this.loadMore(), class: "s-infinite-scroll-btn s-button-btn s-button-primary", type: "button" }, index.h("span", { class: "s-button-text s-infinite-scroll-btn-text" }, this.loadMoreText ? this.loadMoreText : salla.lang.get('common.elements.load_more')), index.h("span", { class: "s-button-loader s-button-loader-center s-infinite-scroll-btn-loader", ref: btnLoader => { this.btnLoader = btnLoader; }, style: { "display": "none" } }))))))); } get host() { return index.getElement(this); } }; SallaComments.style = sallaCommentsCss; const sallaRatingStarsCss = ""; const SallaRatingStars = class { constructor(hostRef) { index.registerInstance(this, hostRef); this.translationsLoaded = false; this.labels = []; this.reviewLabel = ''; this.selectedStar = 0; /** * Sets input name. */ this.name = 'rating'; /** * Sets the height and width of the component. Defaults to medium. */ this.size = 'medium'; /** * Number of reviews to display. */ this.reviews = 0; /** * Allows the rating to be editable. */ this.editable = false; } async componentWillLoad() { await new Promise(resolve => { salla.lang.onLoaded(() => { this.labels = [ salla.lang.get('pages.rating.poor'), salla.lang.get('pages.rating.average'), salla.lang.get('pages.rating.good'), salla.lang.get('pages.rating.very_good'), salla.lang.get('pages.rating.excellent') ]; if (this.value && this.withLabel) { this.reviewLabel = this.labels[this.value - 1]; } if (this.reviewsElement) { this.reviewsElement.innerText = `(${salla.helpers.number(salla.lang.choice('pages.rating.reviews', this.reviews))})`; } this.translationsLoaded = true; resolve(); }); }); } initiateRating() { this.host.addEventListener('click', this.handleRating.bind(this)); } handleRating() { if (!this.starsElem) return; let activeStars = this.starsElem.querySelectorAll('.s-rating-stars-hovered'); let selected = activeStars[activeStars.length - 1]; if (!selected) return; let selectedIndex = parseInt(selected.getAttribute('data-star')); this.starsElem.querySelector('.rating_hidden_input').value = selectedIndex.toString(); this.starsElem.querySelectorAll('.s-rating-stars-btn-star') .forEach((star, index) => Helper.Helper.toggleElementClassIf(star, 's-rating-stars-selected', 's-rating-stars-unselected', () => index < selectedIndex)); this.starsElem.querySelectorAll('[aria-pressed]').forEach(star => star.removeAttribute('aria-pressed')); selected.setAttribute('aria-pressed', 'true'); this.selectedStar = selectedIndex; this.withLabel && (this.reviewLabel = this.labels[selectedIndex - 1]); } triggerRatingProgrammatically(index) { if (!this.starsElem) return; const stars = this.starsElem.querySelectorAll('.s-rating-stars-btn-star'); if (stars && index >= 0 && index <= stars.length) { // Simulate the hovering effect stars.forEach((s, i) => { s.classList.toggle('s-rating-stars-hovered', i <= index); }); // Trigger the same logic as clicking this.handleRating(); } } highlightSelectedStars() { let hoveredClass = 's-rating-stars-hovered', stars = this.starsElem?.querySelectorAll('.s-rating-stars-btn-star'); stars?.forEach((star, index) => { star.addEventListener('mouseover', () => { for (let i = 0; i <= index; i++) { stars[i].classList.add(hoveredClass); } this.withLabel && (this.reviewLabel = this.labels[index]); }); star.addEventListener('mouseout', () => { star.classList.remove(hoveredClass); this.withLabel && (this.reviewLabel = this.selectedStar ? this.labels[this.selectedStar - 1] : ''); }); }); this.starsElem?.addEventListener('mouseout', () => stars.forEach(star => star.classList.remove(hoveredClass))); } createStars(n) { let stars = []; for (let i = 0; i < 5; i++) { stars.push(index.h("span", { class: { 's-rating-stars-btn-star': true, ['s-rating-stars-' + this.size]: true, 's-rating-stars-selected': i < n }, innerHTML: star2.Star })); } if (this.reviews > 0) { stars.push(index.h("span", { class: "s-rating-stars-reviews", ref: el => this.reviewsElement = el }, "(", salla.helpers.number(salla.lang.choice('pages.rating.reviews', this.reviews)), ")")); } return stars; } render() { return this.translationsLoaded ? (this.host.closest('.swiper-slide')?.classList.contains('swiper-slide-duplicate') ? '' : (index.h(index.Host, null, (this.value || this.value == 0) && !this.editable ? index.h("div", { class: "s-rating-stars-wrapper" }, this.createStars(this.value), this.withLabel && this.reviewLabel ? index.h("span", { class: "s-rating-stars-label" }, this.reviewLabel) : '') : index.h("div", { class: "s-rating-stars-wrapper" }, index.h("div", { class: "s-rating-stars-element", ref: (el) => this.starsElem = el }, index.h("input", { type: "hidden", class: "rating_hidden_input", name: this.name, value: "" }), [1, 2, 3, 4, 5].map(star => index.h("button", { class: `s-rating-stars-btn-star s-rating-stars-` + this.size, "data-star": star }, index.h("span", { innerHTML: star2.Star })))), this.withLabel && this.reviewLabel ? index.h("span", { class: "s-rating-stars-label" }, this.reviewLabel) : '')))) : (index.h(index.Host, null)); } componentDidLoad() { this.initiateRating(); this.highlightSelectedStars(); if (this.value && this.editable) { const stars = this.starsElem?.querySelectorAll('.s-rating-stars-btn-star'); if (stars && this.value >= 0 && this.value <= stars.length) { this.triggerRatingProgrammatically(this.value - 1); } } } get host() { return index.getElement(this); } }; SallaRatingStars.style = sallaRatingStarsCss; var IconStar2 = ` <svg width="18" height="17" view-box="0 0 18 17" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M9.00045 13.6951L3.71036 16.6563L4.89186 10.71L0.440918 6.59397L6.4612 5.88017L9.00045 0.375122L11.5396 5.88017L17.5599 6.59397L13.109 10.71L14.2905 16.6563L9.00045 13.6951Z" fill="currentcolor" /> </svg>`; var IconStar2Muted = ` <svg width="18" height="17" view-box="0 0 18 17" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M9.00045 13.6951L3.71036 16.6563L4.89186 10.71L0.440918 6.59397L6.4612 5.88017L9.00045 0.375122L11.5396 5.88017L17.5599 6.59397L13.109 10.71L14.2905 16.6563L9.00045 13.6951Z" fill="#DDDDDD" /> </svg>`; var IconFire2 = ` <svg width="12" height="17" view-box="0 0 12 17" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M6 16.2501C9.10658 16.2501 11.625 13.7317 11.625 10.6251C11.625 9.976 11.4523 9.35252 11.25 8.77232C9.99998 10.0075 9.05002 10.6251 8.4 10.6251C11.3966 5.37512 9.75 3.12512 5.25 0.125122C5.625 3.87475 3.15302 5.58043 2.14634 6.52757C1.0559 7.5535 0.375 9.00977 0.375 10.6251C0.375 13.7317 2.89339 16.2501 6 16.2501ZM6.53205 2.92636C8.96333 4.98908 8.97495 6.59185 7.09725 9.88157C6.5265 10.8815 7.2486 12.1251 8.4 12.1251C8.9163 12.1251 9.43807 11.9745 9.98917 11.6789C9.52342 13.4466 7.91393 14.7501 6 14.7501C3.72182 14.7501 1.875 12.9033 1.875 10.6251C1.875 9.47072 2.34959 8.39582 3.17419 7.62002C3.2687 7.53115 3.74812 7.1062 3.76858 7.08782C4.08646 6.8017 4.34835 6.54985 4.60718 6.2727C5.52998 5.28461 6.19283 4.18735 6.53205 2.92636Z" fill="currentcolor" /> </svg>`; const sallaReviewCardCss = ":host{display:block}"; const SallaReviewCard = class { constructor(hostRef) { index.registerInstance(this, hostRef); this.currentSlide = 0; this.showPurchaseCount = false; this.startPoint = { x: 0, y: 0 }; this.isSwiping = false; this.isRTL = "rtl"; this.handlePointerDown = (e) => { // Only handle primary pointer (first touch/mouse) if (!e.isPrimary) return; this.sliderElement?.setPointerCapture(e.pointerId); this.startSwipe(e.clientX, e.clientY); e.preventDefault(); }; this.handlePointerMove = (e) => { if (!this.isSwiping || !e.isPrimary) return; e.preventDefault(); }; this.handlePointerUp = (e) => { if (!this.isSwiping || !e.isPrimary) return; this.sliderElement?.releasePointerCapture(e.pointerId); this.endSwipe(e.clientX, e.clientY); }; this.handlePointerCancel = (e) => { if (!this.isSwiping || !e.isPrimary) return; this.sliderElement?.releasePointerCapture(e.pointerId); this.isSwiping = false; }; this.goToSlide = (index) => { this.currentSlide = Math.max(0, Math.min(index, this.images.length - 1)); }; } async componentDidLoad() { await salla.onReady(); this.showPurchaseCount = !!salla.config.get('store.settings.product.total_sold_enabled', false); this.isRTL = salla.config.get('theme.is_rtl', true); this.purchasedCount = salla.lang.getWithDefault('blocks.home.reviews.purchased_count', this.isRTL ? ` تم شراءه ${this.review.product?.sold_quantity} مرة` : `Purchased ${this.review.product.sold_quantity} times`, { count: this.review.product?.sold_quantity }); this.initializeSlider(); } disconnectedCallback() { this.removeEventListeners(); } get images() { const { review } = this; return review?.images?.length > 1 ? review.images.map((image, idx) => ({ url: image, alt: '', id: idx })) : review?.product?.images || []; } get hasMultipleImages() { return this.images.length > 1; } get slideTransform() { const direction = this.isRTL ? 1 : -1; return `translateX(${this.currentSlide * 100 * direction}%)`; } initializeSlider() { if (!this.hasMultipleImages) return; this.sliderElement = this.el.querySelector(".s-review-card-slider-container"); if (!this.sliderElement) return; // Enable pointer events and set touch-action this.sliderElement.style.touchAction = 'pan-y pinch-zoom'; this.addEventListeners(); } addEventListeners() { if (!this.sliderElement) return; this.sliderElement.addEventListener("pointerdown", this.handlePointerDown, { passive: false }); this.sliderElement.addEventListener("pointermove", this.handlePointerMove, { passive: false }); this.sliderElement.addEventListener("pointerup", this.handlePointerUp, { passive: true }); this.sliderElement.addEventListener("pointercancel", this.handlePointerCancel, { passive: true }); } removeEventListeners() { if (!this.sliderElement) return; this.sliderElement.removeEventListener("pointerdown", this.handlePointerDown); this.sliderElement.removeEventListener("pointermove", this.handlePointerMove); this.sliderElement.removeEventListener("pointerup", this.handlePointerUp); this.sliderElement.removeEventListener("pointercancel", this.handlePointerCancel); } startSwipe(x, y) { this.startPoint = { x, y }; this.isSwiping = true; } componentDidRender() { this.removeEventListeners(); this.addEventListeners(); } endSwipe(x, y) { this.isSwiping = false; const dx = x - this.startPoint.x; const dy = y - this.startPoint.y; this.processSwipe(dx, dy); } processSwipe(dx, dy) { const MIN_SWIPE_DISTANCE = 10; if (Math.abs(dx) < MIN_SWIPE_DISTANCE || Math.abs(dy) > Math.abs(dx)) return; const isLeftSwipe = dx < 0; const shouldGoNext = this.isRTL ? !isLeftSwipe : isLeftSwipe; if (shouldGoNext) { this.goToNextSlide(); } else { this.goToPrevSlide(); } } goToNextSlide() { if (this.currentSlide < this.images.length - 1) { this.currentSlide++; } } goToPrevSlide() { if (this.currentSlide > 0) { this.currentSlide--; } } renderStars() { return Array(5) .fill(null) .map((_, index$1) => index.h("span", { key: index$1, innerHTML: this.review.stars >= index$1 + 1 ? IconStar2 : IconStar2Muted })); } renderDots() { return this.images.map(({ url }, index$1) => (index.h("button", { key: url || index$1, type: "button", class: `s-review-card-slider-dot ${this.currentSlide === index$1 ? "active" : ""}`, onClick: () => this.goToSlide(index$1), "aria-label": `Go to slide ${index$1 + 1}`, onPointerDown: () => this.goToSlide(index$1) }))); } renderSlider() { if (!this.hasMultipleImages) return null; return (index.h("div", { class: "s-review-card-slider-container" }, index.h("div", { class: "s-review-card-slides", style: { transform: this.slideTransform } }, this.images.map((image) => (index.h("div", { key: image?.id, class: "s-review-card-slider-slide" }, index.h("img", { src: image.url, alt: image.alt || "Product image", width: 275, height: 275, loading: "lazy", draggable: false }))))), index.h("div", { class: "s-review-card-slider-dots" }, this.renderDots()))); } renderSingleImage() { const image = this.review?.product?.image; if (!image || this.hasMultipleImages) return null; return (index.h("img", { src: image.url, alt: image.alt || "Product image", class: "s-review-card-image", width: 275, height: 275, loading: "lazy", decoding: "async", draggable: false })); } renderHeader() { return (index.h("div", { class: "s-review-card-header" }, index.h("div", { class: "s-review-card-reviewer-name" }, index.h("p", null, this.review?.name), this.review?.has_order && index.h("span", { class: "s-review-card-verified-icon", innerHTML: check.SICheck })), index.h("div", { class: "s-review-card-stars" }, this.renderStars()))); } renderProductInfo() { const product = this.review?.product; if (!product) return null; return (index.h("a", { href: this.review?.product?.url, class: "s-review-card-product-container" }, index.h("img", { alt: product.image?.alt || "Product", src: product.image?.url, class: "s-review-card-product-image", width: 60, height: 60, loading: "lazy", decoding: "async", draggable: false }), index.h("div", { class: "s-review-card-product-details" }, index.h("p", { class: "s-review-card-product-details-name" }, product.name), this.showPurchaseCount ? index.h("p", { class: "s-review-card-product-details-purchase-count" }, index.h("span", { innerHTML: IconFire2 }), this.purchasedCount) : null))); } render() { return index.h("div", { key: 'e5a7774e29dff0fd1ce5ce1e8ca01ee2367f5078', class: "s-review-card-container" }, this.renderSlider(), this.renderSingleImage(), renderDivider(), index.h("div", { key: '69c22179536a152eddb72a4790f8bd20c9b6b886', class: "s-review-card-content" }, this.renderHeader(), index.h("p", { key: '28e980d499e7078006486e94f5e87f5b71790400', class: "s-review-card-review-content", innerHTML: this.review?.content }), renderDivider(), this.renderProductInfo())); } get el() { return index.getElement(this); } }; const renderDivider = (className) => (index.h("div", { class: `s-review-card-divider ${""}` })); SallaReviewCard.style = sallaReviewCardCss; var IconQuoteOpen = `<!-- Generated by IcoMoon.io --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <title>quote-open</title> <path d="M8 12v-5.333c0-0.737-0.596-1.333-1.333-1.333-3.676 0-6.667 2.991-6.667 6.667v8c0 3.676 2.991 6.667 6.667 6.667h1.333c3.676 0 6.667-2.991 6.667-6.667v-1.333c0-3.676-2.991-6.667-6.667-6.667zM12 20c0 2.205-1.795 4-4 4h-1.333c-2.205 0-4-1.795-4-4v-8c0-1.739 1.115-3