@gravity-ui/page-constructor 4.40.6 → 4.41.1-alpha.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 (98) hide show
  1. package/build/cjs/components/Author/Author.css +4 -2
  2. package/build/cjs/components/Author/Author.js +2 -2
  3. package/build/cjs/editor/components/CodeEditor/CodeEditor.css +56 -0
  4. package/build/cjs/editor/components/CodeEditor/CodeEditor.d.ts +12 -0
  5. package/build/cjs/editor/components/CodeEditor/CodeEditor.js +31 -0
  6. package/build/cjs/editor/components/CodeEditor/constants.d.ts +2 -0
  7. package/build/cjs/editor/components/CodeEditor/constants.js +20 -0
  8. package/build/cjs/editor/components/Layout/Layout.css +2 -1
  9. package/build/cjs/editor/containers/Editor/Editor.js +9 -5
  10. package/build/cjs/editor/containers/Form/Form.css +5 -2
  11. package/build/cjs/editor/containers/Form/Form.d.ts +10 -3
  12. package/build/cjs/editor/containers/Form/Form.js +12 -16
  13. package/build/cjs/editor/hooks/useCodeValidator.d.ts +4 -0
  14. package/build/cjs/editor/hooks/useCodeValidator.js +10 -0
  15. package/build/cjs/editor/hooks/useFormSpec.d.ts +2 -2
  16. package/build/cjs/editor/hooks/useFormSpec.js +2 -6
  17. package/build/cjs/editor/store/main/index.d.ts +12 -0
  18. package/build/cjs/editor/store/{index.js → main/index.js} +10 -16
  19. package/build/cjs/editor/store/{reducer.d.ts → main/reducer.d.ts} +5 -15
  20. package/build/cjs/editor/store/{reducer.js → main/reducer.js} +1 -7
  21. package/build/cjs/editor/store/{utils.d.ts → main/utils.d.ts} +3 -3
  22. package/build/cjs/editor/store/settings/index.d.ts +12 -0
  23. package/build/cjs/editor/store/settings/index.js +21 -0
  24. package/build/cjs/editor/store/settings/reducer.d.ts +37 -0
  25. package/build/cjs/editor/store/settings/reducer.js +32 -0
  26. package/build/cjs/editor/styles/root.css +2 -1
  27. package/build/cjs/editor/types/index.d.ts +5 -0
  28. package/build/cjs/editor/types/index.js +7 -1
  29. package/build/cjs/editor/utils/code.d.ts +6 -0
  30. package/build/cjs/editor/utils/code.js +11 -0
  31. package/build/cjs/editor/utils/validation.d.ts +13 -0
  32. package/build/cjs/editor/utils/validation.js +57 -0
  33. package/build/cjs/models/constructor-items/common.d.ts +1 -0
  34. package/build/cjs/models/constructor-items/sub-blocks.d.ts +12 -1
  35. package/build/cjs/schema/constants.d.ts +4 -0
  36. package/build/cjs/sub-blocks/Quote/Quote.css +11 -18
  37. package/build/cjs/sub-blocks/Quote/Quote.js +7 -6
  38. package/build/cjs/sub-blocks/Quote/schema.d.ts +4 -0
  39. package/build/cjs/sub-blocks/Quote/schema.js +4 -1
  40. package/build/cjs/text-transform/config.js +10 -4
  41. package/build/esm/components/Author/Author.css +4 -2
  42. package/build/esm/components/Author/Author.js +2 -2
  43. package/build/esm/editor/components/CodeEditor/CodeEditor.css +56 -0
  44. package/build/esm/editor/components/CodeEditor/CodeEditor.d.ts +13 -0
  45. package/build/esm/editor/components/CodeEditor/CodeEditor.js +27 -0
  46. package/build/esm/editor/components/CodeEditor/constants.d.ts +2 -0
  47. package/build/esm/editor/components/CodeEditor/constants.js +17 -0
  48. package/build/esm/editor/components/Layout/Layout.css +2 -1
  49. package/build/esm/editor/containers/Editor/Editor.js +9 -5
  50. package/build/esm/editor/containers/Form/Form.css +5 -2
  51. package/build/esm/editor/containers/Form/Form.d.ts +10 -3
  52. package/build/esm/editor/containers/Form/Form.js +9 -13
  53. package/build/esm/editor/hooks/useCodeValidator.d.ts +4 -0
  54. package/build/esm/editor/hooks/useCodeValidator.js +6 -0
  55. package/build/esm/editor/hooks/useFormSpec.d.ts +2 -2
  56. package/build/esm/editor/hooks/useFormSpec.js +2 -6
  57. package/build/esm/editor/store/main/index.d.ts +12 -0
  58. package/build/esm/editor/store/{index.js → main/index.js} +9 -15
  59. package/build/esm/editor/store/{reducer.d.ts → main/reducer.d.ts} +5 -15
  60. package/build/esm/editor/store/{reducer.js → main/reducer.js} +0 -6
  61. package/build/esm/editor/store/{utils.d.ts → main/utils.d.ts} +3 -3
  62. package/build/esm/editor/store/settings/index.d.ts +12 -0
  63. package/build/esm/editor/store/settings/index.js +17 -0
  64. package/build/esm/editor/store/settings/reducer.d.ts +37 -0
  65. package/build/esm/editor/store/settings/reducer.js +28 -0
  66. package/build/esm/editor/styles/root.css +2 -1
  67. package/build/esm/editor/types/index.d.ts +5 -0
  68. package/build/esm/editor/types/index.js +6 -0
  69. package/build/esm/editor/utils/code.d.ts +6 -0
  70. package/build/esm/editor/utils/code.js +6 -0
  71. package/build/esm/editor/utils/validation.d.ts +13 -0
  72. package/build/esm/editor/utils/validation.js +51 -0
  73. package/build/esm/models/constructor-items/common.d.ts +1 -0
  74. package/build/esm/models/constructor-items/sub-blocks.d.ts +12 -1
  75. package/build/esm/schema/constants.d.ts +4 -0
  76. package/build/esm/sub-blocks/Quote/Quote.css +11 -18
  77. package/build/esm/sub-blocks/Quote/Quote.js +8 -7
  78. package/build/esm/sub-blocks/Quote/schema.d.ts +4 -0
  79. package/build/esm/sub-blocks/Quote/schema.js +4 -1
  80. package/build/esm/text-transform/config.js +10 -4
  81. package/package.json +4 -1
  82. package/server/models/constructor-items/common.d.ts +1 -0
  83. package/server/models/constructor-items/sub-blocks.d.ts +12 -1
  84. package/server/text-transform/config.js +10 -4
  85. package/styles/styles.css +1 -0
  86. package/styles/styles.scss +1 -0
  87. package/styles/variables.scss +2 -0
  88. package/widget/index.js +1 -1
  89. package/build/cjs/editor/components/YamlEditor/YamlEditor.css +0 -12
  90. package/build/cjs/editor/components/YamlEditor/YamlEditor.d.ts +0 -6
  91. package/build/cjs/editor/components/YamlEditor/YamlEditor.js +0 -34
  92. package/build/cjs/editor/store/index.d.ts +0 -16
  93. package/build/esm/editor/components/YamlEditor/YamlEditor.css +0 -12
  94. package/build/esm/editor/components/YamlEditor/YamlEditor.d.ts +0 -7
  95. package/build/esm/editor/components/YamlEditor/YamlEditor.js +0 -30
  96. package/build/esm/editor/store/index.d.ts +0 -16
  97. /package/build/cjs/editor/store/{utils.js → main/utils.js} +0 -0
  98. /package/build/esm/editor/store/{utils.js → main/utils.js} +0 -0
