@gravity-ui/dynamic-forms 1.8.0 → 1.9.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 (65) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/build/cjs/lib/core/components/Form/DynamicField.js +2 -2
  3. package/build/cjs/lib/core/components/Form/constants.js +1 -2
  4. package/build/cjs/lib/core/components/Form/hooks/useField.js +17 -13
  5. package/build/cjs/lib/core/components/Form/hooks/useIntegrationFF.js +19 -4
  6. package/build/cjs/lib/core/components/Form/utils/common.js +1 -3
  7. package/build/cjs/lib/kit/components/CopyButton/CopyButton.css +4 -0
  8. package/build/cjs/lib/kit/components/CopyButton/CopyButton.js +20 -0
  9. package/build/cjs/lib/kit/components/CopyButton/index.js +4 -0
  10. package/build/cjs/lib/kit/components/Inputs/ArrayBase/ArrayBase.js +3 -6
  11. package/build/cjs/lib/kit/components/Inputs/CardOneOf/CardOneOf.js +1 -2
  12. package/build/cjs/lib/kit/components/Inputs/ObjectBase/ObjectBase.js +2 -3
  13. package/build/cjs/lib/kit/components/Inputs/ObjectValueInput/ObjectValueInput.js +1 -2
  14. package/build/cjs/lib/kit/components/Inputs/OneOf/OneOf.js +1 -2
  15. package/build/cjs/lib/kit/components/Inputs/OneOfCard/OneOfCard.js +1 -2
  16. package/build/cjs/lib/kit/components/Inputs/Secret/Secret.js +1 -2
  17. package/build/cjs/lib/kit/components/Inputs/TableArrayInput/TableArrayInput.js +3 -6
  18. package/build/cjs/lib/kit/components/Inputs/TextLink/TextLink.js +1 -2
  19. package/build/cjs/lib/kit/components/ViewLayouts/ViewRow/ViewRow.css +3 -0
  20. package/build/cjs/lib/kit/components/ViewLayouts/ViewRow/ViewRow.js +3 -1
  21. package/build/cjs/lib/kit/components/ViewLayouts/ViewTableCell/ViewTableCell.css +9 -3
  22. package/build/cjs/lib/kit/components/ViewLayouts/ViewTableCell/ViewTableCell.js +7 -1
  23. package/build/cjs/lib/kit/components/ViewLayouts/ViewTransparent/ViewTransparent.css +4 -0
  24. package/build/cjs/lib/kit/components/ViewLayouts/ViewTransparent/ViewTransparent.js +4 -1
  25. package/build/cjs/lib/kit/components/Views/ArrayBaseView.js +2 -1
  26. package/build/cjs/lib/kit/components/index.js +1 -0
  27. package/build/cjs/lib/kit/constants/config.js +5 -0
  28. package/build/cjs/lib/kit/styles/mixins.css +0 -0
  29. package/build/esm/lib/core/components/Form/DynamicField.d.ts +1 -0
  30. package/build/esm/lib/core/components/Form/DynamicField.js +2 -2
  31. package/build/esm/lib/core/components/Form/constants.d.ts +0 -1
  32. package/build/esm/lib/core/components/Form/constants.js +0 -1
  33. package/build/esm/lib/core/components/Form/hooks/useField.d.ts +1 -1
  34. package/build/esm/lib/core/components/Form/hooks/useField.js +18 -14
  35. package/build/esm/lib/core/components/Form/hooks/useIntegrationFF.d.ts +1 -1
  36. package/build/esm/lib/core/components/Form/hooks/useIntegrationFF.js +19 -4
  37. package/build/esm/lib/core/components/Form/types/field.d.ts +1 -0
  38. package/build/esm/lib/core/components/Form/utils/common.js +2 -4
  39. package/build/esm/lib/core/types/specs.d.ts +2 -0
  40. package/build/esm/lib/kit/components/CopyButton/CopyButton.css +4 -0
  41. package/build/esm/lib/kit/components/CopyButton/CopyButton.d.ts +12 -0
  42. package/build/esm/lib/kit/components/CopyButton/CopyButton.js +16 -0
  43. package/build/esm/lib/kit/components/CopyButton/index.d.ts +1 -0
  44. package/build/esm/lib/kit/components/CopyButton/index.js +1 -0
  45. package/build/esm/lib/kit/components/Inputs/ArrayBase/ArrayBase.js +4 -7
  46. package/build/esm/lib/kit/components/Inputs/CardOneOf/CardOneOf.js +1 -2
  47. package/build/esm/lib/kit/components/Inputs/ObjectBase/ObjectBase.js +2 -3
  48. package/build/esm/lib/kit/components/Inputs/ObjectValueInput/ObjectValueInput.js +1 -2
  49. package/build/esm/lib/kit/components/Inputs/OneOf/OneOf.js +1 -2
  50. package/build/esm/lib/kit/components/Inputs/OneOfCard/OneOfCard.js +1 -2
  51. package/build/esm/lib/kit/components/Inputs/Secret/Secret.js +1 -2
  52. package/build/esm/lib/kit/components/Inputs/TableArrayInput/TableArrayInput.js +4 -7
  53. package/build/esm/lib/kit/components/Inputs/TextLink/TextLink.js +1 -2
  54. package/build/esm/lib/kit/components/ViewLayouts/ViewRow/ViewRow.css +3 -0
  55. package/build/esm/lib/kit/components/ViewLayouts/ViewRow/ViewRow.js +3 -1
  56. package/build/esm/lib/kit/components/ViewLayouts/ViewTableCell/ViewTableCell.css +9 -3
  57. package/build/esm/lib/kit/components/ViewLayouts/ViewTableCell/ViewTableCell.js +7 -1
  58. package/build/esm/lib/kit/components/ViewLayouts/ViewTransparent/ViewTransparent.css +4 -0
  59. package/build/esm/lib/kit/components/ViewLayouts/ViewTransparent/ViewTransparent.js +4 -1
  60. package/build/esm/lib/kit/components/Views/ArrayBaseView.js +2 -1
  61. package/build/esm/lib/kit/components/index.d.ts +1 -0
  62. package/build/esm/lib/kit/components/index.js +1 -0
  63. package/build/esm/lib/kit/constants/config.js +5 -0
  64. package/build/esm/lib/kit/styles/mixins.css +0 -0
  65. package/package.json +2 -1
