|
@@ -1,9 +1,340 @@
|
|
|
-import React from "react";
|
|
|
+import React, {useState, useEffect, useRef} from "react";
|
|
|
+import DataGrid, {Column} from "devextreme-react/data-grid";
|
|
|
+import {Alert} from "./AlertDialog.js";
|
|
|
+import {addNumbersToGrid, numbersToArrOfArr} from "./helpers.js";
|
|
|
+import "./App.css";
|
|
|
+
|
|
|
+let imdtRes;
|
|
|
+let imdtResIdx;
|
|
|
+let resArr = [];
|
|
|
+let noOfDigits = 0;
|
|
|
|
|
|
function Subtraction() {
|
|
|
+ const [input, setInput] = useState(""); // initial user input like 34+56.7
|
|
|
+ const [realResult, setRealResult] = useState(0); // real result of calculation
|
|
|
+ const [carryArr, setCarryArr] = useState([]); // array of carries [1","0"]
|
|
|
+ const [commaIdx, setCommaIdx] = useState(0); // int: comma position
|
|
|
+ const [numbers, setNumbers] = useState(0); // array of arrays with input numbers
|
|
|
+ const [showAlert, setShowAlert] = useState(false);
|
|
|
+ const [numbersGrid, setNumbersGrid] = useState([{id: 1, number: ""}]);
|
|
|
+ const [resultsGrid, setResultsGrid] = useState([{id: 1, number: ""}]);
|
|
|
+ const [stepsGrid, setStepsGrid] = useState([{id: 1, step: ""}]);
|
|
|
+
|
|
|
+ // focus the input field
|
|
|
+ let calculationInput = useRef(null);
|
|
|
+ useEffect(()=>{
|
|
|
+ if(calculationInput.current && calculationInput.current.value === ""){
|
|
|
+ calculationInput.current.focus();
|
|
|
+ }
|
|
|
+ },[])
|
|
|
+
|
|
|
+ const handleInput = (e) => {
|
|
|
+ setInput(e.target.value);
|
|
|
+ }
|
|
|
+
|
|
|
+ const handleResChange = (e, nosLeft) => {
|
|
|
+ if(typeof e === "string"){
|
|
|
+ imdtRes = e;
|
|
|
+ }else{
|
|
|
+ imdtRes = e.target.value;
|
|
|
+ }
|
|
|
+
|
|
|
+ // remove deleted input
|
|
|
+ if(imdtRes === ""){
|
|
|
+ resArr.shift();
|
|
|
+ }else{
|
|
|
+ // add input number to result array
|
|
|
+ resArr.unshift(imdtRes);
|
|
|
+ if(nosLeft === 0){
|
|
|
+ setShowAlert(true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const handleCarryChange = (e, noOfDigits, idx) => {
|
|
|
+ if(typeof carryArr === "undefined"){
|
|
|
+ setCarryArr(Array(noOfDigits).fill("-"));
|
|
|
+ }
|
|
|
+ let carryArrCopy = [...carryArr]
|
|
|
+ carryArrCopy[idx] = e.target.value
|
|
|
+ setCarryArr(carryArrCopy);
|
|
|
+ }
|
|
|
+
|
|
|
+ const handleSubmit = (e) => {
|
|
|
+ startCalculation();
|
|
|
+ e.preventDefault(); // avoid page reload
|
|
|
+ }
|
|
|
+
|
|
|
+ const startCalculation = () => {
|
|
|
+ if (input.includes("-")){
|
|
|
+ let numbers = input.split("-").map(x => parseFloat(x.replace(",",".")));
|
|
|
+ let realRes = numbers[0]
|
|
|
+ for (let i=1; i<numbers.length; i++){
|
|
|
+ realRes -= numbers[i]
|
|
|
+ }
|
|
|
+ setRealResult(realRes);
|
|
|
+
|
|
|
+ let [numbersArr, commaIdx] = numbersToArrOfArr(numbers);
|
|
|
+ setNumbers(numbersArr);
|
|
|
+ setCommaIdx(commaIdx);
|
|
|
+
|
|
|
+ setNumbersGrid(addNumbersToGrid(numbersArr, "-"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const ResultCarryForm = ({handleResChange, handleCarryChange}) => {
|
|
|
+ let resInputField = useRef(null);
|
|
|
+ let carryInputField = useRef(null);
|
|
|
+
|
|
|
+ // focus the input field
|
|
|
+ useEffect(()=>{
|
|
|
+ if(resInputField.current && resInputField.current.value === ""){
|
|
|
+ resInputField.current.focus();
|
|
|
+ }
|
|
|
+ },[])
|
|
|
+
|
|
|
+ let labelText = "";
|
|
|
+ let nosLeft = imdtResIdx + 1;
|
|
|
+ let carryText = "Übertrag = ";
|
|
|
+ let carryOffset = 0;
|
|
|
+
|
|
|
+ if(typeof numbers === "object" && imdtResIdx !== -1){
|
|
|
+ if(typeof imdtResIdx === "undefined" || imdtResIdx === null){
|
|
|
+ noOfDigits = Math.min(...numbers.map(n => n.length));
|
|
|
+ imdtResIdx = noOfDigits-1;
|
|
|
+ }
|
|
|
+ if(imdtResIdx === commaIdx){
|
|
|
+ handleResChange(".", nosLeft)
|
|
|
+ imdtResIdx = imdtResIdx - 1;
|
|
|
+ carryOffset = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let n in numbers){ // iterate numbers
|
|
|
+ if(nosLeft === 1 && carryArr[0] === "0"){
|
|
|
+ nosLeft = 0;
|
|
|
+ imdtResIdx = -1;
|
|
|
+ handleResChange(" ", 0)
|
|
|
+ return <></>
|
|
|
+ }
|
|
|
+ let digit = numbers[parseInt(n)][imdtResIdx];
|
|
|
+ if(digit === "" || digit === " "){
|
|
|
+ digit = 0;
|
|
|
+ }
|
|
|
+ labelText += digit;
|
|
|
+
|
|
|
+ if (parseInt(n) === numbers.length - 1){
|
|
|
+ if(carryArr[imdtResIdx+carryOffset]!=="undefind" && carryArr[imdtResIdx+carryOffset] > 0){
|
|
|
+ labelText += " + " + carryArr[imdtResIdx+carryOffset].toString();
|
|
|
+ }
|
|
|
+ labelText += " = ";
|
|
|
+ }else{
|
|
|
+ labelText += " - ";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(carryArr[imdtResIdx+carryOffset] > -1 || imdtResIdx === noOfDigits-1){
|
|
|
+ imdtRes = -1;
|
|
|
+ imdtResIdx = imdtResIdx - 1;
|
|
|
+ nosLeft = nosLeft - 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <form>
|
|
|
+ <label htmlFor="input_result" id="input_result_label">
|
|
|
+ {labelText}
|
|
|
+ </label>
|
|
|
+ <input
|
|
|
+ onChange={(e) => handleResChange(e, nosLeft)}
|
|
|
+ type="text" id="input_result" size="2" tabIndex={1}
|
|
|
+ aria-labelledby="input_result_label" aria-required="true"
|
|
|
+ ref={resInputField}/>
|
|
|
+ <br/>
|
|
|
+ <label htmlFor="input_carry" id="input_carry_label">
|
|
|
+ {carryText}
|
|
|
+ </label>
|
|
|
+ <input
|
|
|
+ onChange={(e) => handleCarryChange(e, noOfDigits, imdtResIdx)}
|
|
|
+ type="text" id="input_carry" size="2" tabIndex={2}
|
|
|
+ aria-labelledby="input_carry_label" aria-required="true"
|
|
|
+ ref={carryInputField}/>
|
|
|
+ </form>
|
|
|
+ );
|
|
|
+ }else{
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ </>
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const showIdmtResults = () => {
|
|
|
+ setShowAlert(false);
|
|
|
+ let realResArr = realResult.toString().split("");
|
|
|
+ while(realResArr.length < resArr.length){
|
|
|
+ realResArr.unshift(" "); // add " " before
|
|
|
+ }
|
|
|
+
|
|
|
+ let carries = carryArr.map(x => x || "0")
|
|
|
+ carries.push("0");
|
|
|
+ while(carries.length < resArr.length){
|
|
|
+ carries.unshift("0"); // add "0" before
|
|
|
+ }
|
|
|
+
|
|
|
+ let foundComma = false;
|
|
|
+ let stepsGridCopy = [];
|
|
|
+ for (let i=0; i<resArr.length-1; i++) {
|
|
|
+ let text = "";
|
|
|
+ let trueNumbers = false;
|
|
|
+ let idxNumbers = resArr.length - i - 1;
|
|
|
+ let idxCarry = carries.length - i - 1;
|
|
|
+
|
|
|
+ let realDigitRes = 0;
|
|
|
+ for (let j=0; j<numbers.length; j++) {
|
|
|
+ let no = numbers[j][idxNumbers];
|
|
|
+
|
|
|
+ if (j === 0){
|
|
|
+ realDigitRes = parseInt(no);
|
|
|
+ }
|
|
|
+ else if (no!=="." && no!==" "){
|
|
|
+ trueNumbers = true;
|
|
|
+ realDigitRes -= parseInt(no);
|
|
|
+ }
|
|
|
+
|
|
|
+ text += no
|
|
|
+ if (j<numbers.length-1){
|
|
|
+ text += " - "
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ignore indexes without real digits
|
|
|
+ if (resArr[idxNumbers]!==" " || carries[idxCarry]!==" "){
|
|
|
+ trueNumbers = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // carry array has no "."
|
|
|
+ if (resArr[idxNumbers] === "."){
|
|
|
+ foundComma = true;
|
|
|
+ trueNumbers = false;
|
|
|
+ }
|
|
|
+ if (foundComma){
|
|
|
+ idxCarry += 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // add carry only if > 0
|
|
|
+ if (carries[idxCarry]!=="0"){
|
|
|
+ realDigitRes -= parseInt(carries[idxCarry]);
|
|
|
+ text += " - " + carries[idxCarry] // + " Übertrag"
|
|
|
+ }
|
|
|
+ console.log(realDigitRes)
|
|
|
+ let realCarry = Math.abs(parseInt(realDigitRes/10)).toString()
|
|
|
+ realDigitRes = (realDigitRes % 10)
|
|
|
+ if (realDigitRes < 0){
|
|
|
+ realDigitRes = realDigitRes + 10
|
|
|
+ }
|
|
|
+ realDigitRes = realDigitRes.toString()
|
|
|
+ text += " = "
|
|
|
+
|
|
|
+ if (trueNumbers){
|
|
|
+ text += resArr[idxNumbers]
|
|
|
+ text += " und " + carries[idxCarry-1] + " Übertrag "
|
|
|
+ text = text.replace(/ /g, "0")
|
|
|
+ text += resArr[idxNumbers]===realDigitRes && carries[idxCarry-1]===realCarry ? "(Richtig)" : "(Falsch)";
|
|
|
+ stepsGridCopy.push({step: text});
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ setStepsGrid(stepsGridCopy);
|
|
|
+ let btnSubmit = document.createElement("button");
|
|
|
+ btnSubmit.innerHTML = "Ergebnis abgeben";
|
|
|
+ btnSubmit.addEventListener("click", finishCalculation);
|
|
|
+ document.getElementById("idmtResultSteps").appendChild(btnSubmit);
|
|
|
+ }
|
|
|
+
|
|
|
+ const finishCalculation = () => {
|
|
|
+ setShowAlert(false);
|
|
|
+ document.getElementById("idmtResultSteps").innerHTML = "";
|
|
|
+ let resCalc = resArr.filter(n => n !== " ").join("");
|
|
|
+ setResultsGrid([{number: resCalc}]);
|
|
|
+ resCalc = parseFloat(resCalc);
|
|
|
+
|
|
|
+ let message = "";
|
|
|
+ console.log(resCalc, realResult);
|
|
|
+ if(resCalc === realResult){
|
|
|
+ message = "Richtig!";
|
|
|
+ }else{
|
|
|
+ message = "Das ist leider falsch.";
|
|
|
+ }
|
|
|
+ message = "<p>" + message + "</p>";
|
|
|
+ document.getElementById("finishCalculation").innerHTML = message;
|
|
|
+ }
|
|
|
+
|
|
|
return (
|
|
|
<div id="content">
|
|
|
+
|
|
|
<h1>Subtraction</h1>
|
|
|
+
|
|
|
+ <form onSubmit={(e) => handleSubmit(e)}>
|
|
|
+ <label htmlFor="calculationInput">
|
|
|
+ Rechnung:
|
|
|
+ </label>
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ id="calculationInput"
|
|
|
+ onChange={(e) => handleInput(e)}
|
|
|
+ aria-label="Rechnung"
|
|
|
+ aria-required="true"
|
|
|
+ ref={calculationInput}/>
|
|
|
+ <input type="submit" value="berechnen"/>
|
|
|
+ </form>
|
|
|
+
|
|
|
+ <div id="overview">
|
|
|
+ <DataGrid
|
|
|
+ dataSource={numbersGrid}
|
|
|
+ keyExpr="id"
|
|
|
+ focusedRowEnabled={true}
|
|
|
+ showBorders={true}
|
|
|
+ showColumnHeaders={false}
|
|
|
+ >
|
|
|
+ <Column dataField="number" />
|
|
|
+ </DataGrid>
|
|
|
+
|
|
|
+ <hr></hr>
|
|
|
+
|
|
|
+ <DataGrid
|
|
|
+ dataSource={resultsGrid}
|
|
|
+ focusedRowEnabled={true}
|
|
|
+ showBorders={true}
|
|
|
+ showColumnHeaders={false}
|
|
|
+ >
|
|
|
+ <Column dataField="number" />
|
|
|
+ </DataGrid>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div id="calculation">
|
|
|
+ <ResultCarryForm
|
|
|
+ handleResChange={handleResChange}
|
|
|
+ handleCarryChange={handleCarryChange}/>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div id="finishCalculation"></div>
|
|
|
+
|
|
|
+ <div id="idmtResultSteps">
|
|
|
+ <DataGrid
|
|
|
+ dataSource={stepsGrid}
|
|
|
+ focusedRowEnabled={true}
|
|
|
+ showBorders={true}
|
|
|
+ showColumnHeaders={false}
|
|
|
+ >
|
|
|
+ <Column dataField="step" />
|
|
|
+ </DataGrid>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <Alert
|
|
|
+ showAlert={showAlert}
|
|
|
+ finishCalc={finishCalculation}
|
|
|
+ showIdmtResults={showIdmtResults}
|
|
|
+ />
|
|
|
+
|
|
|
</div>
|
|
|
);
|
|
|
}
|