@@ -32,4 +32,9 @@ export declare enum ViewModeItem {
32
32
  Tablet = "tablet",
33
33
  Mobile = "mobile"
34
34
  }
35
+ export declare enum FormTab {
36
+ Blocks = "blocks",
37
+ Page = "page",
38
+ Code = "code"
39
+ }
35
40
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ViewModeItem = void 0;
3
+ exports.FormTab = exports.ViewModeItem = void 0;
4
4
  var ViewModeItem;
5
5
  (function (ViewModeItem) {
6
6
  ViewModeItem["Edititng"] = "editing";
@@ -8,3 +8,9 @@ var ViewModeItem;
8
8
  ViewModeItem["Tablet"] = "tablet";
9
9
  ViewModeItem["Mobile"] = "mobile";
10
10
  })(ViewModeItem = exports.ViewModeItem || (exports.ViewModeItem = {}));
11
+ var FormTab;
12
+ (function (FormTab) {
13
+ FormTab["Blocks"] = "blocks";
14
+ FormTab["Page"] = "page";
15
+ FormTab["Code"] = "code";
16
+ })(FormTab = exports.FormTab || (exports.FormTab = {}));
@@ -0,0 +1,6 @@
1
+ export declare function parseCode(code: string): {
2
+ blocks: import("../../models").ConstructorBlock[];
3
+ menu?: import("../../models").Menu | undefined;
4
+ background?: import("../../models").ThemedMediaProps | undefined;
5
+ animated?: boolean | undefined;
6
+ };
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseCode = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const js_yaml_1 = tslib_1.__importDefault(require("js-yaml"));
6
+ function parseCode(code) {
7
+ var _a;
8
+ const pageContent = js_yaml_1.default.load(code);
9
+ return Object.assign(Object.assign({}, pageContent), { blocks: (_a = pageContent.blocks) === null || _a === void 0 ? void 0 : _a.filter(Boolean) });
10
+ }
11
+ exports.parseCode = parseCode;
@@ -0,0 +1,13 @@
1
+ import { ValidateFunction } from 'ajv';
2
+ import { JSONSchema4 } from 'json-schema';
3
+ export interface CodeEditorMessageProps {
4
+ text: string;
5
+ status: CodeEditorMessageStatus;
6
+ }
7
+ export declare enum CodeEditorMessageStatus {
8
+ SUCCESS = "success",
9
+ WARNING = "warning",
10
+ ERROR = "error"
11
+ }
12
+ export declare function createValidator(schema: JSONSchema4): ValidateFunction<unknown>;
13
+ export declare function validate(content: string, validator: ValidateFunction): CodeEditorMessageProps;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validate = exports.createValidator = exports.CodeEditorMessageStatus = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const ajv_1 = tslib_1.__importDefault(require("ajv"));
6
+ const ajv_keywords_1 = tslib_1.__importDefault(require("ajv-keywords"));
7
+ const js_yaml_1 = tslib_1.__importDefault(require("js-yaml"));
8
+ const js_yaml_source_map_1 = tslib_1.__importDefault(require("js-yaml-source-map"));
9
+ const isArray_1 = tslib_1.__importDefault(require("lodash/isArray"));
10
+ const SUCCESS_MESSAGE = 'Valid';
11
+ var CodeEditorMessageStatus;
12
+ (function (CodeEditorMessageStatus) {
13
+ CodeEditorMessageStatus["SUCCESS"] = "success";
14
+ CodeEditorMessageStatus["WARNING"] = "warning";
15
+ CodeEditorMessageStatus["ERROR"] = "error";
16
+ })(CodeEditorMessageStatus = exports.CodeEditorMessageStatus || (exports.CodeEditorMessageStatus = {}));
17
+ function createValidator(schema) {
18
+ const ajv = new ajv_1.default({ $data: true, allErrors: true, schemas: [schema], strict: false });
19
+ // TODO: select is deprecated, replace with discriminator:
20
+ // https://github.com/ajv-validator/ajv-keywords#selectselectcasesselectdefault
21
+ (0, ajv_keywords_1.default)(ajv, 'select');
22
+ return ajv.compile(schema);
23
+ }
24
+ exports.createValidator = createValidator;
25
+ function validate(content, validator) {
26
+ let result;
27
+ if (!content) {
28
+ return { status: CodeEditorMessageStatus.SUCCESS, text: SUCCESS_MESSAGE };
29
+ }
30
+ try {
31
+ const jsYamlMap = new js_yaml_source_map_1.default();
32
+ const data = js_yaml_1.default.load(content, { listener: jsYamlMap.listen() });
33
+ validator(data);
34
+ if (validator.errors) {
35
+ const messages = validator.errors.map(({ instancePath, schemaPath, message, params }) => {
36
+ const pointer = jsYamlMap.lookup(instancePath.split('/').filter(Boolean));
37
+ const stringParams = Object.entries(params).map(([key, value]) => {
38
+ if ((0, isArray_1.default)(value)) {
39
+ return `${key}: ${value.join(' | ')}`;
40
+ }
41
+ return `${key}: ${value}`;
42
+ });
43
+ const ref = pointer ? `${pointer.line}: ` : '';
44
+ return `${ref}${instancePath || schemaPath}: ${message}\n${stringParams.join('\n')}`;
45
+ });
46
+ result = { status: CodeEditorMessageStatus.WARNING, text: messages.join('\n\n') };
47
+ }
48
+ else {
49
+ result = { status: CodeEditorMessageStatus.SUCCESS, text: SUCCESS_MESSAGE };
50
+ }
51
+ }
52
+ catch ({ message }) {
53
+ result = { status: CodeEditorMessageStatus.ERROR, text: message };
54
+ }
55
+ return result;
56
+ }
57
+ exports.validate = validate;
@@ -346,6 +346,7 @@ export interface AuthorProps extends QAProps {
346
346
  className?: string;
347
347
  authorContainerClassName?: string;
348
348
  type?: AuthorType;
349
+ theme?: ContentTheme;
349
350
  }
