1
0

Addition.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. import React, { useState, useEffect, useRef } from "react";
  2. import Button from '@material-ui/core/Button';
  3. import Dialog from '@material-ui/core/Dialog';
  4. import DialogActions from '@material-ui/core/DialogActions';
  5. import DialogContent from '@material-ui/core/DialogContent';
  6. import DialogContentText from '@material-ui/core/DialogContentText';
  7. import DialogTitle from '@material-ui/core/DialogTitle';
  8. import DataGrid, {Column} from 'devextreme-react/data-grid';
  9. import "./App.css";
  10. let imdtRes;
  11. let imdtResIdx;
  12. let resArr = [];
  13. function Addition() {
  14. const [input, setInput] = useState(""); // initial user input like 34+56.7
  15. const [realResult, setRealResult] = useState(0); // real result of calculation
  16. const [carryArr, setCarryArr] = useState([]); // array of carries [1","0"]
  17. const [commaIdx, setCommaIdx] = useState(0); // int: comma position
  18. const [numbers, setNumbers] = useState(0); // array of arrays with input numbers
  19. const [showAlert, setShowAlert] = useState(false);
  20. const [numbersGrid, setNumbersGrid] = useState([{id: 1, number: ""}]);
  21. const [resultsGrid, setResultsGrid] = useState([{id: 1, number: ""}]);
  22. // focus the input field
  23. let calculationInput = useRef(null);
  24. useEffect(()=>{
  25. if(calculationInput.current && calculationInput.current.value === ""){
  26. calculationInput.current.focus();
  27. }
  28. },[])
  29. const handleInput = (e) => {
  30. setInput(e.target.value);
  31. }
  32. const handleResChange = (e, nosLeft) => {
  33. if(typeof e === "string"){
  34. imdtRes = e;
  35. let carryArrCopy = [...carryArr]
  36. carryArrCopy[nosLeft] = carryArrCopy[nosLeft+1]
  37. setCarryArr(carryArrCopy)
  38. }else{
  39. imdtRes = e.target.value;
  40. }
  41. if(imdtRes === ""){
  42. resArr.shift();
  43. }else{
  44. resArr.unshift(imdtRes);
  45. if(nosLeft === 0){
  46. setShowAlert(true);
  47. }
  48. }
  49. }
  50. const handleCarryChange = (e, noOfDigits, idx) => {
  51. if(typeof carryArr === "undefined"){
  52. setCarryArr(Array(noOfDigits).fill("-"));
  53. }
  54. let carryArrCopy = [...carryArr]
  55. carryArrCopy[idx] = e.target.value
  56. setCarryArr(carryArrCopy);
  57. }
  58. const handleSubmit = (e) => {
  59. startCalculation();
  60. e.preventDefault(); // avoid page reload
  61. }
  62. const afterCommaLen = (number) => {
  63. let noStrList = number.toString().split(".");
  64. if (noStrList.length > 1){
  65. return noStrList[1].length
  66. }
  67. return 0
  68. }
  69. const addNumbersToDiv = (numbers, noOfDigits) => {
  70. console.log("---", carryArr.join(""))
  71. // document.getElementById("overview").innerHTML += carryArr.join("");
  72. }
  73. function numbersToArrOfArr(numbers) {
  74. let befComma = Math.max(...numbers).toString().split(".")[0].length;
  75. let afterComma = Math.max(...numbers.map(x => afterCommaLen(x)));
  76. let withComma = Math.max(...numbers.map(x => x.toString().indexOf(".")));
  77. let arrLength;
  78. if(withComma < 0){ // no comma found
  79. arrLength = befComma;
  80. }else{
  81. arrLength = befComma + afterComma + 1; // add comma and after comma len
  82. }
  83. let numbersArr = numbers.map(x => x.toString().split(""));
  84. for (let numArr of numbersArr){
  85. let commaIdx = numArr.indexOf(".");
  86. // without comma, add before
  87. while(commaIdx===-1 && numArr.length < befComma){
  88. numArr.unshift("&nbsp;"); // add " " before comma
  89. }
  90. // without comma, add after
  91. while(commaIdx===-1 && numArr.length < arrLength){
  92. numArr.push("&nbsp;"); // add " " after comma
  93. }
  94. // with comma, add before
  95. while(commaIdx>-1 && commaIdx!==befComma){
  96. numArr.unshift("&nbsp;"); // add " " before comma
  97. commaIdx = numArr.indexOf(".");
  98. }
  99. // with comma, add after
  100. while(numArr.length < arrLength){
  101. numArr.push("&nbsp;"); // add " " after comma
  102. }
  103. numArr.unshift("&nbsp;"); // for carry
  104. }
  105. setNumbers(numbersArr);
  106. const nrGrid = []
  107. for (let noIdx in numbersArr){
  108. let nr = numbersArr[noIdx].join("").replace(/&nbsp;/g, " ");
  109. if (noIdx == numbersArr.length-1){
  110. nr = "+ " + nr;
  111. }
  112. nrGrid.push({id: parseInt(noIdx)+3, number: nr});
  113. }
  114. setNumbersGrid(nrGrid);
  115. setCommaIdx(befComma+1);
  116. return [numbersArr, arrLength]
  117. }
  118. const startCalculation = () => {
  119. if (input.includes("+")){
  120. let numbers = input.split("+").map(x => parseFloat(x.replace(",",".")));
  121. setRealResult(numbers.reduce((x,y) => x+y, 0));
  122. let [numbersArr, arrLength] = numbersToArrOfArr(numbers);
  123. addNumbersToDiv(numbersArr, arrLength);
  124. }
  125. }
  126. const ResultCarryForm = ({handleResChange, handleCarryChange}) => {
  127. let resInputField = useRef(null);
  128. let carryInputField = useRef(null);
  129. // focus the input field
  130. useEffect(()=>{
  131. if(resInputField.current && resInputField.current.value === ""){
  132. resInputField.current.focus();
  133. }
  134. },[])
  135. let labelText = "";
  136. let nosLeft = imdtResIdx + 1;
  137. let noOfDigits = 0;
  138. let carryText = "Übertrag = ";
  139. if(typeof numbers === "object" && imdtResIdx !== -1){
  140. if(typeof imdtResIdx === "undefined"){
  141. noOfDigits = Math.min(...numbers.map(n => n.length));
  142. imdtResIdx = noOfDigits-1;
  143. }
  144. if(imdtResIdx === commaIdx){
  145. handleResChange(".", nosLeft)
  146. imdtResIdx = imdtResIdx - 1;
  147. }
  148. for (let n in numbers){ // iterate numbers
  149. if(nosLeft === 1 && carryArr[0] === "0"){
  150. nosLeft = 0;
  151. imdtResIdx = -1;
  152. handleResChange("&nbsp;", 0)
  153. return <></>
  154. }
  155. let digit = numbers[parseInt(n)][imdtResIdx];
  156. if(digit === "" || digit === "&nbsp;"){
  157. digit = 0;
  158. }
  159. labelText += digit;
  160. if (parseInt(n) === numbers.length - 1){
  161. if(carryArr[imdtResIdx+1]!=="undefind" && carryArr[imdtResIdx+1] > 0){
  162. labelText += " + " + carryArr[imdtResIdx+1].toString();
  163. }
  164. labelText += " = ";
  165. }else{
  166. labelText += " + "; // TODO: replace with -/* etc.
  167. }
  168. }
  169. if(carryArr[imdtResIdx] > -1 || imdtResIdx === noOfDigits-1){
  170. imdtRes = -1;
  171. imdtResIdx = imdtResIdx - 1;
  172. nosLeft = nosLeft - 1;
  173. }
  174. return (
  175. <form>
  176. <label htmlFor="input_result" id="input_result_label">
  177. {labelText}
  178. </label>
  179. <input
  180. onChange={(e) => handleResChange(e, nosLeft)}
  181. type="text" id="input_result" size="2" tabIndex={1}
  182. aria-labelledby="input_result_label" aria-required="true"
  183. ref={resInputField}/>
  184. <br/>
  185. <label htmlFor="input_carry" id="input_carry_label">
  186. {carryText}
  187. </label>
  188. <input
  189. onChange={(e) => handleCarryChange(e, noOfDigits, imdtResIdx)}
  190. type="text" id="input_carry" size="2" tabIndex={2}
  191. aria-labelledby="input_carry_label" aria-required="true"
  192. ref={carryInputField}/>
  193. </form>
  194. );
  195. }else{
  196. return (
  197. <>
  198. </>
  199. );
  200. }
  201. }
  202. const showIdmtResults = () => {
  203. setShowAlert(false);
  204. let realResArr = realResult.toString().split("");
  205. while(realResArr.length < resArr.length){
  206. realResArr.unshift("&nbsp;"); // add " " before
  207. }
  208. let carries = carryArr.map(x => x || "0")
  209. carries.push("0");
  210. while(carries.length < resArr.length){
  211. carries.unshift("0"); // add "0" before
  212. }
  213. let foundComma = false;
  214. for (let i=0; i<resArr.length-1; i++) {
  215. let text = "";
  216. let trueNumbers = false;
  217. let idxNumbers = resArr.length - i - 1;
  218. let idxCarry = carries.length - i - 1;
  219. let realSum = 0;
  220. for (let j=0; j<numbers.length; j++) {
  221. let no = numbers[j][idxNumbers];
  222. if (no!=="." && no!=="&nbsp;"){
  223. trueNumbers = true;
  224. realSum += parseInt(no);
  225. }
  226. text += no
  227. if (j<numbers.length-1){
  228. text += " + "
  229. }
  230. }
  231. // ignore indexes without real digits
  232. if (resArr[idxNumbers]!=="&nbsp;" || carries[idxCarry]!=="&nbsp;"){
  233. trueNumbers = true;
  234. }
  235. // carry array has no "."
  236. if (resArr[idxNumbers] === "."){
  237. foundComma = true;
  238. trueNumbers = false;
  239. }
  240. if (foundComma){
  241. idxCarry += 1;
  242. }
  243. // add carry only if > 0
  244. if (carries[idxCarry]!=="0"){
  245. realSum += parseInt(carries[idxCarry]);
  246. text += " + " + carries[idxCarry] + " Übertrag"
  247. }
  248. let realCarry = parseInt(realSum/10).toString()
  249. realSum = (realSum % 10).toString()
  250. text += " = "
  251. if (trueNumbers){
  252. text += resArr[idxNumbers]
  253. text = text.replace("&nbsp;", "0")
  254. text += resArr[idxNumbers]===realSum && carries[idxCarry-1]===realCarry ? "\t\tRichtig" : "\t\tFalsch";
  255. let paragraph = document.createElement("p");
  256. paragraph.innerHTML = text
  257. document.getElementById("idmtResultSteps").appendChild(paragraph);
  258. let btn = document.createElement("button");
  259. btn.innerHTML = "bearbeiten: " + text;
  260. btn.addEventListener("click", () => startOver(i));
  261. document.getElementById("idmtResultSteps").appendChild(btn);
  262. }
  263. }
  264. let btnSubmit = document.createElement("button");
  265. btnSubmit.innerHTML = "submit result";
  266. btnSubmit.addEventListener("click", () => console.log("end"));
  267. document.getElementById("idmtResultSteps").appendChild(btnSubmit);
  268. }
  269. const startOver = (idx) => {
  270. console.log("clicked at " + idx)
  271. }
  272. const finishCalculation = () => {
  273. setShowAlert(false);
  274. let resCalc = resArr.filter(n => n !== "&nbsp;").join("")
  275. setResultsGrid([{number: resCalc}]);
  276. resCalc = parseFloat(resCalc);
  277. let message = ""
  278. if(resCalc === realResult){
  279. message = "Richtig!"
  280. }else{
  281. message = "Das ist leider falsch."
  282. }
  283. message = "<p>" + message + "</p>"
  284. document.getElementById("finishCalculation").innerHTML = message
  285. }
  286. const Alert = () => {
  287. return(
  288. <div>
  289. <Dialog
  290. open={showAlert}
  291. onClose={finishCalculation}
  292. aria-labelledby="alert-dialog-title"
  293. aria-describedby="alert-dialog-description"
  294. >
  295. <DialogTitle id="alert-dialog-title">{"Ergebnis überprüfen oder abgeben?"}</DialogTitle>
  296. <DialogContent>
  297. <DialogContentText id="alert-dialog-description">
  298. Willst du dein Ergebnis nochmal überprüfen oder gleich abgeben?
  299. </DialogContentText>
  300. </DialogContent>
  301. <DialogActions>
  302. <Button onClick={showIdmtResults} color="primary" autoFocus>
  303. Prüfen
  304. </Button>
  305. <Button onClick={finishCalculation} color="primary">
  306. Abgeben
  307. </Button>
  308. </DialogActions>
  309. </Dialog>
  310. </div>
  311. )};
  312. return (
  313. <div id="content">
  314. <h1>Addition</h1>
  315. <form onSubmit={(e) => handleSubmit(e)}>
  316. <label htmlFor="calculationInput">
  317. Rechnung:
  318. </label>
  319. <input
  320. type="text"
  321. id="calculationInput"
  322. onChange={(e) => handleInput(e)}
  323. aria-label="Rechnung"
  324. aria-required="true"
  325. ref={calculationInput}/>
  326. <input type="submit" value="berechnen"/>
  327. </form>
  328. <div id="overview">
  329. <DataGrid
  330. dataSource={numbersGrid}
  331. keyExpr="id"
  332. focusedRowEnabled={true}
  333. showBorders={true}
  334. showColumnHeaders={false}
  335. >
  336. <Column dataField="number" />
  337. </DataGrid>
  338. <hr></hr>
  339. <DataGrid
  340. dataSource={resultsGrid}
  341. focusedRowEnabled={true}
  342. showBorders={true}
  343. showColumnHeaders={false}
  344. >
  345. <Column dataField="number" />
  346. </DataGrid>
  347. </div>
  348. <div id="calculation">
  349. <ResultCarryForm
  350. handleResChange={handleResChange}
  351. handleCarryChange={handleCarryChange}/>
  352. </div>
  353. <div id="idmtResultSteps"></div>
  354. <div id="finishCalculation"></div>
  355. <Alert/>
  356. </div>
  357. );
  358. }
  359. export default Addition;