diff --git a/src/lib/core/components/Form/Controller.tsx b/src/lib/core/components/Form/Controller.tsx index bdc6fb7d..985c923a 100644 --- a/src/lib/core/components/Form/Controller.tsx +++ b/src/lib/core/components/Form/Controller.tsx @@ -17,6 +17,7 @@ export interface ControllerProps, ) => void) | null; + parentOnUnmount: ((childName: string) => void) | null; } export const Controller = ({ @@ -24,12 +25,21 @@ export const Controller = ({ name, initialValue, parentOnChange, + parentOnUnmount, }: ControllerProps) => { const {tools} = useDynamicFormsCtx(); const {inputEntity, Layout} = useComponents(spec); const render = useRender({name, spec, inputEntity, Layout}); const validate = useValidate(spec); - const renderProps = useField({name, initialValue, spec, validate, tools, parentOnChange}); + const renderProps = useField({ + name, + initialValue, + spec, + validate, + tools, + parentOnChange, + parentOnUnmount, + }); if (_.isString(name) && isCorrectSpec(spec)) { return render(renderProps); diff --git a/src/lib/core/components/Form/DynamicField.tsx b/src/lib/core/components/Form/DynamicField.tsx index f9cb7abe..09774662 100644 --- a/src/lib/core/components/Form/DynamicField.tsx +++ b/src/lib/core/components/Form/DynamicField.tsx @@ -44,6 +44,7 @@ export const DynamicField: React.FC = ({name, spec, config, M spec={spec} name={name} parentOnChange={null} + parentOnUnmount={null} initialValue={_.get(tools.initialValue, name)} /> {watcher} diff --git a/src/lib/core/components/Form/hooks/useField.tsx b/src/lib/core/components/Form/hooks/useField.tsx index fc6a88f4..699faeca 100644 --- a/src/lib/core/components/Form/hooks/useField.tsx +++ b/src/lib/core/components/Form/hooks/useField.tsx @@ -27,6 +27,7 @@ export interface FieldProps { childErrors: Record, ) => void) | null; + parentOnUnmount: ((childName: string) => void) | null; } export const useField = ({ @@ -36,6 +37,7 @@ export const useField = ({ validate: propsValidate, tools, parentOnChange, + parentOnUnmount, }: FieldProps): FieldRenderProps => { const firstRenderRef = React.useRef(true); @@ -249,7 +251,7 @@ export const useField = ({ firstRenderRef.current = false; return () => { - (parentOnChange ? parentOnChange : tools.onChange)(name, state.value, {[name]: false}); + (parentOnUnmount ? parentOnUnmount : tools.onUnmount)(name); }; }, []); diff --git a/src/lib/core/components/Form/hooks/useStore.tsx b/src/lib/core/components/Form/hooks/useStore.tsx index bea8cfc6..24514bac 100644 --- a/src/lib/core/components/Form/hooks/useStore.tsx +++ b/src/lib/core/components/Form/hooks/useStore.tsx @@ -76,6 +76,14 @@ export const useStore = (name: string) => { values: _.set({...store.values}, name, value), errors: errors || {}, })), + onUnmount: (name: string) => + setStore((store) => ({ + ...store, + errors: _.omit( + store.errors, + Object.keys(store.errors).filter((key) => key.startsWith(name)), + ), + })), submitFailed, }), [store.initialValue, setStore, submitFailed], diff --git a/src/lib/core/components/Form/types/context.ts b/src/lib/core/components/Form/types/context.ts index b298cee4..956ddea3 100644 --- a/src/lib/core/components/Form/types/context.ts +++ b/src/lib/core/components/Form/types/context.ts @@ -10,6 +10,7 @@ export interface DynamicFormsContext { tools: { initialValue: FieldObjectValue; onChange: (name: string, value: FieldValue, errors?: Record) => void; + onUnmount: (name: string) => void; submitFailed: boolean; }; } diff --git a/src/lib/kit/components/Inputs/ArrayBase/ArrayBase.tsx b/src/lib/kit/components/Inputs/ArrayBase/ArrayBase.tsx index f3fc695e..036e55a1 100644 --- a/src/lib/kit/components/Inputs/ArrayBase/ArrayBase.tsx +++ b/src/lib/kit/components/Inputs/ArrayBase/ArrayBase.tsx @@ -43,7 +43,7 @@ export const ArrayBase: ArrayInput = ({spec, name, arrayInput, input}) => { if (!spec.items?.required) { if (isArraySpec(spec.items)) { - item = {OBJECT_ARRAY_FLAG: true, OBJECT_ARRAY_CNT: 0}; + item = {[OBJECT_ARRAY_FLAG]: true, [OBJECT_ARRAY_CNT]: 0}; } else if (isObjectSpec(spec.items)) { item = {}; } @@ -86,6 +86,11 @@ export const ArrayBase: ArrayInput = ({spec, name, arrayInput, input}) => { [input.onChange, input.name], ); + const parentOnUnmount = React.useCallback( + (childName: string) => input.onChange((currentValue) => currentValue, {[childName]: false}), + [input.onChange], + ); + const items = React.useMemo( () => keys.map((key, idx) => { @@ -99,13 +104,14 @@ export const ArrayBase: ArrayInput = ({spec, name, arrayInput, input}) => { `]} parentOnChange={parentOnChange} + parentOnUnmount={parentOnUnmount} spec={itemSpec} name={`${name}.<${key}>`} key={`${name}.<${key}>`} /> ); }), - [keys.join(''), name, getItemSpec, parentOnChange, input.value], + [keys.join(''), name, getItemSpec, parentOnChange, parentOnUnmount, input.value], ); if (!itemSpecCorrect) { diff --git a/src/lib/kit/components/Inputs/CardOneOf/CardOneOf.tsx b/src/lib/kit/components/Inputs/CardOneOf/CardOneOf.tsx index 800ef212..0103b7cc 100644 --- a/src/lib/kit/components/Inputs/CardOneOf/CardOneOf.tsx +++ b/src/lib/kit/components/Inputs/CardOneOf/CardOneOf.tsx @@ -57,6 +57,11 @@ export const CardOneOf: ObjectIndependentInput = (props) => { [input.onChange, input.name], ); + const parentOnUnmount = React.useCallback( + (childName: string) => input.onChange((currentValue) => currentValue, {[childName]: false}), + [input.onChange], + ); + useErrorChecker({name, meta, open, setOpen}); return ( @@ -74,6 +79,7 @@ export const CardOneOf: ObjectIndependentInput = (props) => { spec={specProperties[oneOfValue]} name={`${name}.${oneOfValue}`} parentOnChange={parentOnChange} + parentOnUnmount={parentOnUnmount} key={`${name}.${oneOfValue}`} /> ) : null} diff --git a/src/lib/kit/components/Inputs/ObjectBase/ObjectBase.tsx b/src/lib/kit/components/Inputs/ObjectBase/ObjectBase.tsx index abe89e9c..f6947f4b 100644 --- a/src/lib/kit/components/Inputs/ObjectBase/ObjectBase.tsx +++ b/src/lib/kit/components/Inputs/ObjectBase/ObjectBase.tsx @@ -47,6 +47,11 @@ export const ObjectBase: ObjectIndependentInput = ({spec, name, Layout, ...restP [restProps.input.onChange, restProps.input.name], ); + const parentOnUnmount = React.useCallback( + (childName: string) => + restProps.input.onChange((currentValue) => currentValue, {[childName]: false}), + [restProps.input.onChange], + ); const content = React.useMemo(() => { if (!_.isObjectLike(spec.properties) || !Object.keys(spec.properties || {}).length) { return null; @@ -67,13 +72,22 @@ export const ObjectBase: ObjectIndependentInput = ({spec, name, Layout, ...restP spec={specProperties[property]} name={`${name ? name + '.' : ''}${property}`} parentOnChange={parentOnChange} + parentOnUnmount={parentOnUnmount} key={`${name ? name + '.' : ''}${property}`} /> ) : null, )} ); - }, [spec.properties, spec.viewSpec.order, name, restProps.input.value, addBtn, parentOnChange]); + }, [ + spec.properties, + spec.viewSpec.order, + name, + restProps.input.value, + addBtn, + parentOnChange, + parentOnUnmount, + ]); if (!Layout || !content) { return content; diff --git a/src/lib/kit/components/Inputs/OneOf/OneOf.tsx b/src/lib/kit/components/Inputs/OneOf/OneOf.tsx index ecec2fcd..338db945 100644 --- a/src/lib/kit/components/Inputs/OneOf/OneOf.tsx +++ b/src/lib/kit/components/Inputs/OneOf/OneOf.tsx @@ -27,6 +27,12 @@ export const OneOf: ObjectIndependentInput = (props) => { [props.input.onChange, props.input.name], ); + const parentOnUnmount = React.useCallback( + (childName: string) => + props.input.onChange((currentValue) => currentValue, {[childName]: false}), + [props.input.onChange], + ); + return (
{specProperties[oneOfValue] ? ( @@ -36,6 +42,7 @@ export const OneOf: ObjectIndependentInput = (props) => { spec={specProperties[oneOfValue]} name={`${props.name}.${oneOfValue}`} parentOnChange={parentOnChange} + parentOnUnmount={parentOnUnmount} key={`${props.name}.${oneOfValue}`} /> diff --git a/src/lib/kit/components/Inputs/OneOfCard/OneOfCard.tsx b/src/lib/kit/components/Inputs/OneOfCard/OneOfCard.tsx index 5e2a7a57..12a90ddf 100644 --- a/src/lib/kit/components/Inputs/OneOfCard/OneOfCard.tsx +++ b/src/lib/kit/components/Inputs/OneOfCard/OneOfCard.tsx @@ -74,6 +74,11 @@ export const OneOfCard: ObjectIndependentInput = (props) => { [input.onChange, input.name], ); + const parentOnUnmount = React.useCallback( + (childName: string) => input.onChange((currentValue) => currentValue, {[childName]: false}), + [input.onChange], + ); + useErrorChecker({name, meta, open, setOpen}); return ( @@ -92,6 +97,7 @@ export const OneOfCard: ObjectIndependentInput = (props) => { spec={specProperties[oneOfValue]} name={`${name}.${oneOfValue}`} parentOnChange={parentOnChange} + parentOnUnmount={parentOnUnmount} key={`${name}.${oneOfValue}`} /> ) : null} diff --git a/src/lib/kit/components/Inputs/Secret/Secret.tsx b/src/lib/kit/components/Inputs/Secret/Secret.tsx index 55f284dc..b7f53032 100644 --- a/src/lib/kit/components/Inputs/Secret/Secret.tsx +++ b/src/lib/kit/components/Inputs/Secret/Secret.tsx @@ -27,6 +27,11 @@ export const Secret: ObjectIndependentInput = ({spec, name, input}) => { [input.onChange, input.name], ); + const parentOnUnmount = React.useCallback( + (childName: string) => input.onChange((currentValue) => currentValue, {[childName]: false}), + [input.onChange], + ); + if (!_.isObjectLike(spec.properties)) { return null; } @@ -43,6 +48,7 @@ export const Secret: ObjectIndependentInput = ({spec, name, input}) => { spec={specProperties[SECRET_PROPERTY_NAME]} name={`${name}.${SECRET_PROPERTY_NAME}`} parentOnChange={parentOnChange} + parentOnUnmount={parentOnUnmount} key={`${name}.${SECRET_PROPERTY_NAME}`} /> ); diff --git a/src/lib/kit/components/Inputs/TableArrayInput/TableArrayInput.tsx b/src/lib/kit/components/Inputs/TableArrayInput/TableArrayInput.tsx index 2642fe8c..9bc18bd4 100644 --- a/src/lib/kit/components/Inputs/TableArrayInput/TableArrayInput.tsx +++ b/src/lib/kit/components/Inputs/TableArrayInput/TableArrayInput.tsx @@ -67,6 +67,11 @@ export const TableArrayInput: ArrayInput = ({spec, name, arrayInput, input}) => [input.onChange, input.name], ); + const parentOnUnmount = React.useCallback( + (childName: string) => input.onChange((currentValue) => currentValue, {[childName]: false}), + [input.onChange], + ); + const columns = React.useMemo(() => { const { items, @@ -129,6 +134,7 @@ export const TableArrayInput: ArrayInput = ({spec, name, arrayInput, input}) => spec={entitySpec} name={`${name}.<${key}>.${property}`} parentOnChange={parentOnChange} + parentOnUnmount={parentOnUnmount} />
); @@ -136,7 +142,7 @@ export const TableArrayInput: ArrayInput = ({spec, name, arrayInput, input}) => })); return [idxColumn, ...columns, removeColumn]; - }, [name, spec, onItemRemove, parentOnChange, input.value]); + }, [name, spec, onItemRemove, parentOnChange, parentOnUnmount, input.value]); if (!columns) { return null; diff --git a/src/stories/components/InputPreview/SpecSelector.tsx b/src/stories/components/InputPreview/SpecSelector.tsx index c1f69ee3..8bc5fad4 100644 --- a/src/stories/components/InputPreview/SpecSelector.tsx +++ b/src/stories/components/InputPreview/SpecSelector.tsx @@ -62,6 +62,12 @@ export const SpecSelector: ObjectIndependentInput = (props) => { [props.input.onChange, props.input.name], ); + const parentOnUnmount = React.useCallback( + (childName: string) => + props.input.onChange((currentValue) => currentValue, {[childName]: false}), + [props.input.onChange], + ); + const typeSpec = React.useMemo( () => ({ type: SpecTypes.String, @@ -87,6 +93,7 @@ export const SpecSelector: ObjectIndependentInput = (props) => { spec={optionsSpec} name={`${props.name}.${type}`} parentOnChange={parentOnChange} + parentOnUnmount={parentOnUnmount} key={`${props.name}.${type}`} />