@gravity-ui/page-constructor 5.27.0 → 5.27.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.
- package/README.md +54 -0
- package/build/cjs/blocks/HeaderSlider/HeaderSlider.css +0 -10
- package/build/cjs/blocks/HeaderSlider/HeaderSlider.js +2 -2
- package/build/cjs/blocks/Slider/Slider.css +2 -0
- package/build/cjs/blocks/Slider/Slider.d.ts +1 -0
- package/build/cjs/blocks/Slider/Slider.js +85 -31
- package/build/cjs/blocks/Slider/i18n/en.json +3 -1
- package/build/cjs/blocks/Slider/i18n/index.d.ts +1 -1
- package/build/cjs/blocks/Slider/i18n/ru.json +3 -1
- package/build/cjs/blocks/Slider/utils.d.ts +10 -0
- package/build/cjs/blocks/Slider/utils.js +85 -1
- package/build/cjs/blocks/SliderNew/Arrow/Arrow.css +23 -19
- package/build/cjs/blocks/SliderNew/Arrow/Arrow.d.ts +4 -1
- package/build/cjs/blocks/SliderNew/Arrow/Arrow.js +5 -4
- package/build/cjs/blocks/SliderNew/Slider.css +103 -22
- package/build/cjs/blocks/SliderNew/Slider.d.ts +2 -1
- package/build/cjs/blocks/SliderNew/Slider.js +22 -9
- package/build/cjs/blocks/SliderNew/i18n/en.json +3 -1
- package/build/cjs/blocks/SliderNew/i18n/index.d.ts +1 -1
- package/build/cjs/blocks/SliderNew/i18n/ru.json +3 -1
- package/build/cjs/blocks/SliderNew/useSlider.d.ts +8 -6
- package/build/cjs/blocks/SliderNew/useSlider.js +4 -2
- package/build/cjs/blocks/SliderNew/useSliderPagination.d.ts +9 -0
- package/build/cjs/blocks/SliderNew/useSliderPagination.js +36 -0
- package/build/cjs/blocks/SliderNew/utils.d.ts +2 -0
- package/build/cjs/blocks/SliderNew/utils.js +13 -1
- package/build/cjs/components/FullscreenImage/FullscreenImage.css +48 -8
- package/build/cjs/components/FullscreenImage/FullscreenImage.d.ts +5 -0
- package/build/cjs/components/FullscreenImage/FullscreenImage.js +17 -3
- package/build/cjs/components/Media/Image/Image.d.ts +1 -0
- package/build/cjs/components/Media/Image/Image.js +7 -5
- package/build/cjs/components/Media/Media.css +4 -0
- package/build/cjs/components/Media/Media.d.ts +1 -0
- package/build/cjs/components/Media/Media.js +3 -2
- package/build/cjs/components/MediaBase/MediaBase.js +1 -1
- package/build/cjs/components/ReactPlayer/ReactPlayer.js +1 -1
- package/build/cjs/constructor-items.d.ts +1 -1
- package/build/cjs/models/constructor-items/blocks.d.ts +2 -1
- package/build/cjs/models/constructor-items/blocks.js +1 -0
- package/build/cjs/navigation/hooks/useShowBorder.js +3 -2
- package/build/cjs/sub-blocks/Content/Content.css +14 -4
- package/build/cjs/sub-blocks/ImageCard/ImageCard.css +8 -0
- package/build/esm/blocks/HeaderSlider/HeaderSlider.css +0 -10
- package/build/esm/blocks/HeaderSlider/HeaderSlider.js +1 -1
- package/build/esm/blocks/Slider/Slider.css +2 -0
- package/build/esm/blocks/Slider/Slider.d.ts +1 -0
- package/build/esm/blocks/Slider/Slider.js +86 -32
- package/build/esm/blocks/Slider/i18n/en.json +3 -1
- package/build/esm/blocks/Slider/i18n/index.d.ts +1 -1
- package/build/esm/blocks/Slider/i18n/ru.json +3 -1
- package/build/esm/blocks/Slider/utils.d.ts +10 -0
- package/build/esm/blocks/Slider/utils.js +82 -0
- package/build/esm/blocks/SliderNew/Arrow/Arrow.css +23 -19
- package/build/esm/blocks/SliderNew/Arrow/Arrow.d.ts +4 -1
- package/build/esm/blocks/SliderNew/Arrow/Arrow.js +5 -4
- package/build/esm/blocks/SliderNew/Slider.css +103 -22
- package/build/esm/blocks/SliderNew/Slider.d.ts +2 -1
- package/build/esm/blocks/SliderNew/Slider.js +22 -9
- package/build/esm/blocks/SliderNew/i18n/en.json +3 -1
- package/build/esm/blocks/SliderNew/i18n/index.d.ts +1 -1
- package/build/esm/blocks/SliderNew/i18n/ru.json +3 -1
- package/build/esm/blocks/SliderNew/useSlider.d.ts +8 -6
- package/build/esm/blocks/SliderNew/useSlider.js +6 -3
- package/build/esm/blocks/SliderNew/useSliderPagination.d.ts +9 -0
- package/build/esm/blocks/SliderNew/useSliderPagination.js +32 -0
- package/build/esm/blocks/SliderNew/utils.d.ts +2 -0
- package/build/esm/blocks/SliderNew/utils.js +10 -0
- package/build/esm/components/FullscreenImage/FullscreenImage.css +48 -8
- package/build/esm/components/FullscreenImage/FullscreenImage.d.ts +5 -0
- package/build/esm/components/FullscreenImage/FullscreenImage.js +18 -4
- package/build/esm/components/Media/Image/Image.d.ts +1 -0
- package/build/esm/components/Media/Image/Image.js +7 -5
- package/build/esm/components/Media/Media.css +4 -0
- package/build/esm/components/Media/Media.d.ts +1 -0
- package/build/esm/components/Media/Media.js +3 -2
- package/build/esm/components/MediaBase/MediaBase.js +1 -1
- package/build/esm/components/ReactPlayer/ReactPlayer.js +1 -1
- package/build/esm/constructor-items.d.ts +1 -1
- package/build/esm/models/constructor-items/blocks.d.ts +2 -1
- package/build/esm/models/constructor-items/blocks.js +1 -0
- package/build/esm/navigation/hooks/useShowBorder.js +1 -1
- package/build/esm/sub-blocks/Content/Content.css +14 -4
- package/build/esm/sub-blocks/ImageCard/ImageCard.css +8 -0
- package/package.json +2 -1
- package/server/models/constructor-items/blocks.d.ts +2 -1
- package/server/models/constructor-items/blocks.js +1 -0
- package/styles/mixins.scss +1 -1
- package/widget/index.js +1 -1
|
@@ -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",
|
|
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
|
|
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,
|
|
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 '../
|
|
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) => {
|
|
@@ -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 {
|
|
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
|
|
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
|
|
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(
|
|
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 (
|
|
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
|
-
},
|
|
80
|
+
}, autoplaySpeed);
|
|
71
81
|
}
|
|
72
|
-
}, [
|
|
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 =
|
|
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
|
-
}
|
|
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
|
-
|
|
117
|
-
|
|
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
|
-
}
|
|
123
|
-
const barSlidesCount = childrenCount -
|
|
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-
|
|
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 <
|
|
175
|
+
if (0 <= currentIndexDiff && currentIndexDiff < slidesCountByBreakpoint) {
|
|
145
176
|
currentSlideNumber = currentIndex + 1;
|
|
146
177
|
}
|
|
147
|
-
else if (currentIndexDiff >=
|
|
148
|
-
currentSlideNumber = index -
|
|
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
|
-
|
|
188
|
+
const result = slidesCountByBreakpoint > 0 &&
|
|
158
189
|
0 <= currentIndexDiff &&
|
|
159
|
-
currentIndexDiff <
|
|
190
|
+
currentIndexDiff < slidesCountByBreakpoint;
|
|
191
|
+
return result;
|
|
160
192
|
};
|
|
161
193
|
const renderDot = (index) => {
|
|
162
|
-
|
|
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:
|
|
193
|
-
autoplaySpeed
|
|
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:
|
|
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) =>
|
|
234
|
-
|
|
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 +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;
|
|
@@ -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
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
1
2
|
import pickBy from 'lodash/pickBy';
|
|
2
3
|
import { BREAKPOINTS } from '../../constants';
|
|
3
4
|
import { SliderBreakpointNames } from './models';
|
|
@@ -8,6 +9,37 @@ export const DEFAULT_SLIDE_BREAKPOINTS = {
|
|
|
8
9
|
[SliderBreakpointNames.Sm]: 1.15,
|
|
9
10
|
};
|
|
10
11
|
const BREAKPOINT_NAMES_BY_VALUES = Object.entries(BREAKPOINTS).reduce((acc, [key, value]) => (Object.assign(Object.assign({}, acc), { [value]: key })), {});
|
|
12
|
+
export const isFocusable = (element) => {
|
|
13
|
+
if (!(element instanceof HTMLElement)) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
const tabIndexAttr = element.getAttribute('tabindex');
|
|
17
|
+
const hasTabIndex = tabIndexAttr !== null;
|
|
18
|
+
const tabIndex = Number(tabIndexAttr);
|
|
19
|
+
if (element.ariaHidden === 'true' || (hasTabIndex && tabIndex < 0)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (hasTabIndex && tabIndex >= 0) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
// without this jest fails here for some reason
|
|
26
|
+
let htmlElement;
|
|
27
|
+
switch (true) {
|
|
28
|
+
case element instanceof HTMLAnchorElement:
|
|
29
|
+
htmlElement = element;
|
|
30
|
+
return Boolean(htmlElement.href);
|
|
31
|
+
case element instanceof HTMLInputElement:
|
|
32
|
+
htmlElement = element;
|
|
33
|
+
return htmlElement.type !== 'hidden' && !htmlElement.disabled;
|
|
34
|
+
case element instanceof HTMLSelectElement:
|
|
35
|
+
case element instanceof HTMLTextAreaElement:
|
|
36
|
+
case element instanceof HTMLButtonElement:
|
|
37
|
+
htmlElement = element;
|
|
38
|
+
return !htmlElement.disabled;
|
|
39
|
+
default:
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
11
43
|
export function getSlidesToShowWithDefaults({ contentLength, breakpoints, mobileFullscreen, }) {
|
|
12
44
|
let result;
|
|
13
45
|
if (typeof breakpoints === 'number') {
|
|
@@ -31,3 +63,53 @@ export function getSlidesCountByBreakpoint(breakpoint, breakpoints) {
|
|
|
31
63
|
export function getSlidesToShowCount(breakpoints) {
|
|
32
64
|
return Math.floor(Math.max(...Object.values(breakpoints)));
|
|
33
65
|
}
|
|
66
|
+
const getRovingListItemId = (uniqId, index) => `${uniqId}-roving-tabindex-item-${index}`;
|
|
67
|
+
export function useRovingTabIndex(props) {
|
|
68
|
+
const { itemCount, activeIndex, firstIndex = 0, uniqId } = props;
|
|
69
|
+
const [currentIndex, setCurrentIndex] = useState(firstIndex);
|
|
70
|
+
const hasFocusRef = useRef(false);
|
|
71
|
+
const lastIndex = itemCount + firstIndex - 1;
|
|
72
|
+
const getRovingItemProps = (index) => {
|
|
73
|
+
return {
|
|
74
|
+
id: getRovingListItemId(uniqId, index),
|
|
75
|
+
tabIndex: index === activeIndex ? 0 : -1,
|
|
76
|
+
onFocus: () => {
|
|
77
|
+
setCurrentIndex(index);
|
|
78
|
+
hasFocusRef.current = true;
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
var _a;
|
|
84
|
+
if (!hasFocusRef.current) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
(_a = document.getElementById(getRovingListItemId(uniqId, currentIndex))) === null || _a === void 0 ? void 0 : _a.focus();
|
|
88
|
+
}, [activeIndex, currentIndex, uniqId]);
|
|
89
|
+
const setNextIndex = () => setCurrentIndex((prev) => (prev >= lastIndex ? firstIndex : prev + 1));
|
|
90
|
+
const setPrevIndex = () => setCurrentIndex((prev) => (prev <= firstIndex ? lastIndex : prev - 1));
|
|
91
|
+
const onRovingListKeyDown = (e) => {
|
|
92
|
+
const key = e.key.toLowerCase();
|
|
93
|
+
if (key !== 'tab' && key !== 'enter') {
|
|
94
|
+
e.preventDefault();
|
|
95
|
+
}
|
|
96
|
+
switch (key) {
|
|
97
|
+
case 'arrowleft':
|
|
98
|
+
case 'arrowup':
|
|
99
|
+
setPrevIndex();
|
|
100
|
+
return;
|
|
101
|
+
case 'arrowright':
|
|
102
|
+
case 'arrowdown':
|
|
103
|
+
setNextIndex();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const onRovingListBlur = () => {
|
|
108
|
+
hasFocusRef.current = false;
|
|
109
|
+
};
|
|
110
|
+
const rovingListProps = {
|
|
111
|
+
onKeyDown: onRovingListKeyDown,
|
|
112
|
+
onBlur: onRovingListBlur,
|
|
113
|
+
};
|
|
114
|
+
return { getRovingItemProps, rovingListProps };
|
|
115
|
+
}
|