-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.js
126 lines (119 loc) · 3.39 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/**
* all the code is based on below links with minor change
* https://github.com/aosabook/500lines/blob/master/spreadsheet/code/as-react-livescript/main.ls
* https://github.com/aosabook/500lines/tree/master/spreadsheet
*/
import React, { useState, useEffect } from 'react';
import ReactDom from 'react-dom';
const range = (cur, end) => {
const result = [];
while (cur <= end) {
result.push(cur);
cur = isNaN(cur) ? String.fromCodePoint(cur.codePointAt() + 1) : cur + 1;
}
return result;
};
const sheetDefault = {
A1: 1874,
B1: '+',
C1: 2046,
D1: '⇒',
E1: '=A1+C1',
};
const initSheet = JSON.parse(localStorage.getItem('')) || sheetDefault;
const Sheet = (props) => {
const [worker, setWorker] = useState(props.worker);
const [sheet, setSheet] = useState(initSheet);
const [vals, setVals] = useState(initSheet);
const [errs, setErrs] = useState({});
const [didMount, setDidMount] = useState(false);
const rows = range(1, 20);
const cols = range('A', 'H');
const calc = (sheet) => {
setSheet(sheet);
const timeout = setTimeout(() => {
worker.terminate();
setWorker(new Worker('worker.js'));
}, 99);
worker.onmessage = ({ data: [newErrs, newVals] }) => {
clearTimeout(timeout);
setErrs(newErrs);
setVals(newVals);
localStorage.setItem('', JSON.stringify(sheet));
};
worker.postMessage(sheet);
};
const reset = () => {
calc(sheetDefault);
};
useEffect(() => {
if (!didMount) {
calc(sheet);
setDidMount(true);
}
});
return (
<table>
<tbody>
<tr>
<th>
<button onClick={reset}>↻</button>
</th>
{cols.map((col, i) => (
<th key={i}>{col}</th>
))}
</tr>
{rows.map((row, i) => (
<tr key={i}>
<th>{row}</th>
{cols.map((col, j) => (
<td
key={j}
className={
sheet[col + row] && sheet[col + row] === '=' ? 'formula' : ''
}
>
<input
id={`${col + row}`}
type="text"
value={sheet[col + row] || ''}
onChange={({ target: { id, value } }) => {
calc({ ...sheet, [id]: value });
}}
onKeyDown={({ which }) => {
switch (which) {
case 38:
case 40:
case 13:
const direction = which === 38 ? row - 1 : row + 1;
const cell = document.querySelector(
`#${col + direction}`,
);
if (cell) {
cell.focus();
}
}
}}
/>
<div
className={
errs[col + row] ? 'err' : vals[col + row] ? 'text' : ''
}
>
{errs[col + row] || vals[col + row]}
</div>
</td>
))}
</tr>
))}
</tbody>
</table>
);
};
window.init = () => {
const worker = new Worker('./worker.js');
worker.onmessage = () => {
ReactDom.render(<Sheet worker={worker} />, document.getElementById('root'));
};
worker.postMessage(null);
};