@pyreon/form 0.11.5 → 0.11.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -89
- package/lib/devtools.js.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +3 -3
- package/package.json +16 -16
- package/src/context.ts +5 -5
- package/src/devtools.ts +6 -6
- package/src/index.ts +10 -13
- package/src/tests/devtools.test.ts +53 -53
- package/src/tests/form-additional.test.tsx +117 -117
- package/src/tests/form.test.tsx +374 -374
- package/src/types.ts +3 -3
- package/src/use-field-array.ts +2 -2
- package/src/use-field.ts +4 -4
- package/src/use-form-state.ts +3 -3
- package/src/use-form.ts +16 -16
- package/src/use-watch.ts +3 -3
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Computed, Signal } from
|
|
1
|
+
import type { Computed, Signal } from '@pyreon/reactivity'
|
|
2
2
|
|
|
3
3
|
export type ValidationError = string | undefined
|
|
4
4
|
|
|
@@ -85,7 +85,7 @@ export interface FormState<TValues extends Record<string, unknown>> {
|
|
|
85
85
|
*/
|
|
86
86
|
register: <K extends keyof TValues & string>(
|
|
87
87
|
field: K,
|
|
88
|
-
options?: { type?:
|
|
88
|
+
options?: { type?: 'checkbox' | 'number' },
|
|
89
89
|
) => FieldRegisterProps<TValues[K]>
|
|
90
90
|
/**
|
|
91
91
|
* Submit handler — runs validation, then calls onSubmit if valid.
|
|
@@ -110,7 +110,7 @@ export interface UseFormOptions<TValues extends Record<string, unknown>> {
|
|
|
110
110
|
/** Schema-level validator (runs after field validators). */
|
|
111
111
|
schema?: SchemaValidateFn<TValues>
|
|
112
112
|
/** When to validate: 'blur' (default), 'change', or 'submit'. */
|
|
113
|
-
validateOn?:
|
|
113
|
+
validateOn?: 'blur' | 'change' | 'submit'
|
|
114
114
|
/** Debounce delay in ms for validators (useful for async validators). */
|
|
115
115
|
debounceMs?: number
|
|
116
116
|
}
|
package/src/use-field-array.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Computed, Signal } from
|
|
2
|
-
import { computed, signal } from
|
|
1
|
+
import type { Computed, Signal } from '@pyreon/reactivity'
|
|
2
|
+
import { computed, signal } from '@pyreon/reactivity'
|
|
3
3
|
|
|
4
4
|
export interface FieldArrayItem<T> {
|
|
5
5
|
/** Stable key for keyed rendering. */
|
package/src/use-field.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Computed, Signal } from
|
|
2
|
-
import { computed } from
|
|
3
|
-
import type { FieldRegisterProps, FieldState, FormState, ValidationError } from
|
|
1
|
+
import type { Computed, Signal } from '@pyreon/reactivity'
|
|
2
|
+
import { computed } from '@pyreon/reactivity'
|
|
3
|
+
import type { FieldRegisterProps, FieldState, FormState, ValidationError } from './types'
|
|
4
4
|
|
|
5
5
|
export interface UseFieldResult<T> {
|
|
6
6
|
/** Current field value (reactive signal). */
|
|
@@ -18,7 +18,7 @@ export interface UseFieldResult<T> {
|
|
|
18
18
|
/** Reset the field to its initial value. */
|
|
19
19
|
reset: () => void
|
|
20
20
|
/** Register props for input binding. */
|
|
21
|
-
register: (opts?: { type?:
|
|
21
|
+
register: (opts?: { type?: 'checkbox' }) => FieldRegisterProps<T>
|
|
22
22
|
/** Whether the field has an error (computed). */
|
|
23
23
|
hasError: Computed<boolean>
|
|
24
24
|
/** Whether the field should show its error (touched + has error). */
|
package/src/use-form-state.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Computed } from
|
|
2
|
-
import { computed } from
|
|
3
|
-
import type { FormState, ValidationError } from
|
|
1
|
+
import type { Computed } from '@pyreon/reactivity'
|
|
2
|
+
import { computed } from '@pyreon/reactivity'
|
|
3
|
+
import type { FormState, ValidationError } from './types'
|
|
4
4
|
|
|
5
5
|
export interface FormStateSummary<TValues extends Record<string, unknown>> {
|
|
6
6
|
isSubmitting: boolean
|
package/src/use-form.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { onUnmount } from
|
|
2
|
-
import type { Signal } from
|
|
3
|
-
import { computed, effect, signal } from
|
|
1
|
+
import { onUnmount } from '@pyreon/core'
|
|
2
|
+
import type { Signal } from '@pyreon/reactivity'
|
|
3
|
+
import { computed, effect, signal } from '@pyreon/reactivity'
|
|
4
4
|
import type {
|
|
5
5
|
FieldRegisterProps,
|
|
6
6
|
FieldState,
|
|
7
7
|
FormState,
|
|
8
8
|
UseFormOptions,
|
|
9
9
|
ValidationError,
|
|
10
|
-
} from
|
|
10
|
+
} from './types'
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Create a signal-based form. Returns reactive field states, form-level
|
|
@@ -30,7 +30,7 @@ import type {
|
|
|
30
30
|
export function useForm<TValues extends Record<string, unknown>>(
|
|
31
31
|
options: UseFormOptions<TValues>,
|
|
32
32
|
): FormState<TValues> {
|
|
33
|
-
const { initialValues, onSubmit, validators, schema, validateOn =
|
|
33
|
+
const { initialValues, onSubmit, validators, schema, validateOn = 'blur', debounceMs } = options
|
|
34
34
|
|
|
35
35
|
// Build field states
|
|
36
36
|
const fieldEntries = Object.entries(initialValues) as [
|
|
@@ -117,7 +117,7 @@ export function useForm<TValues extends Record<string, unknown>>(
|
|
|
117
117
|
: runValidation
|
|
118
118
|
|
|
119
119
|
// Auto-validate on change if configured
|
|
120
|
-
if (validateOn ===
|
|
120
|
+
if (validateOn === 'change') {
|
|
121
121
|
effect(() => {
|
|
122
122
|
const v = valueSig()
|
|
123
123
|
validateField(v)
|
|
@@ -136,7 +136,7 @@ export function useForm<TValues extends Record<string, unknown>>(
|
|
|
136
136
|
},
|
|
137
137
|
setTouched: () => {
|
|
138
138
|
touchedSig.set(true)
|
|
139
|
-
if (validateOn ===
|
|
139
|
+
if (validateOn === 'blur') {
|
|
140
140
|
validateField(valueSig.peek())
|
|
141
141
|
}
|
|
142
142
|
},
|
|
@@ -248,7 +248,7 @@ export function useForm<TValues extends Record<string, unknown>>(
|
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
const handleSubmit = async (e?: Event) => {
|
|
251
|
-
if (e && typeof e.preventDefault ===
|
|
251
|
+
if (e && typeof e.preventDefault === 'function') {
|
|
252
252
|
e.preventDefault()
|
|
253
253
|
}
|
|
254
254
|
|
|
@@ -286,7 +286,7 @@ export function useForm<TValues extends Record<string, unknown>>(
|
|
|
286
286
|
const setFieldValue = <K extends keyof TValues>(field: K, value: TValues[K]) => {
|
|
287
287
|
if (!fields[field]) {
|
|
288
288
|
throw new Error(
|
|
289
|
-
`[@pyreon/form] Field "${String(field)}" does not exist. Available fields: ${fieldEntries.map(([n]) => n).join(
|
|
289
|
+
`[@pyreon/form] Field "${String(field)}" does not exist. Available fields: ${fieldEntries.map(([n]) => n).join(', ')}`,
|
|
290
290
|
)
|
|
291
291
|
}
|
|
292
292
|
fields[field].setValue(value)
|
|
@@ -295,7 +295,7 @@ export function useForm<TValues extends Record<string, unknown>>(
|
|
|
295
295
|
const setFieldError = (field: keyof TValues, error: ValidationError) => {
|
|
296
296
|
if (!fields[field]) {
|
|
297
297
|
throw new Error(
|
|
298
|
-
`[@pyreon/form] Field "${String(field)}" does not exist. Available fields: ${fieldEntries.map(([n]) => n).join(
|
|
298
|
+
`[@pyreon/form] Field "${String(field)}" does not exist. Available fields: ${fieldEntries.map(([n]) => n).join(', ')}`,
|
|
299
299
|
)
|
|
300
300
|
}
|
|
301
301
|
fields[field].error.set(error)
|
|
@@ -324,9 +324,9 @@ export function useForm<TValues extends Record<string, unknown>>(
|
|
|
324
324
|
|
|
325
325
|
const register = <K extends keyof TValues & string>(
|
|
326
326
|
field: K,
|
|
327
|
-
opts?: { type?:
|
|
327
|
+
opts?: { type?: 'checkbox' | 'number' },
|
|
328
328
|
): FieldRegisterProps<TValues[K]> => {
|
|
329
|
-
const cacheKey = `${field}:${opts?.type ??
|
|
329
|
+
const cacheKey = `${field}:${opts?.type ?? 'text'}`
|
|
330
330
|
const cached = registerCache.get(cacheKey)
|
|
331
331
|
if (cached) return cached as FieldRegisterProps<TValues[K]>
|
|
332
332
|
|
|
@@ -335,9 +335,9 @@ export function useForm<TValues extends Record<string, unknown>>(
|
|
|
335
335
|
value: fieldState.value,
|
|
336
336
|
onInput: (e: Event) => {
|
|
337
337
|
const target = e.target as HTMLInputElement
|
|
338
|
-
if (opts?.type ===
|
|
338
|
+
if (opts?.type === 'checkbox') {
|
|
339
339
|
fieldState.setValue(target.checked as TValues[K])
|
|
340
|
-
} else if (opts?.type ===
|
|
340
|
+
} else if (opts?.type === 'number') {
|
|
341
341
|
const num = target.valueAsNumber
|
|
342
342
|
fieldState.setValue((Number.isNaN(num) ? target.value : num) as TValues[K])
|
|
343
343
|
} else {
|
|
@@ -349,7 +349,7 @@ export function useForm<TValues extends Record<string, unknown>>(
|
|
|
349
349
|
},
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
-
if (opts?.type ===
|
|
352
|
+
if (opts?.type === 'checkbox') {
|
|
353
353
|
props.checked = computed(() => Boolean(fieldState.value()))
|
|
354
354
|
}
|
|
355
355
|
|
|
@@ -395,7 +395,7 @@ function structuredEqual(a: unknown, b: unknown, depth = 0): boolean {
|
|
|
395
395
|
return true
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
-
if (typeof a ===
|
|
398
|
+
if (typeof a === 'object' && typeof b === 'object') {
|
|
399
399
|
const aObj = a as Record<string, unknown>
|
|
400
400
|
const bObj = b as Record<string, unknown>
|
|
401
401
|
const aKeys = Object.keys(aObj)
|
package/src/use-watch.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Computed, Signal } from
|
|
2
|
-
import { computed } from
|
|
3
|
-
import type { FormState } from
|
|
1
|
+
import type { Computed, Signal } from '@pyreon/reactivity'
|
|
2
|
+
import { computed } from '@pyreon/reactivity'
|
|
3
|
+
import type { FormState } from './types'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Watch specific field values reactively. Returns a computed signal
|