233 lines
8.4 KiB
JavaScript
233 lines
8.4 KiB
JavaScript
(function (ventana, documento, $) {
|
|
"use strict";
|
|
|
|
if (ventana.SIGPanelLateral) {
|
|
return;
|
|
}
|
|
|
|
// Uso: data-sig-panel-abrir="#MiPanel" o $("#MiPanel").panelLateral("abrir").
|
|
// Eventos por panel: abrir.sig.panel, abierto.sig.panel, cerrar.sig.panel, cerrado.sig.panel.
|
|
// Los eventos abrir y cerrar admiten preventDefault(). No se cierran otros paneles.
|
|
var abiertos = [];
|
|
|
|
function resolver(destino) {
|
|
if (typeof destino === "string") {
|
|
destino = documento.getElementById(destino.replace(/^#/, ""));
|
|
}
|
|
if (destino && destino.jquery) {
|
|
destino = destino[0];
|
|
}
|
|
return destino && $(destino).hasClass("sig_panel_lateral") ? destino : null;
|
|
}
|
|
|
|
function instancia(panel) {
|
|
var estado = $.data(panel, "sig.panel");
|
|
if (!estado) {
|
|
estado = {
|
|
panel: panel,
|
|
abierto: false,
|
|
ocupado: false,
|
|
origenFoco: null,
|
|
ubicacion: null,
|
|
fondo: null
|
|
};
|
|
$.data(panel, "sig.panel", estado);
|
|
$(panel).attr("data-sig-panel-initialized", "1");
|
|
if (!panel.hasAttribute("tabindex")) {
|
|
panel.setAttribute("tabindex", "-1");
|
|
}
|
|
}
|
|
return estado;
|
|
}
|
|
|
|
function ultimoAbierto() {
|
|
return abiertos.length ? abiertos[abiertos.length - 1] : null;
|
|
}
|
|
|
|
function actualizarPagina() {
|
|
$(documento.body).toggleClass("sig_panel_activo", abiertos.length > 0);
|
|
}
|
|
|
|
function abrir(destino, origen) {
|
|
var panel = resolver(destino);
|
|
if (!panel) {
|
|
return;
|
|
}
|
|
var estado = instancia(panel);
|
|
if (estado.ocupado) {
|
|
return;
|
|
}
|
|
if (estado.abierto) {
|
|
// Elevar en el orden del DOM conserva el mismo z-index para todos.
|
|
abiertos = abiertos.filter(function (actual) { return actual !== estado; });
|
|
abiertos.push(estado);
|
|
panel.parentNode.appendChild(panel);
|
|
panel.focus();
|
|
return;
|
|
}
|
|
|
|
estado.ocupado = true;
|
|
var evento = $.Event("abrir.sig.panel", { relatedTarget: origen || null });
|
|
$(panel).trigger(evento);
|
|
if (evento.isDefaultPrevented()) {
|
|
estado.ocupado = false;
|
|
return;
|
|
}
|
|
|
|
estado.origenFoco = origen || documento.activeElement;
|
|
// Conserva el formulario WebForms y evita recortes o contextos de apilado de sus ancestros.
|
|
// Al cerrar se devuelve exactamente al lugar donde fue declarado.
|
|
estado.ubicacion = documento.createComment("Ubicación del panel lateral");
|
|
panel.parentNode.insertBefore(estado.ubicacion, panel);
|
|
var contenedor = $(panel).closest("form")[0] || documento.body;
|
|
if ($(panel).attr("data-sig-panel-fondo") !== "false") {
|
|
estado.fondo = $("<div>", {
|
|
"class": "sig_panel_fondo",
|
|
"aria-hidden": "true"
|
|
})[0];
|
|
$.data(estado.fondo, "sig.panel.propietario", panel);
|
|
contenedor.appendChild(estado.fondo);
|
|
}
|
|
contenedor.appendChild(panel);
|
|
estado.abierto = true;
|
|
abiertos.push(estado);
|
|
$(panel).addClass("sig_panel_open").attr("aria-hidden", "false");
|
|
actualizarPagina();
|
|
panel.focus();
|
|
estado.ocupado = false;
|
|
$(panel).trigger($.Event("abierto.sig.panel", { relatedTarget: origen || null }));
|
|
}
|
|
|
|
function retirar(estado) {
|
|
abiertos = abiertos.filter(function (actual) { return actual !== estado; });
|
|
if (estado.fondo) {
|
|
$(estado.fondo).remove();
|
|
estado.fondo = null;
|
|
}
|
|
estado.abierto = false;
|
|
actualizarPagina();
|
|
}
|
|
|
|
function cerrar(destino) {
|
|
var panel = resolver(destino);
|
|
var estado = panel ? $.data(panel, "sig.panel") : null;
|
|
if (!estado || !estado.abierto || estado.ocupado) {
|
|
return;
|
|
}
|
|
estado.ocupado = true;
|
|
var evento = $.Event("cerrar.sig.panel");
|
|
$(panel).trigger(evento);
|
|
if (evento.isDefaultPrevented()) {
|
|
estado.ocupado = false;
|
|
return;
|
|
}
|
|
|
|
var teniaFoco = ultimoAbierto() === estado;
|
|
$(panel).removeClass("sig_panel_open").attr("aria-hidden", "true");
|
|
if (estado.ubicacion && estado.ubicacion.parentNode) {
|
|
estado.ubicacion.parentNode.replaceChild(panel, estado.ubicacion);
|
|
}
|
|
estado.ubicacion = null;
|
|
retirar(estado);
|
|
if (teniaFoco) {
|
|
var siguiente = ultimoAbierto();
|
|
if (siguiente) {
|
|
siguiente.panel.focus();
|
|
} else if (estado.origenFoco && documento.documentElement.contains(estado.origenFoco)) {
|
|
estado.origenFoco.focus();
|
|
}
|
|
}
|
|
estado.ocupado = false;
|
|
$(panel).trigger("cerrado.sig.panel");
|
|
}
|
|
|
|
ventana.SIGPanelLateral = {
|
|
abrir: abrir,
|
|
cerrar: cerrar
|
|
};
|
|
|
|
$.fn.panelLateral = function (accion) {
|
|
return this.each(function () {
|
|
if (accion === "cerrar") {
|
|
cerrar(this);
|
|
} else if (accion === "abrir" || !accion) {
|
|
abrir(this);
|
|
}
|
|
});
|
|
};
|
|
|
|
// Delegación única: los botones nuevos de un UpdatePanel funcionan sin reinicialización.
|
|
$(documento).on("click.sigPanelLateral", "[data-sig-panel-abrir]", function (evento) {
|
|
var destino = resolver($(this).attr("data-sig-panel-abrir"));
|
|
if (destino) {
|
|
evento.preventDefault();
|
|
abrir(destino, this);
|
|
}
|
|
});
|
|
$(documento).on("click.sigPanelLateral", "[data-sig-panel-cerrar]", function (evento) {
|
|
var destino = $(this).attr("data-sig-panel-cerrar") || $(this).closest(".sig_panel_lateral");
|
|
if (resolver(destino)) {
|
|
evento.preventDefault();
|
|
cerrar(destino);
|
|
}
|
|
});
|
|
$(documento).on("click.sigPanelLateral", ".sig_panel_fondo", function () {
|
|
cerrar($.data(this, "sig.panel.propietario"));
|
|
});
|
|
$(documento).on("keydown.sigPanelLateral", function (evento) {
|
|
var estado = ultimoAbierto();
|
|
if (!estado || $(".modal.in:visible, .sweet-alert.visible, .swal2-container:visible, .select2-container--open, .select2-drop-active:visible").length) {
|
|
return;
|
|
}
|
|
if (evento.key === "Escape") {
|
|
evento.preventDefault();
|
|
cerrar(estado.panel);
|
|
return;
|
|
}
|
|
if (evento.key !== "Tab") {
|
|
return;
|
|
}
|
|
|
|
var controles = $(estado.panel).find("a[href], button, input, select, textarea, [tabindex]")
|
|
.filter(":visible").filter(function () {
|
|
return !this.disabled && $(this).attr("tabindex") !== "-1";
|
|
});
|
|
var primero = controles[0];
|
|
var ultimo = controles[controles.length - 1];
|
|
var dentro = estado.panel.contains(documento.activeElement);
|
|
if (!primero) {
|
|
evento.preventDefault();
|
|
estado.panel.focus();
|
|
} else if (evento.shiftKey && (documento.activeElement === primero ||
|
|
documento.activeElement === estado.panel || !dentro)) {
|
|
evento.preventDefault();
|
|
ultimo.focus();
|
|
} else if (!evento.shiftKey && (documento.activeElement === ultimo || !dentro)) {
|
|
evento.preventDefault();
|
|
primero.focus();
|
|
}
|
|
});
|
|
|
|
// Si un UpdatePanel sustituye la ubicación original, libera la instancia antigua.
|
|
// No agrega otro puente a PageRequestManager ni conserva paneles huérfanos en el formulario.
|
|
var observador = new MutationObserver(function () {
|
|
abiertos.slice().forEach(function (estado) {
|
|
if (documento.documentElement.contains(estado.ubicacion) &&
|
|
documento.documentElement.contains(estado.panel)) {
|
|
return;
|
|
}
|
|
var identificador = estado.panel.id;
|
|
retirar(estado);
|
|
$(estado.panel).remove();
|
|
if (estado.ubicacion && estado.ubicacion.parentNode) {
|
|
estado.ubicacion.parentNode.removeChild(estado.ubicacion);
|
|
}
|
|
var reemplazo = identificador ? documento.getElementById(identificador) : null;
|
|
if (reemplazo) {
|
|
abrir(reemplazo);
|
|
}
|
|
});
|
|
});
|
|
observador.observe(documento.documentElement, { childList: true, subtree: true });
|
|
}(window, document, jQuery));
|