diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6710bde..1c56964 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## v0.1.4
+
+- feat: add `validator` export with built-in validators for easy use
+
## v0.1.3
- feat: add `validate` method
diff --git a/README.md b/README.md
index 2099d54..668092e 100644
--- a/README.md
+++ b/README.md
@@ -51,59 +51,15 @@ const { field, handleSubmit, isValid, errors, state, rawState, setValue, setValu
Use `field()` from the previous hook on your inputs, should support most input types:
```tsx
+// you can import some built-in validators, or create your own
+import { validator } from 'formplex-react'
+
n < 18 ? "Must be 18 or over" : null,
- parse: Number,
-})} />
-
- ...
-
-```
-
-## Quick-start
-
-See the [full documentation](https://chenasraf.github.io/formplex-react/) for all the available
-options, return values and more examples.
-
-### Use the hook
-
-Start by calling the hook, passing in any options you would like for the form, and get the return
-values as needed.
-
-This is a full example of a hook usage with all the available options and return values. All options
-are optional, see the docs for each for more information.
-
-```tsx
-const { field, handleSubmit, isValid, errors, state, rawState, setValue, setValues } =
- useForm({
- initialState: {
- firstName: 'John',
- lastName: 'Doe',
- },
- autoValidateBehavior: 'onChange',
- errorMessages: {
- required: 'This field is required',
- minLength: (n) => `Must be more than ${n} chars long`,
- maxLength: (n) => `Must be less than ${n} chars long`,
- },
- onSubmit(values, e) {
- console.log('Form submitted:', values)
- fetch('/submit', { method: 'POST', body: JSON.stringify(values) })
- },
- })
-```
-
-### Register an input
-
-Use `field()` from the previous hook on your inputs, should support most input types:
-
-```tsx
-
- n < 18 ? "Must be 18 or over" : null,
+ validate: validator.min(18, 'Must be 18 or over'),
+ // You can implement the above validator yourself like this:
+ // validate: (n) => n < 18 ? "Must be 18 or over" : null,
parse: Number,
})} />
diff --git a/package.json b/package.json
index 1f7d10e..e12af08 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "formplex-react",
- "version": "0.1.3",
+ "version": "0.1.4",
"description": "Incredibly easy & flexible React form hooks",
"keywords": [
"react",
diff --git a/src/index.ts b/src/index.ts
index 2d90680..4b7c559 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,2 +1,3 @@
export * from './use-form'
export * from './types'
+export * as validator from './validators'
diff --git a/src/types.ts b/src/types.ts
index 52daa57..fed7b3b 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -118,7 +118,6 @@ export interface UseFormReturn {
*/
validate(): boolean
}
-
/**
* Options for every field. See each property for more information.
*/
@@ -186,17 +185,16 @@ export interface FieldOptions {
* @see {@link UseFormOptions.errorMessages | UseFormOptions.errorMessages} for global error messages for the default validation methods
* @see {@link FieldOptions.errorMessages} for field-specific error messages for the default validation methods
*/
- validate?: (value: T[K]) => string | undefined | null
+ validate?: Validator
/**
- * A custom parser for the field. This will be called when the field is updated, and will cause the
- * {@link UseFormReturn.state} object to be updated with the result of this function.
- *
- * {@link UseFormReturn.rawState | UseFormReturn.rawState} will always contain the raw value of the field as it was
- * placed here.
+ * Parse the value of the field before it is set in the form state.
*
* @param value The value of the field.
- * @returns The parsed value of the field.
+ * @returns The parsed value.
+ *
+ * @see {@link UseFormReturn.state} for the parsed form state
+ * @see {@link UseFormReturn.rawState} for the raw form state
*/
parse?: (value: string) => T[K]
@@ -253,6 +251,69 @@ export interface ErrorMessage {
message: string
}
+/**
+ * Validation function for a field.
+ *
+ * @typeParam T - The type of the form field.
+ */
+export type Validator = (value: T) => string | undefined | null
+
+/**
+ * A function that receives the validation argument of a field, such as the minimum length or the regular expression,
+ * and returns the error message for the field.
+ *
+ * @typeParam T The type of the validation argument (e.g. `minLength` has `number` to represent the minimum length).
+ * @param validation The validation that was not met.
+ */
+export type MessageResolver = (validation: T) => string
+
+/**
+ * Map of custom error messages for the default validation methods.
+ *
+ * @see {@link UseFormOptions.errorMessages | UseFormOptions.errorMessages} for global error messages for the default validation methods
+ * @see {@link FieldOptions.errorMessages | FieldOptions.errorMessages} for field-specific error messages for the default validation methods
+ */
+export interface ErrorStrings {
+ /**
+ * Error message for when the field is required but missing.
+ *
+ * Default: `"Required"`
+ *
+ * @see {@link FieldOptions.required | FieldOptions.required} for defining a field as required
+ */
+ required: string
+
+ /**
+ * Error message for when the field length is too short.
+ *
+ * Can either be a string, or a function resolving to a string.
+ *
+ * Default:
+ * ```ts
+ * (n) => `Must be at least ${n} characters long`
+ * ```
+ *
+ * @see {@link FieldOptions.minLength | FieldOptions.minLength} for defining a minimum length for the field
+ * @see {@link MessageResolver} for the function signature
+ */
+ minLength: string | MessageResolver
+
+ /**
+ * Error message for when the field length is too long.
+ *
+ * Can either be a string, or a function resolving to a string.
+ *
+ * Default:
+ * ```ts
+ * (n) => `Must be no more than ${n} characters long`
+ * ```
+ *
+ * @see {@link FieldOptions.maxLength | FieldOptions.maxLength} for defining a maximum length for the field
+ * @see {@link MessageResolver} for the function signature
+ */
+ maxLength: string | MessageResolver
+}
+
/** @hidden */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export type FieldReturn = {
@@ -285,48 +346,3 @@ export type ChangeEvent = {
}
/** @hidden */
export type BlurEvent = ChangeEvent
-
-/**
- * Map of custom error messages for the default validation methods.
- *
- * @see {@link UseFormOptions.errorMessages | UseFormOptions.errorMessages} for global error messages for the default validation methods
- * @see {@link FieldOptions.errorMessages | FieldOptions.errorMessages} for field-specific error messages for the default validation methods
- */
-export interface ErrorStrings {
- /**
- * Error message for when the field is required but missing.
- *
- * Default: `"Required"`
- *
- * @see {@link FieldOptions.required | FieldOptions.required} for defining a field as required
- */
- required: string
-
- /**
- * Error message for when the field length is too short.
- *
- * Can either be a string, or a function resolving to a string.
- *
- * Default:
- * ```ts
- * (n) => `Must be at least ${n} characters long`
- * ```
- *
- * @see {@link FieldOptions.minLength | FieldOptions.minLength} for defining a minimum length for the field
- */
- minLength: string | ((length: number) => string)
-
- /**
- * Error message for when the field length is too long.
- *
- * Can either be a string, or a function resolving to a string.
- *
- * Default:
- * ```ts
- * (n) => `Must be no more than ${n} characters long`
- * ```
- *
- * @see {@link FieldOptions.maxLength | FieldOptions.maxLength} for defining a maximum length for the field
- */
- maxLength: string | ((length: number) => string)
-}
diff --git a/src/use-form.ts b/src/use-form.ts
index 21e1d89..1b20951 100644
--- a/src/use-form.ts
+++ b/src/use-form.ts
@@ -9,6 +9,7 @@ import {
UseFormOptions,
UseFormReturn,
} from './types'
+import { parseStr } from './utils'
/**
* The main hook for using forms. See each option and return property for more information
@@ -60,9 +61,6 @@ export function useForm({
value: T[K],
options: FieldOptions,
): ErrorMessage | undefined {
- const parseStr = (o: string | ((...args: unknown[]) => string), val: unknown) =>
- typeof o === 'function' ? o(val) : o
-
const errorStrings = {
...errorMessages,
...(options?.errorMessages ?? {}),
diff --git a/src/utils.ts b/src/utils.ts
new file mode 100644
index 0000000..7cbeaf0
--- /dev/null
+++ b/src/utils.ts
@@ -0,0 +1,6 @@
+import { MessageResolver } from './types'
+
+/** @hidden */
+export function parseStr(o: string | MessageResolver, val: T) {
+ return typeof o === 'function' ? o(val) : o
+}
diff --git a/src/validators.ts b/src/validators.ts
new file mode 100644
index 0000000..fddf8a2
--- /dev/null
+++ b/src/validators.ts
@@ -0,0 +1,72 @@
+import { MessageResolver, Validator } from './types'
+import { parseStr } from './utils'
+
+/**
+ * Combine multiple validators into one. The first validator to return an error message will be used.
+ *
+ * @param validators - The validators to combine.
+ * @returns A validator that combines the given validators.
+ */
+export function combine(...validators: Validator[]): Validator {
+ return (value: unknown) => {
+ for (const validator of validators) {
+ const error = validator(value)
+ if (error) {
+ return error
+ }
+ }
+ }
+}
+
+/**
+ * Create a validator that checks if the value is at or above a certain number.
+ * @param n The number to check against.
+ * @param message The error message to use if the value is below `n`.
+ * @returns A validator that checks if the value is `n` or above.
+ */
+export function min(n: number, message: string | MessageResolver): Validator {
+ return (value) => (value < n ? parseStr(message, n) : null)
+}
+
+/**
+ * Create a validator that checks if the value is at or below a certain number.
+ * @param n The number to check against.
+ * @param message The error message to use if the value is above `n`.
+ * @returns A validator that checks if the value is `n` or below.
+ */
+export function max(n: number, message: string | MessageResolver): Validator {
+ return (value) => (value > n ? parseStr(message, n) : null)
+}
+
+/**
+ * Create a validator that checks if the string is no less than `n` characters long.
+ * @param n The minimum length of the string.
+ * @param message The error message to use if the string is too short.
+ * @returns A validator that checks if the string is at least `n` characters long.
+ */
+export function minLength(n: number, message: string | MessageResolver): Validator {
+ return (value) => (value.length < n ? parseStr(message, n) : null)
+}
+
+/**
+ * Create a validator that checks if the string is no more than `n` characters long.
+ * @param n The maximum length of the string.
+ * @param message The error message to use if the string is too long.
+ * @returns A validator that checks if the string is at most `n` characters long.
+ */
+export function maxLength(n: number, message: string | MessageResolver): Validator {
+ return (value) => (value.length > n ? parseStr(message, n) : null)
+}
+
+/**
+ * Create a validator that checks if the string matches a regular expression.
+ * @param regex The regular expression to match against.
+ * @param message The error message to use if the string does not match the regular expression.
+ * @returns A validator that checks if the string matches the regular expression.
+ */
+export function pattern(
+ regex: RegExp,
+ message: string | MessageResolver,
+): Validator {
+ return (value) => (regex.test(value) ? null : parseStr(message, regex))
+}