@gravity-ui/dynamic-forms 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/build/cjs/lib/core/components/Form/Controller.js +1 -1
  2. package/build/cjs/lib/kit/components/Inputs/TextContent/TextContent.css +11 -0
  3. package/build/cjs/lib/kit/components/Inputs/TextContent/TextContent.js +25 -8
  4. package/build/cjs/lib/kit/components/Inputs/TextContent/utils.js +22 -0
  5. package/build/cjs/lib/kit/components/Layouts/Transparent/Transparent.css +0 -3
  6. package/build/cjs/lib/kit/components/LazyLoader/LazyLoader.js +22 -0
  7. package/build/cjs/lib/kit/components/LazyLoader/index.js +4 -0
  8. package/build/cjs/lib/kit/components/TogglerCard/TogglerCard.css +18 -0
  9. package/build/cjs/lib/kit/components/TogglerCard/TogglerCard.js +17 -0
  10. package/build/cjs/lib/kit/components/TogglerCard/index.js +4 -0
  11. package/build/cjs/lib/kit/components/ViewLayouts/ViewTransparent/ViewTransparent.css +1 -0
  12. package/build/cjs/lib/kit/components/index.js +2 -0
  13. package/build/cjs/lib/kit/hooks/useOneOf/useOneOf.css +10 -0
  14. package/build/cjs/lib/kit/hooks/useOneOf/useOneOf.js +34 -9
  15. package/build/cjs/lib/kit/utils/common.js +10 -3
  16. package/build/esm/lib/core/components/Form/Controller.js +1 -1
  17. package/build/esm/lib/core/types/specs.d.ts +13 -2
  18. package/build/esm/lib/kit/components/Inputs/TextContent/TextContent.css +11 -0
  19. package/build/esm/lib/kit/components/Inputs/TextContent/TextContent.js +26 -9
  20. package/build/esm/lib/kit/components/Inputs/TextContent/utils.d.ts +2 -0
  21. package/build/esm/lib/kit/components/Inputs/TextContent/utils.js +17 -0
  22. package/build/esm/lib/kit/components/Layouts/Transparent/Transparent.css +0 -3
  23. package/build/esm/lib/kit/components/LazyLoader/LazyLoader.d.ts +6 -0
  24. package/build/esm/lib/kit/components/LazyLoader/LazyLoader.js +17 -0
  25. package/build/esm/lib/kit/components/LazyLoader/index.d.ts +1 -0
  26. package/build/esm/lib/kit/components/LazyLoader/index.js +1 -0
  27. package/build/esm/lib/kit/components/TogglerCard/TogglerCard.css +18 -0
  28. package/build/esm/lib/kit/components/TogglerCard/TogglerCard.d.ts +12 -0
  29. package/build/esm/lib/kit/components/TogglerCard/TogglerCard.js +13 -0
  30. package/build/esm/lib/kit/components/TogglerCard/index.d.ts +1 -0
  31. package/build/esm/lib/kit/components/TogglerCard/index.js +1 -0
  32. package/build/esm/lib/kit/components/ViewLayouts/ViewTransparent/ViewTransparent.css +1 -0
  33. package/build/esm/lib/kit/components/index.d.ts +2 -0
  34. package/build/esm/lib/kit/components/index.js +2 -0
  35. package/build/esm/lib/kit/hooks/useOneOf/useOneOf.css +10 -0
  36. package/build/esm/lib/kit/hooks/useOneOf/useOneOf.js +34 -9
  37. package/build/esm/lib/kit/utils/common.js +10 -3
  38. package/package.json +1 -1
@@ -28,7 +28,7 @@ const Controller = ({ spec, name, value, parentOnChange, parentOnUnmount, }) =>
28
28
  useField: renderProps,
29
29
  useSearch: withSearch,
30
30
  }, __mirror);
