{"version":3,"file":"variant-option-elements-section.min.js","mappings":"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","sources":["webpack://lange/./sections/variant-option-elements/variant-option-group/index.ts","webpack://lange/./sections/variant-option-elements/variant-options/index.ts"],"sourcesContent":["import type { CustomElement } from '@integrabeauty/custom-elements';\nimport html from './index.html';\nimport styles from './index.scss';\n\ntype ProductGuideButton = HTMLElementTagNameMap['product-guide-button'];\n\n/**\n * Configuration for a product guide modal trigger. Used for size, color, dosage, and other variant-\n * specific reference guides.\n */\ninterface ProductGuide {\n  /**\n   * The source URL of the product guide icon\n   */\n  iconSrc: string;\n\n  /**\n   * The id of the product guide content to show when the guide button is clicked\n   */\n  id: string;\n\n  /**\n   * The title of the product guide, shown next to the icon in the guide button\n   */\n  title: string;\n}\n\n/**\n * Represents a single option group for a product's variants.\n *\n * This is closely modeled after Shopify's liquid object `product_option`.\n *\n * A product option, such as size or color.\n *\n * @see https://shopify.dev/docs/api/liquid/objects/product_option\n */\nclass VariantOptionGroup extends HTMLElement implements CustomElement {\n  public static get observedAttributes() {\n    return ['data-selected-value'];\n  }\n\n  declare readonly dataset: {\n    /**\n     * The product handle to redirect to if the user clicks the color option provided in the\n     * colorAltOption dataset value.\n     */\n    colorAltHandle: string;\n\n    /**\n     * Defines the color options which should trigger a redirect based on the passed in\n     * colorAltHandle value.\n     */\n    colorAltOption: string;\n\n    /**\n     * Product option name\n     *\n     * @see https://shopify.dev/docs/api/liquid/objects/product_option\n     */\n    name: string;\n\n    /**\n     * The 1-based index of the product option in the product.options_with_values array.\n     *\n     * This string contains an integer.\n     *\n     * @see https://shopify.dev/docs/api/liquid/objects/product_option\n     */\n    position: string;\n\n    /**\n     * Shopify product id, the id of the product to which this option group pertains.\n     *\n     * This string contains an integer.\n     */\n    productId: string;\n\n    /**\n     * The initial short description of the currently selected product variant.\n     *\n     * At the moment we assume that only one product option group has a description. If we need to\n     * support more than one, we need to enhance this logic.\n     */\n    selectedDescription: string;\n\n    /**\n     * The currently selected product option value. Comes from product_option.selected_value.\n     *\n     * Liquid leaves the attribute off when no value is selected, so this is undefined until the\n     * customer picks one.\n     *\n     * @see https://shopify.dev/docs/api/liquid/objects/product_option\n     */\n    selectedValue?: string;\n\n    /**\n     * A boolean value (true or false) that indicates whether the option group is a single choice.\n     * Single choice is still useful when we want to show the customer the attribute of the product\n     * (like size for hair care products), but we don't want to show all the interface elements that\n     * are present for real choices.\n     */\n    singleChoiceMode: string;\n\n    /**\n     * A boolean value (true or false) that indicates whether color swatches should be used if the\n     * variant option group is \"Color\".\n     */\n    useSwatchesForColor: string;\n  };\n\n  private declare description: string;\n  private declare productGuide: ProductGuide;\n  private onChangeBound = this.onChange.bind(this);\n  private onPageShowBound = this.onPageShow.bind(this);\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    this.addEventListener('change', this.onChangeBound);\n    addEventListener('pageshow', this.onPageShowBound);\n\n    this.description = this.dataset.selectedDescription;\n\n    this.gatherGuideData();\n    this.render();\n  }\n\n  public disconnectedCallback() {\n    this.removeEventListener('change', this.onChangeBound);\n    removeEventListener('pageshow', this.onPageShowBound);\n  }\n\n  public attributeChangedCallback(name: string, oldValue: string, newValue: string) {\n    this.initializeShadowRoot();\n\n    if (name === 'data-selected-value' && oldValue !== newValue) {\n      this.render();\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 gatherGuideData() {\n    const selector = 'product-guide-button[slot=\"product-guide-button\"]';\n    const element = this.querySelector<ProductGuideButton>(selector);\n    if (element) {\n      this.productGuide = {\n        id: element.dataset.id,\n        iconSrc: element.dataset.icon,\n        title: element.dataset.title || 'Guide'\n      };\n    }\n  }\n\n  /**\n   * Sends the customer to the separate product that carries this color, because some colors live on\n   * their own product instead of being a variant of this one.\n   *\n   * The view param is carried over so that a customer who arrived on an offer version of the page\n   * stays on that version after the redirect.\n   */\n  private redirectToColorAltProduct() {\n    const newUrl = new URL(`/products/${this.dataset.colorAltHandle}`, 'https://langehair.com');\n\n    const urlParams = new URLSearchParams(location.search);\n    if (urlParams.get('view') === 'offer-1') {\n      newUrl.searchParams.set('view', 'offer-1');\n    }\n\n    location.href = newUrl.toString();\n  }\n\n  /**\n   * Checks the radio for the selected value again when the browser restores the page from the\n   * back/forward cache.\n   *\n   * Picking a color that lives on another product checks that color's radio and then leaves the\n   * page without updating the selected value. The browser saves the page with that radio still\n   * checked, so going back would show the other product's color.\n   */\n  private onPageShow(event: PageTransitionEvent) {\n    if (event.persisted) {\n      const inputs = this.querySelectorAll<HTMLInputElement>('input[type=\"radio\"]');\n      for (const input of inputs) {\n        input.checked = input.value === this.dataset.selectedValue;\n      }\n    }\n  }\n\n  private onChange(event: Event) {\n    if (!(event.target instanceof Element)) {\n      return;\n    }\n\n    const input = event.target.closest<HTMLInputElement>('input[type=\"radio\"]');\n    if (!input) {\n      console.debug('input radio not found');\n      return;\n    }\n\n    const selected = input.value;\n\n    if (selected.toUpperCase() === this.dataset.colorAltOption?.toUpperCase()) {\n      this.redirectToColorAltProduct();\n      return;\n    }\n\n    const id = parseInt(this.dataset.productId, 10);\n    const position = parseInt(this.dataset.position, 10);\n    this.description = input.dataset.description;\n\n    type Detail = WindowEventMap['product-variant-option-group-changed']['detail'];\n    const changeEvent = new CustomEvent<Detail>('product-variant-option-group-changed', {\n      bubbles: true,\n      composed: true,\n      detail: {\n        id,\n        position,\n        option_group: this.dataset.name,\n        selected_option: selected,\n        description: this.description\n      }\n    });\n    this.dispatchEvent(changeEvent);\n\n    // The selected value attribute is an observed attribute. The assignment triggers render. That\n    // is why there is no explicit call to render following this assignment, and is also why this\n    // assignment is done last.\n\n    this.dataset.selectedValue = selected;\n  }\n\n  private render() {\n    const titleEl = this.shadowRoot.querySelector('.title');\n    titleEl.textContent = `Selected ${this.dataset.name}`;\n\n    const selectedEl = this.shadowRoot.querySelector<HTMLElement>('.selected');\n\n    // The attribute is missing until something is selected. Assigning undefined to textContent\n    // would write out the literal word undefined.\n    selectedEl.textContent = this.dataset.selectedValue ?? '';\n\n    const descriptionEl = this.shadowRoot.querySelector<HTMLElement>('.description');\n    if (this.description) {\n      descriptionEl.textContent = ` - ${this.description}`;\n    } else {\n      descriptionEl.textContent = '';\n    }\n\n    if (this.dataset.singleChoiceMode === 'true') {\n      titleEl.textContent = this.dataset.name;\n\n      selectedEl.hidden = true;\n      descriptionEl.hidden = true;\n    } else {\n      selectedEl.hidden = false;\n      descriptionEl.hidden = false;\n    }\n\n    const guideButton = this.shadowRoot.querySelector<HTMLButtonElement>('.guide');\n    const currentContentId = guideButton.dataset.contentId;\n    // only initialize the button once\n    if (this.productGuide && !currentContentId) {\n      guideButton.dataset.contentId = this.productGuide.id;\n\n      const guideIcon = guideButton.querySelector('img');\n      guideIcon.src = this.productGuide.iconSrc;\n\n      const guideTitle = guideButton.querySelector('span');\n      guideTitle.textContent = this.productGuide.title;\n\n      guideButton.addEventListener('click', onGuideButtonClick);\n      guideButton.hidden = false;\n    }\n  }\n}\n\nfunction onGuideButtonClick(event: MouseEvent) {\n  if (!(event.currentTarget instanceof HTMLElement)) {\n    return;\n  }\n\n  const focusEvent = new Event('focus-stack-push-requested', {\n    bubbles: true,\n    composed: true\n  });\n  event.currentTarget.dispatchEvent(focusEvent);\n\n  type ProductGuideModalRequestedEvent = WindowEventMap['product-guide-modal-requested'];\n  type Detail = ProductGuideModalRequestedEvent['detail'];\n\n  const modalEvent = new CustomEvent<Detail>('product-guide-modal-requested', {\n    bubbles: true,\n    composed: true,\n    detail: {\n      contentId: event.currentTarget.dataset.contentId\n    }\n  });\n  event.currentTarget.dispatchEvent(modalEvent);\n}\n\n/**\n * This event is emitted exclusively for consumption by the parent variant-options custom element\n * and represents a change in 1 of 3 potential option groups available to Shopify products. This\n * update is used by the variant-options element to determine the newly selected variant for a\n * particular product and emit the product-variant-option-changed event to all subscribed listeners.\n */\ntype ProductVariantOptionGroupChangedEvent = CustomEvent<{\n  /**\n   * The short description of the newly selected option value, such as a size description. Comes\n   * from the data-description attribute of the radio input the customer picked. Empty when the\n   * option value has no description, which is the usual case outside of size.\n   */\n  description: string;\n\n  /**\n   * Shopify product id, the id of the product to which the changed option group pertains.\n   */\n  id: number;\n\n  /**\n   * The name of the option group that changed, such as Size or Color.\n   */\n  option_group: string;\n\n  /**\n   * The 1-based index of the option group in the product.options_with_values array.\n   */\n  position: number;\n\n  /**\n   * The option value the customer just picked, such as Large or Blush.\n   */\n  selected_option: string;\n}>;\n\ndeclare global {\n  interface GlobalEventHandlersEventMap {\n    'product-variant-option-group-changed': ProductVariantOptionGroupChangedEvent;\n  }\n\n  interface HTMLElementTagNameMap {\n    'variant-option-group': VariantOptionGroup;\n  }\n}\n\nif (!customElements.get('variant-option-group')) {\n  customElements.define('variant-option-group', VariantOptionGroup);\n}\n","import type { CustomElement } from '@integrabeauty/custom-elements';\nimport html from './index.html';\nimport styles from './index.scss';\n\n/**\n * Represents a single option group for a product's variants\n */\nclass VariantOptions extends HTMLElement implements CustomElement {\n  public static get observedAttributes() {\n    return ['data-selected-variant'];\n  }\n\n  declare readonly dataset: {\n\n    /**\n     * Bundles created using the Shopify app use an abstraction product created by the application\n     * to represent the bundle. If a product's variant options are being presented in the context of\n     * a product in a bundle this attribute identifies the product ID for the abstraction product\n     * created by the Shopify Bundle App to represent that bundle.\n     */\n    bundleProductId: string;\n\n    /**\n     * Discount code associated with the product, relayed to listeners in the variant option changed\n     * event so a discount can be applied for the selected variant.\n     */\n    discountCode: string;\n\n    /**\n     * Shopify product ID\n     */\n    productId: string;\n\n    /**\n     * Product title for the variant options\n     */\n    productTitle: string;\n\n    /**\n     * Shopify product type\n     */\n    productType: string;\n\n    /**\n     * URL for product detail page\n     */\n    productUrl: string;\n\n    /**\n     * The currently selected variant ID\n     */\n    selectedVariant: string;\n\n  };\n\n  private optionGroups: ProductVariantOptionGroup[] = [];\n  private onOptionGroupChangedBound = this.onOptionGroupChanged.bind(this);\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    // We shouldn't use addRetroEventListener here because it causes a problem with the variant\n    // selector modal. If we use addRetroEventListener all events are stored in the queue and after\n    // opening the modal again all those events are fired. In this case removeEventListener doesn't\n    // work as expected. It seems like we shouldn't use addRetroEventListener for components that\n    // could be removed from the page.\n    addEventListener('product-variant-option-group-changed', this.onOptionGroupChangedBound);\n    this.ingestOptionGroups();\n  }\n\n  public disconnectedCallback() {\n    removeEventListener('product-variant-option-group-changed', this.onOptionGroupChangedBound);\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 onOptionGroupChanged(event: WindowEventMap['product-variant-option-group-changed']) {\n    if (this.optionGroups.length === 0) {\n      this.ingestOptionGroups();\n    }\n\n    if (parseInt(this.dataset.productId, 10) !== event.detail.id) {\n      return;\n    }\n\n    let queryString = 'variant-element';\n\n    let position = 1;\n    for (const optionGroup of this.optionGroups) {\n      if (optionGroup.name === event.detail.option_group) {\n        optionGroup.selected_option = event.detail.selected_option;\n      }\n\n      if (position === 1) {\n        queryString = queryString + `[data-option1=\"${optionGroup.selected_option}\"]`;\n      }\n\n      if (position === 2) {\n        queryString = queryString + `[data-option2=\"${optionGroup.selected_option}\"]`;\n      }\n\n      if (position === 3) {\n        queryString = queryString + `[data-option3=\"${optionGroup.selected_option}\"]`;\n      }\n\n      position++;\n    }\n\n    const selectedVariant = this.querySelector<VariantElement>(queryString);\n    if (!selectedVariant) {\n      return;\n    }\n\n    this.dataset.selectedVariant = selectedVariant.dataset.id;\n\n    type Detail = WindowEventMap['product-variant-option-changed']['detail'];\n    const changeEvent = new CustomEvent<Detail>('product-variant-option-changed', {\n      bubbles: true,\n      composed: true,\n      detail: {\n        id: parseInt(this.dataset.productId, 10),\n        option_groups: this.optionGroups,\n        selected_variant_id: parseInt(this.dataset.selectedVariant, 10),\n        title: this.dataset.productTitle,\n        type: this.dataset.productType,\n        url: this.dataset.productUrl,\n        bundle_product_id: parseInt(this.dataset.bundleProductId, 10),\n        metafields_discount_code: this.dataset.discountCode\n      }\n    });\n    this.dispatchEvent(changeEvent);\n\n    // Update out-of-stock styling for dependent option groups based on the newly selected option.\n    // This ensures that when option1 changes, the UI immediately reflects which option2/option3\n    // values are available for the selected option1 value.\n    this.setOOSForSecondOptionGroup(event.detail.option_group, event.detail.selected_option,\n      event.detail.position);\n  }\n\n  private parseUniqueValuesAtPosition(position: number) {\n    const variants = this.querySelectorAll('variant-element');\n    const uniques: string[] = [];\n    for (const variant of variants) {\n      let optionValue: string;\n      if (position === 1) {\n        optionValue = variant.dataset.option1;\n      }\n\n      if (position === 2) {\n        optionValue = variant.dataset.option2;\n      }\n\n      if (position === 3) {\n        optionValue = variant.dataset.option3;\n      }\n\n      if (optionValue && !uniques.includes(optionValue)) {\n        uniques.push(optionValue);\n      }\n    }\n\n    return uniques;\n  }\n\n  private getVariantsForOptionValueAndPosition(value: string, position: number) {\n    let positionWord: string;\n    if (position === 1) {\n      positionWord = 'one';\n    } else if (position === 2) {\n      positionWord = 'two';\n    } else if (position === 3) {\n      positionWord = 'three';\n    }\n\n    if (!positionWord) {\n      return;\n    }\n\n    const optionVariants = [];\n    const variants = this.querySelectorAll<VariantElement>(\n      `variant-element[data-option${position}=\"${value}\"]`);\n    for (const variant of variants) {\n      optionVariants.push({\n        available: variant.dataset.available === 'true',\n        compareAtPrice: parseInt(variant.dataset.compareAtPrice, 10) || 0,\n        id: parseInt(variant.dataset.id, 10),\n        price: parseInt(variant.dataset.price, 10),\n        sku: variant.dataset.sku,\n        title: variant.dataset.title\n      });\n    }\n\n    return optionVariants;\n  }\n\n  private ingestOptionGroupAtPosition(position: number, name: string, selected: string) {\n    const options = [];\n    const groupOptions = this.parseUniqueValuesAtPosition(position);\n    for (const groupOption of groupOptions) {\n      options.push({\n        value: groupOption,\n        variants: this.getVariantsForOptionValueAndPosition(groupOption, position)\n      });\n    }\n\n    return {\n      name,\n      selected_option: selected,\n      position,\n      options\n    };\n  }\n\n  private ingestOptionGroups() {\n    const groups = this.querySelectorAll('variant-option-group');\n    this.optionGroups = [];\n    for (const group of groups) {\n      const position = parseInt(group.dataset.position, 10);\n      const newOptionGroup = this.ingestOptionGroupAtPosition(position, group.dataset.name,\n        group.dataset.selectedValue);\n      this.optionGroups.push(newOptionGroup);\n    }\n  }\n\n  /**\n   * Updates the out-of-stock (OOS) visual state for dependent option groups.\n   *\n   * When the first option changes (e.g., selecting a different color), this method updates the OOS\n   * styling on subsequent option groups (e.g., size) to reflect which combinations are actually\n   * available with the newly selected first option.\n   *\n   * For example, if \"Red\" is selected as option1, this will mark size options as out-of-stock if\n   * there's no inventory for \"Red + Small\", \"Red + Medium\", etc.\n   *\n   * This method only operates when mainPosition is 1, as it specifically handles the pattern where\n   * the first option selection constrains availability of subsequent options. It updates the DOM\n   * directly by adding/removing the 'out-of-stock' class on option labels.\n   */\n  private setOOSForSecondOptionGroup(mainOptionName: string, mainOptionValue: string,\n    mainPosition: number) {\n    const groups = this.optionGroups.filter(group => group.name !== mainOptionName);\n    for (const group of groups) {\n      let index = 1;\n      for (const option of group.options) {\n        // Find the variant that matches the selected first option + this dependent option\n        const variant = this.querySelector<VariantElement>(\n          `variant-element[data-option${mainPosition}=\"${mainOptionValue}\"]` +\n          `[data-option${group.position}=\"${option.value}\"]`);\n        if (!variant) {\n          continue;\n        }\n\n        const groupEl = this.querySelector(\n          `variant-option-group[data-position=\"${group.position}\"]`);\n        if (!groupEl) {\n          continue;\n        }\n\n        const label = groupEl.querySelector(\n          `label[for=\"${this.dataset.productId}${group.name.toLowerCase()}${index}\"]`);\n        if (!label) {\n          continue;\n        }\n\n        // Update the label's visual state based on variant availability\n        if (variant.dataset.available === 'true') {\n          label.classList.remove('out-of-stock');\n        } else {\n          label.classList.add('out-of-stock');\n        }\n\n        index++;\n      }\n    }\n  }\n}\n\ntype VariantElement = HTMLElementTagNameMap['variant-element'];\n\ninterface ProductVariantOptionGroup {\n  name: string;\n  options: ProductVariantOption[];\n  position?: number;\n  selected_option: string;\n}\n\ninterface ProductVariantOption {\n  value: string;\n  variants: Partial<{\n    /**\n     * Whether the variant has inventory in stock and is available for sale.\n     */\n    available: boolean;\n\n    backorder_message: string;\n\n    compare_at_price: number;\n\n    customAttributes: {\n      /**\n       * Key or name of the attribute.\n       */\n      key: string;\n\n      /**\n       * Value of the attribute.\n       */\n      value: string;\n    }[];\n\n    hideBackorderInCollection: string;\n\n    /**\n     * Variant id\n     */\n    id: number;\n\n    price: number;\n\n    sku: string;\n\n    title: string;\n  }>[];\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'variant-options': VariantOptions;\n  }\n}\n\nif (!customElements.get('variant-options')) {\n  customElements.define('variant-options', VariantOptions);\n}\n"],"names":["VariantOptionGroup","HTMLElement","observedAttributes","onChangeBound","this","onChange","bind","onPageShowBound","onPageShow","connectedCallback","initializeShadowRoot","addEventListener","description","dataset","selectedDescription","gatherGuideData","render","disconnectedCallback","removeEventListener","attributeChangedCallback","name","oldValue","newValue","shadowRoot","attachShadow","mode","innerHTML","element","querySelector","productGuide","id","iconSrc","icon","title","redirectToColorAltProduct","newUrl","URL","colorAltHandle","URLSearchParams","location","search","get","searchParams","set","href","toString","event","persisted","inputs","querySelectorAll","input","checked","value","selectedValue","_this$dataset$colorAl","target","Element","closest","console","debug","selected","toUpperCase","colorAltOption","parseInt","productId","position","changeEvent","CustomEvent","bubbles","composed","detail","option_group","selected_option","dispatchEvent","_this$dataset$selecte","titleEl","textContent","selectedEl","descriptionEl","singleChoiceMode","hidden","guideButton","currentContentId","contentId","src","onGuideButtonClick","currentTarget","focusEvent","Event","modalEvent","customElements","define","VariantOptions","optionGroups","onOptionGroupChangedBound","onOptionGroupChanged","ingestOptionGroups","length","queryString","optionGroup","selectedVariant","option_groups","selected_variant_id","productTitle","type","productType","url","productUrl","bundle_product_id","bundleProductId","metafields_discount_code","discountCode","setOOSForSecondOptionGroup","parseUniqueValuesAtPosition","variants","uniques","variant","optionValue","option1","option2","option3","includes","push","getVariantsForOptionValueAndPosition","positionWord","optionVariants","available","compareAtPrice","price","sku","ingestOptionGroupAtPosition","options","groupOptions","groupOption","groups","group","newOptionGroup","mainOptionName","mainOptionValue","mainPosition","filter","index","option","groupEl","label","toLowerCase","classList","remove","add"],"sourceRoot":""}