@seatlayer/react 0.4.0 → 0.5.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/README.md CHANGED
@@ -60,3 +60,34 @@ The browser **holds**; your **server books** with a secret key — a browser nev
60
60
  books directly. See the [integration guide](https://docs.seatlayer.io/getting-started/how-it-works/).
61
61
 
62
62
  Built on [`@seatlayer/js`](https://www.npmjs.com/package/@seatlayer/js).
63
+
64
+ ## Embed the chart Designer
65
+
66
+ `EmbeddedDesigner` gives an organizer a native-feeling Designer inside your React
67
+ application. Mint `designerUrl` from your backend — never from a browser using an
68
+ account secret key. The component verifies messages by iframe source, Designer origin,
69
+ and the chart/workspace ids you provide.
70
+
71
+ ```tsx
72
+ import { EmbeddedDesigner } from '@seatlayer/react';
73
+
74
+ export function VenueEditor({ session }: { session: {
75
+ designerUrl: string; chartId: string; workspaceId: string;
76
+ } }) {
77
+ return (
78
+ <EmbeddedDesigner
79
+ designerUrl={session.designerUrl}
80
+ expectedChartId={session.chartId}
81
+ expectedWorkspaceId={session.workspaceId}
82
+ style={{ width: '100%', height: 'calc(100vh - 96px)' }}
83
+ onSaved={({ chartId }) => refreshVenue(chartId)}
84
+ onPublished={({ chartId }) => refreshVenue(chartId)}
85
+ onClose={() => closeVenueEditor()}
86
+ onError={({ code, message }) => showError(code ?? message)}
87
+ />
88
+ );
89
+ }
90
+ ```
91
+
92
+ Changing `designerUrl` replaces the iframe, so a newly-minted fragment token never
93
+ continues an earlier session. See [the embedded Designer guide](https://docs.seatlayer.io/guides/embedded-designer/).
package/dist/index.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ EmbeddedDesigner: () => EmbeddedDesigner,
23
24
  SeatingChart: () => SeatingChart
24
25
  });
25
26
  module.exports = __toCommonJS(index_exports);
@@ -77,15 +78,65 @@ var SeatingChart = (0, import_react.forwardRef)(
77
78
  setSeatTier: (seatId, tierId) => chartRef.current?.setSeatTier(seatId, tierId),
78
79
  getFloors: () => chartRef.current?.getFloors() ?? [],
79
80
  setFloor: (floorId) => chartRef.current?.setFloor(floorId),
80
- setColorblindSafe: (on) => chartRef.current?.setColorblindSafe(on)
81
+ setColorblindSafe: (on) => chartRef.current?.setColorblindSafe(on),
82
+ zoomIn: () => chartRef.current?.zoomIn(),
83
+ zoomOut: () => chartRef.current?.zoomOut(),
84
+ zoomToFit: () => chartRef.current?.zoomToFit()
81
85
  }),
82
86
  []
83
87
  );
84
88
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: containerRef, className, style });
85
89
  }
86
90
  );
91
+
92
+ // src/EmbeddedDesigner.tsx
93
+ var import_react2 = require("react");
94
+ var import_js2 = require("@seatlayer/js");
95
+ var import_jsx_runtime2 = require("react/jsx-runtime");
96
+ var EmbeddedDesigner = (0, import_react2.forwardRef)(
97
+ function EmbeddedDesigner2(props, ref) {
98
+ const containerRef = (0, import_react2.useRef)(null);
99
+ const designerRef = (0, import_react2.useRef)(null);
100
+ const callbacks = (0, import_react2.useRef)(props);
101
+ callbacks.current = props;
102
+ (0, import_react2.useEffect)(() => {
103
+ const container = containerRef.current;
104
+ if (!container) return;
105
+ const designer = new import_js2.EmbeddedDesigner({
106
+ designerUrl: props.designerUrl,
107
+ container,
108
+ expectedChartId: props.expectedChartId,
109
+ expectedWorkspaceId: props.expectedWorkspaceId,
110
+ title: props.title,
111
+ className: props.iframeClassName,
112
+ style: props.iframeStyle,
113
+ allow: props.allow,
114
+ referrerPolicy: props.referrerPolicy,
115
+ onReady: (message) => callbacks.current.onReady?.(message),
116
+ onSaved: (message) => callbacks.current.onSaved?.(message),
117
+ onPublished: (message) => callbacks.current.onPublished?.(message),
118
+ onClose: (message) => callbacks.current.onClose?.(message),
119
+ onError: (message) => callbacks.current.onError?.(message)
120
+ });
121
+ designer.mount();
122
+ designerRef.current = designer;
123
+ return () => {
124
+ designer.destroy();
125
+ designerRef.current = null;
126
+ };
127
+ }, [props.designerUrl, props.expectedChartId, props.expectedWorkspaceId, props.title, props.iframeClassName, props.iframeStyle, props.allow, props.referrerPolicy]);
128
+ (0, import_react2.useImperativeHandle)(ref, () => ({
129
+ setDesignerUrl: (designerUrl) => {
130
+ designerRef.current?.setDesignerUrl(designerUrl);
131
+ },
132
+ getIframe: () => designerRef.current?.getIframe() ?? null
133
+ }), []);
134
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: containerRef, className: props.className, style: props.style });
135
+ }
136
+ );
87
137
  // Annotate the CommonJS export names for ESM import in node:
88
138
  0 && (module.exports = {
139
+ EmbeddedDesigner,
89
140
  SeatingChart
90
141
  });
