Skip to content

Commit 85f58ab

Browse files
fix: fix support for decimals with . in it
1 parent 5b42099 commit 85f58ab

File tree

2 files changed

+80
-18
lines changed

2 files changed

+80
-18
lines changed

example/src/App.tsx

+47-11
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,24 @@ import { View } from 'react-native';
44
import { useFormState, Form } from '../../src/index';
55
import { Button, HelperText, TextInput } from 'react-native-paper';
66

7+
type FormType = {
8+
email: string;
9+
telephone: string;
10+
password: string;
11+
age: number | undefined;
12+
money: number | undefined;
13+
};
714
export default function App() {
815
const [
9-
{ errors, submit, formProps, hasError },
10-
{ email, telephone, password },
11-
] = useFormState(
16+
{ values, errors, submit, formProps, hasError },
17+
fh,
18+
] = useFormState<FormType>(
1219
{
1320
email: '',
1421
telephone: '',
1522
password: '',
23+
age: 0,
24+
money: 0,
1625
},
1726
{
1827
onChange: () => {
@@ -24,20 +33,25 @@ export default function App() {
2433
},
2534
}
2635
);
36+
37+
console.log({ values });
2738
return (
2839
<View
2940
style={{
3041
flex: 1,
31-
maxWidth: 500,
32-
alignSelf: 'center',
42+
marginTop: 100,
43+
marginLeft: 12,
44+
marginRight: 12,
45+
// alignSelf: 'center',
3346
}}
3447
>
3548
<Form {...formProps}>
3649
<TextInput
3750
mode="outlined"
3851
error={hasError('email')}
39-
{...email('email', {
52+
{...fh.email('email', {
4053
validate: (v) => {
54+
//@ts-ignore
4155
return looksLikeMail(v) ? true : 'Email-address is invalid';
4256
},
4357
})}
@@ -48,13 +62,14 @@ export default function App() {
4862
</HelperText>
4963
<TextInput
5064
mode="outlined"
51-
{...telephone('telephone', {
65+
{...fh.telephone('telephone', {
5266
validate: (v) => {
5367
console.log({ v });
68+
//@ts-ignore
5469
return looksLikeTelephone(v) ? true : 'Telephone is invalid';
5570
},
5671
})}
57-
label="Telefoon"
72+
label="Telephone"
5873
error={hasError('telephone')}
5974
/>
6075
<HelperText type="error" visible={hasError('telephone')}>
@@ -63,18 +78,39 @@ export default function App() {
6378

6479
<TextInput
6580
mode="outlined"
66-
{...password('password', {
81+
{...fh.password('password', {
6782
required: true,
6883
minLength: 3,
6984
maxLength: 10,
7085
})}
71-
label="Wachtwoord"
86+
label="Password"
7287
error={hasError('password')}
7388
/>
7489
<HelperText type="error" visible={hasError('password')}>
7590
{errors.password}
7691
</HelperText>
77-
<Button mode="contained" onPress={submit}>
92+
93+
<TextInput
94+
mode="outlined"
95+
{...fh.number('age', {
96+
required: true,
97+
minLength: 3,
98+
maxLength: 10,
99+
})}
100+
label="Age"
101+
error={hasError('password')}
102+
/>
103+
<TextInput
104+
mode="outlined"
105+
{...fh.decimal('money', {
106+
required: true,
107+
minLength: 3,
108+
maxLength: 10,
109+
})}
110+
label="Money bank account"
111+
error={hasError('password')}
112+
/>
113+
<Button mode="contained" onPress={submit} style={{ marginTop: 24 }}>
78114
Save
79115
</Button>
80116
</Form>

src/useFormState.ts

+33-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import * as React from 'react';
2-
import type {
2+
import {
33
LayoutChangeEvent,
44
NativeSyntheticEvent,
55
TextInput,
66
TextInputFocusEventData,
77
TextInputProps,
8+
Platform,
89
} from 'react-native';
910

1011
import { FormContext, FormContextType } from './FormContext';
@@ -61,6 +62,9 @@ type FieldsBoolean<T> = {
6162
type FieldsError<T> = {
6263
[key in keyof T]?: boolean | string | undefined;
6364
};
65+
type FieldsLastCharacters<T> = {
66+
[key in keyof T]?: string | undefined;
67+
};
6468

6569
type ReferencerReturns = TextInputProps & { ref: React.Ref<TextInput> };
6670
export type ReferencerType = (
@@ -86,6 +90,10 @@ export function indexer(): IndexerType {
8690
};
8791
}
8892

93+
function withoutLastCharacter(s: string) {
94+
return s.substring(0, s.length - 1);
95+
}
96+
8997
export function useFormContext(): FormContextType & {
9098
formIndex: number;
9199
} {
@@ -248,6 +256,9 @@ export default function useFormState<T>(
248256
const [touched, sTouched] = React.useState<FieldsBoolean<T>>({});
249257
const [errors, sErrors] = React.useState<FieldsError<T>>({});
250258
const [values, setValues] = React.useState<T>(initialState);
259+
const [lastCharacters, setLastCharacters] = React.useState<
260+
FieldsLastCharacters<T>
261+
>({});
251262

252263
const valuesRef = useLatest(values);
253264
const onChangeRef = useLatest(options?.onChange);
@@ -272,11 +283,12 @@ export default function useFormState<T>(
272283
v: T[K],
273284
allV: T
274285
) => {
275-
let err: boolean | string | undefined = undefined;
286+
let err: boolean | string | undefined;
276287

277288
if (h) {
278289
err = h.validate?.(v, allV);
279290
if (!err) {
291+
// TODO: add locale support
280292
if (h?.required === true && !v) {
281293
err = `${k} is required`;
282294
} else if (h.minLength !== undefined && `${v}`.length < h.minLength) {
@@ -375,13 +387,27 @@ export default function useFormState<T>(
375387
...ctx.referencer(k as any, ctx.formIndex),
376388
testID: k as string,
377389
onChangeText: referencedCallback(`number.${k}`, (n: string) => {
378-
if (n !== '') {
379-
changeValue(k, Number(n) as any, h);
390+
const endsWithSeparator = n.endsWith(',') || n.endsWith('.');
391+
392+
if (endsWithSeparator) {
393+
setLastCharacters((prev) => ({ ...prev, [k]: n[n.length - 1] }));
394+
} else {
395+
setLastCharacters((prev) => ({ ...prev, [k]: undefined }));
396+
}
397+
398+
if (n === '') {
399+
changeValue(k, null as any, h);
400+
} else {
401+
changeValue(
402+
k,
403+
Number(endsWithSeparator ? withoutLastCharacter(n) : n) as any,
404+
h
405+
);
380406
}
381407
}),
382408
onBlur: blur(k, h),
383409
onLayout: layout(k, h),
384-
value: `${(values?.[k] || '') as string}`,
410+
value: `${(values?.[k] || '') as string}${lastCharacters[k] || ''}`,
385411
});
386412

387413
const number = <K extends keyof T>(
@@ -477,7 +503,7 @@ export default function useFormState<T>(
477503
autoCompleteType: 'username',
478504
autoCapitalize: 'none',
479505
autoCorrect: false,
480-
selectTextOnFocus: true,
506+
selectTextOnFocus: Platform.OS !== 'web',
481507
});
482508

483509
const password = <K extends keyof T>(
@@ -489,7 +515,7 @@ export default function useFormState<T>(
489515
autoCompleteType: 'password',
490516
secureTextEntry: true,
491517
autoCorrect: false,
492-
selectTextOnFocus: true,
518+
selectTextOnFocus: Platform.OS !== 'web',
493519
});
494520

495521
const email = <K extends keyof T>(

0 commit comments

Comments
 (0)