mirror of
https://github.com/OHTU-German-learning-website/OHTU-German-learning-website.git
synced 2026-10-12 19:13:51 +03:00
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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user