@gravity-ui/dynamic-forms 3.0.1 → 3.1.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/build/cjs/lib/core/components/Form/Controller.js +2 -1
- package/build/cjs/lib/core/components/Form/DynamicField.js +3 -2
- package/build/cjs/lib/core/components/Form/hooks/useField.js +20 -2
- package/build/cjs/lib/core/components/Form/hooks/useStore.js +6 -6
- package/build/cjs/lib/core/components/View/DynamicView.js +1 -2
- package/build/cjs/lib/kit/components/Inputs/Text/Text.js +16 -31
- package/build/cjs/lib/kit/components/ViewLayouts/ViewAccordeon/ViewAccordeon.js +2 -1
- package/build/cjs/lib/kit/components/ViewLayouts/ViewCardAccordeon.js +2 -1
- package/build/esm/lib/core/components/Form/Controller.js +2 -1
- package/build/esm/lib/core/components/Form/DynamicField.d.ts +2 -1
- package/build/esm/lib/core/components/Form/DynamicField.js +3 -2
- package/build/esm/lib/core/components/Form/hooks/useField.d.ts +3 -2
- package/build/esm/lib/core/components/Form/hooks/useField.js +20 -2
- package/build/esm/lib/core/components/Form/hooks/useStore.js +7 -7
- package/build/esm/lib/core/components/Form/types/context.d.ts +2 -1
- package/build/esm/lib/core/components/View/DynamicView.d.ts +2 -2
- package/build/esm/lib/core/components/View/DynamicView.js +1 -2
- package/build/esm/lib/core/components/View/types/context.d.ts +2 -2
- package/build/esm/lib/kit/components/Inputs/Text/Text.d.ts +1 -2
- package/build/esm/lib/kit/components/Inputs/Text/Text.js +17 -33
- package/build/esm/lib/kit/components/ViewLayouts/ViewAccordeon/ViewAccordeon.js +2 -1
- package/build/esm/lib/kit/components/ViewLayouts/ViewCardAccordeon.js +2 -1
- package/package.json +6 -6
- package/build/cjs/lib/kit/components/Inputs/Text/Text.css +0 -6
- package/build/esm/lib/kit/components/Inputs/Text/Text.css +0 -6
|
@@ -5,7 +5,7 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
6
6
|
const hooks_1 = require("./hooks");
|
|
7
7
|
const Controller = ({ spec, name, value, parentOnChange, parentOnUnmount, }) => {
|
|
8
|
-
const { tools, __mirror } = (0, hooks_1.useDynamicFormsCtx)();
|
|
8
|
+
const { tools, externalErrors, __mirror } = (0, hooks_1.useDynamicFormsCtx)();
|
|
9
9
|
const { inputEntity, Layout } = (0, hooks_1.useComponents)(spec);
|
|
10
10
|
const render = (0, hooks_1.useRender)({ name, spec, inputEntity, Layout });
|
|
11
11
|
const validate = (0, hooks_1.useValidate)(spec);
|
|
@@ -18,6 +18,7 @@ const Controller = ({ spec, name, value, parentOnChange, parentOnUnmount, }) =>
|
|
|
18
18
|
tools,
|
|
19
19
|
parentOnChange,
|
|
20
20
|
parentOnUnmount,
|
|
21
|
+
externalErrors,
|
|
21
22
|
});
|
|
22
23
|
const withSearch = (0, hooks_1.useSearch)(spec, renderProps.input.value, name);
|
|
23
24
|
(0, hooks_1.useControllerMirror)(name, {
|
|
@@ -9,7 +9,7 @@ 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, __mirror, }) => {
|
|
12
|
+
const DynamicField = ({ name, spec, config, Monaco, generateRandomValue, search, withoutInsertFFDebounce, errors: externalErrors, __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);
|
|
@@ -20,8 +20,9 @@ const DynamicField = ({ name, spec, config, Monaco, generateRandomValue, search,
|
|
|
20
20
|
Monaco: (0, react_is_1.isValidElementType)(Monaco) ? Monaco : undefined,
|
|
21
21
|
generateRandomValue,
|
|
22
22
|
tools,
|
|
23
|
+
externalErrors,
|
|
23
24
|
__mirror,
|
|
24
|
-
}), [tools, config, Monaco, __mirror, generateRandomValue]);
|
|
25
|
+
}), [tools, config, Monaco, __mirror, generateRandomValue, externalErrors]);
|
|
25
26
|
const searchContext = react_1.default.useMemo(() => ({
|
|
26
27
|
setField,
|
|
27
28
|
removeField,
|
|
@@ -7,7 +7,7 @@ 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, }) => {
|
|
10
|
+
const useField = ({ name, spec, initialValue, value: externalValue, validate: propsValidate, tools, parentOnChange, parentOnUnmount: externalParentOnUnmount, externalErrors, }) => {
|
|
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(() => {
|
|
@@ -27,7 +27,13 @@ const useField = ({ name, spec, initialValue, value: externalValue, validate: pr
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
|
|
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;
|
|
35
|
+
}
|
|
36
|
+
const error = (validate === null || validate === void 0 ? void 0 : validate(value)) || externalError;
|
|
31
37
|
const dirty = !lodash_1.default.isEqual(value, initialValue);
|
|
32
38
|
return {
|
|
33
39
|
active: false,
|
|
@@ -138,6 +144,18 @@ const useField = ({ name, spec, initialValue, value: externalValue, validate: pr
|
|
|
138
144
|
(parentOnChange ? parentOnChange : tools.onChange)(name, state.value, Object.assign(Object.assign({}, state.childErrors), { [name]: state.error }));
|
|
139
145
|
}
|
|
140
146
|
}, [state.value]);
|
|
147
|
+
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 }));
|
|
157
|
+
}
|
|
158
|
+
}, [externalErrors]);
|
|
141
159
|
react_1.default.useEffect(() => {
|
|
142
160
|
firstRenderRef.current = false;
|
|
143
161
|
return () => {
|
|
@@ -7,14 +7,14 @@ const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
|
7
7
|
const react_final_form_1 = require("react-final-form");
|
|
8
8
|
const utils_1 = require("../utils");
|
|
9
9
|
const useStore = (name) => {
|
|
10
|
-
const
|
|
10
|
+
const formState = (0, react_final_form_1.useFormState)();
|
|
11
11
|
const firstRenderRef = react_1.default.useRef(true);
|
|
12
12
|
const [store, setStore] = react_1.default.useState(() => {
|
|
13
13
|
const values = (0, utils_1.transformArrIn)({
|
|
14
|
-
[name]: lodash_1.default.get(
|
|
14
|
+
[name]: lodash_1.default.get(formState.values, name),
|
|
15
15
|
});
|
|
16
16
|
const initialValue = (0, utils_1.transformArrIn)({
|
|
17
|
-
[name]: lodash_1.default.get(
|
|
17
|
+
[name]: lodash_1.default.get(formState.initialValues, name),
|
|
18
18
|
});
|
|
19
19
|
return {
|
|
20
20
|
name,
|
|
@@ -23,7 +23,7 @@ const useStore = (name) => {
|
|
|
23
23
|
errors: {},
|
|
24
24
|
};
|
|
25
25
|
});
|
|
26
|
-
const submitFailed =
|
|
26
|
+
const submitFailed = formState.submitFailed;
|
|
27
27
|
const tools = react_1.default.useMemo(() => ({
|
|
28
28
|
initialValue: store.initialValue,
|
|
29
29
|
onChange: (name, value, errors) => setStore((store) => (Object.assign(Object.assign({}, store), { values: lodash_1.default.set(Object.assign({}, store.values), name, lodash_1.default.clone(value)), errors: errors || {} }))),
|
|
@@ -33,10 +33,10 @@ const useStore = (name) => {
|
|
|
33
33
|
react_1.default.useEffect(() => {
|
|
34
34
|
if (!firstRenderRef.current) {
|
|
35
35
|
const values = (0, utils_1.transformArrIn)({
|
|
36
|
-
[name]: lodash_1.default.get(
|
|
36
|
+
[name]: lodash_1.default.get(formState.values, name),
|
|
37
37
|
});
|
|
38
38
|
const initialValue = (0, utils_1.transformArrIn)({
|
|
39
|
-
[name]: lodash_1.default.get(
|
|
39
|
+
[name]: lodash_1.default.get(formState.initialValues, name),
|
|
40
40
|
});
|
|
41
41
|
setStore({
|
|
42
42
|
name: name,
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DynamicView = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const react_1 = tslib_1.__importDefault(require("react"));
|
|
6
|
-
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
7
6
|
const react_is_1 = require("react-is");
|
|
8
7
|
const helpers_1 = require("../../helpers");
|
|
9
8
|
const ViewController_1 = require("./ViewController");
|
|
@@ -12,7 +11,7 @@ const hooks_1 = require("./hooks");
|
|
|
12
11
|
const DynamicView = ({ value, spec, config, Link, Monaco }) => {
|
|
13
12
|
const DynamicFormsCtx = (0, hooks_1.useCreateContext)();
|
|
14
13
|
const context = react_1.default.useMemo(() => ({ config, value, Link, Monaco: (0, react_is_1.isValidElementType)(Monaco) ? Monaco : undefined }), [config, value, Link, Monaco]);
|
|
15
|
-
if (
|
|
14
|
+
if ((0, helpers_1.isCorrectSpec)(spec) && (0, helpers_2.isCorrectViewConfig)(config)) {
|
|
16
15
|
return (react_1.default.createElement(DynamicFormsCtx.Provider, { value: context },
|
|
17
16
|
react_1.default.createElement(ViewController_1.ViewController, { spec: spec, name: "" })));
|
|
18
17
|
}
|
|
@@ -3,38 +3,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Text = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const react_1 = tslib_1.__importDefault(require("react"));
|
|
6
|
-
const
|
|
6
|
+
const components_1 = require("@gravity-ui/components");
|
|
7
7
|
const uikit_1 = require("@gravity-ui/uikit");
|
|
8
8
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
onChange
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (type === 'password') {
|
|
25
|
-
const onClick = () => {
|
|
26
|
-
setHideValue((hideValue) => !hideValue);
|
|
27
|
-
};
|
|
28
|
-
return (react_1.default.createElement("div", { className: b() },
|
|
29
|
-
input.value ? (react_1.default.createElement(uikit_1.CopyToClipboard, { text: String(value), timeout: 500 }, (state) => (react_1.default.createElement(uikit_1.Button, { view: "flat-secondary", className: b('button'), size: "s" },
|
|
30
|
-
react_1.default.createElement(uikit_1.Icon, { size: 14, data: state === uikit_1.CopyToClipboardStatus.Pending
|
|
31
|
-
? icons_1.Copy
|
|
32
|
-
: icons_1.CopyCheck }))))) : null,
|
|
33
|
-
react_1.default.createElement(uikit_1.Button, { view: "flat-secondary", onClick: onClick, className: b('button'), size: "s" },
|
|
34
|
-
react_1.default.createElement(uikit_1.Icon, { data: hideValue ? icons_1.Eye : icons_1.EyeSlash, size: 14 }))));
|
|
35
|
-
}
|
|
36
|
-
return undefined;
|
|
37
|
-
}, [hideValue, input.value, type, value]);
|
|
38
|
-
return (react_1.default.createElement(uikit_1.TextInput, { type: hideValue ? 'password' : 'text', value: lodash_1.default.isNil(value) ? '' : `${value}`, hasClear: true, onBlur: onBlur, onFocus: onFocus, onUpdate: handleChange, disabled: spec.viewSpec.disabled, placeholder: spec.viewSpec.placeholder, autoComplete: type === 'password' ? 'new-password' : undefined, qa: name, rightContent: additionalRightContent }));
|
|
9
|
+
const Text = ({ name, input: { value, onBlur, onChange, onFocus }, spec, }) => {
|
|
10
|
+
const props = {
|
|
11
|
+
value: lodash_1.default.isNil(value) ? '' : `${value}`,
|
|
12
|
+
hasClear: true,
|
|
13
|
+
onBlur: onBlur,
|
|
14
|
+
onFocus: onFocus,
|
|
15
|
+
onUpdate: onChange,
|
|
16
|
+
disabled: spec.viewSpec.disabled,
|
|
17
|
+
placeholder: spec.viewSpec.placeholder,
|
|
18
|
+
qa: name,
|
|
19
|
+
};
|
|
20
|
+
if (spec.viewSpec.type === 'password') {
|
|
21
|
+
return (react_1.default.createElement(components_1.PasswordInput, Object.assign({}, props, { autoComplete: "new-password", showCopyButton: true, showRevealButton: true })));
|
|
22
|
+
}
|
|
23
|
+
return react_1.default.createElement(uikit_1.TextInput, Object.assign({}, props, { type: "text" }));
|
|
39
24
|
};
|
|
40
25
|
exports.Text = Text;
|
|
@@ -3,10 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ViewAccordeon = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const react_1 = tslib_1.__importDefault(require("react"));
|
|
6
|
+
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
6
7
|
const utils_1 = require("../../../utils");
|
|
7
8
|
const SimpleVerticalAccordeon_1 = require("../../SimpleVerticalAccordeon");
|
|
8
9
|
const ViewAccordeon = ({ name, value, spec, children, }) => {
|
|
9
|
-
const [open, setOpen] = react_1.default.useState(true);
|
|
10
|
+
const [open, setOpen] = react_1.default.useState(lodash_1.default.isBoolean(spec.viewSpec.layoutOpen) ? spec.viewSpec.layoutOpen : true);
|
|
10
11
|
if (!(0, utils_1.isNotEmptyValue)(value, spec)) {
|
|
11
12
|
return null;
|
|
12
13
|
}
|
|
@@ -3,10 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ViewCardAccordeon = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const react_1 = tslib_1.__importDefault(require("react"));
|
|
6
|
+
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
6
7
|
const __1 = require("../");
|
|
7
8
|
const utils_1 = require("../../utils");
|
|
8
9
|
const ViewCardAccordeon = ({ name, value, spec, children, }) => {
|
|
9
|
-
const [open, setOpen] = react_1.default.useState(true);
|
|
10
|
+
const [open, setOpen] = react_1.default.useState(lodash_1.default.isBoolean(spec.viewSpec.layoutOpen) ? spec.viewSpec.layoutOpen : true);
|
|
10
11
|
const onToggle = react_1.default.useCallback(() => setOpen((f) => !f), [setOpen]);
|
|
11
12
|
if (!(0, utils_1.isNotEmptyValue)(value, spec)) {
|
|
12
13
|
return null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import { useComponents, useControllerMirror, useDynamicFormsCtx, useField, useRender, useSearch, useValidate, } from './hooks';
|
|
3
3
|
export const Controller = ({ spec, name, value, parentOnChange, parentOnUnmount, }) => {
|
|
4
|
-
const { tools, __mirror } = useDynamicFormsCtx();
|
|
4
|
+
const { tools, externalErrors, __mirror } = useDynamicFormsCtx();
|
|
5
5
|
const { inputEntity, Layout } = useComponents(spec);
|
|
6
6
|
const render = useRender({ name, spec, inputEntity, Layout });
|
|
7
7
|
const validate = useValidate(spec);
|
|
@@ -14,6 +14,7 @@ export const Controller = ({ spec, name, value, parentOnChange, parentOnUnmount,
|
|
|
14
14
|
tools,
|
|
15
15
|
parentOnChange,
|
|
16
16
|
parentOnUnmount,
|
|
17
|
+
externalErrors,
|
|
17
18
|
});
|
|
18
19
|
const withSearch = useSearch(spec, renderProps.input.value, name);
|
|
19
20
|
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 { DynamicFormConfig, FieldValue, WonderMirror } from './types';
|
|
4
|
+
import { BaseValidateError, DynamicFormConfig, FieldValue, WonderMirror } from './types';
|
|
5
5
|
export interface DynamicFieldProps {
|
|
6
6
|
name: string;
|
|
7
7
|
spec: Spec;
|
|
@@ -10,6 +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
14
|
__mirror?: WonderMirror;
|
|
14
15
|
}
|
|
15
16
|
export declare const DynamicField: React.FC<DynamicFieldProps>;
|
|
@@ -5,7 +5,7 @@ import { isCorrectSpec } from '../../helpers';
|
|
|
5
5
|
import { Controller } from './Controller';
|
|
6
6
|
import { useCreateContext, useCreateSearchContext, useDynamicFieldMirror, useIntegrationFF, useSearchStore, useStore, } from './hooks';
|
|
7
7
|
import { getDefaultSearchFunction, isCorrectConfig } from './utils';
|
|
8
|
-
export const DynamicField = ({ name, spec, config, Monaco, generateRandomValue, search, withoutInsertFFDebounce, __mirror, }) => {
|
|
8
|
+
export const DynamicField = ({ name, spec, config, Monaco, generateRandomValue, search, withoutInsertFFDebounce, errors: externalErrors, __mirror, }) => {
|
|
9
9
|
const DynamicFormsCtx = useCreateContext();
|
|
10
10
|
const SearchContext = useCreateSearchContext();
|
|
11
11
|
const { tools, store } = useStore(name);
|
|
@@ -16,8 +16,9 @@ export const DynamicField = ({ name, spec, config, Monaco, generateRandomValue,
|
|
|
16
16
|
Monaco: isValidElementType(Monaco) ? Monaco : undefined,
|
|
17
17
|
generateRandomValue,
|
|
18
18
|
tools,
|
|
19
|
+
externalErrors,
|
|
19
20
|
__mirror,
|
|
20
|
-
}), [tools, config, Monaco, __mirror, generateRandomValue]);
|
|
21
|
+
}), [tools, config, Monaco, __mirror, generateRandomValue, externalErrors]);
|
|
21
22
|
const searchContext = React.useMemo(() => ({
|
|
22
23
|
setField,
|
|
23
24
|
removeField,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Spec } from '../../../types';
|
|
2
|
-
import { DynamicFormsContext, FieldRenderProps, FieldValue, ValidateError } from '../types';
|
|
2
|
+
import { BaseValidateError, 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,5 +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
13
|
}
|
|
13
|
-
export declare const useField: <Value extends FieldValue, SpecType extends Spec>({ name, spec, initialValue, value: externalValue, validate: propsValidate, tools, parentOnChange, parentOnUnmount: externalParentOnUnmount, }: 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, externalErrors, }: UseFieldProps<Value, SpecType>) => FieldRenderProps<Value>;
|
|
@@ -3,7 +3,7 @@ import _ from 'lodash';
|
|
|
3
3
|
import { isArraySpec, isNumberSpec, isObjectSpec } from '../../../helpers';
|
|
4
4
|
import { OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG } from '../constants';
|
|
5
5
|
import { isArrayItem, transformArrIn, transformArrOut } from '../utils';
|
|
6
|
-
export const useField = ({ name, spec, initialValue, value: externalValue, validate: propsValidate, tools, parentOnChange, parentOnUnmount: externalParentOnUnmount, }) => {
|
|
6
|
+
export const useField = ({ name, spec, initialValue, value: externalValue, validate: propsValidate, tools, parentOnChange, parentOnUnmount: externalParentOnUnmount, externalErrors, }) => {
|
|
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(() => {
|
|
@@ -23,7 +23,13 @@ export const useField = ({ name, spec, initialValue, value: externalValue, valid
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
let externalError = _.get(externalErrors, name);
|
|
27
|
+
if (!(_.isString(externalError) ||
|
|
28
|
+
_.isBoolean(externalError) ||
|
|
29
|
+
_.isUndefined(externalError))) {
|
|
30
|
+
externalError = undefined;
|
|
31
|
+
}
|
|
32
|
+
const error = (validate === null || validate === void 0 ? void 0 : validate(value)) || externalError;
|
|
27
33
|
const dirty = !_.isEqual(value, initialValue);
|
|
28
34
|
return {
|
|
29
35
|
active: false,
|
|
@@ -134,6 +140,18 @@ export const useField = ({ name, spec, initialValue, value: externalValue, valid
|
|
|
134
140
|
(parentOnChange ? parentOnChange : tools.onChange)(name, state.value, Object.assign(Object.assign({}, state.childErrors), { [name]: state.error }));
|
|
135
141
|
}
|
|
136
142
|
}, [state.value]);
|
|
143
|
+
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 }));
|
|
153
|
+
}
|
|
154
|
+
}, [externalErrors]);
|
|
137
155
|
React.useEffect(() => {
|
|
138
156
|
firstRenderRef.current = false;
|
|
139
157
|
return () => {
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import _ from 'lodash';
|
|
3
|
-
import {
|
|
3
|
+
import { useFormState } from 'react-final-form';
|
|
4
4
|
import { transformArrIn } from '../utils';
|
|
5
5
|
export const useStore = (name) => {
|
|
6
|
-
const
|
|
6
|
+
const formState = useFormState();
|
|
7
7
|
const firstRenderRef = React.useRef(true);
|
|
8
8
|
const [store, setStore] = React.useState(() => {
|
|
9
9
|
const values = transformArrIn({
|
|
10
|
-
[name]: _.get(
|
|
10
|
+
[name]: _.get(formState.values, name),
|
|
11
11
|
});
|
|
12
12
|
const initialValue = transformArrIn({
|
|
13
|
-
[name]: _.get(
|
|
13
|
+
[name]: _.get(formState.initialValues, name),
|
|
14
14
|
});
|
|
15
15
|
return {
|
|
16
16
|
name,
|
|
@@ -19,7 +19,7 @@ export const useStore = (name) => {
|
|
|
19
19
|
errors: {},
|
|
20
20
|
};
|
|
21
21
|
});
|
|
22
|
-
const submitFailed =
|
|
22
|
+
const submitFailed = formState.submitFailed;
|
|
23
23
|
const tools = React.useMemo(() => ({
|
|
24
24
|
initialValue: store.initialValue,
|
|
25
25
|
onChange: (name, value, errors) => setStore((store) => (Object.assign(Object.assign({}, store), { values: _.set(Object.assign({}, store.values), name, _.clone(value)), errors: errors || {} }))),
|
|
@@ -29,10 +29,10 @@ export const useStore = (name) => {
|
|
|
29
29
|
React.useEffect(() => {
|
|
30
30
|
if (!firstRenderRef.current) {
|
|
31
31
|
const values = transformArrIn({
|
|
32
|
-
[name]: _.get(
|
|
32
|
+
[name]: _.get(formState.values, name),
|
|
33
33
|
});
|
|
34
34
|
const initialValue = transformArrIn({
|
|
35
|
-
[name]: _.get(
|
|
35
|
+
[name]: _.get(formState.initialValues, name),
|
|
36
36
|
});
|
|
37
37
|
setStore({
|
|
38
38
|
name: 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 { StringSpec } from '../../../types';
|
|
4
|
-
import { DynamicFormConfig, FieldValue, ValidateError, WonderMirror } from './';
|
|
4
|
+
import { BaseValidateError, DynamicFormConfig, FieldValue, ValidateError, WonderMirror } from './';
|
|
5
5
|
export interface DynamicFormsContext {
|
|
6
6
|
config: DynamicFormConfig;
|
|
7
7
|
Monaco?: React.ComponentType<MonacoEditorProps>;
|
|
@@ -12,5 +12,6 @@ export interface DynamicFormsContext {
|
|
|
12
12
|
onUnmount: (name: string) => void;
|
|
13
13
|
submitFailed: boolean;
|
|
14
14
|
};
|
|
15
|
+
externalErrors?: Record<string, BaseValidateError>;
|
|
15
16
|
__mirror?: WonderMirror;
|
|
16
17
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { MonacoEditorProps } from 'react-monaco-editor/lib/types';
|
|
3
|
-
import {
|
|
3
|
+
import { FormValue, Spec } from '../../types';
|
|
4
4
|
import { DynamicViewConfig } from './types';
|
|
5
5
|
export interface DynamicViewProps {
|
|
6
|
-
value:
|
|
6
|
+
value: FormValue;
|
|
7
7
|
spec: Spec;
|
|
8
8
|
config: DynamicViewConfig;
|
|
9
9
|
Link?: React.ComponentType<{
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import _ from 'lodash';
|
|
3
2
|
import { isValidElementType } from 'react-is';
|
|
4
3
|
import { isCorrectSpec } from '../../helpers';
|
|
5
4
|
import { ViewController } from './ViewController';
|
|
@@ -8,7 +7,7 @@ import { useCreateContext } from './hooks';
|
|
|
8
7
|
export const DynamicView = ({ value, spec, config, Link, Monaco }) => {
|
|
9
8
|
const DynamicFormsCtx = useCreateContext();
|
|
10
9
|
const context = React.useMemo(() => ({ config, value, Link, Monaco: isValidElementType(Monaco) ? Monaco : undefined }), [config, value, Link, Monaco]);
|
|
11
|
-
if (
|
|
10
|
+
if (isCorrectSpec(spec) && isCorrectViewConfig(config)) {
|
|
12
11
|
return (React.createElement(DynamicFormsCtx.Provider, { value: context },
|
|
13
12
|
React.createElement(ViewController, { spec: spec, name: "" })));
|
|
14
13
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { MonacoEditorProps } from 'react-monaco-editor/lib/types';
|
|
3
|
-
import {
|
|
3
|
+
import { FormValue, Spec } from '../../../types';
|
|
4
4
|
import { DynamicViewConfig } from './';
|
|
5
5
|
export interface DynamicViewContext {
|
|
6
6
|
config: DynamicViewConfig;
|
|
7
|
-
value:
|
|
7
|
+
value: FormValue;
|
|
8
8
|
Link?: React.ComponentType<{
|
|
9
9
|
value: FormValue;
|
|
10
10
|
link: Spec['viewSpec']['link'];
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import { NumberInputProps, StringInputProps } from '../../../../core';
|
|
2
|
-
|
|
3
|
-
export declare const Text: <T extends NumberInputProps | StringInputProps>({ name, input, spec }: T) => JSX.Element;
|
|
2
|
+
export declare const Text: <T extends NumberInputProps | StringInputProps>({ name, input: { value, onBlur, onChange, onFocus }, spec, }: T) => JSX.Element;
|
|
@@ -1,36 +1,20 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { PasswordInput } from '@gravity-ui/components';
|
|
3
|
+
import { TextInput } from '@gravity-ui/uikit';
|
|
4
4
|
import _ from 'lodash';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
const additionalRightContent = React.useMemo(() => {
|
|
21
|
-
if (type === 'password') {
|
|
22
|
-
const onClick = () => {
|
|
23
|
-
setHideValue((hideValue) => !hideValue);
|
|
24
|
-
};
|
|
25
|
-
return (React.createElement("div", { className: b() },
|
|
26
|
-
input.value ? (React.createElement(CopyToClipboard, { text: String(value), timeout: 500 }, (state) => (React.createElement(Button, { view: "flat-secondary", className: b('button'), size: "s" },
|
|
27
|
-
React.createElement(Icon, { size: 14, data: state === CopyToClipboardStatus.Pending
|
|
28
|
-
? Copy
|
|
29
|
-
: CopyCheck }))))) : null,
|
|
30
|
-
React.createElement(Button, { view: "flat-secondary", onClick: onClick, className: b('button'), size: "s" },
|
|
31
|
-
React.createElement(Icon, { data: hideValue ? Eye : EyeSlash, size: 14 }))));
|
|
32
|
-
}
|
|
33
|
-
return undefined;
|
|
34
|
-
}, [hideValue, input.value, type, value]);
|
|
35
|
-
return (React.createElement(TextInput, { type: hideValue ? 'password' : 'text', value: _.isNil(value) ? '' : `${value}`, hasClear: true, onBlur: onBlur, onFocus: onFocus, onUpdate: handleChange, disabled: spec.viewSpec.disabled, placeholder: spec.viewSpec.placeholder, autoComplete: type === 'password' ? 'new-password' : undefined, qa: name, rightContent: additionalRightContent }));
|
|
5
|
+
export const Text = ({ name, input: { value, onBlur, onChange, onFocus }, spec, }) => {
|
|
6
|
+
const props = {
|
|
7
|
+
value: _.isNil(value) ? '' : `${value}`,
|
|
8
|
+
hasClear: true,
|
|
9
|
+
onBlur: onBlur,
|
|
10
|
+
onFocus: onFocus,
|
|
11
|
+
onUpdate: onChange,
|
|
12
|
+
disabled: spec.viewSpec.disabled,
|
|
13
|
+
placeholder: spec.viewSpec.placeholder,
|
|
14
|
+
qa: name,
|
|
15
|
+
};
|
|
16
|
+
if (spec.viewSpec.type === 'password') {
|
|
17
|
+
return (React.createElement(PasswordInput, Object.assign({}, props, { autoComplete: "new-password", showCopyButton: true, showRevealButton: true })));
|
|
18
|
+
}
|
|
19
|
+
return React.createElement(TextInput, Object.assign({}, props, { type: "text" }));
|
|
36
20
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import _ from 'lodash';
|
|
2
3
|
import { isNotEmptyValue } from '../../../utils';
|
|
3
4
|
import { SimpleVerticalAccordeon } from '../../SimpleVerticalAccordeon';
|
|
4
5
|
export const ViewAccordeon = ({ name, value, spec, children, }) => {
|
|
5
|
-
const [open, setOpen] = React.useState(true);
|
|
6
|
+
const [open, setOpen] = React.useState(_.isBoolean(spec.viewSpec.layoutOpen) ? spec.viewSpec.layoutOpen : true);
|
|
6
7
|
if (!isNotEmptyValue(value, spec)) {
|
|
7
8
|
return null;
|
|
8
9
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import _ from 'lodash';
|
|
2
3
|
import { Card } from '../';
|
|
3
4
|
import { isNotEmptyValue } from '../../utils';
|
|
4
5
|
export const ViewCardAccordeon = ({ name, value, spec, children, }) => {
|
|
5
|
-
const [open, setOpen] = React.useState(true);
|
|
6
|
+
const [open, setOpen] = React.useState(_.isBoolean(spec.viewSpec.layoutOpen) ? spec.viewSpec.layoutOpen : true);
|
|
6
7
|
const onToggle = React.useCallback(() => setOpen((f) => !f), [setOpen]);
|
|
7
8
|
if (!isNotEmptyValue(value, spec)) {
|
|
8
9
|
return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gravity-ui/dynamic-forms",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "build/cjs/index.js",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@bem-react/classname": "^1.6.0",
|
|
41
|
-
"@gravity-ui/components": "^2.
|
|
41
|
+
"@gravity-ui/components": "^2.8.0",
|
|
42
42
|
"@gravity-ui/i18n": "^1.1.0",
|
|
43
|
-
"@gravity-ui/icons": "^2.
|
|
43
|
+
"@gravity-ui/icons": "^2.8.1",
|
|
44
44
|
"lodash": "^4.17.20"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
"@babel/preset-typescript": "^7.18.6",
|
|
49
49
|
"@commitlint/cli": "^17.0.0",
|
|
50
50
|
"@commitlint/config-conventional": "^17.0.0",
|
|
51
|
-
"@gravity-ui/eslint-config": "^2.
|
|
52
|
-
"@gravity-ui/prettier-config": "^1.0
|
|
51
|
+
"@gravity-ui/eslint-config": "^2.2.0",
|
|
52
|
+
"@gravity-ui/prettier-config": "^1.1.0",
|
|
53
53
|
"@gravity-ui/stylelint-config": "^2.0.0",
|
|
54
54
|
"@gravity-ui/tsconfig": "^1.0.0",
|
|
55
|
-
"@gravity-ui/uikit": "^5.
|
|
55
|
+
"@gravity-ui/uikit": "^5.19.1",
|
|
56
56
|
"@storybook/addon-essentials": "^7.0.27",
|
|
57
57
|
"@storybook/preset-scss": "^1.0.3",
|
|
58
58
|
"@storybook/react": "^7.0.27",
|