Skip to content

Commit 33c4837

Browse files
feat: allow math expression eval to be disabled, default to off
1 parent 6f3598c commit 33c4837

File tree

2 files changed

+38
-14
lines changed

2 files changed

+38
-14
lines changed

lib/components/number-input/composite-number-input.tsx

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,19 @@ export type CompositeNumberInputProps = Omit<NumberInputProps, 'onChange' | 'min
4141
* An optional callback to constrain the value. For example, to round it to the nearest multiple of 8.
4242
*/
4343
constrainValue?: (v: number) => number;
44+
/**
45+
* Whether to allow math expressions (e.g. "1+2/3*4"). Defaults to false.
46+
* Expressions are evaluated using the math-expression-evaluator library. Trigonometric functions use degrees.
47+
*/
48+
allowMath?: boolean;
4449
};
4550

4651
const roundToMultiple = (value: number, multiple: number): number => {
4752
return Math.round(value / multiple) * multiple;
4853
};
4954

5055
const mexp = new Mexp();
51-
const isValidCharacter = (_: string) => true;
56+
const isValidCharacterAllowMath = (_: string) => true;
5257

5358
export const CompositeNumberInput: ComponentWithAs<
5459
ComponentWithAs<'div', NumberInputProps>,
@@ -64,6 +69,7 @@ export const CompositeNumberInput: ComponentWithAs<
6469
onChange: _onChange,
6570
defaultValue,
6671
constrainValue,
72+
allowMath,
6773
...rest
6874
} = props;
6975
const [localValue, setLocalValue] = useState(String(value));
@@ -84,27 +90,44 @@ export const CompositeNumberInput: ComponentWithAs<
8490
}, []);
8591

8692
const pushLocalValue = useCallback(() => {
87-
let localValueAsNumber = Number(localValue);
88-
89-
if (isNaN(localValueAsNumber)) {
90-
localValueAsNumber = mexp.eval(localValue);
91-
92-
if (isNaN(localValueAsNumber)) {
93-
setLocalValue(String(isNumber(defaultValue) ? defaultValue : min));
94-
return;
93+
let nextValue;
94+
if (allowMath) {
95+
try {
96+
nextValue = mexp.eval(localValue);
97+
} catch {
98+
nextValue = NaN;
9599
}
100+
} else {
101+
nextValue = Number(localValue);
102+
}
103+
104+
if (isNaN(nextValue)) {
105+
setLocalValue(String(isNumber(defaultValue) ? defaultValue : min));
106+
return;
96107
}
97108

98109
// Otherwise, we round the value to the nearest multiple if integer, else 3 decimals
99110
const roundedValue = isInteger
100-
? roundToMultiple(localValueAsNumber, _fineStep ?? _step)
101-
: Number(localValueAsNumber.toFixed(precision));
111+
? roundToMultiple(nextValue, _fineStep ?? _step)
112+
: Number(nextValue.toFixed(precision));
102113
// Clamp to min/max
103114
const clampedValue = clamp(roundedValue, min, max);
104115
const constrainedValue = constrainValue ? constrainValue(clampedValue) : clampedValue;
105116
_onChange(constrainedValue);
106117
setLocalValue(String(constrainedValue));
107-
}, [_fineStep, _onChange, _step, defaultValue, isInteger, localValue, max, min, precision, constrainValue]);
118+
}, [
119+
allowMath,
120+
isInteger,
121+
_fineStep,
122+
_step,
123+
precision,
124+
min,
125+
max,
126+
constrainValue,
127+
_onChange,
128+
localValue,
129+
defaultValue,
130+
]);
108131

109132
const onClickStepper = useCallback(() => {
110133
pushLocalValue();
@@ -137,7 +160,7 @@ export const CompositeNumberInput: ComponentWithAs<
137160
precision={precision}
138161
variant="filled"
139162
onKeyDown={onKeyDown}
140-
isValidCharacter={isValidCharacter}
163+
isValidCharacter={allowMath ? isValidCharacterAllowMath : undefined}
141164
{...rest}
142165
>
143166
<NumberInputField onBlur={pushLocalValue} />

lib/components/number-input/number-input.stories.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,16 @@ const Component = (props: CompositeNumberInputProps) => {
1919
const [value, setValue] = useState(1024);
2020
return (
2121
<CompositeNumberInput
22-
{...props}
2322
defaultValue={1024}
23+
{...props}
2424
min={64}
2525
max={4096}
2626
step={64}
2727
fineStep={8}
2828
value={value}
2929
onChange={setValue}
3030
constrainValue={constrainValue}
31+
allowMath
3132
/>
3233
);
3334
};

0 commit comments

Comments
 (0)