feat: implement tailwind validations and basic example
This commit is contained in:
17
src/app.js
17
src/app.js
@@ -210,6 +210,19 @@ function resetSuccessIndicators() {
|
||||
elements.runBtn.classList.remove("re-run");
|
||||
}
|
||||
|
||||
function updateEditorForMode(mode) {
|
||||
const codeInput = elements.codeInput;
|
||||
const editorLabel = document.querySelector(".editor-label");
|
||||
|
||||
if (mode === "tailwind") {
|
||||
codeInput.placeholder = "Enter Tailwind classes (e.g., bg-blue-500 text-white p-4)";
|
||||
if (editorLabel) editorLabel.textContent = "Tailwind Classes:";
|
||||
} else {
|
||||
codeInput.placeholder = "Enter your CSS code here...";
|
||||
if (editorLabel) editorLabel.textContent = "CSS Code:";
|
||||
}
|
||||
}
|
||||
|
||||
// Configure editor layout based on display type
|
||||
function resetEditorLayout(lesson) {
|
||||
elements.validationIndicators.innerHTML = "";
|
||||
@@ -229,8 +242,12 @@ function loadCurrentLesson() {
|
||||
}
|
||||
|
||||
const lesson = state.currentModule.lessons[state.currentLessonIndex];
|
||||
const mode = lesson.mode || state.currentModule?.mode || "css";
|
||||
lessonEngine.setLesson(lesson);
|
||||
|
||||
// Update UI based on mode
|
||||
updateEditorForMode(mode);
|
||||
|
||||
// Reset any success indicators
|
||||
resetSuccessIndicators();
|
||||
|
||||
|
||||
@@ -11,22 +11,24 @@ import carouselConfig from "../../lessons/02-css-only-carousel.json";
|
||||
// import selectorsConfig from "../../lessons/02-selectors.json";
|
||||
// import colorsConfig from "../../lessons/03-colors.json";
|
||||
// import typographyConfig from "../../lessons/04-typography.json";
|
||||
// import unitVariablesConfig from "../../lessons/05-units-variables.json";
|
||||
import unitVariablesConfig from "../../lessons/05-units-variables.json";
|
||||
// import transitionsAnimationsConfig from "../../lessons/06-transitions-animations.json";
|
||||
// import layoutConfig from "../../lessons/07-layouts.json";
|
||||
// import responsiveConfig from "../../lessons/08-responsive.json";
|
||||
import tailwindConfig from "../../lessons/10-tailwind-basics.json";
|
||||
|
||||
// Module store
|
||||
const moduleStore = [
|
||||
// basicsConfig,
|
||||
basicSelectorsConfig,
|
||||
// advancedSelectorsConfig,
|
||||
advancedSelectorsConfig,
|
||||
tailwindConfig
|
||||
// carouselConfig
|
||||
// boxModelConfig,
|
||||
// selectorsConfig,
|
||||
// colorsConfig
|
||||
// typographyConfig,
|
||||
// unitVariablesConfig,
|
||||
// unitVariablesConfig
|
||||
// transitionsAnimationsConfig,
|
||||
// layoutConfig,
|
||||
// responsiveConfig
|
||||
@@ -37,8 +39,13 @@ const moduleStore = [
|
||||
* @returns {Promise<Array>} Promise resolving to array of modules
|
||||
*/
|
||||
export async function loadModules() {
|
||||
// In a real app, we might load these from a server
|
||||
return moduleStore;
|
||||
return moduleStore.map((module) => ({
|
||||
...module,
|
||||
lessons: module.lessons.map((lesson) => ({
|
||||
...lesson,
|
||||
mode: module.mode || "css"
|
||||
}))
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,13 +2,79 @@
|
||||
* Validator - Functions to validate user CSS code
|
||||
*/
|
||||
|
||||
export function validateUserCode(userCode, lesson) {
|
||||
const mode = lesson.mode || "css";
|
||||
|
||||
if (mode === "tailwind") {
|
||||
return validateTailwindClasses(userCode, lesson);
|
||||
} else {
|
||||
return validateCssCode(userCode, lesson);
|
||||
}
|
||||
}
|
||||
|
||||
function validateTailwindClasses(userClasses, lesson) {
|
||||
if (!lesson || !lesson.validations) {
|
||||
return { isValid: true, message: "No validations specified for this lesson." };
|
||||
}
|
||||
|
||||
let result = {
|
||||
isValid: true,
|
||||
validCases: 0,
|
||||
totalCases: lesson.validations.length,
|
||||
message: "Your Tailwind classes look CRISPY!"
|
||||
};
|
||||
|
||||
for (const validation of lesson.validations) {
|
||||
const { type, value, message } = validation;
|
||||
let validationPassed = false;
|
||||
|
||||
switch (type) {
|
||||
case "contains_class":
|
||||
validationPassed = userClasses.split(/\s+/).includes(value);
|
||||
if (!validationPassed) {
|
||||
result = {
|
||||
...result,
|
||||
isValid: false,
|
||||
message: message || `Your classes should include "${value}".`
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
case "contains_pattern":
|
||||
const regex = new RegExp(value);
|
||||
validationPassed = regex.test(userClasses);
|
||||
if (!validationPassed) {
|
||||
result = {
|
||||
...result,
|
||||
isValid: false,
|
||||
message: message || "Your classes don't match the expected pattern."
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Fall back to original CSS validation for other types
|
||||
validationPassed = containsValidation(userClasses, value);
|
||||
}
|
||||
|
||||
if (validationPassed) {
|
||||
result.validCases++;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
result.validCases = lesson.validations.length;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate user CSS code against the lesson requirements
|
||||
* @param {string} userCode - User submitted CSS code
|
||||
* @param {Object} lesson - The current lesson object
|
||||
* @returns {Object} Validation result with isValid and message properties
|
||||
*/
|
||||
export function validateUserCode(userCode, lesson) {
|
||||
export function validateCssCode(userCode, lesson) {
|
||||
if (!lesson || !lesson.validations) {
|
||||
return { isValid: true, message: "No validations specified for this lesson." };
|
||||
}
|
||||
|
||||
@@ -111,44 +111,64 @@ export class LessonEngine {
|
||||
renderPreview() {
|
||||
if (!this.currentLesson) return;
|
||||
|
||||
const mode = this.currentLesson.mode || this.currentModule?.mode || "css";
|
||||
const { previewHTML, previewBaseCSS, previewContainer, sandboxCSS } = this.currentLesson;
|
||||
|
||||
// Create an iframe for isolated preview rendering
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.style.width = "100%";
|
||||
iframe.style.height = "100%";
|
||||
iframe.style.border = "none";
|
||||
iframe.title = "Preview";
|
||||
|
||||
// Get the preview container
|
||||
const container = document.getElementById(previewContainer || "preview-area");
|
||||
|
||||
// Clear the container and add the iframe
|
||||
container.innerHTML = "";
|
||||
container.appendChild(iframe);
|
||||
|
||||
// Get the complete CSS by combining all parts
|
||||
const userCssWithWrapper = this.getCompleteCss();
|
||||
|
||||
// Write the content to the iframe
|
||||
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
iframeDoc.open();
|
||||
iframeDoc.write(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>${previewBaseCSS}</style>
|
||||
<style>${userCssWithWrapper}</style>
|
||||
<style>${sandboxCSS}</style>
|
||||
</head>
|
||||
<body>
|
||||
${previewHTML || "<div>No preview available</div>"}
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
|
||||
if (mode === "tailwind") {
|
||||
// For Tailwind mode, user code goes directly in HTML classes
|
||||
const htmlWithClasses = this.injectTailwindClasses(previewHTML, this.userCode);
|
||||
iframeDoc.write(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>${previewBaseCSS}</style>
|
||||
<style>${sandboxCSS}</style>
|
||||
</head>
|
||||
<body>
|
||||
${htmlWithClasses}
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
} else {
|
||||
// Original CSS mode
|
||||
const userCssWithWrapper = this.getCompleteCss();
|
||||
iframeDoc.write(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>${previewBaseCSS}</style>
|
||||
<style>${userCssWithWrapper}</style>
|
||||
<style>${sandboxCSS}</style>
|
||||
</head>
|
||||
<body>
|
||||
${previewHTML}
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
}
|
||||
|
||||
iframeDoc.close();
|
||||
}
|
||||
|
||||
injectTailwindClasses(html, userClasses) {
|
||||
// Replace placeholder in HTML with user's Tailwind classes
|
||||
return html.replace(/{{USER_CLASSES}}/g, userClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate user code against the current lesson's requirements
|
||||
* @returns {Object} Validation result
|
||||
|
||||
Reference in New Issue
Block a user