91
142
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/SeatingChart.tsx"],"sourcesContent":["/**\n * @seatlayer/react — the React wrapper for the SeatLayer embed SDK.\n */\nexport { SeatingChart } from './SeatingChart';\nexport type {\n SeatingChartProps,\n SeatingChartHandle,\n SelectedSeat,\n HoldResult,\n BestAvailableResult,\n GAAreaAvailability,\n HoldLineItem,\n} from './SeatingChart';\n","import {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n type CSSProperties,\n} from 'react';\nimport {\n SeatingChart as CoreSeatingChart,\n type SeatingChartOptions,\n type SelectedSeat,\n type HoldResult,\n type BestAvailableResult,\n type GAAreaAvailability,\n type HoldLineItem,\n} from '@seatlayer/js';\n\nexport type { SelectedSeat, HoldResult, BestAvailableResult, GAAreaAvailability, HoldLineItem } from '@seatlayer/js';\nexport type { SeatHoverDetails } from '@seatlayer/js';\n\n/** Imperative handle exposed via `ref` — call these to drive the picker from your app. */\nexport interface SeatingChartHandle {\n /** Hold the current selection. Resolves the hold, or `null` on a 409 conflict. */\n hold(options?: { ttlMs?: number }): Promise<HoldResult | null>;\n /** GA areas with live remaining capacity. */\n getGAAreas(): GAAreaAvailability[];\n /** Atomically hold a quantity from one GA area. */\n holdGA(areaId: string, qty: number, options?: { tierId?: string | null; ttlMs?: number }): Promise<HoldResult | null>;\n /** Ask the server for the `qty` best free seats and hold them atomically. */\n bestAvailable(qty: number, categoryKey?: string): Promise<BestAvailableResult | null>;\n /** Release the current hold (if any). */\n release(): Promise<void>;\n /** The current selection, with prices resolved from the chart categories. */\n getSelection(): SelectedSeat[];\n /**\n * Choose a ticket tier for a selected seat (e.g. Adult → Child). Available\n * `tiers` are on each `SelectedSeat`; `tierId=null` reverts to the default.\n */\n setSeatTier(seatId: string, tierId: string | null): void;\n /**\n * Floors of a multi-floor chart — `[{ id, name }]` (single-floor charts\n * return one entry; empty before render()). Pair with setFloor().\n */\n getFloors(): { id: string; name: string }[];\n /** Switch the shown floor (2D). Warns + no-ops on single-floor charts. */\n setFloor(floorId: string): void;\n /** Toggle colorblind-safe rendering at runtime (see the `colorblindSafe` prop). */\n setColorblindSafe(on: boolean): void;\n}\n\nexport interface SeatingChartProps extends Omit<SeatingChartOptions, 'container'> {\n className?: string;\n style?: CSSProperties;\n}\n\n/**\n * React wrapper around the framework-agnostic `@seatlayer/js` SDK.\n *\n * The underlying canvas is created once and torn down on unmount. Callback props\n * (`onSelectionChange`, `onHold`, `onError`, `onDeckTap`, `onHint`) may change\n * freely between renders without re-mounting the canvas — only `event`,\n * `apiBase`, `maxSelection`, `publicKey`, `locale`, `currency`, and\n * `colorblindSafe` trigger a rebuild. (`messages` is read once per mount;\n * changing it alone does not re-apply.)\n */\nexport const SeatingChart = forwardRef<SeatingChartHandle, SeatingChartProps>(\n function SeatingChart(props, ref) {\n const { className, style, event, apiBase, maxSelection, publicKey, locale, currency, colorblindSafe } = props;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n const chartRef = useRef<CoreSeatingChart | null>(null);\n\n // Always call the latest callbacks without rebuilding the chart.\n const callbacks = useRef(props);\n callbacks.current = props;\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n\n const chart = new CoreSeatingChart({\n container: el,\n event,\n apiBase,\n maxSelection,\n publicKey,\n locale,\n currency,\n colorblindSafe,\n messages: callbacks.current.messages,\n onSelectionChange: (seats) => callbacks.current.onSelectionChange?.(seats),\n onHold: (result) => callbacks.current.onHold?.(result),\n onHoldExpired: () => callbacks.current.onHoldExpired?.(),\n onGAClick: (area) => callbacks.current.onGAClick?.(area),\n onError: (err) => callbacks.current.onError?.(err),\n onDeckTap: (floorId) => callbacks.current.onDeckTap?.(floorId),\n onHint: (message) => callbacks.current.onHint?.(message),\n seatTooltip: props.seatTooltip,\n onSeatHover: (details) => callbacks.current.onSeatHover?.(details),\n });\n chartRef.current = chart;\n void chart.render();\n\n return () => {\n chart.destroy();\n chartRef.current = null;\n };\n // Rebuild only when the identity of the chart changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [event, apiBase, maxSelection, publicKey, locale, currency, colorblindSafe]);\n\n useImperativeHandle(\n ref,\n (): SeatingChartHandle => ({\n hold: (options) => chartRef.current?.hold(options) ?? Promise.resolve(null),\n getGAAreas: () => chartRef.current?.getGAAreas() ?? [],\n holdGA: (areaId, qty, options) => chartRef.current?.holdGA(areaId, qty, options) ?? Promise.resolve(null),\n bestAvailable: (qty, categoryKey) =>\n chartRef.current?.bestAvailable(qty, categoryKey) ?? Promise.resolve(null),\n release: () => chartRef.current?.release() ?? Promise.resolve(),\n getSelection: () => chartRef.current?.getSelection() ?? [],\n setSeatTier: (seatId, tierId) => chartRef.current?.setSeatTier(seatId, tierId),\n getFloors: () => chartRef.current?.getFloors() ?? [],\n setFloor: (floorId) => chartRef.current?.setFloor(floorId),\n setColorblindSafe: (on) => chartRef.current?.setColorblindSafe(on),\n }),\n [],\n );\n\n return <div ref={containerRef} className={className} style={style} />;\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAMO;AACP,gBAQO;AAkHI;AAhEJ,IAAM,mBAAe;AAAA,EAC1B,SAASA,cAAa,OAAO,KAAK;AAChC,UAAM,EAAE,WAAW,OAAO,OAAO,SAAS,cAAc,WAAW,QAAQ,UAAU,eAAe,IAAI;AAExG,UAAM,mBAAe,qBAA8B,IAAI;AACvD,UAAM,eAAW,qBAAgC,IAAI;AAGrD,UAAM,gBAAY,qBAAO,KAAK;AAC9B,cAAU,UAAU;AAEpB,gCAAU,MAAM;AACd,YAAM,KAAK,aAAa;AACxB,UAAI,CAAC,GAAI;AAET,YAAM,QAAQ,IAAI,UAAAC,aAAiB;AAAA,QACjC,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU,UAAU,QAAQ;AAAA,QAC5B,mBAAmB,CAAC,UAAU,UAAU,QAAQ,oBAAoB,KAAK;AAAA,QACzE,QAAQ,CAAC,WAAW,UAAU,QAAQ,SAAS,MAAM;AAAA,QACrD,eAAe,MAAM,UAAU,QAAQ,gBAAgB;AAAA,QACvD,WAAW,CAAC,SAAS,UAAU,QAAQ,YAAY,IAAI;AAAA,QACvD,SAAS,CAAC,QAAQ,UAAU,QAAQ,UAAU,GAAG;AAAA,QACjD,WAAW,CAAC,YAAY,UAAU,QAAQ,YAAY,OAAO;AAAA,QAC7D,QAAQ,CAAC,YAAY,UAAU,QAAQ,SAAS,OAAO;AAAA,QACvD,aAAa,MAAM;AAAA,QACnB,aAAa,CAAC,YAAY,UAAU,QAAQ,cAAc,OAAO;AAAA,MACnE,CAAC;AACD,eAAS,UAAU;AACnB,WAAK,MAAM,OAAO;AAElB,aAAO,MAAM;AACX,cAAM,QAAQ;AACd,iBAAS,UAAU;AAAA,MACrB;AAAA,IAGF,GAAG,CAAC,OAAO,SAAS,cAAc,WAAW,QAAQ,UAAU,cAAc,CAAC;AAE9E;AAAA,MACE;AAAA,MACA,OAA2B;AAAA,QACzB,MAAM,CAAC,YAAY,SAAS,SAAS,KAAK,OAAO,KAAK,QAAQ,QAAQ,IAAI;AAAA,QAC1E,YAAY,MAAM,SAAS,SAAS,WAAW,KAAK,CAAC;AAAA,QACrD,QAAQ,CAAC,QAAQ,KAAK,YAAY,SAAS,SAAS,OAAO,QAAQ,KAAK,OAAO,KAAK,QAAQ,QAAQ,IAAI;AAAA,QACxG,eAAe,CAAC,KAAK,gBACnB,SAAS,SAAS,cAAc,KAAK,WAAW,KAAK,QAAQ,QAAQ,IAAI;AAAA,QAC3E,SAAS,MAAM,SAAS,SAAS,QAAQ,KAAK,QAAQ,QAAQ;AAAA,QAC9D,cAAc,MAAM,SAAS,SAAS,aAAa,KAAK,CAAC;AAAA,QACzD,aAAa,CAAC,QAAQ,WAAW,SAAS,SAAS,YAAY,QAAQ,MAAM;AAAA,QAC7E,WAAW,MAAM,SAAS,SAAS,UAAU,KAAK,CAAC;AAAA,QACnD,UAAU,CAAC,YAAY,SAAS,SAAS,SAAS,OAAO;AAAA,QACzD,mBAAmB,CAAC,OAAO,SAAS,SAAS,kBAAkB,EAAE;AAAA,MACnE;AAAA,MACA,CAAC;AAAA,IACH;AAEA,WAAO,4CAAC,SAAI,KAAK,cAAc,WAAsB,OAAc;AAAA,EACrE;AACF;","names":["SeatingChart","CoreSeatingChart"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/SeatingChart.tsx","../src/EmbeddedDesigner.tsx"],"sourcesContent":["/**\n * @seatlayer/react — the React wrapper for the SeatLayer embed SDK.\n */\nexport { SeatingChart } from './SeatingChart';\nexport { EmbeddedDesigner } from './EmbeddedDesigner';\nexport type {\n SeatingChartProps,\n SeatingChartHandle,\n SelectedSeat,\n HoldResult,\n BestAvailableResult,\n GAAreaAvailability,\n HoldLineItem,\n} from './SeatingChart';\nexport type { EmbeddedDesignerProps, EmbeddedDesignerHandle, EmbeddedDesignerMessage } from './EmbeddedDesigner';\n","import {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n type CSSProperties,\n} from 'react';\nimport {\n SeatingChart as CoreSeatingChart,\n type SeatingChartOptions,\n type SelectedSeat,\n type HoldResult,\n type BestAvailableResult,\n type GAAreaAvailability,\n type HoldLineItem,\n} from '@seatlayer/js';\n\nexport type { SelectedSeat, HoldResult, BestAvailableResult, GAAreaAvailability, HoldLineItem } from '@seatlayer/js';\nexport type { SeatHoverDetails } from '@seatlayer/js';\n\n/** Imperative handle exposed via `ref` — call these to drive the picker from your app. */\nexport interface SeatingChartHandle {\n /** Hold the current selection. Resolves the hold, or `null` on a 409 conflict. */\n hold(options?: { ttlMs?: number }): Promise<HoldResult | null>;\n /** GA areas with live remaining capacity. */\n getGAAreas(): GAAreaAvailability[];\n /** Atomically hold a quantity from one GA area. */\n holdGA(areaId: string, qty: number, options?: { tierId?: string | null; ttlMs?: number }): Promise<HoldResult | null>;\n /** Ask the server for the `qty` best free seats and hold them atomically. */\n bestAvailable(qty: number, categoryKey?: string): Promise<BestAvailableResult | null>;\n /** Release the current hold (if any). */\n release(): Promise<void>;\n /** The current selection, with prices resolved from the chart categories. */\n getSelection(): SelectedSeat[];\n /**\n * Choose a ticket tier for a selected seat (e.g. Adult → Child). Available\n * `tiers` are on each `SelectedSeat`; `tierId=null` reverts to the default.\n */\n setSeatTier(seatId: string, tierId: string | null): void;\n /**\n * Floors of a multi-floor chart — `[{ id, name }]` (single-floor charts\n * return one entry; empty before render()). Pair with setFloor().\n */\n getFloors(): { id: string; name: string }[];\n /** Switch the shown floor (2D). Warns + no-ops on single-floor charts. */\n setFloor(floorId: string): void;\n /** Toggle colorblind-safe rendering at runtime (see the `colorblindSafe` prop). */\n setColorblindSafe(on: boolean): void;\n /** Zoom in one step (same increment as the wheel/pinch gesture). */\n zoomIn(): void;\n /** Zoom out one step. */\n zoomOut(): void;\n /** Reset the camera so the whole chart fits the container. */\n zoomToFit(): void;\n}\n\nexport interface SeatingChartProps extends Omit<SeatingChartOptions, 'container'> {\n className?: string;\n style?: CSSProperties;\n}\n\n/**\n * React wrapper around the framework-agnostic `@seatlayer/js` SDK.\n *\n * The underlying canvas is created once and torn down on unmount. Callback props\n * (`onSelectionChange`, `onHold`, `onError`, `onDeckTap`, `onHint`) may change\n * freely between renders without re-mounting the canvas — only `event`,\n * `apiBase`, `maxSelection`, `publicKey`, `locale`, `currency`, and\n * `colorblindSafe` trigger a rebuild. (`messages` is read once per mount;\n * changing it alone does not re-apply.)\n */\nexport const SeatingChart = forwardRef<SeatingChartHandle, SeatingChartProps>(\n function SeatingChart(props, ref) {\n const { className, style, event, apiBase, maxSelection, publicKey, locale, currency, colorblindSafe } = props;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n const chartRef = useRef<CoreSeatingChart | null>(null);\n\n // Always call the latest callbacks without rebuilding the chart.\n const callbacks = useRef(props);\n callbacks.current = props;\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n\n const chart = new CoreSeatingChart({\n container: el,\n event,\n apiBase,\n maxSelection,\n publicKey,\n locale,\n currency,\n colorblindSafe,\n messages: callbacks.current.messages,\n onSelectionChange: (seats) => callbacks.current.onSelectionChange?.(seats),\n onHold: (result) => callbacks.current.onHold?.(result),\n onHoldExpired: () => callbacks.current.onHoldExpired?.(),\n onGAClick: (area) => callbacks.current.onGAClick?.(area),\n onError: (err) => callbacks.current.onError?.(err),\n onDeckTap: (floorId) => callbacks.current.onDeckTap?.(floorId),\n onHint: (message) => callbacks.current.onHint?.(message),\n seatTooltip: props.seatTooltip,\n onSeatHover: (details) => callbacks.current.onSeatHover?.(details),\n });\n chartRef.current = chart;\n void chart.render();\n\n return () => {\n chart.destroy();\n chartRef.current = null;\n };\n // Rebuild only when the identity of the chart changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [event, apiBase, maxSelection, publicKey, locale, currency, colorblindSafe]);\n\n useImperativeHandle(\n ref,\n (): SeatingChartHandle => ({\n hold: (options) => chartRef.current?.hold(options) ?? Promise.resolve(null),\n getGAAreas: () => chartRef.current?.getGAAreas() ?? [],\n holdGA: (areaId, qty, options) => chartRef.current?.holdGA(areaId, qty, options) ?? Promise.resolve(null),\n bestAvailable: (qty, categoryKey) =>\n chartRef.current?.bestAvailable(qty, categoryKey) ?? Promise.resolve(null),\n release: () => chartRef.current?.release() ?? Promise.resolve(),\n getSelection: () => chartRef.current?.getSelection() ?? [],\n setSeatTier: (seatId, tierId) => chartRef.current?.setSeatTier(seatId, tierId),\n getFloors: () => chartRef.current?.getFloors() ?? [],\n setFloor: (floorId) => chartRef.current?.setFloor(floorId),\n setColorblindSafe: (on) => chartRef.current?.setColorblindSafe(on),\n zoomIn: () => chartRef.current?.zoomIn(),\n zoomOut: () => chartRef.current?.zoomOut(),\n zoomToFit: () => chartRef.current?.zoomToFit(),\n }),\n [],\n );\n\n return <div ref={containerRef} className={className} style={style} />;\n },\n);\n","import {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n type CSSProperties,\n} from 'react';\nimport {\n EmbeddedDesigner as CoreEmbeddedDesigner,\n type EmbeddedDesignerMessage,\n} from '@seatlayer/js';\n\nexport type { EmbeddedDesignerMessage } from '@seatlayer/js';\n\nexport interface EmbeddedDesignerHandle {\n /** Recreate the iframe with a newly-minted session URL. */\n setDesignerUrl(designerUrl: string): void;\n /** The currently mounted iframe, if any. */\n getIframe(): HTMLIFrameElement | null;\n}\n\nexport interface EmbeddedDesignerProps {\n /** Short-lived `designerUrl` returned by your backend. Never pass an `sk_` key. */\n designerUrl: string;\n expectedChartId?: string;\n expectedWorkspaceId?: string;\n title?: string;\n className?: string;\n style?: CSSProperties;\n iframeClassName?: string;\n iframeStyle?: Partial<CSSStyleDeclaration>;\n allow?: string;\n referrerPolicy?: ReferrerPolicy;\n onReady?: (message: EmbeddedDesignerMessage) => void;\n onSaved?: (message: EmbeddedDesignerMessage) => void;\n onPublished?: (message: EmbeddedDesignerMessage) => void;\n onClose?: (message: EmbeddedDesignerMessage) => void;\n onError?: (message: EmbeddedDesignerMessage) => void;\n}\n\n/**\n * Native-feeling React host for the secure SeatLayer Designer iframe.\n * A new `designerUrl` always recreates the iframe, so a new fragment token can\n * never retain state from an earlier chart session.\n */\nexport const EmbeddedDesigner = forwardRef<EmbeddedDesignerHandle, EmbeddedDesignerProps>(\n function EmbeddedDesigner(props, ref) {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const designerRef = useRef<CoreEmbeddedDesigner | null>(null);\n const callbacks = useRef(props);\n callbacks.current = props;\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n const designer = new CoreEmbeddedDesigner({\n designerUrl: props.designerUrl,\n container,\n expectedChartId: props.expectedChartId,\n expectedWorkspaceId: props.expectedWorkspaceId,\n title: props.title,\n className: props.iframeClassName,\n style: props.iframeStyle,\n allow: props.allow,\n referrerPolicy: props.referrerPolicy,\n onReady: (message) => callbacks.current.onReady?.(message),\n onSaved: (message) => callbacks.current.onSaved?.(message),\n onPublished: (message) => callbacks.current.onPublished?.(message),\n onClose: (message) => callbacks.current.onClose?.(message),\n onError: (message) => callbacks.current.onError?.(message),\n });\n designer.mount();\n designerRef.current = designer;\n return () => {\n designer.destroy();\n designerRef.current = null;\n };\n // Session/chart identity changes must recreate the iframe. Callback changes are\n // picked up from the ref without reloading an in-progress Designer session.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [props.designerUrl, props.expectedChartId, props.expectedWorkspaceId, props.title, props.iframeClassName, props.iframeStyle, props.allow, props.referrerPolicy]);\n\n useImperativeHandle(ref, () => ({\n setDesignerUrl: (designerUrl) => { designerRef.current?.setDesignerUrl(designerUrl); },\n getIframe: () => designerRef.current?.getIframe() ?? null,\n }), []);\n\n return <div ref={containerRef} className={props.className} style={props.style} />;\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAMO;AACP,gBAQO;AA2HI;AAnEJ,IAAM,mBAAe;AAAA,EAC1B,SAASA,cAAa,OAAO,KAAK;AAChC,UAAM,EAAE,WAAW,OAAO,OAAO,SAAS,cAAc,WAAW,QAAQ,UAAU,eAAe,IAAI;AAExG,UAAM,mBAAe,qBAA8B,IAAI;AACvD,UAAM,eAAW,qBAAgC,IAAI;AAGrD,UAAM,gBAAY,qBAAO,KAAK;AAC9B,cAAU,UAAU;AAEpB,gCAAU,MAAM;AACd,YAAM,KAAK,aAAa;AACxB,UAAI,CAAC,GAAI;AAET,YAAM,QAAQ,IAAI,UAAAC,aAAiB;AAAA,QACjC,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU,UAAU,QAAQ;AAAA,QAC5B,mBAAmB,CAAC,UAAU,UAAU,QAAQ,oBAAoB,KAAK;AAAA,QACzE,QAAQ,CAAC,WAAW,UAAU,QAAQ,SAAS,MAAM;AAAA,QACrD,eAAe,MAAM,UAAU,QAAQ,gBAAgB;AAAA,QACvD,WAAW,CAAC,SAAS,UAAU,QAAQ,YAAY,IAAI;AAAA,QACvD,SAAS,CAAC,QAAQ,UAAU,QAAQ,UAAU,GAAG;AAAA,QACjD,WAAW,CAAC,YAAY,UAAU,QAAQ,YAAY,OAAO;AAAA,QAC7D,QAAQ,CAAC,YAAY,UAAU,QAAQ,SAAS,OAAO;AAAA,QACvD,aAAa,MAAM;AAAA,QACnB,aAAa,CAAC,YAAY,UAAU,QAAQ,cAAc,OAAO;AAAA,MACnE,CAAC;AACD,eAAS,UAAU;AACnB,WAAK,MAAM,OAAO;AAElB,aAAO,MAAM;AACX,cAAM,QAAQ;AACd,iBAAS,UAAU;AAAA,MACrB;AAAA,IAGF,GAAG,CAAC,OAAO,SAAS,cAAc,WAAW,QAAQ,UAAU,cAAc,CAAC;AAE9E;AAAA,MACE;AAAA,MACA,OAA2B;AAAA,QACzB,MAAM,CAAC,YAAY,SAAS,SAAS,KAAK,OAAO,KAAK,QAAQ,QAAQ,IAAI;AAAA,QAC1E,YAAY,MAAM,SAAS,SAAS,WAAW,KAAK,CAAC;AAAA,QACrD,QAAQ,CAAC,QAAQ,KAAK,YAAY,SAAS,SAAS,OAAO,QAAQ,KAAK,OAAO,KAAK,QAAQ,QAAQ,IAAI;AAAA,QACxG,eAAe,CAAC,KAAK,gBACnB,SAAS,SAAS,cAAc,KAAK,WAAW,KAAK,QAAQ,QAAQ,IAAI;AAAA,QAC3E,SAAS,MAAM,SAAS,SAAS,QAAQ,KAAK,QAAQ,QAAQ;AAAA,QAC9D,cAAc,MAAM,SAAS,SAAS,aAAa,KAAK,CAAC;AAAA,QACzD,aAAa,CAAC,QAAQ,WAAW,SAAS,SAAS,YAAY,QAAQ,MAAM;AAAA,QAC7E,WAAW,MAAM,SAAS,SAAS,UAAU,KAAK,CAAC;AAAA,QACnD,UAAU,CAAC,YAAY,SAAS,SAAS,SAAS,OAAO;AAAA,QACzD,mBAAmB,CAAC,OAAO,SAAS,SAAS,kBAAkB,EAAE;AAAA,QACjE,QAAQ,MAAM,SAAS,SAAS,OAAO;AAAA,QACvC,SAAS,MAAM,SAAS,SAAS,QAAQ;AAAA,QACzC,WAAW,MAAM,SAAS,SAAS,UAAU;AAAA,MAC/C;AAAA,MACA,CAAC;AAAA,IACH;AAEA,WAAO,4CAAC,SAAI,KAAK,cAAc,WAAsB,OAAc;AAAA,EACrE;AACF;;;AC5IA,IAAAC,gBAMO;AACP,IAAAC,aAGO;AA6EI,IAAAC,sBAAA;AA1CJ,IAAM,uBAAmB;AAAA,EAC9B,SAASC,kBAAiB,OAAO,KAAK;AACpC,UAAM,mBAAe,sBAA8B,IAAI;AACvD,UAAM,kBAAc,sBAAoC,IAAI;AAC5D,UAAM,gBAAY,sBAAO,KAAK;AAC9B,cAAU,UAAU;AAEpB,iCAAU,MAAM;AACd,YAAM,YAAY,aAAa;AAC/B,UAAI,CAAC,UAAW;AAChB,YAAM,WAAW,IAAI,WAAAC,iBAAqB;AAAA,QACxC,aAAa,MAAM;AAAA,QACnB;AAAA,QACA,iBAAiB,MAAM;AAAA,QACvB,qBAAqB,MAAM;AAAA,QAC3B,OAAO,MAAM;AAAA,QACb,WAAW,MAAM;AAAA,QACjB,OAAO,MAAM;AAAA,QACb,OAAO,MAAM;AAAA,QACb,gBAAgB,MAAM;AAAA,QACtB,SAAS,CAAC,YAAY,UAAU,QAAQ,UAAU,OAAO;AAAA,QACzD,SAAS,CAAC,YAAY,UAAU,QAAQ,UAAU,OAAO;AAAA,QACzD,aAAa,CAAC,YAAY,UAAU,QAAQ,cAAc,OAAO;AAAA,QACjE,SAAS,CAAC,YAAY,UAAU,QAAQ,UAAU,OAAO;AAAA,QACzD,SAAS,CAAC,YAAY,UAAU,QAAQ,UAAU,OAAO;AAAA,MAC3D,CAAC;AACD,eAAS,MAAM;AACf,kBAAY,UAAU;AACtB,aAAO,MAAM;AACX,iBAAS,QAAQ;AACjB,oBAAY,UAAU;AAAA,MACxB;AAAA,IAIF,GAAG,CAAC,MAAM,aAAa,MAAM,iBAAiB,MAAM,qBAAqB,MAAM,OAAO,MAAM,iBAAiB,MAAM,aAAa,MAAM,OAAO,MAAM,cAAc,CAAC;AAElK,2CAAoB,KAAK,OAAO;AAAA,MAC9B,gBAAgB,CAAC,gBAAgB;AAAE,oBAAY,SAAS,eAAe,WAAW;AAAA,MAAG;AAAA,MACrF,WAAW,MAAM,YAAY,SAAS,UAAU,KAAK;AAAA,IACvD,IAAI,CAAC,CAAC;AAEN,WAAO,6CAAC,SAAI,KAAK,cAAc,WAAW,MAAM,WAAW,OAAO,MAAM,OAAO;AAAA,EACjF;AACF;","names":["SeatingChart","CoreSeatingChart","import_react","import_js","import_jsx_runtime","EmbeddedDesigner","CoreEmbeddedDesigner"]}
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { CSSProperties } from 'react';
3
- import { SeatingChartOptions, HoldResult, GAAreaAvailability, BestAvailableResult, SelectedSeat } from '@seatlayer/js';
4
- export { BestAvailableResult, GAAreaAvailability, HoldLineItem, HoldResult, SelectedSeat } from '@seatlayer/js';
3
+ import { SeatingChartOptions, HoldResult, GAAreaAvailability, BestAvailableResult, SelectedSeat, EmbeddedDesignerMessage } from '@seatlayer/js';
4
+ export { BestAvailableResult, EmbeddedDesignerMessage, GAAreaAvailability, HoldLineItem, HoldResult, SelectedSeat } from '@seatlayer/js';
5
5
 
6
6
  /** Imperative handle exposed via `ref` — call these to drive the picker from your app. */
7
7
  interface SeatingChartHandle {
@@ -39,6 +39,12 @@ interface SeatingChartHandle {
39
39
  setFloor(floorId: string): void;
40
40
  /** Toggle colorblind-safe rendering at runtime (see the `colorblindSafe` prop). */
41
41
  setColorblindSafe(on: boolean): void;
42
+ /** Zoom in one step (same increment as the wheel/pinch gesture). */
43
+ zoomIn(): void;
44
+ /** Zoom out one step. */
45
+ zoomOut(): void;
46
+ /** Reset the camera so the whole chart fits the container. */
47
+ zoomToFit(): void;
42
48
  }
43
49
  interface SeatingChartProps extends Omit<SeatingChartOptions, 'container'> {
44
50
  className?: string;
@@ -56,4 +62,35 @@ interface SeatingChartProps extends Omit<SeatingChartOptions, 'container'> {
56
62
  */
57
63
  declare const SeatingChart: react.ForwardRefExoticComponent<SeatingChartProps & react.RefAttributes<SeatingChartHandle>>;
58
64
 
59
- export { SeatingChart, type SeatingChartHandle, type SeatingChartProps };
65
+ interface EmbeddedDesignerHandle {
66
+ /** Recreate the iframe with a newly-minted session URL. */
67
+ setDesignerUrl(designerUrl: string): void;
68
+ /** The currently mounted iframe, if any. */
69
+ getIframe(): HTMLIFrameElement | null;
70
+ }
71
+ interface EmbeddedDesignerProps {
72
+ /** Short-lived `designerUrl` returned by your backend. Never pass an `sk_` key. */
73
+ designerUrl: string;
74
+ expectedChartId?: string;
75
+ expectedWorkspaceId?: string;
76
+ title?: string;
77
+ className?: string;
78
+ style?: CSSProperties;
79
+ iframeClassName?: string;
80
+ iframeStyle?: Partial<CSSStyleDeclaration>;
81
+ allow?: string;
82
+ referrerPolicy?: ReferrerPolicy;
83
+ onReady?: (message: EmbeddedDesignerMessage) => void;
84
+ onSaved?: (message: EmbeddedDesignerMessage) => void;
85
+ onPublished?: (message: EmbeddedDesignerMessage) => void;
86
+ onClose?: (message: EmbeddedDesignerMessage) => void;
87
+ onError?: (message: EmbeddedDesignerMessage) => void;
88
+ }
89
+ /**
90
+ * Native-feeling React host for the secure SeatLayer Designer iframe.
91
+ * A new `designerUrl` always recreates the iframe, so a new fragment token can
92
+ * never retain state from an earlier chart session.
93
+ */
94
+ declare const EmbeddedDesigner: react.ForwardRefExoticComponent<EmbeddedDesignerProps & react.RefAttributes<EmbeddedDesignerHandle>>;
95
+
96
+ export { EmbeddedDesigner, type EmbeddedDesignerHandle, type EmbeddedDesignerProps, SeatingChart, type SeatingChartHandle, type SeatingChartProps };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { CSSProperties } from 'react';
3
- import { SeatingChartOptions, HoldResult, GAAreaAvailability, BestAvailableResult, SelectedSeat } from '@seatlayer/js';
4
- export { BestAvailableResult, GAAreaAvailability, HoldLineItem, HoldResult, SelectedSeat } from '@seatlayer/js';
3
+ import { SeatingChartOptions, HoldResult, GAAreaAvailability, BestAvailableResult, SelectedSeat, EmbeddedDesignerMessage } from '@seatlayer/js';
4
+ export { BestAvailableResult, EmbeddedDesignerMessage, GAAreaAvailability, HoldLineItem, HoldResult, SelectedSeat } from '@seatlayer/js';
5
5
 
6
6
  /** Imperative handle exposed via `ref` — call these to drive the picker from your app. */
7
7
  interface SeatingChartHandle {
@@ -39,6 +39,12 @@ interface SeatingChartHandle {
39
39
  setFloor(floorId: string): void;
40
40
  /** Toggle colorblind-safe rendering at runtime (see the `colorblindSafe` prop). */
41
41
  setColorblindSafe(on: boolean): void;
42
+ /** Zoom in one step (same increment as the wheel/pinch gesture). */
43
+ zoomIn(): void;
44
+ /** Zoom out one step. */
45
+ zoomOut(): void;
46
+ /** Reset the camera so the whole chart fits the container. */
47
+ zoomToFit(): void;
42
48
  }
43
49
  interface SeatingChartProps extends Omit<SeatingChartOptions, 'container'> {
44
50
  className?: string;
@@ -56,4 +62,35 @@ interface SeatingChartProps extends Omit<SeatingChartOptions, 'container'> {
56
62
  */
57
63
  declare const SeatingChart: react.ForwardRefExoticComponent<SeatingChartProps & react.RefAttributes<SeatingChartHandle>>;
58
64
 
59
- export { SeatingChart, type SeatingChartHandle, type SeatingChartProps };
65
+ interface EmbeddedDesignerHandle {
66
+ /** Recreate the iframe with a newly-minted session URL. */
67
+ setDesignerUrl(designerUrl: string): void;
68
+ /** The currently mounted iframe, if any. */
69
+ getIframe(): HTMLIFrameElement | null;
70
+ }
71
+ interface EmbeddedDesignerProps {
72
+ /** Short-lived `designerUrl` returned by your backend. Never pass an `sk_` key. */
73
+ designerUrl: string;
74
+ expectedChartId?: string;
75
+ expectedWorkspaceId?: string;
76
+ title?: string;
77
+ className?: string;
78
+ style?: CSSProperties;
79
+ iframeClassName?: string;
80
+ iframeStyle?: Partial<CSSStyleDeclaration>;
81
+ allow?: string;
82
+ referrerPolicy?: ReferrerPolicy;
83
+ onReady?: (message: EmbeddedDesignerMessage) => void;
84
+ onSaved?: (message: EmbeddedDesignerMessage) => void;
85
+ onPublished?: (message: EmbeddedDesignerMessage) => void;
86
+ onClose?: (message: EmbeddedDesignerMessage) => void;
87
+ onError?: (message: EmbeddedDesignerMessage) => void;
88
+ }
89
+ /**
90
+ * Native-feeling React host for the secure SeatLayer Designer iframe.
91
+ * A new `designerUrl` always recreates the iframe, so a new fragment token can
92
+ * never retain state from an earlier chart session.
93
+ */
94
+ declare const EmbeddedDesigner: react.ForwardRefExoticComponent<EmbeddedDesignerProps & react.RefAttributes<EmbeddedDesignerHandle>>;
95
+
96
+ export { EmbeddedDesigner, type EmbeddedDesignerHandle, type EmbeddedDesignerProps, SeatingChart, type SeatingChartHandle, type SeatingChartProps };
package/dist/index.js CHANGED
@@ -58,14 +58,71 @@ var SeatingChart = forwardRef(
58
58
  setSeatTier: (seatId, tierId) => chartRef.current?.setSeatTier(seatId, tierId),
59
59
  getFloors: () => chartRef.current?.getFloors() ?? [],
60
60
  setFloor: (floorId) => chartRef.current?.setFloor(floorId),
61
- setColorblindSafe: (on) => chartRef.current?.setColorblindSafe(on)
61
+ setColorblindSafe: (on) => chartRef.current?.setColorblindSafe(on),
62
+ zoomIn: () => chartRef.current?.zoomIn(),
63
+ zoomOut: () => chartRef.current?.zoomOut(),
64
+ zoomToFit: () => chartRef.current?.zoomToFit()
62
65
  }),
63
66
  []
64
67
  );
65
68
  return /* @__PURE__ */ jsx("div", { ref: containerRef, className, style });
66
69
  }
67
70
  );
71
+
72
+ // src/EmbeddedDesigner.tsx
73
+ import {
74
+ forwardRef as forwardRef2,
75
+ useEffect as useEffect2,
76
+ useImperativeHandle as useImperativeHandle2,
77
+ useRef as useRef2
78
+ } from "react";
79
+ import {
80
+ EmbeddedDesigner as CoreEmbeddedDesigner
81
+ } from "@seatlayer/js";
82
+ import { jsx as jsx2 } from "react/jsx-runtime";
83
+ var EmbeddedDesigner = forwardRef2(
84
+ function EmbeddedDesigner2(props, ref) {
85
+ const containerRef = useRef2(null);
86
+ const designerRef = useRef2(null);
87
+ const callbacks = useRef2(props);
88
+ callbacks.current = props;
89
+ useEffect2(() => {
90
+ const container = containerRef.current;
91
+ if (!container) return;
92
+ const designer = new CoreEmbeddedDesigner({
93
+ designerUrl: props.designerUrl,
94
+ container,
95
+ expectedChartId: props.expectedChartId,
96
+ expectedWorkspaceId: props.expectedWorkspaceId,
97
+ title: props.title,
98
+ className: props.iframeClassName,
99
+ style: props.iframeStyle,
100
+ allow: props.allow,
101
+ referrerPolicy: props.referrerPolicy,
102
+ onReady: (message) => callbacks.current.onReady?.(message),
103
+ onSaved: (message) => callbacks.current.onSaved?.(message),
104
+ onPublished: (message) => callbacks.current.onPublished?.(message),
105
+ onClose: (message) => callbacks.current.onClose?.(message),
106
+ onError: (message) => callbacks.current.onError?.(message)
107
+ });
108
+ designer.mount();
109
+ designerRef.current = designer;
110
+ return () => {
111
+ designer.destroy();
112
+ designerRef.current = null;
113
+ };
114
+ }, [props.designerUrl, props.expectedChartId, props.expectedWorkspaceId, props.title, props.iframeClassName, props.iframeStyle, props.allow, props.referrerPolicy]);
115
+ useImperativeHandle2(ref, () => ({
116
+ setDesignerUrl: (designerUrl) => {
117
+ designerRef.current?.setDesignerUrl(designerUrl);
118
+ },
119
+ getIframe: () => designerRef.current?.getIframe() ?? null
120
+ }), []);
121
+ return /* @__PURE__ */ jsx2("div", { ref: containerRef, className: props.className, style: props.style });
122
+ }
123
+ );
68
124
  export {
125
+ EmbeddedDesigner,
69
126
  SeatingChart
70
127
  };
71
128
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/SeatingChart.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n type CSSProperties,\n} from 'react';\nimport {\n SeatingChart as CoreSeatingChart,\n type SeatingChartOptions,\n type SelectedSeat,\n type HoldResult,\n type BestAvailableResult,\n type GAAreaAvailability,\n type HoldLineItem,\n} from '@seatlayer/js';\n\nexport type { SelectedSeat, HoldResult, BestAvailableResult, GAAreaAvailability, HoldLineItem } from '@seatlayer/js';\nexport type { SeatHoverDetails } from '@seatlayer/js';\n\n/** Imperative handle exposed via `ref` — call these to drive the picker from your app. */\nexport interface SeatingChartHandle {\n /** Hold the current selection. Resolves the hold, or `null` on a 409 conflict. */\n hold(options?: { ttlMs?: number }): Promise<HoldResult | null>;\n /** GA areas with live remaining capacity. */\n getGAAreas(): GAAreaAvailability[];\n /** Atomically hold a quantity from one GA area. */\n holdGA(areaId: string, qty: number, options?: { tierId?: string | null; ttlMs?: number }): Promise<HoldResult | null>;\n /** Ask the server for the `qty` best free seats and hold them atomically. */\n bestAvailable(qty: number, categoryKey?: string): Promise<BestAvailableResult | null>;\n /** Release the current hold (if any). */\n release(): Promise<void>;\n /** The current selection, with prices resolved from the chart categories. */\n getSelection(): SelectedSeat[];\n /**\n * Choose a ticket tier for a selected seat (e.g. Adult → Child). Available\n * `tiers` are on each `SelectedSeat`; `tierId=null` reverts to the default.\n */\n setSeatTier(seatId: string, tierId: string | null): void;\n /**\n * Floors of a multi-floor chart — `[{ id, name }]` (single-floor charts\n * return one entry; empty before render()). Pair with setFloor().\n */\n getFloors(): { id: string; name: string }[];\n /** Switch the shown floor (2D). Warns + no-ops on single-floor charts. */\n setFloor(floorId: string): void;\n /** Toggle colorblind-safe rendering at runtime (see the `colorblindSafe` prop). */\n setColorblindSafe(on: boolean): void;\n}\n\nexport interface SeatingChartProps extends Omit<SeatingChartOptions, 'container'> {\n className?: string;\n style?: CSSProperties;\n}\n\n/**\n * React wrapper around the framework-agnostic `@seatlayer/js` SDK.\n *\n * The underlying canvas is created once and torn down on unmount. Callback props\n * (`onSelectionChange`, `onHold`, `onError`, `onDeckTap`, `onHint`) may change\n * freely between renders without re-mounting the canvas — only `event`,\n * `apiBase`, `maxSelection`, `publicKey`, `locale`, `currency`, and\n * `colorblindSafe` trigger a rebuild. (`messages` is read once per mount;\n * changing it alone does not re-apply.)\n */\nexport const SeatingChart = forwardRef<SeatingChartHandle, SeatingChartProps>(\n function SeatingChart(props, ref) {\n const { className, style, event, apiBase, maxSelection, publicKey, locale, currency, colorblindSafe } = props;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n const chartRef = useRef<CoreSeatingChart | null>(null);\n\n // Always call the latest callbacks without rebuilding the chart.\n const callbacks = useRef(props);\n callbacks.current = props;\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n\n const chart = new CoreSeatingChart({\n container: el,\n event,\n apiBase,\n maxSelection,\n publicKey,\n locale,\n currency,\n colorblindSafe,\n messages: callbacks.current.messages,\n onSelectionChange: (seats) => callbacks.current.onSelectionChange?.(seats),\n onHold: (result) => callbacks.current.onHold?.(result),\n onHoldExpired: () => callbacks.current.onHoldExpired?.(),\n onGAClick: (area) => callbacks.current.onGAClick?.(area),\n onError: (err) => callbacks.current.onError?.(err),\n onDeckTap: (floorId) => callbacks.current.onDeckTap?.(floorId),\n onHint: (message) => callbacks.current.onHint?.(message),\n seatTooltip: props.seatTooltip,\n onSeatHover: (details) => callbacks.current.onSeatHover?.(details),\n });\n chartRef.current = chart;\n void chart.render();\n\n return () => {\n chart.destroy();\n chartRef.current = null;\n };\n // Rebuild only when the identity of the chart changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [event, apiBase, maxSelection, publicKey, locale, currency, colorblindSafe]);\n\n useImperativeHandle(\n ref,\n (): SeatingChartHandle => ({\n hold: (options) => chartRef.current?.hold(options) ?? Promise.resolve(null),\n getGAAreas: () => chartRef.current?.getGAAreas() ?? [],\n holdGA: (areaId, qty, options) => chartRef.current?.holdGA(areaId, qty, options) ?? Promise.resolve(null),\n bestAvailable: (qty, categoryKey) =>\n chartRef.current?.bestAvailable(qty, categoryKey) ?? Promise.resolve(null),\n release: () => chartRef.current?.release() ?? Promise.resolve(),\n getSelection: () => chartRef.current?.getSelection() ?? [],\n setSeatTier: (seatId, tierId) => chartRef.current?.setSeatTier(seatId, tierId),\n getFloors: () => chartRef.current?.getFloors() ?? [],\n setFloor: (floorId) => chartRef.current?.setFloor(floorId),\n setColorblindSafe: (on) => chartRef.current?.setColorblindSafe(on),\n }),\n [],\n );\n\n return <div ref={containerRef} className={className} style={style} />;\n },\n);\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP;AAAA,EACE,gBAAgB;AAAA,OAOX;AAkHI;AAhEJ,IAAM,eAAe;AAAA,EAC1B,SAASA,cAAa,OAAO,KAAK;AAChC,UAAM,EAAE,WAAW,OAAO,OAAO,SAAS,cAAc,WAAW,QAAQ,UAAU,eAAe,IAAI;AAExG,UAAM,eAAe,OAA8B,IAAI;AACvD,UAAM,WAAW,OAAgC,IAAI;AAGrD,UAAM,YAAY,OAAO,KAAK;AAC9B,cAAU,UAAU;AAEpB,cAAU,MAAM;AACd,YAAM,KAAK,aAAa;AACxB,UAAI,CAAC,GAAI;AAET,YAAM,QAAQ,IAAI,iBAAiB;AAAA,QACjC,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU,UAAU,QAAQ;AAAA,QAC5B,mBAAmB,CAAC,UAAU,UAAU,QAAQ,oBAAoB,KAAK;AAAA,QACzE,QAAQ,CAAC,WAAW,UAAU,QAAQ,SAAS,MAAM;AAAA,QACrD,eAAe,MAAM,UAAU,QAAQ,gBAAgB;AAAA,QACvD,WAAW,CAAC,SAAS,UAAU,QAAQ,YAAY,IAAI;AAAA,QACvD,SAAS,CAAC,QAAQ,UAAU,QAAQ,UAAU,GAAG;AAAA,QACjD,WAAW,CAAC,YAAY,UAAU,QAAQ,YAAY,OAAO;AAAA,QAC7D,QAAQ,CAAC,YAAY,UAAU,QAAQ,SAAS,OAAO;AAAA,QACvD,aAAa,MAAM;AAAA,QACnB,aAAa,CAAC,YAAY,UAAU,QAAQ,cAAc,OAAO;AAAA,MACnE,CAAC;AACD,eAAS,UAAU;AACnB,WAAK,MAAM,OAAO;AAElB,aAAO,MAAM;AACX,cAAM,QAAQ;AACd,iBAAS,UAAU;AAAA,MACrB;AAAA,IAGF,GAAG,CAAC,OAAO,SAAS,cAAc,WAAW,QAAQ,UAAU,cAAc,CAAC;AAE9E;AAAA,MACE;AAAA,MACA,OAA2B;AAAA,QACzB,MAAM,CAAC,YAAY,SAAS,SAAS,KAAK,OAAO,KAAK,QAAQ,QAAQ,IAAI;AAAA,QAC1E,YAAY,MAAM,SAAS,SAAS,WAAW,KAAK,CAAC;AAAA,QACrD,QAAQ,CAAC,QAAQ,KAAK,YAAY,SAAS,SAAS,OAAO,QAAQ,KAAK,OAAO,KAAK,QAAQ,QAAQ,IAAI;AAAA,QACxG,eAAe,CAAC,KAAK,gBACnB,SAAS,SAAS,cAAc,KAAK,WAAW,KAAK,QAAQ,QAAQ,IAAI;AAAA,QAC3E,SAAS,MAAM,SAAS,SAAS,QAAQ,KAAK,QAAQ,QAAQ;AAAA,QAC9D,cAAc,MAAM,SAAS,SAAS,aAAa,KAAK,CAAC;AAAA,QACzD,aAAa,CAAC,QAAQ,WAAW,SAAS,SAAS,YAAY,QAAQ,MAAM;AAAA,QAC7E,WAAW,MAAM,SAAS,SAAS,UAAU,KAAK,CAAC;AAAA,QACnD,UAAU,CAAC,YAAY,SAAS,SAAS,SAAS,OAAO;AAAA,QACzD,mBAAmB,CAAC,OAAO,SAAS,SAAS,kBAAkB,EAAE;AAAA,MACnE;AAAA,MACA,CAAC;AAAA,IACH;AAEA,WAAO,oBAAC,SAAI,KAAK,cAAc,WAAsB,OAAc;AAAA,EACrE;AACF;","names":["SeatingChart"]}
1
+ {"version":3,"sources":["../src/SeatingChart.tsx","../src/EmbeddedDesigner.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n type CSSProperties,\n} from 'react';\nimport {\n SeatingChart as CoreSeatingChart,\n type SeatingChartOptions,\n type SelectedSeat,\n type HoldResult,\n type BestAvailableResult,\n type GAAreaAvailability,\n type HoldLineItem,\n} from '@seatlayer/js';\n\nexport type { SelectedSeat, HoldResult, BestAvailableResult, GAAreaAvailability, HoldLineItem } from '@seatlayer/js';\nexport type { SeatHoverDetails } from '@seatlayer/js';\n\n/** Imperative handle exposed via `ref` — call these to drive the picker from your app. */\nexport interface SeatingChartHandle {\n /** Hold the current selection. Resolves the hold, or `null` on a 409 conflict. */\n hold(options?: { ttlMs?: number }): Promise<HoldResult | null>;\n /** GA areas with live remaining capacity. */\n getGAAreas(): GAAreaAvailability[];\n /** Atomically hold a quantity from one GA area. */\n holdGA(areaId: string, qty: number, options?: { tierId?: string | null; ttlMs?: number }): Promise<HoldResult | null>;\n /** Ask the server for the `qty` best free seats and hold them atomically. */\n bestAvailable(qty: number, categoryKey?: string): Promise<BestAvailableResult | null>;\n /** Release the current hold (if any). */\n release(): Promise<void>;\n /** The current selection, with prices resolved from the chart categories. */\n getSelection(): SelectedSeat[];\n /**\n * Choose a ticket tier for a selected seat (e.g. Adult → Child). Available\n * `tiers` are on each `SelectedSeat`; `tierId=null` reverts to the default.\n */\n setSeatTier(seatId: string, tierId: string | null): void;\n /**\n * Floors of a multi-floor chart — `[{ id, name }]` (single-floor charts\n * return one entry; empty before render()). Pair with setFloor().\n */\n getFloors(): { id: string; name: string }[];\n /** Switch the shown floor (2D). Warns + no-ops on single-floor charts. */\n setFloor(floorId: string): void;\n /** Toggle colorblind-safe rendering at runtime (see the `colorblindSafe` prop). */\n setColorblindSafe(on: boolean): void;\n /** Zoom in one step (same increment as the wheel/pinch gesture). */\n zoomIn(): void;\n /** Zoom out one step. */\n zoomOut(): void;\n /** Reset the camera so the whole chart fits the container. */\n zoomToFit(): void;\n}\n\nexport interface SeatingChartProps extends Omit<SeatingChartOptions, 'container'> {\n className?: string;\n style?: CSSProperties;\n}\n\n/**\n * React wrapper around the framework-agnostic `@seatlayer/js` SDK.\n *\n * The underlying canvas is created once and torn down on unmount. Callback props\n * (`onSelectionChange`, `onHold`, `onError`, `onDeckTap`, `onHint`) may change\n * freely between renders without re-mounting the canvas — only `event`,\n * `apiBase`, `maxSelection`, `publicKey`, `locale`, `currency`, and\n * `colorblindSafe` trigger a rebuild. (`messages` is read once per mount;\n * changing it alone does not re-apply.)\n */\nexport const SeatingChart = forwardRef<SeatingChartHandle, SeatingChartProps>(\n function SeatingChart(props, ref) {\n const { className, style, event, apiBase, maxSelection, publicKey, locale, currency, colorblindSafe } = props;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n const chartRef = useRef<CoreSeatingChart | null>(null);\n\n // Always call the latest callbacks without rebuilding the chart.\n const callbacks = useRef(props);\n callbacks.current = props;\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n\n const chart = new CoreSeatingChart({\n container: el,\n event,\n apiBase,\n maxSelection,\n publicKey,\n locale,\n currency,\n colorblindSafe,\n messages: callbacks.current.messages,\n onSelectionChange: (seats) => callbacks.current.onSelectionChange?.(seats),\n onHold: (result) => callbacks.current.onHold?.(result),\n onHoldExpired: () => callbacks.current.onHoldExpired?.(),\n onGAClick: (area) => callbacks.current.onGAClick?.(area),\n onError: (err) => callbacks.current.onError?.(err),\n onDeckTap: (floorId) => callbacks.current.onDeckTap?.(floorId),\n onHint: (message) => callbacks.current.onHint?.(message),\n seatTooltip: props.seatTooltip,\n onSeatHover: (details) => callbacks.current.onSeatHover?.(details),\n });\n chartRef.current = chart;\n void chart.render();\n\n return () => {\n chart.destroy();\n chartRef.current = null;\n };\n // Rebuild only when the identity of the chart changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [event, apiBase, maxSelection, publicKey, locale, currency, colorblindSafe]);\n\n useImperativeHandle(\n ref,\n (): SeatingChartHandle => ({\n hold: (options) => chartRef.current?.hold(options) ?? Promise.resolve(null),\n getGAAreas: () => chartRef.current?.getGAAreas() ?? [],\n holdGA: (areaId, qty, options) => chartRef.current?.holdGA(areaId, qty, options) ?? Promise.resolve(null),\n bestAvailable: (qty, categoryKey) =>\n chartRef.current?.bestAvailable(qty, categoryKey) ?? Promise.resolve(null),\n release: () => chartRef.current?.release() ?? Promise.resolve(),\n getSelection: () => chartRef.current?.getSelection() ?? [],\n setSeatTier: (seatId, tierId) => chartRef.current?.setSeatTier(seatId, tierId),\n getFloors: () => chartRef.current?.getFloors() ?? [],\n setFloor: (floorId) => chartRef.current?.setFloor(floorId),\n setColorblindSafe: (on) => chartRef.current?.setColorblindSafe(on),\n zoomIn: () => chartRef.current?.zoomIn(),\n zoomOut: () => chartRef.current?.zoomOut(),\n zoomToFit: () => chartRef.current?.zoomToFit(),\n }),\n [],\n );\n\n return <div ref={containerRef} className={className} style={style} />;\n },\n);\n","import {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n type CSSProperties,\n} from 'react';\nimport {\n EmbeddedDesigner as CoreEmbeddedDesigner,\n type EmbeddedDesignerMessage,\n} from '@seatlayer/js';\n\nexport type { EmbeddedDesignerMessage } from '@seatlayer/js';\n\nexport interface EmbeddedDesignerHandle {\n /** Recreate the iframe with a newly-minted session URL. */\n setDesignerUrl(designerUrl: string): void;\n /** The currently mounted iframe, if any. */\n getIframe(): HTMLIFrameElement | null;\n}\n\nexport interface EmbeddedDesignerProps {\n /** Short-lived `designerUrl` returned by your backend. Never pass an `sk_` key. */\n designerUrl: string;\n expectedChartId?: string;\n expectedWorkspaceId?: string;\n title?: string;\n className?: string;\n style?: CSSProperties;\n iframeClassName?: string;\n iframeStyle?: Partial<CSSStyleDeclaration>;\n allow?: string;\n referrerPolicy?: ReferrerPolicy;\n onReady?: (message: EmbeddedDesignerMessage) => void;\n onSaved?: (message: EmbeddedDesignerMessage) => void;\n onPublished?: (message: EmbeddedDesignerMessage) => void;\n onClose?: (message: EmbeddedDesignerMessage) => void;\n onError?: (message: EmbeddedDesignerMessage) => void;\n}\n\n/**\n * Native-feeling React host for the secure SeatLayer Designer iframe.\n * A new `designerUrl` always recreates the iframe, so a new fragment token can\n * never retain state from an earlier chart session.\n */\nexport const EmbeddedDesigner = forwardRef<EmbeddedDesignerHandle, EmbeddedDesignerProps>(\n function EmbeddedDesigner(props, ref) {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const designerRef = useRef<CoreEmbeddedDesigner | null>(null);\n const callbacks = useRef(props);\n callbacks.current = props;\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n const designer = new CoreEmbeddedDesigner({\n designerUrl: props.designerUrl,\n container,\n expectedChartId: props.expectedChartId,\n expectedWorkspaceId: props.expectedWorkspaceId,\n title: props.title,\n className: props.iframeClassName,\n style: props.iframeStyle,\n allow: props.allow,\n referrerPolicy: props.referrerPolicy,\n onReady: (message) => callbacks.current.onReady?.(message),\n onSaved: (message) => callbacks.current.onSaved?.(message),\n onPublished: (message) => callbacks.current.onPublished?.(message),\n onClose: (message) => callbacks.current.onClose?.(message),\n onError: (message) => callbacks.current.onError?.(message),\n });\n designer.mount();\n designerRef.current = designer;\n return () => {\n designer.destroy();\n designerRef.current = null;\n };\n // Session/chart identity changes must recreate the iframe. Callback changes are\n // picked up from the ref without reloading an in-progress Designer session.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [props.designerUrl, props.expectedChartId, props.expectedWorkspaceId, props.title, props.iframeClassName, props.iframeStyle, props.allow, props.referrerPolicy]);\n\n useImperativeHandle(ref, () => ({\n setDesignerUrl: (designerUrl) => { designerRef.current?.setDesignerUrl(designerUrl); },\n getIframe: () => designerRef.current?.getIframe() ?? null,\n }), []);\n\n return <div ref={containerRef} className={props.className} style={props.style} />;\n },\n);\n"],"mappings":"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","names":["SeatingChart","forwardRef","useEffect","useImperativeHandle","useRef","jsx","EmbeddedDesigner"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seatlayer/react",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "React component for the SeatLayer embed SDK — render an interactive seat picker and hold seats.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://docs.seatlayer.io",
@@ -35,7 +35,7 @@
35
35
  "ticketing"
36
36
  ],
37
37
  "dependencies": {
38
- "@seatlayer/js": "^0.4.0"
38
+ "@seatlayer/js": "^0.5.0"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "react": ">=17.0.0"