{"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","webpack://lange/./scripts/sections/quantity-input-element/quantity-input/index.scss","webpack://lange/./scripts/sections/quantity-input-element/quantity-input/index.html"],"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  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\n     * to 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\n     * 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    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: boolean;\n\n  public shadowRoot!: ShadowRoot;\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  constructor() {\n    super();\n\n    this.attachShadow({ mode: 'open' });\n    this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    this.isInCartItem = false;\n  }\n\n  public connectedCallback() {\n    this.render();\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    if (!this.isConnected || newValue === oldValue) {\n      return;\n    }\n\n    this.render();\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\n      // the whole mini cart modal's HTML is overwritten by the HTML fetch from the Shopify\n      // endpoint. Trying to wait the \"ofir-cart-call-ended\" event and then enable buttons cause\n      // a bug because this event fires after the mini card has been updated and always enable\n      // \"-\"/\"+\" buttons but 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\n    // `max` or `min`. If the final input value remains the same, there's no need to rerender and\n    // send an 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","export default \":host{--side-size:3rem;border:1px solid #e6e7e8!important;border-radius:.125rem;box-sizing:initial;display:grid;font-size:1rem;grid-template-columns:repeat(3,var(--side-size));grid-template-rows:var(--side-size);text-align:center}button{background-color:#0000;border:none;border-radius:.125rem;cursor:pointer;padding:0;position:relative;transition-duration:.15s;transition-property:background-color}button:focus{outline:2px solid #000;outline-offset:1px}button:disabled{cursor:default}button:disabled:after,button:disabled:before{background-color:#e6e7e8}button:after,button:before{background-color:#000;content:\\\"\\\";height:.125rem;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);width:.75rem}button:after{transform:translate(-50%,-50%) rotate(90deg)}button.minus-button:after{content:none}input{appearance:textfield;background-color:#0000;border:none;font-family:inherit;font-size:inherit;padding:.5rem;text-align:inherit}input:focus{outline:2px solid #000;outline-offset:1px}input::-webkit-inner-spin-button,input::-webkit-outer-spin-button{appearance:none;margin:0}.sr-only{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0,0,0,0);border-width:0;white-space:nowrap}\";","// Module\nvar code = \"<button type=\\\"button\\\" class=\\\"minus-button\\\"><span class=\\\"sr-only\\\">Decrease quantity by 1</span></button><input type=\\\"number\\\" name=\\\"quantity\\\" aria-label=\\\"Quantity\\\" min=\\\"1\\\" max=\\\"99\\\" value=\\\"1\\\"><button type=\\\"button\\\" class=\\\"plus-button\\\"><span class=\\\"sr-only\\\">Increase quantity by 1</span></button>\";\n// Exports\nexport default code;"],"names":["QuantityInput","HTMLElement","observedAttributes","constructor","super","initialized","onQuantityDecreasedBound","this","onQuantityDecreased","bind","onQuantityIncreasedBound","onQuantityIncreased","onInputChangedBound","onInputChanged","onCartUpdateStartedBound","onCartUpdateStarted","attachShadow","mode","shadowRoot","innerHTML","concat","isInCartItem","connectedCallback","render","addEventListener","disconnectedCallback","querySelector","removeEventListener","attributeChangedCallback","_name","oldValue","newValue","isConnected","setInputValue","value","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":""}