@gravity-ui/page-constructor 5.27.0 → 5.27.1-alpha.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 (88) hide show
  1. package/README.md +54 -0
  2. package/build/cjs/blocks/HeaderSlider/HeaderSlider.css +0 -10
  3. package/build/cjs/blocks/HeaderSlider/HeaderSlider.js +2 -2
  4. package/build/cjs/blocks/Slider/Slider.css +2 -0
  5. package/build/cjs/blocks/Slider/Slider.d.ts +1 -0
  6. package/build/cjs/blocks/Slider/Slider.js +85 -31
  7. package/build/cjs/blocks/Slider/i18n/en.json +3 -1
  8. package/build/cjs/blocks/Slider/i18n/index.d.ts +1 -1
  9. package/build/cjs/blocks/Slider/i18n/ru.json +3 -1
  10. package/build/cjs/blocks/Slider/utils.d.ts +10 -0
  11. package/build/cjs/blocks/Slider/utils.js +85 -1
  12. package/build/cjs/blocks/SliderNew/Arrow/Arrow.css +23 -19
  13. package/build/cjs/blocks/SliderNew/Arrow/Arrow.d.ts +4 -1
  14. package/build/cjs/blocks/SliderNew/Arrow/Arrow.js +5 -4
  15. package/build/cjs/blocks/SliderNew/Slider.css +104 -15
  16. package/build/cjs/blocks/SliderNew/Slider.d.ts +2 -1
  17. package/build/cjs/blocks/SliderNew/Slider.js +22 -9
  18. package/build/cjs/blocks/SliderNew/i18n/en.json +3 -1
  19. package/build/cjs/blocks/SliderNew/i18n/index.d.ts +1 -1
  20. package/build/cjs/blocks/SliderNew/i18n/ru.json +3 -1
  21. package/build/cjs/blocks/SliderNew/useSlider.d.ts +8 -6
  22. package/build/cjs/blocks/SliderNew/useSlider.js +4 -2
  23. package/build/cjs/blocks/SliderNew/useSliderPagination.d.ts +9 -0
  24. package/build/cjs/blocks/SliderNew/useSliderPagination.js +36 -0
  25. package/build/cjs/blocks/SliderNew/utils.d.ts +2 -0
  26. package/build/cjs/blocks/SliderNew/utils.js +13 -1
  27. package/build/cjs/components/FullscreenImage/FullscreenImage.css +91 -16
  28. package/build/cjs/components/FullscreenImage/FullscreenImage.d.ts +5 -0
  29. package/build/cjs/components/FullscreenImage/FullscreenImage.js +19 -5
  30. package/build/cjs/components/Media/Image/Image.d.ts +1 -0
  31. package/build/cjs/components/Media/Image/Image.js +7 -5
  32. package/build/cjs/components/Media/Media.css +4 -0
  33. package/build/cjs/components/Media/Media.d.ts +1 -0
  34. package/build/cjs/components/Media/Media.js +3 -2
  35. package/build/cjs/components/MediaBase/MediaBase.js +1 -1
  36. package/build/cjs/components/ReactPlayer/ReactPlayer.js +1 -1
  37. package/build/cjs/constructor-items.d.ts +1 -1
  38. package/build/cjs/models/constructor-items/blocks.d.ts +2 -1
  39. package/build/cjs/models/constructor-items/blocks.js +1 -0
  40. package/build/cjs/navigation/hooks/useShowBorder.js +3 -2
  41. package/build/cjs/sub-blocks/Content/Content.css +14 -4
  42. package/build/cjs/sub-blocks/ImageCard/ImageCard.css +8 -0
  43. package/build/esm/blocks/HeaderSlider/HeaderSlider.css +0 -10
  44. package/build/esm/blocks/HeaderSlider/HeaderSlider.js +1 -1
  45. package/build/esm/blocks/Slider/Slider.css +2 -0
  46. package/build/esm/blocks/Slider/Slider.d.ts +1 -0
  47. package/build/esm/blocks/Slider/Slider.js +86 -32
  48. package/build/esm/blocks/Slider/i18n/en.json +3 -1
  49. package/build/esm/blocks/Slider/i18n/index.d.ts +1 -1
  50. package/build/esm/blocks/Slider/i18n/ru.json +3 -1
  51. package/build/esm/blocks/Slider/utils.d.ts +10 -0
  52. package/build/esm/blocks/Slider/utils.js +82 -0
  53. package/build/esm/blocks/SliderNew/Arrow/Arrow.css +23 -19
  54. package/build/esm/blocks/SliderNew/Arrow/Arrow.d.ts +4 -1
  55. package/build/esm/blocks/SliderNew/Arrow/Arrow.js +5 -4
  56. package/build/esm/blocks/SliderNew/Slider.css +104 -15
  57. package/build/esm/blocks/SliderNew/Slider.d.ts +2 -1
  58. package/build/esm/blocks/SliderNew/Slider.js +22 -9
  59. package/build/esm/blocks/SliderNew/i18n/en.json +3 -1
  60. package/build/esm/blocks/SliderNew/i18n/index.d.ts +1 -1
  61. package/build/esm/blocks/SliderNew/i18n/ru.json +3 -1
  62. package/build/esm/blocks/SliderNew/useSlider.d.ts +8 -6
  63. package/build/esm/blocks/SliderNew/useSlider.js +6 -3
  64. package/build/esm/blocks/SliderNew/useSliderPagination.d.ts +9 -0
  65. package/build/esm/blocks/SliderNew/useSliderPagination.js +32 -0
  66. package/build/esm/blocks/SliderNew/utils.d.ts +2 -0
  67. package/build/esm/blocks/SliderNew/utils.js +10 -0
  68. package/build/esm/components/FullscreenImage/FullscreenImage.css +91 -16
  69. package/build/esm/components/FullscreenImage/FullscreenImage.d.ts +5 -0
  70. package/build/esm/components/FullscreenImage/FullscreenImage.js +20 -6
  71. package/build/esm/components/Media/Image/Image.d.ts +1 -0
  72. package/build/esm/components/Media/Image/Image.js +7 -5
  73. package/build/esm/components/Media/Media.css +4 -0
  74. package/build/esm/components/Media/Media.d.ts +1 -0
  75. package/build/esm/components/Media/Media.js +3 -2
  76. package/build/esm/components/MediaBase/MediaBase.js +1 -1
  77. package/build/esm/components/ReactPlayer/ReactPlayer.js +1 -1
  78. package/build/esm/constructor-items.d.ts +1 -1
  79. package/build/esm/models/constructor-items/blocks.d.ts +2 -1
  80. package/build/esm/models/constructor-items/blocks.js +1 -0
  81. package/build/esm/navigation/hooks/useShowBorder.js +1 -1
  82. package/build/esm/sub-blocks/Content/Content.css +14 -4
  83. package/build/esm/sub-blocks/ImageCard/ImageCard.css +8 -0
  84. package/package.json +2 -1
  85. package/server/models/constructor-items/blocks.d.ts +2 -1
  86. package/server/models/constructor-items/blocks.js +1 -0
  87. package/styles/mixins.scss +1 -1
  88. package/widget/index.js +1 -1