350
351
  export interface TitleProps {
351
352
  title?: TitleItemProps | string;
@@ -63,16 +63,27 @@ export interface HubspotFormProps extends HubspotEventHandlers, AnalyticsEventsB
63
63
  createDOMElement?: boolean;
64
64
  }
65
65
  export interface QuoteProps extends Themable, CardBaseProps {
66
- text: string;
66
+ text?: string;
67
+ yfmText?: string;
67
68
  image: ThemedImage;
68
69
  logo: ImageProps;
69
70
  color?: string;
71
+ /**
72
+ * @deprecated use property button instead
73
+ */
70
74
  url?: string;
75
+ /**
76
+ * @deprecated use property button instead
77
+ */
71
78
  urlTitle?: string;
72
79
  author?: AuthorItem;
80
+ /**
81
+ * @deprecated use property button instead
82
+ */
73
83
  buttonText?: string;
74
84
  theme?: TextTheme;
75
85
  quoteType?: QuoteType;
86
+ button?: ButtonProps;
76
87
  }
77
88
  export interface BackgroundCardProps extends CardBaseProps, AnalyticsEventsBase, Omit<ContentBlockProps, 'colSizes' | 'centered'> {
78
89
  url?: string;
@@ -125,6 +125,10 @@ export declare const cardSchemas: {
125
125
  type: string;
126
126
  contentType: string;
127
127
  };
128
+ yfmText: {
129
+ type: string;
130
+ contentType: string;
131
+ };
128
132
  image: {
129
133
  oneOf: (({
130
134
  oneOf: ({
@@ -76,6 +76,17 @@ unpredictable css rules order in build */
76
76
  .pc-quote__content_quote-type_english-double .pc-quote__text::after {
77
77
  content: "”";
78
78
  }
79
+ .pc-quote__content .yfm {
80
+ font-size: var(--g-text-body-3-font-size);
81
+ line-height: var(--g-text-body-3-line-height);
82
+ }
83
+ .pc-quote__content .yfm > * {
84
+ display: inline;
85
+ }
86
+ .pc-quote__content .yfm:after {
87
+ position: relative;
88
+ left: -5px;
89
+ }
79
90
  .pc-quote__image {
80
91
  width: 100%;
81
92
  height: 100%;
@@ -87,24 +98,6 @@ unpredictable css rules order in build */
87
98
  align-items: flex-end;
88
99
  margin-top: 20px;
89
100
  }
90
- .pc-quote__author_theme_dark, .pc-quote_theme_dark {
91
- color: var(--g-color-text-light-primary);
92
- }
93
- .pc-quote__author_theme_dark h1,
94
- .pc-quote__author_theme_dark h2,
95
- .pc-quote__author_theme_dark h3,
96
- .pc-quote__author_theme_dark h4,
97
- .pc-quote__author_theme_dark h5,
98
- .pc-quote__author_theme_dark h6,
99
- .pc-quote__author_theme_dark .yfm, .pc-quote_theme_dark h1,
100
- .pc-quote_theme_dark h2,
101
- .pc-quote_theme_dark h3,
102
- .pc-quote_theme_dark h4,
103
- .pc-quote_theme_dark h5,
104
- .pc-quote_theme_dark h6,
105
- .pc-quote_theme_dark .yfm {
106
- color: var(--g-color-text-light-primary);
107
- }
108
101
  .pc-quote__author {
109
102
  max-width: calc(60% - 20px);
110
103
  margin-right: 20px;
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const react_1 = tslib_1.__importStar(require("react"));
5
- const uikit_1 = require("@gravity-ui/uikit");
6
5
  const components_1 = require("../../components");
7
6
  const utils_1 = require("../../components/Media/Image/utils");
8
7
  const theme_1 = require("../../context/theme");
@@ -11,23 +10,25 @@ const models_1 = require("../../models");
11
10
  const utils_2 = require("../../utils");
12
11
  const b = (0, utils_2.block)('quote');
13
12
  const Quote = (props) => {
14
- const { theme: textTheme = 'light', color, image, border = 'shadow', text, logo, author, url, urlTitle, buttonText, quoteType = models_1.QuoteType.Chevron, } = props;
13
+ const { theme: textTheme = 'light', color, image, border = 'shadow', text, yfmText, logo, author, url, urlTitle, buttonText, quoteType = models_1.QuoteType.Chevron, button, } = props;
15
14
  const theme = (0, theme_1.useTheme)();
16
15
  const imageThemed = (0, utils_2.getThemedValue)(image, theme);
17
16
  const imageData = (0, utils_1.getMediaImage)(imageThemed);
18
17
  const handleAnalytics = (0, hooks_1.useAnalytics)(models_1.DefaultEventNames.QuoteButton, url);
19
18
  const handleButtonClick = (0, react_1.useCallback)(() => handleAnalytics(), [handleAnalytics]);
20
19
  const renderFooter = Boolean(author || url) && (react_1.default.createElement("div", { className: b('author-wrapper') },
21
- author && (react_1.default.createElement(components_1.Author, { className: b('author', { theme: textTheme }), author: author, type: models_1.AuthorType.Line })),
22
- url && buttonText && (react_1.default.createElement(uikit_1.Button, { view: "outlined", size: "xl", href: url, className: b('link-button', { theme: textTheme }), onClick: handleButtonClick, title: urlTitle }, buttonText))));
20
+ author && (react_1.default.createElement(components_1.Author, { className: b('author', { theme: textTheme }), author: author, type: models_1.AuthorType.Line, theme: textTheme })),
21
+ url && buttonText && !button && (react_1.default.createElement(components_1.Button, { theme: "outlined", size: "xl", url: url, className: b('link-button', { theme: textTheme }), onClick: handleButtonClick, urlTitle: urlTitle, text: buttonText })),
22
+ button && react_1.default.createElement(components_1.Button, Object.assign({ size: "xl" }, button))));
23
23
  const logoProps = (0, utils_1.getMediaImage)(logo);
24
24
  return (react_1.default.createElement("div", { className: b({ theme: textTheme, border }), style: color ? { backgroundColor: color } : {} },
25
25
  react_1.default.createElement("div", { key: text, className: b('content-wrapper') },
26
26
  react_1.default.createElement("div", null,
27
27
  react_1.default.createElement(components_1.Image, Object.assign({ className: b('logo') }, logoProps)),
28
28
  react_1.default.createElement("div", { className: b('content', { 'quote-type': quoteType }) },
29
- react_1.default.createElement("span", { className: b('text') },
30
- react_1.default.createElement(components_1.HTML, null, text)))),
29
+ text && (react_1.default.createElement("span", { className: b('text') },
30
+ react_1.default.createElement(components_1.HTML, null, text))),
31
+ yfmText && (react_1.default.createElement(components_1.YFMWrapper, { className: b('text'), content: yfmText, modifiers: { constructor: true } })))),
31
32
  renderFooter),
32
33
  react_1.default.createElement("div", { className: b('image-wrapper') },
33
34
  react_1.default.createElement(components_1.Image, Object.assign({}, imageData, { className: b('image') })))));
@@ -7,6 +7,10 @@ export declare const Quote: {
7
7
  type: string;
8
8
  contentType: string;
9
9
  };
10
+ yfmText: {
11
+ type: string;
12
+ contentType: string;
13
+ };
10
14
  image: {
11
15
  oneOf: (({
12
16
  oneOf: ({
@@ -6,10 +6,13 @@ const common_1 = require("../../schema/validators/common");
6
6
  exports.Quote = {
7
7
  quote: {
8
8
  additionalProperties: false,
9
- required: ['text', 'image', 'logo'],
9
+ required: ['image', 'logo'],
10
10
  properties: Object.assign(Object.assign({}, common_1.BaseProps), { text: {
11
11
  type: 'string',
12
12
  contentType: 'text',
13
+ }, yfmText: {
14
+ type: 'string',
15
+ contentType: 'text',
13
16
  }, image: (0, common_1.withTheme)(schema_1.ImageProps), logo: schema_1.ImageProps, color: {
14
17
  type: 'string',
15
18
  }, url: {
@@ -130,10 +130,16 @@ exports.config = {
130
130
  transformer: common_1.yfmTransformer,
131
131
  },
132
132
  ],
133
- [models_1.SubBlockType.Quote]: {
134
- fields: ['text'],
135
- transformer: common_1.typografTransformer,
136
- },
133
+ [models_1.SubBlockType.Quote]: [
134
+ {
135
+ fields: ['text'],
136
+ transformer: common_1.typografTransformer,
137
+ },
138
+ {
139
+ fields: ['yfmText'],
140
+ transformer: common_1.yfmTransformer,
141
+ },
142
+ ],
137
143
  [models_1.BlockType.ExtendedFeaturesBlock]: [
138
144
  ...exports.blockHeaderTransformer,
139
145
  {
@@ -5,7 +5,6 @@ unpredictable css rules order in build */
5
5
  line-height: var(--g-text-body-3-line-height);
6
6
  display: flex;
7
7
  flex-direction: column;
8
- color: var(--g-color-text-secondary);
9
8
  }
10
9
  .pc-author__avatar {
11
10
  width: 80px;
@@ -25,11 +24,11 @@ unpredictable css rules order in build */
25
24
  .pc-author__name {
26
25
  font-size: var(--g-text-body-3-font-size);
27
26
  line-height: var(--g-text-body-3-line-height);
28
- color: var(--g-color-text-primary);
29
27
  }
30
28
  .pc-author__description {
31
29
  font-size: var(--g-text-body-2-font-size);
32
30
  line-height: var(--g-text-body-2-line-height);
31
+ color: var(--g-color-text-secondary);
33
32
  padding-top: 4px;
34
33
  }
35
34
  .pc-author__label:not(:first-child) {
@@ -53,4 +52,7 @@ unpredictable css rules order in build */
53
52
  }
54
53
  .pc-author_type_line .pc-author__label:not(:first-child) {
55
54
  margin-left: 16px;
55
+ }
56
+ .pc-author_theme_dark .pc-author__description {
57
+ color: var(--g-color-text-light-secondary);
56
58
  }
@@ -6,7 +6,7 @@ import { Image } from '../index';
6
6
  import './Author.css';
7
7
  const b = block('author');
8
8
  const Author = (props) => {
9
- const { author, className, authorContainerClassName, type = AuthorType.Column, qa } = props;
9
+ const { author, className, authorContainerClassName, type = AuthorType.Column, qa, theme, } = props;
10
10
  const { firstName, secondName, description, avatar } = author;
11
11
  const name = secondName ? `${firstName} ${secondName}` : firstName;
12
12
  const isAvatarJSX = React.isValidElement(avatar);
@@ -14,7 +14,7 @@ const Author = (props) => {
14
14
  if (!isAvatarJSX && avatar) {
15
15
  avatarProps = getMediaImage(avatar);
16
16
  }
17
- return (React.createElement("div", { className: b({ type }, className), "data-qa": qa },
17
+ return (React.createElement("div", { className: b({ type, theme }, className), "data-qa": qa },
18
18
  avatar && (React.createElement("div", { className: b('avatar', authorContainerClassName) }, isAvatarJSX ? avatar : React.createElement(Image, Object.assign({}, avatarProps)))),
19
19
  React.createElement("div", { className: b('label') },
20
20
  React.createElement("div", { className: b('name') }, name),
@@ -0,0 +1,56 @@
1
+ .pc-code-editor {
2
+ height: 100%;
3
+ position: relative;
4
+ overflow: hidden;
5
+ }
6
+ .pc-code-editor_fullscreen {
7
+ position: fixed;
8
+ top: 0;
9
+ left: 0;
10
+ width: 100%;
11
+ height: 100vh;
12
+ z-index: 1000;
13
+ background: var(--g-color-base-background);
14
+ }
15
+ .pc-code-editor_fullscreen .pc-code-editor__header {
16
+ margin-top: var(--pc-editor-header-height);
17
+ }
18
+ .pc-code-editor__code {
19
+ width: 100%;
20
+ height: 100%;
21
+ }
22
+ .pc-code-editor__header, .pc-code-editor__footer {
23
+ padding: 0 20px;
24
+ background: var(--g-color-base-background);
25
+ }
26
+ .pc-code-editor__header {
27
+ display: flex;
28
+ align-items: center;
29
+ justify-content: flex-end;
30
+ height: var(--pc-editor-code-header-height);
31
+ border-bottom: 1px solid var(--g-color-line-generic);
32
+ }
33
+ .pc-code-editor__footer {
34
+ position: absolute;
35
+ left: 0;
36
+ bottom: 0;
37
+ width: 100%;
38
+ min-height: var(--pc-editor-code-header-height);
39
+ border-top: 1px solid var(--g-color-line-generic);
40
+ }
41
+ .pc-code-editor__message-container {
42
+ max-height: 140px;
43
+ padding: 12px;
44
+ overflow-y: auto;
45
+ font-family: Menlo, Monaco, "Courier New", monospace;
46
+ white-space: pre-wrap;
47
+ }
48
+ .pc-code-editor__message_status_success {
49
+ color: var(--g-color-text-positive);
50
+ }
51
+ .pc-code-editor__message_status_warning {
52
+ color: var(--g-color-text-warning-heavy);
53
+ }
54
+ .pc-code-editor__message_status_error {
55
+ color: var(--g-color-text-danger);
56
+ }
@@ -0,0 +1,13 @@
1
+ import { PageContent } from '../../../models';
2
+ import { CodeEditorMessageProps } from '../../utils/validation';
3
+ import './CodeEditor.css';
4
+ interface CodeEditorProps {
5
+ content: PageContent;
6
+ fullscreenModeOn: boolean;
7
+ validator: (code: string) => CodeEditorMessageProps;
8
+ onFullscreenModeOnUpdate: (fullscreenModeOn: boolean) => void;
9
+ onChange: (content: PageContent) => void;
10
+ message?: CodeEditorMessageProps;
11
+ }
12
+ export declare const CodeEditor: ({ content, onChange, validator, fullscreenModeOn, onFullscreenModeOnUpdate, }: CodeEditorProps) => JSX.Element;
13
+ export {};
@@ -0,0 +1,27 @@
1
+ import React, { useCallback, useMemo, useState } from 'react';
2
+ import { ChevronsCollapseUpRight, ChevronsExpandUpRight } from '@gravity-ui/icons';
3
+ import { Button, Icon } from '@gravity-ui/uikit';
4
+ import yaml from 'js-yaml';
5
+ import MonacoEditor from 'react-monaco-editor';
6
+ import { block } from '../../../utils';
7
+ import { parseCode } from '../../utils/code';
8
+ import { options } from './constants';
9
+ import './CodeEditor.css';
10
+ const b = block('code-editor');
11
+ export const CodeEditor = ({ content, onChange, validator, fullscreenModeOn, onFullscreenModeOnUpdate, }) => {
12
+ const value = useMemo(() => yaml.dump(content), [content]);
13
+ const [message, setMessage] = useState(() => validator(value));
14
+ const onChangeWithValidation = useCallback((code) => {
15
+ const validationResult = validator(code);
16
+ setMessage(validationResult);
17
+ onChange(parseCode(code));
18
+ }, [onChange, validator]);
19
+ return (React.createElement("div", { className: b({ fullscreen: fullscreenModeOn }) },
20
+ React.createElement("div", { className: b('header') },
21
+ React.createElement(Button, { view: "flat-secondary", onClick: () => onFullscreenModeOnUpdate(!fullscreenModeOn) },
22
+ React.createElement(Icon, { data: fullscreenModeOn ? ChevronsCollapseUpRight : ChevronsExpandUpRight, size: 16 }))),
23
+ React.createElement("div", { className: b('code') },
24
+ React.createElement(MonacoEditor, { key: String(fullscreenModeOn), value: value, language: "yaml", options: options, onChange: onChangeWithValidation, theme: "vs" })),
25
+ React.createElement("div", { className: b('footer') }, message && (React.createElement("div", { className: b('message-container') },
26
+ React.createElement("div", { className: b('message', { status: message.status }) }, message.text))))));
27
+ };
@@ -0,0 +1,2 @@
1
+ import { monaco } from 'react-monaco-editor';
2
+ export declare const options: monaco.editor.IStandaloneEditorConstructionOptions;
@@ -0,0 +1,17 @@
1
+ export const options = {
2
+ wordWrap: 'on',
3
+ renderLineHighlight: 'none',
4
+ selectOnLineNumbers: true,
5
+ renderWhitespace: 'all',
6
+ automaticLayout: true,
7
+ minimap: {
8
+ enabled: false,
9
+ },
10
+ overviewRulerLanes: 0,
11
+ hideCursorInOverviewRuler: true,
12
+ scrollbar: {
13
+ vertical: 'hidden',
14
+ },
15
+ overviewRulerBorder: false,
16
+ readOnly: false,
17
+ };
@@ -2,11 +2,12 @@
2
2
  unpredictable css rules order in build */
3
3
  body {
4
4
  --pc-editor-header-height: 48px;
5
+ --pc-editor-code-header-height: 36px;
5
6
  --pc-editor-divider-width: 12px;
7
+ --pc-editor-left-column-width: calc(400px + var(--pc-editor-divider-width));
6
8
  --pc-editor-base-color: var(--g-color-base-brand);
7
9
  --pc-editor-control-color: var(--g-color-base-brand);
8
10
  --pc-editor-control-icon-color: var(--g-color-text-dark-primary);
9
- --pc-editor-left-column-width: calc(400px + var(--pc-editor-divider-width));
10
11
  }
11
12
 
12
13
  .pc-editor-layout__left, .pc-editor-layout__right {
@@ -1,23 +1,27 @@
1
1
  import { __rest } from "tslib";
2
2
  import React, { useEffect, useMemo } from 'react';
3
3
  import { PageConstructor, PageConstructorProvider } from '../../../containers/PageConstructor';
4
+ import { generateDefaultSchema } from '../../../schema';
4
5
  import AddBlock from '../../components/AddBlock/AddBlock';
5
6
  import EditBlock from '../../components/EditBlock/EditBlock';
6
7
  import { ErrorBoundary } from '../../components/ErrorBoundary/ErrorBoundary';
7
8
  import Layout from '../../components/Layout/Layout';
8
9
  import { NotFoundBlock } from '../../components/NotFoundBlock/NotFoundBlock';
9
10
  import { EditorContext } from '../../context';
10
- import useFormSpec from '../../hooks/useFormSpec';
11
- import { useEditorState } from '../../store';
11
+ import { useCodeValidator } from '../../hooks/useCodeValidator';
12
+ import { useMainState } from '../../store/main';
13
+ import { useSettingsState } from '../../store/settings';
12
14
  import { ViewModeItem } from '../../types';
13
15
  import { addCustomDecorator, checkIsMobile, getBlockId } from '../../utils';
14
16
  import { Form } from '../Form/Form';
15
17
  export const Editor = (_a) => {
16
18
  var { customSchema, onChange, providerProps, transformContent, deviceEmulationSettings } = _a, rest = __rest(_a, ["customSchema", "onChange", "providerProps", "transformContent", "deviceEmulationSettings"]);
17
- const { content, activeBlockIndex, errorBoundaryState, viewMode, theme, onContentUpdate, onViewModeUpdate, onAdd, onSelect, injectEditBlockProps, onThemeUpdate, } = useEditorState(rest);
18
- const formSpecs = useFormSpec(customSchema);
19
+ const { content, activeBlockIndex, errorBoundaryState, onContentUpdate, onAdd, onSelect, injectEditBlockProps, } = useMainState(rest);
20
+ const { viewMode, theme, onViewModeUpdate, onThemeUpdate, formTab, onFormTabUpdate, codeFullscreeModeOn, onCodeFullscreeModeOnUpdate, } = useSettingsState();
19
21
  const isEditingMode = viewMode === ViewModeItem.Edititng;
20
22
  const transformedContent = useMemo(() => (transformContent ? transformContent(content, { viewMode }) : content), [content, transformContent, viewMode]);
23
+ const schema = useMemo(() => generateDefaultSchema(customSchema), [customSchema]);
24
+ const codeValidator = useCodeValidator(schema);
21
25
  const outgoingProps = useMemo(() => {
22
26
  const custom = isEditingMode
23
27
  ? addCustomDecorator([
@@ -54,7 +58,7 @@ export const Editor = (_a) => {
54
58
  return (React.createElement(EditorContext.Provider, { value: context },
55
59
  React.createElement(Layout, { mode: viewMode, onModeChange: onViewModeUpdate, theme: theme, onThemeChange: onThemeUpdate },
56
60
  isEditingMode && (React.createElement(Layout.Left, null,
57
- React.createElement(Form, { content: content, onChange: onContentUpdate, activeBlockIndex: activeBlockIndex, onSelect: onSelect, spec: formSpecs }))),
61
+ React.createElement(Form, { content: content, onChange: onContentUpdate, activeBlockIndex: activeBlockIndex, activeTab: formTab, codeFullscreeModeOn: codeFullscreeModeOn, schema: schema, codeValidator: codeValidator, onActiveTabUpdate: onFormTabUpdate, onCodeFullscreeModeOnUpdate: onCodeFullscreeModeOnUpdate, onSelect: onSelect }))),
58
62
  React.createElement(Layout.Right, null,
59
63
  React.createElement(ErrorBoundary, { key: errorBoundaryState },
60
64
  React.createElement(PageConstructorProvider, Object.assign({}, providerProps, { theme: theme }),
@@ -89,6 +89,9 @@ unpredictable css rules order in build */
89
89
  .pc-editor-form__block-form {
90
90
  margin-bottom: 16px;
91
91
  }
92
- .pc-editor-form_yaml-editor-enabled {
93
- height: 100%;
92
+ .pc-editor-form_code-editor-active {
93
+ height: calc(100% - var(--pc-editor-code-header-height));
94
+ }
95
+ .pc-editor-form_code-editor-active .pc-editor-form__tabs {
96
+ margin-bottom: 0;
94
97
  }
@@ -1,12 +1,19 @@
1
1
  import React from 'react';
2
+ import { JSONSchema4 } from 'json-schema';
2
3
  import { PageContent } from '../../../models';
3
- import { FormSpecs } from '../../dynamic-forms-custom/parser/types';
4
+ import { FormTab } from '../../types';
5
+ import { CodeEditorMessageProps } from '../../utils/validation';
4
6
  import './Form.css';
5
7
  export interface FormProps {
6
8
  content: PageContent;
9
+ schema: JSONSchema4;
7
10
  activeBlockIndex: number;
8
- spec: FormSpecs;
11
+ activeTab: FormTab;
12
+ codeFullscreeModeOn: boolean;
13
+ onActiveTabUpdate: (tab: FormTab) => void;
14
+ onCodeFullscreeModeOnUpdate: (codeFullscreeModeOn: boolean) => void;
15
+ codeValidator: (code: string) => CodeEditorMessageProps;
9
16
  onChange: (content: PageContent) => void;
10
17
  onSelect: (index: number) => void;
11
18
  }
12
- export declare const Form: React.MemoExoticComponent<({ content, onChange, activeBlockIndex, onSelect, spec }: FormProps) => JSX.Element>;
19
+ export declare const Form: React.MemoExoticComponent<({ content, onChange, activeBlockIndex, onSelect, schema, codeValidator, activeTab, onActiveTabUpdate, codeFullscreeModeOn, onCodeFullscreeModeOnUpdate, }: FormProps) => JSX.Element>;
@@ -3,23 +3,19 @@ import React, { Fragment, memo } from 'react';
3
3
  import { Tabs } from '@gravity-ui/uikit';
4
4
  import { block, getBlockKey } from '../../../utils';
5
5
  import { BlockForm } from '../../components/BlockForm/BlockForm';
6
+ import { CodeEditor } from '../../components/CodeEditor/CodeEditor';
6
7
  import { PagePropsForm } from '../../components/PagePropsForm/PagePropsForm';
7
- import { YamlEditor } from '../../components/YamlEditor/YamlEditor';
8
+ import useFormSpec from '../../hooks/useFormSpec';
9
+ import { FormTab } from '../../types';
8
10
  import './Form.css';
9
- var FormTab;
10
- (function (FormTab) {
11
- FormTab["Blocks"] = "blocks";
12
- FormTab["Page"] = "page";
13
- FormTab["Yaml"] = "yaml";
14
- })(FormTab || (FormTab = {}));
15
11
  const b = block('editor-form');
16
12
  const tabsItems = Object.values(FormTab).map((tab) => ({
17
13
  id: tab,
18
14
  title: tab,
19
15
  }));
20
- export const Form = memo(({ content, onChange, activeBlockIndex, onSelect, spec }) => {
21
- const [activeTab, setActiveTab] = React.useState(FormTab.Blocks);
16
+ export const Form = memo(({ content, onChange, activeBlockIndex, onSelect, schema, codeValidator, activeTab, onActiveTabUpdate, codeFullscreeModeOn, onCodeFullscreeModeOnUpdate, }) => {
22
17
  const _a = content || {}, { blocks } = _a, page = __rest(_a, ["blocks"]);
18
+ const spec = useFormSpec(schema);
23
19
  const { blocks: blocksSpec, page: pageSpec } = spec || {};
24
20
  let form;
25
21
  switch (activeTab) {
@@ -40,13 +36,13 @@ export const Form = memo(({ content, onChange, activeBlockIndex, onSelect, spec
40
36
  }, onSelect: () => onSelect(index) }))) : null)));
41
37
  break;
42
38
  }
43
- case FormTab.Yaml: {
44
- form = React.createElement(YamlEditor, { content: content });
39
+ case FormTab.Code: {
40
+ form = (React.createElement(CodeEditor, { content: content, onChange: onChange, validator: codeValidator, fullscreenModeOn: codeFullscreeModeOn, onFullscreenModeOnUpdate: onCodeFullscreeModeOnUpdate }));
45
41
  break;
46
42
  }
47
43
  }
48
- return (React.createElement("div", { className: b({ 'yaml-editor-enabled': activeTab === FormTab.Yaml }) },
49
- React.createElement(Tabs, { activeTab: activeTab, className: b('tabs'), items: tabsItems, onSelectTab: setActiveTab }),
44
+ return (React.createElement("div", { className: b({ 'code-editor-active': activeTab === FormTab.Code }) },
45
+ React.createElement(Tabs, { activeTab: activeTab, className: b('tabs'), items: tabsItems, onSelectTab: onActiveTabUpdate }),
50
46
  form));
51
47
  });
52
48
  Form.displayName = 'Form';
@@ -0,0 +1,4 @@
1
+ import { JSONSchema4 } from 'json-schema';
2
+ import { CodeEditorMessageProps } from '../utils/validation';
3
+ export type CodeValidator = (code: string) => CodeEditorMessageProps;
4
+ export declare function useCodeValidator(schema: JSONSchema4): CodeValidator;
@@ -0,0 +1,6 @@
1
+ import { useCallback, useMemo } from 'react';
2
+ import { createValidator, validate } from '../utils/validation';
3
+ export function useCodeValidator(schema) {
4
+ const validator = useMemo(() => createValidator(schema), [schema]);
5
+ return useCallback((code) => validate(code, validator), [validator]);
6
+ }
@@ -1,2 +1,2 @@
1
- import { SchemaCustomConfig } from '../../schema';
2
- export default function useFormSpec(customSchema?: SchemaCustomConfig): import("../dynamic-forms-custom/parser/types").FormSpecs;
1
+ import { JSONSchema4 } from 'json-schema';
2
+ export default function useFormSpec(schema: JSONSchema4): import("../dynamic-forms-custom/parser/types").FormSpecs;