@gravity-ui/dynamic-forms 4.4.0 → 4.6.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 (47) hide show
  1. package/build/cjs/lib/core/components/Form/DynamicField.js +3 -3
  2. package/build/cjs/lib/core/components/Form/hooks/useIntegrationFF.js +2 -2
  3. package/build/cjs/lib/core/components/View/DynamicView.js +8 -2
  4. package/build/cjs/lib/core/components/View/index.js +3 -0
  5. package/build/cjs/lib/kit/components/Inputs/DateInput/DateInput.css +3 -0
  6. package/build/cjs/lib/kit/components/Inputs/DateInput/DateInput.js +50 -0
  7. package/build/cjs/lib/kit/components/Inputs/DateInput/index.js +4 -0
  8. package/build/cjs/lib/kit/components/Inputs/TableArrayInput/TableArrayInput.js +7 -2
  9. package/build/cjs/lib/kit/components/Inputs/index.js +1 -0
  10. package/build/cjs/lib/kit/components/ViewLayouts/ViewAccordeon/ViewAccordeon.js +5 -1
  11. package/build/cjs/lib/kit/components/ViewLayouts/ViewRow/ViewRow.css +3 -0
  12. package/build/cjs/lib/kit/components/ViewLayouts/ViewRow/ViewRow.js +4 -0
  13. package/build/cjs/lib/kit/components/Views/DateView/DateView.js +22 -0
  14. package/build/cjs/lib/kit/components/Views/DateView/index.js +4 -0
  15. package/build/cjs/lib/kit/components/Views/TableArrayView/TableArrayView.js +9 -3
  16. package/build/cjs/lib/kit/components/Views/index.js +1 -0
  17. package/build/cjs/lib/kit/constants/config.js +2 -0
  18. package/build/esm/lib/core/components/Form/DynamicField.d.ts +1 -0
  19. package/build/esm/lib/core/components/Form/DynamicField.js +3 -3
  20. package/build/esm/lib/core/components/Form/hooks/useIntegrationFF.d.ts +1 -1
  21. package/build/esm/lib/core/components/Form/hooks/useIntegrationFF.js +2 -2
  22. package/build/esm/lib/core/components/View/DynamicView.d.ts +2 -1
  23. package/build/esm/lib/core/components/View/DynamicView.js +8 -2
  24. package/build/esm/lib/core/components/View/index.d.ts +1 -0
  25. package/build/esm/lib/core/components/View/index.js +1 -0
  26. package/build/esm/lib/core/components/View/types/context.d.ts +1 -0
  27. package/build/esm/lib/core/types/specs.d.ts +5 -0
  28. package/build/esm/lib/kit/components/Inputs/DateInput/DateInput.css +3 -0
  29. package/build/esm/lib/kit/components/Inputs/DateInput/DateInput.d.ts +9 -0
  30. package/build/esm/lib/kit/components/Inputs/DateInput/DateInput.js +46 -0
  31. package/build/esm/lib/kit/components/Inputs/DateInput/index.d.ts +1 -0
  32. package/build/esm/lib/kit/components/Inputs/DateInput/index.js +1 -0
  33. package/build/esm/lib/kit/components/Inputs/TableArrayInput/TableArrayInput.js +8 -3
  34. package/build/esm/lib/kit/components/Inputs/index.d.ts +1 -0
  35. package/build/esm/lib/kit/components/Inputs/index.js +1 -0
  36. package/build/esm/lib/kit/components/ViewLayouts/ViewAccordeon/ViewAccordeon.js +5 -1
  37. package/build/esm/lib/kit/components/ViewLayouts/ViewRow/ViewRow.css +3 -0
  38. package/build/esm/lib/kit/components/ViewLayouts/ViewRow/ViewRow.js +4 -0
  39. package/build/esm/lib/kit/components/Views/DateView/DateView.d.ts +3 -0
  40. package/build/esm/lib/kit/components/Views/DateView/DateView.js +18 -0
  41. package/build/esm/lib/kit/components/Views/DateView/index.d.ts +1 -0
  42. package/build/esm/lib/kit/components/Views/DateView/index.js +1 -0
  43. package/build/esm/lib/kit/components/Views/TableArrayView/TableArrayView.js +11 -5
  44. package/build/esm/lib/kit/components/Views/index.d.ts +1 -0
  45. package/build/esm/lib/kit/components/Views/index.js +1 -0
  46. package/build/esm/lib/kit/constants/config.js +3 -1
  47. package/package.json +3 -1
@@ -3,19 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DynamicField = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const react_1 = tslib_1.__importDefault(require("react"));
6
+ const get_1 = tslib_1.__importDefault(require("lodash/get"));
6
7
  const isFunction_1 = tslib_1.__importDefault(require("lodash/isFunction"));
7
8
  const isString_1 = tslib_1.__importDefault(require("lodash/isString"));
8
- const get_1 = tslib_1.__importDefault(require("lodash/get"));
9
9
  const react_is_1 = require("react-is");
10
10
  const helpers_1 = require("../../helpers");
11
11
  const Controller_1 = require("./Controller");
12
12
  const hooks_1 = require("./hooks");
13
13
  const utils_1 = require("./utils");
