@seatlayer/js 0.1.3 → 0.2.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/index.cjs +60 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -3
- package/dist/index.d.ts +64 -3
- package/dist/index.js +61 -11
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -66,10 +66,10 @@ var PubApi = class {
|
|
|
66
66
|
objects(key) {
|
|
67
67
|
return request(this.base, `/pub/events/${encodeURIComponent(key)}/objects`);
|
|
68
68
|
}
|
|
69
|
-
hold(key,
|
|
69
|
+
hold(key, selections, ttlMs, replaceHoldId) {
|
|
70
70
|
return request(this.base, `/pub/events/${encodeURIComponent(key)}/hold`, {
|
|
71
71
|
method: "POST",
|
|
72
|
-
body: {
|
|
72
|
+
body: { selections, ...ttlMs ? { ttlMs } : {}, ...replaceHoldId ? { replaceHoldId } : {} }
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
75
|
bestAvailable(key, qty, categoryKey) {
|
|
@@ -121,9 +121,16 @@ var SeatingChart = class {
|
|
|
121
121
|
maxSelection: options.maxSelection ?? DEFAULT_MAX_SELECTION,
|
|
122
122
|
currency: options.currency,
|
|
123
123
|
onSelectionChange: (seats) => this.opts.onSelectionChange?.(seats),
|
|
124
|
-
onHold: (h) => this.opts.onHold?.({ holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats }),
|
|
124
|
+
onHold: (h) => this.opts.onHold?.({ holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items }),
|
|
125
|
+
onHoldExpired: () => this.opts.onHoldExpired?.(),
|
|
126
|
+
onGAClick: (areaId) => {
|
|
127
|
+
const area = this.controller.getGAAreas().find((candidate) => candidate.id === areaId);
|
|
128
|
+
if (area) this.opts.onGAClick?.(area);
|
|
129
|
+
},
|
|
125
130
|
onError: (err) => this.opts.onError?.(err),
|
|
126
|
-
onDeckTap: (floorId) => this.opts.onDeckTap?.(floorId)
|
|
131
|
+
onDeckTap: (floorId) => this.opts.onDeckTap?.(floorId),
|
|
132
|
+
onHint: (message) => this.opts.onHint?.(message),
|
|
133
|
+
colorblindSafe: options.colorblindSafe
|
|
127
134
|
});
|
|
128
135
|
}
|
|
129
136
|
/** Fetch the chart, mount the renderer, seed statuses and go live. Idempotent. */
|
|
@@ -139,8 +146,19 @@ var SeatingChart = class {
|
|
|
139
146
|
host.style.position = "relative";
|
|
140
147
|
this.mount.appendChild(host);
|
|
141
148
|
this.hostEl = host;
|
|
142
|
-
const
|
|
143
|
-
if (!
|
|
149
|
+
const info = await this.controller.render(host);
|
|
150
|
+
if (!info) {
|
|
151
|
+
this.rendered = false;
|
|
152
|
+
return this;
|
|
153
|
+
}
|
|
154
|
+
if (info.mode === "test") {
|
|
155
|
+
host.style.overflow = "hidden";
|
|
156
|
+
const ribbon = document.createElement("div");
|
|
157
|
+
ribbon.textContent = (0, import_core.t)("picker.testMode");
|
|
158
|
+
ribbon.setAttribute("aria-label", (0, import_core.t)("picker.testMode"));
|
|
159
|
+
ribbon.style.cssText = "position:absolute;top:18px;right:-34px;z-index:6;transform:rotate(45deg);width:140px;text-align:center;padding:4px 0;background:#f4b740;color:#1a1200;font:800 10.5px/1.4 -apple-system,BlinkMacSystemFont,sans-serif;letter-spacing:.12em;box-shadow:0 2px 8px rgba(0,0,0,.25);pointer-events:none;";
|
|
160
|
+
host.appendChild(ribbon);
|
|
161
|
+
}
|
|
144
162
|
return this;
|
|
145
163
|
}
|
|
146
164
|
/** Current selection with prices resolved from the chart categories. */
|
|
@@ -148,10 +166,22 @@ var SeatingChart = class {
|
|
|
148
166
|
return this.controller.getSelection();
|
|
149
167
|
}
|
|
150
168
|
/** Hold the current selection. Resolves the hold, or null on a 409 conflict. */
|
|
151
|
-
async hold() {
|
|
169
|
+
async hold(options = {}) {
|
|
170
|
+
try {
|
|
171
|
+
const h = await this.controller.hold(void 0, options.ttlMs);
|
|
172
|
+
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : null;
|
|
173
|
+
} catch (err) {
|
|
174
|
+
this.opts.onError?.(err);
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
getGAAreas() {
|
|
179
|
+
return this.controller.getGAAreas();
|
|
180
|
+
}
|
|
181
|
+
async holdGA(areaId, qty, options = {}) {
|
|
152
182
|
try {
|
|
153
|
-
const h = await this.controller.
|
|
154
|
-
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats } : null;
|
|
183
|
+
const h = await this.controller.holdGA(areaId, qty, options);
|
|
184
|
+
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : null;
|
|
155
185
|
} catch (err) {
|
|
156
186
|
this.opts.onError?.(err);
|
|
157
187
|
return null;
|
|
@@ -161,7 +191,7 @@ var SeatingChart = class {
|
|
|
161
191
|
async bestAvailable(qty, categoryKey) {
|
|
162
192
|
try {
|
|
163
193
|
const h = await this.controller.bestAvailable(qty, categoryKey);
|
|
164
|
-
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, labels: h.labels, seats: h.seats } : null;
|
|
194
|
+
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, labels: h.labels, seats: h.seats, items: h.items } : null;
|
|
165
195
|
} catch (err) {
|
|
166
196
|
this.opts.onError?.(err);
|
|
167
197
|
return null;
|
|
@@ -177,6 +207,26 @@ var SeatingChart = class {
|
|
|
177
207
|
setSeatTier(seatId, tierId) {
|
|
178
208
|
this.controller.setSeatTier(seatId, tierId);
|
|
179
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Floors of a multi-floor chart — `[{ id, name }]` (single-floor charts
|
|
212
|
+
* return one entry; empty before render()). Pair with setFloor() to build a
|
|
213
|
+
* host-side floor switcher.
|
|
214
|
+
*/
|
|
215
|
+
getFloors() {
|
|
216
|
+
return this.controller.getFloors();
|
|
217
|
+
}
|
|
218
|
+
/** Switch the shown floor (2D). Warns + no-ops on single-floor charts. */
|
|
219
|
+
setFloor(floorId) {
|
|
220
|
+
if (this.controller.getFloors().length <= 1) {
|
|
221
|
+
console.warn("seatmap: setFloor() ignored \u2014 this chart has a single floor");
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
this.controller.setFloor(floorId);
|
|
225
|
+
}
|
|
226
|
+
/** Toggle colorblind-safe rendering at runtime (see options.colorblindSafe). */
|
|
227
|
+
setColorblindSafe(on) {
|
|
228
|
+
this.controller.setColorblindSafe(on);
|
|
229
|
+
}
|
|
180
230
|
/** Release the current hold (if any). No-op when nothing is held. */
|
|
181
231
|
async release() {
|
|
182
232
|
await this.controller.release();
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/SeatingChart.ts","../src/api.ts"],"sourcesContent":["/**\n * @seatlayer/js — the framework-agnostic SeatLayer embed SDK.\n *\n * Works in any JS environment (plain HTML, React, Vue, Svelte, Angular, …).\n * Framework wrappers (@seatlayer/react, …) build on top of this.\n */\nexport { SeatingChart } from './SeatingChart';\nexport type { SeatingChartOptions, SelectedSeat } from './SeatingChart';\nexport { ApiError } from './api';\nexport type { HoldResult, HoldConflict, BestAvailableResult } from './api';\n","/**\n * SeatingChart — the embeddable buyer picker.\n *\n * A thin wrapper over the shared PickerController (src/picker/PickerController):\n * it owns the mount <div> + the public embed contract (hold-only — the SDK hands\n * the holdId to the host page for a server-side book) and delegates all transport\n * + booking to the controller, so the SDK inherits every fix made for the live\n * buyer page and the demo picker.\n */\nimport { PickerController, loadLocale, setStringOverrides, type PickerSeat } from '@seatlayer/core';\nimport { PubApi, type BestAvailableResult, type HoldResult } from './api';\n\nconst DEFAULT_API_BASE = 'https://api.seatlayer.io';\nconst DEFAULT_MAX_SELECTION = 10;\n\n/** A seat as surfaced to the host page (prices resolved from the chart's categories). */\nexport type SelectedSeat = PickerSeat;\n\nexport interface SeatingChartOptions {\n /** CSS selector or an HTMLElement to render into. */\n container: string | HTMLElement;\n /** Event key, e.g. `ev_xxx`. */\n event: string;\n /** API origin. Defaults to https://api.seatlayer.io. */\n apiBase?: string;\n /** Reserved for future authenticated rendering — accepted + stored, not yet sent. */\n publicKey?: string;\n /** Max seats selectable at once (default 10). */\n maxSelection?: number;\n /**\n * BCP 47 language for the widget UI — `'de'`, `'es-MX'`, etc. Falls back to\n * the browser language, then English. Built-in: en, es, de, fr. The German\n * bundle (etc.) is fetched on demand so unused languages cost nothing.\n */\n locale?: string;\n /**\n * Per-key string overrides layered over the active locale — white-label copy\n * without shipping a whole bundle, e.g. `{ 'map.fromPrice': 'ab {price}' }`.\n */\n messages?: Record<string, string>;\n /** ISO 4217 currency for on-map prices (default USD). */\n currency?: string;\n onSelectionChange?: (seats: SelectedSeat[]) => void;\n onHold?: (result: HoldResult) => void;\n onError?: (err: unknown) => void;\n /**\n * Multi-floor charts only: fires when the buyer taps a deck in the stacked\n * 3D view, after the picker switches to that floor — lets the host page sync\n * its own floor UI (tabs, labels) with the map.\n */\n onDeckTap?: (floorId: string) => void;\n}\n\nfunction resolveContainer(container: string | HTMLElement): HTMLElement {\n if (typeof container === 'string') {\n const el = document.querySelector(container);\n if (!el) throw new Error(`seatmap: container \"${container}\" not found`);\n return el as HTMLElement;\n }\n if (!(container instanceof HTMLElement)) {\n throw new Error('seatmap: container must be a CSS selector or an HTMLElement');\n }\n return container;\n}\n\nexport class SeatingChart {\n private readonly opts: SeatingChartOptions;\n private readonly controller: PickerController;\n /** Reserved for future authenticated rendering — stored, not yet sent on any request. */\n readonly publicKey?: string;\n\n private mount: HTMLElement | null = null;\n private hostEl: HTMLDivElement | null = null;\n private rendered = false;\n\n constructor(options: SeatingChartOptions) {\n if (!options || typeof options !== 'object') throw new Error('seatmap: options object is required');\n if (!options.container) throw new Error('seatmap: `container` is required');\n if (!options.event || typeof options.event !== 'string') throw new Error('seatmap: `event` key is required');\n\n this.opts = options;\n this.publicKey = options.publicKey;\n const api = new PubApi((options.apiBase ?? DEFAULT_API_BASE).replace(/\\/+$/, ''));\n this.controller = new PickerController({\n transport: api,\n eventKey: options.event,\n maxSelection: options.maxSelection ?? DEFAULT_MAX_SELECTION,\n currency: options.currency,\n onSelectionChange: (seats) => this.opts.onSelectionChange?.(seats),\n onHold: (h) => this.opts.onHold?.({ holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats }),\n onError: (err) => this.opts.onError?.(err),\n onDeckTap: (floorId) => this.opts.onDeckTap?.(floorId),\n });\n }\n\n /** Fetch the chart, mount the renderer, seed statuses and go live. Idempotent. */\n async render(): Promise<this> {\n if (this.rendered) return this;\n this.rendered = true;\n\n // Resolve + load the UI language before the first paint so on-map labels\n // (\"N LEFT\", \"FROM …\", the map aria-label) render translated. English and\n // already-loaded locales resolve synchronously; others fetch one small chunk.\n await loadLocale(this.opts.locale);\n if (this.opts.messages) setStringOverrides(this.opts.messages);\n\n // Mount an owned <div> inside the caller's container so we never fight their\n // layout and can cleanly remove it on destroy().\n this.mount = resolveContainer(this.opts.container);\n const host = document.createElement('div');\n host.style.width = '100%';\n host.style.height = '100%';\n host.style.position = 'relative';\n this.mount.appendChild(host);\n this.hostEl = host;\n\n const ok = await this.controller.render(host);\n if (!ok) this.rendered = false; // render() already emitted the error\n return this;\n }\n\n /** Current selection with prices resolved from the chart categories. */\n getSelection(): SelectedSeat[] {\n return this.controller.getSelection();\n }\n\n /** Hold the current selection. Resolves the hold, or null on a 409 conflict. */\n async hold(): Promise<HoldResult | null> {\n try {\n const h = await this.controller.hold();\n return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats } : null;\n } catch (err) {\n this.opts.onError?.(err);\n return null;\n }\n }\n\n /** Ask the server for the `qty` best free seats and hold them atomically. */\n async bestAvailable(qty: number, categoryKey?: string): Promise<BestAvailableResult | null> {\n try {\n const h = await this.controller.bestAvailable(qty, categoryKey);\n return h ? { holdId: h.holdId, expiresAt: h.expiresAt, labels: h.labels, seats: h.seats } : null;\n } catch (err) {\n this.opts.onError?.(err);\n return null;\n }\n }\n\n /**\n * Choose a ticket tier for a selected seat (e.g. Adult → Child). The seat's\n * available `tiers` are on each `SelectedSeat` from `getSelection()` /\n * `onSelectionChange`. Re-emits the selection with the new tier + price, and\n * the tier rides along in the next `hold()` / `onHold` per seat. `tierId=null`\n * reverts to the default tier.\n */\n setSeatTier(seatId: string, tierId: string | null): void {\n this.controller.setSeatTier(seatId, tierId);\n }\n\n /** Release the current hold (if any). No-op when nothing is held. */\n async release(): Promise<void> {\n await this.controller.release();\n }\n\n /** Tear everything down: close the socket, stop timers, drop the canvas. */\n destroy(): void {\n this.controller.destroy();\n if (this.hostEl && this.hostEl.parentNode) this.hostEl.parentNode.removeChild(this.hostEl);\n this.hostEl = null;\n this.mount = null;\n this.rendered = false;\n }\n}\n","/**\n * Minimal client for the public embed surface of workers/api (the `/pub/*`\n * routes). Deliberately self-contained — it does NOT reuse src/lib/api.ts,\n * which bakes in a build-time API base and dashboard session credentials. The\n * SDK runs cross-origin on a third-party ticketing page, so:\n * - apiBase is per-instance (constructor option), not a build constant;\n * - credentials are omitted (no cookie to send, avoids CORS-credential setup);\n * - no custom headers on mutating calls (keeps the CORS preflight trivial).\n */\nimport type { ChartDoc, PickerSeat as SelectedSeat } from '@seatlayer/core';\n\nexport interface HoldConflict {\n label: string;\n reason: string;\n}\n\nexport class ApiError extends Error {\n status: number;\n code?: string;\n /** Present when a hold 409s because seats were just taken/held. */\n conflicts?: HoldConflict[];\n /** Present when best-available 409s ('not_enough_together' | 'sold_out'). */\n reason?: string;\n\n constructor(status: number, message: string, code?: string, conflicts?: HoldConflict[], reason?: string) {\n super(message);\n this.name = 'ApiError';\n this.status = status;\n this.code = code;\n this.conflicts = conflicts;\n this.reason = reason;\n }\n}\n\nexport interface PubChartResult {\n event: { key: string; name: string };\n doc: ChartDoc;\n}\n\nexport interface PubObjectsResult {\n /** Every non-free seat's status, keyed by seat label. */\n seats: Record<string, string>;\n updatedAt: number;\n}\n\nexport interface HoldResult {\n holdId: string;\n expiresAt: number;\n /** The held seats with the buyer's chosen ticket tier per seat (present on hold). */\n seats?: SelectedSeat[];\n}\n\n/** Best-available response — the server-picked seats plus the hold they landed in. */\nexport interface BestAvailableResult {\n holdId: string;\n expiresAt: number;\n labels: string[];\n seats?: SelectedSeat[];\n}\n\nasync function request<T>(\n base: string,\n path: string,\n init: { method?: 'GET' | 'POST'; body?: unknown } = {},\n): Promise<T> {\n const method = init.method ?? 'GET';\n const headers: Record<string, string> = {};\n let body: string | undefined;\n if (init.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n body = JSON.stringify(init.body);\n }\n\n const res = await fetch(`${base}${path}`, { method, headers, body, credentials: 'omit' });\n\n const isJson = (res.headers.get('content-type') ?? '').includes('application/json');\n const data = isJson ? await res.json().catch(() => null) : null;\n\n if (!res.ok) {\n const err = data as\n | { error?: string; code?: string; conflicts?: HoldConflict[]; reason?: string }\n | null;\n throw new ApiError(res.status, err?.error ?? `request_failed_${res.status}`, err?.code, err?.conflicts, err?.reason);\n }\n return data as T;\n}\n\n/** Public-surface client bound to one apiBase (e.g. https://api.seatlayer.io). */\nexport class PubApi {\n constructor(private readonly base: string) {}\n\n chart(key: string): Promise<PubChartResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/chart`);\n }\n\n objects(key: string): Promise<PubObjectsResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/objects`);\n }\n\n hold(key: string, labels: string[]): Promise<HoldResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/hold`, {\n method: 'POST',\n body: { labels },\n });\n }\n\n bestAvailable(key: string, qty: number, categoryKey?: string): Promise<BestAvailableResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/best-available`, {\n method: 'POST',\n body: { qty, ...(categoryKey ? { categoryKey } : {}) },\n });\n }\n\n release(key: string, labels: string[], holdId: string): Promise<{ ok: true }> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/release`, {\n method: 'POST',\n body: { labels, holdId },\n });\n }\n\n socketUrl(key: string): string {\n const wsBase = this.base.replace(/^http/, 'ws');\n return `${wsBase}/pub/events/${encodeURIComponent(key)}/subscribe`;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/SeatingChart.ts","../src/api.ts"],"sourcesContent":["/**\n * @seatlayer/js — the framework-agnostic SeatLayer embed SDK.\n *\n * Works in any JS environment (plain HTML, React, Vue, Svelte, Angular, …).\n * Framework wrappers (@seatlayer/react, …) build on top of this.\n */\nexport { SeatingChart } from './SeatingChart';\nexport type { SeatingChartOptions, SelectedSeat, GAAreaAvailability } from './SeatingChart';\nexport { ApiError } from './api';\nexport type { HoldResult, HoldConflict, HoldLineItem, BestAvailableResult } from './api';\n","/**\n * SeatingChart — the embeddable buyer picker.\n *\n * A thin wrapper over the shared PickerController (src/picker/PickerController):\n * it owns the mount <div> + the public embed contract (hold-only — the SDK hands\n * the holdId to the host page for a server-side book) and delegates all transport\n * + booking to the controller, so the SDK inherits every fix made for the live\n * buyer page and the demo picker.\n */\nimport { PickerController, loadLocale, setStringOverrides, t, type PickerSeat } from '@seatlayer/core';\nimport { PubApi, type BestAvailableResult, type HoldResult } from './api';\n\nconst DEFAULT_API_BASE = 'https://api.seatlayer.io';\nconst DEFAULT_MAX_SELECTION = 10;\n\n/** A seat as surfaced to the host page (prices resolved from the chart's categories). */\nexport type SelectedSeat = PickerSeat;\nexport interface GAAreaAvailability {\n id: string; label: string; capacity: number; available: number; categoryKey: string; price: number; currency: string;\n tiers?: Array<{ id: string; name: string; price: number }>;\n}\n\nexport interface SeatingChartOptions {\n /** CSS selector or an HTMLElement to render into. */\n container: string | HTMLElement;\n /** Event key, e.g. `ev_xxx`. */\n event: string;\n /** API origin. Defaults to https://api.seatlayer.io. */\n apiBase?: string;\n /** Reserved for future authenticated rendering — accepted + stored, not yet sent. */\n publicKey?: string;\n /** Max seats selectable at once (default 10). */\n maxSelection?: number;\n /**\n * BCP 47 language for the widget UI — `'de'`, `'es-MX'`, etc. Falls back to\n * the browser language, then English. Built-in: en, es, de, fr. The German\n * bundle (etc.) is fetched on demand so unused languages cost nothing.\n */\n locale?: string;\n /**\n * Per-key string overrides layered over the active locale — white-label copy\n * without shipping a whole bundle, e.g. `{ 'map.fromPrice': 'ab {price}' }`.\n */\n messages?: Record<string, string>;\n /** ISO 4217 currency for on-map prices (default USD). */\n currency?: string;\n /**\n * Colorblind-safe rendering: category hues switch to an Okabe-Ito palette\n * and booked seats render hollow, so state never relies on hue alone.\n * Toggleable later with setColorblindSafe().\n */\n colorblindSafe?: boolean;\n onSelectionChange?: (seats: SelectedSeat[]) => void;\n onHold?: (result: HoldResult) => void;\n onHoldExpired?: () => void;\n onGAClick?: (area: GAAreaAvailability) => void;\n onError?: (err: unknown) => void;\n /**\n * Multi-floor charts only: fires when the buyer taps a deck in the stacked\n * 3D view, after the picker switches to that floor — lets the host page sync\n * its own floor UI (tabs, labels) with the map.\n */\n onDeckTap?: (floorId: string) => void;\n /**\n * Non-blocking, localized selection advice — currently the orphan-seat hint\n * (the selection would strand a single free seat between taken neighbors).\n * `null` clears it. Purely informational; nothing is ever prevented.\n */\n onHint?: (message: string | null) => void;\n}\n\nfunction resolveContainer(container: string | HTMLElement): HTMLElement {\n if (typeof container === 'string') {\n const el = document.querySelector(container);\n if (!el) throw new Error(`seatmap: container \"${container}\" not found`);\n return el as HTMLElement;\n }\n if (!(container instanceof HTMLElement)) {\n throw new Error('seatmap: container must be a CSS selector or an HTMLElement');\n }\n return container;\n}\n\nexport class SeatingChart {\n private readonly opts: SeatingChartOptions;\n private readonly controller: PickerController;\n /** Reserved for future authenticated rendering — stored, not yet sent on any request. */\n readonly publicKey?: string;\n\n private mount: HTMLElement | null = null;\n private hostEl: HTMLDivElement | null = null;\n private rendered = false;\n\n constructor(options: SeatingChartOptions) {\n if (!options || typeof options !== 'object') throw new Error('seatmap: options object is required');\n if (!options.container) throw new Error('seatmap: `container` is required');\n if (!options.event || typeof options.event !== 'string') throw new Error('seatmap: `event` key is required');\n\n this.opts = options;\n this.publicKey = options.publicKey;\n const api = new PubApi((options.apiBase ?? DEFAULT_API_BASE).replace(/\\/+$/, ''));\n this.controller = new PickerController({\n transport: api,\n eventKey: options.event,\n maxSelection: options.maxSelection ?? DEFAULT_MAX_SELECTION,\n currency: options.currency,\n onSelectionChange: (seats) => this.opts.onSelectionChange?.(seats),\n onHold: (h) => this.opts.onHold?.({ holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items }),\n onHoldExpired: () => this.opts.onHoldExpired?.(),\n onGAClick: (areaId) => {\n const area = this.controller.getGAAreas().find((candidate) => candidate.id === areaId);\n if (area) this.opts.onGAClick?.(area);\n },\n onError: (err) => this.opts.onError?.(err),\n onDeckTap: (floorId) => this.opts.onDeckTap?.(floorId),\n onHint: (message) => this.opts.onHint?.(message),\n colorblindSafe: options.colorblindSafe,\n });\n }\n\n /** Fetch the chart, mount the renderer, seed statuses and go live. Idempotent. */\n async render(): Promise<this> {\n if (this.rendered) return this;\n this.rendered = true;\n\n // Resolve + load the UI language before the first paint so on-map labels\n // (\"N LEFT\", \"FROM …\", the map aria-label) render translated. English and\n // already-loaded locales resolve synchronously; others fetch one small chunk.\n await loadLocale(this.opts.locale);\n if (this.opts.messages) setStringOverrides(this.opts.messages);\n\n // Mount an owned <div> inside the caller's container so we never fight their\n // layout and can cleanly remove it on destroy().\n this.mount = resolveContainer(this.opts.container);\n const host = document.createElement('div');\n host.style.width = '100%';\n host.style.height = '100%';\n host.style.position = 'relative';\n this.mount.appendChild(host);\n this.hostEl = host;\n\n const info = await this.controller.render(host);\n if (!info) {\n this.rendered = false;\n return this;\n }\n if (info.mode === 'test') {\n host.style.overflow = 'hidden';\n const ribbon = document.createElement('div');\n ribbon.textContent = t('picker.testMode');\n ribbon.setAttribute('aria-label', t('picker.testMode'));\n ribbon.style.cssText =\n 'position:absolute;top:18px;right:-34px;z-index:6;transform:rotate(45deg);' +\n 'width:140px;text-align:center;padding:4px 0;background:#f4b740;color:#1a1200;' +\n 'font:800 10.5px/1.4 -apple-system,BlinkMacSystemFont,sans-serif;letter-spacing:.12em;' +\n 'box-shadow:0 2px 8px rgba(0,0,0,.25);pointer-events:none;';\n host.appendChild(ribbon);\n }\n return this;\n }\n\n /** Current selection with prices resolved from the chart categories. */\n getSelection(): SelectedSeat[] {\n return this.controller.getSelection();\n }\n\n /** Hold the current selection. Resolves the hold, or null on a 409 conflict. */\n async hold(options: { ttlMs?: number } = {}): Promise<HoldResult | null> {\n try {\n const h = await this.controller.hold(undefined, options.ttlMs);\n return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : null;\n } catch (err) {\n this.opts.onError?.(err);\n return null;\n }\n }\n\n getGAAreas(): GAAreaAvailability[] {\n return this.controller.getGAAreas();\n }\n\n async holdGA(\n areaId: string,\n qty: number,\n options: { tierId?: string | null; ttlMs?: number } = {},\n ): Promise<HoldResult | null> {\n try {\n const h = await this.controller.holdGA(areaId, qty, options);\n return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : null;\n } catch (err) {\n this.opts.onError?.(err);\n return null;\n }\n }\n\n /** Ask the server for the `qty` best free seats and hold them atomically. */\n async bestAvailable(qty: number, categoryKey?: string): Promise<BestAvailableResult | null> {\n try {\n const h = await this.controller.bestAvailable(qty, categoryKey);\n return h ? { holdId: h.holdId, expiresAt: h.expiresAt, labels: h.labels, seats: h.seats, items: h.items } : null;\n } catch (err) {\n this.opts.onError?.(err);\n return null;\n }\n }\n\n /**\n * Choose a ticket tier for a selected seat (e.g. Adult → Child). The seat's\n * available `tiers` are on each `SelectedSeat` from `getSelection()` /\n * `onSelectionChange`. Re-emits the selection with the new tier + price, and\n * the tier rides along in the next `hold()` / `onHold` per seat. `tierId=null`\n * reverts to the default tier.\n */\n setSeatTier(seatId: string, tierId: string | null): void {\n this.controller.setSeatTier(seatId, tierId);\n }\n\n /**\n * Floors of a multi-floor chart — `[{ id, name }]` (single-floor charts\n * return one entry; empty before render()). Pair with setFloor() to build a\n * host-side floor switcher.\n */\n getFloors(): { id: string; name: string }[] {\n return this.controller.getFloors();\n }\n\n /** Switch the shown floor (2D). Warns + no-ops on single-floor charts. */\n setFloor(floorId: string): void {\n if (this.controller.getFloors().length <= 1) {\n console.warn('seatmap: setFloor() ignored — this chart has a single floor');\n return;\n }\n this.controller.setFloor(floorId);\n }\n\n /** Toggle colorblind-safe rendering at runtime (see options.colorblindSafe). */\n setColorblindSafe(on: boolean): void {\n this.controller.setColorblindSafe(on);\n }\n\n /** Release the current hold (if any). No-op when nothing is held. */\n async release(): Promise<void> {\n await this.controller.release();\n }\n\n /** Tear everything down: close the socket, stop timers, drop the canvas. */\n destroy(): void {\n this.controller.destroy();\n if (this.hostEl && this.hostEl.parentNode) this.hostEl.parentNode.removeChild(this.hostEl);\n this.hostEl = null;\n this.mount = null;\n this.rendered = false;\n }\n}\n","/**\n * Minimal client for the public embed surface of workers/api (the `/pub/*`\n * routes). Deliberately self-contained — it does NOT reuse src/lib/api.ts,\n * which bakes in a build-time API base and dashboard session credentials. The\n * SDK runs cross-origin on a third-party ticketing page, so:\n * - apiBase is per-instance (constructor option), not a build constant;\n * - credentials are omitted (no cookie to send, avoids CORS-credential setup);\n * - no custom headers on mutating calls (keeps the CORS preflight trivial).\n */\nimport type { ChartDoc, PickerSeat as SelectedSeat } from '@seatlayer/core';\n\nexport interface HoldConflict {\n label: string;\n status: string;\n}\n\nexport interface HoldLineItem {\n label: string; objectId: string; objectType: 'seat' | 'booth' | 'ga'; categoryKey: string;\n tierId: string | null;\n /** Price in major currency units (for example 45 means $45.00). */\n unitPrice: number;\n currency: string;\n quantity?: number;\n}\n\nexport class ApiError extends Error {\n status: number;\n code?: string;\n /** Present when a hold 409s because seats were just taken/held. */\n conflicts?: HoldConflict[];\n /** Present when best-available 409s ('not_enough_together' | 'sold_out'). */\n reason?: string;\n\n constructor(status: number, message: string, code?: string, conflicts?: HoldConflict[], reason?: string) {\n super(message);\n this.name = 'ApiError';\n this.status = status;\n this.code = code;\n this.conflicts = conflicts;\n this.reason = reason;\n }\n}\n\nexport interface PubChartResult {\n event: { key: string; name: string };\n doc: ChartDoc;\n}\n\nexport interface PubObjectsResult {\n /** Every non-free seat's status, keyed by seat label. */\n seats: Record<string, string>;\n updatedAt: number;\n}\n\nexport interface HoldResult {\n holdId: string;\n expiresAt: number;\n /** The held seats with the buyer's chosen ticket tier per seat (present on hold). */\n seats?: SelectedSeat[];\n items?: HoldLineItem[];\n}\n\n/** Best-available response — the server-picked seats plus the hold they landed in. */\nexport interface BestAvailableResult {\n holdId: string;\n expiresAt: number;\n labels: string[];\n seats?: SelectedSeat[];\n items?: HoldResult['items'];\n}\n\nasync function request<T>(\n base: string,\n path: string,\n init: { method?: 'GET' | 'POST'; body?: unknown } = {},\n): Promise<T> {\n const method = init.method ?? 'GET';\n const headers: Record<string, string> = {};\n let body: string | undefined;\n if (init.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n body = JSON.stringify(init.body);\n }\n\n const res = await fetch(`${base}${path}`, { method, headers, body, credentials: 'omit' });\n\n const isJson = (res.headers.get('content-type') ?? '').includes('application/json');\n const data = isJson ? await res.json().catch(() => null) : null;\n\n if (!res.ok) {\n const err = data as\n | { error?: string; code?: string; conflicts?: HoldConflict[]; reason?: string }\n | null;\n throw new ApiError(res.status, err?.error ?? `request_failed_${res.status}`, err?.code, err?.conflicts, err?.reason);\n }\n return data as T;\n}\n\n/** Public-surface client bound to one apiBase (e.g. https://api.seatlayer.io). */\nexport class PubApi {\n constructor(private readonly base: string) {}\n\n chart(key: string): Promise<PubChartResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/chart`);\n }\n\n objects(key: string): Promise<PubObjectsResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/objects`);\n }\n\n hold(key: string, selections: Array<{ label: string; tierId?: string | null }>, ttlMs?: number, replaceHoldId?: string): Promise<HoldResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/hold`, {\n method: 'POST',\n body: { selections, ...(ttlMs ? { ttlMs } : {}), ...(replaceHoldId ? { replaceHoldId } : {}) },\n });\n }\n\n bestAvailable(key: string, qty: number, categoryKey?: string): Promise<BestAvailableResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/best-available`, {\n method: 'POST',\n body: { qty, ...(categoryKey ? { categoryKey } : {}) },\n });\n }\n\n release(key: string, labels: string[], holdId: string): Promise<{ ok: true }> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/release`, {\n method: 'POST',\n body: { labels, holdId },\n });\n }\n\n socketUrl(key: string): string {\n const wsBase = this.base.replace(/^http/, 'ws');\n return `${wsBase}/pub/events/${encodeURIComponent(key)}/subscribe`;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -12,7 +12,18 @@ import { PickerSeat } from '@seatlayer/core';
|
|
|
12
12
|
|
|
13
13
|
interface HoldConflict {
|
|
14
14
|
label: string;
|
|
15
|
-
|
|
15
|
+
status: string;
|
|
16
|
+
}
|
|
17
|
+
interface HoldLineItem {
|
|
18
|
+
label: string;
|
|
19
|
+
objectId: string;
|
|
20
|
+
objectType: 'seat' | 'booth' | 'ga';
|
|
21
|
+
categoryKey: string;
|
|
22
|
+
tierId: string | null;
|
|
23
|
+
/** Price in major currency units (for example 45 means $45.00). */
|
|
24
|
+
unitPrice: number;
|
|
25
|
+
currency: string;
|
|
26
|
+
quantity?: number;
|
|
16
27
|
}
|
|
17
28
|
declare class ApiError extends Error {
|
|
18
29
|
status: number;
|
|
@@ -28,6 +39,7 @@ interface HoldResult {
|
|
|
28
39
|
expiresAt: number;
|
|
29
40
|
/** The held seats with the buyer's chosen ticket tier per seat (present on hold). */
|
|
30
41
|
seats?: PickerSeat[];
|
|
42
|
+
items?: HoldLineItem[];
|
|
31
43
|
}
|
|
32
44
|
/** Best-available response — the server-picked seats plus the hold they landed in. */
|
|
33
45
|
interface BestAvailableResult {
|
|
@@ -35,6 +47,7 @@ interface BestAvailableResult {
|
|
|
35
47
|
expiresAt: number;
|
|
36
48
|
labels: string[];
|
|
37
49
|
seats?: PickerSeat[];
|
|
50
|
+
items?: HoldResult['items'];
|
|
38
51
|
}
|
|
39
52
|
|
|
40
53
|
/**
|
|
@@ -49,6 +62,20 @@ interface BestAvailableResult {
|
|
|
49
62
|
|
|
50
63
|
/** A seat as surfaced to the host page (prices resolved from the chart's categories). */
|
|
51
64
|
type SelectedSeat = PickerSeat;
|
|
65
|
+
interface GAAreaAvailability {
|
|
66
|
+
id: string;
|
|
67
|
+
label: string;
|
|
68
|
+
capacity: number;
|
|
69
|
+
available: number;
|
|
70
|
+
categoryKey: string;
|
|
71
|
+
price: number;
|
|
72
|
+
currency: string;
|
|
73
|
+
tiers?: Array<{
|
|
74
|
+
id: string;
|
|
75
|
+
name: string;
|
|
76
|
+
price: number;
|
|
77
|
+
}>;
|
|
78
|
+
}
|
|
52
79
|
interface SeatingChartOptions {
|
|
53
80
|
/** CSS selector or an HTMLElement to render into. */
|
|
54
81
|
container: string | HTMLElement;
|
|
@@ -73,8 +100,16 @@ interface SeatingChartOptions {
|
|
|
73
100
|
messages?: Record<string, string>;
|
|
74
101
|
/** ISO 4217 currency for on-map prices (default USD). */
|
|
75
102
|
currency?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Colorblind-safe rendering: category hues switch to an Okabe-Ito palette
|
|
105
|
+
* and booked seats render hollow, so state never relies on hue alone.
|
|
106
|
+
* Toggleable later with setColorblindSafe().
|
|
107
|
+
*/
|
|
108
|
+
colorblindSafe?: boolean;
|
|
76
109
|
onSelectionChange?: (seats: SelectedSeat[]) => void;
|
|
77
110
|
onHold?: (result: HoldResult) => void;
|
|
111
|
+
onHoldExpired?: () => void;
|
|
112
|
+
onGAClick?: (area: GAAreaAvailability) => void;
|
|
78
113
|
onError?: (err: unknown) => void;
|
|
79
114
|
/**
|
|
80
115
|
* Multi-floor charts only: fires when the buyer taps a deck in the stacked
|
|
@@ -82,6 +117,12 @@ interface SeatingChartOptions {
|
|
|
82
117
|
* its own floor UI (tabs, labels) with the map.
|
|
83
118
|
*/
|
|
84
119
|
onDeckTap?: (floorId: string) => void;
|
|
120
|
+
/**
|
|
121
|
+
* Non-blocking, localized selection advice — currently the orphan-seat hint
|
|
122
|
+
* (the selection would strand a single free seat between taken neighbors).
|
|
123
|
+
* `null` clears it. Purely informational; nothing is ever prevented.
|
|
124
|
+
*/
|
|
125
|
+
onHint?: (message: string | null) => void;
|
|
85
126
|
}
|
|
86
127
|
declare class SeatingChart {
|
|
87
128
|
private readonly opts;
|
|
@@ -97,7 +138,14 @@ declare class SeatingChart {
|
|
|
97
138
|
/** Current selection with prices resolved from the chart categories. */
|
|
98
139
|
getSelection(): SelectedSeat[];
|
|
99
140
|
/** Hold the current selection. Resolves the hold, or null on a 409 conflict. */
|
|
100
|
-
hold(
|
|
141
|
+
hold(options?: {
|
|
142
|
+
ttlMs?: number;
|
|
143
|
+
}): Promise<HoldResult | null>;
|
|
144
|
+
getGAAreas(): GAAreaAvailability[];
|
|
145
|
+
holdGA(areaId: string, qty: number, options?: {
|
|
146
|
+
tierId?: string | null;
|
|
147
|
+
ttlMs?: number;
|
|
148
|
+
}): Promise<HoldResult | null>;
|
|
101
149
|
/** Ask the server for the `qty` best free seats and hold them atomically. */
|
|
102
150
|
bestAvailable(qty: number, categoryKey?: string): Promise<BestAvailableResult | null>;
|
|
103
151
|
/**
|
|
@@ -108,10 +156,23 @@ declare class SeatingChart {
|
|
|
108
156
|
* reverts to the default tier.
|
|
109
157
|
*/
|
|
110
158
|
setSeatTier(seatId: string, tierId: string | null): void;
|
|
159
|
+
/**
|
|
160
|
+
* Floors of a multi-floor chart — `[{ id, name }]` (single-floor charts
|
|
161
|
+
* return one entry; empty before render()). Pair with setFloor() to build a
|
|
162
|
+
* host-side floor switcher.
|
|
163
|
+
*/
|
|
164
|
+
getFloors(): {
|
|
165
|
+
id: string;
|
|
166
|
+
name: string;
|
|
167
|
+
}[];
|
|
168
|
+
/** Switch the shown floor (2D). Warns + no-ops on single-floor charts. */
|
|
169
|
+
setFloor(floorId: string): void;
|
|
170
|
+
/** Toggle colorblind-safe rendering at runtime (see options.colorblindSafe). */
|
|
171
|
+
setColorblindSafe(on: boolean): void;
|
|
111
172
|
/** Release the current hold (if any). No-op when nothing is held. */
|
|
112
173
|
release(): Promise<void>;
|
|
113
174
|
/** Tear everything down: close the socket, stop timers, drop the canvas. */
|
|
114
175
|
destroy(): void;
|
|
115
176
|
}
|
|
116
177
|
|
|
117
|
-
export { ApiError, type BestAvailableResult, type HoldConflict, type HoldResult, SeatingChart, type SeatingChartOptions, type SelectedSeat };
|
|
178
|
+
export { ApiError, type BestAvailableResult, type GAAreaAvailability, type HoldConflict, type HoldLineItem, type HoldResult, SeatingChart, type SeatingChartOptions, type SelectedSeat };
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,18 @@ import { PickerSeat } from '@seatlayer/core';
|
|
|
12
12
|
|
|
13
13
|
interface HoldConflict {
|
|
14
14
|
label: string;
|
|
15
|
-
|
|
15
|
+
status: string;
|
|
16
|
+
}
|
|
17
|
+
interface HoldLineItem {
|
|
18
|
+
label: string;
|
|
19
|
+
objectId: string;
|
|
20
|
+
objectType: 'seat' | 'booth' | 'ga';
|
|
21
|
+
categoryKey: string;
|
|
22
|
+
tierId: string | null;
|
|
23
|
+
/** Price in major currency units (for example 45 means $45.00). */
|
|
24
|
+
unitPrice: number;
|
|
25
|
+
currency: string;
|
|
26
|
+
quantity?: number;
|
|
16
27
|
}
|
|
17
28
|
declare class ApiError extends Error {
|
|
18
29
|
status: number;
|
|
@@ -28,6 +39,7 @@ interface HoldResult {
|
|
|
28
39
|
expiresAt: number;
|
|
29
40
|
/** The held seats with the buyer's chosen ticket tier per seat (present on hold). */
|
|
30
41
|
seats?: PickerSeat[];
|
|
42
|
+
items?: HoldLineItem[];
|
|
31
43
|
}
|
|
32
44
|
/** Best-available response — the server-picked seats plus the hold they landed in. */
|
|
33
45
|
interface BestAvailableResult {
|
|
@@ -35,6 +47,7 @@ interface BestAvailableResult {
|
|
|
35
47
|
expiresAt: number;
|
|
36
48
|
labels: string[];
|
|
37
49
|
seats?: PickerSeat[];
|
|
50
|
+
items?: HoldResult['items'];
|
|
38
51
|
}
|
|
39
52
|
|
|
40
53
|
/**
|
|
@@ -49,6 +62,20 @@ interface BestAvailableResult {
|
|
|
49
62
|
|
|
50
63
|
/** A seat as surfaced to the host page (prices resolved from the chart's categories). */
|
|
51
64
|
type SelectedSeat = PickerSeat;
|
|
65
|
+
interface GAAreaAvailability {
|
|
66
|
+
id: string;
|
|
67
|
+
label: string;
|
|
68
|
+
capacity: number;
|
|
69
|
+
available: number;
|
|
70
|
+
categoryKey: string;
|
|
71
|
+
price: number;
|
|
72
|
+
currency: string;
|
|
73
|
+
tiers?: Array<{
|
|
74
|
+
id: string;
|
|
75
|
+
name: string;
|
|
76
|
+
price: number;
|
|
77
|
+
}>;
|
|
78
|
+
}
|
|
52
79
|
interface SeatingChartOptions {
|
|
53
80
|
/** CSS selector or an HTMLElement to render into. */
|
|
54
81
|
container: string | HTMLElement;
|
|
@@ -73,8 +100,16 @@ interface SeatingChartOptions {
|
|
|
73
100
|
messages?: Record<string, string>;
|
|
74
101
|
/** ISO 4217 currency for on-map prices (default USD). */
|
|
75
102
|
currency?: string;
|
|
103
|
+
/**
|
|
104
|
+
* Colorblind-safe rendering: category hues switch to an Okabe-Ito palette
|
|
105
|
+
* and booked seats render hollow, so state never relies on hue alone.
|
|
106
|
+
* Toggleable later with setColorblindSafe().
|
|
107
|
+
*/
|
|
108
|
+
colorblindSafe?: boolean;
|
|
76
109
|
onSelectionChange?: (seats: SelectedSeat[]) => void;
|
|
77
110
|
onHold?: (result: HoldResult) => void;
|
|
111
|
+
onHoldExpired?: () => void;
|
|
112
|
+
onGAClick?: (area: GAAreaAvailability) => void;
|
|
78
113
|
onError?: (err: unknown) => void;
|
|
79
114
|
/**
|
|
80
115
|
* Multi-floor charts only: fires when the buyer taps a deck in the stacked
|
|
@@ -82,6 +117,12 @@ interface SeatingChartOptions {
|
|
|
82
117
|
* its own floor UI (tabs, labels) with the map.
|
|
83
118
|
*/
|
|
84
119
|
onDeckTap?: (floorId: string) => void;
|
|
120
|
+
/**
|
|
121
|
+
* Non-blocking, localized selection advice — currently the orphan-seat hint
|
|
122
|
+
* (the selection would strand a single free seat between taken neighbors).
|
|
123
|
+
* `null` clears it. Purely informational; nothing is ever prevented.
|
|
124
|
+
*/
|
|
125
|
+
onHint?: (message: string | null) => void;
|
|
85
126
|
}
|
|
86
127
|
declare class SeatingChart {
|
|
87
128
|
private readonly opts;
|
|
@@ -97,7 +138,14 @@ declare class SeatingChart {
|
|
|
97
138
|
/** Current selection with prices resolved from the chart categories. */
|
|
98
139
|
getSelection(): SelectedSeat[];
|
|
99
140
|
/** Hold the current selection. Resolves the hold, or null on a 409 conflict. */
|
|
100
|
-
hold(
|
|
141
|
+
hold(options?: {
|
|
142
|
+
ttlMs?: number;
|
|
143
|
+
}): Promise<HoldResult | null>;
|
|
144
|
+
getGAAreas(): GAAreaAvailability[];
|
|
145
|
+
holdGA(areaId: string, qty: number, options?: {
|
|
146
|
+
tierId?: string | null;
|
|
147
|
+
ttlMs?: number;
|
|
148
|
+
}): Promise<HoldResult | null>;
|
|
101
149
|
/** Ask the server for the `qty` best free seats and hold them atomically. */
|
|
102
150
|
bestAvailable(qty: number, categoryKey?: string): Promise<BestAvailableResult | null>;
|
|
103
151
|
/**
|
|
@@ -108,10 +156,23 @@ declare class SeatingChart {
|
|
|
108
156
|
* reverts to the default tier.
|
|
109
157
|
*/
|
|
110
158
|
setSeatTier(seatId: string, tierId: string | null): void;
|
|
159
|
+
/**
|
|
160
|
+
* Floors of a multi-floor chart — `[{ id, name }]` (single-floor charts
|
|
161
|
+
* return one entry; empty before render()). Pair with setFloor() to build a
|
|
162
|
+
* host-side floor switcher.
|
|
163
|
+
*/
|
|
164
|
+
getFloors(): {
|
|
165
|
+
id: string;
|
|
166
|
+
name: string;
|
|
167
|
+
}[];
|
|
168
|
+
/** Switch the shown floor (2D). Warns + no-ops on single-floor charts. */
|
|
169
|
+
setFloor(floorId: string): void;
|
|
170
|
+
/** Toggle colorblind-safe rendering at runtime (see options.colorblindSafe). */
|
|
171
|
+
setColorblindSafe(on: boolean): void;
|
|
111
172
|
/** Release the current hold (if any). No-op when nothing is held. */
|
|
112
173
|
release(): Promise<void>;
|
|
113
174
|
/** Tear everything down: close the socket, stop timers, drop the canvas. */
|
|
114
175
|
destroy(): void;
|
|
115
176
|
}
|
|
116
177
|
|
|
117
|
-
export { ApiError, type BestAvailableResult, type HoldConflict, type HoldResult, SeatingChart, type SeatingChartOptions, type SelectedSeat };
|
|
178
|
+
export { ApiError, type BestAvailableResult, type GAAreaAvailability, type HoldConflict, type HoldLineItem, type HoldResult, SeatingChart, type SeatingChartOptions, type SelectedSeat };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/SeatingChart.ts
|
|
2
|
-
import { PickerController, loadLocale, setStringOverrides } from "@seatlayer/core";
|
|
2
|
+
import { PickerController, loadLocale, setStringOverrides, t } from "@seatlayer/core";
|
|
3
3
|
|
|
4
4
|
// src/api.ts
|
|
5
5
|
var ApiError = class extends Error {
|
|
@@ -39,10 +39,10 @@ var PubApi = class {
|
|
|
39
39
|
objects(key) {
|
|
40
40
|
return request(this.base, `/pub/events/${encodeURIComponent(key)}/objects`);
|
|
41
41
|
}
|
|
42
|
-
hold(key,
|
|
42
|
+
hold(key, selections, ttlMs, replaceHoldId) {
|
|
43
43
|
return request(this.base, `/pub/events/${encodeURIComponent(key)}/hold`, {
|
|
44
44
|
method: "POST",
|
|
45
|
-
body: {
|
|
45
|
+
body: { selections, ...ttlMs ? { ttlMs } : {}, ...replaceHoldId ? { replaceHoldId } : {} }
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
bestAvailable(key, qty, categoryKey) {
|
|
@@ -94,9 +94,16 @@ var SeatingChart = class {
|
|
|
94
94
|
maxSelection: options.maxSelection ?? DEFAULT_MAX_SELECTION,
|
|
95
95
|
currency: options.currency,
|
|
96
96
|
onSelectionChange: (seats) => this.opts.onSelectionChange?.(seats),
|
|
97
|
-
onHold: (h) => this.opts.onHold?.({ holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats }),
|
|
97
|
+
onHold: (h) => this.opts.onHold?.({ holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items }),
|
|
98
|
+
onHoldExpired: () => this.opts.onHoldExpired?.(),
|
|
99
|
+
onGAClick: (areaId) => {
|
|
100
|
+
const area = this.controller.getGAAreas().find((candidate) => candidate.id === areaId);
|
|
101
|
+
if (area) this.opts.onGAClick?.(area);
|
|
102
|
+
},
|
|
98
103
|
onError: (err) => this.opts.onError?.(err),
|
|
99
|
-
onDeckTap: (floorId) => this.opts.onDeckTap?.(floorId)
|
|
104
|
+
onDeckTap: (floorId) => this.opts.onDeckTap?.(floorId),
|
|
105
|
+
onHint: (message) => this.opts.onHint?.(message),
|
|
106
|
+
colorblindSafe: options.colorblindSafe
|
|
100
107
|
});
|
|
101
108
|
}
|
|
102
109
|
/** Fetch the chart, mount the renderer, seed statuses and go live. Idempotent. */
|
|
@@ -112,8 +119,19 @@ var SeatingChart = class {
|
|
|
112
119
|
host.style.position = "relative";
|
|
113
120
|
this.mount.appendChild(host);
|
|
114
121
|
this.hostEl = host;
|
|
115
|
-
const
|
|
116
|
-
if (!
|
|
122
|
+
const info = await this.controller.render(host);
|
|
123
|
+
if (!info) {
|
|
124
|
+
this.rendered = false;
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
if (info.mode === "test") {
|
|
128
|
+
host.style.overflow = "hidden";
|
|
129
|
+
const ribbon = document.createElement("div");
|
|
130
|
+
ribbon.textContent = t("picker.testMode");
|
|
131
|
+
ribbon.setAttribute("aria-label", t("picker.testMode"));
|
|
132
|
+
ribbon.style.cssText = "position:absolute;top:18px;right:-34px;z-index:6;transform:rotate(45deg);width:140px;text-align:center;padding:4px 0;background:#f4b740;color:#1a1200;font:800 10.5px/1.4 -apple-system,BlinkMacSystemFont,sans-serif;letter-spacing:.12em;box-shadow:0 2px 8px rgba(0,0,0,.25);pointer-events:none;";
|
|
133
|
+
host.appendChild(ribbon);
|
|
134
|
+
}
|
|
117
135
|
return this;
|
|
118
136
|
}
|
|
119
137
|
/** Current selection with prices resolved from the chart categories. */
|
|
@@ -121,10 +139,22 @@ var SeatingChart = class {
|
|
|
121
139
|
return this.controller.getSelection();
|
|
122
140
|
}
|
|
123
141
|
/** Hold the current selection. Resolves the hold, or null on a 409 conflict. */
|
|
124
|
-
async hold() {
|
|
142
|
+
async hold(options = {}) {
|
|
143
|
+
try {
|
|
144
|
+
const h = await this.controller.hold(void 0, options.ttlMs);
|
|
145
|
+
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : null;
|
|
146
|
+
} catch (err) {
|
|
147
|
+
this.opts.onError?.(err);
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
getGAAreas() {
|
|
152
|
+
return this.controller.getGAAreas();
|
|
153
|
+
}
|
|
154
|
+
async holdGA(areaId, qty, options = {}) {
|
|
125
155
|
try {
|
|
126
|
-
const h = await this.controller.
|
|
127
|
-
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats } : null;
|
|
156
|
+
const h = await this.controller.holdGA(areaId, qty, options);
|
|
157
|
+
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : null;
|
|
128
158
|
} catch (err) {
|
|
129
159
|
this.opts.onError?.(err);
|
|
130
160
|
return null;
|
|
@@ -134,7 +164,7 @@ var SeatingChart = class {
|
|
|
134
164
|
async bestAvailable(qty, categoryKey) {
|
|
135
165
|
try {
|
|
136
166
|
const h = await this.controller.bestAvailable(qty, categoryKey);
|
|
137
|
-
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, labels: h.labels, seats: h.seats } : null;
|
|
167
|
+
return h ? { holdId: h.holdId, expiresAt: h.expiresAt, labels: h.labels, seats: h.seats, items: h.items } : null;
|
|
138
168
|
} catch (err) {
|
|
139
169
|
this.opts.onError?.(err);
|
|
140
170
|
return null;
|
|
@@ -150,6 +180,26 @@ var SeatingChart = class {
|
|
|
150
180
|
setSeatTier(seatId, tierId) {
|
|
151
181
|
this.controller.setSeatTier(seatId, tierId);
|
|
152
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* Floors of a multi-floor chart — `[{ id, name }]` (single-floor charts
|
|
185
|
+
* return one entry; empty before render()). Pair with setFloor() to build a
|
|
186
|
+
* host-side floor switcher.
|
|
187
|
+
*/
|
|
188
|
+
getFloors() {
|
|
189
|
+
return this.controller.getFloors();
|
|
190
|
+
}
|
|
191
|
+
/** Switch the shown floor (2D). Warns + no-ops on single-floor charts. */
|
|
192
|
+
setFloor(floorId) {
|
|
193
|
+
if (this.controller.getFloors().length <= 1) {
|
|
194
|
+
console.warn("seatmap: setFloor() ignored \u2014 this chart has a single floor");
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
this.controller.setFloor(floorId);
|
|
198
|
+
}
|
|
199
|
+
/** Toggle colorblind-safe rendering at runtime (see options.colorblindSafe). */
|
|
200
|
+
setColorblindSafe(on) {
|
|
201
|
+
this.controller.setColorblindSafe(on);
|
|
202
|
+
}
|
|
153
203
|
/** Release the current hold (if any). No-op when nothing is held. */
|
|
154
204
|
async release() {
|
|
155
205
|
await this.controller.release();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/SeatingChart.ts","../src/api.ts"],"sourcesContent":["/**\n * SeatingChart — the embeddable buyer picker.\n *\n * A thin wrapper over the shared PickerController (src/picker/PickerController):\n * it owns the mount <div> + the public embed contract (hold-only — the SDK hands\n * the holdId to the host page for a server-side book) and delegates all transport\n * + booking to the controller, so the SDK inherits every fix made for the live\n * buyer page and the demo picker.\n */\nimport { PickerController, loadLocale, setStringOverrides, type PickerSeat } from '@seatlayer/core';\nimport { PubApi, type BestAvailableResult, type HoldResult } from './api';\n\nconst DEFAULT_API_BASE = 'https://api.seatlayer.io';\nconst DEFAULT_MAX_SELECTION = 10;\n\n/** A seat as surfaced to the host page (prices resolved from the chart's categories). */\nexport type SelectedSeat = PickerSeat;\n\nexport interface SeatingChartOptions {\n /** CSS selector or an HTMLElement to render into. */\n container: string | HTMLElement;\n /** Event key, e.g. `ev_xxx`. */\n event: string;\n /** API origin. Defaults to https://api.seatlayer.io. */\n apiBase?: string;\n /** Reserved for future authenticated rendering — accepted + stored, not yet sent. */\n publicKey?: string;\n /** Max seats selectable at once (default 10). */\n maxSelection?: number;\n /**\n * BCP 47 language for the widget UI — `'de'`, `'es-MX'`, etc. Falls back to\n * the browser language, then English. Built-in: en, es, de, fr. The German\n * bundle (etc.) is fetched on demand so unused languages cost nothing.\n */\n locale?: string;\n /**\n * Per-key string overrides layered over the active locale — white-label copy\n * without shipping a whole bundle, e.g. `{ 'map.fromPrice': 'ab {price}' }`.\n */\n messages?: Record<string, string>;\n /** ISO 4217 currency for on-map prices (default USD). */\n currency?: string;\n onSelectionChange?: (seats: SelectedSeat[]) => void;\n onHold?: (result: HoldResult) => void;\n onError?: (err: unknown) => void;\n /**\n * Multi-floor charts only: fires when the buyer taps a deck in the stacked\n * 3D view, after the picker switches to that floor — lets the host page sync\n * its own floor UI (tabs, labels) with the map.\n */\n onDeckTap?: (floorId: string) => void;\n}\n\nfunction resolveContainer(container: string | HTMLElement): HTMLElement {\n if (typeof container === 'string') {\n const el = document.querySelector(container);\n if (!el) throw new Error(`seatmap: container \"${container}\" not found`);\n return el as HTMLElement;\n }\n if (!(container instanceof HTMLElement)) {\n throw new Error('seatmap: container must be a CSS selector or an HTMLElement');\n }\n return container;\n}\n\nexport class SeatingChart {\n private readonly opts: SeatingChartOptions;\n private readonly controller: PickerController;\n /** Reserved for future authenticated rendering — stored, not yet sent on any request. */\n readonly publicKey?: string;\n\n private mount: HTMLElement | null = null;\n private hostEl: HTMLDivElement | null = null;\n private rendered = false;\n\n constructor(options: SeatingChartOptions) {\n if (!options || typeof options !== 'object') throw new Error('seatmap: options object is required');\n if (!options.container) throw new Error('seatmap: `container` is required');\n if (!options.event || typeof options.event !== 'string') throw new Error('seatmap: `event` key is required');\n\n this.opts = options;\n this.publicKey = options.publicKey;\n const api = new PubApi((options.apiBase ?? DEFAULT_API_BASE).replace(/\\/+$/, ''));\n this.controller = new PickerController({\n transport: api,\n eventKey: options.event,\n maxSelection: options.maxSelection ?? DEFAULT_MAX_SELECTION,\n currency: options.currency,\n onSelectionChange: (seats) => this.opts.onSelectionChange?.(seats),\n onHold: (h) => this.opts.onHold?.({ holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats }),\n onError: (err) => this.opts.onError?.(err),\n onDeckTap: (floorId) => this.opts.onDeckTap?.(floorId),\n });\n }\n\n /** Fetch the chart, mount the renderer, seed statuses and go live. Idempotent. */\n async render(): Promise<this> {\n if (this.rendered) return this;\n this.rendered = true;\n\n // Resolve + load the UI language before the first paint so on-map labels\n // (\"N LEFT\", \"FROM …\", the map aria-label) render translated. English and\n // already-loaded locales resolve synchronously; others fetch one small chunk.\n await loadLocale(this.opts.locale);\n if (this.opts.messages) setStringOverrides(this.opts.messages);\n\n // Mount an owned <div> inside the caller's container so we never fight their\n // layout and can cleanly remove it on destroy().\n this.mount = resolveContainer(this.opts.container);\n const host = document.createElement('div');\n host.style.width = '100%';\n host.style.height = '100%';\n host.style.position = 'relative';\n this.mount.appendChild(host);\n this.hostEl = host;\n\n const ok = await this.controller.render(host);\n if (!ok) this.rendered = false; // render() already emitted the error\n return this;\n }\n\n /** Current selection with prices resolved from the chart categories. */\n getSelection(): SelectedSeat[] {\n return this.controller.getSelection();\n }\n\n /** Hold the current selection. Resolves the hold, or null on a 409 conflict. */\n async hold(): Promise<HoldResult | null> {\n try {\n const h = await this.controller.hold();\n return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats } : null;\n } catch (err) {\n this.opts.onError?.(err);\n return null;\n }\n }\n\n /** Ask the server for the `qty` best free seats and hold them atomically. */\n async bestAvailable(qty: number, categoryKey?: string): Promise<BestAvailableResult | null> {\n try {\n const h = await this.controller.bestAvailable(qty, categoryKey);\n return h ? { holdId: h.holdId, expiresAt: h.expiresAt, labels: h.labels, seats: h.seats } : null;\n } catch (err) {\n this.opts.onError?.(err);\n return null;\n }\n }\n\n /**\n * Choose a ticket tier for a selected seat (e.g. Adult → Child). The seat's\n * available `tiers` are on each `SelectedSeat` from `getSelection()` /\n * `onSelectionChange`. Re-emits the selection with the new tier + price, and\n * the tier rides along in the next `hold()` / `onHold` per seat. `tierId=null`\n * reverts to the default tier.\n */\n setSeatTier(seatId: string, tierId: string | null): void {\n this.controller.setSeatTier(seatId, tierId);\n }\n\n /** Release the current hold (if any). No-op when nothing is held. */\n async release(): Promise<void> {\n await this.controller.release();\n }\n\n /** Tear everything down: close the socket, stop timers, drop the canvas. */\n destroy(): void {\n this.controller.destroy();\n if (this.hostEl && this.hostEl.parentNode) this.hostEl.parentNode.removeChild(this.hostEl);\n this.hostEl = null;\n this.mount = null;\n this.rendered = false;\n }\n}\n","/**\n * Minimal client for the public embed surface of workers/api (the `/pub/*`\n * routes). Deliberately self-contained — it does NOT reuse src/lib/api.ts,\n * which bakes in a build-time API base and dashboard session credentials. The\n * SDK runs cross-origin on a third-party ticketing page, so:\n * - apiBase is per-instance (constructor option), not a build constant;\n * - credentials are omitted (no cookie to send, avoids CORS-credential setup);\n * - no custom headers on mutating calls (keeps the CORS preflight trivial).\n */\nimport type { ChartDoc, PickerSeat as SelectedSeat } from '@seatlayer/core';\n\nexport interface HoldConflict {\n label: string;\n reason: string;\n}\n\nexport class ApiError extends Error {\n status: number;\n code?: string;\n /** Present when a hold 409s because seats were just taken/held. */\n conflicts?: HoldConflict[];\n /** Present when best-available 409s ('not_enough_together' | 'sold_out'). */\n reason?: string;\n\n constructor(status: number, message: string, code?: string, conflicts?: HoldConflict[], reason?: string) {\n super(message);\n this.name = 'ApiError';\n this.status = status;\n this.code = code;\n this.conflicts = conflicts;\n this.reason = reason;\n }\n}\n\nexport interface PubChartResult {\n event: { key: string; name: string };\n doc: ChartDoc;\n}\n\nexport interface PubObjectsResult {\n /** Every non-free seat's status, keyed by seat label. */\n seats: Record<string, string>;\n updatedAt: number;\n}\n\nexport interface HoldResult {\n holdId: string;\n expiresAt: number;\n /** The held seats with the buyer's chosen ticket tier per seat (present on hold). */\n seats?: SelectedSeat[];\n}\n\n/** Best-available response — the server-picked seats plus the hold they landed in. */\nexport interface BestAvailableResult {\n holdId: string;\n expiresAt: number;\n labels: string[];\n seats?: SelectedSeat[];\n}\n\nasync function request<T>(\n base: string,\n path: string,\n init: { method?: 'GET' | 'POST'; body?: unknown } = {},\n): Promise<T> {\n const method = init.method ?? 'GET';\n const headers: Record<string, string> = {};\n let body: string | undefined;\n if (init.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n body = JSON.stringify(init.body);\n }\n\n const res = await fetch(`${base}${path}`, { method, headers, body, credentials: 'omit' });\n\n const isJson = (res.headers.get('content-type') ?? '').includes('application/json');\n const data = isJson ? await res.json().catch(() => null) : null;\n\n if (!res.ok) {\n const err = data as\n | { error?: string; code?: string; conflicts?: HoldConflict[]; reason?: string }\n | null;\n throw new ApiError(res.status, err?.error ?? `request_failed_${res.status}`, err?.code, err?.conflicts, err?.reason);\n }\n return data as T;\n}\n\n/** Public-surface client bound to one apiBase (e.g. https://api.seatlayer.io). */\nexport class PubApi {\n constructor(private readonly base: string) {}\n\n chart(key: string): Promise<PubChartResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/chart`);\n }\n\n objects(key: string): Promise<PubObjectsResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/objects`);\n }\n\n hold(key: string, labels: string[]): Promise<HoldResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/hold`, {\n method: 'POST',\n body: { labels },\n });\n }\n\n bestAvailable(key: string, qty: number, categoryKey?: string): Promise<BestAvailableResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/best-available`, {\n method: 'POST',\n body: { qty, ...(categoryKey ? { categoryKey } : {}) },\n });\n }\n\n release(key: string, labels: string[], holdId: string): Promise<{ ok: true }> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/release`, {\n method: 'POST',\n body: { labels, holdId },\n });\n }\n\n socketUrl(key: string): string {\n const wsBase = this.base.replace(/^http/, 'ws');\n return `${wsBase}/pub/events/${encodeURIComponent(key)}/subscribe`;\n }\n}\n"],"mappings":";AASA,SAAS,kBAAkB,YAAY,0BAA2C;;;ACO3E,IAAM,WAAN,cAAuB,MAAM;AAAA,EAQlC,YAAY,QAAgB,SAAiB,MAAe,WAA4B,QAAiB;AACvG,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,OAAO;AACZ,SAAK,YAAY;AACjB,SAAK,SAAS;AAAA,EAChB;AACF;AA4BA,eAAe,QACb,MACA,MACA,OAAoD,CAAC,GACzC;AACZ,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,UAAkC,CAAC;AACzC,MAAI;AACJ,MAAI,KAAK,SAAS,QAAW;AAC3B,YAAQ,cAAc,IAAI;AAC1B,WAAO,KAAK,UAAU,KAAK,IAAI;AAAA,EACjC;AAEA,QAAM,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,IAAI,EAAE,QAAQ,SAAS,MAAM,aAAa,OAAO,CAAC;AAExF,QAAM,UAAU,IAAI,QAAQ,IAAI,cAAc,KAAK,IAAI,SAAS,kBAAkB;AAClF,QAAM,OAAO,SAAS,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,IAAI;AAE3D,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,MAAM;AAGZ,UAAM,IAAI,SAAS,IAAI,QAAQ,KAAK,SAAS,kBAAkB,IAAI,MAAM,IAAI,KAAK,MAAM,KAAK,WAAW,KAAK,MAAM;AAAA,EACrH;AACA,SAAO;AACT;AAGO,IAAM,SAAN,MAAa;AAAA,EAClB,YAA6B,MAAc;AAAd;AAAA,EAAe;AAAA,EAE5C,MAAM,KAAsC;AAC1C,WAAO,QAAQ,KAAK,MAAM,eAAe,mBAAmB,GAAG,CAAC,QAAQ;AAAA,EAC1E;AAAA,EAEA,QAAQ,KAAwC;AAC9C,WAAO,QAAQ,KAAK,MAAM,eAAe,mBAAmB,GAAG,CAAC,UAAU;AAAA,EAC5E;AAAA,EAEA,KAAK,KAAa,QAAuC;AACvD,WAAO,QAAQ,KAAK,MAAM,eAAe,mBAAmB,GAAG,CAAC,SAAS;AAAA,MACvE,QAAQ;AAAA,MACR,MAAM,EAAE,OAAO;AAAA,IACjB,CAAC;AAAA,EACH;AAAA,EAEA,cAAc,KAAa,KAAa,aAAoD;AAC1F,WAAO,QAAQ,KAAK,MAAM,eAAe,mBAAmB,GAAG,CAAC,mBAAmB;AAAA,MACjF,QAAQ;AAAA,MACR,MAAM,EAAE,KAAK,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC,EAAG;AAAA,IACvD,CAAC;AAAA,EACH;AAAA,EAEA,QAAQ,KAAa,QAAkB,QAAuC;AAC5E,WAAO,QAAQ,KAAK,MAAM,eAAe,mBAAmB,GAAG,CAAC,YAAY;AAAA,MAC1E,QAAQ;AAAA,MACR,MAAM,EAAE,QAAQ,OAAO;AAAA,IACzB,CAAC;AAAA,EACH;AAAA,EAEA,UAAU,KAAqB;AAC7B,UAAM,SAAS,KAAK,KAAK,QAAQ,SAAS,IAAI;AAC9C,WAAO,GAAG,MAAM,eAAe,mBAAmB,GAAG,CAAC;AAAA,EACxD;AACF;;;ADhHA,IAAM,mBAAmB;AACzB,IAAM,wBAAwB;AAwC9B,SAAS,iBAAiB,WAA8C;AACtE,MAAI,OAAO,cAAc,UAAU;AACjC,UAAM,KAAK,SAAS,cAAc,SAAS;AAC3C,QAAI,CAAC,GAAI,OAAM,IAAI,MAAM,uBAAuB,SAAS,aAAa;AACtE,WAAO;AAAA,EACT;AACA,MAAI,EAAE,qBAAqB,cAAc;AACvC,UAAM,IAAI,MAAM,6DAA6D;AAAA,EAC/E;AACA,SAAO;AACT;AAEO,IAAM,eAAN,MAAmB;AAAA,EAUxB,YAAY,SAA8B;AAJ1C,SAAQ,QAA4B;AACpC,SAAQ,SAAgC;AACxC,SAAQ,WAAW;AAGjB,QAAI,CAAC,WAAW,OAAO,YAAY,SAAU,OAAM,IAAI,MAAM,qCAAqC;AAClG,QAAI,CAAC,QAAQ,UAAW,OAAM,IAAI,MAAM,kCAAkC;AAC1E,QAAI,CAAC,QAAQ,SAAS,OAAO,QAAQ,UAAU,SAAU,OAAM,IAAI,MAAM,kCAAkC;AAE3G,SAAK,OAAO;AACZ,SAAK,YAAY,QAAQ;AACzB,UAAM,MAAM,IAAI,QAAQ,QAAQ,WAAW,kBAAkB,QAAQ,QAAQ,EAAE,CAAC;AAChF,SAAK,aAAa,IAAI,iBAAiB;AAAA,MACrC,WAAW;AAAA,MACX,UAAU,QAAQ;AAAA,MAClB,cAAc,QAAQ,gBAAgB;AAAA,MACtC,UAAU,QAAQ;AAAA,MAClB,mBAAmB,CAAC,UAAU,KAAK,KAAK,oBAAoB,KAAK;AAAA,MACjE,QAAQ,CAAC,MAAM,KAAK,KAAK,SAAS,EAAE,QAAQ,EAAE,QAAQ,WAAW,EAAE,WAAW,OAAO,EAAE,MAAM,CAAC;AAAA,MAC9F,SAAS,CAAC,QAAQ,KAAK,KAAK,UAAU,GAAG;AAAA,MACzC,WAAW,CAAC,YAAY,KAAK,KAAK,YAAY,OAAO;AAAA,IACvD,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,SAAwB;AAC5B,QAAI,KAAK,SAAU,QAAO;AAC1B,SAAK,WAAW;AAKhB,UAAM,WAAW,KAAK,KAAK,MAAM;AACjC,QAAI,KAAK,KAAK,SAAU,oBAAmB,KAAK,KAAK,QAAQ;AAI7D,SAAK,QAAQ,iBAAiB,KAAK,KAAK,SAAS;AACjD,UAAM,OAAO,SAAS,cAAc,KAAK;AACzC,SAAK,MAAM,QAAQ;AACnB,SAAK,MAAM,SAAS;AACpB,SAAK,MAAM,WAAW;AACtB,SAAK,MAAM,YAAY,IAAI;AAC3B,SAAK,SAAS;AAEd,UAAM,KAAK,MAAM,KAAK,WAAW,OAAO,IAAI;AAC5C,QAAI,CAAC,GAAI,MAAK,WAAW;AACzB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,eAA+B;AAC7B,WAAO,KAAK,WAAW,aAAa;AAAA,EACtC;AAAA;AAAA,EAGA,MAAM,OAAmC;AACvC,QAAI;AACF,YAAM,IAAI,MAAM,KAAK,WAAW,KAAK;AACrC,aAAO,IAAI,EAAE,QAAQ,EAAE,QAAQ,WAAW,EAAE,WAAW,OAAO,EAAE,MAAM,IAAI;AAAA,IAC5E,SAAS,KAAK;AACZ,WAAK,KAAK,UAAU,GAAG;AACvB,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,cAAc,KAAa,aAA2D;AAC1F,QAAI;AACF,YAAM,IAAI,MAAM,KAAK,WAAW,cAAc,KAAK,WAAW;AAC9D,aAAO,IAAI,EAAE,QAAQ,EAAE,QAAQ,WAAW,EAAE,WAAW,QAAQ,EAAE,QAAQ,OAAO,EAAE,MAAM,IAAI;AAAA,IAC9F,SAAS,KAAK;AACZ,WAAK,KAAK,UAAU,GAAG;AACvB,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,YAAY,QAAgB,QAA6B;AACvD,SAAK,WAAW,YAAY,QAAQ,MAAM;AAAA,EAC5C;AAAA;AAAA,EAGA,MAAM,UAAyB;AAC7B,UAAM,KAAK,WAAW,QAAQ;AAAA,EAChC;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,WAAW,QAAQ;AACxB,QAAI,KAAK,UAAU,KAAK,OAAO,WAAY,MAAK,OAAO,WAAW,YAAY,KAAK,MAAM;AACzF,SAAK,SAAS;AACd,SAAK,QAAQ;AACb,SAAK,WAAW;AAAA,EAClB;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/SeatingChart.ts","../src/api.ts"],"sourcesContent":["/**\n * SeatingChart — the embeddable buyer picker.\n *\n * A thin wrapper over the shared PickerController (src/picker/PickerController):\n * it owns the mount <div> + the public embed contract (hold-only — the SDK hands\n * the holdId to the host page for a server-side book) and delegates all transport\n * + booking to the controller, so the SDK inherits every fix made for the live\n * buyer page and the demo picker.\n */\nimport { PickerController, loadLocale, setStringOverrides, t, type PickerSeat } from '@seatlayer/core';\nimport { PubApi, type BestAvailableResult, type HoldResult } from './api';\n\nconst DEFAULT_API_BASE = 'https://api.seatlayer.io';\nconst DEFAULT_MAX_SELECTION = 10;\n\n/** A seat as surfaced to the host page (prices resolved from the chart's categories). */\nexport type SelectedSeat = PickerSeat;\nexport interface GAAreaAvailability {\n id: string; label: string; capacity: number; available: number; categoryKey: string; price: number; currency: string;\n tiers?: Array<{ id: string; name: string; price: number }>;\n}\n\nexport interface SeatingChartOptions {\n /** CSS selector or an HTMLElement to render into. */\n container: string | HTMLElement;\n /** Event key, e.g. `ev_xxx`. */\n event: string;\n /** API origin. Defaults to https://api.seatlayer.io. */\n apiBase?: string;\n /** Reserved for future authenticated rendering — accepted + stored, not yet sent. */\n publicKey?: string;\n /** Max seats selectable at once (default 10). */\n maxSelection?: number;\n /**\n * BCP 47 language for the widget UI — `'de'`, `'es-MX'`, etc. Falls back to\n * the browser language, then English. Built-in: en, es, de, fr. The German\n * bundle (etc.) is fetched on demand so unused languages cost nothing.\n */\n locale?: string;\n /**\n * Per-key string overrides layered over the active locale — white-label copy\n * without shipping a whole bundle, e.g. `{ 'map.fromPrice': 'ab {price}' }`.\n */\n messages?: Record<string, string>;\n /** ISO 4217 currency for on-map prices (default USD). */\n currency?: string;\n /**\n * Colorblind-safe rendering: category hues switch to an Okabe-Ito palette\n * and booked seats render hollow, so state never relies on hue alone.\n * Toggleable later with setColorblindSafe().\n */\n colorblindSafe?: boolean;\n onSelectionChange?: (seats: SelectedSeat[]) => void;\n onHold?: (result: HoldResult) => void;\n onHoldExpired?: () => void;\n onGAClick?: (area: GAAreaAvailability) => void;\n onError?: (err: unknown) => void;\n /**\n * Multi-floor charts only: fires when the buyer taps a deck in the stacked\n * 3D view, after the picker switches to that floor — lets the host page sync\n * its own floor UI (tabs, labels) with the map.\n */\n onDeckTap?: (floorId: string) => void;\n /**\n * Non-blocking, localized selection advice — currently the orphan-seat hint\n * (the selection would strand a single free seat between taken neighbors).\n * `null` clears it. Purely informational; nothing is ever prevented.\n */\n onHint?: (message: string | null) => void;\n}\n\nfunction resolveContainer(container: string | HTMLElement): HTMLElement {\n if (typeof container === 'string') {\n const el = document.querySelector(container);\n if (!el) throw new Error(`seatmap: container \"${container}\" not found`);\n return el as HTMLElement;\n }\n if (!(container instanceof HTMLElement)) {\n throw new Error('seatmap: container must be a CSS selector or an HTMLElement');\n }\n return container;\n}\n\nexport class SeatingChart {\n private readonly opts: SeatingChartOptions;\n private readonly controller: PickerController;\n /** Reserved for future authenticated rendering — stored, not yet sent on any request. */\n readonly publicKey?: string;\n\n private mount: HTMLElement | null = null;\n private hostEl: HTMLDivElement | null = null;\n private rendered = false;\n\n constructor(options: SeatingChartOptions) {\n if (!options || typeof options !== 'object') throw new Error('seatmap: options object is required');\n if (!options.container) throw new Error('seatmap: `container` is required');\n if (!options.event || typeof options.event !== 'string') throw new Error('seatmap: `event` key is required');\n\n this.opts = options;\n this.publicKey = options.publicKey;\n const api = new PubApi((options.apiBase ?? DEFAULT_API_BASE).replace(/\\/+$/, ''));\n this.controller = new PickerController({\n transport: api,\n eventKey: options.event,\n maxSelection: options.maxSelection ?? DEFAULT_MAX_SELECTION,\n currency: options.currency,\n onSelectionChange: (seats) => this.opts.onSelectionChange?.(seats),\n onHold: (h) => this.opts.onHold?.({ holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items }),\n onHoldExpired: () => this.opts.onHoldExpired?.(),\n onGAClick: (areaId) => {\n const area = this.controller.getGAAreas().find((candidate) => candidate.id === areaId);\n if (area) this.opts.onGAClick?.(area);\n },\n onError: (err) => this.opts.onError?.(err),\n onDeckTap: (floorId) => this.opts.onDeckTap?.(floorId),\n onHint: (message) => this.opts.onHint?.(message),\n colorblindSafe: options.colorblindSafe,\n });\n }\n\n /** Fetch the chart, mount the renderer, seed statuses and go live. Idempotent. */\n async render(): Promise<this> {\n if (this.rendered) return this;\n this.rendered = true;\n\n // Resolve + load the UI language before the first paint so on-map labels\n // (\"N LEFT\", \"FROM …\", the map aria-label) render translated. English and\n // already-loaded locales resolve synchronously; others fetch one small chunk.\n await loadLocale(this.opts.locale);\n if (this.opts.messages) setStringOverrides(this.opts.messages);\n\n // Mount an owned <div> inside the caller's container so we never fight their\n // layout and can cleanly remove it on destroy().\n this.mount = resolveContainer(this.opts.container);\n const host = document.createElement('div');\n host.style.width = '100%';\n host.style.height = '100%';\n host.style.position = 'relative';\n this.mount.appendChild(host);\n this.hostEl = host;\n\n const info = await this.controller.render(host);\n if (!info) {\n this.rendered = false;\n return this;\n }\n if (info.mode === 'test') {\n host.style.overflow = 'hidden';\n const ribbon = document.createElement('div');\n ribbon.textContent = t('picker.testMode');\n ribbon.setAttribute('aria-label', t('picker.testMode'));\n ribbon.style.cssText =\n 'position:absolute;top:18px;right:-34px;z-index:6;transform:rotate(45deg);' +\n 'width:140px;text-align:center;padding:4px 0;background:#f4b740;color:#1a1200;' +\n 'font:800 10.5px/1.4 -apple-system,BlinkMacSystemFont,sans-serif;letter-spacing:.12em;' +\n 'box-shadow:0 2px 8px rgba(0,0,0,.25);pointer-events:none;';\n host.appendChild(ribbon);\n }\n return this;\n }\n\n /** Current selection with prices resolved from the chart categories. */\n getSelection(): SelectedSeat[] {\n return this.controller.getSelection();\n }\n\n /** Hold the current selection. Resolves the hold, or null on a 409 conflict. */\n async hold(options: { ttlMs?: number } = {}): Promise<HoldResult | null> {\n try {\n const h = await this.controller.hold(undefined, options.ttlMs);\n return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : null;\n } catch (err) {\n this.opts.onError?.(err);\n return null;\n }\n }\n\n getGAAreas(): GAAreaAvailability[] {\n return this.controller.getGAAreas();\n }\n\n async holdGA(\n areaId: string,\n qty: number,\n options: { tierId?: string | null; ttlMs?: number } = {},\n ): Promise<HoldResult | null> {\n try {\n const h = await this.controller.holdGA(areaId, qty, options);\n return h ? { holdId: h.holdId, expiresAt: h.expiresAt, seats: h.seats, items: h.items } : null;\n } catch (err) {\n this.opts.onError?.(err);\n return null;\n }\n }\n\n /** Ask the server for the `qty` best free seats and hold them atomically. */\n async bestAvailable(qty: number, categoryKey?: string): Promise<BestAvailableResult | null> {\n try {\n const h = await this.controller.bestAvailable(qty, categoryKey);\n return h ? { holdId: h.holdId, expiresAt: h.expiresAt, labels: h.labels, seats: h.seats, items: h.items } : null;\n } catch (err) {\n this.opts.onError?.(err);\n return null;\n }\n }\n\n /**\n * Choose a ticket tier for a selected seat (e.g. Adult → Child). The seat's\n * available `tiers` are on each `SelectedSeat` from `getSelection()` /\n * `onSelectionChange`. Re-emits the selection with the new tier + price, and\n * the tier rides along in the next `hold()` / `onHold` per seat. `tierId=null`\n * reverts to the default tier.\n */\n setSeatTier(seatId: string, tierId: string | null): void {\n this.controller.setSeatTier(seatId, tierId);\n }\n\n /**\n * Floors of a multi-floor chart — `[{ id, name }]` (single-floor charts\n * return one entry; empty before render()). Pair with setFloor() to build a\n * host-side floor switcher.\n */\n getFloors(): { id: string; name: string }[] {\n return this.controller.getFloors();\n }\n\n /** Switch the shown floor (2D). Warns + no-ops on single-floor charts. */\n setFloor(floorId: string): void {\n if (this.controller.getFloors().length <= 1) {\n console.warn('seatmap: setFloor() ignored — this chart has a single floor');\n return;\n }\n this.controller.setFloor(floorId);\n }\n\n /** Toggle colorblind-safe rendering at runtime (see options.colorblindSafe). */\n setColorblindSafe(on: boolean): void {\n this.controller.setColorblindSafe(on);\n }\n\n /** Release the current hold (if any). No-op when nothing is held. */\n async release(): Promise<void> {\n await this.controller.release();\n }\n\n /** Tear everything down: close the socket, stop timers, drop the canvas. */\n destroy(): void {\n this.controller.destroy();\n if (this.hostEl && this.hostEl.parentNode) this.hostEl.parentNode.removeChild(this.hostEl);\n this.hostEl = null;\n this.mount = null;\n this.rendered = false;\n }\n}\n","/**\n * Minimal client for the public embed surface of workers/api (the `/pub/*`\n * routes). Deliberately self-contained — it does NOT reuse src/lib/api.ts,\n * which bakes in a build-time API base and dashboard session credentials. The\n * SDK runs cross-origin on a third-party ticketing page, so:\n * - apiBase is per-instance (constructor option), not a build constant;\n * - credentials are omitted (no cookie to send, avoids CORS-credential setup);\n * - no custom headers on mutating calls (keeps the CORS preflight trivial).\n */\nimport type { ChartDoc, PickerSeat as SelectedSeat } from '@seatlayer/core';\n\nexport interface HoldConflict {\n label: string;\n status: string;\n}\n\nexport interface HoldLineItem {\n label: string; objectId: string; objectType: 'seat' | 'booth' | 'ga'; categoryKey: string;\n tierId: string | null;\n /** Price in major currency units (for example 45 means $45.00). */\n unitPrice: number;\n currency: string;\n quantity?: number;\n}\n\nexport class ApiError extends Error {\n status: number;\n code?: string;\n /** Present when a hold 409s because seats were just taken/held. */\n conflicts?: HoldConflict[];\n /** Present when best-available 409s ('not_enough_together' | 'sold_out'). */\n reason?: string;\n\n constructor(status: number, message: string, code?: string, conflicts?: HoldConflict[], reason?: string) {\n super(message);\n this.name = 'ApiError';\n this.status = status;\n this.code = code;\n this.conflicts = conflicts;\n this.reason = reason;\n }\n}\n\nexport interface PubChartResult {\n event: { key: string; name: string };\n doc: ChartDoc;\n}\n\nexport interface PubObjectsResult {\n /** Every non-free seat's status, keyed by seat label. */\n seats: Record<string, string>;\n updatedAt: number;\n}\n\nexport interface HoldResult {\n holdId: string;\n expiresAt: number;\n /** The held seats with the buyer's chosen ticket tier per seat (present on hold). */\n seats?: SelectedSeat[];\n items?: HoldLineItem[];\n}\n\n/** Best-available response — the server-picked seats plus the hold they landed in. */\nexport interface BestAvailableResult {\n holdId: string;\n expiresAt: number;\n labels: string[];\n seats?: SelectedSeat[];\n items?: HoldResult['items'];\n}\n\nasync function request<T>(\n base: string,\n path: string,\n init: { method?: 'GET' | 'POST'; body?: unknown } = {},\n): Promise<T> {\n const method = init.method ?? 'GET';\n const headers: Record<string, string> = {};\n let body: string | undefined;\n if (init.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n body = JSON.stringify(init.body);\n }\n\n const res = await fetch(`${base}${path}`, { method, headers, body, credentials: 'omit' });\n\n const isJson = (res.headers.get('content-type') ?? '').includes('application/json');\n const data = isJson ? await res.json().catch(() => null) : null;\n\n if (!res.ok) {\n const err = data as\n | { error?: string; code?: string; conflicts?: HoldConflict[]; reason?: string }\n | null;\n throw new ApiError(res.status, err?.error ?? `request_failed_${res.status}`, err?.code, err?.conflicts, err?.reason);\n }\n return data as T;\n}\n\n/** Public-surface client bound to one apiBase (e.g. https://api.seatlayer.io). */\nexport class PubApi {\n constructor(private readonly base: string) {}\n\n chart(key: string): Promise<PubChartResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/chart`);\n }\n\n objects(key: string): Promise<PubObjectsResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/objects`);\n }\n\n hold(key: string, selections: Array<{ label: string; tierId?: string | null }>, ttlMs?: number, replaceHoldId?: string): Promise<HoldResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/hold`, {\n method: 'POST',\n body: { selections, ...(ttlMs ? { ttlMs } : {}), ...(replaceHoldId ? { replaceHoldId } : {}) },\n });\n }\n\n bestAvailable(key: string, qty: number, categoryKey?: string): Promise<BestAvailableResult> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/best-available`, {\n method: 'POST',\n body: { qty, ...(categoryKey ? { categoryKey } : {}) },\n });\n }\n\n release(key: string, labels: string[], holdId: string): Promise<{ ok: true }> {\n return request(this.base, `/pub/events/${encodeURIComponent(key)}/release`, {\n method: 'POST',\n body: { labels, holdId },\n });\n }\n\n socketUrl(key: string): string {\n const wsBase = this.base.replace(/^http/, 'ws');\n return `${wsBase}/pub/events/${encodeURIComponent(key)}/subscribe`;\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seatlayer/js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "The SeatLayer embed SDK — render an interactive seat picker and hold seats from the browser. Works in any JS framework.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://docs.seatlayer.io",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"reserved-seating"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@seatlayer/core": "^0.
|
|
38
|
+
"@seatlayer/core": "^0.2.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"tsup": "^8.5.1",
|