cross-state 0.18.1 → 0.18.2
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
|
@@ -176,23 +176,27 @@ function FormContainer({
|
|
|
176
176
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
177
177
|
"form",
|
|
178
178
|
{
|
|
179
|
+
noValidate: true,
|
|
179
180
|
...formProps,
|
|
180
181
|
className: [formProps.className, hasTriggeredValidations ? "validated" : void 0].filter(Boolean).join(" "),
|
|
181
|
-
noValidate: true,
|
|
182
182
|
onSubmit: (event) => {
|
|
183
|
+
var _a;
|
|
183
184
|
event.preventDefault();
|
|
184
|
-
_form.validate();
|
|
185
|
+
const isValid = _form.validate();
|
|
185
186
|
let button;
|
|
186
187
|
if (event.nativeEvent instanceof SubmitEvent && (button = event.nativeEvent.submitter) && (button instanceof HTMLButtonElement || button instanceof HTMLInputElement) && button.setCustomValidity) {
|
|
187
188
|
const errors = _form.errors.map(
|
|
188
189
|
({ field, error }) => {
|
|
189
|
-
var
|
|
190
|
-
return ((_b = (
|
|
190
|
+
var _a2, _b;
|
|
191
|
+
return ((_b = (_a2 = _form.options).localizeError) == null ? void 0 : _b.call(_a2, error, field)) ?? error;
|
|
191
192
|
}
|
|
192
193
|
);
|
|
193
194
|
button.setCustomValidity(errors.join("\n"));
|
|
194
195
|
}
|
|
195
196
|
event.currentTarget.reportValidity();
|
|
197
|
+
if (isValid) {
|
|
198
|
+
(_a = formProps.onSubmit) == null ? void 0 : _a.call(formProps, event);
|
|
199
|
+
}
|
|
196
200
|
}
|
|
197
201
|
}
|
|
198
202
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../../src/lib/wildcardMatch.ts","../../../src/react/form/formArray.tsx","../../../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(\n object: any,\n path: [KeyType, ...KeyType[]] | string,\n): Record<KeyType, any> {\n const matches: Record<KeyType, any> = {};\n const [first, second, ...rest] = castArrayPath(path);\n\n if (first === undefined) {\n throw new Error('Path is empty');\n }\n\n if (!(object instanceof Object)) {\n throw new Error('Object is not an object');\n }\n\n for (const [key, value] of Object.entries(object)) {\n if (first !== '*' && first !== key) {\n continue;\n }\n\n if (second === undefined) {\n matches[key] = value;\n continue;\n }\n\n for (const [subKey, subValue] of Object.entries(getWildCardMatches(value, [second, ...rest]))) {\n matches[`${key}.${subKey}`] = subValue;\n }\n }\n\n return matches;\n}\n","import { type PathAsString, type Value } from '@lib/path';\nimport { Fragment, useCallback, type ReactNode } from 'react';\nimport { type ArrayFieldMethods, type Field, type Form } from './form';\n\nexport type ArrayPath<T> = keyof {\n [P in PathAsString<T> as Value<T, P> extends readonly any[] | undefined ? P : never]: never;\n} &\n PathAsString<T> &\n string;\n\nexport interface FormArrayProps<TDraft, TPath extends ArrayPath<TDraft>> {\n name: TPath;\n renderElement?: (props: {\n name: `${TPath}.${number}`;\n index: number;\n remove: () => void;\n }) => ReactNode;\n children?: (props: {\n names: `${TPath}.${number}`[];\n append: (...elements: Value<TDraft, `${TPath}.${number}`>[]) => void;\n remove: (index: number) => void;\n setValue: (\n value: Value<TDraft, TPath> | ((value: Value<TDraft, TPath>) => Value<TDraft, TPath>),\n ) => void;\n }) => ReactNode;\n}\n\nexport function FormArray<TDraft, TPath extends ArrayPath<TDraft>>(\n this: Form<TDraft, any>,\n { name, renderElement, children }: FormArrayProps<TDraft, TPath>,\n) {\n const form = this.useForm();\n\n const names = this.useFormState((form) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n return field.names;\n });\n\n const append = useCallback(\n (...newEntries: Value<TDraft, `${TPath}.${number}`>[]) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n field.append(...newEntries);\n },\n [form],\n );\n\n const remove = useCallback(\n (index: number) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n field.remove(index);\n },\n [form],\n );\n\n const setValue = useCallback(\n (value: Value<TDraft, TPath> | ((value: Value<TDraft, TPath>) => Value<TDraft, TPath>)) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n field.setValue(value);\n },\n [form],\n );\n\n return (\n <>\n {renderElement &&\n names.map((name, index) => (\n <Fragment key={index}>\n {renderElement({\n name,\n index,\n remove: () => remove(index),\n })}\n </Fragment>\n ))}\n\n {children?.({\n names,\n append,\n remove,\n setValue,\n })}\n </>\n );\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 Component,\n type ComponentPropsWithoutRef,\n type ReactNode,\n} from 'react';\nimport { type Form } from './form';\n\ninterface FormFieldComponentProps<TValue, TPath> {\n id: string;\n name: TPath;\n value: TValue;\n onChange: (event: { target: { value: TValue } } | TValue | undefined, ...args: any[]) => void;\n onFocus: (...args: any[]) => void;\n onBlur: (...args: any[]) => void;\n}\n\ntype NativeInputType = 'input' | 'select' | 'textarea';\n\ntype PartialComponentType<P> =\n | (new (props: P, context?: any) => Component<P, any>)\n | ((props: P, context?: any) => ReactNode);\n\nexport type FormFieldComponent<TValue, TPath> =\n | (string | number extends TValue ? NativeInputType : never)\n | PartialComponentType<FormFieldComponentProps<TValue, TPath>>;\n\ntype FieldValue<T extends FormFieldComponent<any, any>> = ComponentPropsWithoutRef<T>['value'];\n\ntype FieldChangeValue<T extends FormFieldComponent<any, any>> =\n ComponentPropsWithoutRef<T> 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, TPath>,\n> = {\n name: TPath;\n component: TComponent;\n commitOnBlur?: boolean;\n commitDebounce?: number;\n inputFilter?: (value: FieldChangeValue<TComponent>) => boolean;\n} & Omit<\n ComponentPropsWithoutRef<TComponent>,\n | 'name'\n | 'component'\n | 'commitOnBlur'\n | 'commitDebounce'\n | 'inputFilter'\n | keyof FormFieldComponentProps<any, any>\n> &\n Partial<Pick<ComponentPropsWithoutRef<TComponent>, keyof FormFieldComponentProps<any, any>>> &\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, any>,\n>(\n this: Form<TDraft, any>,\n {\n // id,\n name,\n component,\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\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 id: _id,\n name,\n value: localValue ?? serialize(value as Value<TDraft, TPath>),\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, connectUrl, createStore, type Store, 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 createContext,\n useContext,\n useEffect,\n useMemo,\n type ComponentPropsWithoutRef,\n type HTMLProps,\n type ReactNode,\n} from 'react';\nimport { ScopeProvider, useScope } from '../scope';\nimport { useStore, type UseStoreOptions } from '../useStore';\nimport { FormArray, type ArrayPath, type FormArrayProps } from './formArray';\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 type 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} & (Value<TDraft, TPath> extends Array<any> ? ArrayFieldMethods<TDraft, TPath> : {});\n\nexport type ArrayFieldMethods<TPath, TValue> = {\n names: TPath[];\n append: (...elements: TValue[]) => void;\n remove: (index: number) => void;\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 const hasTriggeredValidations = form.useFormState((state) => state.hasTriggeredValidations);\n\n return (\n <form\n {...formProps}\n className={[formProps.className, hasTriggeredValidations ? 'validated' : undefined]\n .filter(Boolean)\n .join(' ')}\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 get names() {\n const { value } = this;\n return (Array.isArray(value) ? value.map((_, index) => `${path}.${index}`) : []) as any;\n },\n\n append(...elements: any[]) {\n this.setValue(\n (value) => (Array.isArray(value) ? [...value, ...elements] : elements) as any,\n );\n },\n\n remove(index) {\n this.setValue(\n (value) =>\n (Array.isArray(value)\n ? [...value.slice(0, index), ...value.slice(index + 1)]\n : value) as any,\n );\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 let matched = false;\n\n for (const [field, value] of Object.entries(getWildCardMatches(draft, path))) {\n matched = true;\n if (!validate(value, { draft, original, field })) {\n errors.add({ field, error: validationName });\n }\n }\n\n if (!matched && !path.includes('*')) {\n if (!validate(undefined, { draft, original, field: path })) {\n errors.add({ field: path, 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 get hasTriggeredValidations() {\n return state.get().hasTriggeredValidations;\n },\n\n reset() {\n state.set('draft', undefined);\n state.set('hasTriggeredValidations', false);\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<TPath extends PathAsString<TDraft>>(\n props: Omit<FormFieldProps<TDraft, TPath, 'input'>, 'component'>,\n ): JSX.Element;\n Field<\n TPath extends PathAsString<TDraft>,\n TComponent extends FormFieldComponent<any, TPath> = (\n props: ComponentPropsWithoutRef<'input'> & { name: TPath },\n ) => JSX.Element,\n >(props: FormFieldProps<TDraft, TPath, TComponent>): JSX.Element;\n Field(props: any): JSX.Element {\n return Reflect.apply(FormField, this, [{ component: 'input', ...props }]);\n }\n\n Array<TPath extends ArrayPath<TDraft>>(props: FormArrayProps<TDraft, TPath>) {\n return Reflect.apply(FormArray, 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","form","useCallback","jsxs","Fragment","name","jsx","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/formArray.tsx","../../../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(\n object: any,\n path: [KeyType, ...KeyType[]] | string,\n): Record<KeyType, any> {\n const matches: Record<KeyType, any> = {};\n const [first, second, ...rest] = castArrayPath(path);\n\n if (first === undefined) {\n throw new Error('Path is empty');\n }\n\n if (!(object instanceof Object)) {\n throw new Error('Object is not an object');\n }\n\n for (const [key, value] of Object.entries(object)) {\n if (first !== '*' && first !== key) {\n continue;\n }\n\n if (second === undefined) {\n matches[key] = value;\n continue;\n }\n\n for (const [subKey, subValue] of Object.entries(getWildCardMatches(value, [second, ...rest]))) {\n matches[`${key}.${subKey}`] = subValue;\n }\n }\n\n return matches;\n}\n","import { type PathAsString, type Value } from '@lib/path';\nimport { Fragment, useCallback, type ReactNode } from 'react';\nimport { type ArrayFieldMethods, type Field, type Form } from './form';\n\nexport type ArrayPath<T> = keyof {\n [P in PathAsString<T> as Value<T, P> extends readonly any[] | undefined ? P : never]: never;\n} &\n PathAsString<T> &\n string;\n\nexport interface FormArrayProps<TDraft, TPath extends ArrayPath<TDraft>> {\n name: TPath;\n renderElement?: (props: {\n name: `${TPath}.${number}`;\n index: number;\n remove: () => void;\n }) => ReactNode;\n children?: (props: {\n names: `${TPath}.${number}`[];\n append: (...elements: Value<TDraft, `${TPath}.${number}`>[]) => void;\n remove: (index: number) => void;\n setValue: (\n value: Value<TDraft, TPath> | ((value: Value<TDraft, TPath>) => Value<TDraft, TPath>),\n ) => void;\n }) => ReactNode;\n}\n\nexport function FormArray<TDraft, TPath extends ArrayPath<TDraft>>(\n this: Form<TDraft, any>,\n { name, renderElement, children }: FormArrayProps<TDraft, TPath>,\n) {\n const form = this.useForm();\n\n const names = this.useFormState((form) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n return field.names;\n });\n\n const append = useCallback(\n (...newEntries: Value<TDraft, `${TPath}.${number}`>[]) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n field.append(...newEntries);\n },\n [form],\n );\n\n const remove = useCallback(\n (index: number) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n field.remove(index);\n },\n [form],\n );\n\n const setValue = useCallback(\n (value: Value<TDraft, TPath> | ((value: Value<TDraft, TPath>) => Value<TDraft, TPath>)) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n field.setValue(value);\n },\n [form],\n );\n\n return (\n <>\n {renderElement &&\n names.map((name, index) => (\n <Fragment key={index}>\n {renderElement({\n name,\n index,\n remove: () => remove(index),\n })}\n </Fragment>\n ))}\n\n {children?.({\n names,\n append,\n remove,\n setValue,\n })}\n </>\n );\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 Component,\n type ComponentPropsWithoutRef,\n type ReactNode,\n} from 'react';\nimport { type Form } from './form';\n\nexport interface FormFieldComponentProps<TValue, TPath> {\n id: string;\n name: TPath;\n value: TValue;\n onChange: (event: { target: { value: TValue } } | TValue | undefined, ...args: any[]) => void;\n onFocus: (...args: any[]) => void;\n onBlur: (...args: any[]) => void;\n}\n\ntype NativeInputType = 'input' | 'select' | 'textarea';\n\ntype PartialComponentType<P> =\n | (new (props: P, context?: any) => Component<P, any>)\n | ((props: P, context?: any) => ReactNode);\n\nexport type FormFieldComponent = NativeInputType | PartialComponentType<any>;\n\ntype FieldValue<T extends FormFieldComponent> = ComponentPropsWithoutRef<T>['value'];\n\ntype FieldChangeValue<T extends FormFieldComponent> = ComponentPropsWithoutRef<T> 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,\n> = {\n name: TPath;\n component: TComponent;\n commitOnBlur?: boolean;\n commitDebounce?: number;\n inputFilter?: (value: FieldChangeValue<TComponent>) => boolean;\n} & Omit<ComponentPropsWithoutRef<TComponent>, keyof FormFieldComponentProps<any, any>> &\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,\n>(\n this: Form<TDraft, any>,\n {\n // id,\n name,\n component,\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\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 id: _id,\n name,\n value: localValue ?? serialize(value as Value<TDraft, TPath>),\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, connectUrl, createStore, type Store, 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 createContext,\n useContext,\n useEffect,\n useMemo,\n type ComponentPropsWithoutRef,\n type HTMLProps,\n type ReactNode,\n} from 'react';\nimport { ScopeProvider, useScope } from '../scope';\nimport { useStore, type UseStoreOptions } from '../useStore';\nimport { FormArray, type ArrayPath, type FormArrayProps } from './formArray';\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 type 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} & (Value<TDraft, TPath> extends Array<any> ? ArrayFieldMethods<TDraft, TPath> : {});\n\nexport type ArrayFieldMethods<TPath, TValue> = {\n names: TPath[];\n append: (...elements: TValue[]) => void;\n remove: (index: number) => void;\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 const hasTriggeredValidations = form.useFormState((state) => state.hasTriggeredValidations);\n\n return (\n <form\n noValidate\n {...formProps}\n className={[formProps.className, hasTriggeredValidations ? 'validated' : undefined]\n .filter(Boolean)\n .join(' ')}\n onSubmit={(event) => {\n event.preventDefault();\n\n const isValid = _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\n event.currentTarget.reportValidity();\n\n if (isValid) {\n formProps.onSubmit?.(event);\n }\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 get names() {\n const { value } = this;\n return (Array.isArray(value) ? value.map((_, index) => `${path}.${index}`) : []) as any;\n },\n\n append(...elements: any[]) {\n this.setValue(\n (value) => (Array.isArray(value) ? [...value, ...elements] : elements) as any,\n );\n },\n\n remove(index) {\n this.setValue(\n (value) =>\n (Array.isArray(value)\n ? [...value.slice(0, index), ...value.slice(index + 1)]\n : value) as any,\n );\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 let matched = false;\n\n for (const [field, value] of Object.entries(getWildCardMatches(draft, path))) {\n matched = true;\n if (!validate(value, { draft, original, field })) {\n errors.add({ field, error: validationName });\n }\n }\n\n if (!matched && !path.includes('*')) {\n if (!validate(undefined, { draft, original, field: path })) {\n errors.add({ field: path, 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 get hasTriggeredValidations() {\n return state.get().hasTriggeredValidations;\n },\n\n reset() {\n state.set('draft', undefined);\n state.set('hasTriggeredValidations', false);\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 = (\n props: ComponentPropsWithoutRef<'input'> & { name: TPath },\n ) => JSX.Element,\n >(props: FormFieldProps<TDraft, TPath, TComponent>): JSX.Element;\n\n Field<TPath extends PathAsString<TDraft>>(\n props: Omit<FormFieldProps<TDraft, TPath, 'input'>, 'component'>,\n ): JSX.Element;\n\n Field(props: any): JSX.Element {\n return Reflect.apply(FormField, this, [{ component: 'input', ...props }]);\n }\n\n Array<TPath extends ArrayPath<TDraft>>(props: FormArrayProps<TDraft, TPath>) {\n return Reflect.apply(FormArray, 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","form","useCallback","jsxs","Fragment","name","jsx","useScope","useMemo","useState","useEffect","value","createElement","_a","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
|
@@ -175,23 +175,27 @@ function FormContainer({
|
|
|
175
175
|
return /* @__PURE__ */ jsx(
|
|
176
176
|
"form",
|
|
177
177
|
{
|
|
178
|
+
noValidate: true,
|
|
178
179
|
...formProps,
|
|
179
180
|
className: [formProps.className, hasTriggeredValidations ? "validated" : void 0].filter(Boolean).join(" "),
|
|
180
|
-
noValidate: true,
|
|
181
181
|
onSubmit: (event) => {
|
|
182
|
+
var _a;
|
|
182
183
|
event.preventDefault();
|
|
183
|
-
_form.validate();
|
|
184
|
+
const isValid = _form.validate();
|
|
184
185
|
let button;
|
|
185
186
|
if (event.nativeEvent instanceof SubmitEvent && (button = event.nativeEvent.submitter) && (button instanceof HTMLButtonElement || button instanceof HTMLInputElement) && button.setCustomValidity) {
|
|
186
187
|
const errors = _form.errors.map(
|
|
187
188
|
({ field, error }) => {
|
|
188
|
-
var
|
|
189
|
-
return ((_b = (
|
|
189
|
+
var _a2, _b;
|
|
190
|
+
return ((_b = (_a2 = _form.options).localizeError) == null ? void 0 : _b.call(_a2, error, field)) ?? error;
|
|
190
191
|
}
|
|
191
192
|
);
|
|
192
193
|
button.setCustomValidity(errors.join("\n"));
|
|
193
194
|
}
|
|
194
195
|
event.currentTarget.reportValidity();
|
|
196
|
+
if (isValid) {
|
|
197
|
+
(_a = formProps.onSubmit) == null ? void 0 : _a.call(formProps, event);
|
|
198
|
+
}
|
|
195
199
|
}
|
|
196
200
|
}
|
|
197
201
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../../../src/lib/wildcardMatch.ts","../../../src/react/form/formArray.tsx","../../../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(\n object: any,\n path: [KeyType, ...KeyType[]] | string,\n): Record<KeyType, any> {\n const matches: Record<KeyType, any> = {};\n const [first, second, ...rest] = castArrayPath(path);\n\n if (first === undefined) {\n throw new Error('Path is empty');\n }\n\n if (!(object instanceof Object)) {\n throw new Error('Object is not an object');\n }\n\n for (const [key, value] of Object.entries(object)) {\n if (first !== '*' && first !== key) {\n continue;\n }\n\n if (second === undefined) {\n matches[key] = value;\n continue;\n }\n\n for (const [subKey, subValue] of Object.entries(getWildCardMatches(value, [second, ...rest]))) {\n matches[`${key}.${subKey}`] = subValue;\n }\n }\n\n return matches;\n}\n","import { type PathAsString, type Value } from '@lib/path';\nimport { Fragment, useCallback, type ReactNode } from 'react';\nimport { type ArrayFieldMethods, type Field, type Form } from './form';\n\nexport type ArrayPath<T> = keyof {\n [P in PathAsString<T> as Value<T, P> extends readonly any[] | undefined ? P : never]: never;\n} &\n PathAsString<T> &\n string;\n\nexport interface FormArrayProps<TDraft, TPath extends ArrayPath<TDraft>> {\n name: TPath;\n renderElement?: (props: {\n name: `${TPath}.${number}`;\n index: number;\n remove: () => void;\n }) => ReactNode;\n children?: (props: {\n names: `${TPath}.${number}`[];\n append: (...elements: Value<TDraft, `${TPath}.${number}`>[]) => void;\n remove: (index: number) => void;\n setValue: (\n value: Value<TDraft, TPath> | ((value: Value<TDraft, TPath>) => Value<TDraft, TPath>),\n ) => void;\n }) => ReactNode;\n}\n\nexport function FormArray<TDraft, TPath extends ArrayPath<TDraft>>(\n this: Form<TDraft, any>,\n { name, renderElement, children }: FormArrayProps<TDraft, TPath>,\n) {\n const form = this.useForm();\n\n const names = this.useFormState((form) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n return field.names;\n });\n\n const append = useCallback(\n (...newEntries: Value<TDraft, `${TPath}.${number}`>[]) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n field.append(...newEntries);\n },\n [form],\n );\n\n const remove = useCallback(\n (index: number) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n field.remove(index);\n },\n [form],\n );\n\n const setValue = useCallback(\n (value: Value<TDraft, TPath> | ((value: Value<TDraft, TPath>) => Value<TDraft, TPath>)) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n field.setValue(value);\n },\n [form],\n );\n\n return (\n <>\n {renderElement &&\n names.map((name, index) => (\n <Fragment key={index}>\n {renderElement({\n name,\n index,\n remove: () => remove(index),\n })}\n </Fragment>\n ))}\n\n {children?.({\n names,\n append,\n remove,\n setValue,\n })}\n </>\n );\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 Component,\n type ComponentPropsWithoutRef,\n type ReactNode,\n} from 'react';\nimport { type Form } from './form';\n\ninterface FormFieldComponentProps<TValue, TPath> {\n id: string;\n name: TPath;\n value: TValue;\n onChange: (event: { target: { value: TValue } } | TValue | undefined, ...args: any[]) => void;\n onFocus: (...args: any[]) => void;\n onBlur: (...args: any[]) => void;\n}\n\ntype NativeInputType = 'input' | 'select' | 'textarea';\n\ntype PartialComponentType<P> =\n | (new (props: P, context?: any) => Component<P, any>)\n | ((props: P, context?: any) => ReactNode);\n\nexport type FormFieldComponent<TValue, TPath> =\n | (string | number extends TValue ? NativeInputType : never)\n | PartialComponentType<FormFieldComponentProps<TValue, TPath>>;\n\ntype FieldValue<T extends FormFieldComponent<any, any>> = ComponentPropsWithoutRef<T>['value'];\n\ntype FieldChangeValue<T extends FormFieldComponent<any, any>> =\n ComponentPropsWithoutRef<T> 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, TPath>,\n> = {\n name: TPath;\n component: TComponent;\n commitOnBlur?: boolean;\n commitDebounce?: number;\n inputFilter?: (value: FieldChangeValue<TComponent>) => boolean;\n} & Omit<\n ComponentPropsWithoutRef<TComponent>,\n | 'name'\n | 'component'\n | 'commitOnBlur'\n | 'commitDebounce'\n | 'inputFilter'\n | keyof FormFieldComponentProps<any, any>\n> &\n Partial<Pick<ComponentPropsWithoutRef<TComponent>, keyof FormFieldComponentProps<any, any>>> &\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, any>,\n>(\n this: Form<TDraft, any>,\n {\n // id,\n name,\n component,\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\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 id: _id,\n name,\n value: localValue ?? serialize(value as Value<TDraft, TPath>),\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, connectUrl, createStore, type Store, 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 createContext,\n useContext,\n useEffect,\n useMemo,\n type ComponentPropsWithoutRef,\n type HTMLProps,\n type ReactNode,\n} from 'react';\nimport { ScopeProvider, useScope } from '../scope';\nimport { useStore, type UseStoreOptions } from '../useStore';\nimport { FormArray, type ArrayPath, type FormArrayProps } from './formArray';\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 type 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} & (Value<TDraft, TPath> extends Array<any> ? ArrayFieldMethods<TDraft, TPath> : {});\n\nexport type ArrayFieldMethods<TPath, TValue> = {\n names: TPath[];\n append: (...elements: TValue[]) => void;\n remove: (index: number) => void;\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 const hasTriggeredValidations = form.useFormState((state) => state.hasTriggeredValidations);\n\n return (\n <form\n {...formProps}\n className={[formProps.className, hasTriggeredValidations ? 'validated' : undefined]\n .filter(Boolean)\n .join(' ')}\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 get names() {\n const { value } = this;\n return (Array.isArray(value) ? value.map((_, index) => `${path}.${index}`) : []) as any;\n },\n\n append(...elements: any[]) {\n this.setValue(\n (value) => (Array.isArray(value) ? [...value, ...elements] : elements) as any,\n );\n },\n\n remove(index) {\n this.setValue(\n (value) =>\n (Array.isArray(value)\n ? [...value.slice(0, index), ...value.slice(index + 1)]\n : value) as any,\n );\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 let matched = false;\n\n for (const [field, value] of Object.entries(getWildCardMatches(draft, path))) {\n matched = true;\n if (!validate(value, { draft, original, field })) {\n errors.add({ field, error: validationName });\n }\n }\n\n if (!matched && !path.includes('*')) {\n if (!validate(undefined, { draft, original, field: path })) {\n errors.add({ field: path, 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 get hasTriggeredValidations() {\n return state.get().hasTriggeredValidations;\n },\n\n reset() {\n state.set('draft', undefined);\n state.set('hasTriggeredValidations', false);\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<TPath extends PathAsString<TDraft>>(\n props: Omit<FormFieldProps<TDraft, TPath, 'input'>, 'component'>,\n ): JSX.Element;\n Field<\n TPath extends PathAsString<TDraft>,\n TComponent extends FormFieldComponent<any, TPath> = (\n props: ComponentPropsWithoutRef<'input'> & { name: TPath },\n ) => JSX.Element,\n >(props: FormFieldProps<TDraft, TPath, TComponent>): JSX.Element;\n Field(props: any): JSX.Element {\n return Reflect.apply(FormField, this, [{ component: 'input', ...props }]);\n }\n\n Array<TPath extends ArrayPath<TDraft>>(props: FormArrayProps<TDraft, TPath>) {\n return Reflect.apply(FormArray, 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","form","name","Fragment","value","state","onChange","update"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../../../src/lib/wildcardMatch.ts","../../../src/react/form/formArray.tsx","../../../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(\n object: any,\n path: [KeyType, ...KeyType[]] | string,\n): Record<KeyType, any> {\n const matches: Record<KeyType, any> = {};\n const [first, second, ...rest] = castArrayPath(path);\n\n if (first === undefined) {\n throw new Error('Path is empty');\n }\n\n if (!(object instanceof Object)) {\n throw new Error('Object is not an object');\n }\n\n for (const [key, value] of Object.entries(object)) {\n if (first !== '*' && first !== key) {\n continue;\n }\n\n if (second === undefined) {\n matches[key] = value;\n continue;\n }\n\n for (const [subKey, subValue] of Object.entries(getWildCardMatches(value, [second, ...rest]))) {\n matches[`${key}.${subKey}`] = subValue;\n }\n }\n\n return matches;\n}\n","import { type PathAsString, type Value } from '@lib/path';\nimport { Fragment, useCallback, type ReactNode } from 'react';\nimport { type ArrayFieldMethods, type Field, type Form } from './form';\n\nexport type ArrayPath<T> = keyof {\n [P in PathAsString<T> as Value<T, P> extends readonly any[] | undefined ? P : never]: never;\n} &\n PathAsString<T> &\n string;\n\nexport interface FormArrayProps<TDraft, TPath extends ArrayPath<TDraft>> {\n name: TPath;\n renderElement?: (props: {\n name: `${TPath}.${number}`;\n index: number;\n remove: () => void;\n }) => ReactNode;\n children?: (props: {\n names: `${TPath}.${number}`[];\n append: (...elements: Value<TDraft, `${TPath}.${number}`>[]) => void;\n remove: (index: number) => void;\n setValue: (\n value: Value<TDraft, TPath> | ((value: Value<TDraft, TPath>) => Value<TDraft, TPath>),\n ) => void;\n }) => ReactNode;\n}\n\nexport function FormArray<TDraft, TPath extends ArrayPath<TDraft>>(\n this: Form<TDraft, any>,\n { name, renderElement, children }: FormArrayProps<TDraft, TPath>,\n) {\n const form = this.useForm();\n\n const names = this.useFormState((form) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n return field.names;\n });\n\n const append = useCallback(\n (...newEntries: Value<TDraft, `${TPath}.${number}`>[]) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n field.append(...newEntries);\n },\n [form],\n );\n\n const remove = useCallback(\n (index: number) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n field.remove(index);\n },\n [form],\n );\n\n const setValue = useCallback(\n (value: Value<TDraft, TPath> | ((value: Value<TDraft, TPath>) => Value<TDraft, TPath>)) => {\n const field = form.getField(name) as Field<any, any, any> & ArrayFieldMethods<any, any>;\n field.setValue(value);\n },\n [form],\n );\n\n return (\n <>\n {renderElement &&\n names.map((name, index) => (\n <Fragment key={index}>\n {renderElement({\n name,\n index,\n remove: () => remove(index),\n })}\n </Fragment>\n ))}\n\n {children?.({\n names,\n append,\n remove,\n setValue,\n })}\n </>\n );\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 Component,\n type ComponentPropsWithoutRef,\n type ReactNode,\n} from 'react';\nimport { type Form } from './form';\n\nexport interface FormFieldComponentProps<TValue, TPath> {\n id: string;\n name: TPath;\n value: TValue;\n onChange: (event: { target: { value: TValue } } | TValue | undefined, ...args: any[]) => void;\n onFocus: (...args: any[]) => void;\n onBlur: (...args: any[]) => void;\n}\n\ntype NativeInputType = 'input' | 'select' | 'textarea';\n\ntype PartialComponentType<P> =\n | (new (props: P, context?: any) => Component<P, any>)\n | ((props: P, context?: any) => ReactNode);\n\nexport type FormFieldComponent = NativeInputType | PartialComponentType<any>;\n\ntype FieldValue<T extends FormFieldComponent> = ComponentPropsWithoutRef<T>['value'];\n\ntype FieldChangeValue<T extends FormFieldComponent> = ComponentPropsWithoutRef<T> 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,\n> = {\n name: TPath;\n component: TComponent;\n commitOnBlur?: boolean;\n commitDebounce?: number;\n inputFilter?: (value: FieldChangeValue<TComponent>) => boolean;\n} & Omit<ComponentPropsWithoutRef<TComponent>, keyof FormFieldComponentProps<any, any>> &\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,\n>(\n this: Form<TDraft, any>,\n {\n // id,\n name,\n component,\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\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 id: _id,\n name,\n value: localValue ?? serialize(value as Value<TDraft, TPath>),\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, connectUrl, createStore, type Store, 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 createContext,\n useContext,\n useEffect,\n useMemo,\n type ComponentPropsWithoutRef,\n type HTMLProps,\n type ReactNode,\n} from 'react';\nimport { ScopeProvider, useScope } from '../scope';\nimport { useStore, type UseStoreOptions } from '../useStore';\nimport { FormArray, type ArrayPath, type FormArrayProps } from './formArray';\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 type 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} & (Value<TDraft, TPath> extends Array<any> ? ArrayFieldMethods<TDraft, TPath> : {});\n\nexport type ArrayFieldMethods<TPath, TValue> = {\n names: TPath[];\n append: (...elements: TValue[]) => void;\n remove: (index: number) => void;\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 const hasTriggeredValidations = form.useFormState((state) => state.hasTriggeredValidations);\n\n return (\n <form\n noValidate\n {...formProps}\n className={[formProps.className, hasTriggeredValidations ? 'validated' : undefined]\n .filter(Boolean)\n .join(' ')}\n onSubmit={(event) => {\n event.preventDefault();\n\n const isValid = _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\n event.currentTarget.reportValidity();\n\n if (isValid) {\n formProps.onSubmit?.(event);\n }\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 get names() {\n const { value } = this;\n return (Array.isArray(value) ? value.map((_, index) => `${path}.${index}`) : []) as any;\n },\n\n append(...elements: any[]) {\n this.setValue(\n (value) => (Array.isArray(value) ? [...value, ...elements] : elements) as any,\n );\n },\n\n remove(index) {\n this.setValue(\n (value) =>\n (Array.isArray(value)\n ? [...value.slice(0, index), ...value.slice(index + 1)]\n : value) as any,\n );\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 let matched = false;\n\n for (const [field, value] of Object.entries(getWildCardMatches(draft, path))) {\n matched = true;\n if (!validate(value, { draft, original, field })) {\n errors.add({ field, error: validationName });\n }\n }\n\n if (!matched && !path.includes('*')) {\n if (!validate(undefined, { draft, original, field: path })) {\n errors.add({ field: path, 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 get hasTriggeredValidations() {\n return state.get().hasTriggeredValidations;\n },\n\n reset() {\n state.set('draft', undefined);\n state.set('hasTriggeredValidations', false);\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 = (\n props: ComponentPropsWithoutRef<'input'> & { name: TPath },\n ) => JSX.Element,\n >(props: FormFieldProps<TDraft, TPath, TComponent>): JSX.Element;\n\n Field<TPath extends PathAsString<TDraft>>(\n props: Omit<FormFieldProps<TDraft, TPath, 'input'>, 'component'>,\n ): JSX.Element;\n\n Field(props: any): JSX.Element {\n return Reflect.apply(FormField, this, [{ component: 'input', ...props }]);\n }\n\n Array<TPath extends ArrayPath<TDraft>>(props: FormArrayProps<TDraft, TPath>) {\n return Reflect.apply(FormArray, 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","form","name","Fragment","value","_a","state","onChange","update"],"mappings":"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"}
|
|
@@ -86,10 +86,10 @@ export declare class Form<TDraft, TOriginal extends TDraft = TDraft> {
|
|
|
86
86
|
selector: (form: ReturnType<typeof getFormInstance<TDraft, TOriginal>>) => S;
|
|
87
87
|
children: (selectedState: S) => ReactNode;
|
|
88
88
|
}): import("react/jsx-runtime").JSX.Element;
|
|
89
|
-
Field<TPath extends PathAsString<TDraft
|
|
90
|
-
Field<TPath extends PathAsString<TDraft>, TComponent extends FormFieldComponent<any, TPath> = (props: ComponentPropsWithoutRef<'input'> & {
|
|
89
|
+
Field<TPath extends PathAsString<TDraft>, TComponent extends FormFieldComponent = (props: ComponentPropsWithoutRef<'input'> & {
|
|
91
90
|
name: TPath;
|
|
92
91
|
}) => JSX.Element>(props: FormFieldProps<TDraft, TPath, TComponent>): JSX.Element;
|
|
92
|
+
Field<TPath extends PathAsString<TDraft>>(props: Omit<FormFieldProps<TDraft, TPath, 'input'>, 'component'>): JSX.Element;
|
|
93
93
|
Array<TPath extends ArrayPath<TDraft>>(props: FormArrayProps<TDraft, TPath>): any;
|
|
94
94
|
Error<TPath extends PathAsString<TDraft>>({ name }: FormErrorProps<TDraft, TPath>): any;
|
|
95
95
|
}
|
|
@@ -2,7 +2,7 @@ import { type PathAsString } from '../../index';
|
|
|
2
2
|
import { type Value } from '../../lib/path';
|
|
3
3
|
import { type Component, type ComponentPropsWithoutRef, type ReactNode } from 'react';
|
|
4
4
|
import { type Form } from './form';
|
|
5
|
-
interface FormFieldComponentProps<TValue, TPath> {
|
|
5
|
+
export interface FormFieldComponentProps<TValue, TPath> {
|
|
6
6
|
id: string;
|
|
7
7
|
name: TPath;
|
|
8
8
|
value: TValue;
|
|
@@ -16,22 +16,22 @@ interface FormFieldComponentProps<TValue, TPath> {
|
|
|
16
16
|
}
|
|
17
17
|
type NativeInputType = 'input' | 'select' | 'textarea';
|
|
18
18
|
type PartialComponentType<P> = (new (props: P, context?: any) => Component<P, any>) | ((props: P, context?: any) => ReactNode);
|
|
19
|
-
export type FormFieldComponent
|
|
20
|
-
type FieldValue<T extends FormFieldComponent
|
|
21
|
-
type FieldChangeValue<T extends FormFieldComponent
|
|
19
|
+
export type FormFieldComponent = NativeInputType | PartialComponentType<any>;
|
|
20
|
+
type FieldValue<T extends FormFieldComponent> = ComponentPropsWithoutRef<T>['value'];
|
|
21
|
+
type FieldChangeValue<T extends FormFieldComponent> = ComponentPropsWithoutRef<T> extends {
|
|
22
22
|
onChange?: (update: infer U) => void;
|
|
23
23
|
} ? U extends {
|
|
24
24
|
target: {
|
|
25
25
|
value: infer V;
|
|
26
26
|
};
|
|
27
27
|
} ? V : U : never;
|
|
28
|
-
export type FormFieldProps<TDraft, TPath extends PathAsString<TDraft>, TComponent extends FormFieldComponent
|
|
28
|
+
export type FormFieldProps<TDraft, TPath extends PathAsString<TDraft>, TComponent extends FormFieldComponent> = {
|
|
29
29
|
name: TPath;
|
|
30
30
|
component: TComponent;
|
|
31
31
|
commitOnBlur?: boolean;
|
|
32
32
|
commitDebounce?: number;
|
|
33
33
|
inputFilter?: (value: FieldChangeValue<TComponent>) => boolean;
|
|
34
|
-
} & Omit<ComponentPropsWithoutRef<TComponent>,
|
|
34
|
+
} & Omit<ComponentPropsWithoutRef<TComponent>, keyof FormFieldComponentProps<any, any>> & (Value<TDraft, TPath> extends FieldValue<TComponent> ? {
|
|
35
35
|
serialize?: (value: Value<TDraft, TPath>) => FieldValue<TComponent>;
|
|
36
36
|
} : {
|
|
37
37
|
serialize: (value: Value<TDraft, TPath>) => FieldValue<TComponent>;
|
|
@@ -40,5 +40,5 @@ export type FormFieldProps<TDraft, TPath extends PathAsString<TDraft>, TComponen
|
|
|
40
40
|
} : {
|
|
41
41
|
deserialize: (value: FieldChangeValue<TComponent>) => Value<TDraft, TPath>;
|
|
42
42
|
});
|
|
43
|
-
export declare function FormField<TDraft, TPath extends PathAsString<TDraft>, TComponent extends FormFieldComponent
|
|
43
|
+
export declare function FormField<TDraft, TPath extends PathAsString<TDraft>, TComponent extends FormFieldComponent>(this: Form<TDraft, any>, { name, component, commitOnBlur, commitDebounce, inputFilter, serialize, deserialize, ...restProps }: FormFieldProps<TDraft, TPath, TComponent>): JSX.Element;
|
|
44
44
|
export {};
|