31
- if (lodash_1.default.isString(name) && (0, helpers_1.isCorrectSpec)(spec)) {
31
+ if (lodash_1.default.isString(name) && (0, helpers_1.isCorrectSpec)(spec) && !spec.viewSpec.hideInput) {
32
32
  return withSearch(render(renderProps));
33
33
  }
34
34
  return null;
@@ -5,4 +5,15 @@
5
5
  .df-text-content .yc-label__text {
6
6
  text-align: initial;
7
7
  white-space: initial;
8
+ }
9
+ .df-text-content__icon {
10
+ display: flex;
11
+ align-items: center;
12
+ margin-right: 4px;
13
+ }
14
+ .df-text-content__wrapper {
15
+ display: flex;
16
+ }
17
+ .df-text-content__separator {
18
+ margin: 0 4px;
8
19
  }
@@ -4,21 +4,38 @@ exports.TextContent = 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 lodash_1 = tslib_1.__importDefault(require("lodash"));
7
8
  const utils_1 = require("../../../utils");
9
+ const LazyLoader_1 = require("../../LazyLoader");
10
+ const utils_2 = require("./utils");
8
11
  const b = (0, utils_1.block)('text-content');
9
12
  const TextContent = (_a) => {
10
- var { spec, Layout } = _a, restProps = tslib_1.__rest(_a, ["spec", "Layout"]);
11
- const { themeLabel, layoutDescription } = spec.viewSpec;
12
- if (!layoutDescription) {
13
+ var _b;
14
+ var { spec, Layout, input } = _a, restProps = tslib_1.__rest(_a, ["spec", "Layout", "input"]);
15
+ const { textContentParams, layoutDescription } = spec.viewSpec;
16
+ const text = react_1.default.useMemo(() => ((textContentParams === null || textContentParams === void 0 ? void 0 : textContentParams.text) ? textContentParams === null || textContentParams === void 0 ? void 0 : textContentParams.text : layoutDescription), [layoutDescription, textContentParams === null || textContentParams === void 0 ? void 0 : textContentParams.text]);
17
+ if (!text) {
13
18
  return null;
14
19
  }
15
- let content = react_1.default.createElement("span", { dangerouslySetInnerHTML: { __html: layoutDescription } });
16
- if (themeLabel) {
17
- content = (react_1.default.createElement(uikit_1.Label, { size: "m", theme: themeLabel, className: b() }, content));
20
+ const iconLib = (textContentParams === null || textContentParams === void 0 ? void 0 : textContentParams.icon) ? (react_1.default.createElement(LazyLoader_1.LazyLoader, { component: (0, utils_2.loadIcon)(textContentParams === null || textContentParams === void 0 ? void 0 : textContentParams.icon) })) : null;
21
+ let content = react_1.default.createElement("span", { dangerouslySetInnerHTML: { __html: text } });
22
+ if (textContentParams === null || textContentParams === void 0 ? void 0 : textContentParams.themeLabel) {
23
+ content = (react_1.default.createElement(uikit_1.Label, { size: "m", theme: textContentParams.themeLabel, className: b(), value: input.value, icon: iconLib }, content));
24
+ }
25
+ else {
26
+ content = (react_1.default.createElement("div", { className: b('wrapper') },
27
+ iconLib ? (react_1.default.createElement(uikit_1.Text, { color: (_b = spec.viewSpec.textContentParams) === null || _b === void 0 ? void 0 : _b.iconColor, className: b('icon') }, iconLib)) : null,
28
+ content,
29
+ input.value ? (react_1.default.createElement(react_1.default.Fragment, null,
30
+ react_1.default.createElement("span", { className: b('separator') }, ":"),
31
+ react_1.default.createElement(uikit_1.Text, { color: "secondary" }, input.value))) : null));
18
32
  }
19
33
  if (Layout) {
20
- const _spec = Object.assign(Object.assign({}, spec), { viewSpec: Object.assign(Object.assign({}, spec.viewSpec), { layoutDescription: undefined }) });
21
- return (react_1.default.createElement(Layout, Object.assign({ spec: _spec }, restProps), content));
34
+ const _spec = lodash_1.default.cloneDeep(spec);
35
+ if (!(textContentParams === null || textContentParams === void 0 ? void 0 : textContentParams.text)) {
36
+ _spec.viewSpec.layoutDescription = undefined;
37
+ }
38
+ return (react_1.default.createElement(Layout, Object.assign({ spec: _spec, input: input }, restProps), content));
22
39
  }
23
40
  return content;
24
41
  };
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadIcon = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
+ const loadIcon = (name) => {
7
+ const Icon = react_1.default.lazy(() => {
8
+ return new Promise((resolve) => {
9
+ const icon = Promise.resolve().then(() => tslib_1.__importStar(require(`@gravity-ui/icons`))).then((module) => {
10
+ if (module[name]) {
11
+ return { default: module[name] };
12
+ }
13
+ return {
14
+ default: () => null,
15
+ };
16
+ });
17
+ resolve(icon);
18
+ });
19
+ });
20
+ return Icon;
21
+ };
22
+ exports.loadIcon = loadIcon;
@@ -11,9 +11,6 @@
11
11
  .df-transparent_without-max-width {
12
12
  max-width: unset;
13
13
  }
14
- .df-transparent_without-max-width > .df-error-wrapper {
15
- width: unset;
16
- }
17
14
  .df-transparent__remove-button {
18
15
  margin-left: 5px;
19
16
  }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LazyLoader = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
+ const lodash_1 = tslib_1.__importDefault(require("lodash"));
7
+ const LazyLoader = ({ component, initialFallback = react_1.default.createElement(react_1.default.Fragment, null), }) => {
8
+ const fallback = react_1.default.useRef(() => initialFallback);
9
+ const Component = component;
10
+ const updateFallback = async () => {
11
+ const result = await component._result;
12
+ if (!lodash_1.default.isUndefined(result)) {
13
+ fallback.current = result.default;
14
+ }
15
+ };
16
+ react_1.default.useEffect(() => {
17
+ updateFallback();
18
+ }, [component]);
19
+ return (react_1.default.createElement(react_1.default.Suspense, { fallback: react_1.default.createElement(fallback.current, null) },
20
+ react_1.default.createElement(Component, null)));
21
+ };
22
+ exports.LazyLoader = LazyLoader;
@@ -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("./LazyLoader"), exports);
@@ -0,0 +1,18 @@
1
+ .df-toggler-card {
2
+ width: 254px;
3
+ padding: 10px;
4
+ height: 88px;
5
+ }
6
+ .df-toggler-card__header {
7
+ display: flex;
8
+ justify-content: space-between;
9
+ align-items: baseline;
10
+ }
11
+ .df-toggler-card__text {
12
+ margin-top: 12px;
13
+ display: block;
14
+ margin-right: 15px;
15
+ height: 36px;
16
+ overflow: hidden;
17
+ text-overflow: ellipsis;
18
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TogglerCard = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
+ const components_1 = require("@gravity-ui/components");
7
+ const uikit_1 = require("@gravity-ui/uikit");
8
+ const utils_1 = require("../../utils");
9
+ const b = (0, utils_1.block)('toggler-card');
10
+ const TogglerCard = ({ description, title, text, onClick, disabled, selected, }) => {
11
+ return (react_1.default.createElement(uikit_1.Card, { onClick: onClick, type: "selection", disabled: disabled ? !selected : disabled, selected: selected, size: "m", className: b() },
12
+ react_1.default.createElement("div", { className: b('header') },
13
+ react_1.default.createElement(uikit_1.Text, { variant: "subheader-2", ellipsis: true }, title),
14
+ description ? (react_1.default.createElement(components_1.HelpPopover, { htmlContent: description, placement: ['bottom', 'top'] })) : null),
15
+ react_1.default.createElement(uikit_1.Text, { variant: "body-1", color: "secondary", className: b('text') }, text)));
16
+ };
17
+ exports.TogglerCard = TogglerCard;
@@ -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("./TogglerCard"), exports);
@@ -10,4 +10,5 @@
10
10
  }
11
11
  .df-view-transparent__inner {
12
12
  max-width: 100%;
13
+ width: 100%;
13
14
  }
@@ -8,7 +8,9 @@ tslib_1.__exportStar(require("./ErrorWrapper"), exports);
8
8
  tslib_1.__exportStar(require("./GroupIndent"), exports);
9
9
  tslib_1.__exportStar(require("./Inputs"), exports);
10
10
  tslib_1.__exportStar(require("./Layouts"), exports);
11
+ tslib_1.__exportStar(require("./LazyLoader"), exports);
11
12
  tslib_1.__exportStar(require("./LongValue"), exports);
12
13
  tslib_1.__exportStar(require("./SimpleVerticalAccordeon"), exports);
14
+ tslib_1.__exportStar(require("./TogglerCard"), exports);
13
15
  tslib_1.__exportStar(require("./Views"), exports);
14
16
  tslib_1.__exportStar(require("./ViewLayouts"), exports);
@@ -1,4 +1,14 @@
1
1
  .df-use-oneof__toggler_radio > .df-row {
2
2
  width: unset;
3
3
  max-width: unset;
4
+ }
5
+ .df-use-oneof__toggler_card + .df-group-indent > .df-use-search:not(.df-group-indent) {
6
+ padding-top: 0px;
7
+ margin-top: 15px;
8
+ }
9
+ .df-use-oneof__card {
10
+ display: flex;
11
+ }
12
+ .df-use-oneof__card > :first-child {
13
+ margin-right: 8px;
4
14
  }
@@ -5,6 +5,7 @@ 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
7
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
8
+ const components_1 = require("../../components");
8
9
  const utils_1 = require("../../utils");
9
10
  const b = (0, utils_1.block)('use-oneof');
10
11
  const MAX_TAB_TITLE_LENGTH = 20;
@@ -41,28 +42,52 @@ const useOneOf = ({ props, onTogglerChange }) => {
41
42
  content: title,
42
43
  };
43
44
  }), [spec.description, spec.viewSpec.order, specProperties]);
