Files
Jorge Luis caudillo esparza b7c80baadc Ajustes Lotes
2026-10-02 00:20:26 -07:00

1031 lines
20 KiB
JavaScript

(function (window, $) {
'use strict';
if (!$) {
console.error('ScrollSig requiere jQuery.');
return;
}
var SCROLL_RENDERED = 'scrollsigrendered';
var SCROLL_CONTAINER = 'scrollsig-container';
var SCROLL_STICKY = 'scrollsig-sticky';
/**
* ============================================================
* RENDERIZAR SCROLL SIG
* ============================================================
*
* Busca:
*
* table.scrollsig
*
* que todavía no tengan:
*
* scrollsigrendered
*/
window.RenderizarScrollSig = function () {
$('table.scrollsig')
.not('.' + SCROLL_RENDERED)
.each(function () {
var $grid = $(this);
renderizarGrid($grid);
});
};
/**
* ============================================================
* RENDERIZAR UN GRID
* ============================================================
*/
function renderizarGrid($grid) {
/*
* --------------------------------------------------------
* 1. EJECUTAR FUNCIÓN PREVIA
* --------------------------------------------------------
*
* Ejemplo:
*
* data-scrollfuncion="PrepararGrid"
*
* function PrepararGrid(grid) {
* }
*/
ejecutarFuncionPrevia($grid);
/*
* --------------------------------------------------------
* 2. CREAR CONTENEDOR
* --------------------------------------------------------
*/
crearContenedor($grid);
/*
* --------------------------------------------------------
* 3. CONFIGURAR TAMAÑO
* --------------------------------------------------------
*/
configurarTamano($grid);
/*
* --------------------------------------------------------
* 4. CONFIGURAR FILAS STICKY
* --------------------------------------------------------
*/
configurarFilasSticky($grid);
/*
* --------------------------------------------------------
* 5. MARCAR COMO RENDERIZADO
* --------------------------------------------------------
*/
$grid.addClass(
SCROLL_RENDERED
);
}
/**
* ============================================================
* FUNCIÓN PREVIA
* ============================================================
*
* data-scrollfuncion="MiFuncion"
*
* Se busca:
*
* window.MiFuncion
*
* y se ejecuta:
*
* MiFuncion(grid);
*/
function ejecutarFuncionPrevia($grid) {
var nombreFuncion =
$.trim(
$grid.attr('data-scrollfuncion') || ''
);
if (!nombreFuncion) {
return;
}
var funcion =
obtenerFuncionGlobal(nombreFuncion);
if (typeof funcion !== 'function') {
console.warn(
'ScrollSig: no se encontró la función "' +
nombreFuncion +
'".'
);
return;
}
/*
* Mandamos el elemento TABLE real.
*/
funcion(
$grid[0]
);
}
/**
* Permite incluso nombres como:
*
* MiFuncion
*
* MiModulo.PrepararGrid
*/
function obtenerFuncionGlobal(nombre) {
var partes =
nombre.split('.');
var contexto =
window;
for (
var i = 0;
i < partes.length;
i++
) {
if (
contexto === null ||
contexto === undefined
) {
return null;
}
contexto =
contexto[partes[i]];
}
return contexto;
}
/**
* ============================================================
* CREAR CONTENEDOR
* ============================================================
*
* Antes:
*
* <table class="scrollsig">
*
*
* Después:
*
* <div class="scrollsig-container">
*
* <table class="scrollsig scrollsigrendered">
*
* </div>
*/
function crearContenedor($grid) {
/*
* Protección adicional.
*
* Si por alguna razón ya está dentro
* del contenedor no volvemos a envolverlo.
*/
if (
$grid.parent().hasClass(
SCROLL_CONTAINER
)
) {
return;
}
$grid.wrap(
$('<div>', {
'class': SCROLL_CONTAINER
})
);
}
/**
* ============================================================
* CONFIGURAR TAMAÑO
* ============================================================
*
* PRIORIDAD:
*
* 1. data-scrollmargintop
*
* 2. data-scrollsize
*
* 3. 300px
*/
function configurarTamano($grid) {
var $container =
obtenerContenedor($grid);
if (!$container.length) {
return;
}
var marginTop =
$.trim(
$grid.attr(
'data-scrollmargintop'
) || ''
);
var scrollSize =
$.trim(
$grid.attr(
'data-scrollsize'
) || ''
);
/*
* --------------------------------------------------------
* MARGIN TOP
* --------------------------------------------------------
*
* Ejemplo:
*
* data-scrollmargintop="250"
*
* viewport:
*
* 900px
*
* resultado:
*
* 650px
*/
if (marginTop !== '') {
var margen =
obtenerNumero(
marginTop
);
if (margen !== null) {
$container.attr(
'data-scrollsig-mode',
'margin'
);
$container.attr(
'data-scrollsig-margin',
margen
);
aplicarTamanoPorMargen(
$container,
margen
);
return;
}
}
/*
* --------------------------------------------------------
* SIZE
* --------------------------------------------------------
*
* Ejemplos:
*
* data-scrollsize="500px"
*
* data-scrollsize="50vh"
*
* data-scrollsize="30rem"
*
* data-scrollsize="400"
*/
if (scrollSize !== '') {
scrollSize =
normalizarTamano(
scrollSize
);
$container.attr(
'data-scrollsig-mode',
'size'
);
$container.css(
'max-height',
scrollSize
);
return;
}
/*
* --------------------------------------------------------
* DEFAULT
* --------------------------------------------------------
*/
$container.attr(
'data-scrollsig-mode',
'default'
);
$container.css(
'max-height',
'300px'
);
}
/**
* ============================================================
* TAMAÑO POR MARGEN
* ============================================================
*/
function aplicarTamanoPorMargen(
$container,
margen
) {
var altura =
window.innerHeight -
margen;
/*
* Protección para evitar tamaños negativos
* o absurdamente pequeños.
*/
if (altura < 50) {
altura = 50;
}
$container.css(
'max-height',
altura + 'px'
);
}
/**
* ============================================================
* NORMALIZAR TAMAÑO
* ============================================================
*
* 400
*
* se convierte en:
*
* 400px
*
*
* 400px
* 50vh
* 20rem
*
* se conservan.
*/
function normalizarTamano(valor) {
valor =
$.trim(
String(valor)
);
if (
/^\d+(\.\d+)?$/.test(
valor
)
) {
return valor + 'px';
}
return valor;
}
/**
* ============================================================
* OBTENER NÚMERO
* ============================================================
*
* Acepta:
*
* 250
* 250px
*/
function obtenerNumero(valor) {
if (
valor === null ||
valor === undefined
) {
return null;
}
valor =
String(valor)
.replace('px', '')
.trim();
var numero =
parseFloat(
valor
);
if (isNaN(numero)) {
return null;
}
return numero;
}
/**
* ============================================================
* FILAS STICKY
* ============================================================
*
* data-scrollrowsheader="1"
*
* Por defecto:
*
* 1
*
*
* Si:
*
* data-scrollrowsheader="3"
*
* las primeras 3 filas serán sticky.
*
*
* IMPORTANTE:
*
* El sticky se aplica EXCLUSIVAMENTE al TR.
*
* NO se aplica:
*
* position: sticky
* top
* z-index
*
* a TH ni TD.
*/
function configurarFilasSticky($grid) {
/*
* Primero limpiamos cualquier configuración
* anterior por seguridad.
*/
limpiarSticky($grid);
var cantidad =
parseInt(
$grid.attr(
'data-scrollrowsheader'
),
10
);
/*
* Por defecto la primera fila.
*/
if (
isNaN(cantidad)
) {
cantidad = 1;
}
/*
* Permitir explícitamente:
*
* data-scrollrowsheader="0"
*
* para no tener sticky.
*/
if (cantidad <= 0) {
return;
}
/*
* Tomamos las primeras N filas
* reales de la tabla.
*/
var $filas =
obtenerFilasSuperiores(
$grid,
cantidad
);
if (!$filas.length) {
return;
}
/*
* Empezamos en -1px.
*
* Esto desplaza todo el bloque sticky
* 1px hacia arriba.
*
* Evita la pequeña línea transparente
* que puede aparecer encima del header.
*
* Si tenemos varias filas sticky:
*
* fila 1:
*
* top = -1
*
* fila 2:
*
* top = altura fila 1 - 1
*
* fila 3:
*
* top = altura fila 1
* + altura fila 2
* - 1
*/
var topAcumulado = -1;
$filas.each(function (indice) {
var $fila =
$(this);
/*
* IMPORTANTE:
*
* Toda la configuración sticky
* se aplica directamente al TR.
*/
$fila
.addClass(
SCROLL_STICKY
)
.css({
'top': topAcumulado + 'px',
'z-index': 3 - indice
});
/*
* Obtener altura REAL de la fila.
*
* Se utiliza para posicionar la
* siguiente fila sticky.
*/
var altura =
$fila.outerHeight();
if (
!altura ||
altura < 1
) {
altura = 0;
}
topAcumulado +=
altura;
});
}
/**
* ============================================================
* OBTENER PRIMERAS N FILAS
* ============================================================
*
* Preferimos THEAD.
*
* Si el GridView no genera THEAD,
* usamos las primeras filas de la tabla.
*/
function obtenerFilasSuperiores(
$grid,
cantidad
) {
var $filasThead =
$grid.find(
'> thead > tr'
);
/*
* Si THEAD tiene suficientes filas,
* utilizamos esas.
*/
if ($filasThead.length) {
var $resultado =
$filasThead.slice(
0,
cantidad
);
/*
* Si necesitamos más filas que las
* disponibles en THEAD, completamos
* con TBODY.
*/
if (
$resultado.length <
cantidad
) {
var faltantes =
cantidad -
$resultado.length;
var $tbody =
$grid
.find(
'> tbody > tr'
)
.slice(
0,
faltantes
);
$resultado =
$resultado.add(
$tbody
);
}
return $resultado;
}
/*
* GridView sin THEAD.
*/
return $grid
.find('> tbody > tr')
.slice(
0,
cantidad
);
}
/**
* ============================================================
* LIMPIAR STICKY
* ============================================================
*
* Ahora solamente limpiamos el TR.
*
* Ya NO existe:
*
* scrollsig-sticky-cell
*/
function limpiarSticky($grid) {
$grid
.find('.' + SCROLL_STICKY)
.removeClass(
SCROLL_STICKY
)
.css({
'top': '',
'z-index': ''
});
}
/**
* ============================================================
* OBTENER CONTENEDOR
* ============================================================
*/
function obtenerContenedor($grid) {
return $grid.closest(
'.' + SCROLL_CONTAINER
);
}
/**
* ============================================================
* RECALCULAR SCROLLSIG
* ============================================================
*
* Método público por si en algún momento
* necesitas recalcular manualmente:
*
* RecalcularScrollSig();
*
* o:
*
* RecalcularScrollSig('GvTramites');
*/
window.RecalcularScrollSig = function (
grid
) {
/*
* Un Grid específico.
*/
if (grid) {
var $grid =
obtenerGrid(
grid
);
if (!$grid.length) {
return;
}
recalcularGrid(
$grid
);
return;
}
/*
* Todos los ScrollSig.
*/
$('table.scrollsig.' + SCROLL_RENDERED)
.each(function () {
recalcularGrid(
$(this)
);
});
};
/**
* ============================================================
* RECALCULAR GRID
* ============================================================
*/
function recalcularGrid($grid) {
var $container =
obtenerContenedor(
$grid
);
if (!$container.length) {
return;
}
/*
* Si usa margin top recalculamos
* contra window.innerHeight.
*/
if (
$container.attr(
'data-scrollsig-mode'
) === 'margin'
) {
var margen =
parseFloat(
$container.attr(
'data-scrollsig-margin'
)
);
if (!isNaN(margen)) {
aplicarTamanoPorMargen(
$container,
margen
);
}
}
/*
* Recalcular TOP de sticky rows.
*
* Esto es importante si alguna función
* cambió la altura de los headers.
*/
configurarFilasSticky(
$grid
);
}
/**
* ============================================================
* OBTENER GRID
* ============================================================
*/
function obtenerGrid(grid) {
if (grid instanceof jQuery) {
return grid;
}
if (
typeof HTMLElement !== 'undefined' &&
grid instanceof HTMLElement
) {
return $(grid);
}
if (
typeof grid === 'string'
) {
if (
grid.charAt(0) === '#'
) {
return $(grid);
}
return $(
'#' + grid
);
}
return $();
}
/**
* ============================================================
* RESIZE DE VENTANA
* ============================================================
*
* No ejecutamos el cálculo inmediatamente en cada pixel
* que cambia el navegador.
*
* Esperamos 100 ms para evitar trabajo excesivo.
*/
var timeoutResize = null;
$(window).on(
'resize.scrollsig',
function () {
if (
timeoutResize !== null
) {
clearTimeout(
timeoutResize
);
}
timeoutResize =
setTimeout(
function () {
RecalcularScrollSig();
timeoutResize = null;
},
100
);
}
);
/**
* ============================================================
* PRIMERA CARGA
* ============================================================
*/
$(function () {
RenderizarScrollSig();
});
/**
* ============================================================
* ASP.NET WEBFORMS / UPDATEPANEL
* ============================================================
*
* Si un UpdatePanel reemplaza el GridView:
*
* el nuevo TABLE ya no tendrá:
*
* scrollsigrendered
*
* por lo tanto se procesará nuevamente.
*/
if (
typeof Sys !== 'undefined' &&
Sys.WebForms &&
Sys.WebForms.PageRequestManager
) {
Sys.WebForms.PageRequestManager
.getInstance()
.add_endRequest(
function () {
RenderizarScrollSig();
/*
* Recalculamos también los grids que
* sobrevivieron al postback parcial.
*/
RecalcularScrollSig();
}
);
}
})(window, window.jQuery);