{"version":3,"file":"product-comparison-complex-section.min.js","mappings":"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","sources":["webpack://lange/./sections/product-comparison-complex/product-comparison-chart/index.ts","webpack://lange/./lib/dom-ready.ts","webpack://lange/./lib/cart-event-queue.ts","webpack://lange/./sections/product-comparison-complex/select-option-button/index.ts","webpack://lange/./lib/css-dom.ts","webpack://lange/./sections/product-comparison-complex/index.ts"],"sourcesContent":["import type { CustomElement } from '@integrabeauty/custom-elements';\nimport { ready, type ReleaseHandle } from '../../../lib/dom-ready.js';\nimport html from './index.html';\nimport styles from './index.scss';\n\n/**\n * Comparison table for products/variants.\n *\n * This component displays products as columns and features as rows. The mobile version shows only 2\n * products at a time with dropdown selectors that allow users to choose which products to compare.\n */\nclass ProductComparisonChart extends HTMLElement implements CustomElement {\n  declare readonly dataset: {\n    /**\n     * Whether to show the ratings in the last row of the table.\n     */\n    showRatings: 'false' | 'true';\n  };\n\n  private variants = new Map<string, string>();\n  private selectedVariants: string[] = [];\n  private featureNames = new Map<string, string>();\n\n  private isSticky = false;\n  private rafId = 0;\n  private isVisible = false;\n  private observer: IntersectionObserver;\n\n  private colored = false;\n  private declare releaseReady: ReleaseHandle;\n  private onScrollBound = this.onScroll.bind(this);\n  private onProductSelectorChangedBound = this.onProductSelectorChanged.bind(this);\n  private onReadyBound = this.onReady.bind(this);\n\n  private onReady() {\n    if (this.isConnected) {\n      this.readTableDefinition();\n      this.render();\n    }\n  }\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    this.observer = new IntersectionObserver(entries => {\n      this.isVisible = entries[0].isIntersecting;\n    });\n    this.observer.observe(this);\n\n    addEventListener('scroll', this.onScrollBound, { passive: true });\n\n    this.releaseReady = ready(this.onReadyBound);\n  }\n\n  public disconnectedCallback() {\n    this.observer.disconnect();\n    removeEventListener('scroll', this.onScrollBound);\n    cancelAnimationFrame(this.rafId);\n    this.rafId = 0;\n\n    if (this.releaseReady) {\n      this.releaseReady();\n    }\n\n    const productSelectorElements = this.shadowRoot.querySelectorAll<HTMLSelectElement>(\n      '.product-selectors select');\n    for (const productSelector of productSelectorElements) {\n      productSelector.removeEventListener('change', this.onProductSelectorChangedBound);\n    }\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  private readTableDefinition() {\n    this.variants.clear();\n\n    const variantsEl = this.querySelectorAll<HTMLElement>('[slot=\"variants\"] [data-variant-id]');\n    for (const variantEl of variantsEl) {\n      this.variants.set(variantEl.dataset.variantId, variantEl.textContent?.trim() ?? '');\n    }\n\n    this.selectedVariants = Array.from(this.variants.keys()).slice(0, getCurrentProductLimit());\n\n    const featuresEl = this.querySelectorAll<HTMLElement>('[slot=\"features\"] [data-feature-no]');\n    for (const featureEl of featuresEl) {\n      this.featureNames.set(featureEl.dataset.featureNo, featureEl.textContent?.trim() ?? '');\n    }\n  }\n\n  private onProductSelectorChanged(_event: Event) {\n    this.selectedVariants = [];\n\n    const productSelectorElements = this.shadowRoot.querySelectorAll<HTMLSelectElement>(\n      '.product-selectors select');\n    for (const productSelectorEl of productSelectorElements) {\n      const selectedVariantId = productSelectorEl.value;\n      this.selectedVariants.push(selectedVariantId);\n    }\n\n    this.render();\n  }\n\n  private render() {\n    this.colored = false;\n\n    const thead = this.shadowRoot.querySelector('thead');\n    thead.innerHTML = '';\n\n    this.renderProductSelectors();\n    this.renderProductCards();\n\n    const tbody = this.shadowRoot.querySelector('tbody');\n    tbody.innerHTML = '';\n\n    this.renderCtaRow();\n    this.renderViewDetailsRow();\n    this.renderFeatureRows();\n\n    if (this.dataset.showRatings === 'true') {\n      this.renderRatingsRow();\n    }\n  }\n\n  private renderProductSelectors() {\n    const productSelectors = document.createElement('tr');\n    productSelectors.classList.add('product-selectors');\n\n    let i = 1;\n    for (const variantId of this.selectedVariants) {\n      const cell = document.createElement('td');\n      cell.setAttribute('headers', 'comparison-col' + i++);\n\n      const selector = document.createElement('select');\n      selector.setAttribute('aria-label', 'Select a product to compare');\n      selector.setAttribute('data-click-track', 'Comparison Chart Product Selector');\n      for (const [id, name] of this.variants.entries()) {\n        const option = document.createElement('option');\n        option.setAttribute('value', id);\n        option.textContent = name;\n        if (id === variantId) {\n          option.selected = true;\n        }\n\n        selector.appendChild(option);\n      }\n\n      cell.appendChild(selector);\n\n      productSelectors.appendChild(cell);\n    }\n\n    const thead = this.shadowRoot.querySelector('thead');\n    thead.appendChild(productSelectors);\n\n    const productSelectorElements = this.shadowRoot.querySelectorAll<HTMLSelectElement>(\n      '.product-selectors select');\n    for (const productSelector of productSelectorElements) {\n      productSelector.addEventListener('change', this.onProductSelectorChangedBound);\n    }\n  }\n\n  private renderProductCards() {\n    const productCards = document.createElement('tr');\n    productCards.classList.add('product-cards');\n\n    let i = 1;\n    for (const variantId of this.selectedVariants) {\n      const cell = document.createElement('th');\n      cell.setAttribute('scope', 'col');\n      cell.setAttribute('id', 'comparison-col' + i++);\n\n      const productCard = this.querySelector(\n        '[slot=\"product-cards\"] [data-variant-id=\"' + variantId + '\"]');\n      if (productCard) {\n        cell.innerHTML = productCard.innerHTML;\n      }\n\n      productCards.appendChild(cell);\n    }\n\n    const thead = this.shadowRoot.querySelector('thead');\n    thead.appendChild(productCards);\n  }\n\n  private renderCtaRow() {\n    const ctaRow = document.createElement('tr');\n    ctaRow.classList.add('cta-row');\n\n    let i = 1;\n    for (const variantId of this.selectedVariants) {\n      const cell = document.createElement('td');\n      cell.setAttribute('headers', 'comparison-col' + i++);\n\n      const addToCart = this.querySelector(\n        '[slot=\"add-to-cart-row\"] [data-variant-id=\"' + variantId + '\"]');\n      if (addToCart) {\n        cell.innerHTML = addToCart.innerHTML;\n      }\n\n      ctaRow.appendChild(cell);\n    }\n\n    const tbody = this.shadowRoot.querySelector('tbody');\n    tbody.appendChild(ctaRow);\n  }\n\n  private renderFeatureRows() {\n    const tbody = this.shadowRoot.querySelector('tbody');\n\n    for (const featureNo of this.featureNames.keys()) {\n      this.colored = !this.colored;\n\n      const headingRow = document.createElement('tr');\n      headingRow.classList.add('chart-heading');\n      if (this.colored) {\n        headingRow.classList.add('colored');\n      }\n\n      const heading = document.createElement('td');\n      heading.setAttribute('colspan', this.selectedVariants.length.toString());\n      heading.textContent = this.featureNames.get(featureNo) ?? '';\n\n      headingRow.appendChild(heading);\n\n      tbody.appendChild(headingRow);\n\n      const row = document.createElement('tr');\n      if (this.colored) {\n        row.classList.add('colored');\n      }\n\n      let i = 1;\n      for (const variantId of this.selectedVariants) {\n        const cell = document.createElement('td');\n        cell.setAttribute('headers', 'comparison-col' + i++);\n\n        const feature = this.querySelector(\n          '[slot=\"feature-rows\"] ' +\n          '[data-variant-id=\"' + variantId + '\"] ' +\n          '[data-feature-no=\"' + featureNo + '\"]');\n        if (feature) {\n          cell.innerHTML = feature.innerHTML;\n        }\n\n        row.appendChild(cell);\n      }\n\n      tbody.appendChild(row);\n    }\n  }\n\n  private renderViewDetailsRow() {\n    const viewDetailsRow = document.createElement('tr');\n    viewDetailsRow.classList.add('view-details-row');\n\n    let i = 1;\n    for (const variantId of this.selectedVariants) {\n      const cell = document.createElement('td');\n      cell.setAttribute('headers', 'comparison-col' + i++);\n\n      const viewDetails = this.querySelector(\n        '[slot=\"view-details-row\"] [data-variant-id=\"' + variantId + '\"]');\n      if (viewDetails) {\n        cell.innerHTML = viewDetails.innerHTML;\n      }\n\n      viewDetailsRow.appendChild(cell);\n    }\n\n    const tbody = this.shadowRoot.querySelector('tbody');\n    tbody.appendChild(viewDetailsRow);\n  }\n\n  private renderRatingsRow() {\n    const tbody = this.shadowRoot.querySelector('tbody');\n    this.colored = !this.colored;\n\n    const headingRow = document.createElement('tr');\n    headingRow.classList.add('chart-heading');\n    if (this.colored) {\n      headingRow.classList.add('colored');\n    }\n\n    const heading = document.createElement('td');\n    heading.setAttribute('colspan', this.selectedVariants.length.toString());\n    heading.textContent = 'Ratings';\n\n    headingRow.appendChild(heading);\n    tbody.appendChild(headingRow);\n\n    const ratingsRow = document.createElement('tr');\n    if (this.colored) {\n      ratingsRow.classList.add('colored');\n    }\n\n    let i = 1;\n    for (const variantId of this.selectedVariants) {\n      const cell = document.createElement('td');\n      cell.setAttribute('headers', 'comparison-col' + i++);\n\n      const wrapper = document.createElement('div');\n      wrapper.classList.add('rating');\n\n      const rating = this.querySelector(\n        '[slot=\"rating-stars\"] [data-variant-id=\"' + variantId + '\"]');\n      if (rating) {\n        wrapper.innerHTML = rating.innerHTML;\n      }\n\n      cell.appendChild(wrapper);\n\n      ratingsRow.appendChild(cell);\n    }\n\n    tbody.appendChild(ratingsRow);\n  }\n\n  /**\n   * Defers the sticky header update to the next animation frame. Skips the update entirely when the\n   * chart is not visible, so scrolling elsewhere on the page does no work here.\n   */\n  private onScroll(_event: Event) {\n    if (!this.isVisible || this.rafId) {\n      return;\n    }\n\n    this.rafId = requestAnimationFrame(() => {\n      this.rafId = 0;\n      this.updateStickyHeader();\n    });\n  }\n\n  /**\n   * Keeps the CTA row pinned just below the header as the user scrolls through the chart. The row\n   * is translated rather than made `position: sticky` because `border-collapse: collapse` on the\n   * table prevents sticky from working on table cells in browsers. Looks like it's not a bug but\n   * how things should work according to the specification.\n   *\n   * @see https://issues.chromium.org/issues/40511425\n   *\n   * The header bottom position comes from `getHeaderBottom`, which reads the header element's\n   * bounding rect so the offset tracks the header correctly when the promo banner hides or shows.\n   */\n  private updateStickyHeader() {\n    const tbody = this.shadowRoot.querySelector('tbody');\n    const row = tbody.querySelector<HTMLElement>('.cta-row');\n    if (!row) {\n      return;\n    }\n\n    const chartRect = tbody.getBoundingClientRect();\n    const headerBottom = getHeaderBottom();\n\n    if (chartRect.top < headerBottom && !this.isSticky) {\n      this.isSticky = true;\n      tbody.classList.add('sticky');\n    }\n\n    if (!this.isSticky) {\n      return;\n    }\n\n    const offset = headerBottom - chartRect.top;\n    const maxOffset = chartRect.height - row.offsetHeight;\n    if (offset <= 0 || maxOffset <= 0) {\n      row.style.transform = '';\n      tbody.classList.remove('sticky');\n      this.isSticky = false;\n      return;\n    }\n\n    row.style.transform = `translateY(${Math.min(offset, maxOffset)}px)`;\n  }\n}\n\nfunction getHeaderBottom() {\n  const headerWrapper = document.querySelector<HTMLElement>('header .wrapper');\n  if (!headerWrapper) {\n    return 0;\n  }\n\n  return headerWrapper.getBoundingClientRect().bottom;\n}\n\nfunction getCurrentProductLimit() {\n  const screenWidth = innerWidth;\n  if (screenWidth > 960) {\n    return 4;\n  }\n\n  if (screenWidth > 640) {\n    return 3;\n  }\n\n  return 2;\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'product-comparison-chart': ProductComparisonChart;\n  }\n}\n\nif (!customElements.get('product-comparison-chart')) {\n  customElements.define('product-comparison-chart', ProductComparisonChart);\n}\n","/**\n * Releases the handle returned from the ready() function.\n *\n * This is idempotent. Additional calls are pointless but harmless.\n */\nexport type ReleaseHandle = () => void;\n\n/**\n * A subset of events that pertain to document ready state.\n */\ntype LoadingEventMap = Pick<DocumentEventMap, 'DOMContentLoaded' | 'load'>;\n\n/**\n * Calls the provided callback when the document is ready. This works both when the document is\n * already ready and when it is not, so callers do not have to care. This exists because it ensures\n * that each caller consistently expresses its dependency on document readiness, and to save a bit\n * of boilerplate.\n *\n * A document is considered ready when it has been parsed. This corresponds to the ready state of\n * 'interactive'. When the document has been fully loaded, the ready state is 'complete'. The\n * callback runs either way, so callers do not have to check which one they are in.\n *\n * Ready means the html is parsed and the DOM is there to query. It does not mean the page's other\n * scripts have run. Deferred scripts and module scripts run after the ready state turns\n * 'interactive' and before DOMContentLoaded fires, so a callback here can run before them. When a\n * caller needs some other script to have run already, this is the wrong tool, and it should wait on\n * whatever that script produces instead.\n *\n * That remedy has a catch of its own when the calling bundle is loaded with async. An async bundle\n * can execute after DOMContentLoaded has already fired, so a plain listener for that event never\n * runs and the work silently never happens. Nothing here can detect that, because by the time this\n * file executes the event is long gone. Catching it needs something registered at parse time,\n * before any bundle loads.\n *\n * There is a second trap right next to that one, and it applies only when waiting for\n * DOMContentLoaded. The ready state stays 'interactive' after DOMContentLoaded has fired, all the\n * way until 'complete', so the ready state cannot tell you whether DOMContentLoaded is still coming\n * or has already gone. Waiting for load is not exposed to this, because the ready state turns\n * 'complete' and load fires in the same task, so seeing 'complete' means load has fired too.\n *\n * When the document is already ready, the callback is invoked via setTimeout. This way, if the\n * callback throws, the error surfaces to the global error handler in a way that is similar to what\n * happens when an event listener throws.\n *\n * We might one day want to move this to an NPM library.\n *\n * Beware of a quirk here. setTimeout yields, and the busier the frontend, the longer it takes to\n * fire. Page load is exactly when the frontend is busiest. The timeout is a floor and not a\n * guarantee, so there is no real bound on how long that yield lasts. What this means is that using\n * this library can introduce more delay than you might expect. Compare code that runs on async load\n * and checks ready state and invokes immediately to code that runs after setTimeout. The former\n * runs in the same tick, the latter runs in \"some later tick\". That later tick can be surprisingly\n * much later. That can cause initial paint issues.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event\n * @see https://html.spec.whatwg.org/multipage/parsing.html#the-end\n */\nexport function ready(callback: (event?: Event) => void,\n  type: keyof LoadingEventMap = 'DOMContentLoaded') {\n  if (document.readyState === 'complete' || (type === 'DOMContentLoaded' &&\n    document.readyState === 'interactive')) {\n    const timer = setTimeout(callback, 0);\n    const handle: ReleaseHandle = () => clearTimeout(timer);\n    return handle;\n  } else {\n    addEventListener(type, callback);\n    const handle: ReleaseHandle = () => removeEventListener(type, callback);\n    return handle;\n  }\n}\n","// Accessors for the ambient cart event queue seeded and filled by the always-on cart lifecycle\n// snippet. The snippet registers a parse-time listener that buffers every cart event into the\n// queue, so a section whose bundle loads later can replay what it missed. Reaching the queue\n// through this lib, rather than the global directly, keeps the coupling to the ambient layer\n// explicit. The buffered events come off the queue untyped, so the guards here narrow each one\n// during replay.\n\ntype CartUpdatedEvent = GlobalEventHandlersEventMap['cart-updated'];\ntype CartUpdateErredEvent = GlobalEventHandlersEventMap['cart-update-erred'];\n\n/**\n * Returns the ambient queue of buffered cart events. A consumer replays this as it initializes,\n * then listens live for further events.\n */\nexport function getEventQueue() {\n  return cart_lifecycle.queue;\n}\n\n/**\n * Whether the value is a cart updated event. A consumer replaying the queue narrows each buffered\n * event with this before handling it.\n */\nexport function isCartUpdatedEvent(value: any): value is CartUpdatedEvent {\n  return value instanceof Event && value.type === 'cart-updated';\n}\n\n/**\n * Whether the value is a cart update erred event.\n */\nexport function isCartUpdateErredEvent(value: any): value is CartUpdateErredEvent {\n  return value instanceof Event && value.type === 'cart-update-erred';\n}\n","import type { CustomElement } from '@integrabeauty/custom-elements';\nimport { getEventQueue, isCartUpdatedEvent } from '../../../lib/cart-event-queue.js';\nimport html from './index.html';\nimport styles from './index.scss';\n\n/**\n * The per-column call to action in the product comparison chart for products that have more than\n * one variant. Single-variant products use the plain add to cart button instead.\n *\n * This exists so a shopper can pick a variant straight from the comparison chart without first\n * going to the product page.\n *\n * Product data is gathered from nested product elements.\n *\n * On click it broadcasts a request for the variant selector to open for this product.\n */\nclass SelectOptionButton extends HTMLElement implements CustomElement {\n  declare readonly dataset: {\n    /**\n     * Selected Shopify country code, it is used by the add to cart button.\n     */\n    countryCode: string;\n\n    /**\n     * Currency ISO Code. Required.\n     */\n    currencyCode: string;\n  };\n\n  private onSelectOptionClickBound = this.onSelectOptionClick.bind(this);\n  private onCartUpdatedBound = this.onCartUpdated.bind(this);\n  private product: Partial<Product> = {};\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    for (const event of getEventQueue()) {\n      if (isCartUpdatedEvent(event)) {\n        try {\n          this.onCartUpdatedBound(event);\n        } catch (error) {\n          console.warn(error);\n        }\n      }\n    }\n\n    addEventListener('cart-updated', this.onCartUpdatedBound);\n\n    this.addEventListener('click', this.onSelectOptionClickBound);\n    this.ingestProduct();\n  }\n\n  public disconnectedCallback() {\n    this.removeEventListener('click', this.onSelectOptionClickBound);\n\n    removeEventListener('cart-updated', this.onCartUpdatedBound);\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  private onCartUpdated(event: GlobalEventHandlersEventMap['cart-updated']) {\n    if (event.detail.is_initial) {\n      const button = this.shadowRoot.querySelector('button');\n      button.disabled = false;\n    }\n  }\n\n  private ingestProduct() {\n    type VariantElement = HTMLElementTagNameMap['variant-element'];\n\n    const productElement = this.querySelector<HTMLElementTagNameMap['product-element']>(\n      'product-element[slot=\"data\"]');\n    if (!productElement) {\n      return;\n    }\n\n    this.product.available = productElement.dataset.available === 'true';\n    this.product.id = parseInt(productElement.dataset.id, 10);\n    this.product.metafields_title = productElement.dataset.metafieldsTitle;\n    this.product.metafields_subtitle = productElement.dataset.metafieldsSubtitle;\n    this.product.selected_variant_id = parseInt(productElement.dataset.selectedVariantId, 10);\n    this.product.title = productElement.dataset.title;\n    this.product.type = productElement.dataset.type;\n    this.product.url = productElement.dataset.url;\n    this.product.metafields_discount_code = productElement.dataset.discountCode;\n    this.product.metafields_discount_percentage =\n      parseFloat(productElement.dataset.discountPercentage);\n    this.product.metafields_discount_override = productElement.dataset.discountOverride;\n    this.product.reviews_average = productElement.dataset.reviewsAverage ?\n      parseFloat(productElement.dataset.reviewsAverage) : 0;\n    this.product.reviews_count = productElement.dataset.reviewsCount ?\n      parseInt(productElement.dataset.reviewsCount, 10) : 0;\n    this.product.tags = productElement.dataset.tags;\n    this.product.option_groups = [];\n\n    const optionGroupElements = productElement.querySelectorAll('product-option-group');\n    for (const optionGroupElement of optionGroupElements) {\n      const optionGroup = <Product['option_groups'][0]>{};\n      optionGroup.name = optionGroupElement.dataset.name;\n      optionGroup.selected_option = optionGroupElement.dataset.selectedOption;\n      if (optionGroupElement.dataset.position) {\n        optionGroup.position = parseInt(optionGroupElement.dataset.position, 10);\n      }\n\n      optionGroup.options = [];\n\n      type ProductOption = HTMLElementTagNameMap['product-option'];\n      const productOptionElements = optionGroupElement.querySelectorAll<ProductOption>(\n        'product-option[data-value]');\n\n      for (const productOptionElement of productOptionElements) {\n        const productOption = <Product['option_groups'][0]['options'][0]>{};\n        productOption.value = productOptionElement.dataset.value;\n        productOption.variants = [];\n\n        const variantElements = productOptionElement.querySelectorAll<VariantElement>(\n          'variant-element[data-id]');\n\n        for (const variantElement of variantElements) {\n          const variant = <ProductVariant>{};\n          variant.available = variantElement.dataset.available === 'true';\n          variant.backorder = variantElement.dataset.backorder;\n          variant.backorder_selling_plan_id = variantElement.dataset.backorderSellingPlanId ?\n            parseInt(variantElement.dataset.backorderSellingPlanId, 10) : null;\n          variant.compare_at_price = variantElement.dataset.compareAtPrice ?\n            parseInt(variantElement.dataset.compareAtPrice, 10) : null;\n          variant.id = parseInt(variantElement.dataset.id, 10);\n          variant.price = parseInt(variantElement.dataset.price, 10);\n          variant.selected = variantElement.dataset.selected === 'true';\n          variant.sku = variantElement.dataset.sku;\n          variant.title = variantElement.dataset.title || productElement.dataset.title || '';\n\n          productOption.variants.push(variant);\n        }\n\n        optionGroup.options.push(productOption);\n      }\n\n      this.product.option_groups.push(optionGroup);\n    }\n\n    this.product.variants = [];\n    const variantElements = this.querySelectorAll<VariantElement>(\n      'product-element[slot=\"data\"] > variant-element[data-id]');\n    for (const variantElement of variantElements) {\n      const variant = <ProductVariant>{};\n      variant.available = variantElement.dataset.available === 'true';\n      variant.compare_at_price = variantElement.dataset.compareAtPrice ?\n        parseInt(variantElement.dataset.compareAtPrice, 10) : null;\n      variant.currency_code = this.dataset.currencyCode;\n      variant.hide_backorder_in_collection = variantElement.dataset.hideBackorderInCollection;\n      variant.id = parseInt(variantElement.dataset.id, 10);\n      variant.image_src = variantElement.dataset.imageSrc || '';\n      variant.option1 = variantElement.dataset.option1;\n      variant.option2 = variantElement.dataset.option2;\n      variant.option3 = variantElement.dataset.option3;\n      variant.price = parseInt(variantElement.dataset.price, 10);\n      variant.selected = variantElement.dataset.selected === 'true';\n      variant.sku = variantElement.dataset.sku;\n      variant.title = variantElement.dataset.title || productElement.dataset.title || '';\n      this.product.variants.push(variant);\n    }\n  }\n\n  private onSelectOptionClick(_event: MouseEvent) {\n    type Detail = WindowEventMap['select-option-requested']['detail'];\n    const requestEvent = new CustomEvent<Detail>('select-option-requested', {\n      bubbles: true,\n      composed: true,\n      detail: {\n        country: this.dataset.countryCode,\n        product: this.product\n      }\n    });\n\n    this.dispatchEvent(requestEvent);\n  }\n}\n\n/**\n * Represents a Shopify product variant\n */\ninterface ProductVariant {\n  /**\n   * Whether the variant is in stock\n   */\n  available: boolean;\n\n  /**\n   * Whether the variant is on backorder\n   */\n  backorder: string;\n\n  /**\n   * The \"Pre-Order\" deferred payment selling plan ID associated with a backordered variant.\n   */\n  backorder_selling_plan_id: number | null;\n\n  /**\n   * The MSRP, or original sales price before discounts, per unit\n   */\n  compare_at_price: number;\n\n  /**\n   * The currency code associated with the price\n   */\n  currency_code: string;\n\n  /**\n   * Boolean as a string to indicate whether to hide any backorder message if the product card is\n   * being viewed in a collection context.\n   */\n  hide_backorder_in_collection: string;\n\n  /**\n   * Variant id\n   *\n   * @example 12345568943\n   */\n  id: number;\n\n  /**\n   * The URL for the variant image\n   */\n  image_src: string;\n\n  /**\n   * The value of the variant for the first product option. If there's no first product option, then\n   * nil is returned.\n   */\n  option1: string;\n\n  /**\n   * The value of the variant for the second product option. If there's no first product option,\n   * then nil is returned.\n   */\n  option2: string;\n\n  /**\n   * The value of the variant for the third product option. If there's no first product option, then\n   * nil is returned.\n   */\n  option3: string;\n\n  /**\n   * The price of the variant in the currency's subunit.\n   *\n   * @see https://shopify.dev/docs/api/liquid/objects/variant#variant-price\n   */\n  price: number;\n\n  /**\n   * Whether this variant is the currently selected variant out of all the variants for the product\n   */\n  selected: boolean;\n\n  /**\n   * The Stock Keeping Unit associated with the variant. This is another kind of product id.\n   */\n  sku: string;\n\n  /**\n   * The title of the variant.\n   */\n  title: string;\n}\n\n/**\n * The complete product shape this section assembles before dispatching, recovered from the shared\n * event contract so the canonical definition lives in one place. The event carries a partial\n * product, so the optional wrapper is stripped here to describe the fully built product.\n */\ntype Product = Required<WindowEventMap['select-option-requested']['detail']['product']>;\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'select-option-button': SelectOptionButton;\n  }\n}\n\nif (!customElements.get('select-option-button')) {\n  customElements.define('select-option-button', SelectOptionButton);\n}\n","/**\n * Generates a style element containing the given content and appends it to the dom.\n *\n * This is an alternative to the webpack style-loader that circumvents the overly complex webpack\n * configuration and plugin behavior. This intentionally avoids using CSSStyleSheet or any other\n * kind of feature that is not broadly supported or accurately polyfilled.\n *\n * This is safe to call before the dom has loaded.\n *\n * This is safe to call on platforms where document.head is not defined.\n *\n * This annotates appended style attributes with a data-lazy attribute so that it is trivial to\n * identify which elements were appended to the dom in this manner. This extra annotation is not\n * expected to have a material performance impact.\n */\nexport function appendCSSText(content: string) {\n  const style = document.createElement('style');\n  style.dataset.lazy = 'true';\n  const node = document.createTextNode(content);\n  style.appendChild(node);\n  const parent = document.head || document.body || document.documentElement;\n  parent.appendChild(style);\n}\n","import './product-comparison-chart/index.js';\nimport './select-option-button/index.js';\nimport { appendCSSText } from '../../lib/css-dom.js';\nimport styles from './index.scss';\n\nappendCSSText(styles);\n"],"names":["ProductComparisonChart","HTMLElement","variants","Map","selectedVariants","featureNames","isSticky","rafId","isVisible","observer","colored","onScrollBound","this","onScroll","bind","onProductSelectorChangedBound","onProductSelectorChanged","onReadyBound","onReady","isConnected","readTableDefinition","render","connectedCallback","initializeShadowRoot","IntersectionObserver","entries","isIntersecting","observe","addEventListener","passive","releaseReady","callback","type","document","readyState","timer","setTimeout","clearTimeout","removeEventListener","ready","disconnectedCallback","disconnect","cancelAnimationFrame","productSelectorElements","shadowRoot","querySelectorAll","productSelector","attachShadow","mode","innerHTML","clear","variantsEl","variantEl","_variantEl$textConten","_variantEl$textConten2","set","dataset","variantId","textContent","trim","Array","from","keys","slice","screenWidth","innerWidth","getCurrentProductLimit","featuresEl","featureEl","_featureEl$textConten","_featureEl$textConten2","featureNo","_event","productSelectorEl","selectedVariantId","value","push","querySelector","renderProductSelectors","renderProductCards","renderCtaRow","renderViewDetailsRow","renderFeatureRows","showRatings","renderRatingsRow","productSelectors","createElement","classList","add","i","cell","setAttribute","selector","id","name","option","selected","appendChild","productCards","productCard","ctaRow","addToCart","tbody","_this$featureNames$ge","headingRow","heading","length","toString","get","row","feature","viewDetailsRow","viewDetails","ratingsRow","wrapper","rating","requestAnimationFrame","updateStickyHeader","chartRect","getBoundingClientRect","headerBottom","headerWrapper","bottom","getHeaderBottom","top","offset","maxOffset","height","offsetHeight","style","transform","remove","Math","min","isCartUpdatedEvent","Event","customElements","define","SelectOptionButton","onSelectOptionClickBound","onSelectOptionClick","onCartUpdatedBound","onCartUpdated","product","event","cart_lifecycle","queue","error","console","warn","ingestProduct","detail","is_initial","disabled","productElement","available","parseInt","metafields_title","metafieldsTitle","metafields_subtitle","metafieldsSubtitle","selected_variant_id","title","url","metafields_discount_code","discountCode","metafields_discount_percentage","parseFloat","discountPercentage","metafields_discount_override","discountOverride","reviews_average","reviewsAverage","reviews_count","reviewsCount","tags","option_groups","optionGroupElements","optionGroupElement","optionGroup","selected_option","selectedOption","position","options","productOptionElements","productOptionElement","productOption","variantElements","variantElement","variant","backorder","backorder_selling_plan_id","backorderSellingPlanId","compare_at_price","compareAtPrice","price","sku","currency_code","currencyCode","hide_backorder_in_collection","hideBackorderInCollection","image_src","imageSrc","option1","option2","option3","requestEvent","CustomEvent","bubbles","composed","country","countryCode","dispatchEvent","content","lazy","node","createTextNode","head","body","documentElement","appendCSSText"],"sourceRoot":""}