{"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":[]}