{"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\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    this.addEventListener('change', this.onChangeBound);\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  }\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  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","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","_this$dataset$colorAl","target","Element","input","closest","console","debug","selected","value","toUpperCase","colorAltOption","parseInt","productId","position","changeEvent","CustomEvent","bubbles","composed","detail","option_group","selected_option","dispatchEvent","selectedValue","_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","querySelectorAll","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":""}