-
Notifications
You must be signed in to change notification settings - Fork 30
Expand file tree
/
Copy pathcalc.ts
More file actions
97 lines (83 loc) · 2.29 KB
/
calc.ts
File metadata and controls
97 lines (83 loc) · 2.29 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
/* eslint-disable */
import { isStyleDescriptorArray } from "../../utils";
import type { StyleFunctionResolver } from "./resolve";
const calcPrecedence: Record<string, number> = {
"+": 1,
"-": 1,
"*": 2,
"/": 2,
};
export const calc: StyleFunctionResolver = (resolveValue, func) => {
let mode: "number" | "percentage" | undefined;
const values: number[] = [];
const ops: string[] = [];
const args = resolveValue(func[2]);
if (!isStyleDescriptorArray(args)) return;
for (let token of args) {
if (typeof token === "number") {
if (!mode) mode = "number";
if (mode !== "number") return;
values.push(token);
continue;
} else if (typeof token === "string") {
if (token === "(") {
ops.push(token);
} else if (token === ")") {
// Resolve all values within the brackets
while (ops.length && ops[ops.length - 1] !== "(") {
applyCalcOperator(ops.pop()!, values.pop(), values.pop(), values);
}
ops.pop();
} else if (token.endsWith("%")) {
if (!mode) mode = "percentage";
if (mode !== "percentage") return;
values.push(Number.parseFloat(token.slice(0, -1)));
} else {
// This means we have an operator
while (
ops.length &&
ops[ops.length - 1] &&
// @ts-ignore
calcPrecedence[ops[ops.length - 1]] >= calcPrecedence[token]
) {
applyCalcOperator(ops.pop()!, values.pop(), values.pop(), values);
}
ops.push(token);
}
} else {
// We got something unexpected
return;
}
}
while (ops.length) {
applyCalcOperator(ops.pop()!, values.pop(), values.pop(), values);
}
if (!mode) return;
const num = values[0];
if (typeof num !== "number") {
return;
}
const value = Math.round((num + Number.EPSILON) * 100) / 100;
if (mode === "percentage") {
return `${value}%`;
}
return value;
};
function applyCalcOperator(
operator: string,
b = 0, // These are reversed because we pop them off the stack
a = 0,
values: number[],
) {
switch (operator) {
case "+":
return values.push(a + b);
case "-":
return values.push(a - b);
case "*":
return values.push(a * b);
case "/":
return values.push(a / b);
}
return;
}