{"version":3,"file":"quantity-input-element-section-xxxxxxxxxxxxxxxxxxxx.min.js","mappings":"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","sources":["webpack://lange/./scripts/sections/quantity-input-element/quantity-input/index.ts"],"sourcesContent":["import type { CustomElement } from '@integrabeauty/custom-elements';\nimport html from './index.html';\nimport styles from './index.scss';\n\nexport class QuantityInput extends HTMLElement implements CustomElement {\n  static get observedAttributes() {\n    return [\n      'data-min',\n      'data-max'\n    ];\n  }\n\n  declare readonly dataset: {\n    /**\n     * The initial value of the input, number of items. Later, we shouldn't react on this attribute\n     * changes because this component is the source of truth of item quantity.\n     */\n    initValue: string;\n\n    /**\n     * The maximum value of the input\n     */\n    max: string;\n\n    /**\n     * The minimum value of the input\n     */\n    min: string;\n\n    /**\n     * Product ID, a stringified integer. Needed to send with the changed event to distinguish to\n     * which product this event is related\n     */\n    productId: string;\n\n    /**\n     * The source of the component. We need that to distinguish events sending from a cart item or a\n     * PDP.\n     */\n    source: 'cart-variant' | 'pdp';\n\n    /**\n     * A single line_item.key value from the Shopify liquid object.\n     *\n     * @example \"41980707274803:83ce40615fdb0df94ae38ae310adf77d\"\n     *\n     * @see https://shopify.dev/docs/api/liquid/objects/line_item#line_item-key\n     */\n    variantKeys: string;\n  };\n\n  /**\n   * We need to store the value to compare new and old values.\n   */\n  private value: number;\n\n  /**\n   * Indicates if the component is a part of a cart line item. In this case, we need additional UX\n   * logic because the whole cart is being updated.\n   */\n  private isInCartItem = false;\n\n  private initialized = false;\n\n  private onQuantityDecreasedBound = this.onQuantityDecreased.bind(this);\n  private onQuantityIncreasedBound = this.onQuantityIncreased.bind(this);\n  private onInputChangedBound = this.onInputChanged.bind(this);\n  private onCartUpdateStartedBound = this.onCartUpdateStarted.bind(this);\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n    this.render();\n\n    addEventListener('cart-update-started', this.onCartUpdateStartedBound);\n  }\n\n  public disconnectedCallback() {\n    const minusButton = this.shadowRoot.querySelector('.minus-button');\n    minusButton.removeEventListener('click', this.onQuantityDecreasedBound);\n\n    const plusButton = this.shadowRoot.querySelector('.plus-button');\n    plusButton.removeEventListener('click', this.onQuantityIncreasedBound);\n\n    const inputElement = this.shadowRoot.querySelector('input');\n    inputElement.removeEventListener('change', this.onInputChangedBound);\n\n    removeEventListener('cart-update-started', this.onCartUpdateStartedBound);\n  }\n\n  public attributeChangedCallback(_name: string, oldValue: string, newValue: string) {\n    this.initializeShadowRoot();\n\n    if (this.isConnected && newValue !== oldValue) {\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 onQuantityDecreased() {\n    this.setInputValue(this.value - 1, this.value);\n  }\n\n  private onQuantityIncreased() {\n    this.setInputValue(this.value + 1, this.value);\n  }\n\n  private onInputChanged(event: Event) {\n    const newValue = parseInt((<HTMLInputElement>event.target).value, 10);\n    this.setInputValue(newValue, this.value);\n  }\n\n  private onCartUpdateStarted(_event: Event) {\n    if (this.isInCartItem) {\n      // We disable controls but don't need to enable because in the current implementation the\n      // whole mini cart modal's HTML is overwritten by the HTML fetch from the Shopify endpoint.\n      // Trying to wait the \"ofir-cart-call-ended\" event and then enable buttons cause a bug because\n      // this event fires after the mini card has been updated and always enable \"-\"/\"+\" buttons but\n      // that's wrong.\n      this.disableControls();\n    }\n  }\n\n  private setInputValue(newValue: number, oldValue: number) {\n    if (newValue === oldValue) {\n      return;\n    }\n\n    const input = this.shadowRoot.querySelector('input');\n\n    const max = parseInt(input.max, 10);\n    const min = parseInt(input.min, 10);\n\n    if (newValue < min) {\n      input.value = min.toString();\n    } else if (newValue > max) {\n      input.value = max.toString();\n    } else {\n      input.value = newValue.toString();\n    }\n\n    // After setting a new value to the input, it might not be equal to `newValue` but instead `max`\n    // or `min`. If the final input value remains the same, there's no need to rerender and send an\n    // event.\n    const inputUpdatedValue = parseInt(input.value, 10);\n    if (inputUpdatedValue === oldValue) {\n      return;\n    }\n\n    this.value = inputUpdatedValue;\n\n    this.render();\n\n    const quantityChange =\n      new CustomEvent(\n        'quantity-input-changed', {\n          detail: {\n            quantity: inputUpdatedValue,\n            old_quantity: oldValue,\n            source: this.dataset.source || 'none',\n            variant_keys: this.dataset.variantKeys,\n            product_id: this.dataset.productId\n          },\n          bubbles: true\n        });\n    dispatchEvent(quantityChange);\n  }\n\n  private disableControls() {\n    const minusButton = this.shadowRoot.querySelector<HTMLButtonElement>('.minus-button');\n    minusButton.disabled = true;\n\n    const plusButton = this.shadowRoot.querySelector<HTMLButtonElement>('.plus-button');\n    plusButton.disabled = true;\n\n    const input = this.shadowRoot.querySelector('input');\n    input.disabled = true;\n  }\n\n  private render() {\n    const input = this.shadowRoot.querySelector('input');\n\n    if (!this.initialized) {\n      this.initialized = true;\n\n      if (this.dataset.initValue) {\n        input.setAttribute('value', this.dataset.initValue);\n      }\n\n      input.addEventListener('change', this.onInputChangedBound);\n\n      this.value = parseInt(input.value, 10);\n\n      const minusButton = this.shadowRoot.querySelector('.minus-button');\n      minusButton.addEventListener('click', this.onQuantityDecreasedBound);\n\n      const plusButton = this.shadowRoot.querySelector('.plus-button');\n      plusButton.addEventListener('click', this.onQuantityIncreasedBound);\n\n      const parentLineItemEl = this.closest('.cart-line-item');\n      if (parentLineItemEl) {\n        this.isInCartItem = true;\n      }\n    }\n\n    input.ariaLabel = `Quantity ${input.value}`;\n\n    if (this.dataset.min && this.dataset.min !== input.min) {\n      input.setAttribute('min', this.dataset.min);\n    }\n\n    if (this.dataset.max && this.dataset.max !== input.max) {\n      input.setAttribute('max', this.dataset.max);\n    }\n\n    const minusButton = this.shadowRoot.querySelector<HTMLButtonElement>('.minus-button');\n    minusButton.disabled = input.value === input.min;\n\n    const plusButton = this.shadowRoot.querySelector<HTMLButtonElement>('.plus-button');\n    plusButton.disabled = input.value === input.max;\n  }\n}\n\ntype QuantityInputChangedEvent = CustomEvent<{\n  old_quantity: number;\n\n  /**\n   * Product ID, a stringified integer. Needed to distinguish to which product this event is related\n   */\n  product_id: string;\n  quantity: number;\n\n  /**\n   * The event firing source. We need that to distinguish events sending from a cart item or a PDP.\n   */\n  source?: 'cart-variant' | 'pdp';\n\n  /**\n   * A single line_item.key value from the Shopify liquid object.\n   *\n   * @example \"41980707274803:83ce40615fdb0df94ae38ae310adf77d\"\n   *\n   * @see https://shopify.dev/docs/api/liquid/objects/line_item#line_item-key\n   */\n  variant_keys?: string;\n}>;\n\ndeclare global {\n  interface WindowEventMap {\n    'quantity-input-changed': QuantityInputChangedEvent;\n  }\n\n  interface HTMLElementTagNameMap {\n    'quantity-input': QuantityInput;\n  }\n}\n\nif (!customElements.get('quantity-input')) {\n  customElements.define('quantity-input', QuantityInput);\n}\n"],"names":["QuantityInput","HTMLElement","observedAttributes","value","isInCartItem","initialized","onQuantityDecreasedBound","this","onQuantityDecreased","bind","onQuantityIncreasedBound","onQuantityIncreased","onInputChangedBound","onInputChanged","onCartUpdateStartedBound","onCartUpdateStarted","connectedCallback","initializeShadowRoot","render","addEventListener","disconnectedCallback","shadowRoot","querySelector","removeEventListener","attributeChangedCallback","_name","oldValue","newValue","isConnected","attachShadow","mode","innerHTML","setInputValue","event","parseInt","target","_event","disableControls","input","max","min","toString","inputUpdatedValue","quantityChange","CustomEvent","detail","quantity","old_quantity","source","dataset","variant_keys","variantKeys","product_id","productId","bubbles","dispatchEvent","disabled","initValue","setAttribute","closest","ariaLabel","customElements","get","define"],"sourceRoot":""}