@seatlayer/js 0.48.0 → 0.48.1
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/{hostedCheckout-SHQCWN5Y.js → hostedCheckout-F6C6VCCG.js} +1 -1
- package/dist/hostedCheckout-F6C6VCCG.js.map +1 -0
- package/dist/index.cjs +57 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +58 -30
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/hostedCheckout-SHQCWN5Y.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hostedCheckout.ts"],"sourcesContent":["/**\n * Hosted checkout — the payment step, as a module nobody downloads until a\n * buyer actually asks to pay.\n *\n * This is the whole of what `checkout: 'hosted'` adds to SeatPicker: a card\n * that collects an email, starts a payment against `POST /pub/events/:key/\n * checkout`, hands off to the gateway, and waits for the webhook to land. It is\n * a straight port of the panel our own buyer page has shipped since hosted\n * checkout existed (`src/pages/CheckoutPanel.tsx`), with React and the app's\n * shared api client taken out.\n *\n * IT IMPORTS NOTHING AT RUNTIME. Not `@seatlayer/core`, not `./api`, not even a\n * type from `./SeatPicker` — every input arrives through {@link CheckoutMount},\n * including the two API calls, which arrive as functions. That is not\n * fastidiousness: this file is built as a standalone CDN asset\n * (`seatlayer-checkout.mjs`), and a single value import from the engine would\n * pull a second copy of it into that asset and undo the point of splitting.\n *\n * Three states, one card, because they are the same moment in the buyer's\n * journey and share every pixel of chrome:\n *\n * pay a live hold — collect an email and START a payment\n * resume back from a hosted gateway page — the hold and its line items\n * are gone with the old document, so this can only WAIT on the\n * order id that survived in the return URL. It must never offer\n * to start a second payment for a purchase that may have already\n * succeeded.\n * unavailable the event cannot take money here, and the buyer is holding\n * seats. Say which of the three reasons it is, because two of\n * them give opposite advice.\n */\n\n/** The gateways `payment-options` can name. */\nexport type CheckoutProviderName = 'stripe' | 'razorpay';\n\n/**\n * Why `payment-options` came back with an empty list. Mirrors the server enum\n * (`workers/api/src/checkout.ts`); see {@link unavailableCopy} for what each one\n * is allowed to say.\n */\nexport type CheckoutUnavailableReason =\n | 'not_configured'\n | 'payments_off_for_event'\n | 'unavailable_for_event';\n\n/** What `POST /pub/events/:key/checkout` returns. Exactly one handoff is set. */\nexport interface CheckoutSessionResult {\n orderId: string;\n totalMinor: number;\n currency: string;\n expiresAt: number;\n /** Hosted gateway page (Stripe) — leave for it. */\n redirectUrl?: string;\n /** In-page modal gateway (Razorpay) — open it here. */\n clientPayload?: Record<string, unknown>;\n}\n\n/** What `GET /pub/orders/:id/status` returns while the webhook is in flight. */\nexport interface CheckoutOrderStatus {\n orderId: string;\n status: string;\n totalMinor: number;\n currency: string;\n amountFormatted: string;\n seatCount: number;\n // Present once the order is settled: the confirmed card names the seats it\n // just sold and points at the hosted ticket page that outlives this modal.\n tickets?: Array<{\n label: string;\n token: string;\n status: 'issued' | 'checked_in' | 'void';\n checkedInAt: number | null;\n }>;\n /** Hosted ticket page — the durable re-entry point after the card closes. */\n ticketUrl?: string;\n /** Printable A4 PDF, up to three ticket cards per page. */\n pdfUrl?: string;\n}\n\n/** The buyer's held order, flattened out of the widget's CheckoutHandoff. */\nexport interface CheckoutOrderSummary {\n holdId: string;\n /** Epoch ms the hold expires — shown so the buyer knows their deadline. */\n expiresAt: number;\n currency: string;\n /** Total in MAJOR units, already carrying any host `pricing` overrides. */\n total: number;\n /** Buyer-facing unit labels (displayLabel where the designer set one). */\n labels: string[];\n}\n\nexport type CheckoutState =\n | { kind: 'pay'; order: CheckoutOrderSummary; provider: CheckoutProviderName | null }\n | { kind: 'resume'; orderId: string }\n | { kind: 'unavailable'; reason: CheckoutUnavailableReason; seatCount: number };\n\nexport interface CheckoutMount {\n /** Where the card mounts — the widget root, so every `--sl-*` token inherits. */\n root: HTMLElement;\n state: CheckoutState;\n /** `POST /pub/events/:key/checkout`, already bound to the event and client. */\n startSession(input: {\n holdId: string; buyerEmail: string; buyerName?: string;\n }): Promise<CheckoutSessionResult>;\n /** `GET /pub/orders/:id/status`. */\n orderStatus(orderId: string): Promise<CheckoutOrderStatus>;\n /** Buyer backed out, or closed a finished card. The hold is NOT touched. */\n onCancel(): void;\n /** The webhook landed and the order is paid. Fires at most once. */\n onConfirmed(order: CheckoutOrderStatus): void;\n /** Anything the buyer was already told about, forwarded to the host. */\n onError?(err: unknown): void;\n}\n\nexport interface CheckoutHandle {\n destroy(): void;\n}\n\n/** How long to wait on the webhook before telling the buyer to watch their email. */\nconst CONFIRM_TIMEOUT_MS = 90_000;\nconst CONFIRM_POLL_MS = 2_000;\nconst RAZORPAY_SCRIPT = 'https://checkout.razorpay.com/v1/checkout.js';\nconst STYLE_ID = 'seatlayer-checkout-style';\n\n/**\n * The card's stylesheet. Every colour, font and radius is a `--sl-*` token the\n * widget root already defines, so an organizer's accent and a host's `theme`\n * overrides reach the payment step without this module knowing either exists.\n *\n * The `@sl-css` marker opts it into build-time minification — see\n * cdn/minifyCssLiterals.ts. Keep writing it long-hand.\n */\nconst CSS = /* @sl-css */ `\n.sl-hco{position:absolute;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;\n padding:16px;background:color-mix(in srgb, var(--sl-bg) 82%, transparent);backdrop-filter:blur(3px)}\n.sl-hco-card{width:100%;max-width:380px;max-height:100%;overflow:auto;padding:22px;\n background:var(--sl-surface);color:var(--sl-text);border:1px solid var(--sl-line);\n border-radius:var(--sl-radius);box-shadow:0 18px 48px rgba(0,0,0,.28)}\n.sl-hco-title{margin:0 0 14px;font-size:19px;font-weight:650;letter-spacing:-.01em}\n.sl-hco-summary{margin-bottom:16px;padding-bottom:14px;border-bottom:1px solid var(--sl-line)}\n.sl-hco-seats{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px}\n.sl-hco-seat{padding:3px 8px;font-size:12px;font-weight:600;border-radius:calc(var(--sl-radius) * .5);\n background:var(--sl-bg);border:1px solid var(--sl-line)}\n.sl-hco-total{display:flex;justify-content:space-between;align-items:baseline;font-size:14px}\n.sl-hco-total strong{font-size:17px;font-weight:700}\n.sl-hco-label{display:block;margin:12px 0 5px;font-size:12px;font-weight:600;color:var(--sl-muted)}\n.sl-hco-input{width:100%;padding:10px 11px;font:inherit;font-size:15px;color:var(--sl-text);\n background:var(--sl-bg);border:1px solid var(--sl-line);border-radius:calc(var(--sl-radius) * .55)}\n.sl-hco-input:focus-visible{outline:2px solid var(--sl-accent);outline-offset:1px}\n.sl-hco-pay{width:100%;margin-top:16px;padding:12px;font:inherit;font-size:15px;font-weight:650;\n color:var(--sl-accent-ink);background:var(--sl-accent);border:0;\n border-radius:calc(var(--sl-radius) * .55);cursor:pointer}\n.sl-hco-pay[disabled]{opacity:.5;cursor:default}\n.sl-hco-back{width:100%;margin-top:8px;padding:10px;font:inherit;font-size:14px;color:var(--sl-muted);\n background:none;border:0;cursor:pointer;text-decoration:underline}\n.sl-hco-note{margin:12px 0 0;font-size:12px;line-height:1.5;color:var(--sl-muted)}\n.sl-hco-note a{color:inherit;text-decoration:underline;text-underline-offset:2px}\n.sl-hco-status{margin:8px 0 0;font-size:14px;line-height:1.55}\n.sl-hco-error{color:var(--sl-danger, #c0392b)}\n.sl-hco-receipt{display:grid;grid-template-columns:auto 1fr;gap:4px 14px;margin:14px 0 0;font-size:13px}\n.sl-hco-receipt dt{color:var(--sl-muted)}\n.sl-hco-receipt dd{margin:0;text-align:right}\n.sl-hco-ref{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;word-break:break-all}\n.sl-hco-tickets{display:block;width:100%;margin-top:14px;padding:12px;font:inherit;font-size:15px;\n font-weight:650;text-align:center;text-decoration:none;color:var(--sl-accent-ink);\n background:var(--sl-accent);border-radius:calc(var(--sl-radius) * .55)}\n`;\n\nfunction ensureStyle(): void {\n if (typeof document === 'undefined' || document.getElementById(STYLE_ID)) return;\n const el = document.createElement('style');\n el.id = STYLE_ID;\n el.textContent = CSS;\n document.head.appendChild(el);\n}\n\n/**\n * Money for the buyer's eye. `Intl` is the right answer everywhere it exists;\n * the fallback is a plain amount and a code, which is never wrong — only plain.\n */\nexport function formatMoney(amount: number, currency: string): string {\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(amount);\n } catch {\n return `${amount.toFixed(2)} ${currency}`;\n }\n}\n\n/**\n * What a buyer holding seats is told when the event cannot take their money,\n * and why the three differ. Pure, so the wording is unit-tested rather than\n * eyeballed.\n *\n * not_configured The organizer integrates by API and owns payment.\n * SeatLayer holds the inventory; their host takes the\n * money. Nothing is wrong and the buyer's checkout is\n * somewhere else on this very page.\n * payments_off_for_event The organizer sells other events through us and chose\n * not to sell this one online. NOTHING IS WRONG.\n * Telling this buyer to \"let the organiser know\" would\n * send them to complain about a deliberate decision.\n * unavailable_for_event The organizer switched this event on and it still\n * cannot charge — a test key against a live event, or a\n * gateway disconnected after assignment. Something IS\n * wrong and only they can fix it.\n *\n * None of them names a provider, a mode or an account: the buyer is anonymous\n * and the reason enum is the entire disclosure budget.\n */\nexport function unavailableCopy(reason: CheckoutUnavailableReason, seatCount: number): {\n title: string; body: string; detail: string;\n} {\n const held = `${seatCount} ${seatCount === 1 ? 'seat is' : 'seats are'} held for a limited time.`;\n if (reason === 'payments_off_for_event') {\n return {\n title: 'This event isn’t sold online',\n body: `${held} The organiser isn’t taking payment for this event here.`,\n detail: 'Nothing has been charged. Check where you found this event for how to get tickets.',\n };\n }\n if (reason === 'unavailable_for_event') {\n return {\n title: 'This event isn’t taking payment yet',\n body: `${held} Online payment is not switched on for this event.`,\n detail: 'Nothing has been charged. If you were sent here to pay, let the organiser know — '\n + 'only they can turn payment on for this event.',\n };\n }\n return {\n title: 'Finish in the ticketing checkout',\n body: `${held} Payment for this event is taken elsewhere.`,\n detail: 'Nothing has been charged. Continue in the checkout on this page to pay for your seats.',\n };\n}\n\n/**\n * Buyer-facing text for a server error code.\n *\n * Each one says what happened to their MONEY, because that is the only question\n * a buyer has at this moment. None of these paths can have charged them — the\n * charge does not exist until the gateway page — so every message says so.\n */\nexport function errorCopy(code: string | undefined): string {\n switch (code) {\n case 'gateway_not_connected':\n case 'payments_not_enabled_for_event':\n return 'This event is not taking online payments. Contact the organiser to buy these seats.';\n case 'provider_mismatch':\n // Only reachable if a caller sent a provider of its own. The widget never\n // does — it lets the event row decide — so this is a host integration bug,\n // and the buyer is told the one thing that is true for them.\n return 'This event’s payment setup changed while you were choosing. Nothing was charged — '\n + 'please try again.';\n case 'hold_not_active':\n case 'hold_not_found':\n return 'Your seats were released before checkout started. Nothing was charged — please pick again.';\n case 'checkout_already_started':\n return 'A payment for these seats is already in progress. Finish that one, or wait for it to '\n + 'time out before starting again.';\n case 'event_closed':\n return 'Sales for this event have closed.';\n case 'gateway_currency_mismatch':\n case 'unsupported_currency':\n case 'mixed_currency_hold':\n case 'price_unusable':\n return 'These seats cannot be checked out right now because of a pricing configuration '\n + 'problem. Nothing was charged — please contact the organiser.';\n case 'rate_limited':\n return 'Too many attempts. Wait a moment and try again.';\n default:\n return 'We could not start the payment. Nothing was charged — please try again.';\n }\n}\n\n/** Razorpay's script attaches this constructor; typed narrowly at the call site. */\ninterface RazorpayCheckout { open(): void }\ntype RazorpayConstructor = new (options: Record<string, unknown>) => RazorpayCheckout;\n\nfunction loadRazorpay(): Promise<RazorpayConstructor> {\n const existing = (window as unknown as { Razorpay?: RazorpayConstructor }).Razorpay;\n if (existing) return Promise.resolve(existing);\n return new Promise((resolve, reject) => {\n // Reuse an in-flight tag if the buyer retries before the first load settles.\n const previous = document.querySelector<HTMLScriptElement>(`script[src=\"${RAZORPAY_SCRIPT}\"]`);\n const tag = previous ?? document.createElement('script');\n const done = (): void => {\n const ctor = (window as unknown as { Razorpay?: RazorpayConstructor }).Razorpay;\n if (ctor) resolve(ctor);\n else reject(new Error('razorpay_unavailable'));\n };\n tag.addEventListener('load', done, { once: true });\n tag.addEventListener('error', () => reject(new Error('razorpay_script_failed')), { once: true });\n if (previous) return;\n tag.src = RAZORPAY_SCRIPT;\n tag.async = true;\n document.head.appendChild(tag);\n });\n}\n\nfunction el<K extends keyof HTMLElementTagNameMap>(\n tag: K, className?: string, text?: string,\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n if (className) node.className = className;\n // textContent, never innerHTML: an event name, a seat label and a server\n // message all reach this card, and none of them is trusted markup.\n if (text !== undefined) node.textContent = text;\n return node;\n}\n\n/**\n * Mount the payment card.\n *\n * Returns a handle rather than a promise: the card outlives the call (the buyer\n * types, pays, waits), and the widget needs a way to tear it down if the host\n * destroys the picker mid-payment.\n */\nexport function mountCheckout(mount: CheckoutMount): CheckoutHandle {\n ensureStyle();\n\n let live = true;\n let confirmed = false;\n const scrim = el('div', 'sl-hco');\n scrim.setAttribute('role', 'dialog');\n scrim.setAttribute('aria-modal', 'true');\n const card = el('div', 'sl-hco-card');\n scrim.appendChild(card);\n\n const destroy = (): void => {\n live = false;\n scrim.remove();\n document.removeEventListener('keydown', onKey, true);\n };\n const cancel = (): void => {\n destroy();\n mount.onCancel();\n };\n function onKey(event: KeyboardEvent): void {\n if (event.key !== 'Escape' || !scrim.isConnected) return;\n // The picker has its own document-level ESC handler for modal mode. Stopping\n // here means one press closes the payment card, not the whole widget with a\n // payment possibly in flight.\n event.stopPropagation();\n event.preventDefault();\n cancel();\n }\n document.addEventListener('keydown', onKey, true);\n\n const title = el('h2', 'sl-hco-title', 'Checkout');\n const titleId = `sl-hco-t-${Math.random().toString(36).slice(2, 8)}`;\n title.id = titleId;\n scrim.setAttribute('aria-labelledby', titleId);\n\n /** Replace everything under the title — each phase owns the card's body. */\n const show = (...nodes: Node[]): void => {\n card.replaceChildren(title, ...nodes);\n };\n\n const fail = (message: string): void => {\n if (!live) return;\n const status = el('p', 'sl-hco-status sl-hco-error', message);\n status.setAttribute('role', 'alert');\n const back = el('button', 'sl-hco-back', 'Back to seats');\n back.type = 'button';\n back.addEventListener('click', cancel);\n show(status, back);\n };\n\n const waiting = (message: string): void => {\n if (!live) return;\n const status = el('p', 'sl-hco-status', message);\n status.setAttribute('role', 'status');\n show(status);\n };\n\n /**\n * Poll the order until the webhook lands.\n *\n * The buyer's browser is never the authority here — it is only waiting for\n * one. On timeout we tell them the truth (payment likely taken, confirmation\n * by email) rather than claiming a failure that did not happen.\n */\n const awaitConfirmation = async (orderId: string): Promise<void> => {\n waiting('Payment received — confirming your seats…');\n const deadline = Date.now() + CONFIRM_TIMEOUT_MS;\n while (live && Date.now() < deadline) {\n try {\n const body = await mount.orderStatus(orderId);\n if (!live) return;\n if (body.status === 'confirmed') {\n confirmed = true;\n title.textContent = 'Your tickets are confirmed';\n const status = el(\n 'p', 'sl-hco-status',\n `${body.seatCount} ${body.seatCount === 1 ? 'seat' : 'seats'} confirmed. `\n + 'Your tickets — with their door QR codes — are in your email.',\n );\n const receipt = el('dl', 'sl-hco-receipt');\n const seatLabels = (body.tickets ?? []).map((t) => t.label);\n if (seatLabels.length) {\n receipt.append(el('dt', undefined, 'Seats'), el('dd', undefined, seatLabels.join(', ')));\n }\n receipt.append(\n el('dt', undefined, 'Paid'),\n el('dd', undefined, `${body.amountFormatted} ${body.currency}`),\n el('dt', undefined, 'Order'),\n el('dd', 'sl-hco-ref', body.orderId),\n );\n const close = el('button', 'sl-hco-back', 'Close');\n close.type = 'button';\n close.addEventListener('click', cancel);\n if (body.ticketUrl) {\n // The durable exit: a hosted page owning the QRs and the PDF, so\n // closing this card is no longer the end of the buyer's artifacts.\n const view = el('a', 'sl-hco-tickets', 'View tickets & QR codes');\n view.href = body.ticketUrl;\n view.target = '_blank';\n view.rel = 'noreferrer';\n show(status, receipt, view, close);\n } else {\n show(status, receipt, close);\n }\n mount.onConfirmed(body);\n return;\n }\n if (body.status === 'failed' || body.status === 'expired') {\n fail(body.status === 'expired'\n ? 'Your seats were released before payment completed. Nothing was charged — please pick again.'\n : 'The payment did not complete. If you were charged, it has been refunded automatically.');\n return;\n }\n } catch {\n // A transient blip while polling is not an answer. Keep waiting.\n }\n await new Promise((resolve) => setTimeout(resolve, CONFIRM_POLL_MS));\n }\n if (!live || confirmed) return;\n // Deliberately not an error: the money may well have gone through and the\n // webhook is just slow. The confirmation email is the durable receipt.\n fail('Still confirming with the payment provider. If your payment went through, your tickets '\n + 'will arrive by email shortly — you do not need to pay again.');\n };\n\n if (mount.state.kind === 'unavailable') {\n const copy = unavailableCopy(mount.state.reason, mount.state.seatCount);\n title.textContent = copy.title;\n const kicker = el('p', 'sl-hco-label', 'Seats held');\n const back = el('button', 'sl-hco-back', 'Back to seat map');\n back.type = 'button';\n back.addEventListener('click', cancel);\n card.replaceChildren(\n kicker, title,\n el('p', 'sl-hco-status', copy.body),\n el('p', 'sl-hco-note', copy.detail),\n back,\n );\n mount.root.appendChild(scrim);\n back.focus();\n return { destroy };\n }\n\n if (mount.state.kind === 'resume') {\n // A fresh document after a hosted gateway page. Everything except the order\n // id died with the old one, so this can only wait.\n mount.root.appendChild(scrim);\n void awaitConfirmation(mount.state.orderId);\n return { destroy };\n }\n\n const { order, provider } = mount.state;\n const summary = el('div', 'sl-hco-summary');\n const seats = el('div', 'sl-hco-seats');\n for (const label of order.labels) seats.appendChild(el('span', 'sl-hco-seat', label));\n const totalText = formatMoney(order.total, order.currency);\n const totalRow = el('div', 'sl-hco-total');\n totalRow.append(el('span', undefined, 'Total'), el('strong', undefined, totalText));\n summary.append(seats, totalRow);\n\n const form = el('form', 'sl-hco-form');\n const emailLabel = el('label', 'sl-hco-label', 'Email — your tickets go here');\n const email = el('input', 'sl-hco-input');\n email.type = 'email';\n email.required = true;\n email.autocomplete = 'email';\n email.placeholder = 'you@example.com';\n email.id = `${titleId}-email`;\n emailLabel.htmlFor = email.id;\n\n const nameLabel = el('label', 'sl-hco-label', 'Name (optional)');\n const name = el('input', 'sl-hco-input');\n name.type = 'text';\n name.autocomplete = 'name';\n name.placeholder = 'Your name';\n name.id = `${titleId}-name`;\n nameLabel.htmlFor = name.id;\n\n const pay = el('button', 'sl-hco-pay', `Pay ${totalText}`);\n pay.type = 'submit';\n pay.disabled = true;\n const back = el('button', 'sl-hco-back', 'Back to seats');\n back.type = 'button';\n back.addEventListener('click', cancel);\n\n const until = new Date(order.expiresAt)\n .toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });\n const note = el(\n 'p', 'sl-hco-note',\n `Your seats are held until ${until}. Payment is handled by `\n + `${provider === 'razorpay' ? 'Razorpay' : 'Stripe'} — we never see your card details.`,\n );\n const privacy = el('p', 'sl-hco-note');\n privacy.append('The event organizer and SeatLayer use your email to issue and manage your tickets. ');\n const privacyLink = el('a', undefined, 'Privacy Policy');\n privacyLink.href = 'https://seatlayer.io/privacy/';\n privacyLink.target = '_blank';\n privacyLink.rel = 'noreferrer';\n privacy.appendChild(privacyLink);\n\n const emailValid = (): boolean => /.+@.+\\..+/.test(email.value.trim());\n email.addEventListener('input', () => { pay.disabled = !emailValid(); });\n\n const start = async (): Promise<void> => {\n waiting('Opening secure payment…');\n try {\n const body = await mount.startSession({\n holdId: order.holdId,\n buyerEmail: email.value.trim(),\n // Deliberately no `provider`: since W2 the EVENT row decides which\n // gateway charges, and naming one here can only ever 409 on a mismatch\n // the buyer cannot do anything about.\n ...(name.value.trim() ? { buyerName: name.value.trim() } : {}),\n });\n if (!live) return;\n\n // Hosted-page provider: leave the page. Where the gateway sends the buyer\n // back is the SERVER's choice, not ours — see the `checkout` option's note\n // in SeatPicker.\n if (body.redirectUrl) {\n waiting('Taking you to secure payment…');\n window.location.assign(body.redirectUrl);\n return;\n }\n\n // In-page modal provider — the embed never leaves the host's page.\n if (body.clientPayload) {\n const payload = body.clientPayload as {\n key: string; orderId: string; amount: number; currency: string;\n name: string; prefill?: { email?: string; name?: string };\n };\n const Razorpay = await loadRazorpay();\n if (!live) return;\n waiting('Waiting for payment…');\n new Razorpay({\n key: payload.key,\n order_id: payload.orderId,\n amount: payload.amount,\n currency: payload.currency,\n name: payload.name,\n prefill: payload.prefill,\n // The handler fires on the browser's word alone, so it only starts the\n // wait — the webhook is what actually confirms the order.\n handler: () => { void awaitConfirmation(body.orderId); },\n modal: { ondismiss: () => { if (live) details(); } },\n }).open();\n return;\n }\n\n fail(errorCopy('gateway_unavailable'));\n } catch (err) {\n mount.onError?.(err);\n fail(errorCopy((err as { code?: string } | null)?.code));\n }\n };\n\n form.addEventListener('submit', (event) => {\n event.preventDefault();\n if (emailValid()) void start();\n });\n form.append(emailLabel, email, nameLabel, name, privacy, pay, back, note);\n\n const details = (): void => {\n title.textContent = 'Checkout';\n show(summary, form);\n pay.disabled = !emailValid();\n };\n details();\n mount.root.appendChild(scrim);\n email.focus();\n\n return { destroy };\n}\n"],"mappings":"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","names":["el","back"]}
|
package/dist/index.cjs
CHANGED
|
@@ -6448,6 +6448,16 @@ var CSS2 = (
|
|
|
6448
6448
|
.sl-close.on{display:inline-flex}
|
|
6449
6449
|
.sl-close svg{width:14px;height:14px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round}
|
|
6450
6450
|
|
|
6451
|
+
/* A page or popup may already own the event heading. In that case only the
|
|
6452
|
+
duplicate identity disappears; operational header controls stay available.
|
|
6453
|
+
Zero block padding lets an otherwise-empty header collapse completely. */
|
|
6454
|
+
.sl-picker[data-event-details-hidden="true"] .sl-head{padding-block:0;border-bottom-width:0}
|
|
6455
|
+
.sl-picker[data-event-details-hidden="true"] .sl-logo,
|
|
6456
|
+
.sl-picker[data-event-details-hidden="true"] .sl-head-info{display:none!important}
|
|
6457
|
+
.sl-picker[data-event-details-hidden="true"] .sl-hold-pill.on,
|
|
6458
|
+
.sl-picker[data-event-details-hidden="true"] .sl-closed-pill.on,
|
|
6459
|
+
.sl-picker[data-event-details-hidden="true"] .sl-close.on{margin-block:10px}
|
|
6460
|
+
|
|
6451
6461
|
/* body */
|
|
6452
6462
|
.sl-body{display:flex;flex:1;min-height:0}
|
|
6453
6463
|
.sl-map{position:relative;flex:1;min-width:0}
|
|
@@ -6465,7 +6475,7 @@ var CSS2 = (
|
|
|
6465
6475
|
.sl-picker[data-layout="narrow"] .sl-side{width:100%;border-left:0;border-top:1px solid var(--sl-line);
|
|
6466
6476
|
flex:none;height:min(72%,480px);overflow:hidden;transition:height .3s cubic-bezier(.2,.8,.2,1);overscroll-behavior:contain}
|
|
6467
6477
|
.sl-picker[data-layout="narrow"][data-sheet="open"][data-has-selection="false"] .sl-side{height:min(252px,52%)}
|
|
6468
|
-
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side{height:
|
|
6478
|
+
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side{height:76px;overflow:hidden}
|
|
6469
6479
|
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-side > :not(.sl-sheet-head){display:none}
|
|
6470
6480
|
.sl-picker[data-layout="narrow"] .sl-tray{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}
|
|
6471
6481
|
.sl-picker[data-layout="narrow"] .sl-foot{position:static;background:var(--sl-bg)}
|
|
@@ -6500,9 +6510,10 @@ var CSS2 = (
|
|
|
6500
6510
|
head is the tap/swipe toggle target (min 44px), so it reads as one control. */
|
|
6501
6511
|
.sl-sheet-head{display:none;flex-direction:column;justify-content:center;padding:6px 12px 8px;min-height:56px;
|
|
6502
6512
|
cursor:pointer;touch-action:none;user-select:none;-webkit-user-select:none;flex:none}
|
|
6503
|
-
.sl-picker[data-layout="narrow"] .sl-sheet-head{display:flex}
|
|
6504
|
-
.sl-sheet-
|
|
6505
|
-
.sl-sheet-
|
|
6513
|
+
.sl-picker[data-layout="narrow"] .sl-sheet-head{display:flex;min-height:64px;padding:4px 10px 6px}
|
|
6514
|
+
.sl-picker[data-layout="narrow"][data-sheet="peek"] .sl-sheet-head{height:100%}
|
|
6515
|
+
.sl-sheet-grab{width:36px;height:4px;border-radius:999px;background:var(--sl-muted);opacity:.55;margin:1px auto 5px}
|
|
6516
|
+
.sl-sheet-bar{display:flex;align-items:center;gap:8px;min-height:44px}
|
|
6506
6517
|
.sl-sheet-peek{display:flex;align-items:center;gap:7px;flex:1;min-width:0;font-size:13px;font-weight:700;color:var(--sl-text);
|
|
6507
6518
|
white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
6508
6519
|
.sl-sheet-peek .sub{color:var(--sl-muted);font-weight:600}
|
|
@@ -6530,7 +6541,7 @@ var CSS2 = (
|
|
|
6530
6541
|
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filtersec.has,
|
|
6531
6542
|
.sl-picker[data-layout="narrow"][data-has-selection="true"] .sl-filters.has{display:none}
|
|
6532
6543
|
/* Accessibility and colour-safety controls must remain reachable on phones.
|
|
6533
|
-
Keep them out of the
|
|
6544
|
+
Keep them out of the collapsed peek, then reveal their consolidated row whenever
|
|
6534
6545
|
the buyer explicitly opens the ticket panel. */
|
|
6535
6546
|
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-filtersec.has{display:block!important}
|
|
6536
6547
|
.sl-picker[data-layout="narrow"][data-sheet="open"] .sl-filters.has{display:flex!important}
|
|
@@ -6673,15 +6684,10 @@ var CSS2 = (
|
|
|
6673
6684
|
.sl-picker .sl-cta:disabled{background:var(--sl-surface);color:var(--sl-muted);opacity:1;
|
|
6674
6685
|
cursor:not-allowed;filter:none;transform:none}
|
|
6675
6686
|
|
|
6676
|
-
/* Chrome anchor regions (Feature 6) \u2014 every
|
|
6687
|
+
/* Chrome anchor regions (Feature 6) \u2014 every interactive map overlay is APPENDED
|
|
6677
6688
|
INTO one of these positioned flex containers and flows/stacks within it, so no
|
|
6678
|
-
two
|
|
6679
|
-
|
|
6680
|
-
/* align-items:center, not the flex default of stretch. Without it a short pill
|
|
6681
|
-
beside a taller control (TEST MODE next to the Map/3D toggle) is stretched to
|
|
6682
|
-
the row's height, so two pills that should read as a matched pair end up
|
|
6683
|
-
different shapes on different baselines. Column regions set their own
|
|
6684
|
-
cross-axis alignment below and are unaffected. */
|
|
6689
|
+
two controls free-float on top of each other. Regions never overlap: the top
|
|
6690
|
+
strip splits into left/center/right; rails + corners own their edge. */
|
|
6685
6691
|
.sl-anchor{position:absolute;z-index:5;display:flex;align-items:center;gap:8px;pointer-events:none}
|
|
6686
6692
|
.sl-anchor > *{pointer-events:auto}
|
|
6687
6693
|
.sl-anchor[data-region="top-left"]{top:12px;left:12px;flex-wrap:wrap;max-width:38%}
|
|
@@ -6697,18 +6703,17 @@ var CSS2 = (
|
|
|
6697
6703
|
.sl-picker[data-layout="narrow"] .sl-anchor[data-region="top-left"]{max-width:30%}
|
|
6698
6704
|
.sl-picker[data-layout="narrow"] .sl-anchor[data-region="top-center"]{max-width:44%}
|
|
6699
6705
|
|
|
6700
|
-
/* TEST MODE
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
.sl-picker[data-layout="narrow"] .sl-testbadge{padding:0 8px;font-size:8.5px;letter-spacing:.06em}
|
|
6706
|
+
/* TEST MODE is environment context, not an action. A clipped corner ribbon
|
|
6707
|
+
keeps it persistent without impersonating a button or competing with Map/3D.
|
|
6708
|
+
The top-left interactive region moves below it only on test events. */
|
|
6709
|
+
.sl-testbadge{position:absolute;top:17px;left:-38px;z-index:6;width:142px;padding:5px 0;
|
|
6710
|
+
transform:rotate(-45deg);text-align:center;pointer-events:none;
|
|
6711
|
+
font-size:9.5px;font-weight:850;letter-spacing:.13em;line-height:1.2;text-transform:uppercase;
|
|
6712
|
+
white-space:nowrap;background:var(--sl-accent);color:var(--sl-accent-ink);
|
|
6713
|
+
box-shadow:0 2px 8px rgba(0,0,0,.28)}
|
|
6714
|
+
.sl-picker[data-event-mode="test"] .sl-anchor[data-region="top-left"]{top:96px}
|
|
6715
|
+
.sl-picker[data-layout="narrow"] .sl-testbadge{top:14px;left:-35px;width:128px;font-size:8.5px}
|
|
6716
|
+
.sl-picker[data-layout="narrow"][data-event-mode="test"] .sl-anchor[data-region="top-left"]{top:88px}
|
|
6712
6717
|
|
|
6713
6718
|
/* zoom column (flows within the bottom-right region) */
|
|
6714
6719
|
.sl-zoom{display:flex;flex-direction:column;gap:6px}
|
|
@@ -6943,10 +6948,15 @@ var CSS2 = (
|
|
|
6943
6948
|
.sl-ba-actions{grid-column:1/-1;display:grid;grid-template-columns:1fr 1fr;gap:7px}
|
|
6944
6949
|
.sl-ba-actions button{min-height:36px;border-radius:9px;border:1px solid var(--sl-line);font-size:11.5px;font-weight:800}
|
|
6945
6950
|
.sl-ba-actions .replace{border-color:var(--sl-accent);background:var(--sl-accent);color:var(--sl-accent-ink)}
|
|
6946
|
-
.sl-picker[data-layout="narrow"] .sl-ba{padding:
|
|
6951
|
+
.sl-picker[data-layout="narrow"] .sl-ba{padding:9px;gap:6px}
|
|
6952
|
+
.sl-picker[data-layout="narrow"] .sl-ba::after{display:none}
|
|
6953
|
+
.sl-picker[data-layout="narrow"] .sl-ba-title{font-size:12.5px}
|
|
6954
|
+
.sl-picker[data-layout="narrow"] .sl-ba-copy{display:none}
|
|
6947
6955
|
.sl-picker[data-layout="narrow"] .sl-ba-copy .wide{display:none}
|
|
6948
6956
|
.sl-picker[data-layout="narrow"] .sl-ba-copy .narrow{display:inline}
|
|
6949
|
-
.sl-picker[data-layout="narrow"] .sl-ba select{min-height:
|
|
6957
|
+
.sl-picker[data-layout="narrow"] .sl-ba select{min-height:40px}
|
|
6958
|
+
.sl-picker[data-layout="narrow"] .sl-ba-qty button{width:30px;height:30px}
|
|
6959
|
+
.sl-picker[data-layout="narrow"] .sl-ba-go{min-height:40px}
|
|
6950
6960
|
|
|
6951
6961
|
/* screen-reader live region */
|
|
6952
6962
|
.sl-sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
|
|
@@ -7389,6 +7399,8 @@ var SeatPicker = class _SeatPicker {
|
|
|
7389
7399
|
this.fsFallback = false;
|
|
7390
7400
|
this.fsChangeHandler = null;
|
|
7391
7401
|
this.fsEscHandler = null;
|
|
7402
|
+
/** Host-level event chrome owns the duplicate identity outside full screen. */
|
|
7403
|
+
this.eventDetailsHidden = false;
|
|
7392
7404
|
/** True once we've asked the host page to pin us fullscreen (framed, no native). */
|
|
7393
7405
|
this.framedFs = false;
|
|
7394
7406
|
/** Last height (px) posted to a host frame; dedupes redundant reports. */
|
|
@@ -7411,6 +7423,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7411
7423
|
if (!options.event || typeof options.event !== "string") throw new Error("seatmap: `event` key is required");
|
|
7412
7424
|
if (!options.container) throw new Error("seatmap: `container` is required (or use SeatPicker.open())");
|
|
7413
7425
|
this.opts = { ...options, confirmSelection: options.confirmSelection ?? true };
|
|
7426
|
+
this.eventDetailsHidden = !!options.hideEventDetails;
|
|
7414
7427
|
this.hostPricing = options.pricing;
|
|
7415
7428
|
this.apiBase = (options.apiBase ?? DEFAULT_API_BASE2).replace(/\/+$/, "");
|
|
7416
7429
|
this.access = options.transport ? null : createBuyerAccessContext(options, {
|
|
@@ -7695,6 +7708,10 @@ var SeatPicker = class _SeatPicker {
|
|
|
7695
7708
|
}
|
|
7696
7709
|
syncFullscreenButtons() {
|
|
7697
7710
|
const active = !!document.fullscreenElement || this.fsFallback || this.framedFs;
|
|
7711
|
+
const hideEventDetails = this.eventDetailsHidden && !active;
|
|
7712
|
+
this.root?.setAttribute("data-event-details-hidden", String(hideEventDetails));
|
|
7713
|
+
this.els.logo?.toggleAttribute("hidden", hideEventDetails);
|
|
7714
|
+
this.els.headInfo?.toggleAttribute("hidden", hideEventDetails);
|
|
7698
7715
|
this.els.zfs?.setAttribute("aria-pressed", String(active));
|
|
7699
7716
|
this.view3dEl?.querySelector(".sl-view3d-fs")?.setAttribute("aria-pressed", String(active));
|
|
7700
7717
|
}
|
|
@@ -7838,7 +7855,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7838
7855
|
root.innerHTML = `
|
|
7839
7856
|
<div class="sl-head">
|
|
7840
7857
|
<div class="sl-logo" data-ref="logo"></div>
|
|
7841
|
-
<div class="sl-head-info">
|
|
7858
|
+
<div class="sl-head-info" data-ref="headInfo">
|
|
7842
7859
|
<div class="sl-head-name" data-ref="name"></div>
|
|
7843
7860
|
<div class="sl-head-meta" data-ref="meta"></div>
|
|
7844
7861
|
</div>
|
|
@@ -7900,6 +7917,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
7900
7917
|
this.els[el2.dataset.ref] = el2;
|
|
7901
7918
|
});
|
|
7902
7919
|
this.mapHost = this.els.map;
|
|
7920
|
+
this.syncFullscreenButtons();
|
|
7903
7921
|
void this.refreshOfferAvailability(false);
|
|
7904
7922
|
if (this.api.availability) {
|
|
7905
7923
|
this.offerVisibilityHandler = () => {
|
|
@@ -8012,6 +8030,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
8012
8030
|
this.els.boot.remove();
|
|
8013
8031
|
this.startRealtime();
|
|
8014
8032
|
this.salesClosed = !!info.salesClosed || !!this.opts.readOnly;
|
|
8033
|
+
root.dataset.eventMode = info.mode === "test" ? "test" : "live";
|
|
8015
8034
|
this.controller.setViewMode(this.normalizeInitialView(this.opts.initialView));
|
|
8016
8035
|
this.buildRegions();
|
|
8017
8036
|
this.regions["bottom-right"].appendChild(this.els.zoom);
|
|
@@ -8021,7 +8040,7 @@ var SeatPicker = class _SeatPicker {
|
|
|
8021
8040
|
badge.className = "sl-testbadge";
|
|
8022
8041
|
badge.textContent = (0, import_core2.t)("picker.testMode");
|
|
8023
8042
|
badge.setAttribute("aria-label", (0, import_core2.t)("picker.testMode"));
|
|
8024
|
-
this.
|
|
8043
|
+
this.els.map.appendChild(badge);
|
|
8025
8044
|
}
|
|
8026
8045
|
const chartTheme = this.controller.doc?.theme;
|
|
8027
8046
|
Object.entries(resolveTokens(chartTheme, this.opts.theme)).forEach(([k, v]) => root.style.setProperty(k, v));
|
|
@@ -10311,6 +10330,15 @@ var SeatPicker = class _SeatPicker {
|
|
|
10311
10330
|
this.opts.theme = { ...this.opts.theme ?? {}, map: map ?? void 0 };
|
|
10312
10331
|
this.controller.setMapTheme(map);
|
|
10313
10332
|
}
|
|
10333
|
+
/**
|
|
10334
|
+
* Let a host suppress duplicate event identity after mount without remounting
|
|
10335
|
+
* the live picker (and therefore without disturbing a selection or hold).
|
|
10336
|
+
* Full-screen mode still restores the identity until the buyer exits it.
|
|
10337
|
+
*/
|
|
10338
|
+
setEventDetailsHidden(hidden) {
|
|
10339
|
+
this.eventDetailsHidden = hidden;
|
|
10340
|
+
this.syncFullscreenButtons();
|
|
10341
|
+
}
|
|
10314
10342
|
/**
|
|
10315
10343
|
* Replace the host pricing override AFTER mount, and repaint everything that
|
|
10316
10344
|
* shows a price.
|