@@ -5,7 +5,7 @@ const tslib_1 = require("tslib");
5
5
  const react_1 = tslib_1.__importStar(require("react"));
6
6
  const web_1 = require("@react-spring/web");
7
7
  const debounce_1 = tslib_1.__importDefault(require("lodash/debounce"));
8
- const Slider_1 = tslib_1.__importDefault(require("../../../blocks/Slider/Slider"));
8
+ const unstable_1 = require("../../../blocks/unstable");
9
9
  const models_1 = require("../../../models");
10
10
  const utils_1 = require("../../../utils");
11
11
  const BackgroundImage_1 = tslib_1.__importDefault(require("../../BackgroundImage/BackgroundImage"));
@@ -15,7 +15,7 @@ const utils_2 = require("./utils");
15
15
  const b = (0, utils_1.block)('media-component-image');
16
16
  exports.defaultAnimatedDivQa = 'animated-div';
17
17
  const Image = (props) => {
18
- const { parallax, height, imageClassName, isBackground, hasVideoFallback, video, fullscreen, disableImageSliderForArrayInput, qa, onLoad, } = props;
18
+ const { parallax, height, imageClassName, fullscreenClassName, isBackground, hasVideoFallback, video, fullscreen, disableImageSliderForArrayInput, qa, onLoad, } = props;
19
19
  const image = Array.isArray(props.image) && disableImageSliderForArrayInput
20
20
  ? props.image[0]
21
21
  : props.image;
@@ -41,9 +41,9 @@ const Image = (props) => {
41
41
  parallaxInterpolate = springScrollY.to((value) => `translateY(-${Number(value) / parallaxLevel}px)`);
42
42
  }
43
43
  const imageClass = b('item', { withVideo: Boolean(video) && !hasVideoFallback }, imageClassName);
44
- const renderFullscreenImage = (item) => {
44
+ const renderFullscreenImage = (item, sliderData) => {
45
45
  const itemData = (0, utils_2.getMediaImage)(item);
46
- return (react_1.default.createElement(FullscreenImage_1.default, Object.assign({ key: itemData.alt }, itemData, { imageClassName: imageClass, imageStyle: { height }, qa: qaAttributes.fullscreenImage })));
46
+ return (react_1.default.createElement(FullscreenImage_1.default, Object.assign({ key: itemData.alt }, itemData, { imageClassName: imageClass, modalImageClass: fullscreenClassName, imageStyle: { height }, qa: qaAttributes.fullscreenImage, sliderData: sliderData })));
47
47
  };
48
48
  const imageBackground = (oneImage) => {
49
49
  const imageData = (0, utils_2.getMediaImage)(oneImage);
@@ -56,7 +56,9 @@ const Image = (props) => {
56
56
  };
57
57
  const imageSlider = (imageArray) => {
58
58
  const fullscreenItem = fullscreen === undefined || fullscreen;
59
- return (react_1.default.createElement(Slider_1.default, { slidesToShow: 1, type: models_1.SliderType.MediaCard }, imageArray.map((item, index) => (react_1.default.createElement(react_1.Fragment, { key: index }, fullscreenItem ? renderFullscreenImage(item) : imageOnly(item))))));
59
+ return (react_1.default.createElement(unstable_1.SliderNewBlock, { slidesToShow: 1, type: models_1.SliderType.MediaCard }, imageArray.map((item, index) => (react_1.default.createElement(react_1.Fragment, { key: index }, fullscreenItem
60
+ ? renderFullscreenImage(item, { items: imageArray, initialIndex: index })
61
+ : imageOnly(item))))));
60
62
  };
61
63
  if (Array.isArray(image)) {
62
64
  return imageSlider(image);
@@ -8,4 +8,8 @@ unpredictable css rules order in build */
8
8
  overflow: hidden;
9
9
  display: flex;
10
10
  align-items: center;
11
+ }
12
+ .pc-Media__fullscreen-image-cover {
13
+ object-fit: cover;
14
+ object-position: top;
11
15
  }
@@ -3,6 +3,7 @@ import { ImageAdditionProps } from './Image/Image';
3
3
  import { VideoAdditionProps } from './Video/Video';
4
4
  export interface MediaAllProps extends MediaProps, VideoAdditionProps, ImageAdditionProps, QAProps {
5
5
  className?: string;
6
+ isFullscreenImageCover?: boolean;
6
7
  youtubeClassName?: string;
7
8
  autoplay?: boolean;
8
9
  onImageLoad?: () => void;
@@ -12,13 +12,13 @@ const Image_1 = tslib_1.__importDefault(require("./Image/Image"));
12
12
  const Video_1 = tslib_1.__importDefault(require("./Video/Video"));
13
13
  const b = (0, utils_1.block)('Media');
14
14
  const Media = (props) => {
15
- const { image, video, youtube, videoIframe, dataLens, color, height, previewImg, parallax = false, fullscreen, analyticsEvents, className, imageClassName, videoClassName, youtubeClassName, disableImageSliderForArrayInput, playVideo = true, isBackground, playButton, customBarControlsClassName, qa, ratio, autoplay, onImageLoad, iframe, margins, } = props;
15
+ const { image, video, youtube, videoIframe, dataLens, color, height, previewImg, parallax = false, fullscreen, isFullscreenImageCover, analyticsEvents, className, imageClassName, videoClassName, youtubeClassName, disableImageSliderForArrayInput, playVideo = true, isBackground, playButton, customBarControlsClassName, qa, ratio, autoplay, onImageLoad, iframe, margins, } = props;
16
16
  const [hasVideoFallback, setHasVideoFallback] = (0, react_1.useState)(false);
17
17
  const qaAttributes = (0, utils_1.getQaAttrubutes)(qa, 'video');
18
18
  const content = (0, react_1.useMemo)(() => {
19
19
  let result = [];
20
20
  if (image) {
21
- result.push(react_1.default.createElement(Image_1.default, { key: "image", parallax: parallax, image: image, disableImageSliderForArrayInput: disableImageSliderForArrayInput, height: height, imageClassName: imageClassName, isBackground: isBackground, video: video, hasVideoFallback: hasVideoFallback, fullscreen: fullscreen, qa: qaAttributes.image, onLoad: onImageLoad }));
21
+ result.push(react_1.default.createElement(Image_1.default, { key: "image", parallax: parallax, image: image, disableImageSliderForArrayInput: disableImageSliderForArrayInput, height: height, imageClassName: imageClassName, fullscreenClassName: isFullscreenImageCover ? b('fullscreen-image-cover') : undefined, isBackground: isBackground, video: video, hasVideoFallback: hasVideoFallback, fullscreen: fullscreen, qa: qaAttributes.image, onLoad: onImageLoad }));
22
22
  }
23
23
  if (video) {
24
24
  const videoProps = {
@@ -66,6 +66,7 @@ const Media = (props) => {
66
66
  isBackground,
67
67
  hasVideoFallback,
68
68
  fullscreen,
69
+ isFullscreenImageCover,
69
70
  qaAttributes.image,
70
71
  qaAttributes.video,
71
72
  onImageLoad,
@@ -32,7 +32,7 @@ const MediaBase = (props) => {
32
32
  }) },
33
33
  react_1.default.createElement(grid_1.Col, { className: b('content'), sizes: contentSizes }, mediaContent),
34
34
  card ? (react_1.default.createElement(grid_1.Col, { sizes: mediaSizes },
35
- react_1.default.createElement("div", null, card))) : null))));
35
+ react_1.default.createElement("div", { className: b('card') }, card))) : null))));
36
36
  };
