feat: enhance module list rendering with expandable lessons and active lesson tracking

This commit is contained in:
Michael Czechowski
2025-05-20 01:43:57 +02:00
parent b408d8f7b5
commit ded85e9b7d
3 changed files with 241 additions and 24 deletions

View File

@@ -7,40 +7,110 @@ let feedbackElement = null;
let feedbackTimeout = null;
/**
* Render the module list in the sidebar
* Render the module list in the sidebar with expandable lessons
* @param {HTMLElement} container - The container element for the module list
* @param {Array} modules - The list of modules
* @param {Function} onSelectModule - Callback when a module is selected
* @param {Function} onSelectLesson - Callback when a lesson is selected
*/
export function renderModuleList(container, modules, onSelectModule) {
export function renderModuleList(container, modules, onSelectModule, onSelectLesson) {
// Clear the container
container.innerHTML = "<h3>CSS Lessons</h3>";
// Get user progress from localStorage
const progressData = localStorage.getItem("codeCrispies.Progress");
let progress = {};
if (progressData) {
try {
progress = JSON.parse(progressData);
} catch (e) {
console.error("Error parsing progress data:", e);
}
}
// Create list items for each module
modules.forEach((module) => {
const moduleItem = document.createElement("div");
moduleItem.classList.add("module-list-item");
moduleItem.dataset.moduleId = module.id;
moduleItem.textContent = module.title;
// Create module container
const moduleContainer = document.createElement("div");
moduleContainer.classList.add("module-container");
// Get user progress from localStorage to mark completed lessons
const progressData = localStorage.getItem("codeCrispies.Progress");
if (progressData) {
try {
const progress = JSON.parse(progressData);
if (progress[module.id] && progress[module.id].completed.length === module.lessons.length) {
moduleItem.classList.add("completed");
}
} catch (e) {
console.error("Error parsing progress data:", e);
}
// Create module header item (clickable to expand/collapse)
const moduleHeader = document.createElement("div");
moduleHeader.classList.add("module-list-item", "module-header");
moduleHeader.dataset.moduleId = module.id;
// Create module title with expand/collapse indicator
const moduleTitle = document.createElement("span");
moduleTitle.classList.add("module-title");
moduleTitle.textContent = module.title;
// Create expand/collapse icon
const expandIcon = document.createElement("span");
expandIcon.classList.add("expand-icon");
expandIcon.innerHTML = "▶"; // Right-pointing triangle
moduleHeader.appendChild(expandIcon);
moduleHeader.appendChild(moduleTitle);
// Check if the module is completed
if (progress[module.id] && progress[module.id].completed.length === module.lessons.length) {
moduleHeader.classList.add("completed");
}
moduleItem.addEventListener("click", () => {
onSelectModule(module.id);
// Lessons container (initially hidden)
const lessonsContainer = document.createElement("div");
lessonsContainer.classList.add("lessons-container");
lessonsContainer.style.display = "none"; // Initially collapsed
// Create list items for each lesson in this module
module.lessons.forEach((lesson, index) => {
const lessonItem = document.createElement("div");
lessonItem.classList.add("lesson-list-item");
lessonItem.dataset.moduleId = module.id;
lessonItem.dataset.lessonIndex = index;
lessonItem.textContent = lesson.title || `Lesson ${index + 1}`;
// Mark lesson as completed if in progress data
if (progress[module.id] && progress[module.id].completed.includes(index)) {
lessonItem.classList.add("completed");
}
// Mark lesson as current if it's the current lesson
if (progress[module.id] && progress[module.id].current === index) {
lessonItem.classList.add("current");
}
// Add click event to select lesson
lessonItem.addEventListener("click", () => {
// Update UI to show this lesson is selected
document.querySelectorAll(".lesson-list-item").forEach((item) => {
item.classList.remove("active");
});
lessonItem.classList.add("active");
// Call the onSelectLesson callback
onSelectLesson(module.id, index);
});
lessonsContainer.appendChild(lessonItem);
});
container.appendChild(moduleItem);
// Toggle expand/collapse when clicking on module header
moduleHeader.addEventListener("click", () => {
// Toggle visibility of lessons container
const isExpanded = lessonsContainer.style.display !== "none";
lessonsContainer.style.display = isExpanded ? "none" : "block";
// Update expand/collapse icon
expandIcon.innerHTML = isExpanded ? "▶" : "▼";
});
// Add module header and lessons container to module container
moduleContainer.appendChild(moduleHeader);
moduleContainer.appendChild(lessonsContainer);
// Add the complete module container to the sidebar
container.appendChild(moduleContainer);
});
}
@@ -132,3 +202,41 @@ export function clearFeedback() {
}
feedbackElement = null;
}
/**
* Update the active lesson in the sidebar
* @param {string} moduleId - The ID of the module
* @param {number} lessonIndex - The index of the lesson
*/
export function updateActiveLessonInSidebar(moduleId, lessonIndex) {
// Remove active class from all lessons
document.querySelectorAll(".lesson-list-item").forEach((item) => {
item.classList.remove("active");
});
// Find and activate the current lesson
const selector = `.lesson-list-item[data-module-id="${moduleId}"][data-lesson-index="${lessonIndex}"]`;
const currentLessonItem = document.querySelector(selector);
if (currentLessonItem) {
currentLessonItem.classList.add("active");
// Make sure parent module is expanded
const parentLessonsContainer = currentLessonItem.parentElement;
if (parentLessonsContainer && parentLessonsContainer.classList.contains("lessons-container")) {
parentLessonsContainer.style.display = "block";
// Update expand icon
const moduleHeader = parentLessonsContainer.previousElementSibling;
if (moduleHeader) {
const expandIcon = moduleHeader.querySelector(".expand-icon");
if (expandIcon) {
expandIcon.innerHTML = "▼"; // Down arrow when expanded
}
}
// Scroll to ensure the item is visible
currentLessonItem.scrollIntoView({ behavior: "smooth", block: "nearest" });
}
}
}