update modificacion de template adaptativo y modificacion de algunas actividades

This commit is contained in:
2025-09-18 11:58:18 -06:00
parent a57cba5f67
commit 07100d82de
35 changed files with 1004 additions and 400 deletions
+98 -78
View File
@@ -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: false,
};
@@ -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
@@ -36,7 +36,7 @@ function renderPagination(currentIndex, contentArray) {
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 +54,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 +63,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 +80,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 +98,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 +106,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 +128,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 +149,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) => {
@@ -172,36 +172,48 @@ function animateOnScroll(selector, animationClass, options = {}) {
* @function scaleWrapCourseContent
*/
function scaleWrapCourseContent() {
const content = document.querySelector(".wrap-course-content");
const content = document.querySelector('.wrap-course-content');
const mainContent = document.querySelector('#coursenav-main-content');
if (!content) return;
const header = document.querySelector('header');
const footer = document.querySelector('footer');
const headerHeight = header ? header.offsetHeight : 0;
const footerHeight = footer ? footer.offsetHeight : 0;
const vw = window.innerWidth;
const vh = window.innerHeight;
const minWidth = 768;
if (vw < minWidth) {
// Escalar en móviles
const scale = vw / minWidth;
const ww = window.innerWidth;
const wh = window.innerHeight - headerHeight;
if (ww < 1366) {
const scale = Math.min(ww / 1366, wh / 768);
content.style.transform = `scale(${scale})`;
content.style.transformOrigin = "top left";
content.style.width = minWidth + "px";
content.style.height = (vh / scale) + "px";
content.style.position = "fixed";
content.style.left = "0";
content.style.top = "0";
content.style.overflow = "hidden";
content.style.transformOrigin = 'top left';
content.style.width = '1366px';
content.style.height = '768px';
content.style.position = 'absolute';
content.style.left = (ww - 1366 * scale) / 2 + 'px';
content.style.top = headerHeight + (wh - 768 * scale) / 2 + 'px';
content.style.overflow = 'hidden';
if (mainContent) {
const availableHeight = 768 - 80;
mainContent.style.height = availableHeight + 'px';
}
} else {
// Pantalla completa en desktop
content.style.transform = "";
content.style.transformOrigin = "";
content.style.width = "100vw";
content.style.height = "100vh";
content.style.position = "fixed";
content.style.left = "0";
content.style.top = "0";
content.style.overflow = "auto";
content.style.transform = '';
content.style.transformOrigin = '';
content.style.width = '100%';
content.style.height = '100vh';
content.style.position = 'relative';
content.style.left = '';
content.style.top = '';
content.style.overflow = 'hidden';
if (mainContent) {
mainContent.style.height = `calc(100vh - ${headerHeight + footerHeight}px)`;
}
}
content.style.zIndex = "1";
content.style.zIndex = '1';
}
/**
@@ -218,14 +230,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);
@@ -238,22 +250,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 + '%');
}
/**
@@ -269,10 +281,12 @@ 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((slide) => slide.title === targetTitle && slide.moduleTitle === currentSlide.moduleTitle);
const targetIndex = contentArray.findIndex(
(slide) => slide.title === targetTitle && slide.moduleTitle === currentSlide.moduleTitle
);
if (targetIndex !== -1) {
CourseNav.gotoSlide(targetIndex);
@@ -285,7 +299,11 @@ function gotoFirstMenuToolSlide() {
* Configura los event listeners cuando el DOM está completamente cargado.
* @event DOMContentLoaded
*/
document.addEventListener("DOMContentLoaded", () => {
document.addEventListener('DOMContentLoaded', () => {
// Ajustar contenido al cargar y redimensionar
setTimeout(scaleWrapCourseContent, 100);
window.addEventListener('resize', () => setTimeout(scaleWrapCourseContent, 100));
/**
* Evento antes de cambiar de slide.
* @event beforeSlideChange
@@ -293,8 +311,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);
});
/**
@@ -304,34 +322,36 @@ 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 targetIndex = contentArray.findIndex(
(slide) => slide.title === targetTitle && slide.moduleTitle === currentSlide.moduleTitle
);
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);
});
@@ -341,25 +361,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());