Drag and Drop -exercise success message and UI changes (#170)

* Added a success message after the exercise is complete

* Modified UI for drag and drop exercise

* Made dustbin height dynamic

* Made wordboxes align with the dustbins
This commit is contained in:
Salla Rautiainen
2025-03-18 16:17:24 +02:00
committed by GitHub
parent 22cd66bd5a
commit 34b6648a22
4 changed files with 129 additions and 30 deletions
+78 -16
View File
@@ -1,4 +1,3 @@
import update from "immutability-helper";
import { memo, useCallback, useState } from "react";
import { ItemTypes } from "../../../app/lessons/exercises/dragdrop/itemtypes.js";
import { WordBox } from "./wordbox.js";
@@ -14,34 +13,75 @@ export const Area = memo(function Area() {
const initialBoxes = [
{ name: "Kurs", type: ItemTypes.DER },
{ name: "Elefant", type: ItemTypes.DER },
{ name: "Hund", type: ItemTypes.DER },
{ name: "Schule", type: ItemTypes.DIE },
{ name: "Eule", type: ItemTypes.DIE },
{ name: "Zeit", type: ItemTypes.DIE },
{ name: "Schmetterling", type: ItemTypes.DIE },
{ name: "Auto", type: ItemTypes.DAS },
{ name: "Kaninchen", type: ItemTypes.DAS },
{ name: "Geld", type: ItemTypes.DAS },
{ name: "Wort", type: ItemTypes.DAS },
{ name: "Haus", type: ItemTypes.DAS },
{ name: "Bild", type: ItemTypes.DAS },
];
const [dustbins, setDustbins] = useState(initialDustbins);
const [boxes] = useState(initialBoxes);
const [droppedBoxNames, setDroppedBoxNames] = useState([]);
const [isExerciseComplete, setIsExerciseComplete] = useState(false);
function isDropped(boxName) {
return droppedBoxNames.indexOf(boxName) > -1;
}
const checkExerciseComplete = (updatedDustbins) => {
const totalDroppedItems = updatedDustbins.reduce(
(sum, dustbin) => sum + dustbin.droppedItems.length,
0
);
const allBoxesPlaced = totalDroppedItems === initialBoxes.length;
const allItemsCorrect = updatedDustbins.every((dustbin) =>
dustbin.droppedItems.every((item) => item.type === dustbin.accepts[0])
);
console.log("Exercise complete check:", {
totalDroppedItems,
totalBoxes: initialBoxes.length,
allBoxesPlaced,
allItemsCorrect,
});
return allBoxesPlaced && allItemsCorrect;
};
const handleDrop = useCallback(
(index, item) => {
const { name } = item;
setDroppedBoxNames(
update(droppedBoxNames, name ? { $push: [name] } : { $push: [] })
);
setDustbins(
update(dustbins, {
[index]: {
droppedItems: {
$push: item ? [{ name, type: item.type }] : [],
},
},
})
);
const { name, type } = item;
console.log("Handling drop:", { name, type, index });
if (!isDropped(name)) {
const updatedDroppedBoxNames = [...droppedBoxNames, name];
const updatedDustbins = [...dustbins];
updatedDustbins[index] = {
...updatedDustbins[index],
droppedItems: [
...updatedDustbins[index].droppedItems,
{ name, type },
],
};
console.log("Updated dustbin:", updatedDustbins[index]);
setDroppedBoxNames(updatedDroppedBoxNames);
setDustbins(updatedDustbins);
const isComplete = checkExerciseComplete(updatedDustbins);
console.log("Setting exercise complete:", isComplete);
setIsExerciseComplete(isComplete);
}
},
[droppedBoxNames, dustbins]
);
@@ -49,11 +89,19 @@ export const Area = memo(function Area() {
const reset = () => {
setDustbins(initialDustbins);
setDroppedBoxNames([]);
setIsExerciseComplete(false);
};
return (
<div>
<div style={{ overflow: "hidden", clear: "both" }}>
<div
style={{
display: "flex",
flexWrap: "wrap",
width: "calc(36rem + 2 * var(--u-xl))",
gap: "var(--u-md)",
}}
>
{boxes.map(({ name, type }, index) => (
<WordBox
name={name}
@@ -73,7 +121,21 @@ export const Area = memo(function Area() {
/>
))}
</div>
<Button onClick={reset}>Erneut versuchen</Button>
{isExerciseComplete && (
<div className="success-message">
Super! Du hast die Übung abgeschlossen.
</div>
)}
<div
style={{
display: "flex",
justifyContent: "center",
width: "100%",
marginTop: "var(--u-xl)",
}}
>
<Button onClick={reset}>Erneut versuchen</Button>
</div>
</div>
);
});
@@ -1,18 +1,19 @@
.wordbox {
border: var(--radius-2xs) dashed;
background-color: var(--fg8);
background-color: var(--fg3);
color: var(--secondary1);
padding: var(--u-md);
margin-right: var(--u-md);
margin-bottom: var(--u-xl);
margin-right: var(--u-sm);
margin-bottom: var(--u-sm);
cursor: move;
float: left;
}
.dustbin {
height: 12rem;
min-height: 12rem;
width: 12rem;
margin-right: 1.5rem;
margin-bottom: 1.5rem;
margin-right: var(--u-xl);
margin-bottom: var(--u-xl);
margin-top: var(--u-xl);
color: var(--fg3);
font-size: var(--font-lg);
padding: var(--u-lg);
@@ -20,3 +21,40 @@
line-height: normal;
float: left;
}
.success-message {
width: 50%;
background-color: var(--fg3);
color: var(--secondary1);
padding: var(--u-lg);
margin-left: auto;
margin-right: auto;
margin-bottom: var(--u-xl);
border-radius: var(--radius-xs);
box-shadow: 0.5px 0.5px 5px var(--secondary2);
text-align: center;
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.dropped-items {
margin-top: var(--u-lg);
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.dropped-item {
padding: var(--u-md);
background-color: var(--fg3);
color: var(--secondary1);
border-radius: var(--radius-sm);
}
@@ -27,13 +27,12 @@ export const Dustbin = memo(function Dustbin({ accept, droppedItems, onDrop }) {
>
{isActive ? `${accept.join(", ")}` : `${accept.join(", ")}`}
{droppedItems && (
<div>
{droppedItems.map((item) => (
<span key={item.name}>
{droppedItems && droppedItems.length > 0 && (
<div className="dropped-items">
{droppedItems.map((item, index) => (
<div key={`${item.name}-${index}`} className="dropped-item">
{item.name}
<br></br>
</span>
</div>
))}
</div>
)}
@@ -6,7 +6,7 @@ export const WordBox = memo(function WordBox({ name, type, isDropped }) {
const [{ opacity }, drag] = useDrag(
() => ({
type,
item: { name },
item: { name, type },
canDrag: !isDropped,
collect: (monitor) => ({
opacity: monitor.isDragging() ? 0.4 : 1,