@seatsyoo/react 1.0.0 → 1.1.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
@@ -220,8 +220,42 @@ En Next.js (App Router), úsalo dentro de un componente con `"use client"`.
220
220
  No va en el paquete: el visor la descarga de `${server}/sdk/butacas-3d.js` cuando alguien la abre. Si tu web
221
221
  tiene una política de seguridad de contenidos (CSP), añade esa dirección a `script-src`, o pasa `view3d={false}`.
222
222
 
223
- La referencia completa de opciones, avisos y métodos está en la documentación de Seatsyoo (**/docs/visor** y
224
- **/docs/frameworks**).
223
+ ## El diseñador de planos
224
+
225
+ `<SeatsyooDesigner>` abre en tu web el diseñador de planos de Seatsyoo, para que tus clientes dibujen sus recintos.
226
+ Tu servidor abre una sesión para un plano con su clave secreta (`POST /api/v1/charts/{plano}/designer-sessions`, o
227
+ `charts.designerSession` de `@seatsyoo/node`) y le da el token a tu web:
228
+
229
+ ```tsx
230
+ import { SeatsyooDesigner } from "@seatsyoo/react";
231
+
232
+ // Tu servidor abre la sesión y te da el token (aquí, una ruta tuya).
233
+ const pedirToken = (): Promise<string> =>
234
+ fetch("/mi-api/disenador/teatro", { method: "POST" }).then((r) => r.json()).then((s: { token: string }) => s.token);
235
+
236
+ function Disenador({ token, cerrar }: { token: string; cerrar: () => void }) {
237
+ return (
238
+ <SeatsyooDesigner
239
+ server="https://seatsyoo.example"
240
+ token={token}
241
+ refreshToken={pedirToken}
242
+ style={{ height: "80vh" }}
243
+ onPublish={({ version }) => console.log("Publicada la versión", version)}
244
+ onClose={cerrar}
245
+ />
246
+ );
247
+ }
248
+ ```
249
+
250
+ Props: `server`, `token`, `language`, `refreshToken` (pide a tu servidor otro token del mismo plano antes de que
251
+ caduque: sin ella, al caducar deja de guardar), y los avisos `onReady`, `onSave`, `onPublish`, `onClose` (sin ella no
252
+ sale «←») y `onError`; más `className`, `style` e `id` del `<div>`, que necesita alto. Un `token` nuevo se le manda sin
253
+ volver a montarlo; cambiar `server` o `language`, o pasar o quitar `refreshToken` u `onClose`, lo vuelve a montar. Con
254
+ `ref` (`useRef<EmbeddedDesigner>(null)`) tienes el diseñador: `setToken()`, `iframe`. En el servidor (SSR) pinta solo el
255
+ `<div>`.
256
+
257
+ La referencia completa de opciones, avisos y métodos está en la documentación de Seatsyoo (**/docs/visor**,
258
+ **/docs/disenador** y **/docs/frameworks**).
225
259
 
226
260
  ## Licencia
227
261
 
@@ -256,6 +290,9 @@ const chart = useRef<SeatingChart>(null);
256
290
  // At checkout, send only the event and chart.current.holdToken to your server (never the selected keys).
