@goodie-forms/react 1.2.5-alpha → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -0
- package/dist/components/FieldRenderer.d.ts +9 -13
- package/dist/components/FieldRenderer.d.ts.map +1 -1
- package/dist/hooks/useFieldIssues.d.ts +4 -0
- package/dist/hooks/useFieldIssues.d.ts.map +1 -0
- package/dist/hooks/useFieldValue.d.ts +1 -1
- package/dist/hooks/useFieldValue.d.ts.map +1 -1
- package/dist/hooks/useForm.d.ts +18 -6
- package/dist/hooks/useForm.d.ts.map +1 -1
- package/dist/hooks/useFormField.d.ts +3 -2
- package/dist/hooks/useFormField.d.ts.map +1 -1
- package/dist/hooks/useSyncMutableStore.d.ts +2 -0
- package/dist/hooks/useSyncMutableStore.d.ts.map +1 -0
- package/dist/index.d.ts +1 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +140 -169
- package/dist/index.js.map +1 -1
- package/package.json +15 -2
- package/dist/hooks/useFieldErrors.d.ts +0 -4
- package/dist/hooks/useFieldErrors.d.ts.map +0 -1
- package/dist/hooks/useFormErrorObserver.d.ts +0 -7
- package/dist/hooks/useFormErrorObserver.d.ts.map +0 -1
- package/dist/hooks/useFormValuesObserver.d.ts +0 -7
- package/dist/hooks/useFormValuesObserver.d.ts.map +0 -1
- package/dist/hooks/useRenderControl.d.ts +0 -5
- package/dist/hooks/useRenderControl.d.ts.map +0 -1
- package/src/components/FieldRenderer.tsx +0 -166
- package/src/hooks/useFieldErrors.ts +0 -31
- package/src/hooks/useFieldValue.ts +0 -31
- package/src/hooks/useForm.tsx +0 -50
- package/src/hooks/useFormErrorObserver.ts +0 -45
- package/src/hooks/useFormField.tsx +0 -63
- package/src/hooks/useFormValuesObserver.ts +0 -50
- package/src/hooks/useRenderControl.tsx +0 -26
- package/src/index.ts +0 -9
- package/src/utils/composeFns.ts +0 -7
- package/src/utils/groupBy.ts +0 -13
- package/tsconfig.json +0 -8
- package/vite.config.ts +0 -23
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/hooks/useRenderControl.tsx","../src/utils/composeFns.ts","../src/hooks/useForm.tsx","../src/hooks/useFormField.tsx","../src/hooks/useFieldValue.ts","../src/hooks/useFieldErrors.ts","../src/hooks/useFormValuesObserver.ts","../src/utils/groupBy.ts","../src/hooks/useFormErrorObserver.ts","../src/components/FieldRenderer.tsx"],"sourcesContent":["import { startTransition, useRef, useState } from \"react\";\r\n\r\nexport function useRenderControl() {\r\n const [, rerender] = useState(0);\r\n const renderCount = useRef(0);\r\n const renderScheduled = useRef(false);\r\n renderCount.current++;\r\n\r\n const scheduleRerender = () => {\r\n if (renderScheduled.current) return;\r\n renderScheduled.current = true;\r\n\r\n queueMicrotask(() => {\r\n startTransition(() => {\r\n rerender((i) => i + 1);\r\n });\r\n\r\n renderScheduled.current = false;\r\n });\r\n };\r\n\r\n return {\r\n renderCount: renderCount.current,\r\n forceRerender: scheduleRerender,\r\n };\r\n}\r\n","export function composeFns<TFns extends (() => void)[]>(...fns: TFns) {\r\n return () => {\r\n for (const fn of fns) {\r\n fn();\r\n }\r\n };\r\n}\r\n","import { FieldPathBuilder, FormController } from \"@goodie-forms/core\";\r\nimport { useEffect, useState } from \"react\";\r\nimport { useRenderControl } from \"../hooks/useRenderControl\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\n\r\nexport function useForm<TOutput extends object>(\r\n formConfigs: FormController.Configs<TOutput>,\r\n hookConfigs?: {\r\n validateMode?: \"onChange\" | \"onBlur\" | \"onSubmit\";\r\n revalidateMode?: \"onChange\" | \"onBlur\" | \"onSubmit\";\r\n watchIssues?: boolean;\r\n watchValues?: boolean;\r\n },\r\n) {\r\n const [controller] = useState(() => new FormController(formConfigs));\r\n const [paths] = useState(() => new FieldPathBuilder<TOutput>());\r\n\r\n const renderControl = useRenderControl();\r\n\r\n useEffect(() => {\r\n const noop = () => {};\r\n\r\n return composeFns(\r\n controller.events.on(\"submissionStatusChange\", () => {\r\n renderControl.forceRerender();\r\n }),\r\n hookConfigs?.watchIssues\r\n ? controller.events.on(\"fieldIssuesUpdated\", () =>\r\n renderControl.forceRerender(),\r\n )\r\n : noop,\r\n hookConfigs?.watchValues\r\n ? controller.events.on(\"valueChanged\", () =>\r\n renderControl.forceRerender(),\r\n )\r\n : noop,\r\n );\r\n }, [controller]);\r\n\r\n return {\r\n formConfigs,\r\n paths,\r\n hookConfigs,\r\n controller,\r\n };\r\n}\r\n\r\nexport type UseForm<TOutput extends object> = ReturnType<\r\n typeof useForm<TOutput>\r\n>;\r\n","import { FieldPath } from \"@goodie-forms/core\";\r\nimport { useEffect, useState } from \"react\";\r\nimport { UseForm } from \"../hooks/useForm\";\r\nimport { useRenderControl } from \"../hooks/useRenderControl\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\n\r\nexport function useFormField<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(\r\n form: UseForm<TOutput>,\r\n path: TPath,\r\n bindingConfig?: Parameters<typeof form.controller.bindField<TPath>>[1],\r\n) {\r\n const renderControl = useRenderControl();\r\n\r\n const [field, setField] = useState(() => {\r\n let field = form.controller.getField(path);\r\n if (field == null && bindingConfig != null) {\r\n field = form.controller.bindField(path, bindingConfig);\r\n }\r\n return field;\r\n });\r\n\r\n useEffect(() => {\r\n const { events } = form.controller;\r\n\r\n setField(form.controller.getField(path));\r\n\r\n return composeFns(\r\n events.on(\"fieldBound\", (_path) => {\r\n if (!FieldPath.equals(_path, path)) return;\r\n setField(form.controller.getField(path));\r\n }),\r\n events.on(\"fieldUnbound\", (_path) => {\r\n if (!FieldPath.equals(_path, path)) return;\r\n setField(undefined);\r\n }),\r\n events.on(\"valueChanged\", (changedPath) => {\r\n if (\r\n FieldPath.equals(changedPath, path) ||\r\n FieldPath.isDescendant(changedPath, path)\r\n ) {\r\n renderControl.forceRerender();\r\n }\r\n }),\r\n events.on(\"fieldTouchUpdated\", (_path) => {\r\n if (!FieldPath.equals(_path, path)) return;\r\n renderControl.forceRerender();\r\n }),\r\n events.on(\"fieldDirtyUpdated\", (_path) => {\r\n if (!FieldPath.equals(_path, path)) return;\r\n renderControl.forceRerender();\r\n }),\r\n events.on(\"fieldIssuesUpdated\", (_path) => {\r\n if (!FieldPath.equals(_path, path)) return;\r\n renderControl.forceRerender();\r\n }),\r\n );\r\n }, []);\r\n\r\n return field;\r\n}\r\n","import { FieldPath } from \"@goodie-forms/core\";\r\nimport { useEffect } from \"react\";\r\nimport { UseForm } from \"../hooks/useForm\";\r\nimport { useRenderControl } from \"../hooks/useRenderControl\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\n\r\nexport function useFieldValue<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(form: UseForm<TOutput>, path: TPath) {\r\n const renderControl = useRenderControl();\r\n\r\n const value = form.controller.getField(path)?.value;\r\n\r\n useEffect(() => {\r\n return composeFns(\r\n form.controller.events.on(\"fieldBound\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n renderControl.forceRerender();\r\n }\r\n }),\r\n form.controller.events.on(\"valueChanged\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n renderControl.forceRerender();\r\n }\r\n }),\r\n );\r\n }, []);\r\n\r\n return value;\r\n}\r\n","import { FieldPath } from \"@goodie-forms/core\";\r\nimport { useEffect } from \"react\";\r\nimport { UseForm } from \"../hooks/useForm\";\r\nimport { useRenderControl } from \"../hooks/useRenderControl\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\n\r\nexport function useFieldErrors<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(form: UseForm<TOutput>, path: TPath) {\r\n const renderControl = useRenderControl();\r\n\r\n const issues = form.controller.getField(path)?.issues;\r\n\r\n useEffect(() => {\r\n return composeFns(\r\n form.controller.events.on(\"fieldBound\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n renderControl.forceRerender();\r\n }\r\n }),\r\n form.controller.events.on(\"fieldIssuesUpdated\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n renderControl.forceRerender();\r\n }\r\n }),\r\n );\r\n }, []);\r\n\r\n return issues;\r\n}\r\n","import { FieldPath } from \"@goodie-forms/core\";\r\nimport { useEffect } from \"react\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\nimport type { UseForm } from \"./useForm\";\r\nimport { useRenderControl } from \"./useRenderControl\";\r\n\r\n/** @deprecated */\r\nexport function useFormValuesObserver<\r\n TOutput extends object,\r\n TPaths extends FieldPath.Segments[] | undefined = undefined,\r\n>(\r\n form: UseForm<TOutput>,\r\n options?: {\r\n include?: TPaths;\r\n },\r\n) {\r\n const renderControl = useRenderControl();\r\n\r\n const observedValues =\r\n options?.include == null\r\n ? form.controller._data\r\n : options.include.reduce((data, path) => {\r\n const value = FieldPath.getValue(\r\n form.controller._data as TOutput,\r\n path,\r\n )!;\r\n FieldPath.setValue(data, path, value);\r\n return data;\r\n }, {} as TOutput);\r\n\r\n useEffect(() => {\r\n const { events } = form.controller;\r\n\r\n return composeFns(\r\n events.on(\"valueChanged\", (changedPath) => {\r\n const watchingChange =\r\n options?.include == null\r\n ? true\r\n : options.include.some(\r\n (path) =>\r\n FieldPath.equals(path, changedPath) ||\r\n FieldPath.isDescendant(path, changedPath),\r\n );\r\n if (watchingChange) renderControl.forceRerender();\r\n }),\r\n );\r\n }, []);\r\n\r\n return observedValues;\r\n}\r\n","export function groupBy<T, K extends PropertyKey>(\r\n items: readonly T[],\r\n key: (item: T) => K,\r\n): Record<K, T[]> {\r\n const result = {} as Record<K, T[]>;\r\n\r\n for (const item of items) {\r\n const k = key(item);\r\n (result[k] ??= []).push(item);\r\n }\r\n\r\n return result;\r\n}\r\n","import { FieldPath } from \"@goodie-forms/core\";\r\nimport { groupBy } from \"../utils/groupBy\";\r\nimport { useEffect } from \"react\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\nimport type { UseForm } from \"./useForm\";\r\nimport { useRenderControl } from \"./useRenderControl\";\r\n\r\n/** @deprecated */\r\nexport function useFormErrorObserver<\r\n TOutput extends object,\r\n TInclude extends FieldPath.Segments[] | undefined = undefined,\r\n>(\r\n form: UseForm<TOutput>,\r\n options?: {\r\n include?: TInclude;\r\n },\r\n) {\r\n const renderControl = useRenderControl();\r\n\r\n const observedIssues = form.controller._issues.filter((issue) => {\r\n if (options?.include == null) return true;\r\n const normalizedIssuePath = FieldPath.normalize(issue.path);\r\n return options.include.some((path) =>\r\n FieldPath.equals(path, normalizedIssuePath),\r\n );\r\n });\r\n\r\n useEffect(() => {\r\n const { events } = form.controller;\r\n\r\n return composeFns(\r\n events.on(\"fieldIssuesUpdated\", (path) => {\r\n if (options?.include?.includes?.(path) ?? true) {\r\n renderControl.forceRerender();\r\n }\r\n }),\r\n );\r\n }, []);\r\n\r\n return groupBy(observedIssues, (issue) =>\r\n issue.path == null\r\n ? \"$\"\r\n : FieldPath.toStringPath(FieldPath.normalize(issue.path)),\r\n );\r\n}\r\n","import { FieldPath, FormField, NonnullFormField } from \"@goodie-forms/core\";\r\nimport { ChangeEvent, ReactNode, Ref, useEffect, useRef } from \"react\";\r\nimport { UseForm } from \"../hooks/useForm\";\r\nimport { useFormField } from \"../hooks/useFormField\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\n\r\nexport interface RenderParams<TOutput extends object, TValue> {\r\n fieldProps: {\r\n ref: Ref<any | null>;\r\n\r\n value: TValue | undefined;\r\n\r\n onChange: (event: ChangeEvent<EventTarget> | TValue) => void;\r\n onFocus: () => void;\r\n onBlur: () => void;\r\n };\r\n\r\n field: undefined extends TValue\r\n ? FormField<TOutput, TValue>\r\n : NonnullFormField<TOutput, TValue>;\r\n\r\n form: UseForm<TOutput>;\r\n}\r\n\r\ntype DefaultValueProps<TValue> = undefined extends TValue\r\n ? { defaultValue?: TValue | (() => TValue) }\r\n : { defaultValue: TValue | (() => TValue) };\r\n\r\nexport type FieldRendererProps<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n> = {\r\n form: UseForm<TOutput>;\r\n path: TPath;\r\n overrideInitialValue?: boolean;\r\n unbindOnUnmount?: boolean;\r\n render: (\r\n params: RenderParams<TOutput, FieldPath.Resolve<TOutput, NoInfer<TPath>>>,\r\n ) => ReactNode;\r\n} & DefaultValueProps<FieldPath.Resolve<TOutput, NoInfer<TPath>>>;\r\n\r\nexport function FieldRenderer<\r\n TOutput extends object,\r\n const TPath extends FieldPath.Segments,\r\n>(props: FieldRendererProps<TOutput, TPath>) {\r\n type TValue = FieldPath.Resolve<TOutput, TPath>;\r\n\r\n const elementRef = useRef<HTMLElement>(null);\r\n\r\n const field = useFormField(props.form, props.path, {\r\n overrideInitialValue: props.overrideInitialValue ?? true,\r\n defaultValue:\r\n typeof props.defaultValue === \"function\"\r\n ? (props.defaultValue as any)()\r\n : props.defaultValue,\r\n })!;\r\n\r\n const renderedJsx = props.render({\r\n fieldProps: {\r\n ref: elementRef,\r\n value: field.value,\r\n onChange(arg) {\r\n let newValue: TValue;\r\n\r\n if (typeof arg === \"object\" && \"target\" in arg) {\r\n const { target } = arg;\r\n if (target !== field.boundElement) return;\r\n if (!(\"value\" in target)) return;\r\n if (typeof target.value !== \"string\") return;\r\n newValue = target.value as TValue;\r\n } else {\r\n newValue = arg;\r\n }\r\n\r\n field.setValue(newValue, {\r\n shouldTouch: true,\r\n shouldMarkDirty: true,\r\n });\r\n },\r\n onFocus() {\r\n field.touch();\r\n },\r\n onBlur() {\r\n if (\r\n props.form.hookConfigs?.validateMode === \"onBlur\" ||\r\n props.form.hookConfigs?.validateMode === \"onChange\"\r\n ) {\r\n props.form.controller.validateField(props.path);\r\n }\r\n },\r\n },\r\n field: field as any,\r\n form: props.form,\r\n });\r\n\r\n useEffect(() => {\r\n const { events } = props.form.controller;\r\n\r\n return composeFns(\r\n events.on(\"valueChanged\", (_path) => {\r\n if (\r\n !FieldPath.equals(_path, props.path) &&\r\n !FieldPath.isDescendant(_path, props.path)\r\n ) {\r\n return;\r\n }\r\n\r\n if (props.form.hookConfigs?.validateMode === \"onChange\") {\r\n props.form.controller.validateField(props.path);\r\n }\r\n }),\r\n );\r\n }, []);\r\n\r\n useEffect(() => {\r\n field.bindElement(elementRef.current!);\r\n\r\n return () => {\r\n if (props.unbindOnUnmount) {\r\n props.form.controller.unbindField(props.path);\r\n }\r\n };\r\n }, []);\r\n\r\n return <>{renderedJsx}</>;\r\n}\r\n\r\n/* ---- TESTS ---------------- */\r\n\r\n// function TestComp() {\r\n// const form = useForm<{ a?: { b: 99 } }>({});\r\n\r\n// const jsx = (\r\n// <>\r\n// <FieldRenderer\r\n// form={form}\r\n// path={form.paths.fromProxy((data) => data.a.b)}\r\n// defaultValue={() => 99 as const}\r\n// render={({ fieldProps, field }) => {\r\n// // ^?\r\n// return <input {...fieldProps} />;\r\n// }}\r\n// />\r\n\r\n// {/* defaultField olmayabilir, çünkü \"a\" nullable */}\r\n// <FieldRenderer\r\n// form={form}\r\n// path={form.paths.fromProxy((data) => data.a)}\r\n// render={({ ref, value, handlers, field }) => {\r\n// // ^?\r\n// return <></>;\r\n// }}\r\n// />\r\n\r\n// <FieldRenderer\r\n// form={form}\r\n// path={form.paths.fromStringPath(\"a.b\")}\r\n// defaultValue={() => 99 as const}\r\n// render={({ ref, value, handlers, field }) => {\r\n// // ^?\r\n// return <></>;\r\n// }}\r\n// />\r\n// </>\r\n// );\r\n// }\r\n"],"names":["useRenderControl","rerender","useState","renderCount","useRef","renderScheduled","scheduleRerender","startTransition","i","composeFns","fns","fn","useForm","formConfigs","hookConfigs","controller","FormController","paths","FieldPathBuilder","renderControl","useEffect","noop","useFormField","form","path","bindingConfig","field","setField","events","_path","FieldPath","changedPath","useFieldValue","value","fieldPath","useFieldErrors","issues","useFormValuesObserver","options","observedValues","data","groupBy","items","key","result","item","k","useFormErrorObserver","observedIssues","issue","normalizedIssuePath","FieldRenderer","props","elementRef","renderedJsx","arg","newValue","target"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/hooks/useSyncMutableStore.ts","../src/hooks/useForm.tsx","../src/utils/composeFns.ts","../src/hooks/useFormField.tsx","../src/hooks/useFieldValue.ts","../src/hooks/useFieldIssues.ts","../src/components/FieldRenderer.tsx"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from \"react\";\r\n\r\nexport function useSyncMutableStore<T>(\r\n subscribe: (onVersionChange: () => void) => () => void,\r\n getValue: () => T,\r\n) {\r\n const versionRef = useRef(0);\r\n\r\n const subscribeWithVersion = useCallback(\r\n (onStoreChange: () => void) => {\r\n return subscribe(() => {\r\n versionRef.current++;\r\n onStoreChange();\r\n });\r\n },\r\n [subscribe],\r\n );\r\n\r\n const getSnapshot = useCallback(() => {\r\n return versionRef.current;\r\n }, []);\r\n\r\n useSyncExternalStore(subscribeWithVersion, getSnapshot, getSnapshot);\r\n\r\n return getValue();\r\n}\r\n","import { FormController } from \"@goodie-forms/core\";\r\nimport { useCallback, useState, useSyncExternalStore } from \"react\";\r\nimport { useSyncMutableStore } from \"../hooks/useSyncMutableStore\";\r\n\r\nexport function useForm<TOutput extends object>(\r\n formConfigs: FormController.Configs<TOutput>,\r\n hookConfigs?: {\r\n validateMode?: \"onChange\" | \"onBlur\" | \"onSubmit\";\r\n revalidateMode?: \"onChange\" | \"onBlur\" | \"onSubmit\";\r\n },\r\n) {\r\n const [controller] = useState(() => new FormController(formConfigs));\r\n\r\n const subscribe = useCallback(\r\n (onVersionChange: () => void) => {\r\n return controller.events.on(\"submissionStatusChange\", onVersionChange);\r\n },\r\n [controller],\r\n );\r\n\r\n useSyncMutableStore(subscribe, () => controller);\r\n\r\n const useWatchValues = () => {\r\n return useSyncExternalStore(\r\n (onStoreChange) =>\r\n controller.events.on(\"fieldValueChanged\", onStoreChange),\r\n () => controller.data,\r\n () => controller.data,\r\n );\r\n };\r\n\r\n const useWatchIssues = () => {\r\n return useSyncExternalStore(\r\n (onStoreChange) =>\r\n controller.events.on(\"fieldIssuesUpdated\", onStoreChange),\r\n () => controller.issues,\r\n () => controller.issues,\r\n );\r\n };\r\n\r\n const useWatchEvent = <E extends keyof typeof controller.events.events>(\r\n eventName: E,\r\n listener?: NonNullable<(typeof controller.events.events)[E]>[number],\r\n ) => {\r\n return useSyncMutableStore(\r\n (onVersionChange) =>\r\n controller.events.on(eventName, (...args: any[]) => {\r\n (listener as any)?.(...args);\r\n onVersionChange();\r\n }),\r\n () => undefined,\r\n );\r\n };\r\n\r\n return {\r\n formConfigs,\r\n hookConfigs,\r\n controller,\r\n path: controller.path,\r\n watchValues: useWatchValues,\r\n watchIssues: useWatchIssues,\r\n watchEvent: useWatchEvent,\r\n };\r\n}\r\n\r\nexport type UseForm<TOutput extends object> = ReturnType<\r\n typeof useForm<TOutput>\r\n>;\r\n","export function composeFns<TFns extends (() => void)[]>(...fns: TFns) {\r\n return () => {\r\n for (const fn of fns) {\r\n fn();\r\n }\r\n };\r\n}\r\n","import { FieldPath, FormField } from \"@goodie-forms/core\";\r\nimport { useCallback, useState } from \"react\";\r\nimport { UseForm } from \"../hooks/useForm\";\r\nimport { useSyncMutableStore } from \"../hooks/useSyncMutableStore\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\n\r\nexport function useFormField<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(\r\n form: UseForm<TOutput>,\r\n path: TPath,\r\n): FormField<TOutput, FieldPath.Resolve<TOutput, TPath>> | undefined;\r\n\r\nexport function useFormField<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(\r\n form: UseForm<TOutput>,\r\n path: TPath,\r\n registerConfig: Parameters<typeof form.controller.registerField<TPath>>[1],\r\n): FormField<TOutput, FieldPath.Resolve<TOutput, TPath>>;\r\n\r\nexport function useFormField<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(\r\n form: UseForm<TOutput>,\r\n path: TPath,\r\n registerConfig?: Parameters<typeof form.controller.registerField<TPath>>[1],\r\n) {\r\n const { controller } = form;\r\n\r\n useState(() => {\r\n let existing = controller.getField(path);\r\n\r\n if (!existing && registerConfig) {\r\n controller.registerField(path, registerConfig);\r\n }\r\n\r\n return null;\r\n });\r\n\r\n const subscribe = useCallback(\r\n (onVersionChange: () => void) => {\r\n const { events } = controller;\r\n\r\n return composeFns(\r\n events.on(\"fieldRegistered\", (_path) => {\r\n if (FieldPath.equals(_path, path)) {\r\n onVersionChange();\r\n }\r\n }),\r\n events.on(\"fieldUnregistered\", (_path) => {\r\n if (FieldPath.equals(_path, path)) {\r\n onVersionChange();\r\n }\r\n }),\r\n events.on(\"fieldValueChanged\", (changedPath) => {\r\n if (\r\n FieldPath.equals(changedPath, path) ||\r\n FieldPath.isDescendant(changedPath, path)\r\n ) {\r\n onVersionChange();\r\n }\r\n }),\r\n events.on(\"fieldTouchUpdated\", (_path) => {\r\n if (FieldPath.equals(_path, path)) {\r\n onVersionChange();\r\n }\r\n }),\r\n events.on(\"fieldDirtyUpdated\", (_path) => {\r\n if (FieldPath.equals(_path, path)) {\r\n onVersionChange();\r\n }\r\n }),\r\n events.on(\"fieldIssuesUpdated\", (_path) => {\r\n if (FieldPath.equals(_path, path)) {\r\n onVersionChange();\r\n }\r\n }),\r\n );\r\n },\r\n [controller, path],\r\n );\r\n\r\n return useSyncMutableStore(subscribe, () => controller.getField(path));\r\n}\r\n","import { FieldPath } from \"@goodie-forms/core\";\r\nimport { useCallback, useSyncExternalStore } from \"react\";\r\nimport { UseForm } from \"../hooks/useForm\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\n\r\nexport function useFieldValue<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(form: UseForm<TOutput>, path: TPath) {\r\n const { controller } = form;\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => {\r\n const { events } = controller;\r\n\r\n return composeFns(\r\n events.on(\"fieldRegistered\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldUnregistered\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldValueChanged\", (changedPath) => {\r\n if (\r\n FieldPath.equals(changedPath, path) ||\r\n FieldPath.isDescendant(changedPath, path)\r\n ) {\r\n onStoreChange();\r\n }\r\n }),\r\n );\r\n },\r\n [controller, path],\r\n );\r\n\r\n const getSnapshot = useCallback(() => {\r\n return controller.getField(path)?.value;\r\n }, [controller, path]);\r\n\r\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\r\n}\r\n","import { FieldPath } from \"@goodie-forms/core\";\r\nimport { useCallback, useSyncExternalStore } from \"react\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\nimport { UseForm } from \"./useForm\";\r\n\r\nexport function useFieldIssues<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n>(form: UseForm<TOutput>, path: TPath) {\r\n const { controller } = form;\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => {\r\n const { events } = controller;\r\n\r\n return composeFns(\r\n events.on(\"fieldRegistered\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldUnregistered\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n onStoreChange();\r\n }\r\n }),\r\n events.on(\"fieldIssuesUpdated\", (fieldPath) => {\r\n if (FieldPath.equals(path, fieldPath)) {\r\n onStoreChange();\r\n }\r\n }),\r\n );\r\n },\r\n [controller, path],\r\n );\r\n\r\n const getSnapshot = useCallback(() => {\r\n return controller.getField(path)?.issues ?? [];\r\n }, [controller, path]);\r\n\r\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\r\n}\r\n","import {\r\n DeepReadonly,\r\n FieldPath,\r\n FormField,\r\n Suppliable,\r\n} from \"@goodie-forms/core\";\r\nimport { ChangeEvent, ReactNode, Ref, useEffect, useRef } from \"react\";\r\nimport { UseForm } from \"../hooks/useForm\";\r\nimport { useFormField } from \"../hooks/useFormField\";\r\nimport { composeFns } from \"../utils/composeFns\";\r\n\r\nexport interface RenderParams<TOutput extends object, TValue> {\r\n fieldProps: {\r\n ref: Ref<any | null>;\r\n\r\n name: string;\r\n\r\n value: DeepReadonly<TValue> | undefined;\r\n\r\n onChange: (event: ChangeEvent<EventTarget> | TValue) => void;\r\n onFocus: () => void;\r\n onBlur: () => void;\r\n };\r\n\r\n field: FormField<TOutput, TValue>;\r\n\r\n form: UseForm<TOutput>;\r\n}\r\n\r\nexport interface FieldRendererProps<\r\n TOutput extends object,\r\n TPath extends FieldPath.Segments,\r\n> {\r\n form: UseForm<TOutput>;\r\n path: TPath;\r\n defaultValue?: Suppliable<FieldPath.Resolve<TOutput, TPath>>;\r\n overrideInitialValue?: boolean;\r\n unregisterOnUnmount?: boolean;\r\n render: (\r\n params: RenderParams<TOutput, FieldPath.Resolve<TOutput, TPath>>,\r\n ) => ReactNode;\r\n}\r\n\r\nexport function FieldRenderer<\r\n TOutput extends object,\r\n const TPath extends FieldPath.Segments,\r\n>(props: FieldRendererProps<TOutput, TPath>) {\r\n type TValue = FieldPath.Resolve<TOutput, TPath>;\r\n\r\n const elementRef = useRef<HTMLElement>(null);\r\n\r\n const field = useFormField(props.form, props.path, {\r\n overrideInitialValue: props.overrideInitialValue ?? true,\r\n defaultValue: props.defaultValue,\r\n })!;\r\n\r\n const currentValidateMode = props.form.controller.triedSubmitting\r\n ? (props.form.hookConfigs?.revalidateMode ??\r\n props.form.hookConfigs?.validateMode)\r\n : props.form.hookConfigs?.validateMode;\r\n\r\n const renderedJsx = props.render({\r\n fieldProps: {\r\n ref: elementRef,\r\n name: field.stringPath,\r\n value: field.value,\r\n onChange(arg) {\r\n let newValue: TValue;\r\n\r\n if (typeof arg === \"object\" && \"target\" in arg) {\r\n const { target } = arg;\r\n if (target !== field.boundElement) return;\r\n if (!(\"value\" in target)) return;\r\n if (typeof target.value !== \"string\") return;\r\n newValue = target.value as TValue;\r\n } else {\r\n newValue = arg;\r\n }\r\n\r\n field.setValue(newValue, {\r\n shouldTouch: true,\r\n shouldMarkDirty: true,\r\n });\r\n },\r\n onFocus() {\r\n field.touch();\r\n },\r\n onBlur() {\r\n if (\r\n field.issues.length !== 0 ||\r\n currentValidateMode === \"onBlur\" ||\r\n currentValidateMode === \"onChange\"\r\n ) {\r\n props.form.controller.validateField(props.path);\r\n }\r\n },\r\n },\r\n field: field as any,\r\n form: props.form,\r\n });\r\n\r\n useEffect(() => {\r\n const { events } = props.form.controller;\r\n\r\n return composeFns(\r\n events.on(\"fieldValueChanged\", (_path) => {\r\n if (\r\n !FieldPath.equals(_path, props.path) &&\r\n !FieldPath.isDescendant(_path, props.path)\r\n ) {\r\n return;\r\n }\r\n\r\n if (field.issues.length !== 0 || currentValidateMode === \"onChange\") {\r\n props.form.controller.validateField(props.path);\r\n }\r\n }),\r\n );\r\n }, [currentValidateMode]);\r\n\r\n useEffect(() => {\r\n field.bindElement(elementRef.current!);\r\n\r\n return () => {\r\n if (props.unregisterOnUnmount) {\r\n props.form.controller.unregisterField(props.path);\r\n }\r\n };\r\n }, []);\r\n\r\n return <>{renderedJsx}</>;\r\n}\r\n\r\n/* ---- TESTS ---------------- */\r\n\r\n// function TestComp() {\r\n// const form = useForm<{ a?: { b: 99 } }>({});\r\n\r\n// const jsx = (\r\n// <>\r\n// <FieldRenderer\r\n// form={form}\r\n// path={form.paths.fromProxy((data) => data.a.b)}\r\n// defaultValue={() => 99 as const}\r\n// render={({ fieldProps, field }) => {\r\n// // ^?\r\n// return <input {...fieldProps} />;\r\n// }}\r\n// />\r\n\r\n// {/* defaultField olmayabilir, çünkü \"a\" nullable */}\r\n// <FieldRenderer\r\n// form={form}\r\n// path={form.paths.fromProxy((data) => data.a)}\r\n// render={({ ref, value, handlers, field }) => {\r\n// // ^?\r\n// return <></>;\r\n// }}\r\n// />\r\n\r\n// <FieldRenderer\r\n// form={form}\r\n// path={form.paths.fromStringPath(\"a.b\")}\r\n// defaultValue={() => 99 as const}\r\n// render={({ ref, value, handlers, field }) => {\r\n// // ^?\r\n// return <></>;\r\n// }}\r\n// />\r\n// </>\r\n// );\r\n// }\r\n"],"names":["useSyncMutableStore","subscribe","getValue","versionRef","useRef","subscribeWithVersion","useCallback","onStoreChange","getSnapshot","useSyncExternalStore","useForm","formConfigs","hookConfigs","controller","useState","FormController","onVersionChange","useWatchValues","useWatchIssues","useWatchEvent","eventName","listener","args","composeFns","fns","fn","useFormField","form","path","registerConfig","events","_path","FieldPath","changedPath","useFieldValue","fieldPath","useFieldIssues","FieldRenderer","props","elementRef","field","currentValidateMode","renderedJsx","arg","newValue","target","useEffect"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodie-forms/react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "https://github.com/iGoodie/goodie-forms"
|
|
7
|
+
},
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Taha Anılcan Metinyurt",
|
|
10
|
+
"email": "igoodie@programmer.net",
|
|
11
|
+
"url": "https://github.com/iGoodie"
|
|
12
|
+
},
|
|
13
|
+
"license": "CC BY-SA 4.0",
|
|
4
14
|
"type": "module",
|
|
5
15
|
"main": "dist/index.js",
|
|
6
16
|
"types": "dist/index.d.ts",
|
|
@@ -10,6 +20,9 @@
|
|
|
10
20
|
"default": "./dist/index.js"
|
|
11
21
|
}
|
|
12
22
|
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
13
26
|
"publishConfig": {
|
|
14
27
|
"access": "public"
|
|
15
28
|
},
|
|
@@ -18,7 +31,7 @@
|
|
|
18
31
|
"react-dom": "^18 || ^19"
|
|
19
32
|
},
|
|
20
33
|
"dependencies": {
|
|
21
|
-
"@goodie-forms/core": "1.
|
|
34
|
+
"@goodie-forms/core": "1.3.0"
|
|
22
35
|
},
|
|
23
36
|
"devDependencies": {
|
|
24
37
|
"@types/react": "^19.2.9",
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { FieldPath } from '../../../core/src';
|
|
2
|
-
import { UseForm } from '../hooks/useForm';
|
|
3
|
-
export declare function useFieldErrors<TOutput extends object, TPath extends FieldPath.Segments>(form: UseForm<TOutput>, path: TPath): import("@standard-schema/spec").StandardSchemaV1.Issue[] | undefined;
|
|
4
|
-
//# sourceMappingURL=useFieldErrors.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useFieldErrors.d.ts","sourceRoot":"","sources":["../../src/hooks/useFieldErrors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAI3C,wBAAgB,cAAc,CAC5B,OAAO,SAAS,MAAM,EACtB,KAAK,SAAS,SAAS,CAAC,QAAQ,EAChC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,wEAqBpC"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { FieldPath } from '../../../core/src';
|
|
2
|
-
import { UseForm } from './useForm';
|
|
3
|
-
/** @deprecated */
|
|
4
|
-
export declare function useFormErrorObserver<TOutput extends object, TInclude extends FieldPath.Segments[] | undefined = undefined>(form: UseForm<TOutput>, options?: {
|
|
5
|
-
include?: TInclude;
|
|
6
|
-
}): Record<string, import("@standard-schema/spec").StandardSchemaV1.Issue[]>;
|
|
7
|
-
//# sourceMappingURL=useFormErrorObserver.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useFormErrorObserver.d.ts","sourceRoot":"","sources":["../../src/hooks/useFormErrorObserver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAI/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,kBAAkB;AAClB,wBAAgB,oBAAoB,CAClC,OAAO,SAAS,MAAM,EACtB,QAAQ,SAAS,SAAS,CAAC,QAAQ,EAAE,GAAG,SAAS,GAAG,SAAS,EAE7D,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,EACtB,OAAO,CAAC,EAAE;IACR,OAAO,CAAC,EAAE,QAAQ,CAAC;CACpB,4EA6BF"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { FieldPath } from '../../../core/src';
|
|
2
|
-
import { UseForm } from './useForm';
|
|
3
|
-
/** @deprecated */
|
|
4
|
-
export declare function useFormValuesObserver<TOutput extends object, TPaths extends FieldPath.Segments[] | undefined = undefined>(form: UseForm<TOutput>, options?: {
|
|
5
|
-
include?: TPaths;
|
|
6
|
-
}): import('../../../core/src').DeepPartial<TOutput>;
|
|
7
|
-
//# sourceMappingURL=useFormValuesObserver.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useFormValuesObserver.d.ts","sourceRoot":"","sources":["../../src/hooks/useFormValuesObserver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,kBAAkB;AAClB,wBAAgB,qBAAqB,CACnC,OAAO,SAAS,MAAM,EACtB,MAAM,SAAS,SAAS,CAAC,QAAQ,EAAE,GAAG,SAAS,GAAG,SAAS,EAE3D,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,EACtB,OAAO,CAAC,EAAE;IACR,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,qDAmCF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useRenderControl.d.ts","sourceRoot":"","sources":["../../src/hooks/useRenderControl.tsx"],"names":[],"mappings":"AAEA,wBAAgB,gBAAgB;;;EAuB/B"}
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
import { FieldPath, FormField, NonnullFormField } from "@goodie-forms/core";
|
|
2
|
-
import { ChangeEvent, ReactNode, Ref, useEffect, useRef } from "react";
|
|
3
|
-
import { UseForm } from "../hooks/useForm";
|
|
4
|
-
import { useFormField } from "../hooks/useFormField";
|
|
5
|
-
import { composeFns } from "../utils/composeFns";
|
|
6
|
-
|
|
7
|
-
export interface RenderParams<TOutput extends object, TValue> {
|
|
8
|
-
fieldProps: {
|
|
9
|
-
ref: Ref<any | null>;
|
|
10
|
-
|
|
11
|
-
value: TValue | undefined;
|
|
12
|
-
|
|
13
|
-
onChange: (event: ChangeEvent<EventTarget> | TValue) => void;
|
|
14
|
-
onFocus: () => void;
|
|
15
|
-
onBlur: () => void;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
field: undefined extends TValue
|
|
19
|
-
? FormField<TOutput, TValue>
|
|
20
|
-
: NonnullFormField<TOutput, TValue>;
|
|
21
|
-
|
|
22
|
-
form: UseForm<TOutput>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
type DefaultValueProps<TValue> = undefined extends TValue
|
|
26
|
-
? { defaultValue?: TValue | (() => TValue) }
|
|
27
|
-
: { defaultValue: TValue | (() => TValue) };
|
|
28
|
-
|
|
29
|
-
export type FieldRendererProps<
|
|
30
|
-
TOutput extends object,
|
|
31
|
-
TPath extends FieldPath.Segments,
|
|
32
|
-
> = {
|
|
33
|
-
form: UseForm<TOutput>;
|
|
34
|
-
path: TPath;
|
|
35
|
-
overrideInitialValue?: boolean;
|
|
36
|
-
unbindOnUnmount?: boolean;
|
|
37
|
-
render: (
|
|
38
|
-
params: RenderParams<TOutput, FieldPath.Resolve<TOutput, NoInfer<TPath>>>,
|
|
39
|
-
) => ReactNode;
|
|
40
|
-
} & DefaultValueProps<FieldPath.Resolve<TOutput, NoInfer<TPath>>>;
|
|
41
|
-
|
|
42
|
-
export function FieldRenderer<
|
|
43
|
-
TOutput extends object,
|
|
44
|
-
const TPath extends FieldPath.Segments,
|
|
45
|
-
>(props: FieldRendererProps<TOutput, TPath>) {
|
|
46
|
-
type TValue = FieldPath.Resolve<TOutput, TPath>;
|
|
47
|
-
|
|
48
|
-
const elementRef = useRef<HTMLElement>(null);
|
|
49
|
-
|
|
50
|
-
const field = useFormField(props.form, props.path, {
|
|
51
|
-
overrideInitialValue: props.overrideInitialValue ?? true,
|
|
52
|
-
defaultValue:
|
|
53
|
-
typeof props.defaultValue === "function"
|
|
54
|
-
? (props.defaultValue as any)()
|
|
55
|
-
: props.defaultValue,
|
|
56
|
-
})!;
|
|
57
|
-
|
|
58
|
-
const renderedJsx = props.render({
|
|
59
|
-
fieldProps: {
|
|
60
|
-
ref: elementRef,
|
|
61
|
-
value: field.value,
|
|
62
|
-
onChange(arg) {
|
|
63
|
-
let newValue: TValue;
|
|
64
|
-
|
|
65
|
-
if (typeof arg === "object" && "target" in arg) {
|
|
66
|
-
const { target } = arg;
|
|
67
|
-
if (target !== field.boundElement) return;
|
|
68
|
-
if (!("value" in target)) return;
|
|
69
|
-
if (typeof target.value !== "string") return;
|
|
70
|
-
newValue = target.value as TValue;
|
|
71
|
-
} else {
|
|
72
|
-
newValue = arg;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
field.setValue(newValue, {
|
|
76
|
-
shouldTouch: true,
|
|
77
|
-
shouldMarkDirty: true,
|
|
78
|
-
});
|
|
79
|
-
},
|
|
80
|
-
onFocus() {
|
|
81
|
-
field.touch();
|
|
82
|
-
},
|
|
83
|
-
onBlur() {
|
|
84
|
-
if (
|
|
85
|
-
props.form.hookConfigs?.validateMode === "onBlur" ||
|
|
86
|
-
props.form.hookConfigs?.validateMode === "onChange"
|
|
87
|
-
) {
|
|
88
|
-
props.form.controller.validateField(props.path);
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
field: field as any,
|
|
93
|
-
form: props.form,
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
useEffect(() => {
|
|
97
|
-
const { events } = props.form.controller;
|
|
98
|
-
|
|
99
|
-
return composeFns(
|
|
100
|
-
events.on("valueChanged", (_path) => {
|
|
101
|
-
if (
|
|
102
|
-
!FieldPath.equals(_path, props.path) &&
|
|
103
|
-
!FieldPath.isDescendant(_path, props.path)
|
|
104
|
-
) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (props.form.hookConfigs?.validateMode === "onChange") {
|
|
109
|
-
props.form.controller.validateField(props.path);
|
|
110
|
-
}
|
|
111
|
-
}),
|
|
112
|
-
);
|
|
113
|
-
}, []);
|
|
114
|
-
|
|
115
|
-
useEffect(() => {
|
|
116
|
-
field.bindElement(elementRef.current!);
|
|
117
|
-
|
|
118
|
-
return () => {
|
|
119
|
-
if (props.unbindOnUnmount) {
|
|
120
|
-
props.form.controller.unbindField(props.path);
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
}, []);
|
|
124
|
-
|
|
125
|
-
return <>{renderedJsx}</>;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/* ---- TESTS ---------------- */
|
|
129
|
-
|
|
130
|
-
// function TestComp() {
|
|
131
|
-
// const form = useForm<{ a?: { b: 99 } }>({});
|
|
132
|
-
|
|
133
|
-
// const jsx = (
|
|
134
|
-
// <>
|
|
135
|
-
// <FieldRenderer
|
|
136
|
-
// form={form}
|
|
137
|
-
// path={form.paths.fromProxy((data) => data.a.b)}
|
|
138
|
-
// defaultValue={() => 99 as const}
|
|
139
|
-
// render={({ fieldProps, field }) => {
|
|
140
|
-
// // ^?
|
|
141
|
-
// return <input {...fieldProps} />;
|
|
142
|
-
// }}
|
|
143
|
-
// />
|
|
144
|
-
|
|
145
|
-
// {/* defaultField olmayabilir, çünkü "a" nullable */}
|
|
146
|
-
// <FieldRenderer
|
|
147
|
-
// form={form}
|
|
148
|
-
// path={form.paths.fromProxy((data) => data.a)}
|
|
149
|
-
// render={({ ref, value, handlers, field }) => {
|
|
150
|
-
// // ^?
|
|
151
|
-
// return <></>;
|
|
152
|
-
// }}
|
|
153
|
-
// />
|
|
154
|
-
|
|
155
|
-
// <FieldRenderer
|
|
156
|
-
// form={form}
|
|
157
|
-
// path={form.paths.fromStringPath("a.b")}
|
|
158
|
-
// defaultValue={() => 99 as const}
|
|
159
|
-
// render={({ ref, value, handlers, field }) => {
|
|
160
|
-
// // ^?
|
|
161
|
-
// return <></>;
|
|
162
|
-
// }}
|
|
163
|
-
// />
|
|
164
|
-
// </>
|
|
165
|
-
// );
|
|
166
|
-
// }
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { FieldPath } from "@goodie-forms/core";
|
|
2
|
-
import { useEffect } from "react";
|
|
3
|
-
import { UseForm } from "../hooks/useForm";
|
|
4
|
-
import { useRenderControl } from "../hooks/useRenderControl";
|
|
5
|
-
import { composeFns } from "../utils/composeFns";
|
|
6
|
-
|
|
7
|
-
export function useFieldErrors<
|
|
8
|
-
TOutput extends object,
|
|
9
|
-
TPath extends FieldPath.Segments,
|
|
10
|
-
>(form: UseForm<TOutput>, path: TPath) {
|
|
11
|
-
const renderControl = useRenderControl();
|
|
12
|
-
|
|
13
|
-
const issues = form.controller.getField(path)?.issues;
|
|
14
|
-
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
return composeFns(
|
|
17
|
-
form.controller.events.on("fieldBound", (fieldPath) => {
|
|
18
|
-
if (FieldPath.equals(path, fieldPath)) {
|
|
19
|
-
renderControl.forceRerender();
|
|
20
|
-
}
|
|
21
|
-
}),
|
|
22
|
-
form.controller.events.on("fieldIssuesUpdated", (fieldPath) => {
|
|
23
|
-
if (FieldPath.equals(path, fieldPath)) {
|
|
24
|
-
renderControl.forceRerender();
|
|
25
|
-
}
|
|
26
|
-
}),
|
|
27
|
-
);
|
|
28
|
-
}, []);
|
|
29
|
-
|
|
30
|
-
return issues;
|
|
31
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { FieldPath } from "@goodie-forms/core";
|
|
2
|
-
import { useEffect } from "react";
|
|
3
|
-
import { UseForm } from "../hooks/useForm";
|
|
4
|
-
import { useRenderControl } from "../hooks/useRenderControl";
|
|
5
|
-
import { composeFns } from "../utils/composeFns";
|
|
6
|
-
|
|
7
|
-
export function useFieldValue<
|
|
8
|
-
TOutput extends object,
|
|
9
|
-
TPath extends FieldPath.Segments,
|
|
10
|
-
>(form: UseForm<TOutput>, path: TPath) {
|
|
11
|
-
const renderControl = useRenderControl();
|
|
12
|
-
|
|
13
|
-
const value = form.controller.getField(path)?.value;
|
|
14
|
-
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
return composeFns(
|
|
17
|
-
form.controller.events.on("fieldBound", (fieldPath) => {
|
|
18
|
-
if (FieldPath.equals(path, fieldPath)) {
|
|
19
|
-
renderControl.forceRerender();
|
|
20
|
-
}
|
|
21
|
-
}),
|
|
22
|
-
form.controller.events.on("valueChanged", (fieldPath) => {
|
|
23
|
-
if (FieldPath.equals(path, fieldPath)) {
|
|
24
|
-
renderControl.forceRerender();
|
|
25
|
-
}
|
|
26
|
-
}),
|
|
27
|
-
);
|
|
28
|
-
}, []);
|
|
29
|
-
|
|
30
|
-
return value;
|
|
31
|
-
}
|
package/src/hooks/useForm.tsx
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { FieldPathBuilder, FormController } from "@goodie-forms/core";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
|
-
import { useRenderControl } from "../hooks/useRenderControl";
|
|
4
|
-
import { composeFns } from "../utils/composeFns";
|
|
5
|
-
|
|
6
|
-
export function useForm<TOutput extends object>(
|
|
7
|
-
formConfigs: FormController.Configs<TOutput>,
|
|
8
|
-
hookConfigs?: {
|
|
9
|
-
validateMode?: "onChange" | "onBlur" | "onSubmit";
|
|
10
|
-
revalidateMode?: "onChange" | "onBlur" | "onSubmit";
|
|
11
|
-
watchIssues?: boolean;
|
|
12
|
-
watchValues?: boolean;
|
|
13
|
-
},
|
|
14
|
-
) {
|
|
15
|
-
const [controller] = useState(() => new FormController(formConfigs));
|
|
16
|
-
const [paths] = useState(() => new FieldPathBuilder<TOutput>());
|
|
17
|
-
|
|
18
|
-
const renderControl = useRenderControl();
|
|
19
|
-
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
const noop = () => {};
|
|
22
|
-
|
|
23
|
-
return composeFns(
|
|
24
|
-
controller.events.on("submissionStatusChange", () => {
|
|
25
|
-
renderControl.forceRerender();
|
|
26
|
-
}),
|
|
27
|
-
hookConfigs?.watchIssues
|
|
28
|
-
? controller.events.on("fieldIssuesUpdated", () =>
|
|
29
|
-
renderControl.forceRerender(),
|
|
30
|
-
)
|
|
31
|
-
: noop,
|
|
32
|
-
hookConfigs?.watchValues
|
|
33
|
-
? controller.events.on("valueChanged", () =>
|
|
34
|
-
renderControl.forceRerender(),
|
|
35
|
-
)
|
|
36
|
-
: noop,
|
|
37
|
-
);
|
|
38
|
-
}, [controller]);
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
formConfigs,
|
|
42
|
-
paths,
|
|
43
|
-
hookConfigs,
|
|
44
|
-
controller,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export type UseForm<TOutput extends object> = ReturnType<
|
|
49
|
-
typeof useForm<TOutput>
|
|
50
|
-
>;
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { FieldPath } from "@goodie-forms/core";
|
|
2
|
-
import { groupBy } from "../utils/groupBy";
|
|
3
|
-
import { useEffect } from "react";
|
|
4
|
-
import { composeFns } from "../utils/composeFns";
|
|
5
|
-
import type { UseForm } from "./useForm";
|
|
6
|
-
import { useRenderControl } from "./useRenderControl";
|
|
7
|
-
|
|
8
|
-
/** @deprecated */
|
|
9
|
-
export function useFormErrorObserver<
|
|
10
|
-
TOutput extends object,
|
|
11
|
-
TInclude extends FieldPath.Segments[] | undefined = undefined,
|
|
12
|
-
>(
|
|
13
|
-
form: UseForm<TOutput>,
|
|
14
|
-
options?: {
|
|
15
|
-
include?: TInclude;
|
|
16
|
-
},
|
|
17
|
-
) {
|
|
18
|
-
const renderControl = useRenderControl();
|
|
19
|
-
|
|
20
|
-
const observedIssues = form.controller._issues.filter((issue) => {
|
|
21
|
-
if (options?.include == null) return true;
|
|
22
|
-
const normalizedIssuePath = FieldPath.normalize(issue.path);
|
|
23
|
-
return options.include.some((path) =>
|
|
24
|
-
FieldPath.equals(path, normalizedIssuePath),
|
|
25
|
-
);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
const { events } = form.controller;
|
|
30
|
-
|
|
31
|
-
return composeFns(
|
|
32
|
-
events.on("fieldIssuesUpdated", (path) => {
|
|
33
|
-
if (options?.include?.includes?.(path) ?? true) {
|
|
34
|
-
renderControl.forceRerender();
|
|
35
|
-
}
|
|
36
|
-
}),
|
|
37
|
-
);
|
|
38
|
-
}, []);
|
|
39
|
-
|
|
40
|
-
return groupBy(observedIssues, (issue) =>
|
|
41
|
-
issue.path == null
|
|
42
|
-
? "$"
|
|
43
|
-
: FieldPath.toStringPath(FieldPath.normalize(issue.path)),
|
|
44
|
-
);
|
|
45
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { FieldPath } from "@goodie-forms/core";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
|
-
import { UseForm } from "../hooks/useForm";
|
|
4
|
-
import { useRenderControl } from "../hooks/useRenderControl";
|
|
5
|
-
import { composeFns } from "../utils/composeFns";
|
|
6
|
-
|
|
7
|
-
export function useFormField<
|
|
8
|
-
TOutput extends object,
|
|
9
|
-
TPath extends FieldPath.Segments,
|
|
10
|
-
>(
|
|
11
|
-
form: UseForm<TOutput>,
|
|
12
|
-
path: TPath,
|
|
13
|
-
bindingConfig?: Parameters<typeof form.controller.bindField<TPath>>[1],
|
|
14
|
-
) {
|
|
15
|
-
const renderControl = useRenderControl();
|
|
16
|
-
|
|
17
|
-
const [field, setField] = useState(() => {
|
|
18
|
-
let field = form.controller.getField(path);
|
|
19
|
-
if (field == null && bindingConfig != null) {
|
|
20
|
-
field = form.controller.bindField(path, bindingConfig);
|
|
21
|
-
}
|
|
22
|
-
return field;
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
const { events } = form.controller;
|
|
27
|
-
|
|
28
|
-
setField(form.controller.getField(path));
|
|
29
|
-
|
|
30
|
-
return composeFns(
|
|
31
|
-
events.on("fieldBound", (_path) => {
|
|
32
|
-
if (!FieldPath.equals(_path, path)) return;
|
|
33
|
-
setField(form.controller.getField(path));
|
|
34
|
-
}),
|
|
35
|
-
events.on("fieldUnbound", (_path) => {
|
|
36
|
-
if (!FieldPath.equals(_path, path)) return;
|
|
37
|
-
setField(undefined);
|
|
38
|
-
}),
|
|
39
|
-
events.on("valueChanged", (changedPath) => {
|
|
40
|
-
if (
|
|
41
|
-
FieldPath.equals(changedPath, path) ||
|
|
42
|
-
FieldPath.isDescendant(changedPath, path)
|
|
43
|
-
) {
|
|
44
|
-
renderControl.forceRerender();
|
|
45
|
-
}
|
|
46
|
-
}),
|
|
47
|
-
events.on("fieldTouchUpdated", (_path) => {
|
|
48
|
-
if (!FieldPath.equals(_path, path)) return;
|
|
49
|
-
renderControl.forceRerender();
|
|
50
|
-
}),
|
|
51
|
-
events.on("fieldDirtyUpdated", (_path) => {
|
|
52
|
-
if (!FieldPath.equals(_path, path)) return;
|
|
53
|
-
renderControl.forceRerender();
|
|
54
|
-
}),
|
|
55
|
-
events.on("fieldIssuesUpdated", (_path) => {
|
|
56
|
-
if (!FieldPath.equals(_path, path)) return;
|
|
57
|
-
renderControl.forceRerender();
|
|
58
|
-
}),
|
|
59
|
-
);
|
|
60
|
-
}, []);
|
|
61
|
-
|
|
62
|
-
return field;
|
|
63
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { FieldPath } from "@goodie-forms/core";
|
|
2
|
-
import { useEffect } from "react";
|
|
3
|
-
import { composeFns } from "../utils/composeFns";
|
|
4
|
-
import type { UseForm } from "./useForm";
|
|
5
|
-
import { useRenderControl } from "./useRenderControl";
|
|
6
|
-
|
|
7
|
-
/** @deprecated */
|
|
8
|
-
export function useFormValuesObserver<
|
|
9
|
-
TOutput extends object,
|
|
10
|
-
TPaths extends FieldPath.Segments[] | undefined = undefined,
|
|
11
|
-
>(
|
|
12
|
-
form: UseForm<TOutput>,
|
|
13
|
-
options?: {
|
|
14
|
-
include?: TPaths;
|
|
15
|
-
},
|
|
16
|
-
) {
|
|
17
|
-
const renderControl = useRenderControl();
|
|
18
|
-
|
|
19
|
-
const observedValues =
|
|
20
|
-
options?.include == null
|
|
21
|
-
? form.controller._data
|
|
22
|
-
: options.include.reduce((data, path) => {
|
|
23
|
-
const value = FieldPath.getValue(
|
|
24
|
-
form.controller._data as TOutput,
|
|
25
|
-
path,
|
|
26
|
-
)!;
|
|
27
|
-
FieldPath.setValue(data, path, value);
|
|
28
|
-
return data;
|
|
29
|
-
}, {} as TOutput);
|
|
30
|
-
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
const { events } = form.controller;
|
|
33
|
-
|
|
34
|
-
return composeFns(
|
|
35
|
-
events.on("valueChanged", (changedPath) => {
|
|
36
|
-
const watchingChange =
|
|
37
|
-
options?.include == null
|
|
38
|
-
? true
|
|
39
|
-
: options.include.some(
|
|
40
|
-
(path) =>
|
|
41
|
-
FieldPath.equals(path, changedPath) ||
|
|
42
|
-
FieldPath.isDescendant(path, changedPath),
|
|
43
|
-
);
|
|
44
|
-
if (watchingChange) renderControl.forceRerender();
|
|
45
|
-
}),
|
|
46
|
-
);
|
|
47
|
-
}, []);
|
|
48
|
-
|
|
49
|
-
return observedValues;
|
|
50
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { startTransition, useRef, useState } from "react";
|
|
2
|
-
|
|
3
|
-
export function useRenderControl() {
|
|
4
|
-
const [, rerender] = useState(0);
|
|
5
|
-
const renderCount = useRef(0);
|
|
6
|
-
const renderScheduled = useRef(false);
|
|
7
|
-
renderCount.current++;
|
|
8
|
-
|
|
9
|
-
const scheduleRerender = () => {
|
|
10
|
-
if (renderScheduled.current) return;
|
|
11
|
-
renderScheduled.current = true;
|
|
12
|
-
|
|
13
|
-
queueMicrotask(() => {
|
|
14
|
-
startTransition(() => {
|
|
15
|
-
rerender((i) => i + 1);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
renderScheduled.current = false;
|
|
19
|
-
});
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
return {
|
|
23
|
-
renderCount: renderCount.current,
|
|
24
|
-
forceRerender: scheduleRerender,
|
|
25
|
-
};
|
|
26
|
-
}
|