Skip to content

Commit 5af8c03

Browse files
authored
feat: allow undefined value to e.g. show a placeholder
* feat: allow undefined value to e.g. show a placeholder * chore: remove defaultvalue for demo scene
1 parent 18269c6 commit 5af8c03

File tree

4 files changed

+220
-169
lines changed

4 files changed

+220
-169
lines changed

lib/components/CurrencyTextField/CurrencyTextField.tsx

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import TextField, { type TextFieldProps } from '@mui/material/TextField';
99
* Extends Material UI's TextFieldProps, excluding 'onChange' and 'defaultValue'.
1010
*/
1111
interface CurrencyTextFieldProps
12-
extends Omit<TextFieldProps, 'onBlur' | 'onChange' | 'defaultValue'> {
12+
extends Omit<
13+
TextFieldProps,
14+
'onBlur' | 'onChange' | 'defaultValue' | 'type'
15+
> {
1316
/**
1417
* The character used as the decimal separator.
1518
* Defaults to '.'.
@@ -102,32 +105,28 @@ export const CurrencyTextField: React.FC<CurrencyTextFieldProps> = ({
102105
...props
103106
}) => {
104107
// Internal state to manage the input value.
105-
const [internalValue, setInternalValue] = useState<Dinero.Dinero>(
106-
(value || defaultValue) ??
107-
Dinero({
108-
amount: 0,
109-
currency: currency,
110-
precision: precision,
111-
}),
108+
const [internalValue, setInternalValue] = useState<Dinero.Dinero | undefined>(
109+
value ?? defaultValue ?? undefined,
112110
);
113111

114-
/**
115-
* TODO: fix loop when precision changes
116-
*/
117112
useEffect(() => {
118-
setInternalValue((prevState) => ({
119-
...prevState,
120-
currency: currency,
121-
precision: precision,
122-
}));
123-
}, [currency, precision]);
113+
if (internalValue) {
114+
const updatedValue = Dinero({
115+
amount: internalValue.getAmount(),
116+
currency,
117+
precision,
118+
});
119+
120+
// Update only if there's a real change to avoid unnecessary re-renders
121+
if (!updatedValue.equalsTo(internalValue)) {
122+
setInternalValue(updatedValue);
123+
}
124+
}
125+
}, [currency, internalValue, precision]);
124126

