Skip to content

Commit

Permalink
feat: evaluate math in budget inputs
Browse files Browse the repository at this point in the history
fix #46
  • Loading branch information
Xiphe committed Aug 18, 2020
1 parent f667c79 commit 7d1d26f
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 2 deletions.
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"date-fns": "2.15.0",
"electron-settings": "3.2.0",
"electron-updater": "4.3.4",
"expr-eval": "2.0.2",
"focus-visible": "5.1.0",
"fp-ts": "2.8.1",
"io-ts": "2.2.9",
Expand Down
10 changes: 10 additions & 0 deletions src/lib/createNumberFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,21 @@ export default function createNumberFormatter(
maximumFractionDigits: fractionDigits,
});
const fractionDelimiter = formatter.format(1.1).charAt(1);
const delimiters = Array.from(
new Set(
formatter
.format(99999999)
.replace(/9/g, '')
.split('')
.join(fractionDelimiter),
),
);
const cleanPattern = new RegExp(`[^-+0-9${fractionDelimiter}]`, 'g');

return {
fractionStep,
fractionDelimiter,
delimiters,
format(
value: number,
{ thousandDelimiter = true, withFractions = true }: FormatOptions = {},
Expand Down
37 changes: 35 additions & 2 deletions src/lib/useAmountInputProps.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useCallback } from 'react';
import { NumberFormatter } from './createNumberFormatter';
import useInputProps from './useInputProps';
import { Parser } from 'expr-eval';

function isHTMLInputElement(elm: any): elm is HTMLInputElement {
return elm instanceof HTMLInputElement;
Expand All @@ -15,12 +16,44 @@ export default function useCurrencyInput({
value,
onChange,
onKeyDown,
numberFormatter: { fractionDelimiter, format, parse, fractionStep },
numberFormatter: {
fractionDelimiter,
format,
parse,
fractionStep,
delimiters,
},
}: CurrencyInputConfig) {
const { error: _, ...inputProps } = useInputProps<number>({
value,
onChange: onChange,
validate: useCallback((ev) => parse(ev.target.value), [parse]),
validate: useCallback(
(ev) => {
if (typeof ev.target.value !== 'string') {
throw new Error('Unexpected non-string input value');
}

try {
const evaled = Parser.evaluate(
ev.target.value.replace(
new RegExp(`[0-9${delimiters.join('')}]+`, 'g'),
(v) => String(parse(v)),
),
);

if (typeof evaled !== 'number') {
throw new Error(
`Input value evaluated to ${evaled}, expected a number`,
);
}

return evaled;
} catch (err) {
return parse(ev.target.value);
}
},
[parse, delimiters],
),
toInputFormat: useCallback(
(value) => format(value, { thousandDelimiter: false }),
[format],
Expand Down

0 comments on commit 7d1d26f

Please sign in to comment.