Skip to content

Commit

Permalink
feat: rename methods to improve clarity of field-level or form-level
Browse files Browse the repository at this point in the history
  • Loading branch information
lars-berger committed Oct 26, 2024
1 parent d0dcc12 commit 6fcfe73
Show file tree
Hide file tree
Showing 21 changed files with 372 additions and 313 deletions.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
### Functions

Form-level properties & methods

```ts
value: V;
isDirty(): boolean;
isInvalid(): boolean;
isTouched(): boolean;
setValue(value: V | ((val: V) => V), options?: SetValueOptions): void;
unsetDirty(): void;
unsetTouched(): void;
```

Field-level properties & methods

```ts
getFieldValue<P extends FieldPath<V>>(fieldPath: P): FieldValue<V, P>;
isFieldDirty<P extends FieldPath<V>>(fieldPath: P): boolean;
isFieldInvalid<P extends FieldPath<V>>(fieldPath: P): boolean;
isFieldTouched<P extends FieldPath<V>>(fieldPath: P): boolean;
setFieldDirty<P extends FieldPath<V>>(fieldPath: P, options?: SetDirtyOptions): void;
setFieldTouched<P extends FieldPath<V>>(fieldPath: P, options?: SetTouchedOptions): void;
setFieldValue<P extends FieldPath<V>>(fieldPath: P, value: | FieldValue<V, P> | ((val: FieldValue<V, P>) => FieldValue<V, P>), options?: SetValueOptions): void;
unsetFieldDirty<P extends FieldPath<V>>(fieldPath: P, options?: UnsetDirtyOptions): void;
unsetFieldTouched<P extends FieldPath<V>>(fieldPath: P, options?: UnsetTouchedOptions): void;
```
### Type-safe transforms
```tsx
Expand Down
28 changes: 14 additions & 14 deletions src/components/field.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import type {
NestedFormValue,
} from '../types';
import {
getError,
getValue,
isDirty,
isInvalid,
isTouched,
getFieldError,
getFieldValue,
isFieldDirty,
isFieldInvalid,
isFieldTouched,
} from '../methods';

/**
Expand Down Expand Up @@ -77,22 +77,22 @@ export function Field<
const transform = props.transform;

const fieldState = {
error: createMemo(() => getError(formState, fieldPath)),
isDirty: createMemo(() => isDirty(formState, fieldPath)),
isInvalid: createMemo(() => isInvalid(formState, fieldPath)),
isTouched: createMemo(() => isTouched(formState, fieldPath)),
value: createMemo(() => getValue(formState, fieldPath)),
error: createMemo(() => getFieldError(formState, fieldPath)),
isDirty: createMemo(() => isFieldDirty(formState, fieldPath)),
isInvalid: createMemo(() => isFieldInvalid(formState, fieldPath)),
isTouched: createMemo(() => isFieldTouched(formState, fieldPath)),
value: createMemo(() => getFieldValue(formState, fieldPath)),
};

const fieldProps = createMemo(() => {
const value = getValue(formState, fieldPath);
const value = getFieldValue(formState, fieldPath);

// Transform incoming value if there is a transform function.
const incomingValue = transform?.in ? transform.in(value) : value;

return {
value: incomingValue as T,
onBlur: () => formState.setTouched(fieldPath),
onBlur: () => formState.setFieldTouched(fieldPath),
onChange: (eventOrValue: ChangeEvent<T> | T) => {
const value = getChangeValue(eventOrValue);

Expand All @@ -101,8 +101,8 @@ export function Field<
? transform.out(value)
: (value as FieldValue<V, P>);

formState.setValue(fieldPath, outgoingValue);
formState.setDirty(fieldPath);
formState.setFieldValue(fieldPath, outgoingValue);
formState.setFieldDirty(fieldPath);
},
};
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { FormValue, FormState, FieldPath } from '../types';
/**
* Get error for a field (if there is one).
*/
export function getError<V extends FormValue, P extends FieldPath<V>>(
export function getFieldError<V extends FormValue, P extends FieldPath<V>>(
formState: FormState<V>,
fieldPath: P,
): string | null {
Expand Down
6 changes: 3 additions & 3 deletions src/methods/get-value.ts → src/methods/get-field-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import type {
* @example
* ```typescript
* const form = createForm({ name: { first: 'bob' } });
* form.getValue(form, 'name.first') // 'bob'
* form.getFieldValue(form, 'name.first') // 'bob'
* ```
*/
export function getValue<V extends FormValue, P extends FieldPath<V>>(
export function getFieldValue<V extends FormValue, P extends FieldPath<V>>(
formState: FormState<V>,
fieldPath: P,
): FieldValue<V, P> {
Expand All @@ -23,6 +23,6 @@ export function getValue<V extends FormValue, P extends FieldPath<V>>(
.split('.')
.reduce(
(state, key) => (state as any)?.[key],
formState.formValue,
formState.value,
) as FieldValue<V, P>;
}
14 changes: 10 additions & 4 deletions src/methods/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
export * from './get-error';
export * from './get-value';
export * from './get-field-error';
export * from './get-field-value';
export * from './is-dirty';
export * from './is-field-dirty';
export * from './is-field-invalid';
export * from './is-field-touched';
export * from './is-invalid';
export * from './is-touched';
export * from './set-dirty';
export * from './set-touched';
export * from './set-field-dirty';
export * from './set-field-touched';
export * from './set-field-value';
export * from './set-value';
export * from './unset-dirty';
export * from './unset-field-dirty';
export * from './unset-field-touched';
export * from './unset-touched';
35 changes: 5 additions & 30 deletions src/methods/is-dirty.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,15 @@
import type { FieldPath, FormState, FormValue } from '../types';
import { isTraversable } from '../utils';
import type { FormState, FormValue } from '../types';

/**
* Whether the form or form field is dirty.
* Whether the form is dirty.
*
* A form or form field is dirty if:
* * The direct field path has been modified.
* * Any of its descendant field paths have been modified.
* A form is dirty if any of its field paths are dirty.
*
* @param formState Form state.
* @param fieldPath (optional) If provided, checks whether the field is dirty;
* otherwise checks the form as a whole.
*/
export function isDirty<V extends FormValue, P extends FieldPath<V>>(
export function isDirty<V extends FormValue>(
formState: FormState<V>,
fieldPath?: P,
): boolean {
const { formValue } = formState;
const { dirtyFieldPaths } = formState.__internal.fieldStates;

if (!fieldPath) {
return dirtyFieldPaths.size > 0;
}

if (dirtyFieldPaths.has(fieldPath)) {
return true;
}

// No need to check descendants if the value is not an object or array.
if (isTraversable(formValue)) {
for (const dirtyFieldPath of dirtyFieldPaths) {
if (dirtyFieldPath.startsWith(fieldPath)) {
return true;
}
}
}

return false;
return dirtyFieldPaths.size > 0;
}
35 changes: 35 additions & 0 deletions src/methods/is-field-dirty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { FieldPath, FormState, FormValue } from '../types';
import { isTraversable } from '../utils';

/**
* Whether the form field is dirty.
*
* A form field is dirty if:
* * The direct field path has been modified.
* * Any of its descendant field paths have been modified.
*
* @param formState Form state.
* @param fieldPath The field to check for whether it's dirty.
*/
export function isFieldDirty<V extends FormValue, P extends FieldPath<V>>(
formState: FormState<V>,
fieldPath: P,
): boolean {
const { value: formValue } = formState;
const { dirtyFieldPaths } = formState.__internal.fieldStates;

if (dirtyFieldPaths.has(fieldPath)) {
return true;
}

// No need to check descendants if the value is not an object or array.
if (isTraversable(formValue)) {
for (const dirtyFieldPath of dirtyFieldPaths) {
if (dirtyFieldPath.startsWith(fieldPath)) {
return true;
}
}
}

return false;
}
35 changes: 35 additions & 0 deletions src/methods/is-field-invalid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { FieldPath, FormState, FormValue } from '../types';
import { isTraversable } from '../utils';

/**
* Whether the form field is invalid.
*
* A form field is invalid if:
* * The direct field path is invalid.
* * Any of its descendant field paths are invalid.
*
* @param formState Form state.
* @param fieldPath The field to check for whether it's invalid.
*/
export function isFieldInvalid<
V extends FormValue,
P extends FieldPath<V>,
>(formState: FormState<V>, fieldPath: P): boolean {
const { value: formValue } = formState;
const { invalidFieldPaths } = formState.__internal.fieldStates;

if (invalidFieldPaths.has(fieldPath)) {
return true;
}

// No need to check descendants if the value is not an object or array.
if (isTraversable(formValue)) {
for (const invalidFieldPath of invalidFieldPaths) {
if (invalidFieldPath.startsWith(fieldPath)) {
return true;
}
}
}

return false;
}
35 changes: 35 additions & 0 deletions src/methods/is-field-touched.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { FormValue, FormState, FieldPath } from '../types';
import { isTraversable } from '../utils';

/**
* Whether the form field is touched.
*
* A form field is touched if:
* * The direct field path has been touched.
* * Any of its descendant field paths have been touched.
*
* @param formState Form state.
* @param fieldPath The field to check for whether it's touched.
*/
export function isFieldTouched<
V extends FormValue,
P extends FieldPath<V>,
>(formState: FormState<V>, fieldPath: P): boolean {
const { value: formValue } = formState;
const { touchedFieldPaths } = formState.__internal.fieldStates;

if (touchedFieldPaths.has(fieldPath)) {
return true;
}

// No need to check descendants if the value is not an object or array.
if (isTraversable(formValue)) {
for (const touchedFieldPath of touchedFieldPaths) {
if (touchedFieldPath.startsWith(fieldPath)) {
return true;
}
}
}

return false;
}
35 changes: 5 additions & 30 deletions src/methods/is-invalid.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,15 @@
import type { FieldPath, FormState, FormValue } from '../types';
import { isTraversable } from '../utils';
import type { FormState, FormValue } from '../types';

/**
* Whether the form or form field is invalid.
* Whether the form is invalid.
*
* A form or form field is invalid if:
* * The direct field path is invalid.
* * Any of its descendant field paths are invalid.
* A form is invalid if any of its field paths are invalid.
*
* @param formState Form state.
* @param fieldPath (optional) If provided, checks whether the field is invalid;
* otherwise checks the form as a whole.
*/
export function isInvalid<V extends FormValue, P extends FieldPath<V>>(
export function isInvalid<V extends FormValue>(
formState: FormState<V>,
fieldPath?: P,
): boolean {
const { formValue } = formState;
const { invalidFieldPaths } = formState.__internal.fieldStates;

if (!fieldPath) {
return invalidFieldPaths.size > 0;
}

if (invalidFieldPaths.has(fieldPath)) {
return true;
}

// No need to check descendants if the value is not an object or array.
if (isTraversable(formValue)) {
for (const invalidFieldPath of invalidFieldPaths) {
if (invalidFieldPath.startsWith(fieldPath)) {
return true;
}
}
}

return false;
return invalidFieldPaths.size > 0;
}
35 changes: 5 additions & 30 deletions src/methods/is-touched.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,15 @@
import type { FormValue, FormState, FieldPath } from '../types';
import { isTraversable } from '../utils';
import type { FormState, FormValue } from '../types';

/**
* Whether the form or form field is touched.
* Whether the form is touched.
*
* A form or form field is touched if:
* * The direct field path has been touched.
* * Any of its descendant field paths have been touched.
* A form is touched if any of its field paths are touched.
*
* @param formState Form state.
* @param fieldPath (optional) If provided, checks whether the field is touched;
* otherwise checks the form as a whole.
*/
export function isTouched<V extends FormValue, P extends FieldPath<V>>(
export function isTouched<V extends FormValue>(
formState: FormState<V>,
fieldPath?: P,
): boolean {
const { formValue } = formState;
const { touchedFieldPaths } = formState.__internal.fieldStates;

if (!fieldPath) {
return touchedFieldPaths.size > 0;
}

if (touchedFieldPaths.has(fieldPath)) {
return true;
}

// No need to check descendants if the value is not an object or array.
if (isTraversable(formValue)) {
for (const touchedFieldPath of touchedFieldPaths) {
if (touchedFieldPath.startsWith(fieldPath)) {
return true;
}
}
}

return false;
return touchedFieldPaths.size > 0;
}
Loading

0 comments on commit 6fcfe73

Please sign in to comment.