14
- const DynamicField = ({ name, spec, config, Monaco, generateRandomValue, search, withoutInsertFFDebounce, mutators: externalMutators, __mirror, }) => {
14
+ const DynamicField = ({ name, spec, config, Monaco, generateRandomValue, search, withoutInsertFFDebounce, destroyOnUnregister = true, mutators: externalMutators, __mirror, }) => {
15
15
  const DynamicFormsCtx = (0, hooks_1.useCreateContext)();
16
16
  const SearchContext = (0, hooks_1.useCreateSearchContext)();
17
17
  const { tools, store } = (0, hooks_1.useStore)(name);
18
- const watcher = (0, hooks_1.useIntegrationFF)(store, withoutInsertFFDebounce);
18
+ const watcher = (0, hooks_1.useIntegrationFF)(store, withoutInsertFFDebounce, destroyOnUnregister);
19
19
  const { mutatorsStore, mutateDFState } = (0, hooks_1.useMutators)(externalMutators);
20
20
  const { store: searchStore, setField, removeField, isHiddenField } = (0, hooks_1.useSearchStore)();
21
21
  const context = react_1.default.useMemo(() => ({
@@ -10,7 +10,7 @@ const isFunction_1 = tslib_1.__importDefault(require("lodash/isFunction"));
10
10
  const values_1 = tslib_1.__importDefault(require("lodash/values"));
11
11
  const react_final_form_1 = require("react-final-form");
12
12
  const utils_1 = require("../utils");
13
- const useIntegrationFF = (store, withoutDebounce) => {
13
+ const useIntegrationFF = (store, withoutDebounce, destroyOnUnregister) => {
14
14
  const form = (0, react_final_form_1.useForm)();
15
15
  const watcher = react_1.default.useMemo(() => {
16
16
  const props = {
@@ -55,7 +55,7 @@ const useIntegrationFF = (store, withoutDebounce) => {
55
55
  }, [store.values]);
56
56
  react_1.default.useEffect(() => {
57
57
  return () => {
58
- if (store.name) {
58
+ if (store.name && destroyOnUnregister) {
59
59
  form.change(store.name, undefined);
60
60
  }
61
61
  };
@@ -8,9 +8,15 @@ const helpers_1 = require("../../helpers");
8
8
  const ViewController_1 = require("./ViewController");
9
9
  const helpers_2 = require("./helpers");
10
10
  const hooks_1 = require("./hooks");
11
- const DynamicView = ({ value, spec, config, Link, Monaco }) => {
11
+ const DynamicView = ({ value, spec, config, Link, Monaco, showLayoutDescription, }) => {
12
12
  const DynamicFormsCtx = (0, hooks_1.useCreateContext)();
13
- const context = react_1.default.useMemo(() => ({ config, value, Link, Monaco: (0, react_is_1.isValidElementType)(Monaco) ? Monaco : undefined }), [config, value, Link, Monaco]);
13
+ const context = react_1.default.useMemo(() => ({
14
+ config,
15
+ value,
16
+ showLayoutDescription,
17
+ Link,
18
+ Monaco: (0, react_is_1.isValidElementType)(Monaco) ? Monaco : undefined,
19
+ }), [config, value, Link, Monaco, showLayoutDescription]);
14
20
  if ((0, helpers_1.isCorrectSpec)(spec) && (0, helpers_2.isCorrectViewConfig)(config)) {
15
21
  return (react_1.default.createElement(DynamicFormsCtx.Provider, { value: context },
16
22
  react_1.default.createElement(ViewController_1.ViewController, { spec: spec, name: "" })));
@@ -1,7 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useDynamicFormsCtx = void 0;
3
4
  const tslib_1 = require("tslib");
4
5
  tslib_1.__exportStar(require("./DynamicView"), exports);
5
6
  tslib_1.__exportStar(require("./ViewController"), exports);
6
7
  tslib_1.__exportStar(require("./helpers"), exports);
7
8
  tslib_1.__exportStar(require("./types"), exports);
9
+ var hooks_1 = require("./hooks");
10
+ Object.defineProperty(exports, "useDynamicFormsCtx", { enumerable: true, get: function () { return hooks_1.useDynamicFormsCtx; } });
@@ -0,0 +1,3 @@
1
+ .df-date-input {
2
+ width: 100%;
3
+ }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DateInput = exports.DEFAULT_DATE_FORMAT = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importStar(require("react"));
6
+ const date_components_1 = require("@gravity-ui/date-components");
7
+ const date_utils_1 = require("@gravity-ui/date-utils");
8
+ const utils_1 = require("../../../utils");
9
+ exports.DEFAULT_DATE_FORMAT = 'DD-MM-YYYY';
10
+ const b = (0, utils_1.block)('date-input');
11
+ const DateInput = ({ name, input, spec, inputProps, }) => {
12
+ const { value, onChange, onBlur, onFocus } = input;
13
+ const dateInput = spec.viewSpec.dateInput;
14
+ const outputFormat = dateInput === null || dateInput === void 0 ? void 0 : dateInput.outputFormat;
15
+ const onUpdate = (0, react_1.useCallback)((date) => {
16
+ if (!date) {
17
+ onChange(undefined);
18
+ }
19
+ else {
20
+ switch (outputFormat) {
21
+ case 'date_time':
22
+ onChange(date);
23
+ break;
24
+ case 'date':
25
+ onChange(date.toDate());
26
+ break;
27
+ case 'timestamp':
28
+ onChange({
29
+ seconds: Math.floor((date === null || date === void 0 ? void 0 : date.toDate().getTime()) / 1000),
30
+ nanos: 0,
31
+ });
32
+ break;
33
+ case 'string':
34
+ case undefined:
35
+ case '':
36
+ onChange(date.toISOString());
37
+ break;
38
+ default:
39
+ onChange(date.format(outputFormat));
40
+ break;
41
+ }
42
+ }
43
+ }, [outputFormat]);
44
+ const props = Object.assign(Object.assign({ hasClear: true, format: (dateInput === null || dateInput === void 0 ? void 0 : dateInput.printFormat) || exports.DEFAULT_DATE_FORMAT }, inputProps), { onBlur: onBlur, onFocus: onFocus, value: value
45
+ ? (0, date_utils_1.dateTimeParse)(value.seconds ? value.seconds * 1000 : value) || null
46
+ : null, onUpdate, disabled: spec.viewSpec.disabled, placeholder: spec.viewSpec.placeholder });
47
+ return react_1.default.createElement(date_components_1.DatePicker, Object.assign({ className: b(), "data-qa": name }, props));
48
+ };
49
+ exports.DateInput = DateInput;
50
+ exports.default = exports.DateInput;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./DateInput"), exports);
@@ -5,6 +5,7 @@ const tslib_1 = require("tslib");
5
5
  const react_1 = tslib_1.__importDefault(require("react"));
6
6
  const icons_1 = require("@gravity-ui/icons");
7
7
  const uikit_1 = require("@gravity-ui/uikit");
8
+ const components_1 = require("@gravity-ui/components");
8
9
  const noop_1 = tslib_1.__importDefault(require("lodash/noop"));
9
10
  const set_1 = tslib_1.__importDefault(require("lodash/set"));
10
11
  const core_1 = require("../../../../core");
@@ -45,9 +46,13 @@ const TableArrayInput = ({ spec, name, arrayInput, input }) => {
45
46
  template: ({ key }) => (react_1.default.createElement(uikit_1.Button, { view: "flat-secondary", onClick: () => onItemRemove(key), key: `remove-${key}`, qa: `${name}-item-remove-${key}` },
46
47
  react_1.default.createElement(uikit_1.Icon, { data: icons_1.TrashBin, size: 16 }))),
47
48
  };
48
- const columns = table.map(({ property, label }) => ({
49
+ const columns = table.map(({ property, label, description }) => ({
49
50
  id: property,
50
- name: label,
51
+ name: !description
52
+ ? label
53
+ : () => (react_1.default.createElement(uikit_1.Flex, { gap: 0.5, alignItems: "center" },
54
+ label,
55
+ react_1.default.createElement(components_1.HelpPopover, { htmlContent: description, placement: ['bottom', 'top'] }))),
51
56
  template: ({ key, }, idx) => {
52
57
  var _a, _b, _c;
53
58
  const entitySpec = (_a = items === null || items === void 0 ? void 0 : items.properties) === null || _a === void 0 ? void 0 : _a[property];
@@ -5,6 +5,7 @@ tslib_1.__exportStar(require("./ArrayBase"), exports);
5
5
  tslib_1.__exportStar(require("./CardOneOf"), exports);
6
6
  tslib_1.__exportStar(require("./Checkbox"), exports);
7
7
  tslib_1.__exportStar(require("./FileInput"), exports);
8
+ tslib_1.__exportStar(require("./DateInput"), exports);
8
9
  tslib_1.__exportStar(require("./MonacoInput"), exports);
9
10
  tslib_1.__exportStar(require("./MultiOneOf"), exports);
10
11
  tslib_1.__exportStar(require("./MultiSelect"), exports);
@@ -4,13 +4,17 @@ exports.ViewAccordeon = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const react_1 = tslib_1.__importDefault(require("react"));
6
6
  const isBoolean_1 = tslib_1.__importDefault(require("lodash/isBoolean"));
7
+ const core_1 = require("../../../../core");
7
8
  const utils_1 = require("../../../utils");
8
9
  const SimpleVerticalAccordeon_1 = require("../../SimpleVerticalAccordeon");
9
10
  const ViewAccordeon = ({ name, value, spec, children, }) => {
11
+ const { showLayoutDescription } = (0, core_1.useDynamicFormsCtx)();
10
12
  const [open, setOpen] = react_1.default.useState((0, isBoolean_1.default)(spec.viewSpec.layoutOpen) ? spec.viewSpec.layoutOpen : true);
11
13
  if (!(0, utils_1.isNotEmptyValue)(value, spec)) {
12
14
  return null;
13
15
  }
14
- return (react_1.default.createElement(SimpleVerticalAccordeon_1.SimpleVerticalAccordeon, { name: name, title: spec.viewSpec.layoutTitle || '', open: open, onOpenChange: setOpen, hideInsteadOfDestroy: true, withBranchView: true, viewLayout: true }, children));
16
+ return (react_1.default.createElement(SimpleVerticalAccordeon_1.SimpleVerticalAccordeon, { name: name, title: spec.viewSpec.layoutTitle || '', note: showLayoutDescription && spec.viewSpec.layoutDescription
17
+ ? spec.viewSpec.layoutDescription
18
+ : undefined, open: open, onOpenChange: setOpen, hideInsteadOfDestroy: true, withBranchView: true, viewLayout: true }, children));
15
19
  };
16
20
  exports.ViewAccordeon = ViewAccordeon;
@@ -14,6 +14,9 @@
14
14
  align-items: baseline;
15
15
  overflow: hidden;
16
16
  }
17
+ .df-view-row__note {
18
+ margin-inline-start: var(--g-spacing-half);
19
+ }
17
20
  .df-view-row__dots {
18
21
  min-width: 40px;
19
22
  flex-grow: 1;
@@ -4,16 +4,20 @@ exports.ViewRow = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const react_1 = tslib_1.__importDefault(require("react"));
6
6
  const uikit_1 = require("@gravity-ui/uikit");
7
+ const components_1 = require("@gravity-ui/components");
8
+ const core_1 = require("../../../../core");
7
9
  const kit_1 = require("../../../../kit");
8
10
  const utils_1 = require("../../../utils");
9
11
  const b = (0, utils_1.block)('view-row');
10
12
  const ViewRow = ({ value, spec, children, }) => {
13
+ const { showLayoutDescription } = (0, core_1.useDynamicFormsCtx)();
11
14
  if (!(0, utils_1.isNotEmptyValue)(value, spec)) {
12
15
  return null;
13
16
  }
14
17
  return (react_1.default.createElement("div", { className: b() },
15
18
  react_1.default.createElement("div", { className: b('left') },
16
19
  react_1.default.createElement(uikit_1.Text, { whiteSpace: "nowrap", color: "secondary", ellipsis: true }, spec.viewSpec.layoutTitle),
20
+ showLayoutDescription && spec.viewSpec.layoutDescription ? (react_1.default.createElement(components_1.HelpPopover, { className: b('note'), htmlContent: spec.viewSpec.layoutDescription, placement: ['bottom', 'top'] })) : null,
17
21
  react_1.default.createElement("div", { className: b('dots') })),
18
22
  react_1.default.createElement("div", { className: b('right') }, children),
19
23
  react_1.default.createElement(kit_1.CopyButton, { spec: spec, value: value })));
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DateView = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
+ const components_1 = require("../../../components");
7
+ const date_utils_1 = require("@gravity-ui/date-utils");
8
+ const isObject_1 = tslib_1.__importDefault(require("lodash/isObject"));
9
+ const DateView = (_a) => {
10
+ var _b, _c, _d;
11
+ var { value, spec } = _a, restProps = tslib_1.__rest(_a, ["value", "spec"]);
12
+ let formatedValue = value && (0, isObject_1.default)(value) && value.seconds
13
+ ? (value === null || value === void 0 ? void 0 : value.seconds) * 1000
14
+ : value;
15
+ const localSpec = spec.viewSpec;
16
+ const format = ((_b = localSpec.inputProps) === null || _b === void 0 ? void 0 : _b.format) || ((_c = localSpec.dateInput) === null || _c === void 0 ? void 0 : _c.printFormat) || components_1.DEFAULT_DATE_FORMAT;
17
+ if (formatedValue && format) {
18
+ formatedValue = ((_d = (0, date_utils_1.dateTimeParse)(formatedValue)) === null || _d === void 0 ? void 0 : _d.format(format)) || formatedValue;
19
+ }
20
+ return react_1.default.createElement(components_1.BaseView, Object.assign({ spec: spec, value: String(formatedValue) }, restProps));
21
+ };
22
+ exports.DateView = DateView;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./DateView"), exports);
@@ -6,8 +6,10 @@ const react_1 = tslib_1.__importDefault(require("react"));
6
6
  const uikit_1 = require("@gravity-ui/uikit");
7
7
  const core_1 = require("../../../../core");
8
8
  const utils_1 = require("../../../utils");
9
+ const components_1 = require("@gravity-ui/components");
9
10
  const b = (0, utils_1.block)('table-array-view');
10
11
  const TableArrayView = ({ value = [], spec, name }) => {
12
+ const { showLayoutDescription } = (0, core_1.useDynamicFormsCtx)();
11
13
  const columns = react_1.default.useMemo(() => {
12
14
  const { items, viewSpec: { table }, } = spec;
13
15
  if (!(table === null || table === void 0 ? void 0 : table.length) || !(0, core_1.isObjectSpec)(items)) {
@@ -19,9 +21,13 @@ const TableArrayView = ({ value = [], spec, name }) => {
19
21
  sticky: 'left',
20
22
  template: (__, idx) => (react_1.default.createElement("div", { className: b('idx'), key: `idx-${idx}` }, idx + 1)),
21
23
  };
22
- const columns = table.map(({ property, label }) => ({
24
+ const columns = table.map(({ property, label, description }) => ({
23
25
  id: property,
24
- name: label,
26
+ name: description && showLayoutDescription
27
+ ? () => (react_1.default.createElement(uikit_1.Flex, { gap: 0.5, alignItems: "center" },
28
+ label,
29
+ react_1.default.createElement(components_1.HelpPopover, { htmlContent: description, placement: ['bottom', 'top'] })))
30
+ : label,
25
31
  template: (_, idx) => {
26
32
  var _a;
27
33
  const entitySpec = (_a = items === null || items === void 0 ? void 0 : items.properties) === null || _a === void 0 ? void 0 : _a[property];
@@ -37,7 +43,7 @@ const TableArrayView = ({ value = [], spec, name }) => {
37
43
  },
38
44
  }));
39
45
  return [idxColumn, ...columns];
40
- }, [name, spec]);
46
+ }, [name, spec, showLayoutDescription]);
41
47
  if (!columns) {
42
48
  return null;
43
49
  }
@@ -15,3 +15,4 @@ tslib_1.__exportStar(require("./OneOfView"), exports);
15
15
  tslib_1.__exportStar(require("./TableArrayView"), exports);
16
16
  tslib_1.__exportStar(require("./TextAreaView"), exports);
17
17
  tslib_1.__exportStar(require("./TextLinkView"), exports);
18
+ tslib_1.__exportStar(require("./DateView"), exports);
@@ -94,6 +94,7 @@ exports.dynamicConfig = {
94
94
  select: { Component: components_1.Select },
95
95
  base: { Component: components_1.Text },
96
96
  file_input: { Component: components_1.FileInput },
97
+ date_input: { Component: components_1.DateInput },
97
98
  number_with_scale: { Component: components_1.NumberWithScale },
98
99
  monaco_input: { Component: components_1.MonacoInput },
99
100
  text_content: { Component: components_1.TextContent, independent: true },
@@ -193,6 +194,7 @@ exports.dynamicViewConfig = {
193
194
  textarea: { Component: components_1.TextAreaView },
194
195
  select: { Component: components_1.BaseView },
195
196
  base: { Component: components_1.BaseView },
197
+ date_input: { Component: components_1.DateView },
196
198
  file_input: { Component: components_1.FileInputView },
197
199
  number_with_scale: { Component: components_1.NumberWithScaleView },
198
200
  monaco_input: { Component: components_1.MonacoView },
@@ -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
+ destroyOnUnregister?: boolean;
13
14
  mutators?: DynamicFormMutators;
14
15
  __mirror?: WonderMirror;
15
16
  }
@@ -1,17 +1,17 @@
1
1
  import React from 'react';
2
+ import get from 'lodash/get';
2
3
  import isFunction from 'lodash/isFunction';
3
4
  import isString from 'lodash/isString';
4
- import get from 'lodash/get';
5
5
  import { isValidElementType } from 'react-is';
6
6
  import { isCorrectSpec } from '../../helpers';
7
7
  import { Controller } from './Controller';
8
8
  import { useCreateContext, useCreateSearchContext, useDynamicFieldMirror, useIntegrationFF, useMutators, useSearchStore, useStore, } from './hooks';
9
9
  import { getDefaultSearchFunction, isCorrectConfig } from './utils';
10
- export const DynamicField = ({ name, spec, config, Monaco, generateRandomValue, search, withoutInsertFFDebounce, mutators: externalMutators, __mirror, }) => {
10
+ export const DynamicField = ({ name, spec, config, Monaco, generateRandomValue, search, withoutInsertFFDebounce, destroyOnUnregister = true, mutators: externalMutators, __mirror, }) => {
11
11
  const DynamicFormsCtx = useCreateContext();
12
12
  const SearchContext = useCreateSearchContext();
13
13
  const { tools, store } = useStore(name);
14
- const watcher = useIntegrationFF(store, withoutInsertFFDebounce);
14
+ const watcher = useIntegrationFF(store, withoutInsertFFDebounce, destroyOnUnregister);
15
15
  const { mutatorsStore, mutateDFState } = useMutators(externalMutators);
16
16
  const { store: searchStore, setField, removeField, isHiddenField } = useSearchStore();
17
17
  const context = React.useMemo(() => ({
@@ -1,2 +1,2 @@
1
1
  import { DynamicFieldStore } from '../types';
2
- export declare const useIntegrationFF: (store: DynamicFieldStore, withoutDebounce?: boolean) => JSX.Element;
2
+ export declare const useIntegrationFF: (store: DynamicFieldStore, withoutDebounce?: boolean, destroyOnUnregister?: boolean) => JSX.Element;
@@ -6,7 +6,7 @@ import isFunction from 'lodash/isFunction';
6
6
  import values from 'lodash/values';
7
7
  import { Field as FinalFormField, useForm } from 'react-final-form';
8
8
  import { transformArrOut } from '../utils';
9
- export const useIntegrationFF = (store, withoutDebounce) => {
9
+ export const useIntegrationFF = (store, withoutDebounce, destroyOnUnregister) => {
10
10
  const form = useForm();
11
11
  const watcher = React.useMemo(() => {
12
12
  const props = {
@@ -51,7 +51,7 @@ export const useIntegrationFF = (store, withoutDebounce) => {
51
51
  }, [store.values]);
52
52
  React.useEffect(() => {
53
53
  return () => {
54
- if (store.name) {
54
+ if (store.name && destroyOnUnregister) {
55
55
  form.change(store.name, undefined);
56
56
  }
57
57
  };
@@ -11,5 +11,6 @@ export interface DynamicViewProps {
11
11
  link: Spec['viewSpec']['link'];
12
12
  }>;
13
13
  Monaco?: React.ComponentType<MonacoEditorProps>;
14
+ showLayoutDescription?: boolean;
14
15
  }
15
- export declare const DynamicView: ({ value, spec, config, Link, Monaco }: DynamicViewProps) => JSX.Element | null;
16
+ export declare const DynamicView: ({ value, spec, config, Link, Monaco, showLayoutDescription, }: DynamicViewProps) => JSX.Element | null;
@@ -4,9 +4,15 @@ import { isCorrectSpec } from '../../helpers';
4
4
  import { ViewController } from './ViewController';
5
5
  import { isCorrectViewConfig } from './helpers';
6
6
  import { useCreateContext } from './hooks';
7
- export const DynamicView = ({ value, spec, config, Link, Monaco }) => {
7
+ export const DynamicView = ({ value, spec, config, Link, Monaco, showLayoutDescription, }) => {
8
8
  const DynamicFormsCtx = useCreateContext();
9
- const context = React.useMemo(() => ({ config, value, Link, Monaco: isValidElementType(Monaco) ? Monaco : undefined }), [config, value, Link, Monaco]);
9
+ const context = React.useMemo(() => ({
10
+ config,
11
+ value,
12
+ showLayoutDescription,
13
+ Link,
14
+ Monaco: isValidElementType(Monaco) ? Monaco : undefined,
15
+ }), [config, value, Link, Monaco, showLayoutDescription]);
10
16
  if (isCorrectSpec(spec) && isCorrectViewConfig(config)) {
11
17
  return (React.createElement(DynamicFormsCtx.Provider, { value: context },
12
18
  React.createElement(ViewController, { spec: spec, name: "" })));
@@ -2,3 +2,4 @@ export * from './DynamicView';
2
2
  export * from './ViewController';
3
3
  export * from './helpers';
4
4
  export * from './types';
5
+ export { useDynamicFormsCtx } from './hooks';
@@ -2,3 +2,4 @@ export * from './DynamicView';
2
2
  export * from './ViewController';
3
3
  export * from './helpers';
4
4
  export * from './types';
5
+ export { useDynamicFormsCtx } from './hooks';
@@ -5,6 +5,7 @@ import { DynamicViewConfig } from './';
5
5
  export interface DynamicViewContext {
6
6
  config: DynamicViewConfig;
7
7
  value: FormValue;
8
+ showLayoutDescription?: boolean;
8
9
  Link?: React.ComponentType<{
9
10
  value: FormValue;
10
11
  link: Spec['viewSpec']['link'];
@@ -24,6 +24,7 @@ export interface ArraySpec<LinkType = any, InputComponentProps extends Record<st
24
24
  table?: {
25
25
  label: string;
26
26
  property: string;
27
+ description?: string;
27
28
  }[];
28
29
  link?: LinkType;
29
30
  placeholder?: string;
@@ -150,6 +151,10 @@ export interface StringSpec<LinkType = any, InputComponentProps extends Record<s
150
151
  readAsMethod?: ReadAsMethod;
151
152
  ignoreText?: boolean;
152
153
  };
154
+ dateInput?: {
155
+ outputFormat?: string;
156
+ printFormat?: string;
157
+ };
153
158
  copy?: boolean;
154
159
  selectParams?: {
155
160
  filterPlaceholder?: string;
@@ -0,0 +1,3 @@
1
+ .df-date-input {
2
+ width: 100%;
3
+ }
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { DatePickerProps } from '@gravity-ui/date-components';
3
+ import { StringInputProps } from '../../../../core';
4
+ import './DateInput.css';
5
+ export declare const DEFAULT_DATE_FORMAT = "DD-MM-YYYY";
6
+ export interface DateProps extends Omit<DatePickerProps, 'value' | 'disabled' | 'placeholder' | 'qa'> {
7
+ }
8
+ export declare const DateInput: React.FC<StringInputProps<DateProps>>;
9
+ export default DateInput;
@@ -0,0 +1,46 @@
1
+ import React, { useCallback } from 'react';
2
+ import { DatePicker } from '@gravity-ui/date-components';
3
+ import { dateTimeParse } from '@gravity-ui/date-utils';
4
+ import { block } from '../../../utils';
5
+ import './DateInput.css';
6
+ export const DEFAULT_DATE_FORMAT = 'DD-MM-YYYY';
7
+ const b = block('date-input');
8
+ export const DateInput = ({ name, input, spec, inputProps, }) => {
9
+ const { value, onChange, onBlur, onFocus } = input;
10
+ const dateInput = spec.viewSpec.dateInput;
11
+ const outputFormat = dateInput === null || dateInput === void 0 ? void 0 : dateInput.outputFormat;
12
+ const onUpdate = useCallback((date) => {
13
+ if (!date) {
14
+ onChange(undefined);
15
+ }
16
+ else {
17
+ switch (outputFormat) {
18
+ case 'date_time':
19
+ onChange(date);
20
+ break;
21
+ case 'date':
22
+ onChange(date.toDate());
23
+ break;
24
+ case 'timestamp':
25
+ onChange({
26
+ seconds: Math.floor((date === null || date === void 0 ? void 0 : date.toDate().getTime()) / 1000),
27
+ nanos: 0,
28
+ });
29
+ break;
30
+ case 'string':
31
+ case undefined:
32
+ case '':
33
+ onChange(date.toISOString());
34
+ break;
35
+ default:
36
+ onChange(date.format(outputFormat));
37
+ break;
38
+ }
39
+ }
40
+ }, [outputFormat]);
41
+ const props = Object.assign(Object.assign({ hasClear: true, format: (dateInput === null || dateInput === void 0 ? void 0 : dateInput.printFormat) || DEFAULT_DATE_FORMAT }, inputProps), { onBlur: onBlur, onFocus: onFocus, value: value
42
+ ? dateTimeParse(value.seconds ? value.seconds * 1000 : value) || null
43
+ : null, onUpdate, disabled: spec.viewSpec.disabled, placeholder: spec.viewSpec.placeholder });
44
+ return React.createElement(DatePicker, Object.assign({ className: b(), "data-qa": name }, props));
45
+ };
46
+ export default DateInput;
@@ -0,0 +1 @@
1
+ export * from './DateInput';
@@ -0,0 +1 @@
1
+ export * from './DateInput';
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { Plus, TrashBin } from '@gravity-ui/icons';
3
- import { Button, Icon, Table } from '@gravity-ui/uikit';
3
+ import { Button, Flex, Icon, Table } from '@gravity-ui/uikit';
4
+ import { HelpPopover } from '@gravity-ui/components';
4
5
  import noop from 'lodash/noop';
5
6
  import set from 'lodash/set';
6
7
  import { Controller, OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG, isArraySpec, isBooleanSpec, isObjectSpec, transformArrIn, } from '../../../../core';
@@ -42,9 +43,13 @@ export const TableArrayInput = ({ spec, name, arrayInput, input }) => {
42
43
  template: ({ key }) => (React.createElement(Button, { view: "flat-secondary", onClick: () => onItemRemove(key), key: `remove-${key}`, qa: `${name}-item-remove-${key}` },
43
44
  React.createElement(Icon, { data: TrashBin, size: 16 }))),
44
45
  };
45
- const columns = table.map(({ property, label }) => ({
46
+ const columns = table.map(({ property, label, description }) => ({
46
47
  id: property,
47
- name: label,
48
+ name: !description
49
+ ? label
50
+ : () => (React.createElement(Flex, { gap: 0.5, alignItems: "center" },
51
+ label,
52
+ React.createElement(HelpPopover, { htmlContent: description, placement: ['bottom', 'top'] }))),
48
53
  template: ({ key, }, idx) => {
49
54
  var _a, _b, _c;
50
55
  const entitySpec = (_a = items === null || items === void 0 ? void 0 : items.properties) === null || _a === void 0 ? void 0 : _a[property];
@@ -2,6 +2,7 @@ export * from './ArrayBase';
2
2
  export * from './CardOneOf';
3
3
  export * from './Checkbox';
4
4
  export * from './FileInput';
5
+ export * from './DateInput';
5
6
  export * from './MonacoInput';
6
7
  export * from './MultiOneOf';
7
8
  export * from './MultiSelect';
@@ -2,6 +2,7 @@ export * from './ArrayBase';
2
2
  export * from './CardOneOf';
3
3
  export * from './Checkbox';
4
4
  export * from './FileInput';
5
+ export * from './DateInput';
5
6
  export * from './MonacoInput';
6
7
  export * from './MultiOneOf';
7
8
  export * from './MultiSelect';
@@ -1,11 +1,15 @@
1
1
  import React from 'react';
2
2
  import isBoolean from 'lodash/isBoolean';
3
+ import { useDynamicFormsCtx } from '../../../../core';
3
4
  import { isNotEmptyValue } from '../../../utils';
4
5
  import { SimpleVerticalAccordeon } from '../../SimpleVerticalAccordeon';
5
6
  export const ViewAccordeon = ({ name, value, spec, children, }) => {
7
+ const { showLayoutDescription } = useDynamicFormsCtx();
6
8
  const [open, setOpen] = React.useState(isBoolean(spec.viewSpec.layoutOpen) ? spec.viewSpec.layoutOpen : true);
7
9
  if (!isNotEmptyValue(value, spec)) {
8
10
  return null;
9
11
  }
10
- return (React.createElement(SimpleVerticalAccordeon, { name: name, title: spec.viewSpec.layoutTitle || '', open: open, onOpenChange: setOpen, hideInsteadOfDestroy: true, withBranchView: true, viewLayout: true }, children));
12
+ return (React.createElement(SimpleVerticalAccordeon, { name: name, title: spec.viewSpec.layoutTitle || '', note: showLayoutDescription && spec.viewSpec.layoutDescription
13
+ ? spec.viewSpec.layoutDescription
14
+ : undefined, open: open, onOpenChange: setOpen, hideInsteadOfDestroy: true, withBranchView: true, viewLayout: true }, children));
11
15
  };
@@ -14,6 +14,9 @@
14
14
  align-items: baseline;
15
15
  overflow: hidden;
16
16
  }
17
+ .df-view-row__note {
18
+ margin-inline-start: var(--g-spacing-half);
19
+ }
17
20
  .df-view-row__dots {
18
21
  min-width: 40px;
19
22
  flex-grow: 1;
@@ -1,16 +1,20 @@
1
1
  import React from 'react';
2
2
  import { Text } from '@gravity-ui/uikit';
3
+ import { HelpPopover } from '@gravity-ui/components';
4
+ import { useDynamicFormsCtx } from '../../../../core';
3
5
  import { CopyButton } from '../../../../kit';
4
6
  import { block, isNotEmptyValue } from '../../../utils';
5
7
  import './ViewRow.css';
6
8
  const b = block('view-row');
7
9
  export const ViewRow = ({ value, spec, children, }) => {
10
+ const { showLayoutDescription } = useDynamicFormsCtx();
8
11
  if (!isNotEmptyValue(value, spec)) {
9
12
  return null;
10
13
  }
11
14
  return (React.createElement("div", { className: b() },
12
15
  React.createElement("div", { className: b('left') },
13
16
  React.createElement(Text, { whiteSpace: "nowrap", color: "secondary", ellipsis: true }, spec.viewSpec.layoutTitle),
17
+ showLayoutDescription && spec.viewSpec.layoutDescription ? (React.createElement(HelpPopover, { className: b('note'), htmlContent: spec.viewSpec.layoutDescription, placement: ['bottom', 'top'] })) : null,
14
18
  React.createElement("div", { className: b('dots') })),
15
19
  React.createElement("div", { className: b('right') }, children),
16
20
  React.createElement(CopyButton, { spec: spec, value: value })));
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import { StringViewProps } from '../../../../core';
3
+ export declare const DateView: React.FC<StringViewProps>;
@@ -0,0 +1,18 @@
1
+ import { __rest } from "tslib";
2
+ import React from 'react';
3
+ import { BaseView, DEFAULT_DATE_FORMAT } from '../../../components';
4
+ import { dateTimeParse } from '@gravity-ui/date-utils';
5
+ import isObject from 'lodash/isObject';
6
+ export const DateView = (_a) => {
7
+ var _b, _c, _d;
8
+ var { value, spec } = _a, restProps = __rest(_a, ["value", "spec"]);
9
+ let formatedValue = value && isObject(value) && value.seconds
10
+ ? (value === null || value === void 0 ? void 0 : value.seconds) * 1000
11
+ : value;
12
+ const localSpec = spec.viewSpec;
13
+ const format = ((_b = localSpec.inputProps) === null || _b === void 0 ? void 0 : _b.format) || ((_c = localSpec.dateInput) === null || _c === void 0 ? void 0 : _c.printFormat) || DEFAULT_DATE_FORMAT;
14
+ if (formatedValue && format) {
15
+ formatedValue = ((_d = dateTimeParse(formatedValue)) === null || _d === void 0 ? void 0 : _d.format(format)) || formatedValue;
16
+ }
17
+ return React.createElement(BaseView, Object.assign({ spec: spec, value: String(formatedValue) }, restProps));
18
+ };
@@ -0,0 +1 @@
1
+ export * from './DateView';
@@ -0,0 +1 @@
1
+ export * from './DateView';
@@ -1,10 +1,12 @@
1
1
  import React from 'react';
2
- import { Table } from '@gravity-ui/uikit';
3
- import { ViewController, isArraySpec, isBooleanSpec, isObjectSpec, } from '../../../../core';
2
+ import { Flex, Table } from '@gravity-ui/uikit';
3
+ import { ViewController, isArraySpec, isBooleanSpec, isObjectSpec, useDynamicFormsCtx, } from '../../../../core';
4
4
  import { block } from '../../../utils';
5
5
  import './TableArrayView.css';
6
+ import { HelpPopover } from '@gravity-ui/components';
6
7
  const b = block('table-array-view');
7
8
  export const TableArrayView = ({ value = [], spec, name }) => {
9
+ const { showLayoutDescription } = useDynamicFormsCtx();
8
10
  const columns = React.useMemo(() => {
9
11
  const { items, viewSpec: { table }, } = spec;
10
12
  if (!(table === null || table === void 0 ? void 0 : table.length) || !isObjectSpec(items)) {
@@ -16,9 +18,13 @@ export const TableArrayView = ({ value = [], spec, name }) => {
16
18
  sticky: 'left',
17
19
  template: (__, idx) => (React.createElement("div", { className: b('idx'), key: `idx-${idx}` }, idx + 1)),
18
20
  };
19
- const columns = table.map(({ property, label }) => ({
21
+ const columns = table.map(({ property, label, description }) => ({
20
22
  id: property,
21
- name: label,
23
+ name: description && showLayoutDescription
24
+ ? () => (React.createElement(Flex, { gap: 0.5, alignItems: "center" },
25
+ label,
26
+ React.createElement(HelpPopover, { htmlContent: description, placement: ['bottom', 'top'] })))
27
+ : label,
22
28
  template: (_, idx) => {
23
29
  var _a;
24
30
  const entitySpec = (_a = items === null || items === void 0 ? void 0 : items.properties) === null || _a === void 0 ? void 0 : _a[property];
@@ -34,7 +40,7 @@ export const TableArrayView = ({ value = [], spec, name }) => {
34
40
  },
35
41
  }));
36
42
  return [idxColumn, ...columns];
37
- }, [name, spec]);
43
+ }, [name, spec, showLayoutDescription]);
38
44
  if (!columns) {
39
45
  return null;
40
46
  }
@@ -12,3 +12,4 @@ export * from './OneOfView';
12
12
  export * from './TableArrayView';
13
13
  export * from './TextAreaView';
14
14
  export * from './TextLinkView';
15
+ export * from './DateView';
@@ -12,3 +12,4 @@ export * from './OneOfView';
12
12
  export * from './TableArrayView';
13
13
  export * from './TextAreaView';
14
14
  export * from './TextLinkView';
15
+ export * from './DateView';
@@ -1,4 +1,4 @@
1
- import { Accordeon, AccordeonCardForm, ArrayBase, ArrayBaseView, BaseView, CardAccordeon, CardOneOf, CardOneOfView, CardSection, Checkbox, FileInput, FileInputView, Group, Group2, MonacoInput, MonacoView, MultiOneOf, MultiOneOfFlat, MultiOneOfFlatView, MultiOneOfView, MultiSelect, MultiSelectView, NumberWithScale, NumberWithScaleView, ObjectBase, ObjectBaseView, ObjectInline, ObjectInlineView, ObjectValueInput, ObjectValueInputView, OneOf, OneOfFlat, OneOfFlatView, OneOfView, Row, RowVerbose, Secret, Section, Section2, Select, Switch, TableArrayInput, TableArrayView, TableCell, Text, TextArea, TextAreaView, TextContent, TextLink, TextLinkView, Transparent, ViewAccordeon, ViewAccordeonCard, ViewCardAccordeon, ViewCardSection, ViewGroup, ViewGroup2, ViewRow, ViewSection, ViewSection2, ViewTableCell, ViewTransparent, } from '../components';
1
+ import { Accordeon, AccordeonCardForm, ArrayBase, ArrayBaseView, BaseView, CardAccordeon, CardOneOf, CardOneOfView, CardSection, Checkbox, DateInput, DateView, FileInput, FileInputView, Group, Group2, MonacoInput, MonacoView, MultiOneOf, MultiOneOfFlat, MultiOneOfFlatView, MultiOneOfView, MultiSelect, MultiSelectView, NumberWithScale, NumberWithScaleView, ObjectBase, ObjectBaseView, ObjectInline, ObjectInlineView, ObjectValueInput, ObjectValueInputView, OneOf, OneOfFlat, OneOfFlatView, OneOfView, Row, RowVerbose, Secret, Section, Section2, Select, Switch, TableArrayInput, TableArrayView, TableCell, Text, TextArea, TextAreaView, TextContent, TextLink, TextLinkView, Transparent, ViewAccordeon, ViewAccordeonCard, ViewCardAccordeon, ViewCardSection, ViewGroup, ViewGroup2, ViewRow, ViewSection, ViewSection2, ViewTableCell, ViewTransparent, } from '../components';
2
2
  import { getArrayValidator, getBooleanValidator, getNumberValidator, getObjectValidator, getStringValidator, } from '../validators';
3
3
  export const dynamicConfig = {
4
4
  array: {
@@ -91,6 +91,7 @@ export const dynamicConfig = {
91
91
  select: { Component: Select },
92
92
  base: { Component: Text },
93
93
  file_input: { Component: FileInput },
94
+ date_input: { Component: DateInput },
94
95
  number_with_scale: { Component: NumberWithScale },
95
96
  monaco_input: { Component: MonacoInput },
96
97
  text_content: { Component: TextContent, independent: true },
@@ -190,6 +191,7 @@ export const dynamicViewConfig = {
190
191
  textarea: { Component: TextAreaView },
191
192
  select: { Component: BaseView },
192
193
  base: { Component: BaseView },
194
+ date_input: { Component: DateView },
193
195
  file_input: { Component: FileInputView },
194
196
  number_with_scale: { Component: NumberWithScaleView },
195
197
  monaco_input: { Component: MonacoView },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/dynamic-forms",
3
- "version": "4.4.0",
3
+ "version": "4.6.0",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "main": "build/cjs/index.js",
@@ -45,6 +45,8 @@
45
45
  "dependencies": {
46
46
  "@bem-react/classname": "^1.6.0",
47
47
  "@gravity-ui/components": "^3.0.0",
48
+ "@gravity-ui/date-components": "^2.5.0",
49
+ "@gravity-ui/date-utils": "^2.5.3",
48
50
  "@gravity-ui/i18n": "^1.2.0",
49
51
  "@gravity-ui/icons": "^2.8.1",
50
52
  "lodash": "^4.17.20"