-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathMatrix.tsx
More file actions
115 lines (97 loc) · 3.31 KB
/
Matrix.tsx
File metadata and controls
115 lines (97 loc) · 3.31 KB
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
import React, { useState, useImperativeHandle, forwardRef, useEffect } from "react";
import { RoundedArrow } from "./RoundedArrow";
interface MatrixProps {
rows: number;
cols: number;
input?: boolean;
values?: number[][];
className?: string;
matrixName?: string;
locked?: boolean;
}
export interface MatrixRef {
getValues: () => number[][];
}
export const Matrix = forwardRef<MatrixRef, MatrixProps>(({ rows, cols, input = false, values, className, matrixName, locked = false }, ref) => {
const defaultValues = Array.from({ length: rows }, (_, i) =>
Array.from({ length: cols }, (_, j) => values?.[i]?.[j] ?? 0)
);
const [matrixValues, setMatrixValues] = useState(defaultValues);
// Expose the getValues method to parent components
useImperativeHandle(ref, () => ({
getValues: () => matrixValues
}));
const bracket_height = 40 * rows + 8;
const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>, i: number, j: number) => {
const key = e.key;
// Only allow digits
if (!/^\d$/.test(key)) {
e.preventDefault();
return;
}
const currentValue = matrixValues[i][j];
const currentStr = currentValue.toFixed(2);
// Shift digits to the left and add new digit to the right
const newStr = currentStr.slice(1) + key;
const newValue = parseFloat(newStr) * 10;
// Update the matrix value
const newMatrixValues = [...matrixValues];
newMatrixValues[i][j] = newValue;
setMatrixValues(newMatrixValues);
e.preventDefault();
};
useEffect(() => {
if (values) {
setMatrixValues(values);
}
}, [values]);
return (
<div className="relative m-4">
{/* Left bracket */}
<div className="absolute -left-[10px] -top-[10px] -translate-y-1/2">
<RoundedArrow
points={[[0, 0], [-5, 0], [-5, -bracket_height], [0, -bracket_height]]}
color="#666"
strokeWidth={0.75}
className="transform"
radius={2}
arrowHeadSize={0}
/>
</div>
{/* Right bracket */}
<div className="absolute -right-[5px] -top-[10px] -translate-y-1/2">
<RoundedArrow
points={[[0, 0], [5, 0], [5, -bracket_height], [0, -bracket_height]]}
color="#666"
strokeWidth={0.75}
className="transform"
radius={2}
arrowHeadSize={0}
/>
</div>
<div className={`inline-grid gap-2 ${className}`} style={{ gridTemplateColumns: `repeat(${cols}, 60px)` }}>
{matrixValues.map((row, i) =>
row.map((val, j) => (
<input
key={`${i}-${j}`}
type="text"
inputMode="numeric"
value={val.toFixed(2)}
disabled={!input || locked}
onKeyDown={(e) => input && !locked && handleKeyPress(e, i, j)}
readOnly
className={`text-center text-white font-mono text-sm px-2 py-1 bg-black border rounded border-neutral-700 ${
input && !locked ? "focus:outline-none focus:border-blue-500" : "text-neutral-500"
}`}
/>
))
)}
</div>
<p className="mt-2 text-center text-sm font-light">
{matrixName}
</p>
</div>
);
});
// Add display name to the component
Matrix.displayName = 'Matrix';