44
- const selectToggler = react_1.default.useMemo(() => {
45
- var _a, _b;
46
- return ((_a = spec.viewSpec.oneOfParams) === null || _a === void 0 ? void 0 : _a.toggler) !== 'radio' &&
47
- (((_b = spec.viewSpec.oneOfParams) === null || _b === void 0 ? void 0 : _b.toggler) === 'select' ||
45
+ const togglerType = react_1.default.useMemo(() => {
46
+ var _a, _b, _c;
47
+ if (((_a = spec.viewSpec.oneOfParams) === null || _a === void 0 ? void 0 : _a.toggler) === 'card' && options.length < 3) {
48
+ return 'card';
49
+ }
50
+ if (((_b = spec.viewSpec.oneOfParams) === null || _b === void 0 ? void 0 : _b.toggler) !== 'radio' &&
51
+ (((_c = spec.viewSpec.oneOfParams) === null || _c === void 0 ? void 0 : _c.toggler) === 'select' ||
48
52
  options.length > 3 ||
49
- lodash_1.default.some(options, ({ title }) => title.length > MAX_TAB_TITLE_LENGTH));
53
+ lodash_1.default.some(options, ({ title }) => title.length > MAX_TAB_TITLE_LENGTH))) {
54
+ return 'select';
55
+ }
56
+ return 'radio';
50
57
  }, [options, (_a = spec.viewSpec.oneOfParams) === null || _a === void 0 ? void 0 : _a.toggler]);
51
58
  const togglerInput = react_1.default.useMemo(() => {
52
- if (selectToggler) {
59
+ if (togglerType === 'card') {
60
+ return (react_1.default.createElement("div", { className: b('card') }, options.map(({ value }) => {
61
+ var _a, _b, _c;
62
+ const onClick = () => {
63
+ onOneOfChange([value]);
64
+ };
65
+ return (react_1.default.createElement(components_1.TogglerCard, { title: ((_a = specProperties[value]) === null || _a === void 0 ? void 0 : _a.viewSpec.layoutTitle) || '', disabled: spec.viewSpec.disabled, text: ((_b = spec.description) === null || _b === void 0 ? void 0 : _b[value]) || '', description: (_c = specProperties[value]) === null || _c === void 0 ? void 0 : _c.viewSpec.layoutDescription, onClick: onClick, selected: oneOfValue === value, key: value }));
66
+ })));
67
+ }
68
+ if (togglerType === 'select') {
53
69
  return (react_1.default.createElement(uikit_1.Select, { width: "max", value: [oneOfValue], onUpdate: onOneOfChange, options: options, disabled: spec.viewSpec.disabled, filterable: options.length > 7, qa: name }));
54
70
  }
55
71
  return (react_1.default.createElement(uikit_1.RadioButton, { value: oneOfValue, onChange: (event) => onOneOfChange([event.target.value]), disabled: spec.viewSpec.disabled, qa: name }, options.map((option) => (react_1.default.createElement(uikit_1.RadioButton.Option, { key: option.value, value: option.value }, option.title)))));
56
- }, [selectToggler, oneOfValue, spec.viewSpec.disabled, name, options, onOneOfChange]);
72
+ }, [
73
+ togglerType,
74
+ oneOfValue,
75
+ spec.viewSpec.disabled,
76
+ name,
77
+ options,
78
+ onOneOfChange,
79
+ specProperties,
80
+ ]);
57
81
  const toggler = react_1.default.useMemo(() => {
58
82
  if (Layout) {
59
83
  return (react_1.default.createElement("div", { className: b('toggler', {
60
- radio: !selectToggler,
84
+ radio: togglerType === 'radio',
85
+ card: togglerType === 'card',
61
86
  }) },
62
87
  react_1.default.createElement(Layout, Object.assign({}, props), togglerInput)));
63
88
  }
64
89
  return react_1.default.createElement("div", null, togglerInput);
65
- }, [Layout, togglerInput, selectToggler, props]);
90
+ }, [Layout, togglerInput, togglerType, props]);
66
91
  return { oneOfValue, specProperties, toggler, togglerInput };
