@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 +39 -2
- package/dist/SeatsyooDesigner.d.ts +33 -0
- package/dist/SeatsyooDesigner.js +132 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/package.json +2 -2
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
|
-
|
|
224
|
-
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
30
|
+
"@seatsyoo/visor": "^1.1.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"react": "^18.0.0 || ^19.0.0"
|