@gravity-ui/page-constructor 3.7.0 → 3.8.1

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 (28) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/build/cjs/blocks/Header/Header.css +4 -0
  3. package/build/cjs/blocks/Header/Header.js +2 -2
  4. package/build/cjs/blocks/Slider/Arrow/Arrow.js +1 -1
  5. package/build/cjs/blocks/Slider/Slider.css +2 -2
  6. package/build/cjs/blocks/Slider/Slider.js +2 -2
  7. package/build/cjs/components/Media/Image/Image.js +1 -1
  8. package/build/cjs/components/MetaInfo/MetaInfo.js +1 -1
  9. package/build/cjs/components/YFMWrapper/YFMWrapper.js +2 -1
  10. package/build/cjs/editor/components/BlockForm/BlockForm.js +6 -11
  11. package/build/cjs/navigation/components/NavigationItem/components/NavigationDropdown/NavigationDropdown.js +1 -1
  12. package/build/cjs/utils/blocks.d.ts +1 -0
  13. package/build/cjs/utils/blocks.js +43 -1
  14. package/build/esm/blocks/Header/Header.css +4 -0
  15. package/build/esm/blocks/Header/Header.js +2 -2
  16. package/build/esm/blocks/Slider/Arrow/Arrow.js +1 -1
  17. package/build/esm/blocks/Slider/Slider.css +2 -2
  18. package/build/esm/blocks/Slider/Slider.js +2 -2
  19. package/build/esm/components/Media/Image/Image.js +1 -1
  20. package/build/esm/components/MetaInfo/MetaInfo.js +1 -1
  21. package/build/esm/components/YFMWrapper/YFMWrapper.js +2 -1
  22. package/build/esm/editor/components/BlockForm/BlockForm.js +7 -12
  23. package/build/esm/navigation/components/NavigationItem/components/NavigationDropdown/NavigationDropdown.js +1 -1
  24. package/build/esm/utils/blocks.d.ts +1 -0
  25. package/build/esm/utils/blocks.js +41 -0
  26. package/package.json +5 -4
  27. package/server/utils/blocks.d.ts +1 -0
  28. package/server/utils/blocks.js +43 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.8.1](https://github.com/gravity-ui/page-constructor/compare/v3.8.0...v3.8.1) (2023-06-22)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * semantic markup ([#416](https://github.com/gravity-ui/page-constructor/issues/416)) ([1fd532c](https://github.com/gravity-ui/page-constructor/commit/1fd532ca0bcc2e6cd1f030b17be743c27fb2cf73))
9
+
10
+ ## [3.8.0](https://github.com/gravity-ui/page-constructor/compare/v3.7.0...v3.8.0) (2023-06-22)
11
+
12
+
13
+ ### Features
14
+
15
+ * add yfm transoform for editor demo ([#420](https://github.com/gravity-ui/page-constructor/issues/420)) ([1c1a2aa](https://github.com/gravity-ui/page-constructor/commit/1c1a2aa134a3c4d18e01537c1ca874720f23649d))
16
+ * update dynamic forms and fix form render performance ([#418](https://github.com/gravity-ui/page-constructor/issues/418)) ([eb9437d](https://github.com/gravity-ui/page-constructor/commit/eb9437df0d38a9a3433e85d111c45b3a49dbd9aa))
17
+
3
18
  ## [3.7.0](https://github.com/gravity-ui/page-constructor/compare/v3.6.2...v3.7.0) (2023-06-21)
4
19
 
5
20
 
@@ -76,6 +76,9 @@ unpredictable css rules order in build */
76
76
  .pc-header-block__description {
77
77
  margin-top: 16px;
78
78
  font-weight: normal;
79
+ font-size: var(--yc-text-caption-2-font-size);
80
+ line-height: var(--yc-text-caption-2-line-height);
81
+ margin-block-end: var(--yc-text-body-1-line-height);
79
82
  }
80
83
  .pc-header-block__description .yfm,
81
84
  .pc-header-block__description .yfm * {
@@ -167,6 +170,7 @@ unpredictable css rules order in build */
167
170
  .pc-header-block__overtitle {
168
171
  font-size: var(--yc-text-body-3-font-size);
169
172
  line-height: var(--yc-text-body-3-line-height);
173
+ margin-block-start: var(--yc-text-body-3-line-height);
170
174
  margin-bottom: 8px;
171
175
  font-weight: 400;
172
176
  }
@@ -51,12 +51,12 @@ const HeaderBlock = (props) => {
51
51
  'vertical-offset': curVerticalOffset,
52
52
  }) },
53
53
  react_1.default.createElement(grid_1.Col, { sizes: titleSizes, className: b('content-inner') },
54
- overtitle && (react_1.default.createElement("h4", { className: b('overtitle') },
54
+ overtitle && (react_1.default.createElement("p", { className: b('overtitle') },
55
55
  react_1.default.createElement(components_1.HTML, null, overtitle))),
56
56
  react_1.default.createElement("h1", { className: b('title') },
57
57
  status,
58
58
  react_1.default.createElement(components_1.HTML, null, title)),
59
- description && (react_1.default.createElement("h5", { className: b('description') },
59
+ description && (react_1.default.createElement("p", { className: b('description') },
60
60
  react_1.default.createElement(YFMWrapper_1.default, { content: description, modifiers: { constructor: true } }))),
61
61
  buttons && (react_1.default.createElement("div", { className: b('buttons'), "data-qa": "header-buttons" }, buttons &&
62
62
  buttons.map((button, index) => (react_1.default.createElement(components_1.RouterLink, { href: button.url, key: index },
@@ -7,6 +7,6 @@ const utils_1 = require("../../../utils");
7
7
  const b = (0, utils_1.block)('slider-block-arrow');
8
8
  const Arrow = ({ type, handleClick, className, size = 16 }) => (react_1.default.createElement("div", { className: b({ type }, className) },
9
9
  react_1.default.createElement("button", { className: b('button'), onClick: () => handleClick && handleClick(type) },
10
- react_1.default.createElement("div", { className: b('icon-wrapper') },
10
+ react_1.default.createElement("span", { className: b('icon-wrapper') },
11
11
  react_1.default.createElement(ToggleArrow_1.default, { size: size, type: 'horizontal', iconType: "navigation", className: b('icon') })))));
12
12
  exports.default = Arrow;
@@ -133,8 +133,8 @@ unpredictable css rules order in build */
133
133
  display: inline-flex;
134
134
  justify-content: center;
135
135
  }
136
- .pc-SliderBlock__dots-list div.pc-SliderBlock__bar,
137
- .pc-SliderBlock__dots-list div.pc-SliderBlock__dot {
136
+ .pc-SliderBlock__dots-list li.pc-SliderBlock__bar,
137
+ .pc-SliderBlock__dots-list li.pc-SliderBlock__dot {
138
138
  margin: calc(12px / 2) 8px;
139
139
  top: 0;
140
140
  }
@@ -124,13 +124,13 @@ const SliderBlock = (props) => {
124
124
  const renderBar = () => {
125
125
  const barPosition = (DOT_GAP + DOT_WIDTH) * currentIndex;
126
126
  const barWidth = DOT_WIDTH + (DOT_GAP + DOT_WIDTH) * (slidesCountByBreakpoint - 1);
127
- return (slidesCountByBreakpoint > 1 && (react_1.default.createElement("div", { className: b('bar'), style: {
127
+ return (slidesCountByBreakpoint > 1 && (react_1.default.createElement("li", { className: b('bar'), style: {
128
128
  left: barPosition,
129
129
  width: barWidth,
130
130
  } })));
131
131
  };
132
132
  const renderDot = (index) => {
133
- return (react_1.default.createElement("div", { key: index, className: b('dot', { active: index === currentIndex }), onClick: () => handleDotClick(index) }));
133
+ return (react_1.default.createElement("li", { key: index, className: b('dot', { active: index === currentIndex }), onClick: () => handleDotClick(index) }));
134
134
  };
135
135
  const renderNavigation = () => {
136
136
  if (childrenCount <= slidesCountByBreakpoint || !dots || childrenCount === 1) {
@@ -40,7 +40,7 @@ const Image = (props) => {
40
40
  };
41
41
  const imageBackground = (oneImage) => {
42
42
  const imageData = (0, utils_2.getMediaImage)(oneImage);
43
- return (react_1.default.createElement(react_spring_1.animated.div, { style: { transform: parallaxInterpolate } },
43
+ return (react_1.default.createElement(react_spring_1.animated.div, { style: { transform: parallaxInterpolate || 'none' } },
44
44
  react_1.default.createElement(BackgroundImage_1.default, Object.assign({}, imageData, { className: imageClass, style: { height } }))));
45
45
  };
46
46
  const imageOnly = (oneImage) => {
@@ -4,5 +4,5 @@ const tslib_1 = require("tslib");
4
4
  const react_1 = tslib_1.__importDefault(require("react"));
5
5
  const utils_1 = require("../../utils");
6
6
  const b = (0, utils_1.block)('meta-info');
7
- const MetaInfo = ({ items, className }) => (react_1.default.createElement("h4", { className: b(null, className) }, items.map((metaInfoItem) => (react_1.default.createElement("div", { key: metaInfoItem, className: b('item') }, metaInfoItem)))));
7
+ const MetaInfo = ({ items, className }) => (react_1.default.createElement("h4", { className: b(null, className) }, items.map((metaInfoItem) => (react_1.default.createElement("span", { key: metaInfoItem, className: b('item') }, metaInfoItem)))));
8
8
  exports.default = MetaInfo;
@@ -7,6 +7,7 @@ const react_1 = tslib_1.__importDefault(require("react"));
7
7
  const bem_cn_lite_1 = tslib_1.__importDefault(require("bem-cn-lite"));
8
8
  const snakecase_keys_1 = tslib_1.__importDefault(require("snakecase-keys"));
9
9
  const components_1 = require("../../components");
10
+ const blocks_1 = require("../../utils/blocks");
10
11
  const yfm = (0, bem_cn_lite_1.default)('yfm');
11
- const YFMWrapper = ({ content, modifiers, className, itemProp, }) => (react_1.default.createElement(components_1.HTML, { className: yfm(modifiers ? (0, snakecase_keys_1.default)(modifiers) : {}, className), itemProp: itemProp }, content));
12
+ const YFMWrapper = ({ content, modifiers, className, itemProp, }) => (react_1.default.createElement(components_1.HTML, { className: yfm(modifiers ? (0, snakecase_keys_1.default)(modifiers) : {}, className), itemProp: itemProp, block: (0, blocks_1.hasBlockTag)(content) }, content));
12
13
  exports.default = YFMWrapper;
@@ -10,24 +10,19 @@ const config_1 = require("../../dynamic-forms-custom/config");
10
10
  const usePreviousValue_1 = tslib_1.__importDefault(require("../../hooks/usePreviousValue"));
11
11
  exports.BlockForm = (0, react_1.memo)((_a) => {
12
12
  var _b = _a.data, { type } = _b, content = tslib_1.__rest(_b, ["type"]), { onChange, onSelect, active, spec: specRaw } = _a;
13
- // get initial values only at first render, then the form manages data
14
- // eslint-disable-next-line react-hooks/exhaustive-deps
15
- const initialValues = (0, react_1.useMemo)(() => ({ content }), []);
13
+ const initialValues = (0, react_1.useMemo)(() => ({ content }), [content]);
16
14
  const prevContent = (0, usePreviousValue_1.default)(content);
17
15
  const spec = (0, react_1.useMemo)(() => (Object.assign(Object.assign({}, specRaw), { viewSpec: Object.assign(Object.assign({}, specRaw.viewSpec), { layoutOpen: active }) })), [specRaw, active]);
18
- return (react_1.default.createElement(react_final_form_1.Form, { initialValues: initialValues, onSubmit: lodash_1.default.noop }, () => (react_1.default.createElement("div", { onClick: () => {
19
- if (!active) {
20
- onSelect();
21
- }
22
- } },
16
+ if (!active) {
17
+ return (react_1.default.createElement(dynamic_forms_1.SimpleVerticalAccordeon, { open: false, name: type, title: spec.viewSpec.layoutTitle || type, onOpenChange: onSelect }, 1));
18
+ }
19
+ return (react_1.default.createElement(react_final_form_1.Form, { initialValues: initialValues, onSubmit: lodash_1.default.noop }, () => (react_1.default.createElement("div", null,
23
20
  react_1.default.createElement(react_final_form_1.FormSpy, { onChange: ({ values }) => {
24
21
  // fix for FormSpy onChange called twice without content changes
25
22
  if (!lodash_1.default.isEqual(values.content, prevContent)) {
26
23
  onChange(Object.assign({ type }, values.content));
27
24
  }
28
25
  }, subscription: { values: true } }),
29
- react_1.default.createElement(dynamic_forms_1.DynamicField, { name: "content",
30
- // there is no way other way to manage with form open/close state now
31
- key: String(active), spec: spec, config: config_1.dynamicConfig })))));
26
+ react_1.default.createElement(dynamic_forms_1.DynamicField, { name: "content", spec: spec, config: config_1.dynamicConfig, withoutInsertFFDebounce: true })))));
32
27
  });
33
28
  exports.BlockForm.displayName = 'BlockForm';
@@ -10,7 +10,7 @@ const ContentWrapper_1 = require("../ContentWrapper/ContentWrapper");
10
10
  const b = (0, utils_2.block)('navigation-dropdown');
11
11
  const TOGGLE_ARROW_SIZE = 12;
12
12
  exports.NavigationDropdown = react_1.default.forwardRef((_a, ref) => {
13
- var { text, icon, isOpened, className, iconSize } = _a, props = tslib_1.__rest(_a, ["text", "icon", "isOpened", "className", "iconSize"]);
13
+ var { text, icon, isOpened, className, iconSize, type: _ } = _a, props = tslib_1.__rest(_a, ["text", "icon", "isOpened", "className", "iconSize", "type"]);
14
14
  const iconData = icon && (0, utils_1.getMediaImage)(icon);
15
15
  return (react_1.default.createElement("span", Object.assign({ ref: ref }, props, { className: b(null, className) }),
16
16
  react_1.default.createElement(ContentWrapper_1.ContentWrapper, { text: text, icon: iconData, iconSize: iconSize }),
@@ -1,6 +1,7 @@
1
1
  import { Block, CustomConfig, PCShareSocialNetwork, TextSize } from '../models';
2
2
  import { ConstructorBlock } from '../models/constructor';
3
3
  export declare function getHeaderTag(size: TextSize): "h1" | "h2" | "h4" | "h5";
4
+ export declare function hasBlockTag(content: string): boolean;
4
5
  export declare function getBlockKey(block: ConstructorBlock, index: number): string;
5
6
  export declare const getCustomBlockTypes: ({ blocks, headers }?: CustomConfig) => string[];
6
7
  export declare const getCustomItems: ({ blocks, headers, subBlocks }?: CustomConfig) => {
@@ -1,7 +1,43 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getShareLink = exports.getHeaderBlock = exports.getOrderedBlocks = exports.getCustomHeaderTypes = exports.getCustomSubBlockTypes = exports.getCustomItems = exports.getCustomBlockTypes = exports.getBlockKey = exports.getHeaderTag = void 0;
3
+ exports.getShareLink = exports.getHeaderBlock = exports.getOrderedBlocks = exports.getCustomHeaderTypes = exports.getCustomSubBlockTypes = exports.getCustomItems = exports.getCustomBlockTypes = exports.getBlockKey = exports.hasBlockTag = exports.getHeaderTag = void 0;
4
4
  const models_1 = require("../models");
5
+ const BLOCK_ELEMENTS = [
6
+ 'div',
7
+ 'p',
8
+ 'h[1-6]',
9
+ 'address',
10
+ 'article',
11
+ 'aside',
12
+ 'blockquote',
13
+ 'canvas',
14
+ 'dd',
15
+ 'dl',
16
+ 'dt',
17
+ 'fieldset',
18
+ 'figcaption',
19
+ 'footer',
20
+ 'header',
21
+ 'hr',
22
+ 'main',
23
+ 'nav',
24
+ 'section',
25
+ 'video',
26
+ 'pre',
27
+ 'figure',
28
+ 'form',
29
+ 'pre',
30
+ 'ol',
31
+ 'ul',
32
+ 'li',
33
+ 'table',
34
+ 'thead',
35
+ 'tbody',
36
+ 'tfoot',
37
+ 'tr',
38
+ 'th',
39
+ 'td',
40
+ ];
5
41
  function getHeaderTag(size) {
6
42
  switch (size) {
7
43
  case 'l':
@@ -16,6 +52,12 @@ function getHeaderTag(size) {
16
52
  }
17
53
  }
18
54
  exports.getHeaderTag = getHeaderTag;
55
+ function hasBlockTag(content) {
56
+ const blockElementRegex = `/<(${BLOCK_ELEMENTS.join('|')})\b[^>]*>/gi`;
57
+ const blockTags = content.match(blockElementRegex);
58
+ return Boolean(blockTags);
59
+ }
60
+ exports.hasBlockTag = hasBlockTag;
19
61
  function getBlockKey(block, index) {
20
62
  return `${block.type}-${index}`;
21
63
  }
@@ -76,6 +76,9 @@ unpredictable css rules order in build */
76
76
  .pc-header-block__description {
77
77
  margin-top: 16px;
78
78
  font-weight: normal;
79
+ font-size: var(--yc-text-caption-2-font-size);
80
+ line-height: var(--yc-text-caption-2-line-height);
81
+ margin-block-end: var(--yc-text-body-1-line-height);
79
82
  }
80
83
  .pc-header-block__description .yfm,
81
84
  .pc-header-block__description .yfm * {
@@ -167,6 +170,7 @@ unpredictable css rules order in build */
167
170
  .pc-header-block__overtitle {
168
171
  font-size: var(--yc-text-body-3-font-size);
169
172
  line-height: var(--yc-text-body-3-line-height);
173
+ margin-block-start: var(--yc-text-body-3-line-height);
170
174
  margin-bottom: 8px;
171
175
  font-weight: 400;
172
176
  }
@@ -48,12 +48,12 @@ export const HeaderBlock = (props) => {
48
48
  'vertical-offset': curVerticalOffset,
49
49
  }) },
50
50
  React.createElement(Col, { sizes: titleSizes, className: b('content-inner') },
51
- overtitle && (React.createElement("h4", { className: b('overtitle') },
51
+ overtitle && (React.createElement("p", { className: b('overtitle') },
52
52
  React.createElement(HTML, null, overtitle))),
53
53
  React.createElement("h1", { className: b('title') },
54
54
  status,
55
55
  React.createElement(HTML, null, title)),
56
- description && (React.createElement("h5", { className: b('description') },
56
+ description && (React.createElement("p", { className: b('description') },
57
57
  React.createElement(YFMWrapper, { content: description, modifiers: { constructor: true } }))),
58
58
  buttons && (React.createElement("div", { className: b('buttons'), "data-qa": "header-buttons" }, buttons &&
59
59
  buttons.map((button, index) => (React.createElement(RouterLink, { href: button.url, key: index },
@@ -5,6 +5,6 @@ import './Arrow.css';
5
5
  const b = block('slider-block-arrow');
6
6
  const Arrow = ({ type, handleClick, className, size = 16 }) => (React.createElement("div", { className: b({ type }, className) },
7
7
  React.createElement("button", { className: b('button'), onClick: () => handleClick && handleClick(type) },
8
- React.createElement("div", { className: b('icon-wrapper') },
8
+ React.createElement("span", { className: b('icon-wrapper') },
9
9
  React.createElement(ToggleArrow, { size: size, type: 'horizontal', iconType: "navigation", className: b('icon') })))));
10
10
  export default Arrow;
@@ -133,8 +133,8 @@ unpredictable css rules order in build */
133
133
  display: inline-flex;
134
134
  justify-content: center;
135
135
  }
136
- .pc-SliderBlock__dots-list div.pc-SliderBlock__bar,
137
- .pc-SliderBlock__dots-list div.pc-SliderBlock__dot {
136
+ .pc-SliderBlock__dots-list li.pc-SliderBlock__bar,
137
+ .pc-SliderBlock__dots-list li.pc-SliderBlock__dot {
138
138
  margin: calc(12px / 2) 8px;
139
139
  top: 0;
140
140
  }
@@ -121,13 +121,13 @@ export const SliderBlock = (props) => {
121
121
  const renderBar = () => {
122
122
  const barPosition = (DOT_GAP + DOT_WIDTH) * currentIndex;
123
123
  const barWidth = DOT_WIDTH + (DOT_GAP + DOT_WIDTH) * (slidesCountByBreakpoint - 1);
124
- return (slidesCountByBreakpoint > 1 && (React.createElement("div", { className: b('bar'), style: {
124
+ return (slidesCountByBreakpoint > 1 && (React.createElement("li", { className: b('bar'), style: {
125
125
  left: barPosition,
126
126
  width: barWidth,
127
127
  } })));
128
128
  };
129
129
  const renderDot = (index) => {
130
- return (React.createElement("div", { key: index, className: b('dot', { active: index === currentIndex }), onClick: () => handleDotClick(index) }));
130
+ return (React.createElement("li", { key: index, className: b('dot', { active: index === currentIndex }), onClick: () => handleDotClick(index) }));
131
131
  };
132
132
  const renderNavigation = () => {
133
133
  if (childrenCount <= slidesCountByBreakpoint || !dots || childrenCount === 1) {
@@ -38,7 +38,7 @@ const Image = (props) => {
38
38
  };
39
39
  const imageBackground = (oneImage) => {
40
40
  const imageData = getMediaImage(oneImage);
41
- return (React.createElement(animated.div, { style: { transform: parallaxInterpolate } },
41
+ return (React.createElement(animated.div, { style: { transform: parallaxInterpolate || 'none' } },
42
42
  React.createElement(BackgroundImage, Object.assign({}, imageData, { className: imageClass, style: { height } }))));
43
43
  };
44
44
  const imageOnly = (oneImage) => {
@@ -2,5 +2,5 @@ import React from 'react';
2
2
  import { block } from '../../utils';
3
3
  import './MetaInfo.css';
4
4
  const b = block('meta-info');
5
- const MetaInfo = ({ items, className }) => (React.createElement("h4", { className: b(null, className) }, items.map((metaInfoItem) => (React.createElement("div", { key: metaInfoItem, className: b('item') }, metaInfoItem)))));
5
+ const MetaInfo = ({ items, className }) => (React.createElement("h4", { className: b(null, className) }, items.map((metaInfoItem) => (React.createElement("span", { key: metaInfoItem, className: b('item') }, metaInfoItem)))));
6
6
  export default MetaInfo;
@@ -4,6 +4,7 @@ import React from 'react';
4
4
  import block from 'bem-cn-lite';
5
5
  import toSnakeCase from 'snakecase-keys';
6
6
  import { HTML } from '../../components';
7
+ import { hasBlockTag } from '../../utils/blocks';
7
8
  const yfm = block('yfm');
8
- const YFMWrapper = ({ content, modifiers, className, itemProp, }) => (React.createElement(HTML, { className: yfm(modifiers ? toSnakeCase(modifiers) : {}, className), itemProp: itemProp }, content));
9
+ const YFMWrapper = ({ content, modifiers, className, itemProp, }) => (React.createElement(HTML, { className: yfm(modifiers ? toSnakeCase(modifiers) : {}, className), itemProp: itemProp, block: hasBlockTag(content) }, content));
9
10
  export default YFMWrapper;
@@ -1,30 +1,25 @@
1
1
  import { __rest } from "tslib";
2
2
  import React, { memo, useMemo } from 'react';
3
- import { DynamicField } from '@gravity-ui/dynamic-forms';
3
+ import { DynamicField, SimpleVerticalAccordeon } from '@gravity-ui/dynamic-forms';
4
4
  import _ from 'lodash';
5
5
  import { Form as FinalForm, FormSpy } from 'react-final-form';
6
6
  import { dynamicConfig } from '../../dynamic-forms-custom/config';
7
7
  import usePreviousValue from '../../hooks/usePreviousValue';
8
8
  export const BlockForm = memo((_a) => {
9
9
  var _b = _a.data, { type } = _b, content = __rest(_b, ["type"]), { onChange, onSelect, active, spec: specRaw } = _a;
10
- // get initial values only at first render, then the form manages data
11
- // eslint-disable-next-line react-hooks/exhaustive-deps
12
- const initialValues = useMemo(() => ({ content }), []);
10
+ const initialValues = useMemo(() => ({ content }), [content]);
13
11
  const prevContent = usePreviousValue(content);
14
12
  const spec = useMemo(() => (Object.assign(Object.assign({}, specRaw), { viewSpec: Object.assign(Object.assign({}, specRaw.viewSpec), { layoutOpen: active }) })), [specRaw, active]);
15
- return (React.createElement(FinalForm, { initialValues: initialValues, onSubmit: _.noop }, () => (React.createElement("div", { onClick: () => {
16
- if (!active) {
17
- onSelect();
18
- }
19
- } },
13
+ if (!active) {
14
+ return (React.createElement(SimpleVerticalAccordeon, { open: false, name: type, title: spec.viewSpec.layoutTitle || type, onOpenChange: onSelect }, 1));
15
+ }
16
+ return (React.createElement(FinalForm, { initialValues: initialValues, onSubmit: _.noop }, () => (React.createElement("div", null,
20
17
  React.createElement(FormSpy, { onChange: ({ values }) => {
21
18
  // fix for FormSpy onChange called twice without content changes
22
19
  if (!_.isEqual(values.content, prevContent)) {
23
20
  onChange(Object.assign({ type }, values.content));
24
21
  }
25
22
  }, subscription: { values: true } }),
26
- React.createElement(DynamicField, { name: "content",
27
- // there is no way other way to manage with form open/close state now
28
- key: String(active), spec: spec, config: dynamicConfig })))));
23
+ React.createElement(DynamicField, { name: "content", spec: spec, config: dynamicConfig, withoutInsertFFDebounce: true })))));
29
24
  });
30
25
  BlockForm.displayName = 'BlockForm';
@@ -8,7 +8,7 @@ import './NavigationDropdown.css';
8
8
  const b = block('navigation-dropdown');
9
9
  const TOGGLE_ARROW_SIZE = 12;
10
10
  export const NavigationDropdown = React.forwardRef((_a, ref) => {
11
- var { text, icon, isOpened, className, iconSize } = _a, props = __rest(_a, ["text", "icon", "isOpened", "className", "iconSize"]);
11
+ var { text, icon, isOpened, className, iconSize, type: _ } = _a, props = __rest(_a, ["text", "icon", "isOpened", "className", "iconSize", "type"]);
12
12
  const iconData = icon && getMediaImage(icon);
13
13
  return (React.createElement("span", Object.assign({ ref: ref }, props, { className: b(null, className) }),
14
14
  React.createElement(ContentWrapper, { text: text, icon: iconData, iconSize: iconSize }),
@@ -1,6 +1,7 @@
1
1
  import { Block, CustomConfig, PCShareSocialNetwork, TextSize } from '../models';
2
2
  import { ConstructorBlock } from '../models/constructor';
3
3
  export declare function getHeaderTag(size: TextSize): "h1" | "h2" | "h4" | "h5";
4
+ export declare function hasBlockTag(content: string): boolean;
4
5
  export declare function getBlockKey(block: ConstructorBlock, index: number): string;
5
6
  export declare const getCustomBlockTypes: ({ blocks, headers }?: CustomConfig) => string[];
6
7
  export declare const getCustomItems: ({ blocks, headers, subBlocks }?: CustomConfig) => {
@@ -1,4 +1,40 @@
1
1
  import { PCShareSocialNetwork } from '../models';
2
+ const BLOCK_ELEMENTS = [
3
+ 'div',
4
+ 'p',
5
+ 'h[1-6]',
6
+ 'address',
7
+ 'article',
8
+ 'aside',
9
+ 'blockquote',
10
+ 'canvas',
11
+ 'dd',
12
+ 'dl',
13
+ 'dt',
14
+ 'fieldset',
15
+ 'figcaption',
16
+ 'footer',
17
+ 'header',
18
+ 'hr',
19
+ 'main',
20
+ 'nav',
21
+ 'section',
22
+ 'video',
23
+ 'pre',
24
+ 'figure',
25
+ 'form',
26
+ 'pre',
27
+ 'ol',
28
+ 'ul',
29
+ 'li',
30
+ 'table',
31
+ 'thead',
32
+ 'tbody',
33
+ 'tfoot',
34
+ 'tr',
35
+ 'th',
36
+ 'td',
37
+ ];
2
38
  export function getHeaderTag(size) {
3
39
  switch (size) {
4
40
  case 'l':
@@ -12,6 +48,11 @@ export function getHeaderTag(size) {
12
48
  return 'h2';
13
49
  }
14
50
  }
51
+ export function hasBlockTag(content) {
52
+ const blockElementRegex = `/<(${BLOCK_ELEMENTS.join('|')})\b[^>]*>/gi`;
53
+ const blockTags = content.match(blockElementRegex);
54
+ return Boolean(blockTags);
55
+ }
15
56
  export function getBlockKey(block, index) {
16
57
  return `${block.type}-${index}`;
17
58
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/page-constructor",
3
- "version": "3.7.0",
3
+ "version": "3.8.1",
4
4
  "description": "Gravity UI Page Constructor",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -77,7 +77,7 @@
77
77
  "test:watch": "jest --watchAll"
78
78
  },
79
79
  "dependencies": {
80
- "@gravity-ui/dynamic-forms": "1.7.1",
80
+ "@gravity-ui/dynamic-forms": "^1.9.2",
81
81
  "@gravity-ui/i18n": "^1.0.0",
82
82
  "bem-cn-lite": "^4.0.0",
83
83
  "final-form": "^4.20.9",
@@ -89,7 +89,7 @@
89
89
  "react-spring": "^9.3.0",
90
90
  "react-transition-group": "^4.4.2",
91
91
  "react-waypoint": "^10.1.0",
92
- "sanitize-html": "^2.6.1",
92
+ "sanitize-html": "2.6.1",
93
93
  "snakecase-keys": "^5.1.0",
94
94
  "typograf": "^6.14.0",
95
95
  "uuid": "^9.0.0"
@@ -125,7 +125,7 @@
125
125
  "@types/react-dom": "^18.0.10",
126
126
  "@types/react-slick": "^0.23.7",
127
127
  "@types/react-transition-group": "^4.4.4",
128
- "@types/sanitize-html": "^2.6.0",
128
+ "@types/sanitize-html": "2.6.1",
129
129
  "@types/uuid": "^9.0.0",
130
130
  "es5-ext": "0.10.53",
131
131
  "eslint": "^8.34.0",
@@ -137,6 +137,7 @@
137
137
  "gulp-replace": "^1.1.3",
138
138
  "gulp-typescript": "^6.0.0-alpha.1",
139
139
  "html-loader": "^1.3.2",
140
+ "htmlparser2": "^6.1.0",
140
141
  "husky": "^7.0.4",
141
142
  "jest": "^29.3.1",
142
143
  "jest-environment-jsdom": "^29.3.1",
@@ -1,6 +1,7 @@
1
1
  import { Block, CustomConfig, PCShareSocialNetwork, TextSize } from '../models';
2
2
  import { ConstructorBlock } from '../models/constructor';
3
3
  export declare function getHeaderTag(size: TextSize): "h1" | "h2" | "h4" | "h5";
4
+ export declare function hasBlockTag(content: string): boolean;
4
5
  export declare function getBlockKey(block: ConstructorBlock, index: number): string;
5
6
  export declare const getCustomBlockTypes: ({ blocks, headers }?: CustomConfig) => string[];
6
7
  export declare const getCustomItems: ({ blocks, headers, subBlocks }?: CustomConfig) => {
@@ -1,7 +1,43 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getShareLink = exports.getHeaderBlock = exports.getOrderedBlocks = exports.getCustomHeaderTypes = exports.getCustomSubBlockTypes = exports.getCustomItems = exports.getCustomBlockTypes = exports.getBlockKey = exports.getHeaderTag = void 0;
3
+ exports.getShareLink = exports.getHeaderBlock = exports.getOrderedBlocks = exports.getCustomHeaderTypes = exports.getCustomSubBlockTypes = exports.getCustomItems = exports.getCustomBlockTypes = exports.getBlockKey = exports.hasBlockTag = exports.getHeaderTag = void 0;
4
4
  const models_1 = require("../models");
5
+ const BLOCK_ELEMENTS = [
6
+ 'div',
7
+ 'p',
8
+ 'h[1-6]',
9
+ 'address',
10
+ 'article',
11
+ 'aside',
12
+ 'blockquote',
13
+ 'canvas',
14
+ 'dd',
15
+ 'dl',
16
+ 'dt',
17
+ 'fieldset',
18
+ 'figcaption',
19
+ 'footer',
20
+ 'header',
21
+ 'hr',
22
+ 'main',
23
+ 'nav',
24
+ 'section',
25
+ 'video',
26
+ 'pre',
27
+ 'figure',
28
+ 'form',
29
+ 'pre',
30
+ 'ol',
31
+ 'ul',
32
+ 'li',
33
+ 'table',
34
+ 'thead',
35
+ 'tbody',
36
+ 'tfoot',
37
+ 'tr',
38
+ 'th',
39
+ 'td',
40
+ ];
5
41
  function getHeaderTag(size) {
6
42
  switch (size) {
7
43
  case 'l':
@@ -16,6 +52,12 @@ function getHeaderTag(size) {
16
52
  }
17
53
  }
18
54
  exports.getHeaderTag = getHeaderTag;
55
+ function hasBlockTag(content) {
56
+ const blockElementRegex = `/<(${BLOCK_ELEMENTS.join('|')})\b[^>]*>/gi`;
57
+ const blockTags = content.match(blockElementRegex);
58
+ return Boolean(blockTags);
59
+ }
60
+ exports.hasBlockTag = hasBlockTag;
19
61
  function getBlockKey(block, index) {
20
62
  return `${block.type}-${index}`;
21
63
  }