{"version":3,"names":["scCouponFormCss","ScCouponFormStyle0","wp","i18n","__","sprintf","_n","duration_in_months","humanDiscount","monthsLabel","h","exportparts","type","_e","this","discount","redeemable_status","class","clearable","editable","onScClear","_g","_f","promotion","code","input","value","_j","_h","_k","Fragment","slot","translateHumanDiscountWithDuration","isFreeTrial","renderTrialText","discountsDisplayAmount","currency","discountAmount","name","getHumanDiscountRedeemableStatus","_l","loading","busy","size","onClick","applyCoupon","buttonText","outline","error","open"],"sources":["src/components/ui/coupon-form/sc-coupon-form.scss?tag=sc-coupon-form&encapsulation=shadow","src/components/ui/coupon-form/sc-coupon-form.tsx"],"sourcesContent":[":host {\n  display: block;\n}\n\nsc-button {\n  color: var(--sc-color-primary-500);\n}\n\nsc-alert {\n  margin-top: var(--sc-spacing-small);\n}\n\n.coupon-form {\n  position: relative;\n  container-type: inline-size;\n\n  .coupon-button {\n    opacity: 0;\n    visibility: hidden;\n    transform: scale(0.9);\n    transition: all var(--sc-transition-fast) ease;\n    color: var(--sc-input-color);\n  }\n\n  .coupon-button-mobile {\n    margin-top: var(--sc-input-label-margin);\n    display: none;\n  }\n\n  &--has-value {\n    .coupon-button {\n      opacity: 1;\n      visibility: visible;\n      transform: scale(1);\n    }\n  }\n}\n\n@container (max-width: 320px) {\n  .coupon-form {\n    .coupon-button {\n      display: none;\n    }\n    .coupon-button-mobile {\n      display: block;\n    }\n  }\n}\n\n.form {\n  opacity: 0;\n  visibility: hidden;\n  height: 0;\n  transform: translateY(5px);\n  transition: opacity var(--sc-transition-medium) ease, transform var(--sc-transition-medium) ease;\n  position: relative;\n  gap: var(--sc-spacing-small);\n}\n\n.coupon-form--is-open {\n  .form {\n    opacity: 1;\n    visibility: visible;\n    transform: translateY(0);\n    height: auto;\n    margin: var(--sc-spacing-small) 0;\n  }\n\n  .trigger {\n    display: none;\n  }\n}\n\n.trigger {\n  cursor: pointer;\n  font-size: var(--sc-font-size-small);\n  line-height: var(--sc-line-height-dense);\n  color: var(--sc-input-label-color);\n  user-select: none;\n\n  &:hover {\n    text-decoration: underline;\n  }\n}\n\n.coupon-form--is-rtl {\n  .trigger {\n    text-align: right;\n  }\n}\n\n.coupon__status {\n  font-size: var(--sc-font-size-small);\n  line-height: var(--sc-line-height-dense);\n  color: var(--sc-color-warning-700);\n  display: inline-flex;\n  gap: var(--sc-spacing-x-small);\n  align-items: flex-start;\n  text-align: left;\n\n  sc-icon {\n    flex: 0 0 1em;\n    margin-top: 0.25em;\n  }\n}\n","import { Component, Element, h, Prop, State, Watch, Fragment, Method, Event, EventEmitter } from '@stencil/core';\nimport { speak } from '@wordpress/a11y';\nimport { __, sprintf, _n } from '@wordpress/i18n';\nimport { isRtl } from '../../../functions/page-align';\nimport { getHumanDiscount, getHumanDiscountRedeemableStatus } from '../../../functions/price';\nimport { DiscountResponse } from '../../../types';\nimport { state as checkoutState } from '../../../store/checkout';\n\n/**\n * @part base - The elements base wrapper.\n * @part form - The form.\n * @part input__base - The input base.\n * @part input - The input.\n * @part input__form-control - The input form control.\n * @part button__base - The button base element.\n * @part button__label - The button label.\n * @part info - The discount info.\n * @part discount - The discount displayed (% off)\n * @part amount - The discount amount.\n * @part discount-label - The discount label.\n * @part coupon-tag - The coupon tag.\n * @part error__base - The error base.\n * @part error__icon - The error icon\n * @part error__text - The error text.\n * @part error_title - The error title.\n * @part error__message - The error message.\n * @part block-ui - The block ui base component.\n * @part block-ui__content - The block ui content (spinner).\n */\n@Component({\n  tag: 'sc-coupon-form',\n  styleUrl: 'sc-coupon-form.scss',\n  shadow: true,\n})\nexport class ScCouponForm {\n  @Element() el: HTMLScCouponFormElement;\n  private input: HTMLScInputElement;\n  private couponTag: HTMLScTagElement;\n  private addCouponTrigger: HTMLElement;\n\n  /** The label for the coupon form */\n  @Prop() label: string;\n\n  /** Is the form loading */\n  @Prop() loading: boolean;\n\n  /** Is the form calculating */\n  @Prop() busy: boolean;\n\n  /** The placeholder for the input */\n  @Prop() placeholder: string;\n\n  /** The error message */\n  @Prop({ mutable: true }) error: string;\n\n  /** Force the form to show */\n  @Prop() forceOpen: boolean;\n\n  /** The discount */\n  @Prop() discount: DiscountResponse;\n\n  /** Currency */\n  @Prop() currency: string;\n\n  /** The discount amount */\n  @Prop() discountAmount: number;\n\n  /** The discounts display amount */\n  @Prop() discountsDisplayAmount: string;\n\n  /** Has recurring */\n  @Prop() showInterval: boolean;\n\n  /** Is it open */\n  @Prop({ mutable: true }) open: boolean;\n\n  @Prop() collapsed: boolean;\n\n  /** The value of the input */\n  @State() value: string;\n\n  /** When the coupon is applied */\n  @Event() scApplyCoupon: EventEmitter<string>;\n\n  /** The text for apply button */\n  @Prop({ reflect: true }) buttonText: string;\n\n  /** Is the form editable */\n  @Prop() editable: boolean = true;\n\n  /** Auto focus the input when opened. */\n  @Watch('open')\n  handleOpenChange(val) {\n    if (val) {\n      setTimeout(() => this.input.triggerFocus(), 50);\n    }\n  }\n\n  /** Close it when blurred and no value. */\n  handleBlur() {\n    if (!this.value) {\n      this.open = false;\n      this.error = '';\n    }\n  }\n\n  getHumanReadableDiscount() {\n    if (this?.discount?.coupon && this?.discount?.coupon.percent_off) {\n      return getHumanDiscount(this?.discount?.coupon);\n    }\n    return '';\n  }\n\n  /** Apply the coupon. */\n  applyCoupon() {\n    this.scApplyCoupon.emit(this.value);\n  }\n\n  handleKeyDown(e) {\n    if (e?.code === 'Enter') {\n      this.applyCoupon();\n    } else if (e?.code === 'Escape') {\n      this.scApplyCoupon.emit(null);\n      this.open = false;\n      speak(__('Coupon code field closed.', 'surecart'), 'assertive');\n    }\n  }\n\n  translateHumanDiscountWithDuration(humanDiscount) {\n    if (!this.showInterval) return humanDiscount;\n\n    const { duration, duration_in_months } = this.discount?.coupon;\n    switch (duration) {\n      case 'once':\n        return `${humanDiscount} ${__('once', 'surecart')}`;\n      case 'repeating':\n        const monthsLabel = sprintf(_n('%d month', '%d months', duration_in_months, 'surecart'), duration_in_months);\n        // translators: %s is the discount amount, %s is the duration (e.g. 3 months)\n        return sprintf(__('%s for %s', 'surecart'), humanDiscount, monthsLabel);\n      default:\n        return humanDiscount;\n    }\n  }\n\n  /** Focus the input. */\n  @Method()\n  async triggerFocus() {\n    await new Promise(resolve => requestAnimationFrame(resolve));\n\n    if (this?.discount?.promotion?.code) {\n      (this.couponTag.shadowRoot.querySelector('*') as HTMLElement)?.focus();\n    } else if (this.addCouponTrigger) {\n      this.addCouponTrigger.focus();\n    }\n  }\n\n  renderTrialText() {\n    if (this.discount?.coupon?.duration === 'once') {\n      return __('Applies on first payment', 'surecart');\n    }\n    return __('Starting on first payment', 'surecart');\n  }\n\n  render() {\n    const isFreeTrial = !!checkoutState?.checkout?.trial_amount && !checkoutState?.checkout?.amount_due;\n\n    if (this.loading) {\n      return <sc-skeleton style={{ width: '120px', display: 'inline-block' }}></sc-skeleton>;\n    }\n\n    if (this?.discount?.promotion?.code) {\n      let humanDiscount = this.getHumanReadableDiscount();\n\n      return (\n        <sc-line-item exportparts=\"description:info, price-description:discount, price:amount\">\n          <span slot=\"description\">\n            <div part=\"discount-label\">{__('Discount', 'surecart')}</div>\n            <sc-tag\n              exportparts=\"base:coupon-tag\"\n              type={'redeemable' === this.discount?.redeemable_status ? 'success' : 'warning'}\n              class=\"coupon-tag\"\n              clearable={this.editable}\n              onScClear={() => {\n                if (!this.editable) return;\n                this.scApplyCoupon.emit(null);\n                this.open = false;\n              }}\n              onKeyDown={e => {\n                if (!this.editable) return;\n                if (e.key === 'Enter' || e.key === 'Escape') {\n                  speak(__('Coupon was removed.', 'surecart'), 'assertive');\n                  this.scApplyCoupon.emit(null);\n                  this.open = false;\n                }\n              }}\n              ref={el => (this.couponTag = el as HTMLScTagElement)}\n              role=\"button\"\n              // translators: %s is the coupon code.\n              aria-label={sprintf(__('Press enter to remove coupon code %s.', 'surecart'), this?.discount?.promotion?.code || this.input.value || '')}\n            >\n              {this?.discount?.promotion?.code}\n            </sc-tag>\n          </span>\n\n          {'redeemable' === this.discount?.redeemable_status ? (\n            <Fragment>\n              {humanDiscount && (\n                <span class=\"coupon-human-discount\" slot=\"price-description\">\n                  {this.translateHumanDiscountWithDuration(humanDiscount)}\n                </span>\n              )}\n              <span slot={isFreeTrial ? 'price-description' : 'price'}>\n                {isFreeTrial ? (\n                  this.renderTrialText()\n                ) : this.discountsDisplayAmount ? (\n                  this.discountsDisplayAmount\n                ) : (\n                  <sc-format-number type=\"currency\" currency={this?.currency} value={this?.discountAmount}></sc-format-number>\n                )}\n              </span>\n            </Fragment>\n          ) : (\n            <div class=\"coupon__status\" slot=\"price-description\">\n              <sc-icon name=\"alert-triangle\" />\n              {getHumanDiscountRedeemableStatus(this.discount?.redeemable_status)}\n            </div>\n          )}\n        </sc-line-item>\n      );\n    }\n\n    return this.collapsed ? (\n      <div\n        part=\"base\"\n        class={{\n          'coupon-form': true,\n          'coupon-form--is-open': this.open || this.forceOpen,\n          'coupon-form--has-value': !!this.value,\n          'coupon-form--is-rtl': isRtl(),\n        }}\n      >\n        <div\n          part=\"label\"\n          class=\"trigger\"\n          onMouseDown={() => {\n            if (this.open) {\n              return;\n            }\n            this.open = true;\n          }}\n          onKeyDown={e => {\n            if (e.key !== 'Enter' && e.key !== ' ') {\n              return true;\n            }\n            if (this.open) {\n              return;\n            }\n            this.open = true;\n            speak(__('Coupon code field opened. Press Escape button to close it.', 'surecart'), 'assertive');\n          }}\n          tabindex=\"0\"\n          ref={el => (this.addCouponTrigger = el as HTMLElement)}\n          role=\"button\"\n        >\n          <slot name=\"label\">{this.label}</slot>\n        </div>\n\n        <div class=\"form\" part=\"form\">\n          <sc-input\n            exportparts=\"base:input__base, input, form-control:input__form-control\"\n            value={this.value}\n            onScInput={(e: any) => (this.value = e.target.value)}\n            placeholder={this.placeholder}\n            onScBlur={() => this.handleBlur()}\n            onKeyDown={e => this.handleKeyDown(e)}\n            ref={el => (this.input = el as HTMLScInputElement)}\n            aria-label={__('Add coupon code.', 'surecart')}\n          >\n            <sc-button\n              exportparts=\"base:button__base, label:button_label\"\n              slot=\"suffix\"\n              type=\"text\"\n              loading={this.busy}\n              size=\"medium\"\n              class=\"coupon-button\"\n              onClick={() => this.applyCoupon()}\n            >\n              <slot>{this.buttonText}</slot>\n            </sc-button>\n          </sc-input>\n          <sc-button\n            exportparts=\"base:button__base, label:button_label\"\n            type=\"primary\"\n            outline\n            loading={this.busy}\n            size=\"medium\"\n            class=\"coupon-button-mobile\"\n            onClick={() => this.applyCoupon()}\n          >\n            <slot>{this.buttonText}</slot>\n          </sc-button>\n          {!!this.error && (\n            <sc-alert exportparts=\"base:error__base, icon:error__icon, text:error__text, title:error_title, message:error__message\" type=\"danger\" open>\n              <span slot=\"title\">{this.error}</span>\n            </sc-alert>\n          )}\n        </div>\n\n        {this.loading && <sc-block-ui exportparts=\"base:block-ui, content:block-ui__content\"></sc-block-ui>}\n      </div>\n    ) : (\n      <div\n        class={{\n          'coupon-form': true,\n          'coupon-form--has-value': !!this.value,\n          'coupon-form--is-rtl': isRtl(),\n        }}\n      >\n        <sc-input\n          label={this.label}\n          exportparts=\"base:input__base, input, form-control:input__form-control\"\n          value={this.value}\n          onScInput={(e: any) => (this.value = e.target.value)}\n          placeholder={this.placeholder}\n          onScBlur={() => this.handleBlur()}\n          onKeyDown={e => this.handleKeyDown(e)}\n          ref={el => (this.input = el as HTMLScInputElement)}\n        >\n          <sc-button\n            exportparts=\"base:button__base, label:button_label\"\n            slot=\"suffix\"\n            type=\"text\"\n            loading={this.busy}\n            size=\"medium\"\n            class=\"coupon-button\"\n            onClick={() => this.applyCoupon()}\n          >\n            <slot>{this.buttonText}</slot>\n          </sc-button>\n        </sc-input>\n        <sc-button\n          exportparts=\"base:button__base, label:button_label\"\n          type=\"primary\"\n          outline\n          loading={this.busy}\n          size=\"medium\"\n          class=\"coupon-button-mobile\"\n          onClick={() => this.applyCoupon()}\n        >\n          <slot>{this.buttonText}</slot>\n        </sc-button>\n        {!!this.error && (\n          <sc-alert exportparts=\"base:error__base, icon:error__icon, text:error__text, title:error_title, message:error__message\" type=\"danger\" open>\n            <span slot=\"title\">{this.error}</span>\n          </sc-alert>\n        )}\n      </div>\n    );\n  }\n}\n"],"mappings":"+XAAA,MAAMA,EAAkB,60DACxB,MAAAC,EAAeD,E,sqCC2HHE,GAAAC,KAAAC,GAAE,oD,4MAUqBF,GAAAC,KAAAC,GAAE,qB,wBAETF,GAAAC,KAAAE,QAAQH,GAAAC,KAAAG,GAAE,uBAAAC,EAAA,YAAAA,G,OAEvBL,GAAAC,KAAAE,QAAQH,GAAAC,KAAAC,GAAE,wBAAAI,EAAAC,G,sgBAoBZP,GAAAC,KAAAC,GAAE,sC,QAEJF,GAAAC,KAAAC,GAAE,uC,4nBAgB2BF,GAAAC,KAAAC,GAAE,wBAAAM,EAAA,UAAAC,YAAA,kBAAAC,KAAA,iBAAAC,EAAAC,KAAAC,YAAA,MAAAF,SAAA,SAAAA,EAAAG,mBAAA,oBAAAC,MAAA,aAAAC,UAAAJ,KAAAK,SAAAC,UAAA,K,uJAclBlB,GAAAC,KAAAC,GAAE,+C,mGAQAF,GAAAC,KAAAE,QAAQH,GAAAC,KAAAC,GAAE,sDAAAiB,GAAAC,EAAAR,OAAA,MAAAA,YAAA,SAAAA,KAAAC,YAAA,MAAAO,SAAA,SAAAA,EAAAC,aAAA,MAAAF,SAAA,SAAAA,EAAAG,OAAAV,KAAAW,MAAAC,OAAA,MAAAC,GAAAC,EAAAd,OAAA,MAAAA,YAAA,SAAAA,KAAAC,YAAA,MAAAa,SAAA,SAAAA,EAAAL,aAAA,MAAAI,SAAA,SAAAA,EAAAH,OAAA,iBAAAK,EAAAf,KAAAC,YAAA,MAAAc,SAAA,SAAAA,EAAAb,mBAAAN,EAAAoB,EAAA,KAAAtB,GAAAE,EAAA,QAAAO,MAAA,wBAAAc,KAAA,qBAAAjB,KAAAkB,mCAAAxB,IAAAE,EAAA,QAAAqB,KAAAE,EAAA,6BAAAA,EAAAnB,KAAAoB,kBAAApB,KAAAqB,uBAAArB,KAAA,uBAAAJ,EAAA,oBAAAE,KAAA,WAAAwB,SAAAtB,OAAA,MAAAA,YAAA,SAAAA,KAAAsB,SAAAV,MAAAZ,OAAA,MAAAA,YAAA,SAAAA,KAAAuB,mBAAA3B,EAAA,OAAAO,MAAA,iBAAAc,KAAA,qBAAArB,EAAA,WAAA4B,KAAA,mBAAAC,GAAAC,EAAA1B,KAAAC,YAAA,MAAAyB,SAAA,SAAAA,EAAAxB,oB,sXA4DlBd,GAAAC,KAAAC,GAAE,uF,iZAkBIF,GAAAC,KAAAC,GAAE,gCAAAM,EAAA,aAAAC,YAAA,wCAAAoB,KAAA,SAAAnB,KAAA,OAAA6B,QAAA3B,KAAA4B,KAAAC,KAAA,SAAA1B,MAAA,gBAAA2B,QAAA,IAAA9B,KAAA+B,eAAAnC,EAAA,YAAAI,KAAAgC,cAAApC,EAAA,aAAAC,YAAA,wCAAAC,KAAA,UAAAmC,QAAA,KAAAN,QAAA3B,KAAA4B,KAAAC,KAAA,SAAA1B,MAAA,uBAAA2B,QAAA,IAAA9B,KAAA+B,eAAAnC,EAAA,YAAAI,KAAAgC,eAAAhC,KAAAkC,OAAAtC,EAAA,YAAAC,YAAA,kGAAAC,KAAA,SAAAqC,KAAA,MAAAvC,EAAA,QAAAqB,KAAA,SAAAjB,KAAAkC,SAAAlC,KAAA2B,SAAA/B,EAAA,eAAAC,YAAA,8CAAAD,EAAA,OAAAO,MAAA,C","ignoreList":[]}