257
291
  ```
258
292
 
293
+ - **Embedded designer**: `<SeatsyooDesigner server token refreshToken onPublish onClose style={{ height: "80vh" }} />`
294
+ opens the Seatsyoo chart designer in your website, with a session token your server opens with its secret key
295
+ (`POST /api/v1/charts/{chart}/designer-sessions`).
259
296
  - **Props**: every viewer option except `container`, plus `className`, `style` and `id` for the wrapping `<div>`.
260
297
  - **Re-mounting**: changing `server`, `publicKey`, `event`, `link`, `document`, `season`, `seasonEvents`,
261
298
  `channels`, `pricing`, `currency`, `locale`, `language`, `maxSelected`, `session`, `holdToken`, `mode`,
@@ -0,0 +1,33 @@
1
+ import { type EmbeddedDesigner, type EmbeddedDesignerOptions } from "@seatsyoo/visor";
2
+ import { type CSSProperties, type ForwardRefExoticComponent, type RefAttributes } from "react";
3
+ /** Como en <SeatsyooChart>: las opciones opcionales admiten también `undefined`, que es lo mismo que no pasarlas. */
4
+ type AdmitenUndefined<T> = {
5
+ [K in keyof T]: {} extends Pick<T, K> ? T[K] | undefined : T[K];
6
+ };
7
+ /** Props de <SeatsyooDesigner>: las opciones del diseñador (sin `container`: es el propio componente) y las del <div>. */
8
+ export interface SeatsyooDesignerProps extends AdmitenUndefined<Omit<EmbeddedDesignerOptions, "container">> {
9
+ /** Clase del <div> que contiene el diseñador. Ocupa todo su tamaño: dale altura. */
10
+ className?: string | undefined;
11
+ /** Estilo del <div> que contiene el diseñador (p. ej. `{ height: "80vh" }`). */
12
+ style?: CSSProperties | undefined;
13
+ id?: string | undefined;
14
+ }
15
+ /**
16
+ * El diseñador de planos de Seatsyoo en tu web, con el token de una sesión que abre tu servidor con su clave secreta
17
+ * (`POST /api/v1/charts/{plano}/designer-sessions`, o `charts.designerSession` de `@seatsyoo/node`).
18
+ *
19
+ * ```tsx
20
+ * <SeatsyooDesigner
21
+ * server="https://seatsyoo.example"
22
+ * token={token}
23
+ * refreshToken={pedirToken}
24
+ * style={{ height: "80vh" }}
25
+ * onPublish={({ version }) => console.log("Publicada la versión", version)}
26
+ * onClose={() => setAbierto(false)}
27
+ * />
28
+ * ```
29
+ *
30
+ * Con `ref` (`useRef<EmbeddedDesigner>(null)`) tienes el diseñador abierto: `setToken()`, `iframe`…
31
+ */
32
+ export declare const SeatsyooDesigner: ForwardRefExoticComponent<SeatsyooDesignerProps & RefAttributes<EmbeddedDesigner>>;
33
+ export {};
@@ -0,0 +1,132 @@
1
+ // <SeatsyooDesigner>: el diseñador de planos de Seatsyoo, incrustado en tu web, como componente de React (18 y 19).
2
+ //
3
+ // - Se monta en un efecto: en el servidor (SSR) solo se pinta el <div> vacío, y con StrictMode (que monta, desmonta y
4
+ // vuelve a montar) el primero se destruye antes de abrir nada: queda uno. Al desmontar, destroy().
5
+ // - Vuelve a montarse si cambia `server` o `language`, o si se pasa o se quita `refreshToken` u `onClose`: la página del
6
+ // diseñador sabe al abrirse si puede pedir tokens nuevos y si sale «←».
7
+ // - Un `token` nuevo con el diseñador abierto no lo vuelve a montar: se le manda (setToken), como al renovarlo.
8
+ // - Los avisos (onReady, onSave…) y refreshToken llaman siempre a la última función recibida, sin volver a montar.
9
+ import { designer } from "@seatsyoo/visor";
10
+ import { createElement, forwardRef, useEffect, useLayoutEffect, useRef, version, } from "react";
11
+ import { avisarEnConsola, errorDeOpciones } from "./opciones.js";
12
+ // useLayoutEffect no hace nada en el servidor (y React 18 avisa): allí, useEffect, que tampoco se ejecuta.
13
+ const useEfectoAntesDePintar = typeof document === "undefined" ? useEffect : useLayoutEffect;
14
+ /** React 19 llama a la limpieza que devuelve una ref de función en lugar de llamarla con null; React 18, no. */
15
+ const REFS_CON_LIMPIEZA = Number(version.split(".")[0]) >= 19;
16
+ /** Pone el diseñador en la ref de quien usa el componente y devuelve cómo quitarlo, como React con la de un elemento. */
17
+ function ponerEnRef(ref, d) {
18
+ if (typeof ref === "function") {
19
+ const limpieza = ref(d);
20
+ return REFS_CON_LIMPIEZA && typeof limpieza === "function" ? limpieza : () => ref(null);
21
+ }
22
+ if (ref)
23
+ ref.current = d;
24
+ return () => {
25
+ if (ref && ref.current === d)
26
+ ref.current = null;
27
+ };
28
+ }
29
+ function SeatsyooDesignerConRef(props, ref) {
30
+ const { className, style, id, ...opciones } = props;
31
+ const contenedor = useRef(null);
32
+ // Las últimas opciones recibidas: los avisos y el montaje leen de aquí.
33
+ const ultimas = useRef(opciones);
34
+ // El diseñador abierto ahora mismo y el último token que se le dio.
35
+ const abierto = useRef(null);
36
+ // La ref en la que está puesto y cómo quitarlo de ella.
37
+ const enRef = useRef(null);
38
+ useEfectoAntesDePintar(() => {
39
+ ultimas.current = opciones;
40
+ });
41
+ function quitarDeRef() {
42
+ const actual = enRef.current;
43
+ enRef.current = null;
44
+ actual?.quitar();
45
+ }
46
+ function ponerDisenadorEnRef(r, d) {
47
+ if (enRef.current?.ref === r && enRef.current.d === d)
48
+ return;
49
+ quitarDeRef();
50
+ enRef.current = { ref: r, d, quitar: ponerEnRef(r, d) };
51
+ }
52
+ const renueva = !!opciones.refreshToken;
53
+ const cierra = !!opciones.onClose;
54
+ useEffect(() => {
55
+ const div = contenedor.current;
56
+ if (!div)
57
+ return;
58
+ const o = ultimas.current;
59
+ let d;
60
+ try {
61
+ d = designer({
62
+ container: div,
63
+ server: o.server,
64
+ token: o.token,
65
+ ...(o.language ? { language: o.language } : {}),
66
+ // Solo si los hay: la página del diseñador sabe al abrirse si puede pedir tokens y si sale «←».
67
+ ...(o.refreshToken ? { refreshToken: () => (ultimas.current.refreshToken ?? o.refreshToken)() } : {}),
68
+ ...(o.onClose ? { onClose: () => ultimas.current.onClose?.() } : {}),
69
+ onReady: (i) => ultimas.current.onReady?.(i),
70
+ onSave: (i) => ultimas.current.onSave?.(i),
71
+ onPublish: (i) => ultimas.current.onPublish?.(i),
72
+ onError: (e) => (ultimas.current.onError ?? avisarEnConsola)(e),
73
+ });
74
+ }
75
+ catch (err) {
76
+ // Un token que no es de una sesión del diseñador, un contenedor que falta…
77
+ const error = errorDeOpciones(err);
78
+ if (o.onError)
79
+ o.onError(error);
80
+ else
81
+ console.error("[seatsyoo]", error.message);
82
+ return;
83
+ }
84
+ abierto.current = { d, token: o.token };
85
+ ponerDisenadorEnRef(ref, d);
86
+ return () => {
87
+ if (enRef.current?.d === d)
88
+ quitarDeRef();
89
+ d.destroy();
90
+ if (abierto.current?.d === d)
91
+ abierto.current = null;
92
+ };
93
+ }, [opciones.server, opciones.language, renueva, cierra]);
94
+ // Si cambia la ref (otra función u otro objeto), el diseñador pasa a la nueva y se quita de la anterior.
95
+ useEffect(() => {
96
+ if (abierto.current)
97
+ ponerDisenadorEnRef(ref, abierto.current.d);
98
+ }, [ref]);
99
+ // Un token nuevo, al diseñador abierto (si se acaba de montar con él, ya lo tiene).
100
+ useEffect(() => {
101
+ const a = abierto.current;
102
+ if (!a || a.token === opciones.token)
103
+ return;
104
+ try {
105
+ a.d.setToken(opciones.token);
106
+ a.token = opciones.token;
107
+ }
108
+ catch (err) {
109
+ (ultimas.current.onError ?? avisarEnConsola)(errorDeOpciones(err));
110
+ }
111
+ }, [opciones.token]);
112
+ return createElement("div", { ref: contenedor, className, style, id });
113
+ }
114
+ /**
115
+ * El diseñador de planos de Seatsyoo en tu web, con el token de una sesión que abre tu servidor con su clave secreta
116
+ * (`POST /api/v1/charts/{plano}/designer-sessions`, o `charts.designerSession` de `@seatsyoo/node`).
117
+ *
118
+ * ```tsx
119
+ * <SeatsyooDesigner
120
+ * server="https://seatsyoo.example"
121
+ * token={token}
122
+ * refreshToken={pedirToken}
123
+ * style={{ height: "80vh" }}
124
+ * onPublish={({ version }) => console.log("Publicada la versión", version)}
125
+ * onClose={() => setAbierto(false)}
126
+ * />
127
+ * ```
128
+ *
129
+ * Con `ref` (`useRef<EmbeddedDesigner>(null)`) tienes el diseñador abierto: `setToken()`, `iframe`…
130
+ */
131
+ export const SeatsyooDesigner = forwardRef(SeatsyooDesignerConRef);
132
+ SeatsyooDesigner.displayName = "SeatsyooDesigner";
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { SeatsyooChart, type SeatsyooChartProps } from "./SeatsyooChart.js";
2
- export type { ChartDocument, Language, RenderOptions, SeatingChart, SelectedObject, StatusStyle } from "@seatsyoo/visor";
2
+ export { SeatsyooDesigner, type SeatsyooDesignerProps } from "./SeatsyooDesigner.js";
3
+ export type { ChartDocument, EmbeddedDesigner, EmbeddedDesignerOptions, Language, RenderOptions, SeatingChart, SelectedObject, StatusStyle, } from "@seatsyoo/visor";
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
- // @seatsyoo/react: el visor de mapas de asientos de Seatsyoo como componente de React.
1
+ // @seatsyoo/react: el visor de mapas de asientos de Seatsyoo y su diseñador de planos como componentes de React.
2
2
  export { SeatsyooChart } from "./SeatsyooChart.js";
3
+ export { SeatsyooDesigner } from "./SeatsyooDesigner.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seatsyoo/react",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Componente de React para el visor de mapas de asientos de Seatsyoo: elegir butacas, retenerlas y venderlas en tiempo real.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -27,7 +27,7 @@
27
27
  "dist/*.d.ts"
28
28
  ],
29
29
  "dependencies": {
30
- "@seatsyoo/visor": "^1.0.0"
30
+ "@seatsyoo/visor": "^1.1.0"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "react": "^18.0.0 || ^19.0.0"