@@ -1,16 +1,11 @@
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, REMOVED_ITEM } from '../constants';
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, }) => {
6
+ export const useField = ({ name, spec, initialValue, value: externalValue, validate: propsValidate, tools, parentOnChange, parentOnUnmount: externalParentOnUnmount, }) => {
7
7
  const firstRenderRef = React.useRef(true);
8
- const validate = React.useCallback((value) => {
9
- if (value === REMOVED_ITEM) {
10
- return;
11
- }
12
- return propsValidate === null || propsValidate === void 0 ? void 0 : propsValidate(transformArrOut(value));
13
- }, [propsValidate]);
8
+ const validate = React.useCallback((value) => propsValidate === null || propsValidate === void 0 ? void 0 : propsValidate(transformArrOut(value)), [propsValidate]);
14
9
  const [state, setState] = React.useState(() => {
15
10
  let value = _.cloneDeep(externalValue);
16
11
  if (_.isNil(value)) {
@@ -67,20 +62,29 @@ export const useField = ({ name, spec, initialValue, value: externalValue, valid
67
62
  };
68
63
  const onDrop = () => {
69
64
  if (isArrayItem(name)) {
70
- onChange(REMOVED_ITEM);
65
+ (externalParentOnUnmount ? externalParentOnUnmount : tools.onUnmount)(name);
71
66
  }
72
67
  else {
73
68
  onChange(undefined);
74
69
  }
75
70
  };
76
71
  return { onChange, onDrop };
77
- }, [initialValue, setState, name, validate, spec]);
72
+ }, [initialValue, setState, name, validate, spec, externalParentOnUnmount, tools.onUnmount]);
78
73
  const onBlur = React.useCallback(() => {
79
74
  setState((state) => (Object.assign(Object.assign({}, state), { active: false, touched: true })));
80
75
  }, [setState]);
81
76
  const onFocus = React.useCallback(() => {
82
77
  setState((state) => (Object.assign(Object.assign({}, state), { active: true, visited: true })));
83
78
  }, [setState]);
79
+ const parentOnUnmount = React.useCallback((childName) => {
80
+ if (isArraySpec(spec) || isObjectSpec(spec)) {
81
+ onChange((currentValue) => currentValue
82
+ ? _.omit(currentValue, childName.split(`${name}.`)[1])
83
+ : currentValue, {
84
+ [childName]: false,
85
+ });
86
+ }
87
+ }, [onChange, name, spec]);
84
88
  const renderProps = React.useMemo(() => {
85
89
  const onItemAdd = (_value) => {
86
90
  const stateValue = (state.value || {
@@ -92,9 +96,7 @@ export const useField = ({ name, spec, initialValue, value: externalValue, valid
92
96
  setState((state) => (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 })));
93
97
  };
94
98
  const onItemRemove = (idx) => {
95
- const value = Object.assign(Object.assign({}, state.value), { [`<${idx}>`]: REMOVED_ITEM });
96
- const error = validate === null || validate === void 0 ? void 0 : validate(value);
97
- setState((state) => (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 })));
99
+ parentOnUnmount(`${name}.<${idx}>`);
98
100
  };
99
101
  return {
100
102
  input: {
@@ -104,6 +106,7 @@ export const useField = ({ name, spec, initialValue, value: externalValue, valid
104
106
  onBlur,
105
107
  onFocus,
106
108
  onDrop,
109
+ parentOnUnmount,
107
110
  },
108
111
  arrayInput: {
109
112
  name,
@@ -125,6 +128,7 @@ export const useField = ({ name, spec, initialValue, value: externalValue, valid
125
128
  onBlur,
126
129
  onFocus,
127
130
  onDrop,
131
+ parentOnUnmount,
128
132
  ]);
129
133
  React.useEffect(() => {
130
134
  if (!firstRenderRef.current || !_.isEqual(externalValue, state.value) || state.error) {
@@ -134,7 +138,7 @@ export const useField = ({ name, spec, initialValue, value: externalValue, valid
134
138
  React.useEffect(() => {
135
139
  firstRenderRef.current = false;
136
140
  return () => {
137
- (parentOnUnmount ? parentOnUnmount : tools.onUnmount)(name);
141
+ (externalParentOnUnmount ? externalParentOnUnmount : tools.onUnmount)(name);
138
142
  };
139
143
  }, []);
140
144
  return renderProps;
@@ -1,2 +1,2 @@
1
1
  import { DynamicFieldStore } from '../types';
2
- export declare const useIntegrationFF: (store: DynamicFieldStore) => JSX.Element;
2
+ export declare const useIntegrationFF: (store: DynamicFieldStore, withoutDebounce?: boolean) => JSX.Element;
@@ -3,7 +3,7 @@ import _ from 'lodash';
3
3
  import debounce from 'lodash/debounce';
4
4
  import { Field as FinalFormField, useForm } from 'react-final-form';
5
5
  import { transformArrOut } from '../utils';
6
- export const useIntegrationFF = (store) => {
6
+ export const useIntegrationFF = (store, withoutDebounce) => {
7
7
  const form = useForm();
8
8
  const watcher = React.useMemo(() => {
9
9
  const props = {
@@ -31,11 +31,26 @@ export const useIntegrationFF = (store) => {
31
31
  };
32
32
  return React.createElement(FinalFormField, Object.assign({}, props));
33
33
  }, [store.name, store.errors]);
34
- const change = React.useCallback(debounce((value) => {
35
- form.change(store.name, _.get(transformArrOut(value), store.name));
36
- }, 100), [form.change, store.name]);
34
+ const change = React.useMemo(() => {
35
+ const cb = (value) => {
36
+ if (store.name) {
37
+ form.change(store.name, _.get(transformArrOut(value), store.name));
38
+ }
39
+ };
40
+ if (withoutDebounce) {
41
+ return cb;
42
+ }
43
+ return debounce(cb, 100);
44
+ }, [form.change, store.name, withoutDebounce]);
37
45
  React.useEffect(() => {
38
46
  change(store.values);
39
47
  }, [store.values]);
48
+ React.useEffect(() => {
49
+ return () => {
50
+ if (store.name) {
51
+ form.change(store.name, undefined);
52
+ }
53
+ };
54
+ }, []);
40
55
  return watcher;
41
56
  };
@@ -8,6 +8,7 @@ export interface FieldRenderProps<Value extends FieldValue> {
8
8
  onChange: (value: Value | ((currentValue: Value) => Value), childErrors?: Record<string, ValidateError>) => void;
9
9
  onFocus: (event?: React.FocusEvent<HTMLElement>) => void;
10
10
  onDrop: () => void;
11
+ parentOnUnmount: (childName: string) => void;
11
12
  };
12
13
  arrayInput: {
13
14
  name: string;
@@ -1,6 +1,6 @@
1
1
  import _ from 'lodash';
2
2
  import { SpecTypes } from '../../../constants';
3
- import { OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG, REMOVED_ITEM } from '../constants';
3
+ import { OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG } from '../constants';
4
4
  export const isCorrectConfig = (candidate) => Object.values(SpecTypes).every((type) => _.isObjectLike(candidate) &&
5
5
  _.isObjectLike(candidate[type]) &&
6
6
  _.isObjectLike(candidate[type].inputs) &&
@@ -26,9 +26,7 @@ export const transformArrOut = (value) => {
26
26
  if (_.isObject(value) && !_.isArray(value)) {
27
27
  if (value[OBJECT_ARRAY_FLAG]) {
28
28
  const _value = Object.keys(value)
29
- .filter((key) => key !== OBJECT_ARRAY_FLAG &&
30
- key !== OBJECT_ARRAY_CNT &&
31
- value[key] !== REMOVED_ITEM)
29
+ .filter((key) => key !== OBJECT_ARRAY_FLAG && key !== OBJECT_ARRAY_CNT)
32
30
  .map((key) => key.split('<').join('').split('>').join(''))
33
31
  .sort((a, b) => Number(a) - Number(b))
34
32
  .map((key) => transformArrOut(value[`<${key}>`]));
@@ -59,6 +59,7 @@ export interface NumberSpec<LinkType = any> {
59
59
  layoutOpen?: boolean;
60
60
  link?: LinkType;
61
61
  placeholder?: string;
62
+ copy?: boolean;
62
63
  };
63
64
  }
64
65
  export interface ObjectSpec<LinkType = any> {
@@ -121,6 +122,7 @@ export interface StringSpec<LinkType = any> {
121
122
  readAsMethod?: ReadAsMethod;
122
123
  ignoreText?: boolean;
123
124
  };
125
+ copy?: boolean;
124
126
  };
125
127
  }
126
128
  export type Spec = ArraySpec | BooleanSpec | NumberSpec | ObjectSpec | StringSpec;
@@ -0,0 +1,4 @@
1
+ .df-copy-button {
2
+ display: none;
3
+ margin: 2px 0 0 5px;
4
+ }
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import { FormValue, Spec } from '../../../core';
3
+ import './CopyButton.css';
4
+ export interface CopyButtonProps {
5
+ spec: Spec;
6
+ value: FormValue;
7
+ }
8
+ /**
9
+ *
10
+ * Use the with-copy-button scss mixin for visible on hover
11
+ */
12
+ export declare const CopyButton: React.FC<CopyButtonProps>;
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import { ClipboardButton } from '@gravity-ui/uikit';
3
+ import { isNumberSpec, isStringSpec } from '../../../core';
4
+ import { block } from '../../utils';
5
+ import './CopyButton.css';
6
+ const b = block('copy-button');
7
+ /**
8
+ *
9
+ * Use the with-copy-button scss mixin for visible on hover
10
+ */
11
+ export const CopyButton = ({ spec, value }) => {
12
+ if ((isStringSpec(spec) || isNumberSpec(spec)) && spec.viewSpec.copy) {
13
+ return React.createElement(ClipboardButton, { className: b(), text: `${value}`, size: 14 });
14
+ }
15
+ return null;
16
+ };
@@ -0,0 +1 @@
1
+ export * from './CopyButton';
@@ -0,0 +1 @@
1
+ export * from './CopyButton';
@@ -2,12 +2,10 @@ import React from 'react';
2
2
  import { Plus } from '@gravity-ui/icons';
3
3
  import { Button, Icon } from '@gravity-ui/uikit';
4
4
  import _ from 'lodash';
5
- import { Controller, OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG, REMOVED_ITEM, isArraySpec, isCorrectSpec, isObjectSpec, transformArrIn, } from '../../../../core';
5
+ import { Controller, OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG, isArraySpec, isCorrectSpec, isObjectSpec, transformArrIn, } from '../../../../core';
6
6
  export const ArrayBase = ({ spec, name, arrayInput, input }) => {
7
7
  const keys = React.useMemo(() => Object.keys(arrayInput.value || {})
8
- .filter((k) => k !== OBJECT_ARRAY_FLAG &&
9
- k !== OBJECT_ARRAY_CNT &&
10
- arrayInput.value[k] !== REMOVED_ITEM)
8
+ .filter((k) => k !== OBJECT_ARRAY_FLAG && k !== OBJECT_ARRAY_CNT)
11
9
  .map((k) => k.split('<').join('').split('>').join(''))
12
10
  .sort((a, b) => Number(a) - Number(b)), [arrayInput.value]);
13
11
  const itemSpecCorrect = React.useMemo(() => isCorrectSpec(spec.items), [spec.items]);
@@ -35,15 +33,14 @@ export const ArrayBase = ({ spec, name, arrayInput, input }) => {
35
33
  return itemSpec;
36
34
  }, [spec.items, itemSpecCorrect]);
37
35
  const parentOnChange = React.useCallback((childName, childValue, childErrors) => input.onChange((currentValue) => _.set(Object.assign({}, currentValue), childName.split(`${input.name}.`).join(''), childValue), childErrors), [input.onChange, input.name]);
38
- const parentOnUnmount = React.useCallback((childName) => input.onChange((currentValue) => currentValue, { [childName]: false }), [input.onChange]);
39
36
  const items = React.useMemo(() => keys.map((key, idx) => {
40
37
  var _a;
41
38
  const itemSpec = getItemSpec(idx);
42
39
  if (!itemSpec) {
43
40
  return null;
44
41
  }
45
- return (React.createElement(Controller, { value: (_a = input.value) === null || _a === void 0 ? void 0 : _a[`<${key}>`], parentOnChange: parentOnChange, parentOnUnmount: parentOnUnmount, spec: itemSpec, name: `${name}.<${key}>`, key: `${name}.<${key}>` }));
46
- }), [keys.join(''), name, getItemSpec, parentOnChange, parentOnUnmount, input.value]);
42
+ return (React.createElement(Controller, { value: (_a = input.value) === null || _a === void 0 ? void 0 : _a[`<${key}>`], parentOnChange: parentOnChange, parentOnUnmount: input.parentOnUnmount, spec: itemSpec, name: `${name}.<${key}>`, key: `${name}.<${key}>` }));
43
+ }), [keys.join(''), name, getItemSpec, parentOnChange, input.parentOnUnmount, input.value]);
47
44
  if (!itemSpecCorrect) {
48
45
  return null;
49
46
  }
@@ -26,7 +26,6 @@ export const CardOneOf = (props) => {
26
26
  const value = _.set({}, childName.split(`${input.name}.`).join(''), childValue);
27
27
  input.onChange(value, childErrors);
28
28
  }, [input.onChange, input.name]);
29
- const parentOnUnmount = React.useCallback((childName) => input.onChange((currentValue) => currentValue, { [childName]: false }), [input.onChange]);
30
29
  useErrorChecker({ name, meta, open, setOpen });
31
- return (React.createElement(Card, { name: name, title: toggler, description: spec.viewSpec.layoutDescription, actions: actions, open: open, onToggle: onToggle, disableHeaderToggle: true }, specProperties[oneOfValue] ? (React.createElement(Controller, { value: (_a = input.value) === null || _a === void 0 ? void 0 : _a[oneOfValue], spec: specProperties[oneOfValue], name: `${name}.${oneOfValue}`, parentOnChange: parentOnChange, parentOnUnmount: parentOnUnmount, key: `${name}.${oneOfValue}` })) : null));
30
+ return (React.createElement(Card, { name: name, title: toggler, description: spec.viewSpec.layoutDescription, actions: actions, open: open, onToggle: onToggle, disableHeaderToggle: true }, specProperties[oneOfValue] ? (React.createElement(Controller, { value: (_a = input.value) === null || _a === void 0 ? void 0 : _a[oneOfValue], spec: specProperties[oneOfValue], name: `${name}.${oneOfValue}`, parentOnChange: parentOnChange, parentOnUnmount: input.parentOnUnmount, key: `${name}.${oneOfValue}` })) : null));
32
31
  };
@@ -13,7 +13,6 @@ export const ObjectBase = (_a) => {
13
13
  spec.viewSpec.layoutTitle || null));
14
14
  }, [spec.defaultValue, spec.viewSpec.layoutTitle, restProps.input.onChange]);
15
15
  const parentOnChange = React.useCallback((childName, childValue, childErrors) => restProps.input.onChange((currentValue) => _.set(Object.assign({}, currentValue), childName.split(`${restProps.input.name}.`).join(''), childValue), childErrors), [restProps.input.onChange, restProps.input.name]);
16
- const parentOnUnmount = React.useCallback((childName) => restProps.input.onChange((currentValue) => currentValue, { [childName]: false }), [restProps.input.onChange]);
17
16
  const content = React.useMemo(() => {
18
17
  if (!_.isObjectLike(spec.properties) || !Object.keys(spec.properties || {}).length) {
19
18
  return null;
@@ -24,7 +23,7 @@ export const ObjectBase = (_a) => {
24
23
  const specProperties = Object.assign({}, spec.properties);
25
24
  return (React.createElement(React.Fragment, null, (spec.viewSpec.order || Object.keys(specProperties)).map((property) => {
26
25
  var _a;
27
- return specProperties[property] ? (React.createElement(Controller, { value: (_a = restProps.input.value) === null || _a === void 0 ? void 0 : _a[property], spec: specProperties[property], name: `${name ? name + '.' : ''}${property}`, parentOnChange: parentOnChange, parentOnUnmount: parentOnUnmount, key: `${name ? name + '.' : ''}${property}` })) : null;
26
+ return specProperties[property] ? (React.createElement(Controller, { value: (_a = restProps.input.value) === null || _a === void 0 ? void 0 : _a[property], spec: specProperties[property], name: `${name ? name + '.' : ''}${property}`, parentOnChange: parentOnChange, parentOnUnmount: restProps.input.parentOnUnmount, key: `${name ? name + '.' : ''}${property}` })) : null;
28
27
  })));
29
28
  }, [
30
29
  spec.properties,
@@ -33,7 +32,7 @@ export const ObjectBase = (_a) => {
33
32
  restProps.input.value,
34
33
  addBtn,
35
34
  parentOnChange,
36
- parentOnUnmount,
35
+ restProps.input.parentOnUnmount,
37
36
  ]);
38
37
  if (!Layout || !content) {
39
38
  return content;
@@ -6,7 +6,6 @@ export const ObjectValueInput = (props) => {
6
6
  var _a;
7
7
  const { spec, input, name, Layout } = props;
8
8
  const parentOnChange = React.useCallback((childName, childValue, childErrors) => input.onChange((currentValue) => _.set(Object.assign({}, currentValue), childName.split(`${name}.`).join(''), childValue), childErrors), [input.onChange, input.name]);
9
- const parentOnUnmount = React.useCallback((childName) => input.onChange((currentValue) => currentValue, { [childName]: false }), [input.onChange]);
10
9
  const childSpec = React.useMemo(() => {
11
10
  var _a;
12
11
  if ((_a = spec.properties) === null || _a === void 0 ? void 0 : _a[OBJECT_VALUE_PROPERTY_NAME]) {
@@ -19,7 +18,7 @@ export const ObjectValueInput = (props) => {
19
18
  if (!childSpec) {
20
19
  return null;
21
20
  }
22
- const content = (React.createElement(Controller, { value: (_a = input.value) === null || _a === void 0 ? void 0 : _a[OBJECT_VALUE_PROPERTY_NAME], spec: childSpec, name: `${name}.${OBJECT_VALUE_PROPERTY_NAME}`, key: `${name}.${OBJECT_VALUE_PROPERTY_NAME}`, parentOnChange: parentOnChange, parentOnUnmount: parentOnUnmount }));
21
+ const content = (React.createElement(Controller, { value: (_a = input.value) === null || _a === void 0 ? void 0 : _a[OBJECT_VALUE_PROPERTY_NAME], spec: childSpec, name: `${name}.${OBJECT_VALUE_PROPERTY_NAME}`, key: `${name}.${OBJECT_VALUE_PROPERTY_NAME}`, parentOnChange: parentOnChange, parentOnUnmount: input.parentOnUnmount }));
23
22
  if (Layout) {
24
23
  return React.createElement(Layout, Object.assign({}, props), content);
25
24
  }
@@ -13,14 +13,13 @@ const OneOfComponent = (props) => {
13
13
  const value = _.set({}, childName.split(`${props.input.name}.`).join(''), childValue);
14
14
  props.input.onChange(value, childErrors);
15
15
  }, [props.input.onChange, props.input.name]);
16
- const parentOnUnmount = React.useCallback((childName) => props.input.onChange((currentValue) => currentValue, { [childName]: false }), [props.input.onChange]);
17
16
  return (React.createElement("div", { className: b({
18
17
  base: !props.withoutIndent,
19
18
  flat: props.withoutIndent,
20
19
  }) },
21
20
  React.createElement("div", null, toggler),
22
21
  specProperties[oneOfValue] ? (React.createElement(GroupIndent, null,
23
- React.createElement(Controller, { value: (_a = props.input.value) === null || _a === void 0 ? void 0 : _a[oneOfValue], spec: specProperties[oneOfValue], name: `${props.name}.${oneOfValue}`, parentOnChange: parentOnChange, parentOnUnmount: parentOnUnmount, key: `${props.name}.${oneOfValue}` }))) : null));
22
+ React.createElement(Controller, { value: (_a = props.input.value) === null || _a === void 0 ? void 0 : _a[oneOfValue], spec: specProperties[oneOfValue], name: `${props.name}.${oneOfValue}`, parentOnChange: parentOnChange, parentOnUnmount: props.input.parentOnUnmount, key: `${props.name}.${oneOfValue}` }))) : null));
24
23
  };
25
24
  export const OneOf = OneOfComponent;
26
25
  export const OneOfFlat = (props) => (React.createElement(OneOfComponent, Object.assign({}, props, { withoutIndent: true })));
@@ -34,7 +34,6 @@ export const OneOfCard = (props) => {
34
34
  const value = _.set({}, childName.split(`${input.name}.`).join(''), childValue);
35
35
  input.onChange(value, childErrors);
36
36
  }, [input.onChange, input.name]);
37
- const parentOnUnmount = React.useCallback((childName) => input.onChange((currentValue) => currentValue, { [childName]: false }), [input.onChange]);
38
37
  useErrorChecker({ name, meta, open, setOpen });
39
- return (React.createElement(AccordeonCard, { className: b(), name: name, header: toggler, description: spec.viewSpec.layoutDescription || '', open: open, onToggle: onToggle, ignoreHeaderToggle: true, headerActionsTemplate: headerActionsTemplate }, specProperties[oneOfValue] ? (React.createElement(Controller, { value: (_a = props.input.value) === null || _a === void 0 ? void 0 : _a[oneOfValue], spec: specProperties[oneOfValue], name: `${name}.${oneOfValue}`, parentOnChange: parentOnChange, parentOnUnmount: parentOnUnmount, key: `${name}.${oneOfValue}` })) : null));
38
+ return (React.createElement(AccordeonCard, { className: b(), name: name, header: toggler, description: spec.viewSpec.layoutDescription || '', open: open, onToggle: onToggle, ignoreHeaderToggle: true, headerActionsTemplate: headerActionsTemplate }, specProperties[oneOfValue] ? (React.createElement(Controller, { value: (_a = props.input.value) === null || _a === void 0 ? void 0 : _a[oneOfValue], spec: specProperties[oneOfValue], name: `${name}.${oneOfValue}`, parentOnChange: parentOnChange, parentOnUnmount: input.parentOnUnmount, key: `${name}.${oneOfValue}` })) : null));
40
39
  };
@@ -15,11 +15,10 @@ export const Secret = (props) => {
15
15
  return undefined;
16
16
  }, [spec.properties]);
17
17
  const parentOnChange = React.useCallback((childName, childValue, childErrors) => input.onChange((currentValue) => _.set(Object.assign({}, currentValue), childName.split(`${input.name}.`).join(''), childValue), childErrors), [input.onChange, input.name]);
18
- const parentOnUnmount = React.useCallback((childName) => input.onChange((currentValue) => currentValue, { [childName]: false }), [input.onChange]);
19
18
  if (!childSpec) {
20
19
  return null;
21
20
  }
22
- const content = (React.createElement(Controller, { value: (_a = input.value) === null || _a === void 0 ? void 0 : _a[SECRET_PROPERTY_NAME], spec: childSpec, name: `${name}.${SECRET_PROPERTY_NAME}`, parentOnChange: parentOnChange, parentOnUnmount: parentOnUnmount, key: `${name}.${SECRET_PROPERTY_NAME}` }));
21
+ const content = (React.createElement(Controller, { value: (_a = input.value) === null || _a === void 0 ? void 0 : _a[SECRET_PROPERTY_NAME], spec: childSpec, name: `${name}.${SECRET_PROPERTY_NAME}`, parentOnChange: parentOnChange, parentOnUnmount: input.parentOnUnmount, key: `${name}.${SECRET_PROPERTY_NAME}` }));
23
22
  if (Layout) {
24
23
  return React.createElement(Layout, Object.assign({}, props), content);
25
24
  }
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { Plus, Xmark } from '@gravity-ui/icons';
3
3
  import { Button, Icon, Table } from '@gravity-ui/uikit';
4
4
  import _ from 'lodash';
5
- import { Controller, OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG, REMOVED_ITEM, isArraySpec, isBooleanSpec, isObjectSpec, transformArrIn, } from '../../../../core';
5
+ import { Controller, OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG, isArraySpec, isBooleanSpec, isObjectSpec, transformArrIn, } from '../../../../core';
6
6
  import { useSearchContext } from '../../../../core/components/Form/hooks';
7
7
  import { block } from '../../../utils';
8
8
  import './TableArrayInput.css';
@@ -10,9 +10,7 @@ const b = block('table-array');
10
10
  export const TableArrayInput = ({ spec, name, arrayInput, input }) => {
11
11
  const { isHiddenField } = useSearchContext();
12
12
  const keys = React.useMemo(() => Object.keys(arrayInput.value || {})
13
- .filter((k) => k !== OBJECT_ARRAY_FLAG &&
14
- k !== OBJECT_ARRAY_CNT &&
15
- arrayInput.value[k] !== REMOVED_ITEM)
13
+ .filter((k) => k !== OBJECT_ARRAY_FLAG && k !== OBJECT_ARRAY_CNT)
16
14
  .map((k) => k.split('<').join('').split('>').join(''))
17
15
  .sort((a, b) => Number(a) - Number(b))
18
16
  .map((key) => ({
@@ -25,7 +23,6 @@ export const TableArrayInput = ({ spec, name, arrayInput, input }) => {
25
23
  arrayInput.onItemRemove(key);
26
24
  }, [arrayInput.onItemRemove]);
27
25
  const parentOnChange = React.useCallback((childName, childValue, childErrors) => input.onChange((currentValue) => _.set(Object.assign({}, currentValue), childName.split(`${input.name}.`).join(''), childValue), childErrors), [input.onChange, input.name]);
28
- const parentOnUnmount = React.useCallback((childName) => input.onChange((currentValue) => currentValue, { [childName]: false }), [input.onChange]);
29
26
  const columns = React.useMemo(() => {
30
27
  const { items, viewSpec: { table }, } = spec;
31
28
  if (!(table === null || table === void 0 ? void 0 : table.length) || !isObjectSpec(items)) {
@@ -59,11 +56,11 @@ export const TableArrayInput = ({ spec, name, arrayInput, input }) => {
59
56
  arr: isArraySpec(preparedEntitySpec),
60
57
  obj: isObjectSpec(preparedEntitySpec),
61
58
  }), key: `${name}.<${key}>.${property}` },
62
- React.createElement(Controller, { value: (_c = (_b = input.value) === null || _b === void 0 ? void 0 : _b[`<${key}>`]) === null || _c === void 0 ? void 0 : _c[property], spec: preparedEntitySpec, name: `${name}.<${key}>.${property}`, parentOnChange: parentOnChange, parentOnUnmount: parentOnUnmount })));
59
+ React.createElement(Controller, { value: (_c = (_b = input.value) === null || _b === void 0 ? void 0 : _b[`<${key}>`]) === null || _c === void 0 ? void 0 : _c[property], spec: preparedEntitySpec, name: `${name}.<${key}>.${property}`, parentOnChange: parentOnChange, parentOnUnmount: _.noop })));
63
60
  },
64
61
  }));
65
62
  return [idxColumn, ...columns, removeColumn];
66
- }, [name, spec, onItemRemove, parentOnChange, parentOnUnmount, input.value]);
63
+ }, [name, spec, onItemRemove, parentOnChange, input.parentOnUnmount, input.value]);
67
64
  const getRowClassNames = React.useCallback(({ key }) => {
68
65
  var _a;
69
66
  const searchResult = (_a = spec.viewSpec.table) === null || _a === void 0 ? void 0 : _a.every(({ property }) => isHiddenField(`${name}.<${key}>.${property}`));
@@ -6,7 +6,6 @@ export const TextLink = (props) => {
6
6
  var _a;
7
7
  const { spec, input, name, Layout } = props;
8
8
  const parentOnChange = React.useCallback((childName, childValue, childErrors) => input.onChange((currentValue) => _.set(Object.assign({}, currentValue), childName.split(`${name}.`).join(''), childValue), childErrors), [input.onChange, input.name]);
9
- const parentOnUnmount = React.useCallback((childName) => input.onChange((currentValue) => currentValue, { [childName]: false }), [input.onChange]);
10
9
  const childSpec = React.useMemo(() => {
11
10
  var _a;
12
11
  if (((_a = spec.properties) === null || _a === void 0 ? void 0 : _a[TEXT_LINK_PROPERTY_NAME]) &&
@@ -20,7 +19,7 @@ export const TextLink = (props) => {
20
19
  if (!childSpec) {
21
20
  return null;
22
21
  }
23
- const content = (React.createElement(Controller, { value: (_a = input.value) === null || _a === void 0 ? void 0 : _a[TEXT_LINK_PROPERTY_NAME], spec: childSpec, name: `${name}.${TEXT_LINK_PROPERTY_NAME}`, key: `${name}.${TEXT_LINK_PROPERTY_NAME}`, parentOnChange: parentOnChange, parentOnUnmount: parentOnUnmount }));
22
+ const content = (React.createElement(Controller, { value: (_a = input.value) === null || _a === void 0 ? void 0 : _a[TEXT_LINK_PROPERTY_NAME], spec: childSpec, name: `${name}.${TEXT_LINK_PROPERTY_NAME}`, key: `${name}.${TEXT_LINK_PROPERTY_NAME}`, parentOnChange: parentOnChange, parentOnUnmount: input.parentOnUnmount }));
24
23
  if (Layout) {
25
24
  return React.createElement(Layout, Object.assign({}, props), content);
26
25
  }
@@ -37,4 +37,7 @@
37
37
  }
38
38
  .df-view-row__right > .df-view-transparent:last-child {
39
39
  margin-bottom: 0;
40
+ }
41
+ .df-view-row:hover > .df-copy-button {
42
+ display: block;
40
43
  }
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { CopyButton } from '../../../../kit';
2
3
  import { block, isNotEmptyValue } from '../../../utils';
3
4
  import './ViewRow.css';
4
5
  const b = block('view-row');
@@ -10,5 +11,6 @@ export const ViewRow = ({ value, spec, children, }) => {
10
11
  React.createElement("div", { className: b('left') },
11
12
  React.createElement("div", { className: b('title'), title: spec.viewSpec.layoutTitle }, spec.viewSpec.layoutTitle),
12
13
  React.createElement("div", { className: b('dots') })),
13
- React.createElement("div", { className: b('right') }, children)));
14
+ React.createElement("div", { className: b('right') }, children),
15
+ React.createElement(CopyButton, { spec: spec, value: value })));
14
16
  };
@@ -1,14 +1,20 @@
1
1
  .df-view-table-cell {
2
2
  display: flex;
3
+ }
4
+ .df-view-table-cell__inner {
5
+ display: flex;
3
6
  flex-direction: column;
4
7
  justify-content: center;
5
8
  }
6
- .df-view-table-cell > .df-view-row:last-child {
9
+ .df-view-table-cell__inner > .df-view-row:last-child {
7
10
  margin-bottom: 0;
8
11
  }
9
- .df-view-table-cell > .df-view-transparent {
12
+ .df-view-table-cell__inner > .df-view-transparent {
10
13
  margin-bottom: 6px;
11
14
  }
12
- .df-view-table-cell > .df-view-transparent:last-child {
15
+ .df-view-table-cell__inner > .df-view-transparent:last-child {
13
16
  margin-bottom: 0;
17
+ }
18
+ .df-view-table-cell:hover > .df-copy-button {
19
+ display: block;
14
20
  }
@@ -1,5 +1,11 @@
1
1
  import React from 'react';
2
+ import { CopyButton } from '../../../../kit';
2
3
  import { block, isNotEmptyValue } from '../../../utils';
3
4
  import './ViewTableCell.css';
4
5
  const b = block('view-table-cell');
5
- export const ViewTableCell = ({ value, spec, children, }) => (React.createElement("div", { className: b() }, isNotEmptyValue(value, spec) ? children : '—'));
6
+ export const ViewTableCell = ({ value, spec, children, }) => {
7
+ const empty = isNotEmptyValue(value, spec);
8
+ return (React.createElement("div", { className: b() }, empty ? (React.createElement(React.Fragment, null,
9
+ React.createElement("div", { className: b('inner') }, children),
10
+ React.createElement(CopyButton, { spec: spec, value: value }))) : ('—')));
11
+ };
@@ -1,6 +1,10 @@
1
1
  .df-view-transparent {
2
+ display: flex;
2
3
  margin-bottom: 20px;
3
4
  }
4
5
  .df-view-transparent:last-child {
5
6
  margin-bottom: 0;
7
+ }
8
+ .df-view-transparent:hover > .df-copy-button {
9
+ display: block;
6
10
  }
@@ -1,5 +1,8 @@
1
1
  import React from 'react';
2
+ import { CopyButton } from '../../../../kit';
2
3
  import { block, isNotEmptyValue } from '../../../utils';
3
4
  import './ViewTransparent.css';
4
5
  const b = block('view-transparent');
5
- export const ViewTransparent = ({ value, spec, children, }) => isNotEmptyValue(value, spec) ? React.createElement("div", { className: b() }, children) : null;
6
+ export const ViewTransparent = ({ value, spec, children, }) => isNotEmptyValue(value, spec) ? (React.createElement("div", { className: b() },
7
+ React.createElement("div", null, children),
8
+ React.createElement(CopyButton, { spec: spec, value: value }))) : null;
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import _ from 'lodash';
2
3
  import { ViewController, isCorrectSpec } from '../../../core';
3
4
  export const ArrayBaseView = ({ spec, name, value = [] }) => {
4
5
  const itemSpecCorrect = React.useMemo(() => isCorrectSpec(spec.items), [spec.items]);
@@ -12,7 +13,7 @@ export const ArrayBaseView = ({ spec, name, value = [] }) => {
12
13
  : `${idx + 1}` });
13
14
  return itemSpec;
14
15
  }, [spec.items, itemSpecCorrect]);
15
- const items = React.useMemo(() => value.map((__, idx) => {
16
+ const items = React.useMemo(() => _.map(value, (__, idx) => {
16
17
  const itemSpec = getItemSpec(idx);
17
18
  if (!itemSpec) {
18
19
  return null;
@@ -1,5 +1,6 @@
1
1
  export * from './AccordeonCard';
2
2
  export * from './Card';
3
+ export * from './CopyButton';
3
4
  export * from './ErrorWrapper';
4
5
  export * from './GroupIndent';
5
6
  export * from './Inputs';
@@ -1,5 +1,6 @@
1
1
  export * from './AccordeonCard';
2
2
  export * from './Card';
3
+ export * from './CopyButton';
3
4
  export * from './ErrorWrapper';
4
5
  export * from './GroupIndent';
5
6
  export * from './Inputs';
@@ -209,6 +209,7 @@ export const dynamicViewConfig = {
209
209
  },
210
210
  layouts: {
211
211
  row: ViewRow,
212
+ row_verbose: ViewRow,
212
213
  accordeon: ViewAccordeon,
213
214
  section: ViewSection,
214
215
  section2: ViewSection2,
@@ -227,6 +228,7 @@ export const dynamicViewConfig = {
227
228
  },
228
229
  layouts: {
229
230
  row: ViewRow,
231
+ row_verbose: ViewRow,
230
232
  table_item: ViewTableCell,
231
233
  },
232
234
  },
@@ -236,6 +238,7 @@ export const dynamicViewConfig = {
236
238
  },
237
239
  layouts: {
238
240
  row: ViewRow,
241
+ row_verbose: ViewRow,
239
242
  table_item: ViewTableCell,
240
243
  transparent: ViewTransparent,
241
244
  },
@@ -252,6 +255,7 @@ export const dynamicViewConfig = {
252
255
  },
253
256
  layouts: {
254
257
  row: ViewRow,
258
+ row_verbose: ViewRow,
255
259
  accordeon: ViewAccordeon,
256
260
  section: ViewSection,
257
261
  section2: ViewSection2,
@@ -275,6 +279,7 @@ export const dynamicViewConfig = {
275
279
  },
276
280
  layouts: {
277
281
  row: ViewRow,
282
+ row_verbose: ViewRow,
278
283
  table_item: ViewTableCell,
279
284
  transparent: ViewTransparent,
280
285
  section: ViewSection,
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/dynamic-forms",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "main": "build/cjs/index.js",
@@ -57,6 +57,7 @@
57
57
  "@storybook/react": "^6.5.16",
58
58
  "@testing-library/jest-dom": "^5.16.5",
59
59
  "@testing-library/react": "^14.0.0",
60
+ "@testing-library/user-event": "^14.4.3",
60
61
  "@types/jest": "^29.5.0",
61
62
  "@types/lodash": "^4.14.180",
62
63
  "@types/react": "^18.0.28",