Skip to content

Commit

Permalink
Merge pull request #365 from codekids-vt/drag-mulit-choice
Browse files Browse the repository at this point in the history
drag mulit choice
  • Loading branch information
noahpro99 committed Apr 9, 2024
2 parents 239acab + 47885b8 commit 6d36581
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 0 deletions.
4 changes: 4 additions & 0 deletions frontend/src/components/BookImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { ElementPlacement } from "./Computer_IO";
import { ClothingActivity } from "./ClothingActivity";
import BookRushHour from "./BookRushHour";
import { ImageQuestion } from "./ImageQuestion";
import { DragMultiChoice } from "./DragMultiChoice";

export function BookImage({
image,
Expand Down Expand Up @@ -137,6 +138,9 @@ export function BookImage({
{image === "ImageQuestion" && (
<ImageQuestion props={page?.props} setAllowNext={setAllowNext} />
)}
{image === "DragMultiChoice" && (
<DragMultiChoice props={page?.props} setAllowNext={setAllowNext} />
)}
</div>
);
}
161 changes: 161 additions & 0 deletions frontend/src/components/DragMultiChoice.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import React, { Dispatch, SetStateAction, useEffect, useState } from "react";

type Option = {
text: string;
image?: string;
};

type Question = {
text: string;
answer: string;
};

interface DragMultiChoiceProps {
props: {
options: Option[];
questions: Question[];
title: string;
};
setAllowNext: Dispatch<SetStateAction<boolean>>;
}

export const DragMultiChoice: React.FC<DragMultiChoiceProps> = ({
props,
setAllowNext,
}) => {
const { options, questions } = props;

const [answersCorrectness, setAnswersCorrectness] = useState<boolean[]>(
Array(props.questions.length).fill(false),
);
const [availableOptions, setAvailableOptions] = useState<Option[]>(
props.options,
);
const [clickedOption, setClickedOption] = useState<Option | null>(null);
const [message, setMessage] = useState<{
text: string;
correct: boolean;
} | null>(null);

useEffect(() => {
const allCorrect = answersCorrectness.every((correct) => correct);
setAllowNext(allCorrect);
}, [answersCorrectness, setAllowNext]);

const handleDrop = (questionIndex: number, answer: string) => {
const question = questions[questionIndex];
const isCorrect = question.answer === answer;
if (isCorrect) {
setAnswersCorrectness((prev) => {
const updated = [...prev];
updated[questionIndex] = true;
return updated;
});
setAvailableOptions((prev) => {
const updated = prev.filter((option) => option.text !== answer);
return updated;
});
}
setMessage({ text: "Incorrect! Try again.", correct: false });
};

useEffect(() => {
if (availableOptions.length === 0) {
setMessage({ text: "All answers are correct!", correct: true });
}
}, [availableOptions, setMessage]);

return (
<div className="flex flex-col items-center gap-5 w-full h-full justify-center">
<div className="text-2xl">{props.title}</div>
<div className="flex flex-col md:flex-row gap-10 p-5">
<div className="flex flex-col items-start gap-4">
<div className="text-xl font-medium">Options</div>
{availableOptions.map((option, index) => (
<div
key={index}
className={`flex items-center gap-2 p-2 border-2 border-primary-green rounded-xl hover:bg-primary-green hover:text-white cursor-pointer transition duration-300 ease-in-out ${clickedOption?.text === option.text ? "bg-primary-green text-white" : "bg-white text-gray-800"}`}
draggable
onDragStart={(e) => e.dataTransfer.setData("text", option.text)}
onClick={() => setClickedOption(option)}
>
{option.image && (
<img
src={option.image}
alt={option.text}
className="w-16 h-16 object-cover rounded-md"
/>
)}
<p className="text-gray-800 font-medium">{option.text}</p>
</div>
))}
</div>
<div className="hidden md:block h-96 border-r-2 border-gray-200"></div>

<div className="flex flex-col items-start gap-4">
<div className="text-xl font-medium">Questions</div>
{questions.map((question, index) => (
<div
key={index}
className={`flex items-center gap-2 p-2 bg-white shadow-md rounded-lg transition duration-300 ease-in-out ${
answersCorrectness[index] ? "bg-green-100" : "bg-red-100"
}`}
>
{answersCorrectness[index] ? (
<div className="flex justify-center items-center w-32 h-12 gap-2 border-2 border-green-500 bg-green-100 rounded-lg text-green-500">
{options.find((option) => option.text === question.answer)
?.image && (
<img
src={
options.find(
(option) => option.text === question.answer,
)?.image
}
width="32"
height="32"
alt={question.answer}
className="w-8 h-8 object-cover rounded-md"
/>
)}
<p className="text-green-500 font-medium">Correct</p>
</div>
) : (
<div
className="flex justify-center items-center w-32 h-12 border-dashed border-2 border-primary-green rounded-lg text-primary-green cursor-pointer transition duration-300 ease-in-out"
onDrop={(e) => {
e.preventDefault();
const data = e.dataTransfer.getData("text");
handleDrop(index, data);
}}
onDragOver={(e) => e.preventDefault()}
onClick={() => {
if (clickedOption) {
handleDrop(index, clickedOption.text);
setClickedOption(null);
}
}}
>
Drop here
</div>
)}
<p className="text-gray-800 font-medium flex-1">
{question.text}
</p>
</div>
))}
</div>
</div>
{message && (
<div
className={`flex items-center gap-2 p-2 bg-white shadow-md rounded-lg ${
message.correct ? "bg-green-100" : "bg-red-100"
}`}
>
<p className={message.correct ? "text-green-500" : "text-red-500"}>
{message.text}
</p>
</div>
)}
</div>
);
};
22 changes: 22 additions & 0 deletions frontend/src/util/componentEditorDefaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,26 @@ export const editorDefaults: { [key: string]: any } = {
style: Styles.HORIZONTAL,
image: "/sky.jpg",
},
DragMultiChoice: {
title: "What is the title?",
options: [
{
text: "Option 1",
image: "https://codekids-minio.endeavour.cs.vt.edu/codekids/1.webp",
},
{
text: "Option 2",
},
],
questions: [
{
text: "What is the color of the sky?",
answer: "Option 1",
},
{
text: "What is the color of the sky?",
answer: "Option 2",
},
],
},
};

0 comments on commit 6d36581

Please sign in to comment.