{"version":3,"file":"phoenix.js","sources":["../../../src/js/utils.js","../../../src/js/docs.js","../../../src/js/theme/anchor.js","../../../src/js/theme/bigPicture.js","../../../src/js/theme/charts/echarts/echarts-utils.js","../../../src/js/theme/charts/echarts/basic-echarts.js","../../../src/js/theme/charts/echarts/zero-rodamap-chart.js","../../../src/js/theme/choices.js","../../../src/js/theme/countUp.js","../../../src/js/theme/detector.js","../../../src/js/theme/dropzone.js","../../../src/js/theme/featherIcons.js","../../../node_modules/flatpickr/dist/esm/types/options.js","../../../node_modules/flatpickr/dist/esm/l10n/default.js","../../../node_modules/flatpickr/dist/esm/utils/index.js","../../../node_modules/flatpickr/dist/esm/utils/dom.js","../../../node_modules/flatpickr/dist/esm/utils/formatting.js","../../../node_modules/flatpickr/dist/esm/utils/dates.js","../../../node_modules/flatpickr/dist/esm/utils/polyfills.js","../../../node_modules/flatpickr/dist/esm/index.js","../../../src/js/theme/flatpickr.js","../../../src/js/theme/form-validation.js","../../../src/js/theme/glightbox.js","../../../src/js/theme/googleMap.js","../../../src/js/theme/icons.js","../../../src/js/theme/isotope.js","../../../src/js/theme/list.js","../../../src/js/theme/navbar-vertical.js","../../../src/js/theme/phoenix-offcanvas.js","../../../src/js/theme/popover.js","../../../src/js/theme/product-details.js","../../../src/js/theme/quantity.js","../../../src/js/theme/rater.js","../../../src/js/theme/responsiveNavItems.js","../../../src/js/theme/search.js","../../../src/js/theme/simplabar.js","../../../src/js/theme/swiper.js","../../../src/js/theme/node.js","../../../src/js/theme/theme-control.js","../../../src/js/theme/tinymce.js","../../../src/js/theme/toast.js","../../../src/js/theme/todoOffCanvas.js","../../../src/js/theme/tooltip.js","../../../src/js/theme/wizard.js","../../../src/js/theme/navbar-soft-on-scroll.js","../../../src/js/phoenix.js"],"sourcesContent":["/* -------------------------------------------------------------------------- */\n/* Utils */\n/* -------------------------------------------------------------------------- */\nexport const docReady = fn => {\n // see if DOM is already available\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', fn);\n } else {\n setTimeout(fn, 1);\n }\n};\n\nexport const resize = fn => window.addEventListener('resize', fn);\n\nexport const isIterableArray = array => Array.isArray(array) && !!array.length;\n\nexport const camelize = str => {\n const text = str.replace(/[-_\\s.]+(.)?/g, (_, c) =>\n c ? c.toUpperCase() : ''\n );\n return `${text.substr(0, 1).toLowerCase()}${text.substr(1)}`;\n};\n\nexport const getData = (el, data) => {\n try {\n return JSON.parse(el.dataset[camelize(data)]);\n } catch (e) {\n return el.dataset[camelize(data)];\n }\n};\n\n/* ----------------------------- Colors function ---------------------------- */\n\nexport const hexToRgb = hexValue => {\n let hex;\n hexValue.indexOf('#') === 0\n ? (hex = hexValue.substring(1))\n : (hex = hexValue);\n // Expand shorthand form (e.g. \"03F\") to full form (e.g. \"0033FF\")\n const shorthandRegex = /^#?([a-f\\d])([a-f\\d])([a-f\\d])$/i;\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(\n hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b)\n );\n return result\n ? [\n parseInt(result[1], 16),\n parseInt(result[2], 16),\n parseInt(result[3], 16)\n ]\n : null;\n};\n\nexport const rgbaColor = (color = '#fff', alpha = 0.5) =>\n `rgba(${hexToRgb(color)}, ${alpha})`;\n\n/* --------------------------------- Colors --------------------------------- */\n\nexport const getColor = (name, dom = document.documentElement) => {\n return getComputedStyle(dom).getPropertyValue(`--phoenix-${name}`).trim();\n};\n\nexport const hasClass = (el, className) => {\n !el && false;\n return el.classList.value.includes(className);\n};\n\nexport const addClass = (el, className) => {\n el.classList.add(className);\n};\n\nexport const getOffset = el => {\n const rect = el.getBoundingClientRect();\n const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;\n const scrollTop = window.pageYOffset || document.documentElement.scrollTop;\n return { top: rect.top + scrollTop, left: rect.left + scrollLeft };\n};\n\nexport const isScrolledIntoView = el => {\n let top = el.offsetTop;\n let left = el.offsetLeft;\n const width = el.offsetWidth;\n const height = el.offsetHeight;\n\n while (el.offsetParent) {\n // eslint-disable-next-line no-param-reassign\n el = el.offsetParent;\n top += el.offsetTop;\n left += el.offsetLeft;\n }\n\n return {\n all:\n top >= window.pageYOffset &&\n left >= window.pageXOffset &&\n top + height <= window.pageYOffset + window.innerHeight &&\n left + width <= window.pageXOffset + window.innerWidth,\n partial:\n top < window.pageYOffset + window.innerHeight &&\n left < window.pageXOffset + window.innerWidth &&\n top + height > window.pageYOffset &&\n left + width > window.pageXOffset\n };\n};\n\nexport const breakpoints = {\n xs: 0,\n sm: 576,\n md: 768,\n lg: 992,\n xl: 1200,\n xxl: 1540\n};\n\nexport const getBreakpoint = el => {\n const classes = el && el.classList.value;\n let breakpoint;\n if (classes) {\n breakpoint =\n breakpoints[\n classes\n .split(' ')\n .filter(cls => cls.includes('navbar-expand-'))\n .pop()\n .split('-')\n .pop()\n ];\n }\n return breakpoint;\n};\n\n/* --------------------------------- Cookie --------------------------------- */\n\nexport const setCookie = (name, value, expire) => {\n const expires = new Date();\n expires.setTime(expires.getTime() + expire);\n document.cookie = name + '=' + value + ';expires=' + expires.toUTCString();\n};\n\nexport const getCookie = name => {\n var keyValue = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');\n return keyValue ? keyValue[2] : keyValue;\n};\n\nexport const settings = {\n tinymce: {\n theme: 'oxide'\n },\n chart: {\n borderColor: 'rgba(255, 255, 255, 0.8)'\n }\n};\n\n/* -------------------------- Chart Initialization -------------------------- */\n\nexport const newChart = (chart, config) => {\n const ctx = chart.getContext('2d');\n return new window.Chart(ctx, config);\n};\n\n/* ---------------------------------- Store --------------------------------- */\n\nexport const getItemFromStore = (key, defaultValue, store = localStorage) => {\n try {\n return JSON.parse(store.getItem(key)) || defaultValue;\n } catch {\n return store.getItem(key) || defaultValue;\n }\n};\n\nexport const setItemToStore = (key, payload, store = localStorage) =>\n store.setItem(key, payload);\nexport const getStoreSpace = (store = localStorage) =>\n parseFloat(\n (\n escape(encodeURIComponent(JSON.stringify(store))).length /\n (1024 * 1024)\n ).toFixed(2)\n );\n\n/* get Dates between */\n\nexport const getDates = (\n startDate,\n endDate,\n interval = 1000 * 60 * 60 * 24\n) => {\n const duration = endDate - startDate;\n const steps = duration / interval;\n return Array.from(\n { length: steps + 1 },\n (v, i) => new Date(startDate.valueOf() + interval * i)\n );\n};\n\nexport const getPastDates = duration => {\n let days;\n\n switch (duration) {\n case 'week':\n days = 7;\n break;\n case 'month':\n days = 30;\n break;\n case 'year':\n days = 365;\n break;\n\n default:\n days = duration;\n }\n\n const date = new Date();\n const endDate = date;\n const startDate = new Date(new Date().setDate(date.getDate() - (days - 1)));\n return getDates(startDate, endDate);\n};\n\n/* Get Random Number */\nexport const getRandomNumber = (min, max) => {\n return Math.floor(Math.random() * (max - min) + min);\n};\n\nexport default {\n docReady,\n resize,\n isIterableArray,\n camelize,\n getData,\n hasClass,\n addClass,\n hexToRgb,\n rgbaColor,\n getColor,\n // getGrays,\n getOffset,\n isScrolledIntoView,\n getBreakpoint,\n setCookie,\n getCookie,\n newChart,\n settings,\n getItemFromStore,\n setItemToStore,\n getStoreSpace,\n getDates,\n getPastDates,\n getRandomNumber\n};\n","import { Collapse, Toast } from 'bootstrap';\n\nconst docComponentInit = () => {\n const componentCards = document.querySelectorAll('[data-component-card]');\n const iconCopiedToast = document.getElementById('icon-copied-toast');\n const iconCopiedToastInstance = new Toast(iconCopiedToast);\n\n componentCards.forEach(card => {\n const copyCodeBtn = card.querySelector('.copy-code-btn');\n const copyCodeEl = card.querySelector('.code-to-copy');\n const previewBtn = card.querySelector('.preview-btn');\n const collapseElement = card.querySelector('.code-collapse');\n const collapseInstance = Collapse.getOrCreateInstance(collapseElement, {\n toggle: false\n });\n\n previewBtn?.addEventListener('click', () => {\n collapseInstance.toggle();\n });\n\n copyCodeBtn?.addEventListener('click', () => {\n const el = document.createElement('textarea');\n el.value = copyCodeEl.innerHTML;\n document.body.appendChild(el);\n\n el.select();\n document.execCommand('copy');\n document.body.removeChild(el);\n\n iconCopiedToast.querySelector(\n '.toast-body'\n ).innerHTML = `Code has been copied to clipboard.
`;\n iconCopiedToastInstance.show();\n });\n });\n};\n\nexport default docComponentInit;\n","// import AnchorJS from 'anchor-js';\n\nconst anchorJSInit = () => {\n const anchors = new window.AnchorJS({\n icon: '#'\n });\n anchors.add('[data-anchor]');\n};\n\nexport default anchorJSInit;\n","/* -------------------------------------------------------------------------- */\n/* bigPicture */\n/* -------------------------------------------------------------------------- */\nconst bigPictureInit = () => {\n const { getData } = window.phoenix.utils;\n if (window.BigPicture) {\n const bpItems = document.querySelectorAll('[data-bigpicture]');\n bpItems.forEach(bpItem => {\n const userOptions = getData(bpItem, 'bigpicture');\n const defaultOptions = {\n el: bpItem,\n noLoader: true,\n allowfullscreen: true\n };\n const options = window._.merge(defaultOptions, userOptions);\n\n bpItem.addEventListener('click', () => {\n window.BigPicture(options);\n });\n });\n }\n};\n\nexport default bigPictureInit;\n","// import * as echarts from 'echarts';\nconst { merge } = window._;\n\n// form config.js\nexport const echartSetOption = (chart, userOptions, getDefaultOptions) => {\n const themeController = document.body;\n // Merge user options with lodash\n chart.setOption(merge(getDefaultOptions(), userOptions));\n\n themeController.addEventListener(\n 'clickControl',\n ({ detail: { control } }) => {\n if (control === 'phoenixTheme') {\n chart.setOption(window._.merge(getDefaultOptions(), userOptions));\n }\n }\n );\n};\n// -------------------end config.js--------------------\n\nexport const resizeEcharts = () => {\n const $echarts = document.querySelectorAll('[data-echart-responsive]');\n\n if ($echarts.length > 0) {\n $echarts.forEach(item => {\n const echartInstance = echarts.getInstanceByDom(item);\n echartInstance?.resize();\n });\n }\n};\n\nconst navbarVerticalToggle = document.querySelector('.navbar-vertical-toggle');\nnavbarVerticalToggle &&\n navbarVerticalToggle.addEventListener('navbar.vertical.toggle', e => {\n return resizeEcharts();\n });\n\nexport const tooltipFormatter = (params, dateFormatter = 'MMM DD') => {\n let tooltipItem = ``;\n params.forEach(el => {\n tooltipItem += `
\n ${\n window.dayjs(params[0].axisValue).isValid()\n ? window.dayjs(params[0].axisValue).format(dateFormatter)\n : params[0].axisValue\n }\n
\n ${tooltipItem}\n${el.value}
`;\n iconCopiedToastInstance.show();\n }\n });\n }\n};\n\nexport default iconCopiedInit;\n","/*-----------------------------------------------\n| Isotope\n-----------------------------------------------*/\n\nconst isotopeInit = () => {\n const { getData } = window.phoenix.utils;\n const Selector = {\n ISOTOPE_ITEM: '.isotope-item',\n DATA_ISOTOPE: '[data-sl-isotope]',\n DATA_FILTER: '[data-filter]',\n DATA_FILER_NAV: '[data-filter-nav]'\n };\n\n const DATA_KEY = {\n ISOTOPE: 'sl-isotope'\n };\n const ClassName = {\n ACTIVE: 'active'\n };\n\n if (window.Isotope) {\n const masonryItems = document.querySelectorAll(Selector.DATA_ISOTOPE);\n masonryItems.length &&\n masonryItems.forEach(masonryItem => {\n window.imagesLoaded(masonryItem, () => {\n masonryItem.querySelectorAll(Selector.ISOTOPE_ITEM).forEach(item => {\n // eslint-disable-next-line\n item.style.visibility = 'visible';\n });\n\n const userOptions = getData(masonryItem, DATA_KEY.ISOTOPE);\n const defaultOptions = {\n itemSelector: Selector.ISOTOPE_ITEM,\n layoutMode: 'packery'\n };\n\n const options = window._.merge(defaultOptions, userOptions);\n const isotope = new window.Isotope(masonryItem, options);\n\n // --------- filter -----------------\n const filterElement = document.querySelector(Selector.DATA_FILER_NAV);\n filterElement?.addEventListener('click', function (e) {\n const item = e.target.dataset.filter;\n isotope.arrange({ filter: item });\n document.querySelectorAll(Selector.DATA_FILTER).forEach(el => {\n el.classList.remove(ClassName.ACTIVE);\n });\n e.target.classList.add(ClassName.ACTIVE);\n });\n // ---------- filter end ------------\n\n return isotope;\n });\n });\n }\n};\n\nexport default isotopeInit;\n","/* -------------------------------------------------------------------------- */\n/* Data Table */\n/* -------------------------------------------------------------------------- */\n/* eslint-disable no-param-reassign */\nconst togglePaginationButtonDisable = (button, disabled) => {\n button.disabled = disabled;\n button.classList[disabled ? 'add' : 'remove']('disabled');\n};\n\nconst listInit = () => {\n const { getData } = window.phoenix.utils;\n if (window.List) {\n const lists = document.querySelectorAll('[data-list]');\n\n if (lists.length) {\n lists.forEach(el => {\n let options = getData(el, 'list');\n\n if (options.pagination) {\n options = {\n ...options,\n pagination: {\n item: ``,\n ...options.pagination\n }\n };\n }\n\n const paginationButtonNext = el.querySelector(\n '[data-list-pagination=\"next\"]'\n );\n const paginationButtonPrev = el.querySelector(\n '[data-list-pagination=\"prev\"]'\n );\n const viewAll = el.querySelector('[data-list-view=\"*\"]');\n const viewLess = el.querySelector('[data-list-view=\"less\"]');\n const listInfo = el.querySelector('[data-list-info]');\n const list = new List(el, options);\n\n // -------fallback-----------\n\n list.on('updated', function (item) {\n const fallback =\n el.querySelector('.fallback') ||\n document.getElementById(options.fallback);\n\n if (fallback) {\n if (item.matchingItems.length === 0) {\n fallback.classList.remove('d-none');\n } else {\n fallback.classList.add('d-none');\n }\n }\n });\n\n // ---------------------------------------\n\n const totalItem = list.items.length;\n const itemsPerPage = list.page;\n const btnDropdownClose = list.listContainer.querySelector('.btn-close');\n let pageQuantity = Math.ceil(totalItem / itemsPerPage);\n let numberOfcurrentItems = list.visibleItems.length;\n let pageCount = 1;\n\n btnDropdownClose &&\n btnDropdownClose.addEventListener('search.close', () => {\n list.fuzzySearch('');\n });\n\n const updateListControls = () => {\n listInfo &&\n (listInfo.innerHTML = `${list.i} to ${numberOfcurrentItems} Items of ${totalItem}`);\n paginationButtonPrev &&\n togglePaginationButtonDisable(\n paginationButtonPrev,\n pageCount === 1\n );\n paginationButtonNext &&\n togglePaginationButtonDisable(\n paginationButtonNext,\n pageCount === pageQuantity\n );\n\n if (pageCount > 1 && pageCount < pageQuantity) {\n togglePaginationButtonDisable(paginationButtonNext, false);\n togglePaginationButtonDisable(paginationButtonPrev, false);\n }\n };\n\n // List info\n updateListControls();\n\n if (paginationButtonNext) {\n paginationButtonNext.addEventListener('click', e => {\n e.preventDefault();\n pageCount += 1;\n\n const nextInitialIndex = list.i + itemsPerPage;\n nextInitialIndex <= list.size() &&\n list.show(nextInitialIndex, itemsPerPage);\n numberOfcurrentItems += list.visibleItems.length;\n updateListControls();\n });\n }\n\n if (paginationButtonPrev) {\n paginationButtonPrev.addEventListener('click', e => {\n e.preventDefault();\n pageCount -= 1;\n\n numberOfcurrentItems -= list.visibleItems.length;\n const prevItem = list.i - itemsPerPage;\n prevItem > 0 && list.show(prevItem, itemsPerPage);\n updateListControls();\n });\n }\n\n const toggleViewBtn = () => {\n viewLess.classList.toggle('d-none');\n viewAll.classList.toggle('d-none');\n };\n\n if (viewAll) {\n viewAll.addEventListener('click', () => {\n list.show(1, totalItem);\n pageQuantity = 1;\n pageCount = 1;\n numberOfcurrentItems = totalItem;\n updateListControls();\n toggleViewBtn();\n });\n }\n if (viewLess) {\n viewLess.addEventListener('click', () => {\n list.show(1, itemsPerPage);\n pageQuantity = Math.ceil(totalItem / itemsPerPage);\n pageCount = 1;\n numberOfcurrentItems = list.visibleItems.length;\n updateListControls();\n toggleViewBtn();\n });\n }\n // numbering pagination\n if (options.pagination) {\n el.querySelector('.pagination').addEventListener('click', e => {\n if (e.target.classList[0] === 'page') {\n pageCount = Number(e.target.innerText);\n updateListControls();\n }\n });\n }\n });\n }\n }\n};\n\nexport default listInit;\n","/* -------------------------------------------------------------------------- */\n/* Navbar Vertical */\n/* -------------------------------------------------------------------------- */\n\nconst handleNavbarVerticalCollapsed = () => {\n const { getItemFromStore, hasClass, setItemToStore } = window.phoenix.utils;\n const Selector = {\n HTML: 'html',\n NAVBAR_VERTICAL_TOGGLE: '.navbar-vertical-toggle',\n NAVBAR_VERTICAL_COLLAPSE: '.navbar-vertical .navbar-collapse',\n ECHART_RESPONSIVE: '[data-echart-responsive]'\n };\n\n const Events = {\n CLICK: 'click',\n MOUSE_OVER: 'mouseover',\n MOUSE_LEAVE: 'mouseleave',\n NAVBAR_VERTICAL_TOGGLE: 'navbar.vertical.toggle'\n };\n const ClassNames = {\n NAVBAR_VERTICAL_COLLAPSED: 'navbar-vertical-collapsed',\n NAVBAR_VERTICAL_COLLAPSED_HOVER: 'navbar-vertical-collapsed-hover'\n };\n const navbarVerticalToggle = document.querySelector(\n Selector.NAVBAR_VERTICAL_TOGGLE\n );\n const html = document.querySelector(Selector.HTML);\n const navbarVerticalCollapse = document.querySelector(\n Selector.NAVBAR_VERTICAL_COLLAPSE\n );\n\n if (navbarVerticalToggle) {\n navbarVerticalToggle.addEventListener(Events.CLICK, e => {\n navbarVerticalToggle.blur();\n html?.classList.toggle(ClassNames.NAVBAR_VERTICAL_COLLAPSED);\n\n // Set collapse state on localStorage\n const isNavbarVerticalCollapsed = getItemFromStore(\n 'phoenixIsNavbarVerticalCollapsed',\n false\n );\n setItemToStore(\n 'phoenixIsNavbarVerticalCollapsed',\n !isNavbarVerticalCollapsed\n );\n\n const event = new CustomEvent(Events.NAVBAR_VERTICAL_TOGGLE);\n e.currentTarget?.dispatchEvent(event);\n });\n }\n if (navbarVerticalCollapse) {\n navbarVerticalCollapse.addEventListener(Events.MOUSE_OVER, () => {\n if (hasClass(html, ClassNames.NAVBAR_VERTICAL_COLLAPSED)) {\n html?.classList.add(ClassNames.NAVBAR_VERTICAL_COLLAPSED_HOVER);\n }\n });\n navbarVerticalCollapse.addEventListener(Events.MOUSE_LEAVE, () => {\n if (hasClass(html, ClassNames.NAVBAR_VERTICAL_COLLAPSED_HOVER)) {\n html?.classList.remove(ClassNames.NAVBAR_VERTICAL_COLLAPSED_HOVER);\n }\n });\n }\n};\n\nexport default handleNavbarVerticalCollapsed;\n","/* eslint-disable no-new */\n/*-----------------------------------------------\n| Phoenix Offcanvas\n-----------------------------------------------*/\n\nconst phoenixOffcanvasInit = () => {\n const { getData } = window.phoenix.utils;\n const toggleEl = document.querySelector(\"[data-phoenix-toggle='offcanvas']\");\n const offcanvasBackdrop = document.querySelector('[data-phoenix-backdrop]');\n\n if (toggleEl) {\n const offcanvasTarget = getData(toggleEl, 'phoenix-target');\n const offcanvasTargetEl = document.querySelector(offcanvasTarget);\n const closeBtn = offcanvasTargetEl.querySelector(\n \"[data-phoenix-dismiss='offcanvas']\"\n );\n const showFilterCol = () => {\n offcanvasTargetEl.classList.add('show');\n document.body.style.overflow = 'hidden';\n };\n const hideFilterCol = () => {\n offcanvasTargetEl.classList.remove('show');\n document.body.style.removeProperty('overflow');\n };\n toggleEl.addEventListener('click', () => {\n showFilterCol();\n });\n closeBtn.addEventListener('click', () => {\n hideFilterCol();\n });\n if (offcanvasBackdrop) {\n offcanvasBackdrop.addEventListener('click', () => {\n hideFilterCol();\n });\n }\n }\n};\n\nexport default phoenixOffcanvasInit;\n","import { Popover } from 'bootstrap';\n/* -------------------------------------------------------------------------- */\n/* Popover */\n/* -------------------------------------------------------------------------- */\n\nconst popoverInit = () => {\n const popoverTriggerList = Array.from(\n document.querySelectorAll('[data-bs-toggle=\"popover\"]')\n );\n\n popoverTriggerList.map(popoverTriggerEl => {\n return new Popover(popoverTriggerEl);\n });\n};\n\nexport default popoverInit;\n","/* eslint-disable no-new */\n/*-----------------------------------------------\n| Swiper\n-----------------------------------------------*/\n\nconst getThubmnailDirection = () => {\n if (\n window.innerWidth < 768 ||\n (window.innerWidth >= 992 && window.innerWidth < 1200)\n ) {\n return 'horizontal';\n }\n return 'vertical';\n};\n\nconst productDetailsInit = () => {\n const { getData, resize } = window.phoenix.utils;\n const productDetailsEl = document.querySelector('[data-product-details]');\n if (productDetailsEl) {\n const colorVariantEl = productDetailsEl.querySelector(\n '[data-product-color]'\n );\n const productQuantityEl = productDetailsEl.querySelector(\n '[data-product-quantity]'\n );\n const productQuantityInputEl = productDetailsEl.querySelector(\n '[data-quantity] input[type=\"number\"]'\n );\n const productColorVariantConatiner = productDetailsEl.querySelector(\n '[data-product-color-variants]'\n );\n\n const swiperInit = productImages => {\n const productSwiper = productDetailsEl.querySelector(\n '[data-products-swiper]'\n );\n\n const options = getData(productSwiper, 'swiper');\n\n const thumbTarget = getData(productSwiper, 'thumb-target');\n\n const thumbEl = document.getElementById(thumbTarget);\n\n let slides = '';\n productImages.forEach(img => {\n slides += `\n \n `;\n });\n productSwiper.innerHTML = `