@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
package/README.md
CHANGED
|
@@ -109,6 +109,42 @@ interface NavigationLogo {
|
|
|
109
109
|
}
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
+
### Server utils
|
|
113
|
+
|
|
114
|
+
The package provides a set of server utilities for transforming your content.
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const {fullTransform} = require('@gravity-ui/page-constructor/server');
|
|
118
|
+
|
|
119
|
+
const {html} = fullTransform(content, {
|
|
120
|
+
lang,
|
|
121
|
+
extractTitle: true,
|
|
122
|
+
allowHTML: true,
|
|
123
|
+
path: __dirname,
|
|
124
|
+
plugins,
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Under the hood, a package is used to transform Yandex Flavored Markdown into HTML - `diplodoc/transfrom`, so it is also in peer dependencies
|
|
129
|
+
|
|
130
|
+
You can also use useful utilities in the places you need, for example in your custom components
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
const {
|
|
134
|
+
typografToText,
|
|
135
|
+
typografToHTML,
|
|
136
|
+
yfmTransformer,
|
|
137
|
+
} = require('@gravity-ui/page-constructor/server');
|
|
138
|
+
|
|
139
|
+
const post = {
|
|
140
|
+
title: typografToText(title, lang),
|
|
141
|
+
content: typografToHTML(content, lang),
|
|
142
|
+
description: yfmTransformer(lang, description, {plugins}),
|
|
143
|
+
};
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
You can find more utilities in this [section](https://github.com/gravity-ui/page-constructor/tree/main/src/text-transform)
|
|
147
|
+
|
|
112
148
|
### Custom blocks
|
|
113
149
|
|
|
114
150
|
The page constructor lets you use blocks that are user-defined in their app. Blocks are regular React components.
|
|
@@ -316,6 +352,24 @@ npm ci
|
|
|
316
352
|
npm run dev
|
|
317
353
|
```
|
|
318
354
|
|
|
355
|
+
#### Note about Vite
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
import react from '@vitejs/plugin-react-swc';
|
|
359
|
+
import dynamicImport from 'vite-plugin-dynamic-import';
|
|
360
|
+
|
|
361
|
+
export default defineConfig({
|
|
362
|
+
plugins: [
|
|
363
|
+
react(),
|
|
364
|
+
dynamicImport({
|
|
365
|
+
filter: (id) => id.includes('/node_modules/@gravity-ui/page-constructor'),
|
|
366
|
+
}),
|
|
367
|
+
],
|
|
368
|
+
});
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
For Vite, you need to install the `vite-plugin-dynamic-import` plugin and configure the config so that dynamic imports work
|
|
372
|
+
|
|
319
373
|
## Release flow
|
|
320
374
|
|
|
321
375
|
In usual cases we use two types of commits:
|
|
@@ -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
|
}
|
|
@@ -7,14 +7,14 @@ const mobileContext_1 = require("../../context/mobileContext");
|
|
|
7
7
|
const models_1 = require("../../models");
|
|
8
8
|
const utils_1 = require("../../utils");
|
|
9
9
|
const Header_1 = tslib_1.__importDefault(require("../Header/Header"));
|
|
10
|
-
const
|
|
10
|
+
const unstable_1 = require("../unstable");
|
|
11
11
|
const b = (0, utils_1.block)('header-slider-block');
|
|
12
12
|
const HeaderSliderBlock = (_a) => {
|
|
13
13
|
var { items, arrows } = _a, props = tslib_1.__rest(_a, ["items", "arrows"]);
|
|
14
14
|
const isMobile = (0, react_1.useContext)(mobileContext_1.MobileContext);
|
|
15
15
|
const showArrows = isMobile ? false : arrows;
|
|
16
16
|
return (react_1.default.createElement("div", { className: b('wrapper'), "data-qa": "header-slider" },
|
|
17
|
-
react_1.default.createElement(
|
|
17
|
+
react_1.default.createElement(unstable_1.SliderNewBlock, Object.assign({}, props, { arrows: showArrows, slidesToShow: 1, type: models_1.SliderType.HeaderCard, animated: false, blockClassName: b(), arrowSize: 20 }), items.map((item, index) => (react_1.default.createElement("div", { key: index, className: b('item'), "data-qa": `header-slider-item-${index + 1}` },
|
|
18
18
|
react_1.default.createElement(Header_1.default, Object.assign({}, item, { className: b('item-content') }))))))));
|
|
19
19
|
};
|
|
20
20
|
exports.HeaderSliderBlock = HeaderSliderBlock;
|
|
@@ -9,6 +9,7 @@ export interface SliderProps extends Omit<SliderParams, 'children'>, Refable<HTM
|
|
|
9
9
|
dotsClassName?: string;
|
|
10
10
|
blockClassName?: string;
|
|
11
11
|
arrowSize?: number;
|
|
12
|
+
initialIndex?: number;
|
|
12
13
|
}
|
|
13
14
|
export declare const SliderBlock: (props: React.PropsWithChildren<SliderProps>) => JSX.Element;
|
|
14
15
|
export default SliderBlock;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.SliderBlock = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const react_1 = tslib_1.__importStar(require("react"));
|
|
6
|
+
const uikit_1 = require("@gravity-ui/uikit");
|
|
6
7
|
const debounce_1 = tslib_1.__importDefault(require("lodash/debounce"));
|
|
7
8
|
const get_1 = tslib_1.__importDefault(require("lodash/get"));
|
|
8
9
|
const noop_1 = tslib_1.__importDefault(require("lodash/noop"));
|
|
@@ -19,6 +20,7 @@ const useFocus_1 = tslib_1.__importDefault(require("../../hooks/useFocus"));
|
|
|
19
20
|
const models_1 = require("../../models");
|
|
20
21
|
const utils_1 = require("../../utils");
|
|
21
22
|
const Arrow_1 = tslib_1.__importDefault(require("./Arrow/Arrow"));
|
|
23
|
+
const i18n_1 = require("./i18n");
|
|
22
24
|
const utils_2 = require("./utils");
|
|
23
25
|
const b = (0, utils_1.block)('SliderBlock');
|
|
24
26
|
const slick = (0, utils_1.block)('slick-origin');
|
|
@@ -26,12 +28,15 @@ const DOT_WIDTH = 8;
|
|
|
26
28
|
const DOT_GAP = 16;
|
|
27
29
|
const SliderBlock = (props) => {
|
|
28
30
|
var _a;
|
|
29
|
-
const { animated, title, description, type, anchorId, arrows = true, adaptive, autoplay
|
|
31
|
+
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;
|
|
30
32
|
const { isServer } = (0, react_1.useContext)(ssrContext_1.SSRContext);
|
|
31
33
|
const isMobile = (0, react_1.useContext)(mobileContext_1.MobileContext);
|
|
32
34
|
const [breakpoint, setBreakpoint] = (0, react_1.useState)(constants_1.BREAKPOINTS.xl);
|
|
33
|
-
const
|
|
35
|
+
const sliderId = (0, uikit_1.useUniqId)();
|
|
36
|
+
const disclosedChildren = (0, react_1.useMemo)(() => discloseAllNestedChildren(children, sliderId), [children, sliderId]);
|
|
34
37
|
const childrenCount = disclosedChildren.length;
|
|
38
|
+
const isAutoplayEnabled = autoplaySpeed !== undefined && autoplaySpeed > 0;
|
|
39
|
+
const isUserInteractionRef = (0, react_1.useRef)(false);
|
|
35
40
|
const [slidesToShow] = (0, react_1.useState)((0, utils_2.getSlidesToShowWithDefaults)({
|
|
36
41
|
contentLength: childrenCount,
|
|
37
42
|
breakpoints: props.slidesToShow,
|
|
@@ -39,11 +44,16 @@ const SliderBlock = (props) => {
|
|
|
39
44
|
}));
|
|
40
45
|
const slidesToShowCount = (0, utils_2.getSlidesToShowCount)(slidesToShow);
|
|
41
46
|
const slidesCountByBreakpoint = (0, utils_2.getSlidesCountByBreakpoint)(breakpoint, slidesToShow);
|
|
42
|
-
const [currentIndex, setCurrentIndex] = (0, react_1.useState)(
|
|
47
|
+
const [currentIndex, setCurrentIndex] = (0, react_1.useState)(initialIndex);
|
|
43
48
|
const [childStyles, setChildStyles] = (0, react_1.useState)({});
|
|
44
49
|
const [slider, setSlider] = (0, react_1.useState)();
|
|
50
|
+
const prevIndexRef = (0, react_1.useRef)(0);
|
|
45
51
|
const autoplayTimeId = (0, react_1.useRef)();
|
|
46
52
|
const { hasFocus, unsetFocus } = (0, useFocus_1.default)((_a = slider === null || slider === void 0 ? void 0 : slider.innerSlider) === null || _a === void 0 ? void 0 : _a.list);
|
|
53
|
+
const asUserInteraction = (fn) => (...args) => {
|
|
54
|
+
isUserInteractionRef.current = true;
|
|
55
|
+
return fn(...args);
|
|
56
|
+
};
|
|
47
57
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
48
58
|
const onResize = (0, react_1.useCallback)((0, debounce_1.default)(() => {
|
|
49
59
|
if (!slider) {
|
|
@@ -58,7 +68,7 @@ const SliderBlock = (props) => {
|
|
|
58
68
|
}, 100), [slider, breakpoint]);
|
|
59
69
|
const scrollLastSlide = (0, react_1.useCallback)((current) => {
|
|
60
70
|
const lastSlide = childrenCount - slidesToShowCount;
|
|
61
|
-
if (
|
|
71
|
+
if (isAutoplayEnabled && lastSlide === current) {
|
|
62
72
|
// Slick doesn't support autoplay with no infinity scroll
|
|
63
73
|
autoplayTimeId.current = setTimeout(() => {
|
|
64
74
|
if (slider) {
|
|
@@ -70,9 +80,9 @@ const SliderBlock = (props) => {
|
|
|
70
80
|
slider.slickPlay();
|
|
71
81
|
}
|
|
72
82
|
}, 500);
|
|
73
|
-
},
|
|
83
|
+
}, autoplaySpeed);
|
|
74
84
|
}
|
|
75
|
-
}, [
|
|
85
|
+
}, [autoplaySpeed, childrenCount, isAutoplayEnabled, slider, slidesToShowCount]);
|
|
76
86
|
(0, react_1.useEffect)(() => {
|
|
77
87
|
if (hasFocus && autoplayTimeId.current) {
|
|
78
88
|
clearTimeout(autoplayTimeId.current);
|
|
@@ -86,7 +96,7 @@ const SliderBlock = (props) => {
|
|
|
86
96
|
window.addEventListener('resize', onResize, { passive: true });
|
|
87
97
|
return () => window.removeEventListener('resize', onResize);
|
|
88
98
|
}, [onResize]);
|
|
89
|
-
const handleArrowClick = (
|
|
99
|
+
const handleArrowClick = (direction) => {
|
|
90
100
|
let nextIndex;
|
|
91
101
|
if (direction === 'right') {
|
|
92
102
|
nextIndex =
|
|
@@ -99,11 +109,12 @@ const SliderBlock = (props) => {
|
|
|
99
109
|
if (slider) {
|
|
100
110
|
slider.slickGoTo(nextIndex);
|
|
101
111
|
}
|
|
102
|
-
}
|
|
112
|
+
};
|
|
103
113
|
const onBeforeChange = (0, react_1.useCallback)((current, next) => {
|
|
104
114
|
if (handleBeforeChange) {
|
|
105
115
|
handleBeforeChange(current, next);
|
|
106
116
|
}
|
|
117
|
+
prevIndexRef.current = current;
|
|
107
118
|
setCurrentIndex(Math.ceil(next));
|
|
108
119
|
}, [handleBeforeChange]);
|
|
109
120
|
const onAfterChange = (0, react_1.useCallback)((current) => {
|
|
@@ -116,16 +127,33 @@ const SliderBlock = (props) => {
|
|
|
116
127
|
if (!hasFocus) {
|
|
117
128
|
scrollLastSlide(current);
|
|
118
129
|
}
|
|
119
|
-
|
|
120
|
-
|
|
130
|
+
if (isUserInteractionRef.current) {
|
|
131
|
+
const focusIndex = prevIndexRef.current >= current
|
|
132
|
+
? current
|
|
133
|
+
: Math.max(current, prevIndexRef.current + slidesCountByBreakpoint);
|
|
134
|
+
const firstNewSlide = document.getElementById(getSlideId(sliderId, focusIndex));
|
|
135
|
+
if (firstNewSlide) {
|
|
136
|
+
const focusableChild = Array.from(firstNewSlide.querySelectorAll('*')).find(utils_2.isFocusable);
|
|
137
|
+
focusableChild === null || focusableChild === void 0 ? void 0 : focusableChild.focus();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
isUserInteractionRef.current = false;
|
|
141
|
+
}, [handleAfterChange, hasFocus, scrollLastSlide, sliderId, slidesCountByBreakpoint]);
|
|
142
|
+
const handleDotClick = (index) => {
|
|
121
143
|
const nextIndex = index > currentIndex ? index + 1 - slidesCountByBreakpoint : index;
|
|
122
144
|
if (slider) {
|
|
123
145
|
slider.slickGoTo(nextIndex);
|
|
124
146
|
}
|
|
125
|
-
}
|
|
126
|
-
const barSlidesCount = childrenCount -
|
|
147
|
+
};
|
|
148
|
+
const barSlidesCount = childrenCount - slidesCountByBreakpoint + 1;
|
|
127
149
|
const barPosition = (DOT_GAP + DOT_WIDTH) * currentIndex;
|
|
128
150
|
const barWidth = DOT_WIDTH + (DOT_GAP + DOT_WIDTH) * (slidesCountByBreakpoint - 1);
|
|
151
|
+
const { getRovingItemProps, rovingListProps } = (0, utils_2.useRovingTabIndex)({
|
|
152
|
+
itemCount: barSlidesCount,
|
|
153
|
+
activeIndex: currentIndex + 1,
|
|
154
|
+
firstIndex: 1,
|
|
155
|
+
uniqId: sliderId,
|
|
156
|
+
});
|
|
129
157
|
const renderBar = () => {
|
|
130
158
|
return (slidesCountByBreakpoint > 1 && (react_1.default.createElement("li", { className: b('bar'), style: {
|
|
131
159
|
left: barPosition,
|
|
@@ -136,19 +164,22 @@ const SliderBlock = (props) => {
|
|
|
136
164
|
const renderAccessibleBar = (index) => {
|
|
137
165
|
return (
|
|
138
166
|
// To have this key differ from keys used in renderDot function, added `-accessible-bar` part
|
|
139
|
-
react_1.default.createElement(react_1.Fragment, { key: `${index}-accessible-bar` }, slidesCountByBreakpoint > 0 && (react_1.default.createElement("li", { className: b('accessible-bar'), "aria-
|
|
167
|
+
react_1.default.createElement(react_1.Fragment, { key: `${index}-accessible-bar` }, slidesCountByBreakpoint > 0 && (react_1.default.createElement("li", Object.assign({ className: b('accessible-bar'), role: "menuitemradio", "aria-checked": true, "aria-label": (0, i18n_1.i18n)('dot-label', {
|
|
168
|
+
index: currentIndex + 1,
|
|
169
|
+
count: barSlidesCount,
|
|
170
|
+
}), style: {
|
|
140
171
|
left: barPosition,
|
|
141
172
|
width: barWidth,
|
|
142
|
-
} }))));
|
|
173
|
+
} }, getRovingItemProps(currentIndex + 1))))));
|
|
143
174
|
};
|
|
144
175
|
const getCurrentSlideNumber = (index) => {
|
|
145
176
|
const currentIndexDiff = index - currentIndex;
|
|
146
177
|
let currentSlideNumber;
|
|
147
|
-
if (0 <= currentIndexDiff && currentIndexDiff <
|
|
178
|
+
if (0 <= currentIndexDiff && currentIndexDiff < slidesCountByBreakpoint) {
|
|
148
179
|
currentSlideNumber = currentIndex + 1;
|
|
149
180
|
}
|
|
150
|
-
else if (currentIndexDiff >=
|
|
151
|
-
currentSlideNumber = index -
|
|
181
|
+
else if (currentIndexDiff >= slidesCountByBreakpoint) {
|
|
182
|
+
currentSlideNumber = index - slidesCountByBreakpoint + 2;
|
|
152
183
|
}
|
|
153
184
|
else {
|
|
154
185
|
currentSlideNumber = index + 1;
|
|
@@ -157,12 +188,24 @@ const SliderBlock = (props) => {
|
|
|
157
188
|
};
|
|
158
189
|
const isVisibleSlide = (index) => {
|
|
159
190
|
const currentIndexDiff = index - currentIndex;
|
|
160
|
-
|
|
191
|
+
const result = slidesCountByBreakpoint > 0 &&
|
|
161
192
|
0 <= currentIndexDiff &&
|
|
162
|
-
currentIndexDiff <
|
|
193
|
+
currentIndexDiff < slidesCountByBreakpoint;
|
|
194
|
+
return result;
|
|
163
195
|
};
|
|
164
196
|
const renderDot = (index) => {
|
|
165
|
-
|
|
197
|
+
const isVisible = isVisibleSlide(index);
|
|
198
|
+
const currentSlideNumber = getCurrentSlideNumber(index);
|
|
199
|
+
const rovingItemProps = isVisible ? undefined : getRovingItemProps(currentSlideNumber);
|
|
200
|
+
return (react_1.default.createElement("li", Object.assign({ key: index, className: b('dot', { active: index === currentIndex }), onClick: asUserInteraction(() => handleDotClick(index)), onKeyDown: (e) => {
|
|
201
|
+
const key = e.key.toLowerCase();
|
|
202
|
+
if (key === 'space' || key === 'enter') {
|
|
203
|
+
e.currentTarget.click();
|
|
204
|
+
}
|
|
205
|
+
}, role: "menuitemradio", "aria-checked": false, tabIndex: -1, "aria-hidden": isVisible, "aria-label": (0, i18n_1.i18n)('dot-label', {
|
|
206
|
+
index: currentSlideNumber,
|
|
207
|
+
count: barSlidesCount,
|
|
208
|
+
}) }, rovingItemProps)));
|
|
166
209
|
};
|
|
167
210
|
const renderNavigation = () => {
|
|
168
211
|
if (childrenCount <= slidesCountByBreakpoint || !dots || childrenCount === 1) {
|
|
@@ -173,7 +216,7 @@ const SliderBlock = (props) => {
|
|
|
173
216
|
.map((_item, index) => renderDot(index));
|
|
174
217
|
dotsList.splice(currentIndex, 0, renderAccessibleBar(currentIndex));
|
|
175
218
|
return (react_1.default.createElement("div", { className: b('dots', dotsClassName) },
|
|
176
|
-
react_1.default.createElement("ul", { className: b('dots-list') },
|
|
219
|
+
react_1.default.createElement("ul", Object.assign({ className: b('dots-list'), role: "menu", "aria-label": (0, i18n_1.i18n)('pagination-label') }, rovingListProps),
|
|
177
220
|
renderBar(),
|
|
178
221
|
dotsList)));
|
|
179
222
|
};
|
|
@@ -192,17 +235,18 @@ const SliderBlock = (props) => {
|
|
|
192
235
|
infinite: false,
|
|
193
236
|
speed: 1000,
|
|
194
237
|
adaptiveHeight: adaptive,
|
|
195
|
-
autoplay:
|
|
196
|
-
autoplaySpeed
|
|
238
|
+
autoplay: isAutoplayEnabled,
|
|
239
|
+
autoplaySpeed,
|
|
197
240
|
slidesToShow: slidesToShowCount,
|
|
198
241
|
slidesToScroll: 1,
|
|
199
242
|
responsive: (0, utils_2.getSliderResponsiveParams)(slidesToShow),
|
|
200
243
|
beforeChange: onBeforeChange,
|
|
201
244
|
afterChange: onAfterChange,
|
|
202
|
-
initialSlide:
|
|
203
|
-
nextArrow: react_1.default.createElement(Arrow_1.default, { type: "right", handleClick: handleArrowClick, size: arrowSize }),
|
|
204
|
-
prevArrow: react_1.default.createElement(Arrow_1.default, { type: "left", handleClick: handleArrowClick, size: arrowSize }),
|
|
245
|
+
initialSlide: initialIndex,
|
|
246
|
+
nextArrow: (react_1.default.createElement(Arrow_1.default, { type: "right", handleClick: asUserInteraction(handleArrowClick), size: arrowSize })),
|
|
247
|
+
prevArrow: (react_1.default.createElement(Arrow_1.default, { type: "left", handleClick: asUserInteraction(handleArrowClick), size: arrowSize })),
|
|
205
248
|
lazyLoad,
|
|
249
|
+
accessibility: false,
|
|
206
250
|
};
|
|
207
251
|
return (react_1.default.createElement(OutsideClick_1.default, { onOutsideClick: isMobile ? unsetFocus : noop_1.default },
|
|
208
252
|
react_1.default.createElement(react_slick_1.default, Object.assign({}, settings), disclosedChildren),
|
|
@@ -223,23 +267,33 @@ const SliderBlock = (props) => {
|
|
|
223
267
|
react_1.default.createElement(AnimateBlock_1.default, { className: b('animate-slides'), animate: animated }, renderSlider()))));
|
|
224
268
|
};
|
|
225
269
|
exports.SliderBlock = SliderBlock;
|
|
270
|
+
function getSlideId(sliderId, index) {
|
|
271
|
+
return `slider-${sliderId}-child-${index}`;
|
|
272
|
+
}
|
|
226
273
|
// TODO remove this and rework PriceDetailed CLOUDFRONT-12230
|
|
227
|
-
function discloseAllNestedChildren(children) {
|
|
274
|
+
function discloseAllNestedChildren(children, sliderId) {
|
|
228
275
|
if (!children) {
|
|
229
276
|
return [];
|
|
230
277
|
}
|
|
278
|
+
let childIndex = 0;
|
|
279
|
+
const wrapped = (child) => {
|
|
280
|
+
const id = getSlideId(sliderId, childIndex++);
|
|
281
|
+
return (react_1.default.createElement("div", { key: id, id: id }, child));
|
|
282
|
+
};
|
|
231
283
|
return react_1.default.Children.map(children, (child) => {
|
|
232
284
|
var _a;
|
|
233
285
|
if (child) {
|
|
234
286
|
// TODO: if child has 'items' then 'items' determinate like nested children for Slider.
|
|
235
287
|
const nestedChildren = (_a = child.props.data) === null || _a === void 0 ? void 0 : _a.items;
|
|
236
288
|
if (nestedChildren) {
|
|
237
|
-
return nestedChildren.map((nestedChild) =>
|
|
238
|
-
|
|
239
|
-
|
|
289
|
+
return nestedChildren.map((nestedChild) => {
|
|
290
|
+
return wrapped(react_1.default.cloneElement(child, {
|
|
291
|
+
data: Object.assign(Object.assign({}, child.props.data), { items: [nestedChild] }),
|
|
292
|
+
}));
|
|
293
|
+
});
|
|
240
294
|
}
|
|
241
295
|
}
|
|
242
|
-
return child;
|
|
296
|
+
return child && wrapped(child);
|
|
243
297
|
}).filter(Boolean);
|
|
244
298
|
}
|
|
245
299
|
exports.default = exports.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,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getSlidesToShowCount = exports.getSlidesCountByBreakpoint = exports.getSliderResponsiveParams = exports.getSlidesToShowWithDefaults = exports.DEFAULT_SLIDE_BREAKPOINTS = void 0;
|
|
3
|
+
exports.useRovingTabIndex = exports.getSlidesToShowCount = exports.getSlidesCountByBreakpoint = exports.getSliderResponsiveParams = exports.getSlidesToShowWithDefaults = exports.isFocusable = exports.DEFAULT_SLIDE_BREAKPOINTS = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
const react_1 = require("react");
|
|
5
6
|
const pickBy_1 = tslib_1.__importDefault(require("lodash/pickBy"));
|
|
6
7
|
const constants_1 = require("../../constants");
|
|
7
8
|
const models_1 = require("./models");
|
|
@@ -12,6 +13,38 @@ exports.DEFAULT_SLIDE_BREAKPOINTS = {
|
|
|
12
13
|
[models_1.SliderBreakpointNames.Sm]: 1.15,
|
|
13
14
|
};
|
|
14
15
|
const BREAKPOINT_NAMES_BY_VALUES = Object.entries(constants_1.BREAKPOINTS).reduce((acc, [key, value]) => (Object.assign(Object.assign({}, acc), { [value]: key })), {});
|
|
16
|
+
const isFocusable = (element) => {
|
|
17
|
+
if (!(element instanceof HTMLElement)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const tabIndexAttr = element.getAttribute('tabindex');
|
|
21
|
+
const hasTabIndex = tabIndexAttr !== null;
|
|
22
|
+
const tabIndex = Number(tabIndexAttr);
|
|
23
|
+
if (element.ariaHidden === 'true' || (hasTabIndex && tabIndex < 0)) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (hasTabIndex && tabIndex >= 0) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
// without this jest fails here for some reason
|
|
30
|
+
let htmlElement;
|
|
31
|
+
switch (true) {
|
|
32
|
+
case element instanceof HTMLAnchorElement:
|
|
33
|
+
htmlElement = element;
|
|
34
|
+
return Boolean(htmlElement.href);
|
|
35
|
+
case element instanceof HTMLInputElement:
|
|
36
|
+
htmlElement = element;
|
|
37
|
+
return htmlElement.type !== 'hidden' && !htmlElement.disabled;
|
|
38
|
+
case element instanceof HTMLSelectElement:
|
|
39
|
+
case element instanceof HTMLTextAreaElement:
|
|
40
|
+
case element instanceof HTMLButtonElement:
|
|
41
|
+
htmlElement = element;
|
|
42
|
+
return !htmlElement.disabled;
|
|
43
|
+
default:
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
exports.isFocusable = isFocusable;
|
|
15
48
|
function getSlidesToShowWithDefaults({ contentLength, breakpoints, mobileFullscreen, }) {
|
|
16
49
|
let result;
|
|
17
50
|
if (typeof breakpoints === 'number') {
|
|
@@ -39,3 +72,54 @@ function getSlidesToShowCount(breakpoints) {
|
|
|
39
72
|
return Math.floor(Math.max(...Object.values(breakpoints)));
|
|
40
73
|
}
|
|
41
74
|
exports.getSlidesToShowCount = getSlidesToShowCount;
|
|
75
|
+
const getRovingListItemId = (uniqId, index) => `${uniqId}-roving-tabindex-item-${index}`;
|
|
76
|
+
function useRovingTabIndex(props) {
|
|
77
|
+
const { itemCount, activeIndex, firstIndex = 0, uniqId } = props;
|
|
78
|
+
const [currentIndex, setCurrentIndex] = (0, react_1.useState)(firstIndex);
|
|
79
|
+
const hasFocusRef = (0, react_1.useRef)(false);
|
|
80
|
+
const lastIndex = itemCount + firstIndex - 1;
|
|
81
|
+
const getRovingItemProps = (index) => {
|
|
82
|
+
return {
|
|
83
|
+
id: getRovingListItemId(uniqId, index),
|
|
84
|
+
tabIndex: index === activeIndex ? 0 : -1,
|
|
85
|
+
onFocus: () => {
|
|
86
|
+
setCurrentIndex(index);
|
|
87
|
+
hasFocusRef.current = true;
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
(0, react_1.useEffect)(() => {
|
|
92
|
+
var _a;
|
|
93
|
+
if (!hasFocusRef.current) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
(_a = document.getElementById(getRovingListItemId(uniqId, currentIndex))) === null || _a === void 0 ? void 0 : _a.focus();
|
|
97
|
+
}, [activeIndex, currentIndex, uniqId]);
|
|
98
|
+
const setNextIndex = () => setCurrentIndex((prev) => (prev >= lastIndex ? firstIndex : prev + 1));
|
|
99
|
+
const setPrevIndex = () => setCurrentIndex((prev) => (prev <= firstIndex ? lastIndex : prev - 1));
|
|
100
|
+
const onRovingListKeyDown = (e) => {
|
|
101
|
+
const key = e.key.toLowerCase();
|
|
102
|
+
if (key !== 'tab' && key !== 'enter') {
|
|
103
|
+
e.preventDefault();
|
|
104
|
+
}
|
|
105
|
+
switch (key) {
|
|
106
|
+
case 'arrowleft':
|
|
107
|
+
case 'arrowup':
|
|
108
|
+
setPrevIndex();
|
|
109
|
+
return;
|
|
110
|
+
case 'arrowright':
|
|
111
|
+
case 'arrowdown':
|
|
112
|
+
setNextIndex();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const onRovingListBlur = () => {
|
|
117
|
+
hasFocusRef.current = false;
|
|
118
|
+
};
|
|
119
|
+
const rovingListProps = {
|
|
120
|
+
onKeyDown: onRovingListKeyDown,
|
|
121
|
+
onBlur: onRovingListBlur,
|
|
122
|
+
};
|
|
123
|
+
return { getRovingItemProps, rovingListProps };
|
|
124
|
+
}
|
|
125
|
+
exports.useRovingTabIndex = useRovingTabIndex;
|
|
@@ -1,33 +1,25 @@
|
|
|
1
|
-
.pc-slider-new-block-
|
|
1
|
+
.pc-slider-new-block-arrow__inner {
|
|
2
2
|
box-shadow: 0px 4px 24px var(--pc-color-sfx-shadow), 0px 2px 8px var(--pc-color-sfx-shadow);
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
.pc-slider-new-block-
|
|
5
|
+
.pc-slider-new-block-arrow__inner:hover {
|
|
6
6
|
box-shadow: 0px 4px 24px var(--g-color-sfx-shadow), 0px 2px 8px var(--g-color-sfx-shadow);
|
|
7
7
|
cursor: pointer;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
/* use this for style redefinitions to awoid problems with
|
|
11
11
|
unpredictable css rules order in build */
|
|
12
|
-
.pc-slider-new-block-arrow__icon-wrapper, .pc-slider-new-block-
|
|
12
|
+
.pc-slider-new-block-arrow__icon-wrapper, .pc-slider-new-block-arrow__inner, .pc-slider-new-block-arrow {
|
|
13
13
|
display: flex;
|
|
14
14
|
align-items: center;
|
|
15
15
|
justify-content: center;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
.pc-slider-new-block-arrow {
|
|
19
|
-
width: 42px;
|
|
20
|
-
height: 42px;
|
|
21
|
-
cursor: pointer;
|
|
22
|
-
}
|
|
23
18
|
@media (max-width: 576px) {
|
|
24
19
|
.pc-slider-new-block-arrow {
|
|
25
20
|
display: none;
|
|
26
21
|
}
|
|
27
22
|
}
|
|
28
|
-
.pc-slider-new-block-arrow_type_left .pc-slider-new-block-arrow__icon-wrapper {
|
|
29
|
-
transform: rotate(180deg);
|
|
30
|
-
}
|
|
31
23
|
.pc-slider-new-block-arrow__button {
|
|
32
24
|
display: inline-block;
|
|
33
25
|
margin: 0;
|
|
@@ -38,13 +30,6 @@ unpredictable css rules order in build */
|
|
|
38
30
|
color: inherit;
|
|
39
31
|
background: none;
|
|
40
32
|
cursor: pointer;
|
|
41
|
-
width: 42px;
|
|
42
|
-
height: 42px;
|
|
43
|
-
color: var(--g-color-text-secondary);
|
|
44
|
-
border-radius: 100%;
|
|
45
|
-
background-color: var(--g-color-base-background);
|
|
46
|
-
box-shadow: 0 4px 24px var(--pc-color-sfx-shadow), 0 2px 8px var(--pc-color-sfx-shadow);
|
|
47
|
-
transition: box-shadow 0.3s cubic-bezier(0.22, 0.61, 0.36, 1), color 0.3s cubic-bezier(0.22, 0.61, 0.36, 1);
|
|
48
33
|
}
|
|
49
34
|
.pc-slider-new-block-arrow__button:focus {
|
|
50
35
|
outline: 2px solid var(--g-color-line-focus);
|
|
@@ -53,10 +38,29 @@ unpredictable css rules order in build */
|
|
|
53
38
|
.pc-slider-new-block-arrow__button:focus:not(:focus-visible) {
|
|
54
39
|
outline: 0;
|
|
55
40
|
}
|
|
56
|
-
.pc-slider-new-block-
|
|
41
|
+
.pc-slider-new-block-arrow__inner {
|
|
42
|
+
width: 42px;
|
|
43
|
+
height: 42px;
|
|
44
|
+
color: var(--g-color-text-secondary);
|
|
45
|
+
border-radius: 100%;
|
|
46
|
+
background-color: var(--g-color-base-background);
|
|
47
|
+
box-shadow: 0 4px 24px var(--pc-color-sfx-shadow), 0 2px 8px var(--pc-color-sfx-shadow);
|
|
48
|
+
transition: box-shadow 0.3s cubic-bezier(0.22, 0.61, 0.36, 1), color 1s cubic-bezier(0.22, 0.61, 0.36, 1);
|
|
49
|
+
}
|
|
50
|
+
.pc-slider-new-block-arrow__inner_type_left .pc-slider-new-block-arrow__icon-wrapper {
|
|
51
|
+
transform: rotate(180deg);
|
|
52
|
+
}
|
|
53
|
+
.pc-slider-new-block-arrow__inner:hover {
|
|
57
54
|
color: var(--g-color-text-primary);
|
|
58
55
|
box-shadow: 0 2px 12px var(--pc-color-sfx-shadow), 0 4px 24px var(--pc-color-sfx-shadow);
|
|
59
56
|
}
|
|
57
|
+
.pc-slider-new-block-arrow__inner_transparent {
|
|
58
|
+
background-color: transparent;
|
|
59
|
+
box-shadow: none;
|
|
60
|
+
}
|
|
61
|
+
.pc-slider-new-block-arrow__inner_transparent:hover {
|
|
62
|
+
box-shadow: none;
|
|
63
|
+
}
|
|
60
64
|
.pc-slider-new-block-arrow__icon {
|
|
61
65
|
position: relative;
|
|
62
66
|
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import { ClassNameProps } from '../../../models';
|
|
2
3
|
export type ArrowType = 'left' | 'right';
|
|
3
4
|
export interface ArrowProps {
|
|
4
5
|
type: ArrowType;
|
|
6
|
+
transparent?: boolean;
|
|
5
7
|
onClick?: () => void;
|
|
6
8
|
size?: number;
|
|
9
|
+
extraProps?: React.ButtonHTMLAttributes<HTMLButtonElement>;
|
|
7
10
|
}
|
|
8
|
-
declare const Arrow: ({ type, onClick, className, size }: ArrowProps & ClassNameProps) => JSX.Element;
|
|
11
|
+
declare const Arrow: ({ type, transparent, onClick, className, size, extraProps, }: ArrowProps & ClassNameProps) => JSX.Element;
|
|
9
12
|
export default Arrow;
|
|
@@ -6,8 +6,9 @@ const ToggleArrow_1 = tslib_1.__importDefault(require("../../../components/Toggl
|
|
|
6
6
|
const utils_1 = require("../../../utils");
|
|
7
7
|
const i18n_1 = require("../i18n");
|
|
8
8
|
const b = (0, utils_1.block)('slider-new-block-arrow');
|
|
9
|
-
const Arrow = ({ type, onClick, className, size = 16 }) => (react_1.default.createElement("div", { className: b(
|
|
10
|
-
react_1.default.createElement("button", { className: b('button'), onClick: onClick, "aria-label": (0, i18n_1.i18n)(`arrow-${type}`) },
|
|
11
|
-
react_1.default.createElement("
|
|
12
|
-
react_1.default.createElement(
|
|
9
|
+
const Arrow = ({ type, transparent, onClick, className, size = 16, extraProps, }) => (react_1.default.createElement("div", { className: b(null, className) },
|
|
10
|
+
react_1.default.createElement("button", Object.assign({ className: b('button'), onClick: onClick, "aria-label": (0, i18n_1.i18n)(`arrow-${type}`) }, extraProps),
|
|
11
|
+
react_1.default.createElement("div", { className: b('inner', { type, transparent }) },
|
|
12
|
+
react_1.default.createElement("span", { className: b('icon-wrapper') },
|
|
13
|
+
react_1.default.createElement(ToggleArrow_1.default, { size: size, type: 'horizontal', iconType: "navigation", className: b('icon') }))))));
|
|
13
14
|
exports.default = Arrow;
|