37
37
  exports.MediaBase = MediaBase;
38
38
  exports.MediaBase.Card = Card;
@@ -24,7 +24,7 @@ const FPS = 60;
24
24
  // eslint-disable-next-line react/display-name
25
25
  exports.ReactPlayerBlock = react_1.default.forwardRef((props, originRef) => {
26
26
  const isMobile = (0, react_1.useContext)(mobileContext_1.MobileContext);
27
- const { src, previewImgUrl, loop = false, controls = models_1.MediaVideoControlsType.Default, customControlsOptions = {}, muted: initiallyMuted = false, elapsedTime, playButton, className, customBarControlsClassName, showPreview, onClickPreview, analyticsEvents, height, ariaLabel, ratio, autoRatio, contain, } = props;
27
+ const { src, previewImgUrl, loop = false, controls = models_1.MediaVideoControlsType.Default, customControlsOptions = {}, muted: initiallyMuted = false, elapsedTime, playButton, className, customBarControlsClassName, showPreview, onClickPreview, analyticsEvents, height, ariaLabel, ratio, autoRatio, contain = true, } = props;
28
28
  const { type = models_1.PlayButtonType.Default, theme = models_1.PlayButtonThemes.Blue, text, className: buttonClassName, } = playButton || {};
29
29
  const { type: customControlsType = models_1.CustomControlsType.WithMuteButton, muteButtonShown, positioning = models_1.CustomControlsButtonPositioning.Center, } = customControlsOptions;
30
30
  const autoPlay = Boolean(!isMobile && !previewImgUrl && props.autoplay);
@@ -18,7 +18,7 @@ export declare const blockMap: {
18
18
  "map-block": ({ map, border, disableShadow, ...props }: import("./models").MapBlockProps) => JSX.Element;
19
19
  "filter-block": import("react").FC<import("./models").FilterBlockProps>;
20
20
  "form-block": import("react").FC<import("./models").FormBlockProps>;
21
- "slider-new-block": ({ animated, title, description, type, anchorId, arrows, adaptive, autoplay: autoplayMs, dots, className, dotsClassName, disclaimer, children, blockClassName, arrowSize, slidesToShow, onSlideChange, onSlideChangeTransitionStart, onSlideChangeTransitionEnd, onActiveIndexChange, onBreakpoint, }: import("react").PropsWithChildren<import("./blocks/SliderNew/Slider").SliderNewProps>) => JSX.Element;
21
+ "slider-new-block": ({ animated, title, description, type, anchorId, arrows, adaptive, autoplay: autoplayMs, dots, initialSlide, className, dotsClassName, disclaimer, children, blockClassName, arrowSize, slidesToShow, onSlideChange, onSlideChangeTransitionStart, onSlideChangeTransitionEnd, onActiveIndexChange, onBreakpoint, }: import("react").PropsWithChildren<import("./blocks/SliderNew/Slider").SliderNewProps>) => JSX.Element;
22
22
  };
23
23
  export declare const subBlockMap: {
24
24
  divider: ({ size, border }: import("./models").DividerProps) => JSX.Element;
@@ -62,7 +62,8 @@ export declare enum SliderBreakpointNames {
62
62
  }
63
63
  export declare enum SliderType {
64
64
  MediaCard = "media-card",
65
- HeaderCard = "header-card"
65
+ HeaderCard = "header-card",
66
+ FullscreenCard = "fullscreen-card"
66
67
  }
67
68
  export type SliderBreakpointParams = Record<SliderBreakpointNames, number>;
68
69
  export type SlidesToShow = Partial<SliderBreakpointParams> | number;
@@ -38,6 +38,7 @@ var SliderType;
38
38
  (function (SliderType) {
39
39
  SliderType["MediaCard"] = "media-card";
40
40
  SliderType["HeaderCard"] = "header-card";
41
+ SliderType["FullscreenCard"] = "fullscreen-card";
41
42
  })(SliderType = exports.SliderType || (exports.SliderType = {}));
42
43
  var PCShareSocialNetwork;
43
44
  (function (PCShareSocialNetwork) {
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
3
4
  const react_1 = require("react");
4
- const lodash_1 = require("lodash");
5
+ const debounce_1 = tslib_1.__importDefault(require("lodash/debounce"));
5
6
  const useShowBorder = (withBorder, withBorderOnScroll) => {
6
7
  const [showBorder, setShowBorder] = (0, react_1.useState)(withBorder);
7
8
  (0, react_1.useEffect)(() => {
@@ -12,7 +13,7 @@ const useShowBorder = (withBorder, withBorderOnScroll) => {
12
13
  setShowBorder(window.scrollY > 0);
13
14
  }
14
15
  };
15
- const scrollHandler = (0, lodash_1.debounce)(showBorderOnScroll, 20);
16
+ const scrollHandler = (0, debounce_1.default)(showBorderOnScroll, 20);
16
17
  window.addEventListener('scroll', scrollHandler, { passive: true });
17
18
  return () => window.removeEventListener('scroll', scrollHandler);
18
19
  });
@@ -87,6 +87,9 @@ unpredictable css rules order in build */
87
87
  .pc-content_theme_dark .pc-content__title *,
88
88
  .pc-content_theme_dark .pc-content__text .yfm,
89
89
  .pc-content_theme_dark .pc-content__text .yfm *,
90
+ .pc-content_theme_dark .pc-content__list *,
91
+ .pc-content_theme_dark .pc-content__list .yfm,
92
+ .pc-content_theme_dark .pc-content__list .yfm *,
90
93
  .pc-content_theme_dark .pc-content__links a {
91
94
  color: var(--g-color-text-light-primary);
92
95
  }
@@ -101,11 +104,13 @@ unpredictable css rules order in build */
101
104
  .pc-content_theme_dark .pc-content__notice .yfm a:hover {
102
105
  color: var(--g-color-text-light-primary);
103
106
  }
104
- .pc-content_theme_dark .pc-content__text .yfm a {
107
+ .pc-content_theme_dark .pc-content__text .yfm a,
108
+ .pc-content_theme_dark .pc-content__list .yfm a {
105
109
  color: var(--g-color-text-light-primary);
106
110
  text-decoration: underline;
107
111
  }
108
- .pc-content_theme_dark .pc-content__text .yfm a:hover {
112
+ .pc-content_theme_dark .pc-content__text .yfm a:hover,
113
+ .pc-content_theme_dark .pc-content__list .yfm a:hover {
109
114
  color: var(--g-color-text-light-secondary);
110
115
  }
111
116
  .pc-content_theme_dark .pc-content__title a {
@@ -119,6 +124,9 @@ unpredictable css rules order in build */
119
124
  .pc-content_theme_light .pc-content__title *,
120
125
  .pc-content_theme_light .pc-content__text .yfm,
121
126
  .pc-content_theme_light .pc-content__text .yfm *,
127
+ .pc-content_theme_light .pc-content__list *,
128
+ .pc-content_theme_light .pc-content__list .yfm,
129
+ .pc-content_theme_light .pc-content__list .yfm *,
122
130
  .pc-content_theme_light .pc-content__links a {
123
131
  color: var(--g-color-text-dark-primary);
124
132
  }
@@ -133,11 +141,13 @@ unpredictable css rules order in build */
133
141
  .pc-content_theme_light .pc-content__notice .yfm a:hover {
134
142
  color: var(--g-color-text-dark-primary);
135
143
  }
136
- .pc-content_theme_light .pc-content__text .yfm a {
144
+ .pc-content_theme_light .pc-content__text .yfm a,
145
+ .pc-content_theme_light .pc-content__list .yfm a {
137
146
  color: var(--g-color-text-dark-primary);
138
147
  text-decoration: underline;
139
148
  }
140
- .pc-content_theme_light .pc-content__text .yfm a:hover {
149
+ .pc-content_theme_light .pc-content__text .yfm a:hover,
150
+ .pc-content_theme_light .pc-content__list .yfm a:hover {
141
151
  color: var(--g-color-text-dark-secondary);
142
152
  }
143
153
  .pc-content_theme_light .pc-content__title a {
@@ -16,6 +16,14 @@ unpredictable css rules order in build */
16
16
  .pc-image-card_border_line {
17
17
  border: 1px solid var(--g-color-line-generic);
18
18
  }
19
+ @media (min-width: 577px) {
20
+ .slick-slide:not(.slick-active) .pc-image-card {
21
+ box-shadow: none;
22
+ }
23
+ .swiper-slide:not(.swiper-slide-visible) .pc-image-card {
24
+ box-shadow: none;
25
+ }
26
+ }
19
27
  .pc-image-card .pc-image-card__content {
20
28
  display: flex;
21
29
  padding: 32px;
@@ -34,13 +34,6 @@ unpredictable css rules order in build */
34
34
  padding-right: 32px;
35
35
  }
36
36
  }
37
- @media (max-width: 769px) {
38
- .pc-header-slider-block.pc-SliderBlock {
39
- margin-left: -8px;
40
- padding-left: 0;
41
- width: calc(100% + 8px);
42
- }
43
- }
44
37
  @media (max-width: 577px) {
45
38
  .pc-header-slider-block__item-content .pc-header-block__content {
46
39
  padding-left: 0;
@@ -49,7 +42,4 @@ unpredictable css rules order in build */
49
42
  .pc-header-slider-block__item-content .pc-header-block__container-fluid {
50
43
  padding-left: 24px;
51
44
  }
52
- .pc-header-slider-block .slick-track .slick-slide {
53
- max-width: 100%;
54
- }
55
45
  }
@@ -4,7 +4,7 @@ import { MobileContext } from '../../context/mobileContext';
4
4
  import { SliderType } from '../../models';
5
5
  import { block } from '../../utils';
6
6
  import Header from '../Header/Header';
7
- import { SliderBlock } from '../index';
7
+ import { SliderNewBlock as SliderBlock } from '../unstable';
8
8
  import './HeaderSlider.css';
9
9
  const b = block('header-slider-block');
10
10
  export const HeaderSliderBlock = (_a) => {
@@ -105,6 +105,8 @@ unpredictable css rules order in build */
105
105
  height: auto;
106
106
  }
107
107
  .pc-SliderBlock .slick-track .slick-slide > div {
108
+ display: flex;
109
+ width: 100%;
108
110
  height: 100%;
109
111
  }
110
112
  .pc-SliderBlock .slick-arrow {
@@ -10,6 +10,7 @@ export interface SliderProps extends Omit<SliderParams, 'children'>, Refable<HTM
10
10
  dotsClassName?: string;
11
11
  blockClassName?: string;
12
12
  arrowSize?: number;
13
+ initialIndex?: number;
13
14
  }
14
15
  export declare const SliderBlock: (props: React.PropsWithChildren<SliderProps>) => JSX.Element;
15
16
  export default SliderBlock;
@@ -1,4 +1,5 @@
1
1
  import React, { Fragment, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
2
+ import { useUniqId } from '@gravity-ui/uikit';
2
3
  import debounce from 'lodash/debounce';
3
4
  import get from 'lodash/get';
4
5
  import noop from 'lodash/noop';
@@ -15,7 +16,8 @@ import useFocus from '../../hooks/useFocus';
15
16
  import { SliderType, } from '../../models';
16
17
  import { block } from '../../utils';
17
18
  import Arrow from './Arrow/Arrow';
18
- import { getSliderResponsiveParams, getSlidesCountByBreakpoint, getSlidesToShowCount, getSlidesToShowWithDefaults, } from './utils';
19
+ import { i18n } from './i18n';
20
+ import { getSliderResponsiveParams, getSlidesCountByBreakpoint, getSlidesToShowCount, getSlidesToShowWithDefaults, isFocusable, useRovingTabIndex, } from './utils';
19
21
  import './Slider.css';
20
22
  const b = block('SliderBlock');
21
23
  const slick = block('slick-origin');
@@ -23,12 +25,15 @@ const DOT_WIDTH = 8;
23
25
  const DOT_GAP = 16;
24
26
  export const SliderBlock = (props) => {
25
27
  var _a;
26
- const { animated, title, description, type, anchorId, arrows = true, adaptive, autoplay = undefined, dots = true, dotsClassName, disclaimer, children, className, blockClassName, lazyLoad, arrowSize, onAfterChange: handleAfterChange, onBeforeChange: handleBeforeChange, } = props;
28
+ const { animated, title, description, type, anchorId, arrows = true, adaptive, autoplay: autoplaySpeed, dots = true, dotsClassName, disclaimer, children, className, blockClassName, lazyLoad, arrowSize, onAfterChange: handleAfterChange, onBeforeChange: handleBeforeChange, initialIndex = 0, } = props;
27
29
  const { isServer } = useContext(SSRContext);
28
30
  const isMobile = useContext(MobileContext);
29
31
  const [breakpoint, setBreakpoint] = useState(BREAKPOINTS.xl);
30
- const disclosedChildren = useMemo(() => discloseAllNestedChildren(children), [children]);
32
+ const sliderId = useUniqId();
33
+ const disclosedChildren = useMemo(() => discloseAllNestedChildren(children, sliderId), [children, sliderId]);
31
34
  const childrenCount = disclosedChildren.length;
35
+ const isAutoplayEnabled = autoplaySpeed !== undefined && autoplaySpeed > 0;
36
+ const isUserInteractionRef = useRef(false);
32
37
  const [slidesToShow] = useState(getSlidesToShowWithDefaults({
33
38
  contentLength: childrenCount,
34
39
  breakpoints: props.slidesToShow,
@@ -36,11 +41,16 @@ export const SliderBlock = (props) => {
36
41
  }));
37
42
  const slidesToShowCount = getSlidesToShowCount(slidesToShow);
38
43
  const slidesCountByBreakpoint = getSlidesCountByBreakpoint(breakpoint, slidesToShow);
39
- const [currentIndex, setCurrentIndex] = useState(0);
44
+ const [currentIndex, setCurrentIndex] = useState(initialIndex);
40
45
  const [childStyles, setChildStyles] = useState({});
41
46
  const [slider, setSlider] = useState();
47
+ const prevIndexRef = useRef(0);
42
48
  const autoplayTimeId = useRef();
43
49
  const { hasFocus, unsetFocus } = useFocus((_a = slider === null || slider === void 0 ? void 0 : slider.innerSlider) === null || _a === void 0 ? void 0 : _a.list);
50
+ const asUserInteraction = (fn) => (...args) => {
51
+ isUserInteractionRef.current = true;
52
+ return fn(...args);
53
+ };
44
54
  // eslint-disable-next-line react-hooks/exhaustive-deps
45
55
  const onResize = useCallback(debounce(() => {
46
56
  if (!slider) {
@@ -55,7 +65,7 @@ export const SliderBlock = (props) => {
55
65
  }, 100), [slider, breakpoint]);
56
66
  const scrollLastSlide = useCallback((current) => {
57
67
  const lastSlide = childrenCount - slidesToShowCount;
58
- if (autoplay && lastSlide === current) {
68
+ if (isAutoplayEnabled && lastSlide === current) {
59
69
  // Slick doesn't support autoplay with no infinity scroll
60
70
  autoplayTimeId.current = setTimeout(() => {
61
71
  if (slider) {
@@ -67,9 +77,9 @@ export const SliderBlock = (props) => {
67
77
  slider.slickPlay();
68
78
  }
69
79
  }, 500);
70
- }, autoplay);
80
+ }, autoplaySpeed);
71
81
  }
72
- }, [autoplay, childrenCount, slider, slidesToShowCount]);
82
+ }, [autoplaySpeed, childrenCount, isAutoplayEnabled, slider, slidesToShowCount]);
73
83
  useEffect(() => {
74
84
  if (hasFocus && autoplayTimeId.current) {
75
85
  clearTimeout(autoplayTimeId.current);
@@ -83,7 +93,7 @@ export const SliderBlock = (props) => {
83
93
  window.addEventListener('resize', onResize, { passive: true });
84
94
  return () => window.removeEventListener('resize', onResize);
85
95
  }, [onResize]);
86
- const handleArrowClick = useCallback((direction) => {
96
+ const handleArrowClick = (direction) => {
87
97
  let nextIndex;
88
98
  if (direction === 'right') {
89
99
  nextIndex =
@@ -96,11 +106,12 @@ export const SliderBlock = (props) => {
96
106
  if (slider) {
97
107
  slider.slickGoTo(nextIndex);
98
108
  }
99
- }, [childrenCount, currentIndex, slider, slidesCountByBreakpoint]);
109
+ };
100
110
  const onBeforeChange = useCallback((current, next) => {
101
111
  if (handleBeforeChange) {
102
112
  handleBeforeChange(current, next);
103
113
  }
114
+ prevIndexRef.current = current;
104
115
  setCurrentIndex(Math.ceil(next));
105
116
  }, [handleBeforeChange]);
106
117
  const onAfterChange = useCallback((current) => {
@@ -113,16 +124,33 @@ export const SliderBlock = (props) => {
113
124
  if (!hasFocus) {
114
125
  scrollLastSlide(current);
115
126
  }
116
- }, [handleAfterChange, hasFocus, scrollLastSlide]);
117
- const handleDotClick = useCallback((index) => {
127
+ if (isUserInteractionRef.current) {
128
+ const focusIndex = prevIndexRef.current >= current
129
+ ? current
130
+ : Math.max(current, prevIndexRef.current + slidesCountByBreakpoint);
131
+ const firstNewSlide = document.getElementById(getSlideId(sliderId, focusIndex));
132
+ if (firstNewSlide) {
133
+ const focusableChild = Array.from(firstNewSlide.querySelectorAll('*')).find(isFocusable);
134
+ focusableChild === null || focusableChild === void 0 ? void 0 : focusableChild.focus();
135
+ }
136
+ }
137
+ isUserInteractionRef.current = false;
138
+ }, [handleAfterChange, hasFocus, scrollLastSlide, sliderId, slidesCountByBreakpoint]);
139
+ const handleDotClick = (index) => {
118
140
  const nextIndex = index > currentIndex ? index + 1 - slidesCountByBreakpoint : index;
119
141
  if (slider) {
120
142
  slider.slickGoTo(nextIndex);
121
143
  }
122
- }, [slider, currentIndex, slidesCountByBreakpoint]);
123
- const barSlidesCount = childrenCount - slidesToShowCount + 1;
144
+ };
145
+ const barSlidesCount = childrenCount - slidesCountByBreakpoint + 1;
124
146
  const barPosition = (DOT_GAP + DOT_WIDTH) * currentIndex;
125
147
  const barWidth = DOT_WIDTH + (DOT_GAP + DOT_WIDTH) * (slidesCountByBreakpoint - 1);
148
+ const { getRovingItemProps, rovingListProps } = useRovingTabIndex({
149
+ itemCount: barSlidesCount,
150
+ activeIndex: currentIndex + 1,
151
+ firstIndex: 1,
152
+ uniqId: sliderId,
153
+ });
126
154
  const renderBar = () => {
127
155
  return (slidesCountByBreakpoint > 1 && (React.createElement("li", { className: b('bar'), style: {
128
156
  left: barPosition,
@@ -133,19 +161,22 @@ export const SliderBlock = (props) => {
133
161
  const renderAccessibleBar = (index) => {
134
162
  return (
135
163
  // To have this key differ from keys used in renderDot function, added `-accessible-bar` part
136
- React.createElement(Fragment, { key: `${index}-accessible-bar` }, slidesCountByBreakpoint > 0 && (React.createElement("li", { className: b('accessible-bar'), "aria-current": true, "aria-label": `Slide ${currentIndex + 1} of ${barSlidesCount}`, style: {
164
+ React.createElement(Fragment, { key: `${index}-accessible-bar` }, slidesCountByBreakpoint > 0 && (React.createElement("li", Object.assign({ className: b('accessible-bar'), role: "menuitemradio", "aria-checked": true, "aria-label": i18n('dot-label', {
165
+ index: currentIndex + 1,
166
+ count: barSlidesCount,
167
+ }), style: {
137
168
  left: barPosition,
138
169
  width: barWidth,
139
- } }))));
170
+ } }, getRovingItemProps(currentIndex + 1))))));
140
171
  };
141
172
  const getCurrentSlideNumber = (index) => {
142
173
  const currentIndexDiff = index - currentIndex;
143
174
  let currentSlideNumber;
144
- if (0 <= currentIndexDiff && currentIndexDiff < slidesToShowCount) {
175
+ if (0 <= currentIndexDiff && currentIndexDiff < slidesCountByBreakpoint) {
145
176
  currentSlideNumber = currentIndex + 1;
146
177
  }
147
- else if (currentIndexDiff >= slidesToShowCount) {
148
- currentSlideNumber = index - slidesToShowCount + 2;
178
+ else if (currentIndexDiff >= slidesCountByBreakpoint) {
179
+ currentSlideNumber = index - slidesCountByBreakpoint + 2;
149
180
  }
150
181
  else {
151
182
  currentSlideNumber = index + 1;
@@ -154,12 +185,24 @@ export const SliderBlock = (props) => {
154
185
  };
155
186
  const isVisibleSlide = (index) => {
156
187
  const currentIndexDiff = index - currentIndex;
157
- return (slidesCountByBreakpoint > 0 &&
188
+ const result = slidesCountByBreakpoint > 0 &&
158
189
  0 <= currentIndexDiff &&
159
- currentIndexDiff < slidesToShowCount);
190
+ currentIndexDiff < slidesCountByBreakpoint;
191
+ return result;
160
192
  };
161
193
  const renderDot = (index) => {
162
- return (React.createElement("li", { key: index, className: b('dot', { active: index === currentIndex }), onClick: () => handleDotClick(index), "aria-hidden": isVisibleSlide(index) ? true : undefined, "aria-label": `Slide ${getCurrentSlideNumber(index)} of ${barSlidesCount}` }));
194
+ const isVisible = isVisibleSlide(index);
195
+ const currentSlideNumber = getCurrentSlideNumber(index);
196
+ const rovingItemProps = isVisible ? undefined : getRovingItemProps(currentSlideNumber);
197
+ return (React.createElement("li", Object.assign({ key: index, className: b('dot', { active: index === currentIndex }), onClick: asUserInteraction(() => handleDotClick(index)), onKeyDown: (e) => {
198
+ const key = e.key.toLowerCase();
199
+ if (key === 'space' || key === 'enter') {
200
+ e.currentTarget.click();
201
+ }
202
+ }, role: "menuitemradio", "aria-checked": false, tabIndex: -1, "aria-hidden": isVisible, "aria-label": i18n('dot-label', {
203
+ index: currentSlideNumber,
204
+ count: barSlidesCount,
205
+ }) }, rovingItemProps)));
163
206
  };
164
207
  const renderNavigation = () => {
165
208
  if (childrenCount <= slidesCountByBreakpoint || !dots || childrenCount === 1) {
@@ -170,7 +213,7 @@ export const SliderBlock = (props) => {
170
213
  .map((_item, index) => renderDot(index));
171
214
  dotsList.splice(currentIndex, 0, renderAccessibleBar(currentIndex));
172
215
  return (React.createElement("div", { className: b('dots', dotsClassName) },
173
- React.createElement("ul", { className: b('dots-list') },
216
+ React.createElement("ul", Object.assign({ className: b('dots-list'), role: "menu", "aria-label": i18n('pagination-label') }, rovingListProps),
174
217
  renderBar(),
175
218
  dotsList)));
176
219
  };
@@ -189,17 +232,18 @@ export const SliderBlock = (props) => {
189
232
  infinite: false,
190
233
  speed: 1000,
191
234
  adaptiveHeight: adaptive,
192
- autoplay: Boolean(autoplay),
193
- autoplaySpeed: autoplay,
235
+ autoplay: isAutoplayEnabled,
236
+ autoplaySpeed,
194
237
  slidesToShow: slidesToShowCount,
195
238
  slidesToScroll: 1,
196
239
  responsive: getSliderResponsiveParams(slidesToShow),
197
240
  beforeChange: onBeforeChange,
198
241
  afterChange: onAfterChange,
199
- initialSlide: 0,
200
- nextArrow: React.createElement(Arrow, { type: "right", handleClick: handleArrowClick, size: arrowSize }),
201
- prevArrow: React.createElement(Arrow, { type: "left", handleClick: handleArrowClick, size: arrowSize }),
242
+ initialSlide: initialIndex,
243
+ nextArrow: (React.createElement(Arrow, { type: "right", handleClick: asUserInteraction(handleArrowClick), size: arrowSize })),
244
+ prevArrow: (React.createElement(Arrow, { type: "left", handleClick: asUserInteraction(handleArrowClick), size: arrowSize })),
202
245
  lazyLoad,
246
+ accessibility: false,
203
247
  };
204
248
  return (React.createElement(OutsideClick, { onOutsideClick: isMobile ? unsetFocus : noop },
205
249
  React.createElement(SlickSlider, Object.assign({}, settings), disclosedChildren),
@@ -219,23 +263,33 @@ export const SliderBlock = (props) => {
219
263
  React.createElement(Title, { title: title, subtitle: description, className: b('header', { 'no-description': !description }) }),
220
264
  React.createElement(AnimateBlock, { className: b('animate-slides'), animate: animated }, renderSlider()))));
221
265
  };
266
+ function getSlideId(sliderId, index) {
267
+ return `slider-${sliderId}-child-${index}`;
268
+ }
222
269
  // TODO remove this and rework PriceDetailed CLOUDFRONT-12230
223
- function discloseAllNestedChildren(children) {
270
+ function discloseAllNestedChildren(children, sliderId) {
224
271
  if (!children) {
225
272
  return [];
226
273
  }
274
+ let childIndex = 0;
275
+ const wrapped = (child) => {
276
+ const id = getSlideId(sliderId, childIndex++);
277
+ return (React.createElement("div", { key: id, id: id }, child));
278
+ };
227
279
  return React.Children.map(children, (child) => {
228
280
  var _a;
229
281
  if (child) {
230
282
  // TODO: if child has 'items' then 'items' determinate like nested children for Slider.
231
283
  const nestedChildren = (_a = child.props.data) === null || _a === void 0 ? void 0 : _a.items;
232
284
  if (nestedChildren) {
233
- return nestedChildren.map((nestedChild) => React.cloneElement(child, {
234
- data: Object.assign(Object.assign({}, child.props.data), { items: [nestedChild] }),
235
- }));
285
+ return nestedChildren.map((nestedChild) => {
286
+ return wrapped(React.cloneElement(child, {
287
+ data: Object.assign(Object.assign({}, child.props.data), { items: [nestedChild] }),
288
+ }));
289
+ });
236
290
  }
237
291
  }
238
- return child;
292
+ return child && wrapped(child);
239
293
  }).filter(Boolean);
240
294
  }
241
295
  export default SliderBlock;
@@ -1,4 +1,6 @@
1
1
  {
2
2
  "arrow-right": "Next",
3
- "arrow-left": "Previous"
3
+ "arrow-left": "Previous",
4
+ "dot-label": "Page {{index}} of {{count}}",
5
+ "pagination-label": "Pages"
4
6
  }
@@ -1 +1 @@
1
- export declare const i18n: (key: "arrow-right" | "arrow-left", params?: import("@gravity-ui/i18n").Params | undefined) => string;
1
+ export declare const i18n: (key: "arrow-right" | "arrow-left" | "dot-label" | "pagination-label", params?: import("@gravity-ui/i18n").Params | undefined) => string;
@@ -1,4 +1,6 @@
1
1
  {
2
2
  "arrow-right": "Дальше",
3
- "arrow-left": "Назад"
3
+ "arrow-left": "Назад",
4
+ "dot-label": "Страница {{index}} из {{count}}",
5
+ "pagination-label": "Страницы"
4
6
  }
@@ -10,6 +10,7 @@ export interface GetSlidesToShowParams {
10
10
  breakpoints?: SlidesToShow;
11
11
  mobileFullscreen?: boolean;
12
12
  }
13
+ export declare const isFocusable: (element: Element) => boolean;
13
14
  export declare function getSlidesToShowWithDefaults({ contentLength, breakpoints, mobileFullscreen, }: GetSlidesToShowParams): {
14
15
  sm: number;
15
16
  xl: number;
@@ -24,3 +25,12 @@ export declare function getSliderResponsiveParams(breakpoints: SliderBreakpointP
24
25
  }[];
25
26
  export declare function getSlidesCountByBreakpoint(breakpoint: number, breakpoints: SliderBreakpointParams): number;
26
27
  export declare function getSlidesToShowCount(breakpoints: SliderBreakpointParams): number;
28
+ export declare function useRovingTabIndex(props: {
29
+ itemCount: number;
30
+ activeIndex: number;
31
+ firstIndex?: number;
32
+ uniqId: string;
33
+ }): {
34
+ getRovingItemProps: (index: number) => Pick<React.HTMLAttributes<HTMLElement>, 'id' | 'tabIndex' | 'onFocus'>;
35
+ rovingListProps: import("react").HTMLAttributes<HTMLElement>;
36
+ };