@nymbalhq/web-components 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-6XCCAAMF.js → chunk-5G3C53A2.js} +15 -5
- package/dist/chunk-5G3C53A2.js.map +1 -0
- package/dist/{chunk-XE6IWISA.js → chunk-XPFGMYBV.js} +2 -2
- package/dist/elements/nymbal-checkout.d.ts +1 -1
- package/dist/elements/nymbal-checkout.js +1 -1
- package/dist/index.js +2 -2
- package/dist/register-components.js +2 -2
- package/dist/ssr/index.js +2 -2
- package/package.json +3 -3
- package/dist/chunk-6XCCAAMF.js.map +0 -1
- /package/dist/{chunk-XE6IWISA.js.map → chunk-XPFGMYBV.js.map} +0 -0
|
@@ -297,7 +297,7 @@ var NymbalCheckout = class extends NymbalElement {
|
|
|
297
297
|
billingSame = true;
|
|
298
298
|
placing = false;
|
|
299
299
|
placed = false;
|
|
300
|
-
/** True from a step change until the next animation frame
|
|
300
|
+
/** True from a step change until the next animation frame, so a second click of a double click is ignored. */
|
|
301
301
|
stepChanging = false;
|
|
302
302
|
stepTurn = 0;
|
|
303
303
|
heightRelease;
|
|
@@ -532,9 +532,17 @@ var NymbalCheckout = class extends NymbalElement {
|
|
|
532
532
|
<section part="section" data-testid="checkout-shipping" ?hidden=${this.step !== "shipping"}>
|
|
533
533
|
<form part="form" novalidate @submit=${this.onShipping}>
|
|
534
534
|
<h2 part="section-title">Shipping Address</h2>
|
|
535
|
-
<nymbal-address-form
|
|
535
|
+
<nymbal-address-form
|
|
536
|
+
id-prefix="shipping"
|
|
537
|
+
data-testid="shipping-form"
|
|
538
|
+
></nymbal-address-form>
|
|
536
539
|
<div part="actions">
|
|
537
|
-
<button
|
|
540
|
+
<button
|
|
541
|
+
type="button"
|
|
542
|
+
part="back"
|
|
543
|
+
class="btn btn-secondary"
|
|
544
|
+
@click=${this.back("contact")}
|
|
545
|
+
>
|
|
538
546
|
Back
|
|
539
547
|
</button>
|
|
540
548
|
<button
|
|
@@ -643,7 +651,9 @@ var NymbalCheckout = class extends NymbalElement {
|
|
|
643
651
|
${this.renderSteps()}
|
|
644
652
|
${this.error === null ? nothing : html`<div part="error" role="alert" data-testid="checkout-error">${this.error}</div>`}
|
|
645
653
|
<div part="layout">
|
|
646
|
-
<div part="main"
|
|
654
|
+
<div part="main">
|
|
655
|
+
${this.renderContact()} ${this.renderShipping()} ${this.renderPayment()}
|
|
656
|
+
</div>
|
|
647
657
|
${this.renderSummary()}
|
|
648
658
|
</div>
|
|
649
659
|
`;
|
|
@@ -653,4 +663,4 @@ var NymbalCheckout = class extends NymbalElement {
|
|
|
653
663
|
export {
|
|
654
664
|
NymbalCheckout
|
|
655
665
|
};
|
|
656
|
-
//# sourceMappingURL=chunk-
|
|
666
|
+
//# sourceMappingURL=chunk-5G3C53A2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/checkout.ts"],"sourcesContent":["import { css, html, nothing } from 'lit'\nimport type { PaymentConfirmation, PaymentConfirmContext } from '@nymbalhq/sdk'\nimport type { Address, CartItem } from '@nymbalhq/types'\nimport { baseStyles, NymbalElement, propertiesOf, type ElementSchema } from '../base-element.js'\nimport { renderField } from '../form-element.js'\nimport { formatPrice } from '../format.js'\nimport { shownCart } from '../cart-projection.js'\nimport { buttonStyles, fieldStyles } from '../view-styles.js'\nimport { NymbalAddressForm } from './address-form.js'\nimport type { NymbalPaymentElement } from './payment-element.js'\n\nconst SCHEMA: ElementSchema = {\n tag: 'nymbal-checkout',\n reactName: 'Checkout',\n // The cart a server read (cart-projection.ts), shown in the summary until the cart store settles.\n attributes: [{ name: 'cart', prop: 'cart', type: 'json' }],\n events: [\n {\n name: 'nymbal:checkout:placed',\n prop: 'onPlaced',\n detail: '{ orderNumber: string }',\n },\n ],\n parts: [\n 'layout',\n 'main',\n 'title',\n 'steps',\n 'step',\n 'step-active',\n 'step-done',\n 'step-divider',\n 'step-divider-done',\n 'error',\n 'section',\n 'form',\n 'section-title',\n 'field',\n 'label',\n 'input',\n 'billing-same',\n 'billing',\n 'billing-title',\n 'payment',\n 'actions',\n 'back',\n 'submit',\n 'terms',\n 'summary',\n 'summary-title',\n 'items',\n 'item',\n 'item-name',\n 'item-variant',\n 'item-qty',\n 'item-price',\n 'summary-row',\n 'subtotal',\n 'total',\n ],\n slots: [\n 'payment',\n 'title',\n 'contact-continue-label',\n 'shipping-continue-label',\n 'place-order-label',\n 'terms',\n 'summary-title',\n ],\n children: true,\n}\n\nconst STEPS = ['contact', 'shipping', 'payment'] as const\ntype Step = (typeof STEPS)[number]\nconst STEP_LABELS: Record<Step, string> = {\n contact: 'Contact',\n shipping: 'Shipping',\n payment: 'Payment',\n}\n\n/** Outlasts the 500 ms an OS allows between the two clicks of a double click. */\nconst STEP_HEIGHT_HOLD_MS = 600\nconst PAYMENT_FAILED = 'Payment failed. Please try again.'\n\n/** No payment element: nothing to confirm, as for a payments adapter with no client half. */\nconst NO_PAYMENT_STEP: PaymentConfirmation = { ok: true, confirmed: false }\n\n/**\n * The checkout: contact, shipping, then payment with billing, beside the order summary. It drives the SDK's checkout\n * store, and on an order dispatches `nymbal:checkout:placed`; the page navigates. The payment element is the page's\n * light-DOM child in the `payment` slot, so the payment driver's UI shares the document tree and is mounted once.\n */\nexport class NymbalCheckout extends NymbalElement {\n static override schema = SCHEMA\n static uses = [NymbalAddressForm]\n static override properties = propertiesOf(SCHEMA)\n static override styles = [\n baseStyles,\n buttonStyles,\n fieldStyles,\n css`\n [hidden] {\n display: none !important;\n }\n [part~='layout'] {\n display: grid;\n grid-template-columns: 1fr;\n gap: var(--nymbal-spacing-xl, 2rem);\n }\n @media (min-width: 769px) {\n [part~='layout'] {\n grid-template-columns: 1fr 380px;\n align-items: start;\n }\n }\n [part~='title'] {\n margin: 0 0 var(--nymbal-spacing-md, 1rem);\n font-size: 1.75rem;\n font-weight: var(--nymbal-font-weight-bold, 700);\n }\n [part~='steps'] {\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: var(--nymbal-spacing-xl, 2rem);\n padding: var(--nymbal-spacing-md, 1rem) 0;\n counter-reset: step;\n }\n [part~='step'] {\n display: flex;\n align-items: center;\n gap: var(--nymbal-spacing-sm, 0.5rem);\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n color: var(--nymbal-color-text-muted, #71717a);\n white-space: nowrap;\n }\n [part~='step']::before {\n counter-increment: step;\n content: counter(step);\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n flex-shrink: 0;\n border: 2px solid var(--nymbal-color-border, #e4e4e7);\n border-radius: var(--nymbal-radius-full, 9999px);\n font-size: var(--nymbal-font-size-xs, 0.75rem);\n font-weight: var(--nymbal-font-weight-bold, 700);\n }\n [part~='step-active'] {\n color: var(--nymbal-color-primary, #18181b);\n font-weight: var(--nymbal-font-weight-medium, 500);\n }\n [part~='step-active']::before {\n background-color: var(--nymbal-color-primary, #18181b);\n border-color: var(--nymbal-color-primary, #18181b);\n color: var(--nymbal-color-text-inverse, #fff);\n }\n [part~='step-done']::before {\n content: '\\\\2713';\n background-color: var(--nymbal-color-success, #16a34a);\n border-color: var(--nymbal-color-success, #16a34a);\n color: var(--nymbal-color-text-inverse, #fff);\n }\n [part~='step-divider'] {\n width: 40px;\n height: 2px;\n flex-shrink: 0;\n margin: 0 var(--nymbal-spacing-sm, 0.5rem);\n background-color: var(--nymbal-color-border, #e4e4e7);\n }\n [part~='step-divider-done'] {\n background-color: var(--nymbal-color-success, #16a34a);\n }\n @media (max-width: 640px) {\n [part~='step'] span {\n display: none;\n }\n [part~='step-divider'] {\n width: 24px;\n }\n }\n [part~='section-title'] {\n margin: 0 0 var(--nymbal-spacing-md, 1rem);\n font-size: var(--nymbal-font-size-lg, 1.125rem);\n font-weight: var(--nymbal-font-weight-bold, 700);\n color: var(--nymbal-color-primary, #18181b);\n }\n [part~='billing-same'] {\n display: flex;\n align-items: center;\n gap: var(--nymbal-spacing-sm, 0.5rem);\n margin-bottom: var(--nymbal-spacing-md, 1rem);\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n cursor: pointer;\n }\n [part~='billing-title'] {\n margin: 0 0 var(--nymbal-spacing-md, 1rem);\n font-size: var(--nymbal-font-size-base, 1rem);\n font-weight: var(--nymbal-font-weight-bold, 700);\n }\n [part~='payment'] {\n margin-top: var(--nymbal-spacing-md, 1rem);\n }\n [part~='actions'] {\n display: flex;\n justify-content: space-between;\n gap: var(--nymbal-spacing-sm, 0.5rem);\n margin-top: var(--nymbal-spacing-lg, 1.5rem);\n }\n [part~='submit'] {\n flex: 1;\n padding: var(--nymbal-spacing-md, 1rem);\n font-size: var(--nymbal-font-size-base, 1rem);\n }\n [part~='terms'] {\n margin: var(--nymbal-spacing-sm, 0.5rem) 0 0;\n font-size: var(--nymbal-font-size-xs, 0.75rem);\n color: var(--nymbal-color-text-muted, #71717a);\n text-align: center;\n }\n [part~='summary'] {\n position: sticky;\n top: 80px;\n padding: var(--nymbal-spacing-lg, 1.5rem);\n background-color: #fafafa;\n border: 1px solid var(--nymbal-color-border, #e4e4e7);\n border-radius: var(--nymbal-radius-lg, 0.75rem);\n }\n [part~='summary-title'] {\n margin: 0 0 var(--nymbal-spacing-md, 1rem);\n padding-bottom: var(--nymbal-spacing-md, 1rem);\n font-size: var(--nymbal-font-size-lg, 1.125rem);\n font-weight: var(--nymbal-font-weight-bold, 700);\n color: var(--nymbal-color-primary, #18181b);\n border-bottom: 1px solid var(--nymbal-color-border, #e4e4e7);\n }\n [part~='items'] {\n display: flex;\n flex-direction: column;\n gap: var(--nymbal-spacing-sm, 0.5rem);\n margin-bottom: var(--nymbal-spacing-md, 1rem);\n }\n [part~='item'] {\n display: flex;\n justify-content: space-between;\n align-items: start;\n gap: var(--nymbal-spacing-sm, 0.5rem);\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n }\n [part~='item-name'] {\n flex: 1;\n min-width: 0;\n }\n [part~='item-variant'],\n [part~='item-qty'] {\n flex-shrink: 0;\n color: var(--nymbal-color-text-muted, #71717a);\n }\n [part~='item-price'] {\n flex-shrink: 0;\n font-weight: var(--nymbal-font-weight-medium, 500);\n }\n dl {\n margin: 0;\n }\n dd {\n margin: 0;\n font-weight: var(--nymbal-font-weight-medium, 500);\n }\n [part~='summary-row'] {\n display: flex;\n justify-content: space-between;\n padding: var(--nymbal-spacing-xs, 0.25rem) 0;\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n }\n [part~='summary-row'] dt {\n color: var(--nymbal-color-text-muted, #71717a);\n }\n [part~='total'] {\n display: flex;\n justify-content: space-between;\n margin-top: var(--nymbal-spacing-sm, 0.5rem);\n padding-top: var(--nymbal-spacing-md, 1rem);\n font-size: var(--nymbal-font-size-lg, 1.125rem);\n font-weight: var(--nymbal-font-weight-bold, 700);\n color: var(--nymbal-color-primary, #18181b);\n border-top: 1px solid var(--nymbal-color-border, #e4e4e7);\n }\n `,\n ]\n\n private step: Step = 'contact'\n private error: string | null = null\n private billingSame = true\n private placing = false\n private placed = false\n /** True from a step change until the next animation frame, so a second click of a double click is ignored. */\n private stepChanging = false\n private stepTurn = 0\n private heightRelease: ReturnType<typeof setTimeout> | undefined\n\n declare cart: unknown\n\n protected override stores() {\n return [this.client.cart]\n }\n\n protected override storeSettled(): void {\n this.cart = undefined\n }\n\n override connectedCallback(): void {\n super.connectedCallback()\n this.addEventListener('nymbal:payment:status', this.onPaymentStatus)\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback()\n this.removeEventListener('nymbal:payment:status', this.onPaymentStatus)\n this.releaseMainHeight()\n }\n\n private readonly onPaymentStatus = (): void => this.requestUpdate()\n\n private paymentElement(): NymbalPaymentElement | null {\n return this.querySelector<NymbalPaymentElement>('nymbal-payment-element')\n }\n\n /**\n * Read at each render, so a status the child reached during its own upgrade, before this listened, still holds.\n * Without a client (a server render) there is no light DOM to read, and no order to place.\n */\n private paymentBlocked(): boolean {\n return this.readsStores && this.paymentElement()?.status === 'error'\n }\n\n private addressForm(prefix: 'shipping' | 'billing'): NymbalAddressForm | null {\n return this.renderRoot.querySelector<NymbalAddressForm>(\n `nymbal-address-form[id-prefix=\"${prefix}\"]`,\n )\n }\n\n private go(step: Step): void {\n this.holdMainHeight()\n this.step = step\n this.error = null\n this.stepChanging = true\n const turn = ++this.stepTurn\n requestAnimationFrame(() => {\n if (turn === this.stepTurn) this.stepChanging = false\n })\n this.requestUpdate()\n }\n\n /**\n * Keeps the main column at least as tall as the step it replaces until a double click is over. A shorter next step\n * would shorten the page under a still cursor, and the second click would land on whatever rose there (a footer\n * link), outside this element, where neither multi-click guard sees it. Released after 600 ms, past the 500 ms an OS\n * allows between the two clicks of a double click, so the column then settles to the shown step's own height.\n */\n private holdMainHeight(): void {\n const main = this.renderRoot.querySelector<HTMLElement>(\"[part~='main']\")\n if (main === null) return\n const height = main.offsetHeight\n if (height > (Number.parseFloat(main.style.minHeight) || 0))\n main.style.minHeight = `${height}px`\n clearTimeout(this.heightRelease)\n this.heightRelease = setTimeout(() => this.releaseMainHeight(), STEP_HEIGHT_HOLD_MS)\n }\n\n private releaseMainHeight(): void {\n clearTimeout(this.heightRelease)\n this.heightRelease = undefined\n const main = this.renderRoot.querySelector<HTMLElement>(\"[part~='main']\")\n if (main !== null) main.style.minHeight = ''\n }\n\n private showError(message: string): void {\n this.error = message\n this.requestUpdate()\n }\n\n /**\n * Whether a submit of `step`'s form goes ahead. The step setters are synchronous, so by a double click's second\n * click the next step has rendered under the cursor: a submit in the frame of a step change is not the shopper's.\n */\n private accepts(event: Event, step: Step): boolean {\n event.preventDefault()\n return this.step === step && !this.stepChanging\n }\n\n /** The second click of a multi-click is not a decision to continue or to pay; the first one already submitted. */\n private readonly onSubmitClick = (event: MouseEvent): void => {\n if (event.detail > 1) event.preventDefault()\n }\n\n private readonly onContact = (event: Event): void => {\n if (!this.accepts(event, 'contact')) return\n const email =\n this.renderRoot.querySelector<HTMLInputElement>('#contact-email')?.value.trim() ?? ''\n if (email === '') return this.showError('Please enter your email address.')\n this.client.checkout.setEmail(email)\n this.go('shipping')\n }\n\n private readonly onShipping = (event: Event): void => {\n if (!this.accepts(event, 'shipping')) return\n const form = this.addressForm('shipping')\n if (form === null || !form.reportValidity()) return\n this.client.checkout.setShippingAddress(form.value)\n this.client.checkout.setShippingMethod({\n carrier: 'Standard',\n service: 'Standard Shipping',\n amountMinor: 0,\n currency: this.client.cart.getState().currency || 'GBP',\n estimatedDays: 5,\n })\n this.go('payment')\n }\n\n private billingAddress(): Address | null {\n if (this.billingSame) return this.client.checkout.getState().shippingAddress\n const form = this.addressForm('billing')\n if (form === null || !form.reportValidity()) return null\n return form.value\n }\n\n private readonly onPlace = (event: Event): void => {\n if (!this.accepts(event, 'payment')) return\n if (this.placing || this.placed || this.paymentBlocked()) return\n const billing = this.billingAddress()\n if (billing === null) return\n this.placing = true\n this.error = null\n this.requestUpdate()\n void this.place(billing)\n }\n\n /** Calls the SDK before its first `await`, so the in-flight flag set by the caller covers the order request. */\n private async place(billing: Address): Promise<void> {\n const checkout = this.client.checkout\n try {\n checkout.setBillingAddress(billing)\n const result = await checkout.submitPayment()\n if (result === null) {\n // A resubmit of a cart that already became an order (Back, then Place Order) goes to that order.\n const { placedOrderNumber, error } = checkout.getState()\n if (placedOrderNumber) return this.leaveFor(placedOrderNumber)\n return this.stay(error ?? PAYMENT_FAILED)\n }\n if (result.payment.status === 'failed') {\n return this.stay(checkout.getState().error ?? PAYMENT_FAILED)\n }\n // Confirmed only when the driver says the payment went through; otherwise the order shows it awaiting payment.\n if (result.payment.status === 'requires_action') {\n const { email, billingAddress } = checkout.getState()\n const confirmation = await this.confirm(result.paymentIntent.clientSecret, {\n ...(email !== '' && { email }),\n ...(billingAddress !== null && { billing: billingAddress }),\n })\n if (!confirmation.ok) return this.stay(confirmation.error)\n if (confirmation.confirmed) checkout.markConfirmed()\n }\n this.leaveFor(result.orderNumber)\n } catch (err) {\n this.stay(err instanceof Error && err.message !== '' ? err.message : PAYMENT_FAILED)\n }\n }\n\n private confirm(\n clientSecret: string,\n context: PaymentConfirmContext,\n ): Promise<PaymentConfirmation> {\n return this.paymentElement()?.confirm(clientSecret, context) ?? Promise.resolve(NO_PAYMENT_STEP)\n }\n\n private stay(message: string): void {\n this.placing = false\n this.showError(message)\n }\n\n /** Place Order stays disabled from here through the page's navigation. */\n private leaveFor(orderNumber: string): void {\n this.client.checkout.reset()\n this.placing = false\n this.placed = true\n this.requestUpdate()\n this.dispatchEvent(\n new CustomEvent('nymbal:checkout:placed', {\n bubbles: true,\n composed: true,\n detail: { orderNumber },\n }),\n )\n }\n\n private readonly onBillingSame = (event: Event): void => {\n this.billingSame = (event.target as HTMLInputElement).checked\n this.requestUpdate()\n }\n\n private back(step: Step): () => void {\n return () => {\n if (!this.placing && !this.placed) this.go(step)\n }\n }\n\n private renderSteps() {\n const current = STEPS.indexOf(this.step)\n return html`\n <div part=\"steps\" data-testid=\"checkout-steps\">\n ${STEPS.map((step, index) => {\n const state = index < current ? ' step-done' : index === current ? ' step-active' : ''\n return html`\n ${index > 0\n ? html`<div\n part=\"step-divider${index <= current ? ' step-divider-done' : ''}\"\n aria-hidden=\"true\"\n ></div>`\n : nothing}\n <div\n part=\"step${state}\"\n data-testid=\"checkout-step-${step}\"\n aria-current=${index === current ? 'step' : nothing}\n >\n <span>${STEP_LABELS[step]}</span>\n </div>\n `\n })}\n </div>\n `\n }\n\n private renderContact() {\n return html`\n <section part=\"section\" data-testid=\"checkout-contact\" ?hidden=${this.step !== 'contact'}>\n <form part=\"form\" @submit=${this.onContact}>\n <h2 part=\"section-title\">Contact Information</h2>\n ${renderField({\n id: 'contact-email',\n name: 'email',\n label: 'Email Address',\n type: 'email',\n autocomplete: 'email',\n required: true,\n placeholder: 'you@example.com',\n })}\n <div part=\"actions\">\n <button\n type=\"submit\"\n part=\"submit\"\n class=\"btn btn-primary\"\n data-testid=\"contact-continue\"\n @click=${this.onSubmitClick}\n >\n <slot name=\"contact-continue-label\">Continue to Shipping</slot>\n </button>\n </div>\n </form>\n </section>\n `\n }\n\n private renderShipping() {\n return html`\n <section part=\"section\" data-testid=\"checkout-shipping\" ?hidden=${this.step !== 'shipping'}>\n <form part=\"form\" novalidate @submit=${this.onShipping}>\n <h2 part=\"section-title\">Shipping Address</h2>\n <nymbal-address-form\n id-prefix=\"shipping\"\n data-testid=\"shipping-form\"\n ></nymbal-address-form>\n <div part=\"actions\">\n <button\n type=\"button\"\n part=\"back\"\n class=\"btn btn-secondary\"\n @click=${this.back('contact')}\n >\n Back\n </button>\n <button\n type=\"submit\"\n part=\"submit\"\n class=\"btn btn-primary\"\n data-testid=\"shipping-continue\"\n @click=${this.onSubmitClick}\n >\n <slot name=\"shipping-continue-label\">Continue to Payment</slot>\n </button>\n </div>\n </form>\n </section>\n `\n }\n\n private renderPayment() {\n const busy = this.placing || this.placed\n return html`\n <section part=\"section\" data-testid=\"checkout-payment\" ?hidden=${this.step !== 'payment'}>\n <form part=\"form\" novalidate @submit=${this.onPlace}>\n <h2 part=\"section-title\">Payment</h2>\n <label part=\"billing-same\">\n <input\n type=\"checkbox\"\n id=\"billing-same\"\n data-testid=\"billing-same\"\n .checked=${this.billingSame}\n @change=${this.onBillingSame}\n />\n Billing address same as shipping\n </label>\n <div part=\"billing\" data-testid=\"billing-address\" ?hidden=${this.billingSame}>\n <h3 part=\"billing-title\">Billing Address</h3>\n <nymbal-address-form id-prefix=\"billing\"></nymbal-address-form>\n </div>\n <div part=\"payment\"><slot name=\"payment\"></slot></div>\n <div part=\"actions\">\n <button\n type=\"button\"\n part=\"back\"\n class=\"btn btn-secondary\"\n ?disabled=${busy}\n @click=${this.back('shipping')}\n >\n Back\n </button>\n <button\n type=\"submit\"\n part=\"submit\"\n class=\"btn btn-primary\"\n data-testid=\"place-order\"\n ?disabled=${busy || this.paymentBlocked()}\n @click=${this.onSubmitClick}\n >\n ${this.placing\n ? 'Processing...'\n : html`<slot name=\"place-order-label\">Place Order</slot>`}\n </button>\n </div>\n <p part=\"terms\">\n <slot name=\"terms\">By placing your order, you agree to our terms and conditions.</slot>\n </p>\n </form>\n </section>\n `\n }\n\n private renderLine(item: CartItem, currency: string) {\n return html`\n <div part=\"item\">\n <span part=\"item-name\">${item.productName}</span>\n ${item.variantName ? html`<span part=\"item-variant\">${item.variantName}</span>` : nothing}\n <span part=\"item-qty\">x${item.qty}</span>\n <span part=\"item-price\">${formatPrice(item.priceMinor * item.qty, currency)}</span>\n </div>\n `\n }\n\n private renderSummary() {\n const cart = shownCart(this, this.cart, this.readsStores ? this.client : null)\n const currency = cart.currency || 'GBP'\n const subtotal = formatPrice(cart.subtotalMinor, currency)\n return html`\n <aside part=\"summary\" data-testid=\"checkout-summary\">\n <h2 part=\"summary-title\"><slot name=\"summary-title\">Order Summary</slot></h2>\n ${cart.items.length === 0\n ? nothing\n : html`<div part=\"items\">\n ${cart.items.map((item) => this.renderLine(item, currency))}\n </div>`}\n <dl>\n <div part=\"summary-row subtotal\">\n <dt>Subtotal</dt>\n <dd data-testid=\"cart-subtotal\">${subtotal}</dd>\n </div>\n <div part=\"summary-row\">\n <dt>Shipping</dt>\n <dd>Standard, free</dd>\n </div>\n </dl>\n <div part=\"total\">\n <span>Total</span>\n <span>${subtotal}</span>\n </div>\n </aside>\n `\n }\n\n protected override render() {\n return html`\n <h1 part=\"title\"><slot name=\"title\">Checkout</slot></h1>\n ${this.renderSteps()}\n ${this.error === null\n ? nothing\n : html`<div part=\"error\" role=\"alert\" data-testid=\"checkout-error\">${this.error}</div>`}\n <div part=\"layout\">\n <div part=\"main\">\n ${this.renderContact()} ${this.renderShipping()} ${this.renderPayment()}\n </div>\n ${this.renderSummary()}\n </div>\n `\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
} from "./chunk-IYT7VXFS.js";
|
|
59
59
|
import {
|
|
60
60
|
NymbalCheckout
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-5G3C53A2.js";
|
|
62
62
|
import {
|
|
63
63
|
NymbalImageGallery
|
|
64
64
|
} from "./chunk-U2PU4CWB.js";
|
|
@@ -134,4 +134,4 @@ export {
|
|
|
134
134
|
ELEMENTS,
|
|
135
135
|
registerComponents
|
|
136
136
|
};
|
|
137
|
-
//# sourceMappingURL=chunk-
|
|
137
|
+
//# sourceMappingURL=chunk-XPFGMYBV.js.map
|
|
@@ -19,7 +19,7 @@ declare class NymbalCheckout extends NymbalElement {
|
|
|
19
19
|
private billingSame;
|
|
20
20
|
private placing;
|
|
21
21
|
private placed;
|
|
22
|
-
/** True from a step change until the next animation frame
|
|
22
|
+
/** True from a step change until the next animation frame, so a second click of a double click is ignored. */
|
|
23
23
|
private stepChanging;
|
|
24
24
|
private stepTurn;
|
|
25
25
|
private heightRelease;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import '@lit-labs/ssr-client/lit-element-hydrate-support.js'
|
|
2
2
|
import {
|
|
3
3
|
registerComponents
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XPFGMYBV.js";
|
|
5
5
|
import {
|
|
6
6
|
NymbalVariantSelector
|
|
7
7
|
} from "./chunk-H3ZBYNHV.js";
|
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
} from "./chunk-IYT7VXFS.js";
|
|
63
63
|
import {
|
|
64
64
|
NymbalCheckout
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-5G3C53A2.js";
|
|
66
66
|
import {
|
|
67
67
|
NymbalImageGallery
|
|
68
68
|
} from "./chunk-U2PU4CWB.js";
|
|
@@ -2,7 +2,7 @@ import '@lit-labs/ssr-client/lit-element-hydrate-support.js'
|
|
|
2
2
|
import {
|
|
3
3
|
ELEMENTS,
|
|
4
4
|
registerComponents
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-XPFGMYBV.js";
|
|
6
6
|
import "./chunk-H3ZBYNHV.js";
|
|
7
7
|
import "./chunk-7JAAOVCE.js";
|
|
8
8
|
import "./chunk-4COVK727.js";
|
|
@@ -23,7 +23,7 @@ import "./chunk-NE6UHTOT.js";
|
|
|
23
23
|
import "./chunk-VTQVGNVC.js";
|
|
24
24
|
import "./chunk-HPBC3OG7.js";
|
|
25
25
|
import "./chunk-IYT7VXFS.js";
|
|
26
|
-
import "./chunk-
|
|
26
|
+
import "./chunk-5G3C53A2.js";
|
|
27
27
|
import "./chunk-U2PU4CWB.js";
|
|
28
28
|
import "./chunk-42SSME5H.js";
|
|
29
29
|
import "./chunk-2XXBQPDB.js";
|
package/dist/ssr/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import '@lit-labs/ssr-client/lit-element-hydrate-support.js'
|
|
2
2
|
import {
|
|
3
3
|
ELEMENTS
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-XPFGMYBV.js";
|
|
5
5
|
import "../chunk-H3ZBYNHV.js";
|
|
6
6
|
import "../chunk-7JAAOVCE.js";
|
|
7
7
|
import "../chunk-4COVK727.js";
|
|
@@ -25,7 +25,7 @@ import "../chunk-NE6UHTOT.js";
|
|
|
25
25
|
import "../chunk-VTQVGNVC.js";
|
|
26
26
|
import "../chunk-HPBC3OG7.js";
|
|
27
27
|
import "../chunk-IYT7VXFS.js";
|
|
28
|
-
import "../chunk-
|
|
28
|
+
import "../chunk-5G3C53A2.js";
|
|
29
29
|
import "../chunk-U2PU4CWB.js";
|
|
30
30
|
import "../chunk-42SSME5H.js";
|
|
31
31
|
import "../chunk-2XXBQPDB.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nymbalhq/web-components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Interactive commerce UI as native Web Components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro-component",
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@lit-labs/ssr": "^3.3.1",
|
|
56
56
|
"@lit-labs/ssr-client": "^1.1.8",
|
|
57
|
-
"@nymbalhq/sdk": "1.
|
|
58
|
-
"@nymbalhq/types": "1.
|
|
57
|
+
"@nymbalhq/sdk": "1.3.0",
|
|
58
|
+
"@nymbalhq/types": "1.3.0",
|
|
59
59
|
"lit": "^3.3.3"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/checkout.ts"],"sourcesContent":["import { css, html, nothing } from 'lit'\nimport type { PaymentConfirmation, PaymentConfirmContext } from '@nymbalhq/sdk'\nimport type { Address, CartItem } from '@nymbalhq/types'\nimport { baseStyles, NymbalElement, propertiesOf, type ElementSchema } from '../base-element.js'\nimport { renderField } from '../form-element.js'\nimport { formatPrice } from '../format.js'\nimport { shownCart } from '../cart-projection.js'\nimport { buttonStyles, fieldStyles } from '../view-styles.js'\nimport { NymbalAddressForm } from './address-form.js'\nimport type { NymbalPaymentElement } from './payment-element.js'\n\nconst SCHEMA: ElementSchema = {\n tag: 'nymbal-checkout',\n reactName: 'Checkout',\n // The cart a server read (cart-projection.ts), shown in the summary until the cart store settles.\n attributes: [{ name: 'cart', prop: 'cart', type: 'json' }],\n events: [\n {\n name: 'nymbal:checkout:placed',\n prop: 'onPlaced',\n detail: '{ orderNumber: string }',\n },\n ],\n parts: [\n 'layout',\n 'main',\n 'title',\n 'steps',\n 'step',\n 'step-active',\n 'step-done',\n 'step-divider',\n 'step-divider-done',\n 'error',\n 'section',\n 'form',\n 'section-title',\n 'field',\n 'label',\n 'input',\n 'billing-same',\n 'billing',\n 'billing-title',\n 'payment',\n 'actions',\n 'back',\n 'submit',\n 'terms',\n 'summary',\n 'summary-title',\n 'items',\n 'item',\n 'item-name',\n 'item-variant',\n 'item-qty',\n 'item-price',\n 'summary-row',\n 'subtotal',\n 'total',\n ],\n slots: [\n 'payment',\n 'title',\n 'contact-continue-label',\n 'shipping-continue-label',\n 'place-order-label',\n 'terms',\n 'summary-title',\n ],\n children: true,\n}\n\nconst STEPS = ['contact', 'shipping', 'payment'] as const\ntype Step = (typeof STEPS)[number]\nconst STEP_LABELS: Record<Step, string> = {\n contact: 'Contact',\n shipping: 'Shipping',\n payment: 'Payment',\n}\n\n/** Outlasts the 500 ms an OS allows between the two clicks of a double click. */\nconst STEP_HEIGHT_HOLD_MS = 600\nconst PAYMENT_FAILED = 'Payment failed. Please try again.'\n\n/** No payment element: nothing to confirm, as for a payments adapter with no client half. */\nconst NO_PAYMENT_STEP: PaymentConfirmation = { ok: true, confirmed: false }\n\n/**\n * The checkout: contact, shipping, then payment with billing, beside the order summary. It drives the SDK's checkout\n * store, and on an order dispatches `nymbal:checkout:placed`; the page navigates. The payment element is the page's\n * light-DOM child in the `payment` slot, so the payment driver's UI shares the document tree and is mounted once.\n */\nexport class NymbalCheckout extends NymbalElement {\n static override schema = SCHEMA\n static uses = [NymbalAddressForm]\n static override properties = propertiesOf(SCHEMA)\n static override styles = [\n baseStyles,\n buttonStyles,\n fieldStyles,\n css`\n [hidden] {\n display: none !important;\n }\n [part~='layout'] {\n display: grid;\n grid-template-columns: 1fr;\n gap: var(--nymbal-spacing-xl, 2rem);\n }\n @media (min-width: 769px) {\n [part~='layout'] {\n grid-template-columns: 1fr 380px;\n align-items: start;\n }\n }\n [part~='title'] {\n margin: 0 0 var(--nymbal-spacing-md, 1rem);\n font-size: 1.75rem;\n font-weight: var(--nymbal-font-weight-bold, 700);\n }\n [part~='steps'] {\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: var(--nymbal-spacing-xl, 2rem);\n padding: var(--nymbal-spacing-md, 1rem) 0;\n counter-reset: step;\n }\n [part~='step'] {\n display: flex;\n align-items: center;\n gap: var(--nymbal-spacing-sm, 0.5rem);\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n color: var(--nymbal-color-text-muted, #71717a);\n white-space: nowrap;\n }\n [part~='step']::before {\n counter-increment: step;\n content: counter(step);\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n flex-shrink: 0;\n border: 2px solid var(--nymbal-color-border, #e4e4e7);\n border-radius: var(--nymbal-radius-full, 9999px);\n font-size: var(--nymbal-font-size-xs, 0.75rem);\n font-weight: var(--nymbal-font-weight-bold, 700);\n }\n [part~='step-active'] {\n color: var(--nymbal-color-primary, #18181b);\n font-weight: var(--nymbal-font-weight-medium, 500);\n }\n [part~='step-active']::before {\n background-color: var(--nymbal-color-primary, #18181b);\n border-color: var(--nymbal-color-primary, #18181b);\n color: var(--nymbal-color-text-inverse, #fff);\n }\n [part~='step-done']::before {\n content: '\\\\2713';\n background-color: var(--nymbal-color-success, #16a34a);\n border-color: var(--nymbal-color-success, #16a34a);\n color: var(--nymbal-color-text-inverse, #fff);\n }\n [part~='step-divider'] {\n width: 40px;\n height: 2px;\n flex-shrink: 0;\n margin: 0 var(--nymbal-spacing-sm, 0.5rem);\n background-color: var(--nymbal-color-border, #e4e4e7);\n }\n [part~='step-divider-done'] {\n background-color: var(--nymbal-color-success, #16a34a);\n }\n @media (max-width: 640px) {\n [part~='step'] span {\n display: none;\n }\n [part~='step-divider'] {\n width: 24px;\n }\n }\n [part~='section-title'] {\n margin: 0 0 var(--nymbal-spacing-md, 1rem);\n font-size: var(--nymbal-font-size-lg, 1.125rem);\n font-weight: var(--nymbal-font-weight-bold, 700);\n color: var(--nymbal-color-primary, #18181b);\n }\n [part~='billing-same'] {\n display: flex;\n align-items: center;\n gap: var(--nymbal-spacing-sm, 0.5rem);\n margin-bottom: var(--nymbal-spacing-md, 1rem);\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n cursor: pointer;\n }\n [part~='billing-title'] {\n margin: 0 0 var(--nymbal-spacing-md, 1rem);\n font-size: var(--nymbal-font-size-base, 1rem);\n font-weight: var(--nymbal-font-weight-bold, 700);\n }\n [part~='payment'] {\n margin-top: var(--nymbal-spacing-md, 1rem);\n }\n [part~='actions'] {\n display: flex;\n justify-content: space-between;\n gap: var(--nymbal-spacing-sm, 0.5rem);\n margin-top: var(--nymbal-spacing-lg, 1.5rem);\n }\n [part~='submit'] {\n flex: 1;\n padding: var(--nymbal-spacing-md, 1rem);\n font-size: var(--nymbal-font-size-base, 1rem);\n }\n [part~='terms'] {\n margin: var(--nymbal-spacing-sm, 0.5rem) 0 0;\n font-size: var(--nymbal-font-size-xs, 0.75rem);\n color: var(--nymbal-color-text-muted, #71717a);\n text-align: center;\n }\n [part~='summary'] {\n position: sticky;\n top: 80px;\n padding: var(--nymbal-spacing-lg, 1.5rem);\n background-color: #fafafa;\n border: 1px solid var(--nymbal-color-border, #e4e4e7);\n border-radius: var(--nymbal-radius-lg, 0.75rem);\n }\n [part~='summary-title'] {\n margin: 0 0 var(--nymbal-spacing-md, 1rem);\n padding-bottom: var(--nymbal-spacing-md, 1rem);\n font-size: var(--nymbal-font-size-lg, 1.125rem);\n font-weight: var(--nymbal-font-weight-bold, 700);\n color: var(--nymbal-color-primary, #18181b);\n border-bottom: 1px solid var(--nymbal-color-border, #e4e4e7);\n }\n [part~='items'] {\n display: flex;\n flex-direction: column;\n gap: var(--nymbal-spacing-sm, 0.5rem);\n margin-bottom: var(--nymbal-spacing-md, 1rem);\n }\n [part~='item'] {\n display: flex;\n justify-content: space-between;\n align-items: start;\n gap: var(--nymbal-spacing-sm, 0.5rem);\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n }\n [part~='item-name'] {\n flex: 1;\n min-width: 0;\n }\n [part~='item-variant'],\n [part~='item-qty'] {\n flex-shrink: 0;\n color: var(--nymbal-color-text-muted, #71717a);\n }\n [part~='item-price'] {\n flex-shrink: 0;\n font-weight: var(--nymbal-font-weight-medium, 500);\n }\n dl {\n margin: 0;\n }\n dd {\n margin: 0;\n font-weight: var(--nymbal-font-weight-medium, 500);\n }\n [part~='summary-row'] {\n display: flex;\n justify-content: space-between;\n padding: var(--nymbal-spacing-xs, 0.25rem) 0;\n font-size: var(--nymbal-font-size-sm, 0.875rem);\n }\n [part~='summary-row'] dt {\n color: var(--nymbal-color-text-muted, #71717a);\n }\n [part~='total'] {\n display: flex;\n justify-content: space-between;\n margin-top: var(--nymbal-spacing-sm, 0.5rem);\n padding-top: var(--nymbal-spacing-md, 1rem);\n font-size: var(--nymbal-font-size-lg, 1.125rem);\n font-weight: var(--nymbal-font-weight-bold, 700);\n color: var(--nymbal-color-primary, #18181b);\n border-top: 1px solid var(--nymbal-color-border, #e4e4e7);\n }\n `,\n ]\n\n private step: Step = 'contact'\n private error: string | null = null\n private billingSame = true\n private placing = false\n private placed = false\n /** True from a step change until the next animation frame (Decision 4's multi-click rule). */\n private stepChanging = false\n private stepTurn = 0\n private heightRelease: ReturnType<typeof setTimeout> | undefined\n\n declare cart: unknown\n\n protected override stores() {\n return [this.client.cart]\n }\n\n protected override storeSettled(): void {\n this.cart = undefined\n }\n\n override connectedCallback(): void {\n super.connectedCallback()\n this.addEventListener('nymbal:payment:status', this.onPaymentStatus)\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback()\n this.removeEventListener('nymbal:payment:status', this.onPaymentStatus)\n this.releaseMainHeight()\n }\n\n private readonly onPaymentStatus = (): void => this.requestUpdate()\n\n private paymentElement(): NymbalPaymentElement | null {\n return this.querySelector<NymbalPaymentElement>('nymbal-payment-element')\n }\n\n /**\n * Read at each render, so a status the child reached during its own upgrade, before this listened, still holds.\n * Without a client (a server render) there is no light DOM to read, and no order to place.\n */\n private paymentBlocked(): boolean {\n return this.readsStores && this.paymentElement()?.status === 'error'\n }\n\n private addressForm(prefix: 'shipping' | 'billing'): NymbalAddressForm | null {\n return this.renderRoot.querySelector<NymbalAddressForm>(\n `nymbal-address-form[id-prefix=\"${prefix}\"]`,\n )\n }\n\n private go(step: Step): void {\n this.holdMainHeight()\n this.step = step\n this.error = null\n this.stepChanging = true\n const turn = ++this.stepTurn\n requestAnimationFrame(() => {\n if (turn === this.stepTurn) this.stepChanging = false\n })\n this.requestUpdate()\n }\n\n /**\n * Keeps the main column at least as tall as the step it replaces until a double click is over. A shorter next step\n * would shorten the page under a still cursor, and the second click would land on whatever rose there (a footer\n * link), outside this element, where neither multi-click guard sees it. Released after 600 ms, past the 500 ms an OS\n * allows between the two clicks of a double click, so the column then settles to the shown step's own height.\n */\n private holdMainHeight(): void {\n const main = this.renderRoot.querySelector<HTMLElement>(\"[part~='main']\")\n if (main === null) return\n const height = main.offsetHeight\n if (height > (Number.parseFloat(main.style.minHeight) || 0))\n main.style.minHeight = `${height}px`\n clearTimeout(this.heightRelease)\n this.heightRelease = setTimeout(() => this.releaseMainHeight(), STEP_HEIGHT_HOLD_MS)\n }\n\n private releaseMainHeight(): void {\n clearTimeout(this.heightRelease)\n this.heightRelease = undefined\n const main = this.renderRoot.querySelector<HTMLElement>(\"[part~='main']\")\n if (main !== null) main.style.minHeight = ''\n }\n\n private showError(message: string): void {\n this.error = message\n this.requestUpdate()\n }\n\n /**\n * Whether a submit of `step`'s form goes ahead. The step setters are synchronous, so by a double click's second\n * click the next step has rendered under the cursor: a submit in the frame of a step change is not the shopper's.\n */\n private accepts(event: Event, step: Step): boolean {\n event.preventDefault()\n return this.step === step && !this.stepChanging\n }\n\n /** The second click of a multi-click is not a decision to continue or to pay; the first one already submitted. */\n private readonly onSubmitClick = (event: MouseEvent): void => {\n if (event.detail > 1) event.preventDefault()\n }\n\n private readonly onContact = (event: Event): void => {\n if (!this.accepts(event, 'contact')) return\n const email =\n this.renderRoot.querySelector<HTMLInputElement>('#contact-email')?.value.trim() ?? ''\n if (email === '') return this.showError('Please enter your email address.')\n this.client.checkout.setEmail(email)\n this.go('shipping')\n }\n\n private readonly onShipping = (event: Event): void => {\n if (!this.accepts(event, 'shipping')) return\n const form = this.addressForm('shipping')\n if (form === null || !form.reportValidity()) return\n this.client.checkout.setShippingAddress(form.value)\n this.client.checkout.setShippingMethod({\n carrier: 'Standard',\n service: 'Standard Shipping',\n amountMinor: 0,\n currency: this.client.cart.getState().currency || 'GBP',\n estimatedDays: 5,\n })\n this.go('payment')\n }\n\n private billingAddress(): Address | null {\n if (this.billingSame) return this.client.checkout.getState().shippingAddress\n const form = this.addressForm('billing')\n if (form === null || !form.reportValidity()) return null\n return form.value\n }\n\n private readonly onPlace = (event: Event): void => {\n if (!this.accepts(event, 'payment')) return\n if (this.placing || this.placed || this.paymentBlocked()) return\n const billing = this.billingAddress()\n if (billing === null) return\n this.placing = true\n this.error = null\n this.requestUpdate()\n void this.place(billing)\n }\n\n /** Calls the SDK before its first `await`, so the in-flight flag set by the caller covers the order request. */\n private async place(billing: Address): Promise<void> {\n const checkout = this.client.checkout\n try {\n checkout.setBillingAddress(billing)\n const result = await checkout.submitPayment()\n if (result === null) {\n // A resubmit of a cart that already became an order (Back, then Place Order) goes to that order.\n const { placedOrderNumber, error } = checkout.getState()\n if (placedOrderNumber) return this.leaveFor(placedOrderNumber)\n return this.stay(error ?? PAYMENT_FAILED)\n }\n if (result.payment.status === 'failed') {\n return this.stay(checkout.getState().error ?? PAYMENT_FAILED)\n }\n // Confirmed only when the driver says the payment went through; otherwise the order shows it awaiting payment.\n if (result.payment.status === 'requires_action') {\n const { email, billingAddress } = checkout.getState()\n const confirmation = await this.confirm(result.paymentIntent.clientSecret, {\n ...(email !== '' && { email }),\n ...(billingAddress !== null && { billing: billingAddress }),\n })\n if (!confirmation.ok) return this.stay(confirmation.error)\n if (confirmation.confirmed) checkout.markConfirmed()\n }\n this.leaveFor(result.orderNumber)\n } catch (err) {\n this.stay(err instanceof Error && err.message !== '' ? err.message : PAYMENT_FAILED)\n }\n }\n\n private confirm(\n clientSecret: string,\n context: PaymentConfirmContext,\n ): Promise<PaymentConfirmation> {\n return this.paymentElement()?.confirm(clientSecret, context) ?? Promise.resolve(NO_PAYMENT_STEP)\n }\n\n private stay(message: string): void {\n this.placing = false\n this.showError(message)\n }\n\n /** Place Order stays disabled from here through the page's navigation. */\n private leaveFor(orderNumber: string): void {\n this.client.checkout.reset()\n this.placing = false\n this.placed = true\n this.requestUpdate()\n this.dispatchEvent(\n new CustomEvent('nymbal:checkout:placed', {\n bubbles: true,\n composed: true,\n detail: { orderNumber },\n }),\n )\n }\n\n private readonly onBillingSame = (event: Event): void => {\n this.billingSame = (event.target as HTMLInputElement).checked\n this.requestUpdate()\n }\n\n private back(step: Step): () => void {\n return () => {\n if (!this.placing && !this.placed) this.go(step)\n }\n }\n\n private renderSteps() {\n const current = STEPS.indexOf(this.step)\n return html`\n <div part=\"steps\" data-testid=\"checkout-steps\">\n ${STEPS.map((step, index) => {\n const state = index < current ? ' step-done' : index === current ? ' step-active' : ''\n return html`\n ${index > 0\n ? html`<div\n part=\"step-divider${index <= current ? ' step-divider-done' : ''}\"\n aria-hidden=\"true\"\n ></div>`\n : nothing}\n <div\n part=\"step${state}\"\n data-testid=\"checkout-step-${step}\"\n aria-current=${index === current ? 'step' : nothing}\n >\n <span>${STEP_LABELS[step]}</span>\n </div>\n `\n })}\n </div>\n `\n }\n\n private renderContact() {\n return html`\n <section part=\"section\" data-testid=\"checkout-contact\" ?hidden=${this.step !== 'contact'}>\n <form part=\"form\" @submit=${this.onContact}>\n <h2 part=\"section-title\">Contact Information</h2>\n ${renderField({\n id: 'contact-email',\n name: 'email',\n label: 'Email Address',\n type: 'email',\n autocomplete: 'email',\n required: true,\n placeholder: 'you@example.com',\n })}\n <div part=\"actions\">\n <button\n type=\"submit\"\n part=\"submit\"\n class=\"btn btn-primary\"\n data-testid=\"contact-continue\"\n @click=${this.onSubmitClick}\n >\n <slot name=\"contact-continue-label\">Continue to Shipping</slot>\n </button>\n </div>\n </form>\n </section>\n `\n }\n\n private renderShipping() {\n return html`\n <section part=\"section\" data-testid=\"checkout-shipping\" ?hidden=${this.step !== 'shipping'}>\n <form part=\"form\" novalidate @submit=${this.onShipping}>\n <h2 part=\"section-title\">Shipping Address</h2>\n <nymbal-address-form id-prefix=\"shipping\" data-testid=\"shipping-form\"></nymbal-address-form>\n <div part=\"actions\">\n <button type=\"button\" part=\"back\" class=\"btn btn-secondary\" @click=${this.back('contact')}>\n Back\n </button>\n <button\n type=\"submit\"\n part=\"submit\"\n class=\"btn btn-primary\"\n data-testid=\"shipping-continue\"\n @click=${this.onSubmitClick}\n >\n <slot name=\"shipping-continue-label\">Continue to Payment</slot>\n </button>\n </div>\n </form>\n </section>\n `\n }\n\n private renderPayment() {\n const busy = this.placing || this.placed\n return html`\n <section part=\"section\" data-testid=\"checkout-payment\" ?hidden=${this.step !== 'payment'}>\n <form part=\"form\" novalidate @submit=${this.onPlace}>\n <h2 part=\"section-title\">Payment</h2>\n <label part=\"billing-same\">\n <input\n type=\"checkbox\"\n id=\"billing-same\"\n data-testid=\"billing-same\"\n .checked=${this.billingSame}\n @change=${this.onBillingSame}\n />\n Billing address same as shipping\n </label>\n <div part=\"billing\" data-testid=\"billing-address\" ?hidden=${this.billingSame}>\n <h3 part=\"billing-title\">Billing Address</h3>\n <nymbal-address-form id-prefix=\"billing\"></nymbal-address-form>\n </div>\n <div part=\"payment\"><slot name=\"payment\"></slot></div>\n <div part=\"actions\">\n <button\n type=\"button\"\n part=\"back\"\n class=\"btn btn-secondary\"\n ?disabled=${busy}\n @click=${this.back('shipping')}\n >\n Back\n </button>\n <button\n type=\"submit\"\n part=\"submit\"\n class=\"btn btn-primary\"\n data-testid=\"place-order\"\n ?disabled=${busy || this.paymentBlocked()}\n @click=${this.onSubmitClick}\n >\n ${this.placing\n ? 'Processing...'\n : html`<slot name=\"place-order-label\">Place Order</slot>`}\n </button>\n </div>\n <p part=\"terms\">\n <slot name=\"terms\">By placing your order, you agree to our terms and conditions.</slot>\n </p>\n </form>\n </section>\n `\n }\n\n private renderLine(item: CartItem, currency: string) {\n return html`\n <div part=\"item\">\n <span part=\"item-name\">${item.productName}</span>\n ${item.variantName ? html`<span part=\"item-variant\">${item.variantName}</span>` : nothing}\n <span part=\"item-qty\">x${item.qty}</span>\n <span part=\"item-price\">${formatPrice(item.priceMinor * item.qty, currency)}</span>\n </div>\n `\n }\n\n private renderSummary() {\n const cart = shownCart(this, this.cart, this.readsStores ? this.client : null)\n const currency = cart.currency || 'GBP'\n const subtotal = formatPrice(cart.subtotalMinor, currency)\n return html`\n <aside part=\"summary\" data-testid=\"checkout-summary\">\n <h2 part=\"summary-title\"><slot name=\"summary-title\">Order Summary</slot></h2>\n ${cart.items.length === 0\n ? nothing\n : html`<div part=\"items\">\n ${cart.items.map((item) => this.renderLine(item, currency))}\n </div>`}\n <dl>\n <div part=\"summary-row subtotal\">\n <dt>Subtotal</dt>\n <dd data-testid=\"cart-subtotal\">${subtotal}</dd>\n </div>\n <div part=\"summary-row\">\n <dt>Shipping</dt>\n <dd>Standard, free</dd>\n </div>\n </dl>\n <div part=\"total\">\n <span>Total</span>\n <span>${subtotal}</span>\n </div>\n </aside>\n `\n }\n\n protected override render() {\n return html`\n <h1 part=\"title\"><slot name=\"title\">Checkout</slot></h1>\n ${this.renderSteps()}\n ${this.error === null\n ? nothing\n : html`<div part=\"error\" role=\"alert\" data-testid=\"checkout-error\">${this.error}</div>`}\n <div part=\"layout\">\n <div part=\"main\">${this.renderContact()} ${this.renderShipping()} ${this.renderPayment()}</div>\n ${this.renderSummary()}\n </div>\n `\n }\n}\n"],"mappings":"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","names":[]}
|
|
File without changes
|