@gravity-ui/page-constructor 3.8.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
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
+
3
10
  ## [3.8.0](https://github.com/gravity-ui/page-constructor/compare/v3.7.0...v3.8.0) (2023-06-22)
4
11
 
5
12
 
@@ -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,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;
@@ -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.8.0",
3
+ "version": "3.8.1",
4
4
  "description": "Gravity UI Page Constructor",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -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
  }