67
92
  };
68
93
  exports.useOneOf = useOneOf;
@@ -36,7 +36,7 @@ const isNotEmptyValue = (value, spec) => {
36
36
  };
37
37
  exports.isNotEmptyValue = isNotEmptyValue;
38
38
  const prepareSpec = (spec, parseJsonDefaultValue) => {
39
- var _a, _b, _c, _d;
39
+ var _a, _b, _c, _d, _e, _f, _g, _h;
40
40
  if (lodash_1.default.isObjectLike(spec)) {
41
41
  const result = lodash_1.default.cloneDeep(spec);
42
42
  if (lodash_1.default.isString(result.type)) {
@@ -48,7 +48,7 @@ const prepareSpec = (spec, parseJsonDefaultValue) => {
48
48
  try {
49
49
  _defaultValue = JSON.parse(result.defaultValue);
50
50
  }
51
- catch (_e) {
51
+ catch (_j) {
52
52
  _defaultValue = undefined;
53
53
  }
54
54
  }
@@ -70,7 +70,14 @@ const prepareSpec = (spec, parseJsonDefaultValue) => {
70
70
  result.viewSpec.addButtonPosition = result.viewSpec.addButtonPosition.toLowerCase();
71
71
  }
72
72
  if (lodash_1.default.isString((_d = result.viewSpec) === null || _d === void 0 ? void 0 : _d.themeLabel)) {
73
- result.viewSpec.themeLabel = result.viewSpec.themeLabel.toLowerCase();
73
+ result.viewSpec.textContentParams = Object.assign(Object.assign({}, result.viewSpec.textContentParams), { themeLabel: result.viewSpec.themeLabel.toLowerCase() });
74
+ }
75
+ if (lodash_1.default.isString((_f = (_e = result.viewSpec) === null || _e === void 0 ? void 0 : _e.oneOfParams) === null || _f === void 0 ? void 0 : _f.toggler)) {
76
+ result.viewSpec.oneOfParams.toggler = result.viewSpec.oneOfParams.toggler.toLowerCase();
77
+ }
78
+ if (lodash_1.default.isString((_h = (_g = result.viewSpec) === null || _g === void 0 ? void 0 : _g.textContentParams) === null || _h === void 0 ? void 0 : _h.themeLabel)) {
79
+ result.viewSpec.textContentParams.themeLabel =
80
+ result.viewSpec.textContentParams.themeLabel.toLowerCase();
74
81
  }
75
82
  if (lodash_1.default.isString(result.validator)) {
76
83
  result.validator = result.validator.toLowerCase();
@@ -24,7 +24,7 @@ export const Controller = ({ spec, name, value, parentOnChange, parentOnUnmount,
24
24
  useField: renderProps,
25
25
  useSearch: withSearch,
26
26
  }, __mirror);
27
- if (_.isString(name) && isCorrectSpec(spec)) {
27
+ if (_.isString(name) && isCorrectSpec(spec) && !spec.viewSpec.hideInput) {
28
28
  return withSearch(render(renderProps));
29
29
  }
30
30
  return null;
@@ -1,4 +1,5 @@
1
1
  import { LabelProps } from '@gravity-ui/uikit';
2
+ import { ColorTextBaseProps } from '@gravity-ui/uikit/build/esm/components/Text/colorText/colorText';
2
3
  import { ReadAsMethod, SpecTypes } from '../constants';
3
4
  import { ArrayValue, ObjectValue } from './';
4
5
  export interface ArraySpec<LinkType = any> {
@@ -27,6 +28,7 @@ export interface ArraySpec<LinkType = any> {
27
28
  link?: LinkType;
28
29
  placeholder?: string;
29
30
  addButtonPosition?: 'down' | 'right';
31
+ hideInput?: boolean;
30
32
  };
31
33
  }
32
34
  export interface BooleanSpec<LinkType = any> {
@@ -42,6 +44,7 @@ export interface BooleanSpec<LinkType = any> {
42
44
  layoutDescription?: string;
43
45
  layoutOpen?: boolean;
44
46
  link?: LinkType;
47
+ hideInput?: boolean;
45
48
  };
46
49
  }
47
50
  export interface NumberSpec<LinkType = any> {
@@ -62,6 +65,7 @@ export interface NumberSpec<LinkType = any> {
62
65
  link?: LinkType;
63
66
  placeholder?: string;
64
67
  copy?: boolean;
68
+ hideInput?: boolean;
65
69
  };
66
70
  }
67
71
  export interface ObjectSpec<LinkType = any> {
@@ -81,9 +85,10 @@ export interface ObjectSpec<LinkType = any> {
81
85
  order?: string[];
82
86
  link?: LinkType;
83
87
  oneOfParams?: {
84
- toggler?: 'select' | 'radio';
88
+ toggler?: 'select' | 'radio' | 'card';
85
89
  };
86
90
  placeholder?: string;
91
+ hideInput?: boolean;
87
92
  };
88
93
  }
89
94
  export interface StringSpec<LinkType = any> {
@@ -119,7 +124,13 @@ export interface StringSpec<LinkType = any> {
119
124
  };
120
125
  hideValues?: string[];
121
126
  placeholder?: string;
122
- themeLabel?: LabelProps['theme'];
127
+ hideInput?: boolean;
128
+ textContentParams?: {
129
+ themeLabel?: LabelProps['theme'];
130
+ text: string;
131
+ icon?: string;
132
+ iconColor?: ColorTextBaseProps['color'];
133
+ };
123
134
  fileInput?: {
124
135
  accept?: string;
125
136
  readAsMethod?: ReadAsMethod;
@@ -5,4 +5,15 @@
5
5
  .df-text-content .yc-label__text {
6
6
  text-align: initial;
7
7
  white-space: initial;
8
+ }
9
+ .df-text-content__icon {
10
+ display: flex;
11
+ align-items: center;
12
+ margin-right: 4px;
13
+ }
14
+ .df-text-content__wrapper {
15
+ display: flex;
16
+ }
17
+ .df-text-content__separator {
18
+ margin: 0 4px;
8
19
  }
@@ -1,22 +1,39 @@
1
1
  import { __rest } from "tslib";
2
2
  import React from 'react';
3
- import { Label } from '@gravity-ui/uikit';
3
+ import { Label, Text } from '@gravity-ui/uikit';
4
+ import _ from 'lodash';
4
5
  import { block } from '../../../utils';
6
+ import { LazyLoader } from '../../LazyLoader';
7
+ import { loadIcon } from './utils';
5
8
  import './TextContent.css';
6
9
  const b = block('text-content');
7
10
  export const TextContent = (_a) => {
8
- var { spec, Layout } = _a, restProps = __rest(_a, ["spec", "Layout"]);
9
- const { themeLabel, layoutDescription } = spec.viewSpec;
10
- if (!layoutDescription) {
11
+ var _b;
12
+ var { spec, Layout, input } = _a, restProps = __rest(_a, ["spec", "Layout", "input"]);
13
+ const { textContentParams, layoutDescription } = spec.viewSpec;
14
+ const text = React.useMemo(() => ((textContentParams === null || textContentParams === void 0 ? void 0 : textContentParams.text) ? textContentParams === null || textContentParams === void 0 ? void 0 : textContentParams.text : layoutDescription), [layoutDescription, textContentParams === null || textContentParams === void 0 ? void 0 : textContentParams.text]);
15
+ if (!text) {
11
16
  return null;
12
17
  }
13
- let content = React.createElement("span", { dangerouslySetInnerHTML: { __html: layoutDescription } });
14
- if (themeLabel) {
15
- content = (React.createElement(Label, { size: "m", theme: themeLabel, className: b() }, content));
18
+ const iconLib = (textContentParams === null || textContentParams === void 0 ? void 0 : textContentParams.icon) ? (React.createElement(LazyLoader, { component: loadIcon(textContentParams === null || textContentParams === void 0 ? void 0 : textContentParams.icon) })) : null;
19
+ let content = React.createElement("span", { dangerouslySetInnerHTML: { __html: text } });
20
+ if (textContentParams === null || textContentParams === void 0 ? void 0 : textContentParams.themeLabel) {
21
+ content = (React.createElement(Label, { size: "m", theme: textContentParams.themeLabel, className: b(), value: input.value, icon: iconLib }, content));
22
+ }
23
+ else {
24
+ content = (React.createElement("div", { className: b('wrapper') },
25
+ iconLib ? (React.createElement(Text, { color: (_b = spec.viewSpec.textContentParams) === null || _b === void 0 ? void 0 : _b.iconColor, className: b('icon') }, iconLib)) : null,
26
+ content,
27
+ input.value ? (React.createElement(React.Fragment, null,
28
+ React.createElement("span", { className: b('separator') }, ":"),
29
+ React.createElement(Text, { color: "secondary" }, input.value))) : null));
16
30
  }
17
31
  if (Layout) {
18
- const _spec = Object.assign(Object.assign({}, spec), { viewSpec: Object.assign(Object.assign({}, spec.viewSpec), { layoutDescription: undefined }) });
19
- return (React.createElement(Layout, Object.assign({ spec: _spec }, restProps), content));
32
+ const _spec = _.cloneDeep(spec);
33
+ if (!(textContentParams === null || textContentParams === void 0 ? void 0 : textContentParams.text)) {
34
+ _spec.viewSpec.layoutDescription = undefined;
35
+ }
36
+ return (React.createElement(Layout, Object.assign({ spec: _spec, input: input }, restProps), content));
20
37
  }
21
38
  return content;
22
39
  };
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const loadIcon: (name: string) => React.LazyExoticComponent<React.ComponentType<any>>;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ export const loadIcon = (name) => {
3
+ const Icon = React.lazy(() => {
4
+ return new Promise((resolve) => {
5
+ const icon = import(`@gravity-ui/icons`).then((module) => {
6
+ if (module[name]) {
7
+ return { default: module[name] };
8
+ }
9
+ return {
10
+ default: () => null,
11
+ };
12
+ });
13
+ resolve(icon);
14
+ });
15
+ });
16
+ return Icon;
17
+ };
@@ -11,9 +11,6 @@
11
11
  .df-transparent_without-max-width {
12
12
  max-width: unset;
13
13
  }
14
- .df-transparent_without-max-width > .df-error-wrapper {
15
- width: unset;
16
- }
17
14
  .df-transparent__remove-button {
18
15
  margin-left: 5px;
19
16
  }
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ export type LazyLoaderProps = {
3
+ component: React.LazyExoticComponent<React.ComponentType<any>>;
4
+ initialFallback?: JSX.Element;
5
+ };
6
+ export declare const LazyLoader: ({ component, initialFallback, }: LazyLoaderProps) => JSX.Element;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import _ from 'lodash';
3
+ export const LazyLoader = ({ component, initialFallback = React.createElement(React.Fragment, null), }) => {
4
+ const fallback = React.useRef(() => initialFallback);
5
+ const Component = component;
6
+ const updateFallback = async () => {
7
+ const result = await component._result;
8
+ if (!_.isUndefined(result)) {
9
+ fallback.current = result.default;
10
+ }
11
+ };
12
+ React.useEffect(() => {
13
+ updateFallback();
14
+ }, [component]);
15
+ return (React.createElement(React.Suspense, { fallback: React.createElement(fallback.current, null) },
16
+ React.createElement(Component, null)));
17
+ };
@@ -0,0 +1 @@
1
+ export * from './LazyLoader';
@@ -0,0 +1 @@
1
+ export * from './LazyLoader';
@@ -0,0 +1,18 @@
1
+ .df-toggler-card {
2
+ width: 254px;
3
+ padding: 10px;
4
+ height: 88px;
5
+ }
6
+ .df-toggler-card__header {
7
+ display: flex;
8
+ justify-content: space-between;
9
+ align-items: baseline;
10
+ }
11
+ .df-toggler-card__text {
12
+ margin-top: 12px;
13
+ display: block;
14
+ margin-right: 15px;
15
+ height: 36px;
16
+ overflow: hidden;
17
+ text-overflow: ellipsis;
18
+ }
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import './TogglerCard.css';
3
+ interface TogglerCardProps {
4
+ description?: string;
5
+ title: string;
6
+ text: string;
7
+ onClick: () => void;
8
+ disabled?: boolean;
9
+ selected: boolean;
10
+ }
11
+ export declare const TogglerCard: React.FC<TogglerCardProps>;
12
+ export {};
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { HelpPopover } from '@gravity-ui/components';
3
+ import { Card, Text } from '@gravity-ui/uikit';
4
+ import { block } from '../../utils';
5
+ import './TogglerCard.css';
6
+ const b = block('toggler-card');
7
+ export const TogglerCard = ({ description, title, text, onClick, disabled, selected, }) => {
8
+ return (React.createElement(Card, { onClick: onClick, type: "selection", disabled: disabled ? !selected : disabled, selected: selected, size: "m", className: b() },
9
+ React.createElement("div", { className: b('header') },
10
+ React.createElement(Text, { variant: "subheader-2", ellipsis: true }, title),
11
+ description ? (React.createElement(HelpPopover, { htmlContent: description, placement: ['bottom', 'top'] })) : null),
12
+ React.createElement(Text, { variant: "body-1", color: "secondary", className: b('text') }, text)));
13
+ };
@@ -0,0 +1 @@
1
+ export * from './TogglerCard';
@@ -0,0 +1 @@
1
+ export * from './TogglerCard';
@@ -10,4 +10,5 @@
10
10
  }
11
11
  .df-view-transparent__inner {
12
12
  max-width: 100%;
13
+ width: 100%;
13
14
  }
@@ -5,7 +5,9 @@ export * from './ErrorWrapper';
5
5
  export * from './GroupIndent';
6
6
  export * from './Inputs';
7
7
  export * from './Layouts';
8
+ export * from './LazyLoader';
8
9
  export * from './LongValue';
9
10
  export * from './SimpleVerticalAccordeon';
11
+ export * from './TogglerCard';
10
12
  export * from './Views';
11
13
  export * from './ViewLayouts';
@@ -5,7 +5,9 @@ export * from './ErrorWrapper';
5
5
  export * from './GroupIndent';
6
6
  export * from './Inputs';
7
7
  export * from './Layouts';
8
+ export * from './LazyLoader';
8
9
  export * from './LongValue';
9
10
  export * from './SimpleVerticalAccordeon';
11
+ export * from './TogglerCard';
10
12
  export * from './Views';
11
13
  export * from './ViewLayouts';
@@ -1,4 +1,14 @@
1
1
  .df-use-oneof__toggler_radio > .df-row {
2
2
  width: unset;
3
3
  max-width: unset;
4
+ }
5
+ .df-use-oneof__toggler_card + .df-group-indent > .df-use-search:not(.df-group-indent) {
6
+ padding-top: 0px;
7
+ margin-top: 15px;
8
+ }
9
+ .df-use-oneof__card {
10
+ display: flex;
11
+ }
12
+ .df-use-oneof__card > :first-child {
13
+ margin-right: 8px;
4
14
  }
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { RadioButton, Select } from '@gravity-ui/uikit';
3
3
  import _ from 'lodash';
4
+ import { TogglerCard } from '../../components';
4
5
  import { block } from '../../utils';
5
6
  import './useOneOf.css';
6
7
  const b = block('use-oneof');
@@ -38,27 +39,51 @@ export const useOneOf = ({ props, onTogglerChange }) => {
38
39
  content: title,
39
40
  };
40
41
  }), [spec.description, spec.viewSpec.order, specProperties]);
41
- const selectToggler = React.useMemo(() => {
42
- var _a, _b;
43
- return ((_a = spec.viewSpec.oneOfParams) === null || _a === void 0 ? void 0 : _a.toggler) !== 'radio' &&
44
- (((_b = spec.viewSpec.oneOfParams) === null || _b === void 0 ? void 0 : _b.toggler) === 'select' ||
42
+ const togglerType = React.useMemo(() => {
43
+ var _a, _b, _c;
44
+ if (((_a = spec.viewSpec.oneOfParams) === null || _a === void 0 ? void 0 : _a.toggler) === 'card' && options.length < 3) {
45
+ return 'card';
46
+ }
47
+ if (((_b = spec.viewSpec.oneOfParams) === null || _b === void 0 ? void 0 : _b.toggler) !== 'radio' &&
48
+ (((_c = spec.viewSpec.oneOfParams) === null || _c === void 0 ? void 0 : _c.toggler) === 'select' ||
45
49
  options.length > 3 ||
46
- _.some(options, ({ title }) => title.length > MAX_TAB_TITLE_LENGTH));
50
+ _.some(options, ({ title }) => title.length > MAX_TAB_TITLE_LENGTH))) {
51
+ return 'select';
52
+ }
53
+ return 'radio';
47
54
  }, [options, (_a = spec.viewSpec.oneOfParams) === null || _a === void 0 ? void 0 : _a.toggler]);
48
55
  const togglerInput = React.useMemo(() => {
49
- if (selectToggler) {
56
+ if (togglerType === 'card') {
57
+ return (React.createElement("div", { className: b('card') }, options.map(({ value }) => {
58
+ var _a, _b, _c;
59
+ const onClick = () => {
60
+ onOneOfChange([value]);
61
+ };
62
+ return (React.createElement(TogglerCard, { title: ((_a = specProperties[value]) === null || _a === void 0 ? void 0 : _a.viewSpec.layoutTitle) || '', disabled: spec.viewSpec.disabled, text: ((_b = spec.description) === null || _b === void 0 ? void 0 : _b[value]) || '', description: (_c = specProperties[value]) === null || _c === void 0 ? void 0 : _c.viewSpec.layoutDescription, onClick: onClick, selected: oneOfValue === value, key: value }));
63
+ })));
64
+ }
65
+ if (togglerType === 'select') {
50
66
  return (React.createElement(Select, { width: "max", value: [oneOfValue], onUpdate: onOneOfChange, options: options, disabled: spec.viewSpec.disabled, filterable: options.length > 7, qa: name }));
51
67
  }
52
68
  return (React.createElement(RadioButton, { value: oneOfValue, onChange: (event) => onOneOfChange([event.target.value]), disabled: spec.viewSpec.disabled, qa: name }, options.map((option) => (React.createElement(RadioButton.Option, { key: option.value, value: option.value }, option.title)))));
53
- }, [selectToggler, oneOfValue, spec.viewSpec.disabled, name, options, onOneOfChange]);
69
+ }, [
70
+ togglerType,
71
+ oneOfValue,
72
+ spec.viewSpec.disabled,
73
+ name,
74
+ options,
75
+ onOneOfChange,
76
+ specProperties,
77
+ ]);
54
78
  const toggler = React.useMemo(() => {
55
79
  if (Layout) {
56
80
  return (React.createElement("div", { className: b('toggler', {
57
- radio: !selectToggler,
81
+ radio: togglerType === 'radio',
82
+ card: togglerType === 'card',
58
83
  }) },
59
84
  React.createElement(Layout, Object.assign({}, props), togglerInput)));
60
85
  }
61
86
  return React.createElement("div", null, togglerInput);
62
- }, [Layout, togglerInput, selectToggler, props]);
87
+ }, [Layout, togglerInput, togglerType, props]);
63
88
  return { oneOfValue, specProperties, toggler, togglerInput };
64
89
  };
@@ -31,7 +31,7 @@ export const isNotEmptyValue = (value, spec) => {
31
31
  return true;
32
32
  };
33
33
  export const prepareSpec = (spec, parseJsonDefaultValue) => {
34
- var _a, _b, _c, _d;
34
+ var _a, _b, _c, _d, _e, _f, _g, _h;
35
35
  if (_.isObjectLike(spec)) {
36
36
  const result = _.cloneDeep(spec);
37
37
  if (_.isString(result.type)) {
@@ -43,7 +43,7 @@ export const prepareSpec = (spec, parseJsonDefaultValue) => {
43
43
  try {
44
44
  _defaultValue = JSON.parse(result.defaultValue);
45
45
  }
46
- catch (_e) {
46
+ catch (_j) {
47
47
  _defaultValue = undefined;
48
48
  }
49
49
  }
@@ -65,7 +65,14 @@ export const prepareSpec = (spec, parseJsonDefaultValue) => {
65
65
  result.viewSpec.addButtonPosition = result.viewSpec.addButtonPosition.toLowerCase();
66
66
  }
67
67
  if (_.isString((_d = result.viewSpec) === null || _d === void 0 ? void 0 : _d.themeLabel)) {
68
- result.viewSpec.themeLabel = result.viewSpec.themeLabel.toLowerCase();
68
+ result.viewSpec.textContentParams = Object.assign(Object.assign({}, result.viewSpec.textContentParams), { themeLabel: result.viewSpec.themeLabel.toLowerCase() });
69
+ }
70
+ if (_.isString((_f = (_e = result.viewSpec) === null || _e === void 0 ? void 0 : _e.oneOfParams) === null || _f === void 0 ? void 0 : _f.toggler)) {
71
+ result.viewSpec.oneOfParams.toggler = result.viewSpec.oneOfParams.toggler.toLowerCase();
72
+ }
73
+ if (_.isString((_h = (_g = result.viewSpec) === null || _g === void 0 ? void 0 : _g.textContentParams) === null || _h === void 0 ? void 0 : _h.themeLabel)) {
74
+ result.viewSpec.textContentParams.themeLabel =
75
+ result.viewSpec.textContentParams.themeLabel.toLowerCase();
69
76
  }
70
77
  if (_.isString(result.validator)) {
71
78
  result.validator = result.validator.toLowerCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/dynamic-forms",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "main": "build/cjs/index.js",