update
This commit is contained in:
+85
-77
@@ -5,7 +5,7 @@
|
||||
* @property {boolean} DEBUG - Habilita/deshabilita el modo de depuración.
|
||||
*/
|
||||
window.COURSE_CONFIG = {
|
||||
COURSE_CONFIG_URL: 'config.json',
|
||||
COURSE_CONFIG_URL: "config.json",
|
||||
DEBUG: true,
|
||||
};
|
||||
|
||||
@@ -19,8 +19,8 @@ window.COURSE_CONFIG = {
|
||||
* renderPagination(2, courseData.contentArray);
|
||||
*/
|
||||
function renderPagination(currentIndex, contentArray) {
|
||||
const pageNumber = document.getElementById('coursenav-page-number');
|
||||
const totalPages = document.getElementById('coursenav-total-pages');
|
||||
const pageNumber = document.getElementById("coursenav-page-number");
|
||||
const totalPages = document.getElementById("coursenav-total-pages");
|
||||
|
||||
if (!Array.isArray(contentArray)) return;
|
||||
// Gestionar visibilidad de menús primero
|
||||
@@ -30,13 +30,17 @@ function renderPagination(currentIndex, contentArray) {
|
||||
if (!currentSlide) return;
|
||||
|
||||
// Filtrar todos los slides del mismo módulo
|
||||
const moduleSlides = contentArray.filter((slide) => slide.moduleTitle === currentSlide.moduleTitle);
|
||||
const moduleSlides = contentArray.filter(
|
||||
(slide) => slide.moduleTitle === currentSlide.moduleTitle
|
||||
);
|
||||
|
||||
// Encontrar la posición del slide actual dentro del módulo
|
||||
const moduleSlideIndex = moduleSlides.findIndex((slide) => slide.content === currentSlide.content);
|
||||
const moduleSlideIndex = moduleSlides.findIndex(
|
||||
(slide) => slide.content === currentSlide.content
|
||||
);
|
||||
|
||||
if (pageNumber) pageNumber.textContent = moduleSlideIndex + 1;
|
||||
if (totalPages) totalPages.textContent = ' / ' + moduleSlides.length;
|
||||
if (totalPages) totalPages.textContent = " / " + moduleSlides.length;
|
||||
//Navegación personalizada
|
||||
updateNavButtons(moduleSlideIndex, moduleSlides);
|
||||
}
|
||||
@@ -54,8 +58,8 @@ function renderPagination(currentIndex, contentArray) {
|
||||
* @param {Array<Object>} moduleSlides - Un array de los objetos de diapositiva que pertenecen al módulo actual.
|
||||
*/
|
||||
function updateNavButtons(moduleSlideIndex, moduleSlides) {
|
||||
const nextBtn = document.getElementById('coursenav-next-btn');
|
||||
const prevBtn = document.getElementById('coursenav-prev-btn');
|
||||
const nextBtn = document.getElementById("coursenav-next-btn");
|
||||
const prevBtn = document.getElementById("coursenav-prev-btn");
|
||||
|
||||
if (!nextBtn || !prevBtn) return;
|
||||
|
||||
@@ -63,8 +67,8 @@ function updateNavButtons(moduleSlideIndex, moduleSlides) {
|
||||
const isFirstSlide = moduleSlideIndex === 0;
|
||||
const isDebugMode = CourseNav.isDebug();
|
||||
|
||||
nextBtn.classList.toggle('disabled', isLastSlide && !isDebugMode);
|
||||
prevBtn.classList.toggle('disabled', isFirstSlide && !isDebugMode);
|
||||
nextBtn.classList.toggle("disabled", isLastSlide && !isDebugMode);
|
||||
prevBtn.classList.toggle("disabled", isFirstSlide && !isDebugMode);
|
||||
}
|
||||
/**
|
||||
* Gestiona la visibilidad de los menús del curso en la barra de navegación.
|
||||
@@ -80,16 +84,16 @@ function updateNavButtons(moduleSlideIndex, moduleSlides) {
|
||||
* @param {Array<Object>} contentArray - El array completo de objetos de slide del curso.
|
||||
*/
|
||||
function manageMenuVisibility(currentIndex, contentArray) {
|
||||
const courseMenus = document.querySelectorAll('#coursenav-main-menu > ul.course-menu');
|
||||
const courseMenus = document.querySelectorAll("#coursenav-main-menu > ul.course-menu");
|
||||
if (!courseMenus.length) return;
|
||||
|
||||
// El primer menú siempre está visible.
|
||||
courseMenus[0].style.display = 'block';
|
||||
courseMenus[0].style.display = "block";
|
||||
|
||||
// Si no hay contenido, ocultar los demás menús.
|
||||
if (!Array.isArray(contentArray) || !contentArray.length) {
|
||||
for (let i = 1; i < courseMenus.length; i++) {
|
||||
courseMenus[i].style.display = 'none';
|
||||
courseMenus[i].style.display = "none";
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -98,7 +102,7 @@ function manageMenuVisibility(currentIndex, contentArray) {
|
||||
// Si el slide actual no existe o no tiene un título de módulo, ocultar los demás.
|
||||
if (!currentSlide?.moduleTitle) {
|
||||
for (let i = 1; i < courseMenus.length; i++) {
|
||||
courseMenus[i].style.display = 'none';
|
||||
courseMenus[i].style.display = "none";
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -106,12 +110,12 @@ function manageMenuVisibility(currentIndex, contentArray) {
|
||||
const currentModuleTitle = currentSlide.moduleTitle;
|
||||
|
||||
for (let i = 1; i < courseMenus.length; i++) {
|
||||
const menuItems = Array.from(courseMenus[i].querySelectorAll('.coursenav-link'));
|
||||
const menuItems = Array.from(courseMenus[i].querySelectorAll(".coursenav-link"));
|
||||
const shouldShow = menuItems.some((item) => {
|
||||
const itemIndex = parseInt(item.dataset.coursenavindex);
|
||||
return itemIndex >= 0 && contentArray[itemIndex]?.moduleTitle === currentModuleTitle;
|
||||
});
|
||||
courseMenus[i].style.display = shouldShow ? 'block' : 'none';
|
||||
courseMenus[i].style.display = shouldShow ? "block" : "none";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +132,7 @@ function manageMenuVisibility(currentIndex, contentArray) {
|
||||
* scrollToElementTop('#main-content', { behavior: 'smooth', block: 'start' });
|
||||
*/
|
||||
function scrollToElementTop(selector, options = {}) {
|
||||
const defaults = { behavior: 'smooth', block: 'start', inline: 'nearest' };
|
||||
const defaults = { behavior: "smooth", block: "start", inline: "nearest" };
|
||||
const opts = Object.assign(defaults, options);
|
||||
const el = document.querySelector(selector);
|
||||
if (el) el.scrollIntoView(opts);
|
||||
@@ -149,7 +153,7 @@ function scrollToElementTop(selector, options = {}) {
|
||||
* animateOnScroll('.animar', 'animate__fadeIn', { threshold: 0.2 });
|
||||
*/
|
||||
function animateOnScroll(selector, animationClass, options = {}) {
|
||||
const { threshold = 0.1, animateOnce = true, prefix = 'animate__animated' } = options;
|
||||
const { threshold = 0.1, animateOnce = true, prefix = "animate__animated" } = options;
|
||||
|
||||
const cb = (entries, observer) => {
|
||||
entries.forEach((entry) => {
|
||||
@@ -168,37 +172,37 @@ function animateOnScroll(selector, animationClass, options = {}) {
|
||||
}
|
||||
|
||||
function scaleWrapCourseContent() {
|
||||
const content = document.querySelector('.wrap-course-content');
|
||||
const content = document.querySelector(".wrap-course-content");
|
||||
if (!content) return;
|
||||
|
||||
const ww = window.innerWidth;
|
||||
const wh = window.innerHeight;
|
||||
|
||||
// Breakpoint a 1500px
|
||||
let baseWidth, baseHeight;
|
||||
let baseWidth = ww < 1500 ? 1366 : 1920;
|
||||
let baseHeight = ww < 1500 ? 768 : 1080;
|
||||
|
||||
if (ww < 1500) {
|
||||
// Menor a 1500px: usar 1366x768
|
||||
baseWidth = 1366;
|
||||
baseHeight = 768;
|
||||
} else {
|
||||
// 1500px o más: usar 1920x1080
|
||||
baseWidth = 1920;
|
||||
baseHeight = 1080;
|
||||
const scaleX = ww / baseWidth;
|
||||
const scaleY = wh / baseHeight;
|
||||
|
||||
// Prioriza ancho, ajusta si rompe altura
|
||||
let scale = scaleX;
|
||||
if (baseHeight * scaleX > wh) {
|
||||
scale = scaleY;
|
||||
}
|
||||
|
||||
// Escala simple
|
||||
const scale = Math.min(ww / baseWidth, wh / baseHeight);
|
||||
// Limitar reducción excesiva
|
||||
const minScale = 0.8;
|
||||
scale = Math.max(minScale, scale);
|
||||
|
||||
content.style.transform = `scale(${scale})`;
|
||||
content.style.transformOrigin = 'top left';
|
||||
content.style.width = baseWidth + 'px';
|
||||
content.style.height = baseHeight + 'px';
|
||||
content.style.position = 'absolute';
|
||||
content.style.left = (ww - baseWidth * scale) / 2 + 'px';
|
||||
content.style.top = (wh - baseHeight * scale) / 2 + 'px';
|
||||
content.style.overflow = 'hidden';
|
||||
content.style.zIndex = '1';
|
||||
content.style.transformOrigin = "top left";
|
||||
content.style.width = baseWidth + "px";
|
||||
content.style.height = baseHeight + "px";
|
||||
content.style.position = "absolute";
|
||||
content.style.left = (ww - baseWidth * scale) / 2 + "px";
|
||||
content.style.top = (wh - baseHeight * scale) / 2 + "px";
|
||||
content.style.overflow = "hidden";
|
||||
content.style.zIndex = "1";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,14 +219,14 @@ function scaleWrapCourseContent() {
|
||||
*/
|
||||
function renderStepper(stepperEl, progressPercent, mobileEl, stepsCount = 5) {
|
||||
// Validación y obtención de elementos
|
||||
if (typeof stepperEl === 'string') stepperEl = document.querySelector(stepperEl);
|
||||
if (typeof mobileEl === 'string') mobileEl = document.querySelector(mobileEl);
|
||||
if (typeof stepperEl === "string") stepperEl = document.querySelector(stepperEl);
|
||||
if (typeof mobileEl === "string") mobileEl = document.querySelector(mobileEl);
|
||||
if (!(stepperEl instanceof HTMLElement) || !(mobileEl instanceof HTMLElement)) {
|
||||
throw new Error('renderStepper: elementos inválidos.');
|
||||
throw new Error("renderStepper: elementos inválidos.");
|
||||
}
|
||||
|
||||
// Limpiar contenido previo
|
||||
stepperEl.querySelectorAll('.step').forEach((el) => el.remove());
|
||||
stepperEl.querySelectorAll(".step").forEach((el) => el.remove());
|
||||
|
||||
// Calcular posiciones de los pasos
|
||||
const stepPercents = Array.from({ length: stepsCount }, (_, i) => (i / (stepsCount - 1)) * 100);
|
||||
@@ -235,22 +239,22 @@ function renderStepper(stepperEl, progressPercent, mobileEl, stepsCount = 5) {
|
||||
|
||||
// Crear elementos de los pasos
|
||||
stepPercents.forEach((pct, i) => {
|
||||
const step = document.createElement('div');
|
||||
step.classList.add('step');
|
||||
if (i < currentIndex) step.classList.add('completed');
|
||||
const step = document.createElement("div");
|
||||
step.classList.add("step");
|
||||
if (i < currentIndex) step.classList.add("completed");
|
||||
if (i === currentIndex) {
|
||||
step.classList.add('completed');
|
||||
step.setAttribute('data-label', Math.round(pct) + '%');
|
||||
step.classList.add("completed");
|
||||
step.setAttribute("data-label", Math.round(pct) + "%");
|
||||
}
|
||||
stepperEl.appendChild(step);
|
||||
});
|
||||
|
||||
// Actualizar estilos y posición
|
||||
stepperEl.style.setProperty('--pct', progressPercent + '%');
|
||||
stepperEl.style.setProperty("--pct", progressPercent + "%");
|
||||
|
||||
const halfMobile = mobileEl.offsetWidth;
|
||||
mobileEl.style.left = `calc(${progressPercent}% - ${halfMobile}px)`;
|
||||
mobileEl.setAttribute('data-label', progressPercent + '%');
|
||||
mobileEl.setAttribute("data-label", progressPercent + "%");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,7 +270,7 @@ function renderStepper(stepperEl, progressPercent, mobileEl, stepsCount = 5) {
|
||||
*/
|
||||
function gotoFirstMenuToolSlide() {
|
||||
const contentArray = CourseNav.getCourseContentArray();
|
||||
const targetTitle = 'Menús de la herramienta';
|
||||
const targetTitle = "Menús de la herramienta";
|
||||
const currentSlide = CourseNav.getCurrentSlide();
|
||||
// Filtrar todos los slides del mismo módulo
|
||||
const targetIndex = contentArray.findIndex(
|
||||
@@ -284,25 +288,25 @@ function gotoFirstMenuToolSlide() {
|
||||
* Configura los event listeners cuando el DOM está completamente cargado.
|
||||
* @event DOMContentLoaded
|
||||
*/
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Escalado inicial y continuo
|
||||
scaleWrapCourseContent();
|
||||
setTimeout(scaleWrapCourseContent, 100);
|
||||
|
||||
// Escalado en redimensionamiento con debounce
|
||||
let resizeTimeout;
|
||||
window.addEventListener('resize', () => {
|
||||
window.addEventListener("resize", () => {
|
||||
clearTimeout(resizeTimeout);
|
||||
resizeTimeout = setTimeout(scaleWrapCourseContent, 50);
|
||||
});
|
||||
|
||||
// Escalado en cambio de orientación (móviles/tablets)
|
||||
window.addEventListener('orientationchange', () => {
|
||||
window.addEventListener("orientationchange", () => {
|
||||
setTimeout(scaleWrapCourseContent, 200);
|
||||
});
|
||||
|
||||
// Escalado cuando cambia la visibilidad (cambio de pestaña)
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (!document.hidden) {
|
||||
setTimeout(scaleWrapCourseContent, 100);
|
||||
}
|
||||
@@ -315,8 +319,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
* @property {number} detail.currentIndex - Índice del slide actual.
|
||||
* @property {Array} detail.contentArray - Array completo de contenido.
|
||||
*/
|
||||
document.body.addEventListener('beforeSlideChange', (e) => {
|
||||
console.log('Antes de cambiar de slide:', e.detail);
|
||||
document.body.addEventListener("beforeSlideChange", (e) => {
|
||||
console.log("Antes de cambiar de slide:", e.detail);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -326,36 +330,40 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
* @property {number} detail.slideIndex - Índice del nuevo slide.
|
||||
* @property {Array} detail.contentArray - Array completo de contenido.
|
||||
*/
|
||||
document.body.addEventListener('slideChange', (e) => {
|
||||
if (e.detail && typeof e.detail.slideIndex === 'number' && Array.isArray(e.detail.contentArray)) {
|
||||
document.body.addEventListener("slideChange", (e) => {
|
||||
if (
|
||||
e.detail &&
|
||||
typeof e.detail.slideIndex === "number" &&
|
||||
Array.isArray(e.detail.contentArray)
|
||||
) {
|
||||
renderPagination(e.detail.slideIndex, e.detail.contentArray);
|
||||
console.log(e.detail.contentArray[e.detail.slideIndex].content);
|
||||
const contentArray = e.detail.contentArray;
|
||||
const targetTitle = 'Menús de la herramienta';
|
||||
const targetTitle = "Menús de la herramienta";
|
||||
const currentSlide = CourseNav.getCurrentSlide();
|
||||
// Filtrar todos los slides del mismo módulo
|
||||
const targetIndex = contentArray.findIndex(
|
||||
(slide) => slide.title === targetTitle && slide.moduleTitle === currentSlide.moduleTitle
|
||||
);
|
||||
const btn = document.getElementById('coursenav-other-btn');
|
||||
const btn = document.getElementById("coursenav-other-btn");
|
||||
if (!btn) return;
|
||||
if (targetIndex !== -1) {
|
||||
btn.classList.remove('disabled');
|
||||
btn.classList.remove("disabled");
|
||||
} else {
|
||||
btn.classList.add('disabled');
|
||||
btn.classList.add("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
const titleSlide = document.getElementById('coursenav-course-title');
|
||||
const titleSlide = document.getElementById("coursenav-course-title");
|
||||
if (titleSlide) {
|
||||
const slide = e.detail.contentArray[e.detail.slideIndex];
|
||||
const moduleTitle = slide.moduleTitle ? slide.moduleTitle + ' | ' : '';
|
||||
const title = slide.title || 'Sin título';
|
||||
const moduleTitle = slide.moduleTitle ? slide.moduleTitle + " | " : "";
|
||||
const title = slide.title || "Sin título";
|
||||
titleSlide.textContent = moduleTitle + title;
|
||||
}
|
||||
|
||||
const stepper = document.getElementById('stepper');
|
||||
const movil = document.getElementById('step-movil');
|
||||
const stepper = document.getElementById("stepper");
|
||||
const movil = document.getElementById("step-movil");
|
||||
const progreso = CourseNav.getProgressPercent(true);
|
||||
renderStepper(stepper, progreso, movil);
|
||||
});
|
||||
@@ -365,25 +373,25 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
* @event slideCompleted
|
||||
* @property {Object} detail - Detalles del evento.
|
||||
*/
|
||||
document.body.addEventListener('slideCompleted', (e) => {
|
||||
console.log('Slide completado:', e.detail);
|
||||
const stepper = document.getElementById('stepper');
|
||||
const movil = document.getElementById('step-movil');
|
||||
document.body.addEventListener("slideCompleted", (e) => {
|
||||
console.log("Slide completado:", e.detail);
|
||||
const stepper = document.getElementById("stepper");
|
||||
const movil = document.getElementById("step-movil");
|
||||
const progreso = CourseNav.getProgressPercent(true);
|
||||
renderStepper(stepper, progreso, movil);
|
||||
renderPagination(e.detail.slideIndex, CourseNav.getCourseContentArray());
|
||||
});
|
||||
|
||||
// Event listener para botón personalizado
|
||||
const customButtonEvent = document.getElementById('coursenav-other-btn');
|
||||
const customButtonEvent = document.getElementById("coursenav-other-btn");
|
||||
if (customButtonEvent) {
|
||||
customButtonEvent.addEventListener('click', () => {
|
||||
console.log('Botón personalizado clickeado');
|
||||
customButtonEvent.addEventListener("click", () => {
|
||||
console.log("Botón personalizado clickeado");
|
||||
gotoFirstMenuToolSlide();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Escalar contenido al cargar y redimensionar
|
||||
window.addEventListener('DOMContentLoaded', () => scaleWrapCourseContent());
|
||||
window.addEventListener('resize', () => scaleWrapCourseContent());
|
||||
window.addEventListener("DOMContentLoaded", () => scaleWrapCourseContent());
|
||||
window.addEventListener("resize", () => scaleWrapCourseContent());
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user