import { isEqual } from 'lodash'; import React, { FormEvent, PropsWithChildren, useEffect, useRef, useState } from 'react'; import { useCallbackRef } from '../../hooks/use-callback-ref'; import { BoxFormProps, SilkeBox } from '../silke-box'; import { ErrorMap, hasErrors, SilkeObjectField } from '../silke-object-field'; export type SilkeFormProps = { children?: React.ReactNode; value?: T; styles?: React.CSSProperties; className?: string; /** errors can be used to send in external errors to specific fields */ errors?: { [key in keyof T]?: string }; onChange?: (change: T, valid?: boolean) => void; onSubmit?: (form: T, valid?: boolean) => void; onErrors?: (errors: ErrorMap) => void; } & Omit; export function SilkeForm({ children, value, errors, onChange, onSubmit, onErrors, ...rest }: SilkeFormProps) { const [internalValue, setInternalValue] = useState(value || ({} as T)); const prevValueRef = useRef(internalValue); const errorsRef = useRef>({}); const changeCbRef = useCallbackRef(onChange); const submitCbRef = useCallbackRef(onSubmit); useEffect(() => { if (value && !isEqual(value, internalValue)) { prevValueRef.current = value; setInternalValue(value); } }, [value]); const handleChange = (formData: T) => { setInternalValue(formData); if (changeCbRef.current) changeCbRef.current(formData, !hasErrors(errorsRef.current)); }; const handleSubmit = (e: FormEvent) => { e.preventDefault(); e.stopPropagation(); if (submitCbRef.current) submitCbRef.current(internalValue, !hasErrors(errorsRef.current)); }; const handleReset = (e: FormEvent) => { e.preventDefault(); }; const handleErrors = (errors: ErrorMap) => { errorsRef.current = errors; if (onErrors) onErrors(errors); }; return ( {children} ); } export const SilkeFormFields = (props: PropsWithChildren) => { return ( {props.children} ); };