@seatsyoo/vue 0.0.0-stage → 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.
@@ -0,0 +1,226 @@
1
+ // SeatsyooChart: el visor de Seatsyoo como componente de Vue 3 (3.3 o posterior), sin SFC (defineComponent y una
2
+ // función render), para que se use sin compilar plantillas.
3
+ //
4
+ // - Se monta en onMounted: en el servidor (SSR) solo se pinta el <div> vacío.
5
+ // - Antes de desmontar, destroy().
6
+ // - Si cambia una opción de identidad (OPCIONES_DE_IDENTIDAD, por valor), se destruye y se monta otro. Si el nuevo
7
+ // no va a recuperar lo que el comprador tenía retenido (otro evento…), antes se suelta (sueltaAlCambiar).
8
+ // - theme y colorBy se aplican al visor montado (setTheme, setColorBy); statusStyles y marks (modo «manager»),
9
+ // también, comparadas por valor (setStatusStyles, setMarks), aunque se cambien en el sitio.
10
+ // - Los avisos del visor salen como eventos de Vue (ready, change, select…): llegan siempre a los oyentes de
11
+ // ese momento, sin volver a montar.
12
+ import { render, } from "@seatsyoo/visor";
13
+ import { defineComponent, getCurrentInstance, h, onBeforeUnmount, onMounted, shallowRef, toRaw, watch, } from "vue";
14
+ import { SeatsyooDesigner } from "./SeatsyooDesigner.js";
15
+ import { aplicarEnVivo, avisarEnConsola, copiaDeDatos, enVivoDe, errorDeOpciones, identidadDe, igual, opcionesEnVivo, sinIndefinidos, sueltaAlCambiar, } from "./opciones.js";
16
+ // Los booleanos con `default: undefined`: sin él, Vue los pone a false si no se pasan, y el visor los quiere
17
+ // sin indicar (su valor por defecto es «sí»).
18
+ const props = {
19
+ /** URL base del servidor de Seatsyoo, p. ej. https://seatsyoo.example */
20
+ server: { type: String, required: true },
21
+ /** Clave pública del espacio (obligatoria salvo con `link` o `document`). */
22
+ publicKey: String,
23
+ /** Clave del evento. */
24
+ event: String,
25
+ /** O la clave de un enlace público del plano: solo para ver. */
26
+ link: String,
27
+ /** O el plano ya cargado: solo para ver. */
28
+ document: Object,
29
+ /** O la clave de una temporada (abono). */
30
+ season: String,
31
+ /** Abono parcial: los eventos de la temporada elegidos. */
32
+ seasonEvents: Array,
33
+ /** Canales de venta desde los que se compra. */
34
+ channels: Array,
35
+ /**
36
+ * Los precios que pone tu web, por clave de categoría (`{ platea: 30 }`): el visor los enseña en la ficha, la leyenda
37
+ * y el total, y salen en `price` de lo elegido. Seatsyoo no guarda precios.
38
+ */
39
+ pricing: Object,
40
+ /** Moneda de `pricing` (código ISO; si no, euros). */
41
+ currency: String,
42
+ locale: String,
43
+ language: String,
44
+ /** Máximo de plazas seleccionables (por defecto 10). */
45
+ maxSelected: Number,
46
+ /** «continue» (por defecto) recupera la selección al recargar; «start» empieza de cero. */
47
+ session: String,
48
+ /** Token de retención creado por tu servidor. */
49
+ holdToken: String,
50
+ /** «buyer» (por defecto) o «manager». */
51
+ mode: String,
52
+ /** Color base de los objetos (se aplica sin volver a montar). */
53
+ colorBy: String,
54
+ /** Tema (se aplica sin volver a montar). */
55
+ theme: String,
56
+ /**
57
+ * Solo en el modo «manager»: nombre, color e icono de los estados, por su clave (los propios y los de base que se
58
+ * quieran cambiar). Se aplica sin volver a montar (setStatusStyles).
59
+ */
60
+ statusStyles: Object,
61
+ /**
62
+ * Solo en el modo «manager»: marcas sobre plazas que siguen libres, objeto → estado (su aspecto, en `statusStyles`).
63
+ * Se aplica sin volver a montar (setMarks).
64
+ */
65
+ marks: Object,
66
+ showLegend: { type: Boolean, default: undefined },
67
+ showSelectionBar: { type: Boolean, default: undefined },
68
+ /** Planta que se muestra al abrir (id). */
69
+ floor: String,
70
+ sectors: { type: Boolean, default: undefined },
71
+ /** Botón de vista 3D (por defecto sí). */
72
+ view3d: { type: Boolean, default: undefined },
73
+ /** Entrada animada al abrirse: el plano se descubre desde el escenario (por defecto, no). */
74
+ entrance: { type: Boolean, default: undefined },
75
+ /**
76
+ * Textos del plano que se enseñan cambiados (por ejemplo, traducidos), por el texto tal como está en el plano:
77
+ * `{ Platea: "Stalls" }`. Solo cambia lo que se ve: claves, selección y eventos siguen siendo los del plano.
78
+ */
79
+ texts: Object,
80
+ /** Cómo cargar el módulo 3D (por defecto se pide al servidor). */
81
+ load3d: Function,
82
+ };
83
+ // Los parámetros solo dan el tipo de cada evento (Vue valida con lo que devuelven: siempre sí).
84
+ const emits = {
85
+ /** El visor ha cargado el plano y los estados. */
86
+ ready: (chart) => true,
87
+ /** La selección ha cambiado. */
88
+ change: (selection) => true,
89
+ select: (object) => true,
90
+ deselect: (object) => true,
91
+ /** Algo ha fallado: `{ code, message }`. Sin oyente, se escribe en la consola. */
92
+ error: (error) => true,
93
+ /** Se acabó el tiempo de la retención (`@hold-expired`). */
94
+ holdExpired: () => true,
95
+ /** El token de retención ha cambiado (`@hold-token-change`). */
96
+ holdTokenChange: (token) => true,
97
+ /** Se muestra otra planta (`@floor-change`). */
98
+ floorChange: (floor) => true,
99
+ };
100
+ // El componente; su tipo público es SeatsyooChartComponent (abajo).
101
+ const componente = defineComponent({
102
+ name: "SeatsyooChart",
103
+ props,
104
+ emits,
105
+ expose: ["chart"],
106
+ setup(props, { emit }) {
107
+ /** El visor montado, o null (en el servidor, antes de montar, tras desmontar o si las opciones no valen). */
108
+ const chart = shallowRef(null);
109
+ /** El <div> donde se monta (ref de plantilla «el»). */
110
+ const el = shallowRef(null);
111
+ const instancia = getCurrentInstance();
112
+ let vivo = false;
113
+ /** La identidad con que se montó el visor actual (o con que falló el último intento). */
114
+ let montada = null;
115
+ /** Las opciones en vivo (tema, color, estilos y marcas) que tiene el visor actual. */
116
+ let aplicadas = enVivoDe({});
117
+ /** ¿Hay alguien escuchando @error ahora mismo? (Si no, el error va a la consola, como hace el visor.) */
118
+ function escuchanError() {
119
+ const p = instancia?.vnode.props;
120
+ return !!p && (typeof p.onError === "function" || Array.isArray(p.onError) || typeof p.onErrorOnce === "function");
121
+ }
122
+ /** La identidad de las props: lee solo esas props (así el watch solo depende de ellas). */
123
+ function identidad() {
124
+ const id = identidadDe(props);
125
+ // El plano no se copia ni se recorre: si llega reactivo, el visor lo recibe sin proxy.
126
+ id.document = id.document && toRaw(id.document);
127
+ return id;
128
+ }
129
+ function desmontar() {
130
+ const anterior = chart.value;
131
+ chart.value = null;
132
+ montada = null;
133
+ anterior?.destroy();
134
+ }
135
+ function montar(nueva) {
136
+ // Al cambiar de evento (o de lo que sea que el visor nuevo no recupere), se suelta lo que tenía retenido el
137
+ // que se va, con sus avisos (deselect y change con la selección vacía). Al desmontar el componente, no.
138
+ if (chart.value && montada && sueltaAlCambiar(montada, nueva))
139
+ void chart.value.clearSelection();
140
+ desmontar();
141
+ const div = el.value;
142
+ if (!div)
143
+ return;
144
+ montada = nueva;
145
+ const { load3d: hayLoad3d, ...opciones } = nueva;
146
+ const load3d = props.load3d;
147
+ // Copias sin proxy de statusStyles y marks: lo que se compara después y lo que recibe el visor.
148
+ const enVivo = enVivoDe(props);
149
+ try {
150
+ chart.value = render({
151
+ ...sinIndefinidos(opciones),
152
+ ...opcionesEnVivo(props, enVivo),
153
+ container: div,
154
+ onReady: (c) => emit("ready", c),
155
+ onChange: (s) => emit("change", s),
156
+ onSelect: (o) => emit("select", o),
157
+ onDeselect: (o) => emit("deselect", o),
158
+ onHoldExpired: () => emit("holdExpired"),
159
+ onHoldTokenChange: (t) => emit("holdTokenChange", t),
160
+ onFloorChange: (f) => emit("floorChange", f),
161
+ onError: (e) => (escuchanError() ? emit("error", e) : avisarEnConsola(e)),
162
+ // Solo si hay load3d (si no, el visor pide la 3D al servidor); ponerlo o quitarlo cambia la identidad.
163
+ ...(hayLoad3d && load3d ? { load3d: () => (props.load3d ?? load3d)() } : {}),
164
+ });
165
+ }
166
+ catch (err) {
167
+ const error = errorDeOpciones(err);
168
+ if (escuchanError())
169
+ emit("error", error);
170
+ else
171
+ console.error("[seatsyoo]", error.message);
172
+ return;
173
+ }
174
+ aplicadas = enVivo;
175
+ }
176
+ onMounted(() => {
177
+ vivo = true;
178
+ montar(identidad());
179
+ });
180
+ onBeforeUnmount(() => {
181
+ vivo = false;
182
+ desmontar();
183
+ });
184
+ // Antes del de las opciones en vivo: si en el mismo cambio se monta otro visor, ya lleva el tema nuevo.
185
+ watch(identidad, (nueva) => {
186
+ if (vivo && !(montada && igual(nueva, montada)))
187
+ montar(nueva);
188
+ });
189
+ // Copiar statusStyles y marks lee todo lo que tienen: así también avisa si se cambian en el sitio (reactivos).
190
+ watch(() => ({ theme: props.theme, colorBy: props.colorBy, statusStyles: copiaDeDatos(props.statusStyles), marks: copiaDeDatos(props.marks) }), (vivas) => {
191
+ const c = chart.value;
192
+ if (c)
193
+ aplicarEnVivo(c, aplicadas, vivas);
194
+ });
195
+ return { chart, el };
196
+ },
197
+ render() {
198
+ // class, style, id y demás atributos pasan solos a este <div> (inheritAttrs).
199
+ return h("div", { ref: "el" });
200
+ },
201
+ });
202
+ /**
203
+ * El visor de mapas de asientos de Seatsyoo.
204
+ *
205
+ * ```vue
206
+ * <SeatsyooChart
207
+ * ref="plano"
208
+ * server="https://seatsyoo.example"
209
+ * public-key="pk_tu_clave_publica"
210
+ * event="hamlet-2026-11-20"
211
+ * style="height: 600px"
212
+ * @change="(seleccion) => (elegidas = seleccion)"
213
+ * />
214
+ * ```
215
+ *
216
+ * Con una ref de plantilla, `plano.value.chart` es el visor montado (`getSelection()`, `holdToken`…), o `null`
217
+ * mientras no hay ninguno.
218
+ */
219
+ export const SeatsyooChart = componente;
220
+ /** Plugin que registra <SeatsyooChart> y <SeatsyooDesigner> en toda la aplicación: `app.use(SeatsyooPlugin)`. */
221
+ export const SeatsyooPlugin = {
222
+ install(app) {
223
+ app.component("SeatsyooChart", SeatsyooChart);
224
+ app.component("SeatsyooDesigner", SeatsyooDesigner);
225
+ },
226
+ };
@@ -0,0 +1,71 @@
1
+ import { type EmbeddedDesigner, type Language } from "@seatsyoo/visor";
2
+ import { type ComponentOptionsMixin, type DefineComponent, type ExtractPublicPropTypes, type PropType } from "vue";
3
+ type Estado = "draft" | "published" | "published-with-draft";
4
+ declare const props: {
5
+ /** URL base del servidor de Seatsyoo, p. ej. https://seatsyoo.example */
6
+ server: {
7
+ type: StringConstructor;
8
+ required: true;
9
+ };
10
+ /** El token de la sesión del diseñador (dsg_…), que abre tu servidor con su clave secreta. */
11
+ token: {
12
+ type: StringConstructor;
13
+ required: true;
14
+ };
15
+ /** Idioma del diseñador: «es» (por defecto) o «en». */
16
+ language: PropType<Language>;
17
+ /** Pide a tu servidor otro token del mismo plano antes de que caduque (sin ella, al caducar deja de guardar). */
18
+ refreshToken: PropType<() => Promise<string>>;
19
+ };
20
+ declare const emits: {
21
+ /** El diseñador está abierto con el plano. */
22
+ ready: (info: {
23
+ chart: string;
24
+ status: Estado;
25
+ publishedVersion: number | null;
26
+ canPublish: boolean;
27
+ }) => boolean;
28
+ /** Se han guardado los cambios en el borrador. */
29
+ save: (info: {
30
+ chart: string;
31
+ status: Estado;
32
+ }) => boolean;
33
+ /** Se ha publicado una versión nueva. */
34
+ publish: (info: {
35
+ chart: string;
36
+ version: number;
37
+ }) => boolean;
38
+ /** Se ha pulsado «←» (sale solo si hay quien escuche @close al montarse). */
39
+ close: () => boolean;
40
+ /** Algo ha fallado: `{ code, message }`. Sin oyente, se escribe en la consola. */
41
+ error: (error: {
42
+ code: string;
43
+ message: string;
44
+ }) => boolean;
45
+ };
46
+ /** Props de SeatsyooDesigner: las opciones del diseñador, sin `container` ni los avisos (que son eventos). */
47
+ export type SeatsyooDesignerProps = ExtractPublicPropTypes<typeof props>;
48
+ /** Lo que expone el componente a una ref de plantilla. */
49
+ export interface SeatsyooDesignerExposed {
50
+ /** El diseñador abierto (`setToken()`, `iframe`), o null mientras no hay ninguno. */
51
+ designer: EmbeddedDesigner | null;
52
+ }
53
+ /** Su tipo público, escrito a mano como el de SeatsyooChart (con los parámetros que no han cambiado desde Vue 3.3). */
54
+ export type SeatsyooDesignerComponent = DefineComponent<typeof props, SeatsyooDesignerExposed, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, typeof emits>;
55
+ /**
56
+ * 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
57
+ * (`POST /api/v1/charts/{plano}/designer-sessions`, o `charts.designerSession` de `@seatsyoo/node`).
58
+ *
59
+ * ```vue
60
+ * <SeatsyooDesigner
61
+ * server="https://seatsyoo.example"
62
+ * :token="token"
63
+ * :refresh-token="pedirToken"
64
+ * style="height: 80vh"
65
+ * @publish="({ version }) => console.log(version)"
66
+ * @close="abierto = false"
67
+ * />
68
+ * ```
69
+ */
70
+ export declare const SeatsyooDesigner: SeatsyooDesignerComponent;
71
+ export {};
@@ -0,0 +1,138 @@
1
+ // SeatsyooDesigner: el diseñador de planos de Seatsyoo, incrustado en tu web, como componente de Vue 3 (3.3 o posterior),
2
+ // sin SFC (defineComponent y una función render), como SeatsyooChart.
3
+ //
4
+ // - Se monta en onMounted: en el servidor (SSR) solo se pinta el <div> vacío. Antes de desmontar, destroy().
5
+ // - Vuelve a montarse si cambia `server` o `language`, o si se pasa o se quita `refreshToken`: la página del diseñador
6
+ // sabe al abrirse si puede pedir tokens nuevos. «←» sale si al montarse hay quien escuche @close.
7
+ // - Un `token` nuevo con el diseñador abierto no lo vuelve a montar: se le manda (setToken), como al renovarlo.
8
+ // - Los avisos salen como eventos de Vue (ready, save, publish, close, error); refreshToken es una prop (devuelve el
9
+ // token) y se llama siempre la última recibida.
10
+ import { designer } from "@seatsyoo/visor";
11
+ import { defineComponent, getCurrentInstance, h, onBeforeUnmount, onMounted, shallowRef, watch, } from "vue";
12
+ import { avisarEnConsola, errorDeOpciones } from "./opciones.js";
13
+ const props = {
14
+ /** URL base del servidor de Seatsyoo, p. ej. https://seatsyoo.example */
15
+ server: { type: String, required: true },
16
+ /** El token de la sesión del diseñador (dsg_…), que abre tu servidor con su clave secreta. */
17
+ token: { type: String, required: true },
18
+ /** Idioma del diseñador: «es» (por defecto) o «en». */
19
+ language: String,
20
+ /** Pide a tu servidor otro token del mismo plano antes de que caduque (sin ella, al caducar deja de guardar). */
21
+ refreshToken: Function,
22
+ };
23
+ // Los parámetros solo dan el tipo de cada evento (Vue valida con lo que devuelven: siempre sí).
24
+ const emits = {
25
+ /** El diseñador está abierto con el plano. */
26
+ ready: (info) => true,
27
+ /** Se han guardado los cambios en el borrador. */
28
+ save: (info) => true,
29
+ /** Se ha publicado una versión nueva. */
30
+ publish: (info) => true,
31
+ /** Se ha pulsado «←» (sale solo si hay quien escuche @close al montarse). */
32
+ close: () => true,
33
+ /** Algo ha fallado: `{ code, message }`. Sin oyente, se escribe en la consola. */
34
+ error: (error) => true,
35
+ };
36
+ const componente = defineComponent({
37
+ name: "SeatsyooDesigner",
38
+ props,
39
+ emits,
40
+ expose: ["designer"],
41
+ setup(props, { emit }) {
42
+ const abierto = shallowRef(null);
43
+ const el = shallowRef(null);
44
+ const instancia = getCurrentInstance();
45
+ let vivo = false;
46
+ /** El último token que se le dio al diseñador abierto. */
47
+ let token = "";
48
+ /** ¿Hay alguien escuchando ese evento ahora mismo? */
49
+ function escuchan(evento) {
50
+ const p = instancia?.vnode.props;
51
+ const f = p?.[evento] ?? p?.[`${evento}Once`];
52
+ return typeof f === "function" || Array.isArray(f);
53
+ }
54
+ function desmontar() {
55
+ const anterior = abierto.value;
56
+ abierto.value = null;
57
+ anterior?.destroy();
58
+ }
59
+ function montar() {
60
+ desmontar();
61
+ const div = el.value;
62
+ if (!div)
63
+ return;
64
+ const refresh = props.refreshToken;
65
+ try {
66
+ abierto.value = designer({
67
+ container: div,
68
+ server: props.server,
69
+ token: props.token,
70
+ ...(props.language ? { language: props.language } : {}),
71
+ ...(refresh ? { refreshToken: () => (props.refreshToken ?? refresh)() } : {}),
72
+ ...(escuchan("onClose") ? { onClose: () => emit("close") } : {}),
73
+ onReady: (i) => emit("ready", i),
74
+ onSave: (i) => emit("save", i),
75
+ onPublish: (i) => emit("publish", i),
76
+ onError: (e) => (escuchan("onError") ? emit("error", e) : avisarEnConsola(e)),
77
+ });
78
+ token = props.token;
79
+ }
80
+ catch (err) {
81
+ const error = errorDeOpciones(err);
82
+ if (escuchan("onError"))
83
+ emit("error", error);
84
+ else
85
+ console.error("[seatsyoo]", error.message);
86
+ }
87
+ }
88
+ onMounted(() => {
89
+ vivo = true;
90
+ montar();
91
+ });
92
+ onBeforeUnmount(() => {
93
+ vivo = false;
94
+ desmontar();
95
+ });
96
+ watch(() => [props.server, props.language, !!props.refreshToken], () => {
97
+ if (vivo)
98
+ montar();
99
+ });
100
+ watch(() => props.token, (nuevo) => {
101
+ const d = abierto.value;
102
+ if (!vivo || !d || nuevo === token)
103
+ return;
104
+ try {
105
+ d.setToken(nuevo);
106
+ token = nuevo;
107
+ }
108
+ catch (err) {
109
+ const error = errorDeOpciones(err);
110
+ if (escuchan("onError"))
111
+ emit("error", error);
112
+ else
113
+ avisarEnConsola(error);
114
+ }
115
+ });
116
+ return { designer: abierto, el };
117
+ },
118
+ render() {
119
+ // class, style, id y demás atributos pasan solos a este <div> (inheritAttrs).
120
+ return h("div", { ref: "el" });
121
+ },
122
+ });
123
+ /**
124
+ * 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
125
+ * (`POST /api/v1/charts/{plano}/designer-sessions`, o `charts.designerSession` de `@seatsyoo/node`).
126
+ *
127
+ * ```vue
128
+ * <SeatsyooDesigner
129
+ * server="https://seatsyoo.example"
130
+ * :token="token"
131
+ * :refresh-token="pedirToken"
132
+ * style="height: 80vh"
133
+ * @publish="({ version }) => console.log(version)"
134
+ * @close="abierto = false"
135
+ * />
136
+ * ```
137
+ */
138
+ export const SeatsyooDesigner = componente;
@@ -0,0 +1,3 @@
1
+ export { SeatsyooChart, type SeatsyooChartExposed, type SeatsyooChartProps, SeatsyooPlugin } from "./SeatsyooChart.js";
2
+ export { SeatsyooDesigner, type SeatsyooDesignerExposed, type SeatsyooDesignerProps } from "./SeatsyooDesigner.js";
3
+ export type { ChartDocument, EmbeddedDesigner, EmbeddedDesignerOptions, Language, RenderOptions, SeatingChart, SelectedObject, StatusStyle, } from "@seatsyoo/visor";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ // @seatsyoo/vue: el visor de mapas de asientos de Seatsyoo y su diseñador de planos como componentes de Vue 3.
2
+ export { SeatsyooChart, SeatsyooPlugin } from "./SeatsyooChart.js";
3
+ export { SeatsyooDesigner } from "./SeatsyooDesigner.js";
@@ -0,0 +1,99 @@
1
+ import type { RenderOptions, SeatingChart } from "@seatsyoo/visor";
2
+ /**
3
+ * Opciones que, si cambian, vuelven a montar el visor: deciden qué se carga y cómo se vende. Los arrays y los
4
+ * objetos se comparan por su valor, no por la referencia: `:channels="['web']"` en cada render no remonta.
5
+ */
6
+ export declare const OPCIONES_DE_IDENTIDAD: readonly ["server", "publicKey", "event", "link", "document", "season", "seasonEvents", "channels", "pricing", "currency", "locale", "language", "maxSelected", "session", "holdToken", "mode", "showLegend", "showSelectionBar", "floor", "sectors", "view3d", "entrance", "texts"];
7
+ /**
8
+ * Opciones que se aplican al visor ya montado, sin volver a montarlo: theme y colorBy (setTheme y setColorBy) y, para
9
+ * el modo «manager», los estilos de los estados y las marcas sobre plazas libres (setStatusStyles y setMarks). Se
10
+ * comparan por valor, como las de identidad: un objeto nuevo con lo mismo no llama a nada.
11
+ */
12
+ export declare const OPCIONES_EN_VIVO: readonly ["theme", "colorBy", "statusStyles", "marks"];
13
+ /** Avisos del visor: siempre llaman a la última función recibida, sin volver a montar. */
14
+ export declare const AVISOS: readonly ["onReady", "onChange", "onSelect", "onDeselect", "onError", "onHoldExpired", "onHoldTokenChange", "onFloorChange"];
15
+ type ClaveDeIdentidad = (typeof OPCIONES_DE_IDENTIDAD)[number];
16
+ /**
17
+ * Cada opción del visor tiene aquí su regla (vuelve a montar, se aplica en vivo o es un aviso). Si el visor gana una
18
+ * opción nueva, esto deja de compilar hasta decidir qué hace el componente con ella: pasó con `entrance` y `texts`,
19
+ * que llegaron al visor sin que el componente las pasara, y con `statusStyles` y `marks` (en vivo: el visor tiene
20
+ * setStatusStyles y setMarks).
21
+ */
22
+ type SinRegla = Exclude<keyof RenderOptions, ClaveDeIdentidad | (typeof OPCIONES_EN_VIVO)[number] | (typeof AVISOS)[number] | "container" | "load3d">;
23
+ type DebeSerNever<T extends never> = T;
24
+ export type TodasLasOpcionesTienenRegla = DebeSerNever<SinRegla>;
25
+ /**
26
+ * Lo que identifica a un visor montado: las opciones de identidad (copiando arrays y objetos pequeños, para que
27
+ * cambiarlos en el sitio también cuente) y si hay `load3d` (la función en sí puede cambiar sin remontar: se
28
+ * llama siempre a la última).
29
+ */
30
+ export type Identidad = {
31
+ [K in ClaveDeIdentidad]: RenderOptions[K];
32
+ } & {
33
+ load3d: boolean;
34
+ };
35
+ export declare function identidadDe(opciones: Partial<RenderOptions>): Identidad;
36
+ /** Igualdad por valor de datos tipo JSON (cadenas, números, booleanos, null, undefined, arrays y objetos). */
37
+ export declare function igual(a: unknown, b: unknown): boolean;
38
+ /**
39
+ * Copia normalizada de datos tipo JSON (listas y objetos, a cualquier profundidad): los objetos salen con
40
+ * Object.prototype (también un diccionario hecho con `Object.create(null)`) y sin las claves que valen `undefined`
41
+ * (`{ color, icon: undefined }` es lo mismo que `{ color }`: el visor no pinta icono en los dos casos).
42
+ *
43
+ * Es la regla de statusStyles y marks, la misma en React, Vue y Angular: lo aplicado en vivo se guarda así copiado, lo
44
+ * que llega se copia igual antes de compararlo, y el visor recibe esa copia. Así cambiarlo en el sitio también cuenta y
45
+ * el visor no comparte el objeto de la página (setMarks repinta lo que difiere de lo que tenía, y si fuera el mismo
46
+ * objeto ya cambiado no vería nada).
47
+ */
48
+ export declare function copiaDeDatos<T>(valor: T): T;
49
+ /** Con qué valor de cada opción en vivo se montó el visor o se le cambió después (sin indicar, el de por defecto). */
50
+ export interface EnVivo {
51
+ theme: NonNullable<RenderOptions["theme"]>;
52
+ colorBy: NonNullable<RenderOptions["colorBy"]>;
53
+ statusStyles: NonNullable<RenderOptions["statusStyles"]>;
54
+ marks: NonNullable<RenderOptions["marks"]>;
55
+ }
56
+ /**
57
+ * Las opciones en vivo con que se monta un visor. Sin theme ni colorBy, los del visor por defecto; sin statusStyles ni
58
+ * marks, ninguno (`{}`, que para el visor es lo mismo que no pasarlas).
59
+ */
60
+ export declare function enVivoDe(opciones: Partial<RenderOptions>): EnVivo;
61
+ /** Lo que hay que añadir a las opciones de `render` para montar con estas opciones en vivo (las copias guardadas). */
62
+ export declare function opcionesEnVivo(opciones: Partial<RenderOptions>, enVivo: EnVivo): Partial<RenderOptions>;
63
+ /** Los métodos del visor con que se aplican las opciones en vivo. */
64
+ export type VisorEnVivo = Pick<SeatingChart, "setTheme" | "setColorBy" | "setStatusStyles" | "setMarks">;
65
+ /**
66
+ * Lleva al visor montado las opciones en vivo que han cambiado (por valor) desde `aplicadas`, y las apunta en
67
+ * `aplicadas`. Quitar una es volver a la de por defecto: el tema claro, el color por categoría, sin estilos ni marcas.
68
+ */
69
+ export declare function aplicarEnVivo(visor: VisorEnVivo, aplicadas: EnVivo, opciones: Partial<RenderOptions>): void;
70
+ /** Las opciones sin las que valen `undefined` (el visor usa entonces su valor por defecto). */
71
+ export declare function sinIndefinidos<T extends object>(opciones: T): T;
72
+ /** Lo que hace el visor con un error si nadie escucha `onError`: escribirlo en la consola. */
73
+ export declare function avisarEnConsola(error: {
74
+ code: string;
75
+ message: string;
76
+ }): void;
77
+ /**
78
+ * `render` lanza si faltan opciones (qué enseñar, la clave pública…). En un componente no hay quien lo
79
+ * recoja, así que se convierte en un error con el código `invalid-options`.
80
+ */
81
+ export declare function errorDeOpciones(err: unknown): {
82
+ code: string;
83
+ message: string;
84
+ };
85
+ /**
86
+ * Al volver a montar (cambia una opción de identidad), ¿hay que soltar lo que el comprador tiene retenido en el
87
+ * visor que se va?
88
+ *
89
+ * destroy() no suelta lo retenido: así el visor que se monte después en el mismo evento lo recupera (con
90
+ * `session: "continue"`, guarda su token en sessionStorage). Pero si el nuevo no lo va a recuperar (otro evento u
91
+ * otra temporada, otro servidor o clave pública, `session: "start"`, un `holdToken` nuevo, modo «manager»), esas
92
+ * plazas se quedarían bloqueadas para los demás hasta que caduque la retención, sin que la página pueda venderlas
93
+ * ni soltarlas: el comprador ya no las ve y el token del visor nuevo es otro. Entonces se sueltan.
94
+ *
95
+ * Lo retenido con un `holdToken` de la página no se suelta nunca: es de su token y lo lleva su servidor (por
96
+ * ejemplo, un carrito con varias funciones). Tampoco hay nada que soltar en el modo «manager» ni al solo ver.
97
+ */
98
+ export declare function sueltaAlCambiar(antes: Identidad, despues: Identidad): boolean;
99
+ export {};