Estados y estilos de secciones

El booking renderer rastrea cuatro estados para cada contorno de sección: highlighted, selected, unavailable y filtered. Cada estado puede estilizarse de forma independiente mediante el tema svgSectionStyles y alternarse de forma programática a través de la API del renderer. Los estados funcionan de manera uniforme en todos los tipos de contorno: fondos SVG, formas creadas en el Editor, contornos generados automáticamente y contornos de respaldo.

Estados de secciones

Cada contorno de sección puede tener cero o más de estos estados activos simultáneamente:

State Trigger Typical use
highlighted Paso del ratón o programático Retroalimentación visual al pasar el ratón
selected Clic del usuario en una sección de Admisión General (GA) Sección añadida al carrito
unavailable disableSections() Secciones agotadas, restringidas o cerradas
filtered filterSections() Mostrar solo un subconjunto de secciones (p. ej., nivel de precio)

Los estados se almacenan como atributos data-* en los elementos de contorno SVG (data-highlighted, data-selected, data-unavailable, data-filtered) y se estilizan mediante CSS generado a partir de la configuración de tu tema.

Estilos con svgSectionStyles

Pasa un objeto svgSectionStyles dentro de theme al crear el renderer. Cada clave corresponde a un estado, y cada valor controla las propiedades visuales de ese estado.

import { SeatmapBookingRenderer } from '@seatmap.pro/renderer';

const renderer = new SeatmapBookingRenderer(document.getElementById('seatmap'), {
  publicKey: 'your-public-key',
  theme: {
    svgSectionStyles: {
      default: {
        bgColor: 'rgba(200, 200, 200, 0.2)',
        stroke: { color: '#888', width: '2', opacity: 1 },
        cursor: 'pointer',
      },
      hovered: {
        bgColor: 'rgba(33, 150, 243, 0.5)',
        stroke: { color: '#1565C0', width: '3', opacity: 1 },
        cursor: 'pointer',
      },
      selected: {
        bgColor: 'rgba(76, 175, 80, 0.6)',
        stroke: { color: '#2E7D32', width: '3', opacity: 1 },
        cursor: 'pointer',
      },
      unavailable: {
        bgColor: 'rgba(244, 67, 54, 0.4)',
        stroke: { color: '#C62828', width: '3', opacity: 1 },
        cursor: 'not-allowed',
        opacity: 0.5,
      },
      filtered: {
        bgColor: 'rgba(0, 0, 0, 0.15)',
        stroke: { color: '#999', width: '1', opacity: 0.5 },
        cursor: 'default',
        opacity: 0.3,
      },
    },
  },
});
renderer.loadEvent('your-event-id');

Referencia de propiedades de estilo

Cada estado acepta estas propiedades:

Property Type Description
bgColor string Color de relleno del contorno. Usa rgba() para superposiciones semitransparentes sobre el fondo del recinto.
stroke.color string Color del borde del contorno.
stroke.width string Grosor del borde del contorno (p. ej., '2').
stroke.opacity number Opacidad del borde del contorno (de 0 a 1).
sectionName.color string Color del texto de la etiqueta de la sección dentro del contorno.
cursor string Cursor CSS al pasar el ratón sobre la sección (p. ej., 'pointer', 'not-allowed').
opacity number Opacidad general del elemento de contorno (de 0 a 1). Predeterminados: 0.5 para unavailable, 0.3 para filtered. Los valores del tema anulan los predeterminados.

El estado default acepta todas las propiedades listadas arriba. Los demás estados (hovered, selected, unavailable, filtered) admiten además bgColor para el relleno de la superposición.

Interfaz OutlineStates

El renderer rastrea el estado de la sección mediante la interfaz OutlineStates. Cada campo es un booleano opcional: establece solo los campos que quieras cambiar.

interface OutlineStates {
  highlighted?: boolean; // Mouse hover or programmatic highlight
  selected?: boolean; // Section added to cart (GA sections)
  unavailable?: boolean; // Disabled -- cannot interact
  filtered?: boolean; // Dimmed by active filter
}

Al leer el estado de vuelta, los cuatro campos siempre están presentes:

interface IEntityStates {
  highlighted: boolean;
  selected: boolean;
  unavailable: boolean;
  filtered: boolean;
}

Referencia de la API

Desactivar secciones

Marca secciones como no disponibles para impedir la interacción. Las secciones desactivadas muestran el estilo unavailable e ignoran los clics.

// Disable specific sections by ID
renderer.disableSections([101, 102, 103]);

// Disable with reset -- re-enable all previously disabled, then disable these
// Useful for category buttons: each click disables a new set without accumulating
renderer.disableSections([201, 202], { resetAll: true });

// Re-enable specific sections
renderer.enableSections([101, 102, 103]);

También puedes desactivar por nombre de sección:

renderer.disableSectionsByNames(['Floor A', 'Floor B']);
renderer.enableSectionsByNames(['Floor A']);

Filtrar secciones

El filtrado muestra solo las secciones que especifiques, atenuando todas las demás. Es útil para la selección de niveles de precio, el filtrado por accesibilidad o las vistas por categoría.

