cross-state 0.15.1 → 0.16.0
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/dist/cjs/react/index.cjs
CHANGED
|
@@ -55,7 +55,7 @@ function FormField({
|
|
|
55
55
|
const errorString = require$$0.useMemo(
|
|
56
56
|
() => errors.map((error) => {
|
|
57
57
|
var _a, _b;
|
|
58
|
-
return ((_b = (_a = form.options).localizeError) == null ? void 0 : _b.call(_a, error)) ?? error;
|
|
58
|
+
return ((_b = (_a = form.options).localizeError) == null ? void 0 : _b.call(_a, error, name)) ?? error;
|
|
59
59
|
}).join("\n"),
|
|
60
60
|
[errors, form.options.localizeError]
|
|
61
61
|
);
|
|
@@ -127,7 +127,7 @@ function FormContainer({
|
|
|
127
127
|
form,
|
|
128
128
|
...formProps
|
|
129
129
|
}) {
|
|
130
|
-
const
|
|
130
|
+
const _form = form.useForm();
|
|
131
131
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
132
132
|
"form",
|
|
133
133
|
{
|
|
@@ -135,7 +135,17 @@ function FormContainer({
|
|
|
135
135
|
noValidate: true,
|
|
136
136
|
onSubmit: (event) => {
|
|
137
137
|
event.preventDefault();
|
|
138
|
-
validate();
|
|
138
|
+
_form.validate();
|
|
139
|
+
let button;
|
|
140
|
+
if (event.nativeEvent instanceof SubmitEvent && (button = event.nativeEvent.submitter) && (button instanceof HTMLButtonElement || button instanceof HTMLInputElement) && button.setCustomValidity) {
|
|
141
|
+
const errors = _form.errors.map(
|
|
142
|
+
({ field, error }) => {
|
|
143
|
+
var _a, _b;
|
|
144
|
+
return ((_b = (_a = _form.options).localizeError) == null ? void 0 : _b.call(_a, error, field)) ?? error;
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
button.setCustomValidity(errors.join("\n"));
|
|
148
|
+
}
|
|
139
149
|
event.currentTarget.reportValidity();
|
|
140
150
|
}
|
|
141
151
|
}
|
|
@@ -166,9 +176,7 @@ function getFormInstance(original, options, state) {
|
|
|
166
176
|
return state.get().hasTriggeredValidations || !store.deepEqual(comparisonValue, this.value);
|
|
167
177
|
},
|
|
168
178
|
get errors() {
|
|
169
|
-
const blocks = Object.entries(
|
|
170
|
-
options.validations ?? {}
|
|
171
|
-
).filter(([key]) => wildcardMatch(path, key)).map(([, value2]) => value2);
|
|
179
|
+
const blocks = Object.entries(options.validations ?? {}).filter(([key]) => wildcardMatch(path, key)).map(([, value2]) => value2);
|
|
172
180
|
const value = this.value;
|
|
173
181
|
const draftValue = draft.get();
|
|
174
182
|
const errors = [];
|
|
@@ -196,7 +204,7 @@ function getFormInstance(original, options, state) {
|
|
|
196
204
|
)) {
|
|
197
205
|
for (const [field, value] of Object.entries(getWildCardMatches(draft, path))) {
|
|
198
206
|
if (!validate(value, { draft, original, field })) {
|
|
199
|
-
errors.add(
|
|
207
|
+
errors.add({ field, error: validationName });
|
|
200
208
|
}
|
|
201
209
|
}
|
|
202
210
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../../src/lib/wildcardMatch.ts","../../../src/react/form/formError.tsx","../../../src/react/form/formField.tsx","../../../src/react/form/form.tsx","../../../src/react/read.ts","../../../src/react/useDecoupledState.ts","../../../src/lib/castArray.ts","../../../src/react/useUrlParamScope.ts"],"sourcesContent":["import { type KeyType } from './path';\nimport { castArrayPath } from './propAccess';\n\nexport function wildcardMatch(s: KeyType[] | string, w: KeyType[] | string): boolean {\n if (typeof s === 'string') {\n s = castArrayPath(s);\n }\n\n if (typeof w === 'string') {\n w = castArrayPath(w);\n }\n\n return s.length === w.length && s.every((s, i) => w[i] === '*' || s === w[i]);\n}\n\nexport function getWildCardMatches(object: any, path: KeyType[] | string): Record<KeyType, any> {\n const matches: Record<KeyType, any> = {};\n const [first, ...rest] = castArrayPath(path);\n\n if (first === undefined) {\n return object;\n }\n\n if (!(object instanceof Object)) {\n return {};\n }\n\n if (first === '*') {\n for (const [key, value] of Object.entries(object)) {\n matches[key] = getWildCardMatches(value, rest);\n }\n } else {\n matches[first] = getWildCardMatches(object[first], rest);\n }\n\n return matches;\n}\n","import { type Form } from './form';\nimport { type PathAsString } from '@lib/path';\n\nexport type FormErrorProps<TDraft, TPath extends PathAsString<TDraft>> = {\n name: TPath;\n};\n\nexport function FormError<TDraft, TPath extends PathAsString<TDraft>>(\n this: Form<TDraft, any>,\n { name }: FormErrorProps<TDraft, TPath>,\n) {\n const { errors, isDirty } = this.useField(name);\n\n return isDirty ? <>{errors.join(', ')}</> : null;\n}\n","import {\n createElement,\n useEffect,\n useMemo,\n useState,\n type ComponentPropsWithoutRef,\n type ElementType,\n type HTMLProps,\n} from 'react';\nimport { type Form } from './form';\nimport { type PathAsString } from '@index';\nimport { type Value } from '@lib/path';\nimport { useScope } from '@react/scope';\n\nexport type FormFieldComponent<T> = ElementType<{\n id: string;\n value: T;\n onChange: (event: { target: { value: T } } | T | undefined, ...args: any[]) => void;\n onFocus: (...args: any[]) => void;\n onBlur: (...args: any[]) => void;\n}>;\n\ntype FieldValue<T extends FormFieldComponent<any>> = ComponentPropsWithoutRef<T & 'input'> extends {\n value: infer U;\n}\n ? U\n : ComponentPropsWithoutRef<T & 'input'> extends {\n value?: infer U;\n }\n ? U | undefined\n : never;\n\ntype FieldChangeValue<T extends FormFieldComponent<any>> = ComponentPropsWithoutRef<\n T & 'input'\n> extends {\n onChange?: (update: infer U) => void;\n}\n ? U extends { target: { value: infer V } }\n ? V\n : U\n : never;\n\nexport type FormFieldProps<\n TDraft,\n TPath extends PathAsString<TDraft>,\n TComponent extends FormFieldComponent<any>,\n> = {\n name: TPath;\n commitOnBlur?: boolean;\n commitDebounce?: number;\n inputFilter?: (value: FieldChangeValue<TComponent>) => boolean;\n onChange?: ComponentPropsWithoutRef<TComponent>['onChange'];\n onBlur?: ComponentPropsWithoutRef<TComponent>['onBlur'];\n} & (TComponent extends 'input' | ((props: HTMLProps<HTMLInputElement>) => JSX.Element)\n ? { component?: TComponent } | { children?: TComponent }\n : { component: TComponent } | { children: TComponent }) &\n Omit<\n ComponentPropsWithoutRef<TComponent>,\n | 'form'\n | 'name'\n | 'component'\n | 'commitOnBlur'\n | 'commitDebounce'\n | 'value'\n | 'onChange'\n | 'onBlur'\n | 'children'\n > &\n (Value<TDraft, TPath> extends FieldValue<TComponent>\n ? { serialize?: (value: Value<TDraft, TPath>) => FieldValue<TComponent> }\n : { serialize: (value: Value<TDraft, TPath>) => FieldValue<TComponent> }) &\n (FieldChangeValue<TComponent> extends Value<TDraft, TPath>\n ? { deserialize?: (value: FieldChangeValue<TComponent>) => Value<TDraft, TPath> }\n : { deserialize: (value: FieldChangeValue<TComponent>) => Value<TDraft, TPath> });\n\nexport function FormField<\n TDraft,\n TPath extends PathAsString<TDraft>,\n TComponent extends FormFieldComponent<any>,\n>(\n this: Form<TDraft, any>,\n {\n // id,\n name,\n commitOnBlur,\n commitDebounce,\n inputFilter,\n serialize = (x) => x as FieldValue<TComponent>,\n deserialize = (x) => x as Value<TDraft, TPath>,\n ...restProps\n }: FormFieldProps<TDraft, TPath, TComponent>,\n): JSX.Element {\n type T = FieldChangeValue<TComponent>;\n const id = '';\n const component = (('component' in restProps\n ? restProps.component\n : 'children' in restProps\n ? restProps.children\n : undefined) ?? 'input') as TComponent;\n\n const form = this.useForm();\n const state = useScope(this.state);\n const { value, setValue, errors } = this.useField(name);\n\n const errorString = useMemo(\n () => errors.map((error) => form.options.localizeError?.(error) ?? error).join('\\n'),\n [errors, form.options.localizeError],\n );\n const [localValue, setLocalValue] = useState<T>();\n const _id = useMemo(\n () =>\n id || `f${Math.random().toString(36).slice(2, 15)}${Math.random().toString(36).slice(2, 15)}`,\n\n [id],\n );\n\n useEffect(() => {\n if (localValue === undefined || !commitDebounce) {\n return;\n }\n\n const timeout = setTimeout(() => {\n setValue(deserialize(localValue));\n setLocalValue(undefined);\n }, commitDebounce);\n\n return () => clearTimeout(timeout);\n }, [localValue, commitDebounce]);\n\n useEffect(() => {\n const element = document.querySelector(\n [`#${_id} input`, `#${_id} select`, `#${_id} textarea`, `#${_id}`].join(','),\n );\n\n if (\n !(\n element instanceof HTMLInputElement ||\n element instanceof HTMLSelectElement ||\n element instanceof HTMLTextAreaElement\n )\n ) {\n return;\n }\n\n element.setCustomValidity(errorString);\n }, [_id, errorString]);\n\n const props = {\n ...restProps,\n component: undefined,\n children: undefined,\n id: _id,\n name,\n value: localValue ?? serialize(value),\n onChange: (event: { target: { value: T } } | T, ...moreArgs: any[]) => {\n const value =\n typeof event === 'object' && event !== null && 'target' in event\n ? event.target.value\n : event;\n\n if (inputFilter && !inputFilter(value)) {\n return;\n }\n\n if (commitOnBlur || commitDebounce) {\n setLocalValue(value);\n } else {\n setValue(deserialize(value));\n }\n\n restProps.onChange?.(event, ...moreArgs);\n },\n onFocus(...args: any[]) {\n state.set('touched', (touched) => {\n touched = new Set(touched);\n touched.add(_id);\n return touched;\n });\n\n restProps.onFocus?.apply(null, args);\n },\n onBlur(...args: any[]) {\n if (localValue !== undefined) {\n setValue(deserialize(localValue));\n setLocalValue(undefined);\n }\n\n restProps.onBlur?.apply(null, args);\n },\n };\n\n return createElement(component, props);\n}\n","import { Scope, type Store, connectUrl, createStore, type UrlStoreOptions } from '@core';\nimport { autobind } from '@lib/autobind';\nimport { deepEqual } from '@lib/equals';\nimport { hash } from '@lib/hash';\nimport {\n type PathAsString,\n type Value,\n type WildcardPathAsString,\n type WildcardValue,\n} from '@lib/path';\nimport { get } from '@lib/propAccess';\nimport { getWildCardMatches, wildcardMatch } from '@lib/wildcardMatch';\nimport {\n type ReactNode,\n createContext,\n useContext,\n useEffect,\n useMemo,\n type ComponentPropsWithoutRef,\n type HTMLProps,\n} from 'react';\nimport { ScopeProvider, useScope } from '../scope';\nimport { useStore, type UseStoreOptions } from '../useStore';\nimport { FormError, type FormErrorProps } from './formError';\nimport { FormField, type FormFieldComponent, type FormFieldProps } from './formField';\n\n/// /////////////////////////////////////////////////////////////////////////////\n// Form types\n/// /////////////////////////////////////////////////////////////////////////////\n\nexport interface FormOptions<TDraft, TOriginal> {\n defaultValue: TDraft;\n validations?: Validations<TDraft, TOriginal>;\n localizeError?: (error: string) => string | undefined;\n urlState?: boolean | UrlStoreOptions<TDraft>;\n}\n\nexport type Validations<TDraft, TOriginal> = {\n [P in WildcardPathAsString<TDraft>]?: Record<\n string,\n Validation<WildcardValue<TDraft, P>, TDraft, TOriginal>\n >;\n};\n\nexport type Validation<TValue, TDraft, TOriginal> = (\n value: TValue,\n context: { draft: TDraft; original: TOriginal; field: PathAsString<TDraft> },\n) => boolean;\n\nexport interface Field<TDraft, TOriginal, TPath extends PathAsString<TDraft>> {\n originalValue: Value<TOriginal, TPath> | undefined;\n value: Value<TDraft, TPath>;\n setValue: (\n value: Value<TDraft, TPath> | ((value: Value<TDraft, TPath>) => Value<TDraft, TPath>),\n ) => void;\n isDirty: boolean;\n errors: string[];\n}\n\ninterface FormState<TDraft> {\n draft?: TDraft;\n touched: Set<string>;\n errors: Map<string, string[]>;\n hasTriggeredValidations?: boolean;\n}\n\n/// /////////////////////////////////////////////////////////////////////////////\n// Implementation\n/// /////////////////////////////////////////////////////////////////////////////\n\nfunction FormContainer({\n form,\n ...formProps\n}: { form: Form<any, any> } & Omit<HTMLProps<HTMLFormElement>, 'form'>) {\n const { validate } = form.useForm();\n\n return (\n <form\n {...formProps}\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n\n validate();\n event.currentTarget.reportValidity();\n }}\n />\n );\n}\n\nfunction getFormInstance<TDraft, TOriginal extends TDraft>(\n original: TOriginal | undefined,\n options: FormOptions<TDraft, TOriginal>,\n state: Store<FormState<TDraft>>,\n) {\n const instance = {\n original,\n\n draft: state.map(\n (state) => state.draft ?? original ?? options.defaultValue,\n (draft) => (state) => ({ ...state, draft }),\n ),\n\n options,\n\n getField: <TPath extends PathAsString<TDraft>>(\n path: TPath,\n ): Field<TDraft, TOriginal, TPath> => {\n const { draft } = instance;\n\n return {\n get originalValue() {\n return original !== undefined ? get(original as any, path as any) : undefined;\n },\n\n get value() {\n return get(draft.get(), path);\n },\n\n setValue(update) {\n draft.set(path, update);\n },\n\n get isDirty() {\n const comparisonValue = this.originalValue ?? get(options.defaultValue, path);\n\n return state.get().hasTriggeredValidations || !deepEqual(comparisonValue, this.value);\n },\n\n get errors() {\n const blocks: Record<string, Validation<any, any, any>>[] = Object.entries(\n options.validations ?? {},\n )\n .filter(([key]) => wildcardMatch(path, key))\n .map(([, value]) => value);\n\n const value = this.value;\n const draftValue = draft.get();\n const errors: string[] = [];\n\n for (const block of blocks ?? []) {\n for (const [validationName, validate] of Object.entries(block)) {\n if (!validate(value, { draft: draftValue, original, field: path })) {\n errors.push(validationName);\n }\n }\n }\n\n return errors;\n },\n };\n },\n\n get hasChanges() {\n const { draft } = state.get();\n return !!draft && !deepEqual(draft, original ?? options.defaultValue);\n },\n\n get errors(): string[] {\n const draft = instance.draft.get();\n const errors = new Set<string>();\n\n for (const [path, block] of Object.entries(options.validations ?? {})) {\n for (const [validationName, validate] of Object.entries(\n block as Record<string, Validation<any, any, any>>,\n )) {\n for (const [field, value] of Object.entries(getWildCardMatches(draft, path))) {\n if (!validate(value, { draft, original, field })) {\n errors.add(`${field}.${validationName}`);\n }\n }\n }\n }\n\n return [...errors];\n },\n\n get isValid() {\n return instance.errors.length === 0;\n },\n\n validate: () => {\n state.set('hasTriggeredValidations', true);\n return instance.isValid;\n },\n\n reset() {\n state.set('draft', undefined);\n },\n };\n\n return instance;\n}\n\nexport class Form<TDraft, TOriginal extends TDraft = TDraft> {\n context = createContext({\n original: undefined as TOriginal | undefined,\n options: this.options,\n });\n\n state = new Scope<FormState<TDraft>>({\n touched: new Set(),\n errors: new Map(),\n });\n\n constructor(public readonly options: FormOptions<TDraft, TOriginal>) {\n autobind(Form);\n }\n\n useForm() {\n const { original, options } = useContext(this.context);\n const state = useScope(this.state);\n\n return useMemo(() => getFormInstance(original, options, state), [original, options, state]);\n }\n\n useFormState<S>(selector: (state: ReturnType<typeof getFormInstance<TDraft, TOriginal>>) => S) {\n const { original, options } = useContext(this.context);\n const state = useScope(this.state);\n\n return useStore(state.map(() => selector(getFormInstance(original, options, state))));\n }\n\n useField<TPath extends PathAsString<TDraft>>(path: TPath, useStoreOptions?: UseStoreOptions) {\n const form = this.useForm();\n const state = useScope(this.state);\n\n useStore(\n form.draft.map((draft) => get(draft, path)),\n useStoreOptions,\n );\n\n useStore(\n state.map((state) => state.hasTriggeredValidations),\n useStoreOptions,\n );\n\n return form.getField(path);\n }\n\n useHasChanges() {\n const form = this.useForm();\n\n return useStore(form.draft.map(() => form.hasChanges));\n }\n\n useIsValid() {\n const form = this.useForm();\n\n return useStore(form.draft.map(() => form.isValid));\n }\n\n // ///////////////////////////////////////////////////////////////////////////\n // React Components\n // ///////////////////////////////////////////////////////////////////////////\n\n Form({\n original,\n defaultValue,\n validations,\n localizeError,\n urlState,\n ...formProps\n }: {\n original?: TOriginal;\n } & Partial<FormOptions<TDraft, TOriginal>> &\n Omit<HTMLProps<HTMLFormElement>, 'defaultValue'>) {\n const value = useMemo(\n () => ({\n original,\n options: {\n defaultValue: { ...this.options.defaultValue, ...defaultValue },\n validations: { ...this.options.validations, ...validations } as Validations<\n TDraft,\n TOriginal\n >,\n localizeError: localizeError ?? this.options.localizeError,\n },\n }),\n [original, defaultValue, validations],\n );\n\n const store = useMemo(() => {\n return createStore(this.state.defaultValue);\n }, []);\n\n useEffect(() => {\n if (urlState) {\n return connectUrl(\n store.map('draft'),\n typeof urlState === 'object' ? urlState : { key: 'form' },\n );\n }\n\n return undefined;\n }, [store, hash(urlState)]);\n\n return (\n <this.context.Provider value={value}>\n <ScopeProvider scope={this.state} store={store}>\n <FormContainer {...formProps} form={this} />\n </ScopeProvider>\n </this.context.Provider>\n );\n }\n\n Subscribe<S>({\n selector,\n children,\n }: {\n selector: (form: ReturnType<typeof getFormInstance<TDraft, TOriginal>>) => S;\n children: (selectedState: S) => ReactNode;\n }) {\n const selectedState = this.useFormState(selector);\n return <>{children(selectedState)}</>;\n }\n\n Field<\n TPath extends PathAsString<TDraft>,\n TComponent extends FormFieldComponent<any> = (\n props: ComponentPropsWithoutRef<'input'>,\n ) => JSX.Element,\n >(props: FormFieldProps<TDraft, TPath, TComponent>): JSX.Element {\n return Reflect.apply(FormField, this, [props]);\n }\n\n Error<TPath extends PathAsString<TDraft>>({ name }: FormErrorProps<TDraft, TPath>) {\n return Reflect.apply(FormError, this, [{ name }]);\n }\n}\n\nexport function createForm<TDraft, TOriginal extends TDraft = TDraft>(\n options: FormOptions<TDraft, TOriginal>,\n) {\n return new Form(options);\n}\n","import { useCache } from './useCache';\nimport type { UseStoreOptions } from './useStore';\nimport type { Cache } from '@core';\n\nexport function read<T>(cache: Cache<T>, options?: UseStoreOptions): T {\n const { status, value, error } = useCache(cache, options);\n\n if (status === 'value') {\n return value;\n }\n\n if (status === 'error') {\n throw error;\n }\n\n throw cache.state.once((state) => state.status !== 'pending');\n}\n","import { startTransition, useEffect, useMemo, useRef, useState } from 'react';\nimport { type Duration } from '@core';\nimport { debounce } from '@lib/debounce';\nimport { hash } from '@lib/hash';\nimport { throttle } from '@lib/throttle';\n\nexport interface UseDecoupledStateOptions<T> {\n debounce?: Duration;\n throttle?: Duration;\n onCommit?: (value: T) => void;\n}\n\nexport function useDecoupledState<T>(\n value: T,\n onChange: (value: T) => void,\n options: UseDecoupledStateOptions<T> = {},\n): [state: T, setState: (value: T) => void] {\n const [dirty, setDirty] = useState<{ v: T }>();\n const ref = useRef({ onChange, onCommit: options.onCommit });\n\n useEffect(() => {\n ref.current = { onChange, onCommit: options.onCommit };\n }, [onChange]);\n\n const update = useMemo(() => {\n const { onChange, onCommit } = ref.current;\n\n const update = (value: T) => {\n onChange(value);\n setDirty(undefined);\n onCommit?.(value);\n };\n\n let delayedUpdate: (value: T) => void;\n\n if (options.debounce) {\n delayedUpdate = debounce(update, options.debounce);\n } else if (options.throttle) {\n delayedUpdate = throttle(update, options.throttle);\n } else {\n delayedUpdate = (value) => startTransition(() => update(value));\n }\n\n return (value: T) => {\n setDirty({ v: value });\n delayedUpdate(value);\n };\n }, [hash([options.debounce, options.throttle])]);\n\n return [dirty ? dirty.v : value, update];\n}\n","export function castArray<T>(value: T | T[]): T[] {\n return Array.isArray(value) ? value : [value];\n}\n","import { type ReactNode, useEffect } from 'react';\nimport { castArray } from '@lib/castArray';\nimport { hash } from '@lib/hash';\n\nexport function useUrlParamScope({\n key,\n type = 'search',\n}: {\n key: string | string[];\n type?: 'search' | 'hash';\n}) {\n useEffect(\n () => () => {\n const url = new URL(window.location.href);\n const parameters = new URLSearchParams(url[type].slice(1));\n\n for (const _key of castArray(key)) {\n parameters.delete(_key);\n }\n\n url[type] = parameters.toString();\n window.history.replaceState(null, '', url.toString());\n },\n [hash(key), type],\n );\n}\n"],"names":["castArrayPath","s","jsx","Fragment","useScope","useMemo","useState","useEffect","value","createElement","state","get","deepEqual","createContext","Scope","autobind","useContext","useStore","store","createStore","connectUrl","hash","ScopeProvider","useCache","useRef","onChange","update","debounce","throttle","startTransition"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../../src/lib/wildcardMatch.ts","../../../src/react/form/formError.tsx","../../../src/react/form/formField.tsx","../../../src/react/form/form.tsx","../../../src/react/read.ts","../../../src/react/useDecoupledState.ts","../../../src/lib/castArray.ts","../../../src/react/useUrlParamScope.ts"],"sourcesContent":["import { type KeyType } from './path';\nimport { castArrayPath } from './propAccess';\n\nexport function wildcardMatch(s: KeyType[] | string, w: KeyType[] | string): boolean {\n if (typeof s === 'string') {\n s = castArrayPath(s);\n }\n\n if (typeof w === 'string') {\n w = castArrayPath(w);\n }\n\n return s.length === w.length && s.every((s, i) => w[i] === '*' || s === w[i]);\n}\n\nexport function getWildCardMatches(object: any, path: KeyType[] | string): Record<KeyType, any> {\n const matches: Record<KeyType, any> = {};\n const [first, ...rest] = castArrayPath(path);\n\n if (first === undefined) {\n return object;\n }\n\n if (!(object instanceof Object)) {\n return {};\n }\n\n if (first === '*') {\n for (const [key, value] of Object.entries(object)) {\n matches[key] = getWildCardMatches(value, rest);\n }\n } else {\n matches[first] = getWildCardMatches(object[first], rest);\n }\n\n return matches;\n}\n","import { type Form } from './form';\nimport { type PathAsString } from '@lib/path';\n\nexport type FormErrorProps<TDraft, TPath extends PathAsString<TDraft>> = {\n name: TPath;\n};\n\nexport function FormError<TDraft, TPath extends PathAsString<TDraft>>(\n this: Form<TDraft, any>,\n { name }: FormErrorProps<TDraft, TPath>,\n) {\n const { errors, isDirty } = this.useField(name);\n\n return isDirty ? <>{errors.join(', ')}</> : null;\n}\n","import { type PathAsString } from '@index';\nimport { type Value } from '@lib/path';\nimport { useScope } from '@react/scope';\nimport {\n createElement,\n useEffect,\n useMemo,\n useState,\n type ComponentPropsWithoutRef,\n type ComponentType,\n type HTMLProps,\n} from 'react';\nimport { type Form } from './form';\n\ninterface FormFieldComponentProps<T> {\n id: string;\n value: T;\n onChange: (event: { target: { value: T } } | T | undefined, ...args: any[]) => void;\n onFocus: (...args: any[]) => void;\n onBlur: (...args: any[]) => void;\n}\n\ntype NativeInputType = 'input' | 'select' | 'textarea';\n\ntype FieldValue<T extends FormFieldComponent<any>> = ComponentPropsWithoutRef<T & 'input'> extends {\n value: infer U;\n}\n ? U\n : ComponentPropsWithoutRef<T & 'input'> extends {\n value?: infer U;\n }\n ? U | undefined\n : never;\n\ntype FieldChangeValue<T extends FormFieldComponent<any>> = ComponentPropsWithoutRef<\n T & 'input'\n> extends {\n onChange?: (update: infer U) => void;\n}\n ? U extends { target: { value: infer V } }\n ? V\n : U\n : never;\n\nexport type FormFieldComponent<T> =\n | (string | number extends T ? NativeInputType : never)\n | ComponentType<FormFieldComponentProps<T>>;\n\nexport type FormFieldProps<\n TDraft,\n TPath extends PathAsString<TDraft>,\n TComponent extends FormFieldComponent<any>,\n> = {\n name: TPath;\n commitOnBlur?: boolean;\n commitDebounce?: number;\n inputFilter?: (value: FieldChangeValue<TComponent>) => boolean;\n onChange?: ComponentPropsWithoutRef<TComponent>['onChange'];\n onBlur?: ComponentPropsWithoutRef<TComponent>['onBlur'];\n} & (TComponent extends 'input' | ((props: HTMLProps<HTMLInputElement>) => JSX.Element)\n ? { component?: TComponent } | { children?: TComponent }\n : { component: TComponent } | { children: TComponent }) &\n Omit<\n ComponentPropsWithoutRef<TComponent>,\n | 'form'\n | 'name'\n | 'component'\n | 'commitOnBlur'\n | 'commitDebounce'\n | 'value'\n | 'onChange'\n | 'onBlur'\n | 'children'\n > &\n (Value<TDraft, TPath> extends FieldValue<TComponent>\n ? { serialize?: (value: Value<TDraft, TPath>) => FieldValue<TComponent> }\n : { serialize: (value: Value<TDraft, TPath>) => FieldValue<TComponent> }) &\n (FieldChangeValue<TComponent> extends Value<TDraft, TPath>\n ? { deserialize?: (value: FieldChangeValue<TComponent>) => Value<TDraft, TPath> }\n : { deserialize: (value: FieldChangeValue<TComponent>) => Value<TDraft, TPath> });\n\nexport function FormField<\n TDraft,\n TPath extends PathAsString<TDraft>,\n TComponent extends FormFieldComponent<any>,\n>(\n this: Form<TDraft, any>,\n {\n // id,\n name,\n commitOnBlur,\n commitDebounce,\n inputFilter,\n serialize = (x) => x as FieldValue<TComponent>,\n deserialize = (x) => x as Value<TDraft, TPath>,\n ...restProps\n }: FormFieldProps<TDraft, TPath, TComponent>,\n): JSX.Element {\n type T = FieldChangeValue<TComponent>;\n const id = '';\n const component = (('component' in restProps\n ? restProps.component\n : 'children' in restProps\n ? restProps.children\n : undefined) ?? 'input') as TComponent;\n\n const form = this.useForm();\n const state = useScope(this.state);\n const { value, setValue, errors } = this.useField(name);\n\n const errorString = useMemo(\n () => errors.map((error) => form.options.localizeError?.(error, name) ?? error).join('\\n'),\n [errors, form.options.localizeError],\n );\n const [localValue, setLocalValue] = useState<T>();\n const _id = useMemo(\n () =>\n id || `f${Math.random().toString(36).slice(2, 15)}${Math.random().toString(36).slice(2, 15)}`,\n\n [id],\n );\n\n useEffect(() => {\n if (localValue === undefined || !commitDebounce) {\n return;\n }\n\n const timeout = setTimeout(() => {\n setValue(deserialize(localValue));\n setLocalValue(undefined);\n }, commitDebounce);\n\n return () => clearTimeout(timeout);\n }, [localValue, commitDebounce]);\n\n useEffect(() => {\n const element = document.querySelector(\n [`#${_id} input`, `#${_id} select`, `#${_id} textarea`, `#${_id}`].join(','),\n );\n\n if (\n !(\n element instanceof HTMLInputElement ||\n element instanceof HTMLSelectElement ||\n element instanceof HTMLTextAreaElement\n )\n ) {\n return;\n }\n\n element.setCustomValidity(errorString);\n }, [_id, errorString]);\n\n const props = {\n ...restProps,\n component: undefined,\n children: undefined,\n id: _id,\n name,\n value: localValue ?? serialize(value),\n onChange: (event: { target: { value: T } } | T, ...moreArgs: any[]) => {\n const value =\n typeof event === 'object' && event !== null && 'target' in event\n ? event.target.value\n : event;\n\n if (inputFilter && !inputFilter(value)) {\n return;\n }\n\n if (commitOnBlur || commitDebounce) {\n setLocalValue(value);\n } else {\n setValue(deserialize(value));\n }\n\n restProps.onChange?.(event, ...moreArgs);\n },\n onFocus(...args: any[]) {\n state.set('touched', (touched) => {\n touched = new Set(touched);\n touched.add(_id);\n return touched;\n });\n\n restProps.onFocus?.apply(null, args);\n },\n onBlur(...args: any[]) {\n if (localValue !== undefined) {\n setValue(deserialize(localValue));\n setLocalValue(undefined);\n }\n\n restProps.onBlur?.apply(null, args);\n },\n };\n\n return createElement(component, props);\n}\n","import { Scope, type Store, connectUrl, createStore, type UrlStoreOptions } from '@core';\nimport { autobind } from '@lib/autobind';\nimport { deepEqual } from '@lib/equals';\nimport { hash } from '@lib/hash';\nimport {\n type PathAsString,\n type Value,\n type WildcardPathAsString,\n type WildcardValue,\n} from '@lib/path';\nimport { get } from '@lib/propAccess';\nimport { getWildCardMatches, wildcardMatch } from '@lib/wildcardMatch';\nimport {\n type ReactNode,\n createContext,\n useContext,\n useEffect,\n useMemo,\n type ComponentPropsWithoutRef,\n type HTMLProps,\n} from 'react';\nimport { ScopeProvider, useScope } from '../scope';\nimport { useStore, type UseStoreOptions } from '../useStore';\nimport { FormError, type FormErrorProps } from './formError';\nimport { FormField, type FormFieldComponent, type FormFieldProps } from './formField';\n\n/// /////////////////////////////////////////////////////////////////////////////\n// Form types\n/// /////////////////////////////////////////////////////////////////////////////\n\nexport interface FormOptions<TDraft, TOriginal> {\n defaultValue: TDraft;\n validations?: Validations<TDraft, TOriginal>;\n localizeError?: (error: string, field: string) => string | undefined;\n urlState?: boolean | UrlStoreOptions<TDraft>;\n}\n\nexport type Validations<TDraft, TOriginal> = {\n [P in WildcardPathAsString<TDraft>]?: Record<\n string,\n Validation<WildcardValue<TDraft, P>, TDraft, TOriginal>\n >;\n} & Record<string, Record<string, Validation<any, TDraft, TOriginal>>>;\n\nexport type Validation<TValue, TDraft, TOriginal> = (\n value: TValue,\n context: { draft: TDraft; original: TOriginal; field: PathAsString<TDraft> },\n) => boolean;\n\nexport interface Field<TDraft, TOriginal, TPath extends PathAsString<TDraft>> {\n originalValue: Value<TOriginal, TPath> | undefined;\n value: Value<TDraft, TPath>;\n setValue: (\n value: Value<TDraft, TPath> | ((value: Value<TDraft, TPath>) => Value<TDraft, TPath>),\n ) => void;\n isDirty: boolean;\n errors: string[];\n}\n\ninterface FormState<TDraft> {\n draft?: TDraft;\n touched: Set<string>;\n errors: Map<string, string[]>;\n hasTriggeredValidations?: boolean;\n}\n\n/// /////////////////////////////////////////////////////////////////////////////\n// Implementation\n/// /////////////////////////////////////////////////////////////////////////////\n\nfunction FormContainer({\n form,\n ...formProps\n}: { form: Form<any, any> } & Omit<HTMLProps<HTMLFormElement>, 'form'>) {\n const _form = form.useForm();\n\n return (\n <form\n {...formProps}\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n\n _form.validate();\n\n let button;\n\n if (\n event.nativeEvent instanceof SubmitEvent &&\n (button = event.nativeEvent.submitter) &&\n (button instanceof HTMLButtonElement || button instanceof HTMLInputElement) &&\n button.setCustomValidity\n ) {\n const errors = _form.errors.map(\n ({ field, error }) => _form.options.localizeError?.(error, field) ?? error,\n );\n button.setCustomValidity(errors.join('\\n'));\n }\n event.currentTarget.reportValidity();\n }}\n />\n );\n}\n\nfunction getFormInstance<TDraft, TOriginal extends TDraft>(\n original: TOriginal | undefined,\n options: FormOptions<TDraft, TOriginal>,\n state: Store<FormState<TDraft>>,\n) {\n const instance = {\n original,\n\n draft: state.map(\n (state) => state.draft ?? original ?? options.defaultValue,\n (draft) => (state) => ({ ...state, draft }),\n ),\n\n options,\n\n getField: <TPath extends PathAsString<TDraft>>(\n path: TPath,\n ): Field<TDraft, TOriginal, TPath> => {\n const { draft } = instance;\n\n return {\n get originalValue() {\n return original !== undefined ? get(original as any, path as any) : undefined;\n },\n\n get value() {\n return get(draft.get(), path);\n },\n\n setValue(update) {\n draft.set(path, update);\n },\n\n get isDirty() {\n const comparisonValue = this.originalValue ?? get(options.defaultValue, path);\n\n return state.get().hasTriggeredValidations || !deepEqual(comparisonValue, this.value);\n },\n\n get errors() {\n const blocks: (Validation<any, any, any> | Record<string, Validation<any, any, any>>)[] =\n Object.entries(options.validations ?? {})\n .filter(([key]) => wildcardMatch(path, key))\n .map(([, value]) => value);\n\n const value = this.value;\n const draftValue = draft.get();\n const errors: string[] = [];\n\n for (const block of blocks ?? []) {\n for (const [validationName, validate] of Object.entries(block)) {\n if (!validate(value, { draft: draftValue, original, field: path })) {\n errors.push(validationName);\n }\n }\n }\n\n return errors;\n },\n };\n },\n\n get hasChanges() {\n const { draft } = state.get();\n return !!draft && !deepEqual(draft, original ?? options.defaultValue);\n },\n\n get errors() {\n const draft = instance.draft.get();\n const errors = new Set<{ field: string; error: string }>();\n\n for (const [path, block] of Object.entries(options.validations ?? {})) {\n for (const [validationName, validate] of Object.entries(\n block as Record<string, Validation<any, any, any>>,\n )) {\n for (const [field, value] of Object.entries(getWildCardMatches(draft, path))) {\n if (!validate(value, { draft, original, field })) {\n errors.add({ field, error: validationName });\n }\n }\n }\n }\n\n return [...errors];\n },\n\n get isValid() {\n return instance.errors.length === 0;\n },\n\n validate: () => {\n state.set('hasTriggeredValidations', true);\n return instance.isValid;\n },\n\n reset() {\n state.set('draft', undefined);\n },\n };\n\n return instance;\n}\n\nexport class Form<TDraft, TOriginal extends TDraft = TDraft> {\n context = createContext({\n original: undefined as TOriginal | undefined,\n options: this.options,\n });\n\n state = new Scope<FormState<TDraft>>({\n touched: new Set(),\n errors: new Map(),\n });\n\n constructor(public readonly options: FormOptions<TDraft, TOriginal>) {\n autobind(Form);\n }\n\n useForm() {\n const { original, options } = useContext(this.context);\n const state = useScope(this.state);\n\n return useMemo(() => getFormInstance(original, options, state), [original, options, state]);\n }\n\n useFormState<S>(selector: (state: ReturnType<typeof getFormInstance<TDraft, TOriginal>>) => S) {\n const { original, options } = useContext(this.context);\n const state = useScope(this.state);\n\n return useStore(state.map(() => selector(getFormInstance(original, options, state))));\n }\n\n useField<TPath extends PathAsString<TDraft>>(path: TPath, useStoreOptions?: UseStoreOptions) {\n const form = this.useForm();\n const state = useScope(this.state);\n\n useStore(\n form.draft.map((draft) => get(draft, path)),\n useStoreOptions,\n );\n\n useStore(\n state.map((state) => state.hasTriggeredValidations),\n useStoreOptions,\n );\n\n return form.getField(path);\n }\n\n useHasChanges() {\n const form = this.useForm();\n\n return useStore(form.draft.map(() => form.hasChanges));\n }\n\n useIsValid() {\n const form = this.useForm();\n\n return useStore(form.draft.map(() => form.isValid));\n }\n\n // ///////////////////////////////////////////////////////////////////////////\n // React Components\n // ///////////////////////////////////////////////////////////////////////////\n\n Form({\n original,\n defaultValue,\n validations,\n localizeError,\n urlState,\n ...formProps\n }: {\n original?: TOriginal;\n } & Partial<FormOptions<TDraft, TOriginal>> &\n Omit<HTMLProps<HTMLFormElement>, 'defaultValue'>) {\n const value = useMemo(\n () => ({\n original,\n options: {\n defaultValue: { ...this.options.defaultValue, ...defaultValue },\n validations: { ...this.options.validations, ...validations } as Validations<\n TDraft,\n TOriginal\n >,\n localizeError: localizeError ?? this.options.localizeError,\n },\n }),\n [original, defaultValue, validations],\n );\n\n const store = useMemo(() => {\n return createStore(this.state.defaultValue);\n }, []);\n\n useEffect(() => {\n if (urlState) {\n return connectUrl(\n store.map('draft'),\n typeof urlState === 'object' ? urlState : { key: 'form' },\n );\n }\n\n return undefined;\n }, [store, hash(urlState)]);\n\n return (\n <this.context.Provider value={value}>\n <ScopeProvider scope={this.state} store={store}>\n <FormContainer {...formProps} form={this} />\n </ScopeProvider>\n </this.context.Provider>\n );\n }\n\n Subscribe<S>({\n selector,\n children,\n }: {\n selector: (form: ReturnType<typeof getFormInstance<TDraft, TOriginal>>) => S;\n children: (selectedState: S) => ReactNode;\n }) {\n const selectedState = this.useFormState(selector);\n return <>{children(selectedState)}</>;\n }\n\n Field<\n TPath extends PathAsString<TDraft>,\n TComponent extends FormFieldComponent<any> = (\n props: ComponentPropsWithoutRef<'input'>,\n ) => JSX.Element,\n >(props: FormFieldProps<TDraft, TPath, TComponent>): JSX.Element {\n return Reflect.apply(FormField, this, [props]);\n }\n\n Error<TPath extends PathAsString<TDraft>>({ name }: FormErrorProps<TDraft, TPath>) {\n return Reflect.apply(FormError, this, [{ name }]);\n }\n}\n\nexport function createForm<TDraft, TOriginal extends TDraft = TDraft>(\n options: FormOptions<TDraft, TOriginal>,\n) {\n return new Form(options);\n}\n","import { useCache } from './useCache';\nimport type { UseStoreOptions } from './useStore';\nimport type { Cache } from '@core';\n\nexport function read<T>(cache: Cache<T>, options?: UseStoreOptions): T {\n const { status, value, error } = useCache(cache, options);\n\n if (status === 'value') {\n return value;\n }\n\n if (status === 'error') {\n throw error;\n }\n\n throw cache.state.once((state) => state.status !== 'pending');\n}\n","import { startTransition, useEffect, useMemo, useRef, useState } from 'react';\nimport { type Duration } from '@core';\nimport { debounce } from '@lib/debounce';\nimport { hash } from '@lib/hash';\nimport { throttle } from '@lib/throttle';\n\nexport interface UseDecoupledStateOptions<T> {\n debounce?: Duration;\n throttle?: Duration;\n onCommit?: (value: T) => void;\n}\n\nexport function useDecoupledState<T>(\n value: T,\n onChange: (value: T) => void,\n options: UseDecoupledStateOptions<T> = {},\n): [state: T, setState: (value: T) => void] {\n const [dirty, setDirty] = useState<{ v: T }>();\n const ref = useRef({ onChange, onCommit: options.onCommit });\n\n useEffect(() => {\n ref.current = { onChange, onCommit: options.onCommit };\n }, [onChange]);\n\n const update = useMemo(() => {\n const { onChange, onCommit } = ref.current;\n\n const update = (value: T) => {\n onChange(value);\n setDirty(undefined);\n onCommit?.(value);\n };\n\n let delayedUpdate: (value: T) => void;\n\n if (options.debounce) {\n delayedUpdate = debounce(update, options.debounce);\n } else if (options.throttle) {\n delayedUpdate = throttle(update, options.throttle);\n } else {\n delayedUpdate = (value) => startTransition(() => update(value));\n }\n\n return (value: T) => {\n setDirty({ v: value });\n delayedUpdate(value);\n };\n }, [hash([options.debounce, options.throttle])]);\n\n return [dirty ? dirty.v : value, update];\n}\n","export function castArray<T>(value: T | T[]): T[] {\n return Array.isArray(value) ? value : [value];\n}\n","import { type ReactNode, useEffect } from 'react';\nimport { castArray } from '@lib/castArray';\nimport { hash } from '@lib/hash';\n\nexport function useUrlParamScope({\n key,\n type = 'search',\n}: {\n key: string | string[];\n type?: 'search' | 'hash';\n}) {\n useEffect(\n () => () => {\n const url = new URL(window.location.href);\n const parameters = new URLSearchParams(url[type].slice(1));\n\n for (const _key of castArray(key)) {\n parameters.delete(_key);\n }\n\n url[type] = parameters.toString();\n window.history.replaceState(null, '', url.toString());\n },\n [hash(key), type],\n );\n}\n"],"names":["castArrayPath","s","jsx","Fragment","useScope","useMemo","useState","useEffect","value","createElement","state","get","deepEqual","createContext","Scope","autobind","useContext","useStore","store","createStore","connectUrl","hash","ScopeProvider","useCache","useRef","onChange","update","debounce","throttle","startTransition"],"mappings":"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"}
|
package/dist/es/react/index.mjs
CHANGED
|
@@ -54,7 +54,7 @@ function FormField({
|
|
|
54
54
|
const errorString = useMemo(
|
|
55
55
|
() => errors.map((error) => {
|
|
56
56
|
var _a, _b;
|
|
57
|
-
return ((_b = (_a = form.options).localizeError) == null ? void 0 : _b.call(_a, error)) ?? error;
|
|
57
|
+
return ((_b = (_a = form.options).localizeError) == null ? void 0 : _b.call(_a, error, name)) ?? error;
|
|
58
58
|
}).join("\n"),
|
|
59
59
|
[errors, form.options.localizeError]
|
|
60
60
|
);
|
|
@@ -126,7 +126,7 @@ function FormContainer({
|
|
|
126
126
|
form,
|
|
127
127
|
...formProps
|
|
128
128
|
}) {
|
|
129
|
-
const
|
|
129
|
+
const _form = form.useForm();
|
|
130
130
|
return /* @__PURE__ */ jsx(
|
|
131
131
|
"form",
|
|
132
132
|
{
|
|
@@ -134,7 +134,17 @@ function FormContainer({
|
|
|
134
134
|
noValidate: true,
|
|
135
135
|
onSubmit: (event) => {
|
|
136
136
|
event.preventDefault();
|
|
137
|
-
validate();
|
|
137
|
+
_form.validate();
|
|
138
|
+
let button;
|
|
139
|
+
if (event.nativeEvent instanceof SubmitEvent && (button = event.nativeEvent.submitter) && (button instanceof HTMLButtonElement || button instanceof HTMLInputElement) && button.setCustomValidity) {
|
|
140
|
+
const errors = _form.errors.map(
|
|
141
|
+
({ field, error }) => {
|
|
142
|
+
var _a, _b;
|
|
143
|
+
return ((_b = (_a = _form.options).localizeError) == null ? void 0 : _b.call(_a, error, field)) ?? error;
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
button.setCustomValidity(errors.join("\n"));
|
|
147
|
+
}
|
|
138
148
|
event.currentTarget.reportValidity();
|
|
139
149
|
}
|
|
140
150
|
}
|
|
@@ -165,9 +175,7 @@ function getFormInstance(original, options, state) {
|
|
|
165
175
|
return state.get().hasTriggeredValidations || !deepEqual(comparisonValue, this.value);
|
|
166
176
|
},
|
|
167
177
|
get errors() {
|
|
168
|
-
const blocks = Object.entries(
|
|
169
|
-
options.validations ?? {}
|
|
170
|
-
).filter(([key]) => wildcardMatch(path, key)).map(([, value2]) => value2);
|
|
178
|
+
const blocks = Object.entries(options.validations ?? {}).filter(([key]) => wildcardMatch(path, key)).map(([, value2]) => value2);
|
|
171
179
|
const value = this.value;
|
|
172
180
|
const draftValue = draft.get();
|
|
173
181
|
const errors = [];
|
|
@@ -195,7 +203,7 @@ function getFormInstance(original, options, state) {
|
|
|
195
203
|
)) {
|
|
196
204
|
for (const [field, value] of Object.entries(getWildCardMatches(draft, path))) {
|
|
197
205
|
if (!validate(value, { draft, original, field })) {
|
|
198
|
-
errors.add(
|
|
206
|
+
errors.add({ field, error: validationName });
|
|
199
207
|
}
|
|
200
208
|
}
|
|
201
209
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../../src/lib/wildcardMatch.ts","../../../src/react/form/formError.tsx","../../../src/react/form/formField.tsx","../../../src/react/form/form.tsx","../../../src/react/read.ts","../../../src/react/useDecoupledState.ts","../../../src/lib/castArray.ts","../../../src/react/useUrlParamScope.ts"],"sourcesContent":["import { type KeyType } from './path';\nimport { castArrayPath } from './propAccess';\n\nexport function wildcardMatch(s: KeyType[] | string, w: KeyType[] | string): boolean {\n if (typeof s === 'string') {\n s = castArrayPath(s);\n }\n\n if (typeof w === 'string') {\n w = castArrayPath(w);\n }\n\n return s.length === w.length && s.every((s, i) => w[i] === '*' || s === w[i]);\n}\n\nexport function getWildCardMatches(object: any, path: KeyType[] | string): Record<KeyType, any> {\n const matches: Record<KeyType, any> = {};\n const [first, ...rest] = castArrayPath(path);\n\n if (first === undefined) {\n return object;\n }\n\n if (!(object instanceof Object)) {\n return {};\n }\n\n if (first === '*') {\n for (const [key, value] of Object.entries(object)) {\n matches[key] = getWildCardMatches(value, rest);\n }\n } else {\n matches[first] = getWildCardMatches(object[first], rest);\n }\n\n return matches;\n}\n","import { type Form } from './form';\nimport { type PathAsString } from '@lib/path';\n\nexport type FormErrorProps<TDraft, TPath extends PathAsString<TDraft>> = {\n name: TPath;\n};\n\nexport function FormError<TDraft, TPath extends PathAsString<TDraft>>(\n this: Form<TDraft, any>,\n { name }: FormErrorProps<TDraft, TPath>,\n) {\n const { errors, isDirty } = this.useField(name);\n\n return isDirty ? <>{errors.join(', ')}</> : null;\n}\n","import {\n createElement,\n useEffect,\n useMemo,\n useState,\n type ComponentPropsWithoutRef,\n type ElementType,\n type HTMLProps,\n} from 'react';\nimport { type Form } from './form';\nimport { type PathAsString } from '@index';\nimport { type Value } from '@lib/path';\nimport { useScope } from '@react/scope';\n\nexport type FormFieldComponent<T> = ElementType<{\n id: string;\n value: T;\n onChange: (event: { target: { value: T } } | T | undefined, ...args: any[]) => void;\n onFocus: (...args: any[]) => void;\n onBlur: (...args: any[]) => void;\n}>;\n\ntype FieldValue<T extends FormFieldComponent<any>> = ComponentPropsWithoutRef<T & 'input'> extends {\n value: infer U;\n}\n ? U\n : ComponentPropsWithoutRef<T & 'input'> extends {\n value?: infer U;\n }\n ? U | undefined\n : never;\n\ntype FieldChangeValue<T extends FormFieldComponent<any>> = ComponentPropsWithoutRef<\n T & 'input'\n> extends {\n onChange?: (update: infer U) => void;\n}\n ? U extends { target: { value: infer V } }\n ? V\n : U\n : never;\n\nexport type FormFieldProps<\n TDraft,\n TPath extends PathAsString<TDraft>,\n TComponent extends FormFieldComponent<any>,\n> = {\n name: TPath;\n commitOnBlur?: boolean;\n commitDebounce?: number;\n inputFilter?: (value: FieldChangeValue<TComponent>) => boolean;\n onChange?: ComponentPropsWithoutRef<TComponent>['onChange'];\n onBlur?: ComponentPropsWithoutRef<TComponent>['onBlur'];\n} & (TComponent extends 'input' | ((props: HTMLProps<HTMLInputElement>) => JSX.Element)\n ? { component?: TComponent } | { children?: TComponent }\n : { component: TComponent } | { children: TComponent }) &\n Omit<\n ComponentPropsWithoutRef<TComponent>,\n | 'form'\n | 'name'\n | 'component'\n | 'commitOnBlur'\n | 'commitDebounce'\n | 'value'\n | 'onChange'\n | 'onBlur'\n | 'children'\n > &\n (Value<TDraft, TPath> extends FieldValue<TComponent>\n ? { serialize?: (value: Value<TDraft, TPath>) => FieldValue<TComponent> }\n : { serialize: (value: Value<TDraft, TPath>) => FieldValue<TComponent> }) &\n (FieldChangeValue<TComponent> extends Value<TDraft, TPath>\n ? { deserialize?: (value: FieldChangeValue<TComponent>) => Value<TDraft, TPath> }\n : { deserialize: (value: FieldChangeValue<TComponent>) => Value<TDraft, TPath> });\n\nexport function FormField<\n TDraft,\n TPath extends PathAsString<TDraft>,\n TComponent extends FormFieldComponent<any>,\n>(\n this: Form<TDraft, any>,\n {\n // id,\n name,\n commitOnBlur,\n commitDebounce,\n inputFilter,\n serialize = (x) => x as FieldValue<TComponent>,\n deserialize = (x) => x as Value<TDraft, TPath>,\n ...restProps\n }: FormFieldProps<TDraft, TPath, TComponent>,\n): JSX.Element {\n type T = FieldChangeValue<TComponent>;\n const id = '';\n const component = (('component' in restProps\n ? restProps.component\n : 'children' in restProps\n ? restProps.children\n : undefined) ?? 'input') as TComponent;\n\n const form = this.useForm();\n const state = useScope(this.state);\n const { value, setValue, errors } = this.useField(name);\n\n const errorString = useMemo(\n () => errors.map((error) => form.options.localizeError?.(error) ?? error).join('\\n'),\n [errors, form.options.localizeError],\n );\n const [localValue, setLocalValue] = useState<T>();\n const _id = useMemo(\n () =>\n id || `f${Math.random().toString(36).slice(2, 15)}${Math.random().toString(36).slice(2, 15)}`,\n\n [id],\n );\n\n useEffect(() => {\n if (localValue === undefined || !commitDebounce) {\n return;\n }\n\n const timeout = setTimeout(() => {\n setValue(deserialize(localValue));\n setLocalValue(undefined);\n }, commitDebounce);\n\n return () => clearTimeout(timeout);\n }, [localValue, commitDebounce]);\n\n useEffect(() => {\n const element = document.querySelector(\n [`#${_id} input`, `#${_id} select`, `#${_id} textarea`, `#${_id}`].join(','),\n );\n\n if (\n !(\n element instanceof HTMLInputElement ||\n element instanceof HTMLSelectElement ||\n element instanceof HTMLTextAreaElement\n )\n ) {\n return;\n }\n\n element.setCustomValidity(errorString);\n }, [_id, errorString]);\n\n const props = {\n ...restProps,\n component: undefined,\n children: undefined,\n id: _id,\n name,\n value: localValue ?? serialize(value),\n onChange: (event: { target: { value: T } } | T, ...moreArgs: any[]) => {\n const value =\n typeof event === 'object' && event !== null && 'target' in event\n ? event.target.value\n : event;\n\n if (inputFilter && !inputFilter(value)) {\n return;\n }\n\n if (commitOnBlur || commitDebounce) {\n setLocalValue(value);\n } else {\n setValue(deserialize(value));\n }\n\n restProps.onChange?.(event, ...moreArgs);\n },\n onFocus(...args: any[]) {\n state.set('touched', (touched) => {\n touched = new Set(touched);\n touched.add(_id);\n return touched;\n });\n\n restProps.onFocus?.apply(null, args);\n },\n onBlur(...args: any[]) {\n if (localValue !== undefined) {\n setValue(deserialize(localValue));\n setLocalValue(undefined);\n }\n\n restProps.onBlur?.apply(null, args);\n },\n };\n\n return createElement(component, props);\n}\n","import { Scope, type Store, connectUrl, createStore, type UrlStoreOptions } from '@core';\nimport { autobind } from '@lib/autobind';\nimport { deepEqual } from '@lib/equals';\nimport { hash } from '@lib/hash';\nimport {\n type PathAsString,\n type Value,\n type WildcardPathAsString,\n type WildcardValue,\n} from '@lib/path';\nimport { get } from '@lib/propAccess';\nimport { getWildCardMatches, wildcardMatch } from '@lib/wildcardMatch';\nimport {\n type ReactNode,\n createContext,\n useContext,\n useEffect,\n useMemo,\n type ComponentPropsWithoutRef,\n type HTMLProps,\n} from 'react';\nimport { ScopeProvider, useScope } from '../scope';\nimport { useStore, type UseStoreOptions } from '../useStore';\nimport { FormError, type FormErrorProps } from './formError';\nimport { FormField, type FormFieldComponent, type FormFieldProps } from './formField';\n\n/// /////////////////////////////////////////////////////////////////////////////\n// Form types\n/// /////////////////////////////////////////////////////////////////////////////\n\nexport interface FormOptions<TDraft, TOriginal> {\n defaultValue: TDraft;\n validations?: Validations<TDraft, TOriginal>;\n localizeError?: (error: string) => string | undefined;\n urlState?: boolean | UrlStoreOptions<TDraft>;\n}\n\nexport type Validations<TDraft, TOriginal> = {\n [P in WildcardPathAsString<TDraft>]?: Record<\n string,\n Validation<WildcardValue<TDraft, P>, TDraft, TOriginal>\n >;\n};\n\nexport type Validation<TValue, TDraft, TOriginal> = (\n value: TValue,\n context: { draft: TDraft; original: TOriginal; field: PathAsString<TDraft> },\n) => boolean;\n\nexport interface Field<TDraft, TOriginal, TPath extends PathAsString<TDraft>> {\n originalValue: Value<TOriginal, TPath> | undefined;\n value: Value<TDraft, TPath>;\n setValue: (\n value: Value<TDraft, TPath> | ((value: Value<TDraft, TPath>) => Value<TDraft, TPath>),\n ) => void;\n isDirty: boolean;\n errors: string[];\n}\n\ninterface FormState<TDraft> {\n draft?: TDraft;\n touched: Set<string>;\n errors: Map<string, string[]>;\n hasTriggeredValidations?: boolean;\n}\n\n/// /////////////////////////////////////////////////////////////////////////////\n// Implementation\n/// /////////////////////////////////////////////////////////////////////////////\n\nfunction FormContainer({\n form,\n ...formProps\n}: { form: Form<any, any> } & Omit<HTMLProps<HTMLFormElement>, 'form'>) {\n const { validate } = form.useForm();\n\n return (\n <form\n {...formProps}\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n\n validate();\n event.currentTarget.reportValidity();\n }}\n />\n );\n}\n\nfunction getFormInstance<TDraft, TOriginal extends TDraft>(\n original: TOriginal | undefined,\n options: FormOptions<TDraft, TOriginal>,\n state: Store<FormState<TDraft>>,\n) {\n const instance = {\n original,\n\n draft: state.map(\n (state) => state.draft ?? original ?? options.defaultValue,\n (draft) => (state) => ({ ...state, draft }),\n ),\n\n options,\n\n getField: <TPath extends PathAsString<TDraft>>(\n path: TPath,\n ): Field<TDraft, TOriginal, TPath> => {\n const { draft } = instance;\n\n return {\n get originalValue() {\n return original !== undefined ? get(original as any, path as any) : undefined;\n },\n\n get value() {\n return get(draft.get(), path);\n },\n\n setValue(update) {\n draft.set(path, update);\n },\n\n get isDirty() {\n const comparisonValue = this.originalValue ?? get(options.defaultValue, path);\n\n return state.get().hasTriggeredValidations || !deepEqual(comparisonValue, this.value);\n },\n\n get errors() {\n const blocks: Record<string, Validation<any, any, any>>[] = Object.entries(\n options.validations ?? {},\n )\n .filter(([key]) => wildcardMatch(path, key))\n .map(([, value]) => value);\n\n const value = this.value;\n const draftValue = draft.get();\n const errors: string[] = [];\n\n for (const block of blocks ?? []) {\n for (const [validationName, validate] of Object.entries(block)) {\n if (!validate(value, { draft: draftValue, original, field: path })) {\n errors.push(validationName);\n }\n }\n }\n\n return errors;\n },\n };\n },\n\n get hasChanges() {\n const { draft } = state.get();\n return !!draft && !deepEqual(draft, original ?? options.defaultValue);\n },\n\n get errors(): string[] {\n const draft = instance.draft.get();\n const errors = new Set<string>();\n\n for (const [path, block] of Object.entries(options.validations ?? {})) {\n for (const [validationName, validate] of Object.entries(\n block as Record<string, Validation<any, any, any>>,\n )) {\n for (const [field, value] of Object.entries(getWildCardMatches(draft, path))) {\n if (!validate(value, { draft, original, field })) {\n errors.add(`${field}.${validationName}`);\n }\n }\n }\n }\n\n return [...errors];\n },\n\n get isValid() {\n return instance.errors.length === 0;\n },\n\n validate: () => {\n state.set('hasTriggeredValidations', true);\n return instance.isValid;\n },\n\n reset() {\n state.set('draft', undefined);\n },\n };\n\n return instance;\n}\n\nexport class Form<TDraft, TOriginal extends TDraft = TDraft> {\n context = createContext({\n original: undefined as TOriginal | undefined,\n options: this.options,\n });\n\n state = new Scope<FormState<TDraft>>({\n touched: new Set(),\n errors: new Map(),\n });\n\n constructor(public readonly options: FormOptions<TDraft, TOriginal>) {\n autobind(Form);\n }\n\n useForm() {\n const { original, options } = useContext(this.context);\n const state = useScope(this.state);\n\n return useMemo(() => getFormInstance(original, options, state), [original, options, state]);\n }\n\n useFormState<S>(selector: (state: ReturnType<typeof getFormInstance<TDraft, TOriginal>>) => S) {\n const { original, options } = useContext(this.context);\n const state = useScope(this.state);\n\n return useStore(state.map(() => selector(getFormInstance(original, options, state))));\n }\n\n useField<TPath extends PathAsString<TDraft>>(path: TPath, useStoreOptions?: UseStoreOptions) {\n const form = this.useForm();\n const state = useScope(this.state);\n\n useStore(\n form.draft.map((draft) => get(draft, path)),\n useStoreOptions,\n );\n\n useStore(\n state.map((state) => state.hasTriggeredValidations),\n useStoreOptions,\n );\n\n return form.getField(path);\n }\n\n useHasChanges() {\n const form = this.useForm();\n\n return useStore(form.draft.map(() => form.hasChanges));\n }\n\n useIsValid() {\n const form = this.useForm();\n\n return useStore(form.draft.map(() => form.isValid));\n }\n\n // ///////////////////////////////////////////////////////////////////////////\n // React Components\n // ///////////////////////////////////////////////////////////////////////////\n\n Form({\n original,\n defaultValue,\n validations,\n localizeError,\n urlState,\n ...formProps\n }: {\n original?: TOriginal;\n } & Partial<FormOptions<TDraft, TOriginal>> &\n Omit<HTMLProps<HTMLFormElement>, 'defaultValue'>) {\n const value = useMemo(\n () => ({\n original,\n options: {\n defaultValue: { ...this.options.defaultValue, ...defaultValue },\n validations: { ...this.options.validations, ...validations } as Validations<\n TDraft,\n TOriginal\n >,\n localizeError: localizeError ?? this.options.localizeError,\n },\n }),\n [original, defaultValue, validations],\n );\n\n const store = useMemo(() => {\n return createStore(this.state.defaultValue);\n }, []);\n\n useEffect(() => {\n if (urlState) {\n return connectUrl(\n store.map('draft'),\n typeof urlState === 'object' ? urlState : { key: 'form' },\n );\n }\n\n return undefined;\n }, [store, hash(urlState)]);\n\n return (\n <this.context.Provider value={value}>\n <ScopeProvider scope={this.state} store={store}>\n <FormContainer {...formProps} form={this} />\n </ScopeProvider>\n </this.context.Provider>\n );\n }\n\n Subscribe<S>({\n selector,\n children,\n }: {\n selector: (form: ReturnType<typeof getFormInstance<TDraft, TOriginal>>) => S;\n children: (selectedState: S) => ReactNode;\n }) {\n const selectedState = this.useFormState(selector);\n return <>{children(selectedState)}</>;\n }\n\n Field<\n TPath extends PathAsString<TDraft>,\n TComponent extends FormFieldComponent<any> = (\n props: ComponentPropsWithoutRef<'input'>,\n ) => JSX.Element,\n >(props: FormFieldProps<TDraft, TPath, TComponent>): JSX.Element {\n return Reflect.apply(FormField, this, [props]);\n }\n\n Error<TPath extends PathAsString<TDraft>>({ name }: FormErrorProps<TDraft, TPath>) {\n return Reflect.apply(FormError, this, [{ name }]);\n }\n}\n\nexport function createForm<TDraft, TOriginal extends TDraft = TDraft>(\n options: FormOptions<TDraft, TOriginal>,\n) {\n return new Form(options);\n}\n","import { useCache } from './useCache';\nimport type { UseStoreOptions } from './useStore';\nimport type { Cache } from '@core';\n\nexport function read<T>(cache: Cache<T>, options?: UseStoreOptions): T {\n const { status, value, error } = useCache(cache, options);\n\n if (status === 'value') {\n return value;\n }\n\n if (status === 'error') {\n throw error;\n }\n\n throw cache.state.once((state) => state.status !== 'pending');\n}\n","import { startTransition, useEffect, useMemo, useRef, useState } from 'react';\nimport { type Duration } from '@core';\nimport { debounce } from '@lib/debounce';\nimport { hash } from '@lib/hash';\nimport { throttle } from '@lib/throttle';\n\nexport interface UseDecoupledStateOptions<T> {\n debounce?: Duration;\n throttle?: Duration;\n onCommit?: (value: T) => void;\n}\n\nexport function useDecoupledState<T>(\n value: T,\n onChange: (value: T) => void,\n options: UseDecoupledStateOptions<T> = {},\n): [state: T, setState: (value: T) => void] {\n const [dirty, setDirty] = useState<{ v: T }>();\n const ref = useRef({ onChange, onCommit: options.onCommit });\n\n useEffect(() => {\n ref.current = { onChange, onCommit: options.onCommit };\n }, [onChange]);\n\n const update = useMemo(() => {\n const { onChange, onCommit } = ref.current;\n\n const update = (value: T) => {\n onChange(value);\n setDirty(undefined);\n onCommit?.(value);\n };\n\n let delayedUpdate: (value: T) => void;\n\n if (options.debounce) {\n delayedUpdate = debounce(update, options.debounce);\n } else if (options.throttle) {\n delayedUpdate = throttle(update, options.throttle);\n } else {\n delayedUpdate = (value) => startTransition(() => update(value));\n }\n\n return (value: T) => {\n setDirty({ v: value });\n delayedUpdate(value);\n };\n }, [hash([options.debounce, options.throttle])]);\n\n return [dirty ? dirty.v : value, update];\n}\n","export function castArray<T>(value: T | T[]): T[] {\n return Array.isArray(value) ? value : [value];\n}\n","import { type ReactNode, useEffect } from 'react';\nimport { castArray } from '@lib/castArray';\nimport { hash } from '@lib/hash';\n\nexport function useUrlParamScope({\n key,\n type = 'search',\n}: {\n key: string | string[];\n type?: 'search' | 'hash';\n}) {\n useEffect(\n () => () => {\n const url = new URL(window.location.href);\n const parameters = new URLSearchParams(url[type].slice(1));\n\n for (const _key of castArray(key)) {\n parameters.delete(_key);\n }\n\n url[type] = parameters.toString();\n window.history.replaceState(null, '', url.toString());\n },\n [hash(key), type],\n );\n}\n"],"names":["s","value","state","onChange","update"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../../../src/lib/wildcardMatch.ts","../../../src/react/form/formError.tsx","../../../src/react/form/formField.tsx","../../../src/react/form/form.tsx","../../../src/react/read.ts","../../../src/react/useDecoupledState.ts","../../../src/lib/castArray.ts","../../../src/react/useUrlParamScope.ts"],"sourcesContent":["import { type KeyType } from './path';\nimport { castArrayPath } from './propAccess';\n\nexport function wildcardMatch(s: KeyType[] | string, w: KeyType[] | string): boolean {\n if (typeof s === 'string') {\n s = castArrayPath(s);\n }\n\n if (typeof w === 'string') {\n w = castArrayPath(w);\n }\n\n return s.length === w.length && s.every((s, i) => w[i] === '*' || s === w[i]);\n}\n\nexport function getWildCardMatches(object: any, path: KeyType[] | string): Record<KeyType, any> {\n const matches: Record<KeyType, any> = {};\n const [first, ...rest] = castArrayPath(path);\n\n if (first === undefined) {\n return object;\n }\n\n if (!(object instanceof Object)) {\n return {};\n }\n\n if (first === '*') {\n for (const [key, value] of Object.entries(object)) {\n matches[key] = getWildCardMatches(value, rest);\n }\n } else {\n matches[first] = getWildCardMatches(object[first], rest);\n }\n\n return matches;\n}\n","import { type Form } from './form';\nimport { type PathAsString } from '@lib/path';\n\nexport type FormErrorProps<TDraft, TPath extends PathAsString<TDraft>> = {\n name: TPath;\n};\n\nexport function FormError<TDraft, TPath extends PathAsString<TDraft>>(\n this: Form<TDraft, any>,\n { name }: FormErrorProps<TDraft, TPath>,\n) {\n const { errors, isDirty } = this.useField(name);\n\n return isDirty ? <>{errors.join(', ')}</> : null;\n}\n","import { type PathAsString } from '@index';\nimport { type Value } from '@lib/path';\nimport { useScope } from '@react/scope';\nimport {\n createElement,\n useEffect,\n useMemo,\n useState,\n type ComponentPropsWithoutRef,\n type ComponentType,\n type HTMLProps,\n} from 'react';\nimport { type Form } from './form';\n\ninterface FormFieldComponentProps<T> {\n id: string;\n value: T;\n onChange: (event: { target: { value: T } } | T | undefined, ...args: any[]) => void;\n onFocus: (...args: any[]) => void;\n onBlur: (...args: any[]) => void;\n}\n\ntype NativeInputType = 'input' | 'select' | 'textarea';\n\ntype FieldValue<T extends FormFieldComponent<any>> = ComponentPropsWithoutRef<T & 'input'> extends {\n value: infer U;\n}\n ? U\n : ComponentPropsWithoutRef<T & 'input'> extends {\n value?: infer U;\n }\n ? U | undefined\n : never;\n\ntype FieldChangeValue<T extends FormFieldComponent<any>> = ComponentPropsWithoutRef<\n T & 'input'\n> extends {\n onChange?: (update: infer U) => void;\n}\n ? U extends { target: { value: infer V } }\n ? V\n : U\n : never;\n\nexport type FormFieldComponent<T> =\n | (string | number extends T ? NativeInputType : never)\n | ComponentType<FormFieldComponentProps<T>>;\n\nexport type FormFieldProps<\n TDraft,\n TPath extends PathAsString<TDraft>,\n TComponent extends FormFieldComponent<any>,\n> = {\n name: TPath;\n commitOnBlur?: boolean;\n commitDebounce?: number;\n inputFilter?: (value: FieldChangeValue<TComponent>) => boolean;\n onChange?: ComponentPropsWithoutRef<TComponent>['onChange'];\n onBlur?: ComponentPropsWithoutRef<TComponent>['onBlur'];\n} & (TComponent extends 'input' | ((props: HTMLProps<HTMLInputElement>) => JSX.Element)\n ? { component?: TComponent } | { children?: TComponent }\n : { component: TComponent } | { children: TComponent }) &\n Omit<\n ComponentPropsWithoutRef<TComponent>,\n | 'form'\n | 'name'\n | 'component'\n | 'commitOnBlur'\n | 'commitDebounce'\n | 'value'\n | 'onChange'\n | 'onBlur'\n | 'children'\n > &\n (Value<TDraft, TPath> extends FieldValue<TComponent>\n ? { serialize?: (value: Value<TDraft, TPath>) => FieldValue<TComponent> }\n : { serialize: (value: Value<TDraft, TPath>) => FieldValue<TComponent> }) &\n (FieldChangeValue<TComponent> extends Value<TDraft, TPath>\n ? { deserialize?: (value: FieldChangeValue<TComponent>) => Value<TDraft, TPath> }\n : { deserialize: (value: FieldChangeValue<TComponent>) => Value<TDraft, TPath> });\n\nexport function FormField<\n TDraft,\n TPath extends PathAsString<TDraft>,\n TComponent extends FormFieldComponent<any>,\n>(\n this: Form<TDraft, any>,\n {\n // id,\n name,\n commitOnBlur,\n commitDebounce,\n inputFilter,\n serialize = (x) => x as FieldValue<TComponent>,\n deserialize = (x) => x as Value<TDraft, TPath>,\n ...restProps\n }: FormFieldProps<TDraft, TPath, TComponent>,\n): JSX.Element {\n type T = FieldChangeValue<TComponent>;\n const id = '';\n const component = (('component' in restProps\n ? restProps.component\n : 'children' in restProps\n ? restProps.children\n : undefined) ?? 'input') as TComponent;\n\n const form = this.useForm();\n const state = useScope(this.state);\n const { value, setValue, errors } = this.useField(name);\n\n const errorString = useMemo(\n () => errors.map((error) => form.options.localizeError?.(error, name) ?? error).join('\\n'),\n [errors, form.options.localizeError],\n );\n const [localValue, setLocalValue] = useState<T>();\n const _id = useMemo(\n () =>\n id || `f${Math.random().toString(36).slice(2, 15)}${Math.random().toString(36).slice(2, 15)}`,\n\n [id],\n );\n\n useEffect(() => {\n if (localValue === undefined || !commitDebounce) {\n return;\n }\n\n const timeout = setTimeout(() => {\n setValue(deserialize(localValue));\n setLocalValue(undefined);\n }, commitDebounce);\n\n return () => clearTimeout(timeout);\n }, [localValue, commitDebounce]);\n\n useEffect(() => {\n const element = document.querySelector(\n [`#${_id} input`, `#${_id} select`, `#${_id} textarea`, `#${_id}`].join(','),\n );\n\n if (\n !(\n element instanceof HTMLInputElement ||\n element instanceof HTMLSelectElement ||\n element instanceof HTMLTextAreaElement\n )\n ) {\n return;\n }\n\n element.setCustomValidity(errorString);\n }, [_id, errorString]);\n\n const props = {\n ...restProps,\n component: undefined,\n children: undefined,\n id: _id,\n name,\n value: localValue ?? serialize(value),\n onChange: (event: { target: { value: T } } | T, ...moreArgs: any[]) => {\n const value =\n typeof event === 'object' && event !== null && 'target' in event\n ? event.target.value\n : event;\n\n if (inputFilter && !inputFilter(value)) {\n return;\n }\n\n if (commitOnBlur || commitDebounce) {\n setLocalValue(value);\n } else {\n setValue(deserialize(value));\n }\n\n restProps.onChange?.(event, ...moreArgs);\n },\n onFocus(...args: any[]) {\n state.set('touched', (touched) => {\n touched = new Set(touched);\n touched.add(_id);\n return touched;\n });\n\n restProps.onFocus?.apply(null, args);\n },\n onBlur(...args: any[]) {\n if (localValue !== undefined) {\n setValue(deserialize(localValue));\n setLocalValue(undefined);\n }\n\n restProps.onBlur?.apply(null, args);\n },\n };\n\n return createElement(component, props);\n}\n","import { Scope, type Store, connectUrl, createStore, type UrlStoreOptions } from '@core';\nimport { autobind } from '@lib/autobind';\nimport { deepEqual } from '@lib/equals';\nimport { hash } from '@lib/hash';\nimport {\n type PathAsString,\n type Value,\n type WildcardPathAsString,\n type WildcardValue,\n} from '@lib/path';\nimport { get } from '@lib/propAccess';\nimport { getWildCardMatches, wildcardMatch } from '@lib/wildcardMatch';\nimport {\n type ReactNode,\n createContext,\n useContext,\n useEffect,\n useMemo,\n type ComponentPropsWithoutRef,\n type HTMLProps,\n} from 'react';\nimport { ScopeProvider, useScope } from '../scope';\nimport { useStore, type UseStoreOptions } from '../useStore';\nimport { FormError, type FormErrorProps } from './formError';\nimport { FormField, type FormFieldComponent, type FormFieldProps } from './formField';\n\n/// /////////////////////////////////////////////////////////////////////////////\n// Form types\n/// /////////////////////////////////////////////////////////////////////////////\n\nexport interface FormOptions<TDraft, TOriginal> {\n defaultValue: TDraft;\n validations?: Validations<TDraft, TOriginal>;\n localizeError?: (error: string, field: string) => string | undefined;\n urlState?: boolean | UrlStoreOptions<TDraft>;\n}\n\nexport type Validations<TDraft, TOriginal> = {\n [P in WildcardPathAsString<TDraft>]?: Record<\n string,\n Validation<WildcardValue<TDraft, P>, TDraft, TOriginal>\n >;\n} & Record<string, Record<string, Validation<any, TDraft, TOriginal>>>;\n\nexport type Validation<TValue, TDraft, TOriginal> = (\n value: TValue,\n context: { draft: TDraft; original: TOriginal; field: PathAsString<TDraft> },\n) => boolean;\n\nexport interface Field<TDraft, TOriginal, TPath extends PathAsString<TDraft>> {\n originalValue: Value<TOriginal, TPath> | undefined;\n value: Value<TDraft, TPath>;\n setValue: (\n value: Value<TDraft, TPath> | ((value: Value<TDraft, TPath>) => Value<TDraft, TPath>),\n ) => void;\n isDirty: boolean;\n errors: string[];\n}\n\ninterface FormState<TDraft> {\n draft?: TDraft;\n touched: Set<string>;\n errors: Map<string, string[]>;\n hasTriggeredValidations?: boolean;\n}\n\n/// /////////////////////////////////////////////////////////////////////////////\n// Implementation\n/// /////////////////////////////////////////////////////////////////////////////\n\nfunction FormContainer({\n form,\n ...formProps\n}: { form: Form<any, any> } & Omit<HTMLProps<HTMLFormElement>, 'form'>) {\n const _form = form.useForm();\n\n return (\n <form\n {...formProps}\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n\n _form.validate();\n\n let button;\n\n if (\n event.nativeEvent instanceof SubmitEvent &&\n (button = event.nativeEvent.submitter) &&\n (button instanceof HTMLButtonElement || button instanceof HTMLInputElement) &&\n button.setCustomValidity\n ) {\n const errors = _form.errors.map(\n ({ field, error }) => _form.options.localizeError?.(error, field) ?? error,\n );\n button.setCustomValidity(errors.join('\\n'));\n }\n event.currentTarget.reportValidity();\n }}\n />\n );\n}\n\nfunction getFormInstance<TDraft, TOriginal extends TDraft>(\n original: TOriginal | undefined,\n options: FormOptions<TDraft, TOriginal>,\n state: Store<FormState<TDraft>>,\n) {\n const instance = {\n original,\n\n draft: state.map(\n (state) => state.draft ?? original ?? options.defaultValue,\n (draft) => (state) => ({ ...state, draft }),\n ),\n\n options,\n\n getField: <TPath extends PathAsString<TDraft>>(\n path: TPath,\n ): Field<TDraft, TOriginal, TPath> => {\n const { draft } = instance;\n\n return {\n get originalValue() {\n return original !== undefined ? get(original as any, path as any) : undefined;\n },\n\n get value() {\n return get(draft.get(), path);\n },\n\n setValue(update) {\n draft.set(path, update);\n },\n\n get isDirty() {\n const comparisonValue = this.originalValue ?? get(options.defaultValue, path);\n\n return state.get().hasTriggeredValidations || !deepEqual(comparisonValue, this.value);\n },\n\n get errors() {\n const blocks: (Validation<any, any, any> | Record<string, Validation<any, any, any>>)[] =\n Object.entries(options.validations ?? {})\n .filter(([key]) => wildcardMatch(path, key))\n .map(([, value]) => value);\n\n const value = this.value;\n const draftValue = draft.get();\n const errors: string[] = [];\n\n for (const block of blocks ?? []) {\n for (const [validationName, validate] of Object.entries(block)) {\n if (!validate(value, { draft: draftValue, original, field: path })) {\n errors.push(validationName);\n }\n }\n }\n\n return errors;\n },\n };\n },\n\n get hasChanges() {\n const { draft } = state.get();\n return !!draft && !deepEqual(draft, original ?? options.defaultValue);\n },\n\n get errors() {\n const draft = instance.draft.get();\n const errors = new Set<{ field: string; error: string }>();\n\n for (const [path, block] of Object.entries(options.validations ?? {})) {\n for (const [validationName, validate] of Object.entries(\n block as Record<string, Validation<any, any, any>>,\n )) {\n for (const [field, value] of Object.entries(getWildCardMatches(draft, path))) {\n if (!validate(value, { draft, original, field })) {\n errors.add({ field, error: validationName });\n }\n }\n }\n }\n\n return [...errors];\n },\n\n get isValid() {\n return instance.errors.length === 0;\n },\n\n validate: () => {\n state.set('hasTriggeredValidations', true);\n return instance.isValid;\n },\n\n reset() {\n state.set('draft', undefined);\n },\n };\n\n return instance;\n}\n\nexport class Form<TDraft, TOriginal extends TDraft = TDraft> {\n context = createContext({\n original: undefined as TOriginal | undefined,\n options: this.options,\n });\n\n state = new Scope<FormState<TDraft>>({\n touched: new Set(),\n errors: new Map(),\n });\n\n constructor(public readonly options: FormOptions<TDraft, TOriginal>) {\n autobind(Form);\n }\n\n useForm() {\n const { original, options } = useContext(this.context);\n const state = useScope(this.state);\n\n return useMemo(() => getFormInstance(original, options, state), [original, options, state]);\n }\n\n useFormState<S>(selector: (state: ReturnType<typeof getFormInstance<TDraft, TOriginal>>) => S) {\n const { original, options } = useContext(this.context);\n const state = useScope(this.state);\n\n return useStore(state.map(() => selector(getFormInstance(original, options, state))));\n }\n\n useField<TPath extends PathAsString<TDraft>>(path: TPath, useStoreOptions?: UseStoreOptions) {\n const form = this.useForm();\n const state = useScope(this.state);\n\n useStore(\n form.draft.map((draft) => get(draft, path)),\n useStoreOptions,\n );\n\n useStore(\n state.map((state) => state.hasTriggeredValidations),\n useStoreOptions,\n );\n\n return form.getField(path);\n }\n\n useHasChanges() {\n const form = this.useForm();\n\n return useStore(form.draft.map(() => form.hasChanges));\n }\n\n useIsValid() {\n const form = this.useForm();\n\n return useStore(form.draft.map(() => form.isValid));\n }\n\n // ///////////////////////////////////////////////////////////////////////////\n // React Components\n // ///////////////////////////////////////////////////////////////////////////\n\n Form({\n original,\n defaultValue,\n validations,\n localizeError,\n urlState,\n ...formProps\n }: {\n original?: TOriginal;\n } & Partial<FormOptions<TDraft, TOriginal>> &\n Omit<HTMLProps<HTMLFormElement>, 'defaultValue'>) {\n const value = useMemo(\n () => ({\n original,\n options: {\n defaultValue: { ...this.options.defaultValue, ...defaultValue },\n validations: { ...this.options.validations, ...validations } as Validations<\n TDraft,\n TOriginal\n >,\n localizeError: localizeError ?? this.options.localizeError,\n },\n }),\n [original, defaultValue, validations],\n );\n\n const store = useMemo(() => {\n return createStore(this.state.defaultValue);\n }, []);\n\n useEffect(() => {\n if (urlState) {\n return connectUrl(\n store.map('draft'),\n typeof urlState === 'object' ? urlState : { key: 'form' },\n );\n }\n\n return undefined;\n }, [store, hash(urlState)]);\n\n return (\n <this.context.Provider value={value}>\n <ScopeProvider scope={this.state} store={store}>\n <FormContainer {...formProps} form={this} />\n </ScopeProvider>\n </this.context.Provider>\n );\n }\n\n Subscribe<S>({\n selector,\n children,\n }: {\n selector: (form: ReturnType<typeof getFormInstance<TDraft, TOriginal>>) => S;\n children: (selectedState: S) => ReactNode;\n }) {\n const selectedState = this.useFormState(selector);\n return <>{children(selectedState)}</>;\n }\n\n Field<\n TPath extends PathAsString<TDraft>,\n TComponent extends FormFieldComponent<any> = (\n props: ComponentPropsWithoutRef<'input'>,\n ) => JSX.Element,\n >(props: FormFieldProps<TDraft, TPath, TComponent>): JSX.Element {\n return Reflect.apply(FormField, this, [props]);\n }\n\n Error<TPath extends PathAsString<TDraft>>({ name }: FormErrorProps<TDraft, TPath>) {\n return Reflect.apply(FormError, this, [{ name }]);\n }\n}\n\nexport function createForm<TDraft, TOriginal extends TDraft = TDraft>(\n options: FormOptions<TDraft, TOriginal>,\n) {\n return new Form(options);\n}\n","import { useCache } from './useCache';\nimport type { UseStoreOptions } from './useStore';\nimport type { Cache } from '@core';\n\nexport function read<T>(cache: Cache<T>, options?: UseStoreOptions): T {\n const { status, value, error } = useCache(cache, options);\n\n if (status === 'value') {\n return value;\n }\n\n if (status === 'error') {\n throw error;\n }\n\n throw cache.state.once((state) => state.status !== 'pending');\n}\n","import { startTransition, useEffect, useMemo, useRef, useState } from 'react';\nimport { type Duration } from '@core';\nimport { debounce } from '@lib/debounce';\nimport { hash } from '@lib/hash';\nimport { throttle } from '@lib/throttle';\n\nexport interface UseDecoupledStateOptions<T> {\n debounce?: Duration;\n throttle?: Duration;\n onCommit?: (value: T) => void;\n}\n\nexport function useDecoupledState<T>(\n value: T,\n onChange: (value: T) => void,\n options: UseDecoupledStateOptions<T> = {},\n): [state: T, setState: (value: T) => void] {\n const [dirty, setDirty] = useState<{ v: T }>();\n const ref = useRef({ onChange, onCommit: options.onCommit });\n\n useEffect(() => {\n ref.current = { onChange, onCommit: options.onCommit };\n }, [onChange]);\n\n const update = useMemo(() => {\n const { onChange, onCommit } = ref.current;\n\n const update = (value: T) => {\n onChange(value);\n setDirty(undefined);\n onCommit?.(value);\n };\n\n let delayedUpdate: (value: T) => void;\n\n if (options.debounce) {\n delayedUpdate = debounce(update, options.debounce);\n } else if (options.throttle) {\n delayedUpdate = throttle(update, options.throttle);\n } else {\n delayedUpdate = (value) => startTransition(() => update(value));\n }\n\n return (value: T) => {\n setDirty({ v: value });\n delayedUpdate(value);\n };\n }, [hash([options.debounce, options.throttle])]);\n\n return [dirty ? dirty.v : value, update];\n}\n","export function castArray<T>(value: T | T[]): T[] {\n return Array.isArray(value) ? value : [value];\n}\n","import { type ReactNode, useEffect } from 'react';\nimport { castArray } from '@lib/castArray';\nimport { hash } from '@lib/hash';\n\nexport function useUrlParamScope({\n key,\n type = 'search',\n}: {\n key: string | string[];\n type?: 'search' | 'hash';\n}) {\n useEffect(\n () => () => {\n const url = new URL(window.location.href);\n const parameters = new URLSearchParams(url[type].slice(1));\n\n for (const _key of castArray(key)) {\n parameters.delete(_key);\n }\n\n url[type] = parameters.toString();\n window.history.replaceState(null, '', url.toString());\n },\n [hash(key), type],\n );\n}\n"],"names":["s","value","state","onChange","update"],"mappings":"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"}
|
|
@@ -7,12 +7,12 @@ import { type FormFieldComponent, type FormFieldProps } from './formField';
|
|
|
7
7
|
export interface FormOptions<TDraft, TOriginal> {
|
|
8
8
|
defaultValue: TDraft;
|
|
9
9
|
validations?: Validations<TDraft, TOriginal>;
|
|
10
|
-
localizeError?: (error: string) => string | undefined;
|
|
10
|
+
localizeError?: (error: string, field: string) => string | undefined;
|
|
11
11
|
urlState?: boolean | UrlStoreOptions<TDraft>;
|
|
12
12
|
}
|
|
13
13
|
export type Validations<TDraft, TOriginal> = {
|
|
14
14
|
[P in WildcardPathAsString<TDraft>]?: Record<string, Validation<WildcardValue<TDraft, P>, TDraft, TOriginal>>;
|
|
15
|
-
}
|
|
15
|
+
} & Record<string, Record<string, Validation<any, TDraft, TOriginal>>>;
|
|
16
16
|
export type Validation<TValue, TDraft, TOriginal> = (value: TValue, context: {
|
|
17
17
|
draft: TDraft;
|
|
18
18
|
original: TOriginal;
|
|
@@ -37,7 +37,10 @@ declare function getFormInstance<TDraft, TOriginal extends TDraft>(original: TOr
|
|
|
37
37
|
options: FormOptions<TDraft, TOriginal>;
|
|
38
38
|
getField: <TPath extends PathAsString<TDraft>>(path: TPath) => Field<TDraft, TOriginal, TPath>;
|
|
39
39
|
readonly hasChanges: boolean;
|
|
40
|
-
readonly errors:
|
|
40
|
+
readonly errors: {
|
|
41
|
+
field: string;
|
|
42
|
+
error: string;
|
|
43
|
+
}[];
|
|
41
44
|
readonly isValid: boolean;
|
|
42
45
|
validate: () => boolean;
|
|
43
46
|
reset(): void;
|
|
@@ -56,7 +59,10 @@ export declare class Form<TDraft, TOriginal extends TDraft = TDraft> {
|
|
|
56
59
|
options: FormOptions<TDraft, TOriginal>;
|
|
57
60
|
getField: <TPath extends import("../../lib/typeHelpers").ArrayToStringPath<import("../../lib/path")._PathAsArray<TDraft, false, 5, []>>>(path: TPath) => Field<TDraft, TOriginal, TPath>;
|
|
58
61
|
readonly hasChanges: boolean;
|
|
59
|
-
readonly errors:
|
|
62
|
+
readonly errors: {
|
|
63
|
+
field: string;
|
|
64
|
+
error: string;
|
|
65
|
+
}[];
|
|
60
66
|
readonly isValid: boolean;
|
|
61
67
|
validate: () => boolean;
|
|
62
68
|
reset(): void;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type ComponentPropsWithoutRef, type ElementType, type HTMLProps } from 'react';
|
|
2
|
-
import { type Form } from './form';
|
|
3
1
|
import { type PathAsString } from '../../index';
|
|
4
2
|
import { type Value } from '../../lib/path';
|
|
5
|
-
|
|
3
|
+
import { type ComponentPropsWithoutRef, type ComponentType, type HTMLProps } from 'react';
|
|
4
|
+
import { type Form } from './form';
|
|
5
|
+
interface FormFieldComponentProps<T> {
|
|
6
6
|
id: string;
|
|
7
7
|
value: T;
|
|
8
8
|
onChange: (event: {
|
|
@@ -12,7 +12,8 @@ export type FormFieldComponent<T> = ElementType<{
|
|
|
12
12
|
} | T | undefined, ...args: any[]) => void;
|
|
13
13
|
onFocus: (...args: any[]) => void;
|
|
14
14
|
onBlur: (...args: any[]) => void;
|
|
15
|
-
}
|
|
15
|
+
}
|
|
16
|
+
type NativeInputType = 'input' | 'select' | 'textarea';
|
|
16
17
|
type FieldValue<T extends FormFieldComponent<any>> = ComponentPropsWithoutRef<T & 'input'> extends {
|
|
17
18
|
value: infer U;
|
|
18
19
|
} ? U : ComponentPropsWithoutRef<T & 'input'> extends {
|
|
@@ -25,6 +26,7 @@ type FieldChangeValue<T extends FormFieldComponent<any>> = ComponentPropsWithout
|
|
|
25
26
|
value: infer V;
|
|
26
27
|
};
|
|
27
28
|
} ? V : U : never;
|
|
29
|
+
export type FormFieldComponent<T> = (string | number extends T ? NativeInputType : never) | ComponentType<FormFieldComponentProps<T>>;
|
|
28
30
|
export type FormFieldProps<TDraft, TPath extends PathAsString<TDraft>, TComponent extends FormFieldComponent<any>> = {
|
|
29
31
|
name: TPath;
|
|
30
32
|
commitOnBlur?: boolean;
|