@gravity-ui/dynamic-forms 3.1.0 → 3.2.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.
Files changed (38) hide show
  1. package/build/cjs/lib/core/components/Form/Controller.js +12 -3
  2. package/build/cjs/lib/core/components/Form/DynamicField.js +6 -4
  3. package/build/cjs/lib/core/components/Form/constants.js +2 -1
  4. package/build/cjs/lib/core/components/Form/hooks/index.js +3 -0
  5. package/build/cjs/lib/core/components/Form/hooks/useField.js +30 -21
  6. package/build/cjs/lib/core/components/Form/hooks/useMutateDFState.js +6 -0
  7. package/build/cjs/lib/core/components/Form/hooks/useMutators.js +23 -0
  8. package/build/cjs/lib/core/components/Form/hooks/useStoreValue.js +6 -0
  9. package/build/cjs/lib/core/components/Form/index.js +4 -0
  10. package/build/cjs/lib/core/components/Form/types/index.js +1 -0
  11. package/build/cjs/lib/core/components/Form/types/mutators.js +2 -0
  12. package/build/cjs/lib/core/components/Form/utils/common.js +5 -1
  13. package/build/esm/lib/core/components/Form/Controller.d.ts +1 -1
  14. package/build/esm/lib/core/components/Form/Controller.js +12 -3
  15. package/build/esm/lib/core/components/Form/DynamicField.d.ts +2 -2
  16. package/build/esm/lib/core/components/Form/DynamicField.js +7 -5
  17. package/build/esm/lib/core/components/Form/constants.d.ts +1 -0
  18. package/build/esm/lib/core/components/Form/constants.js +1 -0
  19. package/build/esm/lib/core/components/Form/hooks/index.d.ts +3 -0
  20. package/build/esm/lib/core/components/Form/hooks/index.js +3 -0
  21. package/build/esm/lib/core/components/Form/hooks/useField.d.ts +3 -3
  22. package/build/esm/lib/core/components/Form/hooks/useField.js +32 -23
  23. package/build/esm/lib/core/components/Form/hooks/useMutateDFState.d.ts +1 -0
  24. package/build/esm/lib/core/components/Form/hooks/useMutateDFState.js +2 -0
  25. package/build/esm/lib/core/components/Form/hooks/useMutators.d.ts +5 -0
  26. package/build/esm/lib/core/components/Form/hooks/useMutators.js +18 -0
  27. package/build/esm/lib/core/components/Form/hooks/useStoreValue.d.ts +1 -0
  28. package/build/esm/lib/core/components/Form/hooks/useStoreValue.js +2 -0
  29. package/build/esm/lib/core/components/Form/index.d.ts +1 -0
  30. package/build/esm/lib/core/components/Form/index.js +1 -0
  31. package/build/esm/lib/core/components/Form/types/context.d.ts +4 -2
  32. package/build/esm/lib/core/components/Form/types/index.d.ts +1 -0
  33. package/build/esm/lib/core/components/Form/types/index.js +1 -0
  34. package/build/esm/lib/core/components/Form/types/mutators.d.ts +7 -0
  35. package/build/esm/lib/core/components/Form/types/mutators.js +1 -0
  36. package/build/esm/lib/core/components/Form/utils/common.d.ts +3 -0
  37. package/build/esm/lib/core/components/Form/utils/common.js +2 -0
  38. package/package.json +1 -1
@@ -2,10 +2,19 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Controller = void 0;
4
4
  const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
5
6
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
7
+ const constants_1 = require("./constants");
6
8
  const hooks_1 = require("./hooks");
7
- const Controller = ({ spec, name, value, parentOnChange, parentOnUnmount, }) => {
8
- const { tools, externalErrors, __mirror } = (0, hooks_1.useDynamicFormsCtx)();
9
+ const Controller = ({ spec: _spec, name, value, parentOnChange, parentOnUnmount, }) => {
10
+ const { tools, mutators, __mirror } = (0, hooks_1.useDynamicFormsCtx)();
11
+ const spec = react_1.default.useMemo(() => {
12
+ const specMutator = lodash_1.default.get(mutators.spec, name, constants_1.EMPTY_MUTATOR);
13
+ if (specMutator !== constants_1.EMPTY_MUTATOR) {
14
+ return lodash_1.default.merge(_spec, specMutator);
15
+ }
16
+ return _spec;
17
+ }, [_spec, mutators.spec, name]);
9
18
  const { inputEntity, Layout } = (0, hooks_1.useComponents)(spec);
10
19
  const render = (0, hooks_1.useRender)({ name, spec, inputEntity, Layout });
11
20
  const validate = (0, hooks_1.useValidate)(spec);
@@ -18,7 +27,7 @@ const Controller = ({ spec, name, value, parentOnChange, parentOnUnmount, }) =>
18
27
  tools,
19
28
  parentOnChange,
20
29
  parentOnUnmount,
21
- externalErrors,
30
+ mutators,
22
31
  });
23
32
  const withSearch = (0, hooks_1.useSearch)(spec, renderProps.input.value, name);