125-
/**
126-
* Update the internal value when the value prop changes.
127-
* This is necessary to keep the input value in sync with the value prop.
128-
*/
127+
// Update internal value only if `value` prop changes and is defined
129128
useEffect(() => {
130-
if (value) {
129+
if (value !== undefined) {
131130
setInternalValue(value);
132131
}
133132
}, [value]);
@@ -149,9 +148,8 @@ export const CurrencyTextField: React.FC<CurrencyTextFieldProps> = ({
149148
precision: precision,
150149
});
151150

152-
if (!dineroValue.equalsTo(internalValue)) {
151+
if (!internalValue || !dineroValue.equalsTo(internalValue)) {
153152
onChange?.(dineroValue, values.formattedValue);
154-
155153
setInternalValue(dineroValue);
156154
}
157155
};
@@ -165,7 +163,7 @@ export const CurrencyTextField: React.FC<CurrencyTextFieldProps> = ({
165163
event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>,
166164
) => {
167165
if (onBlur) {
168-
onBlur(event, internalValue);
166+
onBlur(event, internalValue as Dinero.Dinero);
169167
}
170168
};
171169

@@ -181,18 +179,20 @@ export const CurrencyTextField: React.FC<CurrencyTextFieldProps> = ({
181179
* Formats the dinero value to a matching number that can be use in NumericFormat
182180
*/
183181
const formattedValue = useMemo(() => {
184-
const amount = internalValue?.getAmount() ?? 0;
185-
const precision = internalValue?.getPrecision() ?? 2;
186-
const factor = Math.pow(10, precision);
182+
if (!internalValue) return ''; // Return empty string if no value
183+
const amount = internalValue.getAmount();
184+
const factor = Math.pow(10, internalValue.getPrecision());
187185
return (amount / factor).toFixed(precision);
188-
}, [internalValue]);
186+
}, [internalValue, precision]);
189187

190188
/**
191189
* Formats the dinero value to a matching number that can be use in NumericFormat
192190
*/
193191
const formattedDefaultValue = useMemo(() => {
194-
const amount = defaultValue?.getAmount() ?? 0;
195-
const precision = defaultValue?.getPrecision() ?? 2;
192+
if (!defaultValue) return '';
193+
194+
const amount = defaultValue.getAmount() ?? 0;
195+
const precision = defaultValue.getPrecision() ?? 2;
196196
const factor = Math.pow(10, precision);
197197
return (amount / factor).toFixed(precision);
198198
}, [defaultValue]);
@@ -215,7 +215,7 @@ export const CurrencyTextField: React.FC<CurrencyTextFieldProps> = ({
215215
onFocus={handleFocus}
216216
onValueChange={handleValueChange}
217217
{...props}
218-
type={'text'}
218+
type="text"
219219
/>
220220
);
221221
};

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@
4444
"devDependencies": {
4545
"@emotion/react": "^11.13.0",
4646
"@emotion/styled": "^11.13.0",
47-
"@mui/material": "^5.16.4",
48-
"@mui/styles": "^5.16.4",
47+
"@mui/material": "^5.16.7",
48+
"@mui/styles": "^5.16.7",
4949
"@types/dinero.js": "^1.9.4",
5050
"@types/node": "^20.14.6",
5151
"@types/react": "^18.2.66",
@@ -57,11 +57,11 @@
5757
"eslint": "^8.57.0",
5858
"eslint-plugin-react-hooks": "^4.6.0",
5959
"eslint-plugin-react-refresh": "^0.4.6",
60-
"prettier": "^3.3.2",
60+
"prettier": "3.3.3",
6161
"react": "^18.3.1",
6262
"react-dom": "^18.3.1",
6363
"typescript": "^5.2.2",
64-
"vite": "^5.2.0",
64+
"vite": "^5.4.10",
6565
"vite-plugin-dts": "^4.0.1"
6666
},
6767
"dependencies": {

src/App.tsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ export const App: React.FC = () => {
77
const [brutto, setBrutto] = React.useState<Dinero.Dinero>(
88
Dinero({ amount: 5000, currency: 'EUR', precision: 2 }),
99
);
10+
const [addBrutto, setAddBrutto] = React.useState<Dinero.Dinero | undefined>(
11+
undefined,
12+
);
1013

1114
return (
1215
<Box
@@ -27,6 +30,25 @@ export const App: React.FC = () => {
2730
setBrutto(value);
2831
}}
2932
/>
33+
<CurrencyTextField
34+
label={'Zusätzliches Einkommen'}
35+
name={'add-einkommen'}
36+
value={addBrutto}
37+
variant={'outlined'}
38+
currencySymbol="€"
39+
currency="EUR"
40+
precision={2}
41+
minimumValue={0}
42+
InputLabelProps={{
43+
shrink: true,
44+
}}
45+
decimalCharacter=","
46+
digitGroupSeparator="."
47+
placeholder="0.00"
48+
onChange={(value) => {
49+
setAddBrutto(value);
50+
}}
51+
/>
3052
<Button
3153
variant="contained"
3254
color="primary"
@@ -47,7 +69,9 @@ export const App: React.FC = () => {
4769
>
4870
Auf 50 € setzen
4971
</Button>
50-
<Typography>Dinero amount: {brutto.getAmount()}</Typography>
72+
<Typography>
73+
Dinero amount: {brutto.getAmount() + (addBrutto?.getAmount() || 0)}
74+
</Typography>
5175
</Box>
5276
);
5377
};

0 commit comments

Comments
 (0)