// Show only these sections -- all others get the "filtered" style
const premiumIds = [101, 102, 103];
renderer.filterSections(premiumIds);

// Remove all filters -- restore all sections to normal
renderer.removeFilterSections();

La lógica del filtro invierte los IDs que pasas: las secciones incluidas en el array permanecen normales, y las secciones no incluidas en el array obtienen el estado filtered.

Consultar secciones

// Get all sections with their current state
const sections = renderer.getSections();
sections.forEach((section) => {
  console.log(section.id, section.name, section.isGa, section.disabled, section.filtered);
});

Lista completa de métodos

Method Description
disableSections(ids, options?) Desactivar secciones por IDs numéricos. Pasa { resetAll: true } para reactivar todas antes de desactivar.
enableSections(ids) Reactivar secciones previamente desactivadas.
disableSectionsByNames(names, options?) Desactivar secciones por cadena de nombre.
enableSectionsByNames(names) Reactivar secciones por cadena de nombre.
filterSections(ids, options?) Mostrar solo las secciones especificadas; atenuar todas las demás.
removeFilterSections(ids?) Quitar el filtro de secciones concretas, o de todas si no se pasan IDs.
getSections() Devuelve todas las secciones con su estado actual (id, name, isGa, disabled, filtered).
setOutlineStates(id, states) Bajo nivel: establecer estados concretos en una sección. Pasa null para limpiar. Pasa undefined como id para aplicar a todas.
getOutlineStates(id) Bajo nivel: leer el estado actual de una sección. Devuelve los cuatro campos booleanos.
onSectionStateChange(callback) Registrar un callback que se dispara cuando cambia el estado de cualquier sección. Recibe el ID de la sección.

Migración desde los nombres de métodos heredados

Los nombres de método anteriores *SvgSections* están obsoletos pero siguen funcionando. Actualízalos cuando te resulte conveniente:

Deprecated Replacement
disableSvgSectionsByIds() disableSections()
enableSvgSectionsByIds() enableSections()
disableSvgSectionsByNames() disableSectionsByNames()
enableSvgSectionsByNames() enableSectionsByNames()
filterSvgSectionsByIds() filterSections()
removeFilterSvgSectionsByIds() removeFilterSections()

Los nuevos nombres reflejan que estos métodos funcionan de forma idéntica en todos los tipos de sección, no solo en las secciones vinculadas a SVG.

Al llamar a métodos obsoletos, el renderer registra una sola vez una advertencia en la consola con el reemplazo recomendado. Para suprimir estas advertencias durante la migración:

const renderer = new SeatmapBookingRenderer(element, {
  publicKey: 'your-public-key',
  suppressDeprecationWarnings: true,
});

API de estado de bajo nivel

Para casos de uso avanzados – frameworks de UI personalizados, sincronización de estado externa o aplicación de varios estados simultáneamente – usa directamente la API de estado de bajo nivel.

// Set multiple states on a single section
renderer.setOutlineStates(101, {
  highlighted: true,
  unavailable: true,
});

// Read current state
const state = renderer.getOutlineStates(101);
// Returns: { highlighted: true, selected: false, unavailable: true, filtered: false }

// Clear all states on a section
renderer.setOutlineStates(101, null);

// Clear all states on all sections (pass undefined as id)
renderer.setOutlineStates(undefined, null);

La API de bajo nivel establece estados directamente sin efectos secundarios (sin recálculo de precios, sin actualizaciones del modelo de datos). Usa los métodos de alto nivel (disableSections, filterSections) para los flujos estándar que también actualizan el modelo de datos.

Callback de cambio de estado

Registra un callback para recibir notificaciones cuando cambie el estado de cualquier sección. Es útil para mantener una UI externa (barra lateral, leyenda, tooltip) sincronizada con el estado del renderer.

renderer.onSectionStateChange((sectionId) => {
  const state = renderer.getOutlineStates(sectionId);
  updateSidebar(sectionId, state);
});

El callback se dispara con todos los cambios de estado: paso del ratón, selección, desactivación, filtrado y actualizaciones programáticas.

La opción resetAll

Tanto disableSections como filterSections aceptan un parámetro opcional { resetAll: true }. Cuando se establece, el renderer primero limpia todos los estados de ese tipo aplicados previamente antes de aplicar los nuevos.

Esto es útil para patrones de selección exclusiva, como los botones de filtro por categoría:

// Category filter buttons -- each click shows only that category
document.getElementById('btn-vip').addEventListener('click', () => {
  renderer.filterSections(vipSectionIds, { resetAll: true });
});

document.getElementById('btn-standard').addEventListener('click', () => {
  renderer.filterSections(standardSectionIds, { resetAll: true });
});

document.getElementById('btn-all').addEventListener('click', () => {
  renderer.removeFilterSections();
});

Sin resetAll, tendrías que llamar a removeFilterSections() antes de cada llamada a filterSections(), lo que puede provocar un parpadeo visible.

Paso del ratón y selección de secciones

El resaltado al pasar el ratón y la selección de secciones se habilitan mediante los ajustes del renderer:

const renderer = new SeatmapBookingRenderer(document.getElementById('seatmap'), {
  publicKey: 'your-public-key',
  highlightSectionOnHover: true,
  enableSectionSelection: true,
  onSectionSelect: (section) => {
    console.log('Selected:', section.id, section.name);
    return true; // return true to confirm selection
  },
  onSectionDeselect: (section) => {
    console.log('Deselected:', section.id, section.name);
    return true;
  },
  onSectionMouseEnter: (section) => {
    // Update external UI (tooltip, sidebar, etc.)
  },
  onSectionMouseLeave: () => {
    // Clear external UI
  },
});

Cuando highlightSectionOnHover es true, pasar el ratón sobre una sección aplica el estilo hovered. Cuando enableSectionSelection es true, hacer clic en una sección de Admisión General (GA) alterna el estilo selected y dispara los callbacks de selección/deselección.

Combinaciones de estados

Varios estados pueden estar activos en la misma sección. El renderer aplica CSS con reglas de especificidad:

Combination Visual result
selected + paso del ratón Borde más grueso, opacidad completa
unavailable + paso del ratón Opacidad ligeramente mayor (sigue mostrando no disponible)
selected + unavailable Se muestra con opacidad reducida (unavailable tiene prioridad visual)

Tipos de contorno

El renderer crea contornos de sección a partir de varias fuentes. Los cuatro tipos responden a los estados y estilos de forma idéntica:

Source Origin Attribute
Fondo SVG SVG del plano con vínculos de sección data-outline-source="svg"
Forma Rectángulos, polígonos y trazados creados en el Editor data-outline-source="shape"
Auto Contornos generados por el Editor a partir de grupos de asientos data-outline-source="auto"
Respaldo Generados en tiempo de ejecución a partir de posiciones de asientos data-outline-source="fallback"

No necesitas tratar estos tipos de forma diferente. Todos los métodos públicos de la API funcionan de manera uniforme en todos los tipos. El atributo data-outline-source es un detalle interno expuesto solo para depuración.

Visibilidad de contornos durante la animación

Durante las animaciones de paneo y zoom, la capa SVG de contornos se oculta por defecto por rendimiento. Los contornos reaparecen al completarse la animación. Para mantener los contornos visibles durante las animaciones (a costa de rendimiento), establece:

const renderer = new SeatmapBookingRenderer(element, {
  publicKey: 'your-public-key',
  showOutlineLayerOnAnimation: true,
});

Ejemplo completo

Este ejemplo crea un renderer con estados estilizados, desactiva las secciones agotadas y filtra a un nivel de precio específico al actuar el usuario:

import { SeatmapBookingRenderer } from '@seatmap.pro/renderer';

const renderer = new SeatmapBookingRenderer(document.getElementById('seatmap'), {
  publicKey: 'your-public-key',
  highlightSectionOnHover: true,
  enableSectionSelection: true,
  theme: {
    svgSectionStyles: {
      default: {
        bgColor: 'rgba(100, 100, 100, 0.1)',
        stroke: { color: '#666', width: '1', opacity: 0.8 },
        cursor: 'pointer',
      },
      hovered: {
        bgColor: 'rgba(33, 150, 243, 0.4)',
        stroke: { color: '#1976D2', width: '2', opacity: 1 },
      },
      selected: {
        bgColor: 'rgba(76, 175, 80, 0.5)',
        stroke: { color: '#388E3C', width: '2', opacity: 1 },
      },
      unavailable: {
        bgColor: 'rgba(158, 158, 158, 0.3)',
        stroke: { color: '#9E9E9E', width: '1', opacity: 0.5 },
        cursor: 'not-allowed',
        opacity: 0.4,
      },
      filtered: {
        bgColor: 'rgba(0, 0, 0, 0.08)',
        stroke: { color: '#ccc', width: '1', opacity: 0.3 },
        cursor: 'default',
        opacity: 0.2,
      },
    },
  },
  onSectionSelect: (section) => {
    console.log('Added to cart:', section.name);
    return true;
  },
  onSectionDeselect: (section) => {
    console.log('Removed from cart:', section.name);
    return true;
  },
});

renderer.loadEvent('your-event-id').then(() => {
  // Disable sold-out sections after event loads
  const soldOutIds = [301, 302];
  renderer.disableSections(soldOutIds);
});

// Filter to a price tier when user clicks a button
document.getElementById('filter-premium').addEventListener('click', () => {
  const premiumIds = [101, 102, 103];
  renderer.filterSections(premiumIds);
});

document.getElementById('clear-filter').addEventListener('click', () => {
  renderer.removeFilterSections();
});

Playground

Usa el playground interactivo para probar todos los estados de sección en tiempo real. Carga el preset unifiedSectionStates en /playground/?preset=unifiedSectionStates para ver:

  • Contornos estilizados en todos los tipos de contorno (SVG, forma, respaldo)
  • Botones para desactivar, activar, filtrar, quitar filtro, seleccionar y restablecer
  • Inspector DOM que muestra en vivo los atributos data-*, los estilos calculados y los valores de cursor
  • Registro en consola de todos los eventos de sección