24
33
  (0, hooks_1.useControllerMirror)(name, {
@@ -9,20 +9,22 @@ const helpers_1 = require("../../helpers");
9
9
  const Controller_1 = require("./Controller");
10
10
  const hooks_1 = require("./hooks");
11
11
  const utils_1 = require("./utils");
12
- const DynamicField = ({ name, spec, config, Monaco, generateRandomValue, search, withoutInsertFFDebounce, errors: externalErrors, __mirror, }) => {
12
+ const DynamicField = ({ name, spec, config, Monaco, generateRandomValue, search, withoutInsertFFDebounce, mutators: externalMutators, __mirror, }) => {
13
13
  const DynamicFormsCtx = (0, hooks_1.useCreateContext)();
14
14
  const SearchContext = (0, hooks_1.useCreateSearchContext)();
15
15
  const { tools, store } = (0, hooks_1.useStore)(name);
16
16
  const watcher = (0, hooks_1.useIntegrationFF)(store, withoutInsertFFDebounce);
17
+ const { mutators, mutateDFState } = (0, hooks_1.useMutators)(externalMutators);
17
18
  const { store: searchStore, setField, removeField, isHiddenField } = (0, hooks_1.useSearchStore)();
18
19
  const context = react_1.default.useMemo(() => ({
19
20
  config,
20
21
  Monaco: (0, react_is_1.isValidElementType)(Monaco) ? Monaco : undefined,
21
22
  generateRandomValue,
22
- tools,
23
- externalErrors,
23
+ tools: Object.assign(Object.assign({}, tools), { mutateDFState }),
24
+ store,
25
+ mutators,
24
26
  __mirror,
25
- }), [tools, config, Monaco, __mirror, generateRandomValue, externalErrors]);
27
+ }), [tools, config, Monaco, __mirror, generateRandomValue, mutators, mutateDFState, store]);
26
28
  const searchContext = react_1.default.useMemo(() => ({
27
29
  setField,
28
30
  removeField,
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SPEC_TYPE_FOR_GENERATE_BUTTON = exports.OBJECT_ARRAY_CNT = exports.OBJECT_ARRAY_FLAG = void 0;
3
+ exports.EMPTY_MUTATOR = exports.SPEC_TYPE_FOR_GENERATE_BUTTON = exports.OBJECT_ARRAY_CNT = exports.OBJECT_ARRAY_FLAG = void 0;
4
4
  exports.OBJECT_ARRAY_FLAG = '____arr-obj';
5
5
  exports.OBJECT_ARRAY_CNT = '____arr-obj-cnt';
6
6
  exports.SPEC_TYPE_FOR_GENERATE_BUTTON = ['base', 'password', 'textarea'];
7
+ exports.EMPTY_MUTATOR = '____empty-mutator';
@@ -9,8 +9,11 @@ tslib_1.__exportStar(require("./useDynamicFormsCtx"), exports);
9
9
  tslib_1.__exportStar(require("./useField"), exports);
10
10
  tslib_1.__exportStar(require("./useGenerateRandomValue"), exports);
11
11
  tslib_1.__exportStar(require("./useIntegrationFF"), exports);
12
+ tslib_1.__exportStar(require("./useMutateDFState"), exports);
13
+ tslib_1.__exportStar(require("./useMutators"), exports);
12
14
  tslib_1.__exportStar(require("./useRender"), exports);
13
15
  tslib_1.__exportStar(require("./useStore"), exports);
16
+ tslib_1.__exportStar(require("./useStoreValue"), exports);
14
17
  tslib_1.__exportStar(require("./useValidate"), exports);
15
18
  tslib_1.__exportStar(require("./useMonaco"), exports);
16
19
  tslib_1.__exportStar(require("./useSearchStore"), exports);
@@ -7,11 +7,15 @@ const lodash_1 = tslib_1.__importDefault(require("lodash"));
7
7
  const helpers_1 = require("../../../helpers");
8
8
  const constants_1 = require("../constants");
9
9
  const utils_1 = require("../utils");
10
- const useField = ({ name, spec, initialValue, value: externalValue, validate: propsValidate, tools, parentOnChange, parentOnUnmount: externalParentOnUnmount, externalErrors, }) => {
10
+ const useField = ({ name, spec, initialValue, value: externalValue, validate: propsValidate, tools, parentOnChange, parentOnUnmount: externalParentOnUnmount, mutators, }) => {
11
11
  const firstRenderRef = react_1.default.useRef(true);
12
12
  const validate = react_1.default.useCallback((value) => propsValidate === null || propsValidate === void 0 ? void 0 : propsValidate((0, utils_1.transformArrOut)(value)), [propsValidate]);
13
13
  const [state, setState] = react_1.default.useState(() => {
14
+ const valueMutator = lodash_1.default.get(mutators.values, name, constants_1.EMPTY_MUTATOR);
14
15
  let value = lodash_1.default.cloneDeep(externalValue);
16
+ if ((0, utils_1.isValueMutatorCorrect)(valueMutator, spec) && valueMutator !== constants_1.EMPTY_MUTATOR) {
17
+ value = valueMutator;
18
+ }
15
19
  if (lodash_1.default.isNil(value)) {
16
20
  if (spec.defaultValue) {
17
21
  value = (0, utils_1.transformArrIn)(spec.defaultValue);
@@ -27,13 +31,11 @@ const useField = ({ name, spec, initialValue, value: externalValue, validate: pr
27
31
  }
28
32
  }
29
33
  }
30
- let externalError = lodash_1.default.get(externalErrors, name);
31
- if (!(lodash_1.default.isString(externalError) ||
32
- lodash_1.default.isBoolean(externalError) ||
33
- lodash_1.default.isUndefined(externalError))) {
34
- externalError = undefined;
34
+ let errorMutator = lodash_1.default.get(mutators.errors, name);
35
+ if (!(0, utils_1.isErrorMutatorCorrect)(errorMutator)) {
36
+ errorMutator = undefined;
35
37
  }
36
- const error = (validate === null || validate === void 0 ? void 0 : validate(value)) || externalError;
38
+ const error = (validate === null || validate === void 0 ? void 0 : validate(value)) || errorMutator;
37
39
  const dirty = !lodash_1.default.isEqual(value, initialValue);
38
40
  return {
39
41
  active: false,
@@ -49,11 +51,11 @@ const useField = ({ name, spec, initialValue, value: externalValue, validate: pr
49
51
  childErrors: {},
50
52
  };
51
53
  });
52
- const { onChange, onDrop } = react_1.default.useMemo(() => {
53
- const onChange = (valOrSetter, childErrors) => {
54
+ const { onChange, onLocalChange, onDrop } = react_1.default.useMemo(() => {
55
+ const onLocalChange = (valOrSetter, childErrors, errorMutator) => {
54
56
  setState((state) => {
55
57
  const _value = lodash_1.default.isFunction(valOrSetter) ? valOrSetter(state.value) : valOrSetter;
56
- const error = validate === null || validate === void 0 ? void 0 : validate(_value);
58
+ const error = (validate === null || validate === void 0 ? void 0 : validate(_value)) || errorMutator;
57
59
  let value = (0, utils_1.transformArrIn)(_value);
58
60
  if ((0, helpers_1.isNumberSpec)(spec) && !error) {
59
61
  value = (value ? Number(value) : undefined);
@@ -69,6 +71,7 @@ const useField = ({ name, spec, initialValue, value: externalValue, validate: pr
69
71
  return Object.assign(Object.assign({}, state), { dirty: !lodash_1.default.isEqual(value, initialValue), error, invalid: Boolean(error), modified: true, pristine: value === initialValue, touched: true, valid: !error, value, visited: true, childErrors: newChildErrors });
70
72
  });
71
73
  };
74
+ const onChange = (valOrSetter, childErrors) => onLocalChange(valOrSetter, childErrors);
72
75
  const onDrop = () => {
73
76
  if ((0, utils_1.isArrayItem)(name)) {
74
77
  (externalParentOnUnmount ? externalParentOnUnmount : tools.onUnmount)(name);
@@ -77,7 +80,7 @@ const useField = ({ name, spec, initialValue, value: externalValue, validate: pr
77
80
  onChange(undefined, { [name]: false });
78
81
  }
79
82
  };
80
- return { onChange, onDrop };
83
+ return { onChange, onLocalChange, onDrop };
81
84
  }, [initialValue, setState, name, validate, spec, externalParentOnUnmount, tools.onUnmount]);
82
85
  const onBlur = react_1.default.useCallback(() => {
83
86
  setState((state) => (Object.assign(Object.assign({}, state), { active: false, touched: true })));
@@ -145,17 +148,23 @@ const useField = ({ name, spec, initialValue, value: externalValue, validate: pr
145
148
  }
146
149
  }, [state.value]);
147
150
  react_1.default.useEffect(() => {
148
- const externalError = lodash_1.default.get(externalErrors, name);
149
- if (!firstRenderRef.current &&
150
- (lodash_1.default.isString(externalError) ||
151
- lodash_1.default.isBoolean(externalError) ||
152
- lodash_1.default.isUndefined(externalError)) &&
153
- state.error !== externalError &&
154
- !(state.error && !externalError)) {
155
- setState(Object.assign(Object.assign({}, state), { error: externalError }));
156
- (parentOnChange ? parentOnChange : tools.onChange)(name, state.value, Object.assign(Object.assign({}, state.childErrors), { [name]: externalError }));
151
+ if (!firstRenderRef.current) {
152
+ const valueMutator = lodash_1.default.get(mutators.values, name, constants_1.EMPTY_MUTATOR);
153
+ let errorMutator = lodash_1.default.get(mutators.errors, name);
154
+ if (!(0, utils_1.isErrorMutatorCorrect)(errorMutator)) {
155
+ errorMutator = undefined;
156
+ }
157
+ if ((0, utils_1.isValueMutatorCorrect)(valueMutator, spec) &&
158
+ valueMutator !== state.value &&
159
+ valueMutator !== constants_1.EMPTY_MUTATOR) {
160
+ onLocalChange(valueMutator, undefined, errorMutator);
161
+ }
162
+ else if (state.error !== errorMutator && !(state.error && !errorMutator)) {
163
+ setState(Object.assign(Object.assign({}, state), { error: errorMutator }));
164
+ (parentOnChange ? parentOnChange : tools.onChange)(name, state.value, Object.assign(Object.assign({}, state.childErrors), { [name]: errorMutator }));
165
+ }
157
166
  }
158
- }, [externalErrors]);
167
+ }, [mutators]);
159
168
  react_1.default.useEffect(() => {
160
169
  firstRenderRef.current = false;
161
170
  return () => {
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useMutateDFState = void 0;
4
+ const useDynamicFormsCtx_1 = require("./useDynamicFormsCtx");
5
+ const useMutateDFState = () => (0, useDynamicFormsCtx_1.useDynamicFormsCtx)().tools.mutateDFState;
6
+ exports.useMutateDFState = useMutateDFState;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useMutators = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
+ const lodash_1 = tslib_1.__importDefault(require("lodash"));
7
+ const useMutators = (externalMutators) => {
8
+ const firstRenderRef = react_1.default.useRef(true);
9
+ const [store, setStore] = react_1.default.useState(externalMutators || {});
10
+ const mutateDFState = react_1.default.useCallback((mutators) => {
11
+ setStore((store) => (Object.assign(Object.assign(Object.assign(Object.assign({}, store), (mutators.errors ? { errors: lodash_1.default.merge(store.errors, mutators.errors) } : {})), (mutators.values ? { values: lodash_1.default.merge(store.values, mutators.values) } : {})), (mutators.spec ? { spec: lodash_1.default.merge(store.spec, mutators.spec) } : {}))));
12
+ }, [setStore]);
13
+ react_1.default.useEffect(() => {
14
+ if (firstRenderRef.current) {
15
+ firstRenderRef.current = false;
16
+ }
17
+ else if (externalMutators) {
18
+ mutateDFState(externalMutators);
19
+ }
20
+ }, [externalMutators]);
21
+ return { mutators: store, mutateDFState };
22
+ };
23
+ exports.useMutators = useMutators;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useStoreValue = void 0;
4
+ const useDynamicFormsCtx_1 = require("./useDynamicFormsCtx");
5
+ const useStoreValue = () => (0, useDynamicFormsCtx_1.useDynamicFormsCtx)().store;
6
+ exports.useStoreValue = useStoreValue;
@@ -1,8 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useStoreValue = exports.useMutateDFState = void 0;
3
4
  const tslib_1 = require("tslib");
4
5
  tslib_1.__exportStar(require("./constants"), exports);
5
6
  tslib_1.__exportStar(require("./Controller"), exports);
6
7
  tslib_1.__exportStar(require("./DynamicField"), exports);
8
+ var hooks_1 = require("./hooks");
9
+ Object.defineProperty(exports, "useMutateDFState", { enumerable: true, get: function () { return hooks_1.useMutateDFState; } });
10
+ Object.defineProperty(exports, "useStoreValue", { enumerable: true, get: function () { return hooks_1.useStoreValue; } });
7
11
  tslib_1.__exportStar(require("./types"), exports);
8
12
  tslib_1.__exportStar(require("./utils"), exports);
@@ -9,6 +9,7 @@ tslib_1.__exportStar(require("./field"), exports);
9
9
  tslib_1.__exportStar(require("./input"), exports);
10
10
  tslib_1.__exportStar(require("./layout"), exports);
11
11
  tslib_1.__exportStar(require("./mirror"), exports);
12
+ tslib_1.__exportStar(require("./mutators"), exports);
12
13
  tslib_1.__exportStar(require("./number"), exports);
13
14
  tslib_1.__exportStar(require("./object"), exports);
14
15
  tslib_1.__exportStar(require("./store"), exports);
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.withGenerateButton = exports.isArrayItem = exports.transformArrOut = exports.transformArrIn = exports.isCorrectConfig = void 0;
3
+ exports.isValueMutatorCorrect = exports.isErrorMutatorCorrect = exports.withGenerateButton = exports.isArrayItem = exports.transformArrOut = exports.transformArrIn = exports.isCorrectConfig = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
6
6
  const constants_1 = require("../../../constants");
@@ -54,3 +54,7 @@ const withGenerateButton = (spec) => (0, helpers_1.isStringSpec)(spec) &&
54
54
  constants_2.SPEC_TYPE_FOR_GENERATE_BUTTON.includes(spec.viewSpec.type) &&
55
55
  spec.viewSpec.generateRandomValueButton;
56
56
  exports.withGenerateButton = withGenerateButton;
57
+ const isErrorMutatorCorrect = (errorMutator) => lodash_1.default.isString(errorMutator) || lodash_1.default.isBoolean(errorMutator) || lodash_1.default.isUndefined(errorMutator);
58
+ exports.isErrorMutatorCorrect = isErrorMutatorCorrect;
59
+ const isValueMutatorCorrect = (valueMutator, spec) => typeof valueMutator === spec.type || (lodash_1.default.isArray(valueMutator) && spec.type === constants_1.SpecTypes.Array);
60
+ exports.isValueMutatorCorrect = isValueMutatorCorrect;
@@ -7,4 +7,4 @@ export interface ControllerProps<Value extends FieldValue, SpecType extends Spec
7
7
  parentOnChange: ((childName: string, childValue: FieldValue, childErrors: Record<string, ValidateError>) => void) | null;
8
8
  parentOnUnmount: ((childName: string) => void) | null;
9
9
  }
10
- export declare const Controller: <Value extends FieldValue, SpecType extends Spec>({ spec, name, value, parentOnChange, parentOnUnmount, }: ControllerProps<Value, SpecType>) => JSX.Element | null;
10
+ export declare const Controller: <Value extends FieldValue, SpecType extends Spec>({ spec: _spec, name, value, parentOnChange, parentOnUnmount, }: ControllerProps<Value, SpecType>) => JSX.Element | null;
@@ -1,7 +1,16 @@
1
+ import React from 'react';
1
2
  import _ from 'lodash';
3
+ import { EMPTY_MUTATOR } from './constants';
2
4
  import { useComponents, useControllerMirror, useDynamicFormsCtx, useField, useRender, useSearch, useValidate, } from './hooks';
3
- export const Controller = ({ spec, name, value, parentOnChange, parentOnUnmount, }) => {
4
- const { tools, externalErrors, __mirror } = useDynamicFormsCtx();
5
+ export const Controller = ({ spec: _spec, name, value, parentOnChange, parentOnUnmount, }) => {
6
+ const { tools, mutators, __mirror } = useDynamicFormsCtx();
7
+ const spec = React.useMemo(() => {
8
+ const specMutator = _.get(mutators.spec, name, EMPTY_MUTATOR);
9
+ if (specMutator !== EMPTY_MUTATOR) {
10
+ return _.merge(_spec, specMutator);
11
+ }
12
+ return _spec;
13
+ }, [_spec, mutators.spec, name]);
5
14
  const { inputEntity, Layout } = useComponents(spec);
6
15
  const render = useRender({ name, spec, inputEntity, Layout });
7
16
  const validate = useValidate(spec);
@@ -14,7 +23,7 @@ export const Controller = ({ spec, name, value, parentOnChange, parentOnUnmount,
14
23
  tools,
15
24
  parentOnChange,
16
25
  parentOnUnmount,
17
- externalErrors,
26
+ mutators,
18
27
  });
19
28
  const withSearch = useSearch(spec, renderProps.input.value, name);
20
29
  useControllerMirror(name, {
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { MonacoEditorProps } from 'react-monaco-editor/lib/types';
3
3
  import { Spec, StringSpec } from '../../types';
4
- import { BaseValidateError, DynamicFormConfig, FieldValue, WonderMirror } from './types';
4
+ import { DynamicFormConfig, DynamicFormMutators, FieldValue, WonderMirror } from './types';
5
5
  export interface DynamicFieldProps {
6
6
  name: string;
7
7
  spec: Spec;
@@ -10,7 +10,7 @@ export interface DynamicFieldProps {
10
10
  search?: string | ((spec: Spec, input: FieldValue, name: string) => boolean);
11
11
  generateRandomValue?: (spec: StringSpec) => string;
12
12
  withoutInsertFFDebounce?: boolean;
13
- errors?: Record<string, BaseValidateError>;
13
+ mutators?: DynamicFormMutators;
14
14
  __mirror?: WonderMirror;
15
15
  }
16
16
  export declare const DynamicField: React.FC<DynamicFieldProps>;
@@ -3,22 +3,24 @@ import _ from 'lodash';
3
3
  import { isValidElementType } from 'react-is';
4
4
  import { isCorrectSpec } from '../../helpers';
5
5
  import { Controller } from './Controller';
6
- import { useCreateContext, useCreateSearchContext, useDynamicFieldMirror, useIntegrationFF, useSearchStore, useStore, } from './hooks';
6
+ import { useCreateContext, useCreateSearchContext, useDynamicFieldMirror, useIntegrationFF, useMutators, useSearchStore, useStore, } from './hooks';
7
7
  import { getDefaultSearchFunction, isCorrectConfig } from './utils';
8
- export const DynamicField = ({ name, spec, config, Monaco, generateRandomValue, search, withoutInsertFFDebounce, errors: externalErrors, __mirror, }) => {
8
+ export const DynamicField = ({ name, spec, config, Monaco, generateRandomValue, search, withoutInsertFFDebounce, mutators: externalMutators, __mirror, }) => {
9
9
  const DynamicFormsCtx = useCreateContext();
10
10
  const SearchContext = useCreateSearchContext();
11
11
  const { tools, store } = useStore(name);
12
12
  const watcher = useIntegrationFF(store, withoutInsertFFDebounce);
13
+ const { mutators, mutateDFState } = useMutators(externalMutators);
13
14
  const { store: searchStore, setField, removeField, isHiddenField } = useSearchStore();
14
15
  const context = React.useMemo(() => ({
15
16
  config,
16
17
  Monaco: isValidElementType(Monaco) ? Monaco : undefined,
17
18
  generateRandomValue,
18
- tools,
19
- externalErrors,
19
+ tools: Object.assign(Object.assign({}, tools), { mutateDFState }),
20
+ store,
21
+ mutators,
20
22
  __mirror,
21
- }), [tools, config, Monaco, __mirror, generateRandomValue, externalErrors]);
23
+ }), [tools, config, Monaco, __mirror, generateRandomValue, mutators, mutateDFState, store]);
22
24
  const searchContext = React.useMemo(() => ({
23
25
  setField,
24
26
  removeField,
@@ -1,3 +1,4 @@
1
1
  export declare const OBJECT_ARRAY_FLAG = "____arr-obj";
2
2
  export declare const OBJECT_ARRAY_CNT = "____arr-obj-cnt";
3
3
  export declare const SPEC_TYPE_FOR_GENERATE_BUTTON: string[];
4
+ export declare const EMPTY_MUTATOR = "____empty-mutator";
@@ -1,3 +1,4 @@
1
1
  export const OBJECT_ARRAY_FLAG = '____arr-obj';
2
2
  export const OBJECT_ARRAY_CNT = '____arr-obj-cnt';
3
3
  export const SPEC_TYPE_FOR_GENERATE_BUTTON = ['base', 'password', 'textarea'];
4
+ export const EMPTY_MUTATOR = '____empty-mutator';
@@ -6,8 +6,11 @@ export * from './useDynamicFormsCtx';
6
6
  export * from './useField';
7
7
  export * from './useGenerateRandomValue';
8
8
  export * from './useIntegrationFF';
9
+ export * from './useMutateDFState';
10
+ export * from './useMutators';
9
11
  export * from './useRender';
10
12
  export * from './useStore';
13
+ export * from './useStoreValue';
11
14
  export * from './useValidate';
12
15
  export * from './useMonaco';
13
16
  export * from './useSearchStore';
@@ -6,8 +6,11 @@ export * from './useDynamicFormsCtx';
6
6
  export * from './useField';
7
7
  export * from './useGenerateRandomValue';
8
8
  export * from './useIntegrationFF';
9
+ export * from './useMutateDFState';
10
+ export * from './useMutators';
9
11
  export * from './useRender';
10
12
  export * from './useStore';
13
+ export * from './useStoreValue';
11
14
  export * from './useValidate';
12
15
  export * from './useMonaco';
13
16
  export * from './useSearchStore';
@@ -1,5 +1,5 @@
1
1
  import { Spec } from '../../../types';
2
- import { BaseValidateError, DynamicFormsContext, FieldRenderProps, FieldValue, ValidateError } from '../types';
2
+ import { DynamicFormMutators, DynamicFormsContext, FieldRenderProps, FieldValue, ValidateError } from '../types';
3
3
  export interface UseFieldProps<Value extends FieldValue, SpecType extends Spec> {
4
4
  name: string;
5
5
  spec: SpecType;
@@ -9,6 +9,6 @@ export interface UseFieldProps<Value extends FieldValue, SpecType extends Spec>
9
9
  tools: DynamicFormsContext['tools'];
10
10
  parentOnChange: ((childName: string, childValue: FieldValue, childErrors: Record<string, ValidateError>) => void) | null;
11
11
  parentOnUnmount: ((childName: string) => void) | null;
12
- externalErrors?: Record<string, BaseValidateError>;
12
+ mutators: DynamicFormMutators;
13
13
  }
14
- export declare const useField: <Value extends FieldValue, SpecType extends Spec>({ name, spec, initialValue, value: externalValue, validate: propsValidate, tools, parentOnChange, parentOnUnmount: externalParentOnUnmount, externalErrors, }: UseFieldProps<Value, SpecType>) => FieldRenderProps<Value>;
14
+ export declare const useField: <Value extends FieldValue, SpecType extends Spec>({ name, spec, initialValue, value: externalValue, validate: propsValidate, tools, parentOnChange, parentOnUnmount: externalParentOnUnmount, mutators, }: UseFieldProps<Value, SpecType>) => FieldRenderProps<Value>;
@@ -1,13 +1,17 @@
1
1
  import React from 'react';
2
2
  import _ from 'lodash';
3
3
  import { isArraySpec, isNumberSpec, isObjectSpec } from '../../../helpers';
4
- import { OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG } from '../constants';
5
- import { isArrayItem, transformArrIn, transformArrOut } from '../utils';
6
- export const useField = ({ name, spec, initialValue, value: externalValue, validate: propsValidate, tools, parentOnChange, parentOnUnmount: externalParentOnUnmount, externalErrors, }) => {
4
+ import { EMPTY_MUTATOR, OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG } from '../constants';
5
+ import { isArrayItem, isErrorMutatorCorrect, isValueMutatorCorrect, transformArrIn, transformArrOut, } from '../utils';
6
+ export const useField = ({ name, spec, initialValue, value: externalValue, validate: propsValidate, tools, parentOnChange, parentOnUnmount: externalParentOnUnmount, mutators, }) => {
7
7
  const firstRenderRef = React.useRef(true);
8
8
  const validate = React.useCallback((value) => propsValidate === null || propsValidate === void 0 ? void 0 : propsValidate(transformArrOut(value)), [propsValidate]);
9
9
  const [state, setState] = React.useState(() => {
10
+ const valueMutator = _.get(mutators.values, name, EMPTY_MUTATOR);
10
11
  let value = _.cloneDeep(externalValue);
12
+ if (isValueMutatorCorrect(valueMutator, spec) && valueMutator !== EMPTY_MUTATOR) {
13
+ value = valueMutator;
14
+ }
11
15
  if (_.isNil(value)) {
12
16
  if (spec.defaultValue) {
13
17
  value = transformArrIn(spec.defaultValue);
@@ -23,13 +27,11 @@ export const useField = ({ name, spec, initialValue, value: externalValue, valid
23
27
  }
24
28
  }
25
29
  }
26
- let externalError = _.get(externalErrors, name);
27
- if (!(_.isString(externalError) ||
28
- _.isBoolean(externalError) ||
29
- _.isUndefined(externalError))) {
30
- externalError = undefined;
30
+ let errorMutator = _.get(mutators.errors, name);
31
+ if (!isErrorMutatorCorrect(errorMutator)) {
32
+ errorMutator = undefined;
31
33
  }
32
- const error = (validate === null || validate === void 0 ? void 0 : validate(value)) || externalError;
34
+ const error = (validate === null || validate === void 0 ? void 0 : validate(value)) || errorMutator;
33
35
  const dirty = !_.isEqual(value, initialValue);
34
36
  return {
35
37
  active: false,
@@ -45,11 +47,11 @@ export const useField = ({ name, spec, initialValue, value: externalValue, valid
45
47
  childErrors: {},
46
48
  };
47
49
  });
48
- const { onChange, onDrop } = React.useMemo(() => {
49
- const onChange = (valOrSetter, childErrors) => {
50
+ const { onChange, onLocalChange, onDrop } = React.useMemo(() => {
51
+ const onLocalChange = (valOrSetter, childErrors, errorMutator) => {
50
52
  setState((state) => {
51
53
  const _value = _.isFunction(valOrSetter) ? valOrSetter(state.value) : valOrSetter;
52
- const error = validate === null || validate === void 0 ? void 0 : validate(_value);
54
+ const error = (validate === null || validate === void 0 ? void 0 : validate(_value)) || errorMutator;
53
55
  let value = transformArrIn(_value);
54
56
  if (isNumberSpec(spec) && !error) {
55
57
  value = (value ? Number(value) : undefined);
@@ -65,6 +67,7 @@ export const useField = ({ name, spec, initialValue, value: externalValue, valid
65
67
  return Object.assign(Object.assign({}, state), { dirty: !_.isEqual(value, initialValue), error, invalid: Boolean(error), modified: true, pristine: value === initialValue, touched: true, valid: !error, value, visited: true, childErrors: newChildErrors });
66
68
  });
67
69
  };
70
+ const onChange = (valOrSetter, childErrors) => onLocalChange(valOrSetter, childErrors);
68
71
  const onDrop = () => {
69
72
  if (isArrayItem(name)) {
70
73
  (externalParentOnUnmount ? externalParentOnUnmount : tools.onUnmount)(name);
@@ -73,7 +76,7 @@ export const useField = ({ name, spec, initialValue, value: externalValue, valid
73
76
  onChange(undefined, { [name]: false });
74
77
  }
75
78
  };
76
- return { onChange, onDrop };
79
+ return { onChange, onLocalChange, onDrop };
77
80
  }, [initialValue, setState, name, validate, spec, externalParentOnUnmount, tools.onUnmount]);
78
81
  const onBlur = React.useCallback(() => {
79
82
  setState((state) => (Object.assign(Object.assign({}, state), { active: false, touched: true })));
@@ -141,17 +144,23 @@ export const useField = ({ name, spec, initialValue, value: externalValue, valid
141
144
  }
142
145
  }, [state.value]);
143
146
  React.useEffect(() => {
144
- const externalError = _.get(externalErrors, name);
145
- if (!firstRenderRef.current &&
146
- (_.isString(externalError) ||
147
- _.isBoolean(externalError) ||
148
- _.isUndefined(externalError)) &&
149
- state.error !== externalError &&
150
- !(state.error && !externalError)) {
151
- setState(Object.assign(Object.assign({}, state), { error: externalError }));
152
- (parentOnChange ? parentOnChange : tools.onChange)(name, state.value, Object.assign(Object.assign({}, state.childErrors), { [name]: externalError }));
147
+ if (!firstRenderRef.current) {
148
+ const valueMutator = _.get(mutators.values, name, EMPTY_MUTATOR);
149
+ let errorMutator = _.get(mutators.errors, name);
150
+ if (!isErrorMutatorCorrect(errorMutator)) {
151
+ errorMutator = undefined;
152
+ }
153
+ if (isValueMutatorCorrect(valueMutator, spec) &&
154
+ valueMutator !== state.value &&
155
+ valueMutator !== EMPTY_MUTATOR) {
156
+ onLocalChange(valueMutator, undefined, errorMutator);
157
+ }
158
+ else if (state.error !== errorMutator && !(state.error && !errorMutator)) {
159
+ setState(Object.assign(Object.assign({}, state), { error: errorMutator }));
160
+ (parentOnChange ? parentOnChange : tools.onChange)(name, state.value, Object.assign(Object.assign({}, state.childErrors), { [name]: errorMutator }));
161
+ }
153
162
  }
154
- }, [externalErrors]);
163
+ }, [mutators]);
155
164
  React.useEffect(() => {
156
165
  firstRenderRef.current = false;
157
166
  return () => {
@@ -0,0 +1 @@
1
+ export declare const useMutateDFState: () => (mutators: import("..").DynamicFormMutators) => void;
@@ -0,0 +1,2 @@
1
+ import { useDynamicFormsCtx } from './useDynamicFormsCtx';
2
+ export const useMutateDFState = () => useDynamicFormsCtx().tools.mutateDFState;
@@ -0,0 +1,5 @@
1
+ import { DynamicFormMutators } from '../types';
2
+ export declare const useMutators: (externalMutators?: DynamicFormMutators) => {
3
+ mutators: DynamicFormMutators;
4
+ mutateDFState: (mutators: DynamicFormMutators) => void;
5
+ };
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import _ from 'lodash';
3
+ export const useMutators = (externalMutators) => {
4
+ const firstRenderRef = React.useRef(true);
5
+ const [store, setStore] = React.useState(externalMutators || {});
6
+ const mutateDFState = React.useCallback((mutators) => {
7
+ setStore((store) => (Object.assign(Object.assign(Object.assign(Object.assign({}, store), (mutators.errors ? { errors: _.merge(store.errors, mutators.errors) } : {})), (mutators.values ? { values: _.merge(store.values, mutators.values) } : {})), (mutators.spec ? { spec: _.merge(store.spec, mutators.spec) } : {}))));
8
+ }, [setStore]);
9
+ React.useEffect(() => {
10
+ if (firstRenderRef.current) {
11
+ firstRenderRef.current = false;
12
+ }
13
+ else if (externalMutators) {
14
+ mutateDFState(externalMutators);
15
+ }
16
+ }, [externalMutators]);
17
+ return { mutators: store, mutateDFState };
18
+ };
@@ -0,0 +1 @@
1
+ export declare const useStoreValue: () => import("..").DynamicFieldStore;
@@ -0,0 +1,2 @@
1
+ import { useDynamicFormsCtx } from './useDynamicFormsCtx';
2
+ export const useStoreValue = () => useDynamicFormsCtx().store;
@@ -1,5 +1,6 @@
1
1
  export * from './constants';
2
2
  export * from './Controller';
3
3
  export * from './DynamicField';
4
+ export { useMutateDFState, useStoreValue } from './hooks';
4
5
  export * from './types';
5
6
  export * from './utils';
@@ -1,5 +1,6 @@
1
1
  export * from './constants';
2
2
  export * from './Controller';
3
3
  export * from './DynamicField';
4
+ export { useMutateDFState, useStoreValue } from './hooks';
4
5
  export * from './types';
5
6
  export * from './utils';
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { MonacoEditorProps } from 'react-monaco-editor/lib/types';
3
3
  import { StringSpec } from '../../../types';
4
- import { BaseValidateError, DynamicFormConfig, FieldValue, ValidateError, WonderMirror } from './';
4
+ import { DynamicFieldStore, DynamicFormConfig, DynamicFormMutators, FieldValue, ValidateError, WonderMirror } from './';
5
5
  export interface DynamicFormsContext {
6
6
  config: DynamicFormConfig;
7
7
  Monaco?: React.ComponentType<MonacoEditorProps>;
@@ -11,7 +11,9 @@ export interface DynamicFormsContext {
11
11
  onChange: (name: string, value: FieldValue, errors?: Record<string, ValidateError>) => void;
12
12
  onUnmount: (name: string) => void;
13
13
  submitFailed: boolean;
14
+ mutateDFState: (mutators: DynamicFormMutators) => void;
14
15
  };
15
- externalErrors?: Record<string, BaseValidateError>;
16
+ store: DynamicFieldStore;
17
+ mutators: DynamicFormMutators;
16
18
  __mirror?: WonderMirror;
17
19
  }
@@ -6,6 +6,7 @@ export * from './field';
6
6
  export * from './input';
7
7
  export * from './layout';
8
8
  export * from './mirror';
9
+ export * from './mutators';
9
10
  export * from './number';
10
11
  export * from './object';
11
12
  export * from './store';
@@ -6,6 +6,7 @@ export * from './field';
6
6
  export * from './input';
7
7
  export * from './layout';
8
8
  export * from './mirror';
9
+ export * from './mutators';
9
10
  export * from './number';
10
11
  export * from './object';
11
12
  export * from './store';
@@ -0,0 +1,7 @@
1
+ import { FormValue, Spec } from '../../../types';
2
+ import { BaseValidateError } from './';
3
+ export interface DynamicFormMutators {
4
+ errors?: Record<string, BaseValidateError>;
5
+ values?: Record<string, FormValue>;
6
+ spec?: Record<string, Partial<Spec>>;
7
+ }
@@ -1,6 +1,9 @@
1
1
  import { FormValue, Spec } from '../../../types';
2
+ import { ValidateError } from '../types';
2
3
  export declare const isCorrectConfig: (candidate: any) => boolean;
3
4
  export declare const transformArrIn: <Type extends FormValue, ReturnType_1 extends FormValue = Type>(value: Type) => ReturnType_1;
4
5
  export declare const transformArrOut: <Type extends FormValue, ReturnType_1 extends FormValue = Type>(value: Type) => ReturnType_1;
5
6
  export declare const isArrayItem: (name: string) => boolean;
6
7
  export declare const withGenerateButton: (spec: Spec) => boolean | undefined;
8
+ export declare const isErrorMutatorCorrect: (errorMutator: ValidateError) => boolean;
9
+ export declare const isValueMutatorCorrect: (valueMutator: FormValue, spec: Spec) => boolean;
@@ -45,3 +45,5 @@ export const isArrayItem = (name) => name[name.length - 1] === '>';
45
45
  export const withGenerateButton = (spec) => isStringSpec(spec) &&
46
46
  SPEC_TYPE_FOR_GENERATE_BUTTON.includes(spec.viewSpec.type) &&
47
47
  spec.viewSpec.generateRandomValueButton;
48
+ export const isErrorMutatorCorrect = (errorMutator) => _.isString(errorMutator) || _.isBoolean(errorMutator) || _.isUndefined(errorMutator);
49
+ export const isValueMutatorCorrect = (valueMutator, spec) => typeof valueMutator === spec.type || (_.isArray(valueMutator) && spec.type === SpecTypes.Array);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/dynamic-forms",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "main": "build/cjs/index.js",