@gravity-ui/page-constructor 5.27.0 → 5.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/blocks/Slider/Slider.css +1 -1
- package/build/cjs/blocks/Slider/Slider.js +83 -29
- 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.d.ts +3 -1
- package/build/cjs/blocks/SliderNew/Arrow/Arrow.js +2 -2
- package/build/cjs/blocks/SliderNew/Slider.js +20 -8
- 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/esm/blocks/Slider/Slider.css +1 -1
- package/build/esm/blocks/Slider/Slider.js +84 -30
- 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.d.ts +3 -1
- package/build/esm/blocks/SliderNew/Arrow/Arrow.js +2 -2
- package/build/esm/blocks/SliderNew/Slider.js +20 -8
- 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/package.json +2 -1
- package/widget/index.js +1 -1
|
@@ -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, } = 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,
|
|
@@ -42,8 +47,13 @@ const SliderBlock = (props) => {
|
|
|
42
47
|
const [currentIndex, setCurrentIndex] = (0, react_1.useState)(0);
|
|
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
245
|
initialSlide: 0,
|
|
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 }),
|
|
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,9 +1,11 @@
|
|
|
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;
|
|
5
6
|
onClick?: () => void;
|
|
6
7
|
size?: number;
|
|
8
|
+
extraProps?: React.ButtonHTMLAttributes<HTMLButtonElement>;
|
|
7
9
|
}
|
|
8
|
-
declare const Arrow: ({ type, onClick, className, size }: ArrowProps & ClassNameProps) => JSX.Element;
|
|
10
|
+
declare const Arrow: ({ type, onClick, className, size, extraProps }: ArrowProps & ClassNameProps) => JSX.Element;
|
|
9
11
|
export default Arrow;
|
|
@@ -6,8 +6,8 @@ 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({ type }, className) },
|
|
10
|
-
react_1.default.createElement("button", { className: b('button'), onClick: onClick, "aria-label": (0, i18n_1.i18n)(`arrow-${type}`) },
|
|
9
|
+
const Arrow = ({ type, onClick, className, size = 16, extraProps }) => (react_1.default.createElement("div", { className: b({ type }, className) },
|
|
10
|
+
react_1.default.createElement("button", Object.assign({ className: b('button'), onClick: onClick, "aria-label": (0, i18n_1.i18n)(`arrow-${type}`) }, extraProps),
|
|
11
11
|
react_1.default.createElement("span", { className: b('icon-wrapper') },
|
|
12
12
|
react_1.default.createElement(ToggleArrow_1.default, { size: size, type: 'horizontal', iconType: "navigation", className: b('icon') })))));
|
|
13
13
|
exports.default = Arrow;
|
|
@@ -10,17 +10,29 @@ const AnimateBlock_1 = tslib_1.__importDefault(require("../../components/Animate
|
|
|
10
10
|
const Title_1 = tslib_1.__importDefault(require("../../components/Title/Title"));
|
|
11
11
|
const utils_1 = require("../../utils");
|
|
12
12
|
const Arrow_1 = tslib_1.__importDefault(require("./Arrow/Arrow"));
|
|
13
|
+
const i18n_1 = require("./i18n");
|
|
13
14
|
const useSlider_1 = require("./useSlider");
|
|
15
|
+
const useSliderPagination_1 = require("./useSliderPagination");
|
|
14
16
|
require("swiper/swiper-bundle.css");
|
|
15
17
|
const b = (0, utils_1.block)('SliderNewBlock');
|
|
16
18
|
swiper_1.default.use([swiper_1.Autoplay, swiper_1.A11y, swiper_1.Pagination]);
|
|
17
19
|
const SliderNewBlock = ({ animated, title, description, type, anchorId, arrows = true, adaptive, autoplay: autoplayMs, dots = true, className, dotsClassName, disclaimer, children, blockClassName, arrowSize, slidesToShow, onSlideChange, onSlideChangeTransitionStart, onSlideChangeTransitionEnd, onActiveIndexChange, onBreakpoint, }) => {
|
|
18
|
-
const { childrenCount, breakpoints,
|
|
20
|
+
const { autoplay, isLocked, childrenCount, breakpoints, onSwiper, onPrev, onNext, setIsLocked } = (0, useSlider_1.useSlider)({
|
|
19
21
|
slidesToShow,
|
|
20
22
|
children,
|
|
21
23
|
type,
|
|
22
24
|
autoplayMs,
|
|
23
25
|
});
|
|
26
|
+
const isA11yControlHidden = Boolean(autoplay);
|
|
27
|
+
const controlTabIndex = isA11yControlHidden ? -1 : 0;
|
|
28
|
+
const paginationProps = (0, useSliderPagination_1.useSliderPagination)({
|
|
29
|
+
enabled: dots,
|
|
30
|
+
isA11yControlHidden,
|
|
31
|
+
controlTabIndex,
|
|
32
|
+
bulletClass: b('dot', dotsClassName),
|
|
33
|
+
bulletActiveClass: b('dot_active'),
|
|
34
|
+
paginationLabel: (0, i18n_1.i18n)('pagination-label'),
|
|
35
|
+
});
|
|
24
36
|
return (react_1.default.createElement("div", { className: b({
|
|
25
37
|
'one-slide': childrenCount === 1,
|
|
26
38
|
'only-arrows': !(title === null || title === void 0 ? void 0 : title.text) && !description && arrows,
|
|
@@ -30,14 +42,14 @@ const SliderNewBlock = ({ animated, title, description, type, anchorId, arrows =
|
|
|
30
42
|
anchorId && react_1.default.createElement(Anchor_1.default, { id: anchorId }),
|
|
31
43
|
react_1.default.createElement(Title_1.default, { title: title, subtitle: description, className: b('header', { 'no-description': !description }) }),
|
|
32
44
|
react_1.default.createElement(AnimateBlock_1.default, { className: b('animate-slides'), animate: animated },
|
|
33
|
-
react_1.default.createElement(react_2.Swiper, { className: b('slider', className), onSwiper: onSwiper,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}, speed: 1000, autoplay: autoplay, autoHeight: adaptive, initialSlide: 0, noSwiping: false, breakpoints: breakpoints, onSlideChange: onSlideChange, onSlideChangeTransitionStart: onSlideChangeTransitionStart, onSlideChangeTransitionEnd: onSlideChangeTransitionEnd, onActiveIndexChange: onActiveIndexChange, onBreakpoint: onBreakpoint, onLock: () => setIsLocked(true), onUnlock: () => setIsLocked(false), watchSlidesVisibility: true, watchOverflow: true }, react_1.default.Children.map(children, (elem, index) => (react_1.default.createElement(react_2.SwiperSlide, { className: b('slide'), key: index }, elem)))),
|
|
45
|
+
react_1.default.createElement(react_2.Swiper, Object.assign({ className: b('slider', className), onSwiper: onSwiper, speed: 1000, autoplay: autoplay, autoHeight: adaptive, initialSlide: 0, noSwiping: false, breakpoints: breakpoints, onSlideChange: onSlideChange, onSlideChangeTransitionStart: onSlideChangeTransitionStart, onSlideChangeTransitionEnd: onSlideChangeTransitionEnd, onActiveIndexChange: onActiveIndexChange, onBreakpoint: onBreakpoint, onLock: () => setIsLocked(true), onUnlock: () => setIsLocked(false), watchSlidesVisibility: true, watchOverflow: true, a11y: {
|
|
46
|
+
slideLabelMessage: '',
|
|
47
|
+
paginationBulletMessage: (0, i18n_1.i18n)('dot-label', { index: '{{index}}' }),
|
|
48
|
+
} }, paginationProps), react_1.default.Children.map(children, (elem, index) => (react_1.default.createElement(react_2.SwiperSlide, { className: b('slide'), key: index }, ({ isVisible }) => (react_1.default.createElement("div", { "aria-hidden": !isA11yControlHidden && !isVisible }, elem)))))),
|
|
38
49
|
arrows && !isLocked && (react_1.default.createElement(react_1.Fragment, null,
|
|
39
|
-
react_1.default.createElement(
|
|
40
|
-
|
|
50
|
+
react_1.default.createElement("div", { "aria-hidden": isA11yControlHidden },
|
|
51
|
+
react_1.default.createElement(Arrow_1.default, { className: b('arrow', { prev: true }), type: "left", onClick: onPrev, size: arrowSize, extraProps: { tabIndex: controlTabIndex } }),
|
|
52
|
+
react_1.default.createElement(Arrow_1.default, { className: b('arrow', { next: true }), type: "right", onClick: onNext, size: arrowSize, extraProps: { tabIndex: controlTabIndex } })))),
|
|
41
53
|
react_1.default.createElement("div", { className: b('footer') }, disclaimer ? (react_1.default.createElement("div", { className: b('disclaimer', { size: (disclaimer === null || disclaimer === void 0 ? void 0 : disclaimer.size) || 'm' }) }, disclaimer === null || disclaimer === void 0 ? void 0 : disclaimer.text)) : null))));
|
|
42
54
|
};
|
|
43
55
|
exports.SliderNewBlock = SliderNewBlock;
|
|
@@ -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,11 +1,12 @@
|
|
|
1
1
|
import React, { PropsWithChildren } from 'react';
|
|
2
2
|
import type { Swiper } from 'swiper';
|
|
3
3
|
import { SlidesToShow } from '../../models';
|
|
4
|
-
|
|
5
|
-
autoplayMs?: number
|
|
6
|
-
type?: string
|
|
7
|
-
slidesToShow?: SlidesToShow
|
|
8
|
-
}
|
|
4
|
+
type UseSliderProps = PropsWithChildren<{
|
|
5
|
+
autoplayMs?: number;
|
|
6
|
+
type?: string;
|
|
7
|
+
slidesToShow?: SlidesToShow;
|
|
8
|
+
}>;
|
|
9
|
+
export declare const useSlider: ({ children, autoplayMs, type, ...props }: UseSliderProps) => {
|
|
9
10
|
slider: Swiper | undefined;
|
|
10
11
|
onSwiper: React.Dispatch<React.SetStateAction<Swiper | undefined>>;
|
|
11
12
|
onNext: () => void;
|
|
@@ -15,7 +16,8 @@ export declare const useSlider: ({ children, autoplayMs, type, slidesToShow, }:
|
|
|
15
16
|
isLocked: boolean;
|
|
16
17
|
setIsLocked: React.Dispatch<React.SetStateAction<boolean>>;
|
|
17
18
|
autoplay: false | {
|
|
18
|
-
delay: number
|
|
19
|
+
delay: number;
|
|
19
20
|
disableOnInteraction: boolean;
|
|
20
21
|
};
|
|
21
22
|
};
|
|
23
|
+
export {};
|
|
@@ -5,11 +5,13 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const react_1 = tslib_1.__importStar(require("react"));
|
|
6
6
|
const models_1 = require("../../models");
|
|
7
7
|
const utils_1 = require("./utils");
|
|
8
|
-
const useSlider = (
|
|
8
|
+
const useSlider = (_a) => {
|
|
9
|
+
var { children, autoplayMs, type } = _a, props = tslib_1.__rest(_a, ["children", "autoplayMs", "type"]);
|
|
9
10
|
const [slider, setSlider] = (0, react_1.useState)();
|
|
10
11
|
const [isLocked, setIsLocked] = (0, react_1.useState)(false);
|
|
12
|
+
const slidesToShow = (0, utils_1.useMemoized)(props.slidesToShow);
|
|
11
13
|
const childrenCount = react_1.default.Children.count(children);
|
|
12
|
-
const autoplayEnabled =
|
|
14
|
+
const autoplayEnabled = autoplayMs !== undefined && autoplayMs > 0;
|
|
13
15
|
const breakpoints = (0, react_1.useMemo)(() => {
|
|
14
16
|
return (0, utils_1.getSliderResponsiveParams)({
|
|
15
17
|
contentLength: childrenCount,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Swiper as SwiperProps } from 'swiper/swiper-react';
|
|
2
|
+
export declare const useSliderPagination: (props: {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
isA11yControlHidden: boolean;
|
|
5
|
+
controlTabIndex: number;
|
|
6
|
+
bulletClass: string;
|
|
7
|
+
bulletActiveClass: string;
|
|
8
|
+
paginationLabel: string;
|
|
9
|
+
}) => Pick<SwiperProps, 'pagination' | 'onPaginationUpdate'> | undefined;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useSliderPagination = void 0;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
5
|
+
const useSliderPagination = (props) => {
|
|
6
|
+
if (!props.enabled) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const { isA11yControlHidden, controlTabIndex, bulletClass, bulletActiveClass, paginationLabel } = props;
|
|
10
|
+
return {
|
|
11
|
+
pagination: {
|
|
12
|
+
clickable: true,
|
|
13
|
+
bulletClass,
|
|
14
|
+
bulletActiveClass,
|
|
15
|
+
},
|
|
16
|
+
onPaginationUpdate: (slider) => {
|
|
17
|
+
const pagination = slider.pagination.el;
|
|
18
|
+
(0, utils_1.setElementAtrributes)(pagination, {
|
|
19
|
+
role: 'menu',
|
|
20
|
+
'aria-hidden': isA11yControlHidden,
|
|
21
|
+
'aria-label': paginationLabel,
|
|
22
|
+
});
|
|
23
|
+
const bullets = pagination.querySelectorAll(`.${bulletClass}`);
|
|
24
|
+
bullets.forEach((bullet) => {
|
|
25
|
+
const isActive = bullet.classList.contains(bulletActiveClass);
|
|
26
|
+
(0, utils_1.setElementAtrributes)(bullet, {
|
|
27
|
+
role: 'menuitemradio',
|
|
28
|
+
'aria-hidden': isA11yControlHidden,
|
|
29
|
+
'aria-checked': isActive,
|
|
30
|
+
tabindex: controlTabIndex,
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
exports.useSliderPagination = useSliderPagination;
|
|
@@ -12,3 +12,5 @@ export interface GetSlidesToShowParams {
|
|
|
12
12
|
mobileFullscreen?: boolean;
|
|
13
13
|
}
|
|
14
14
|
export declare function getSliderResponsiveParams({ contentLength, slidesToShow, mobileFullscreen, }: GetSlidesToShowParams): Record<number, SwiperOptions>;
|
|
15
|
+
export declare const useMemoized: <T>(value: T) => T;
|
|
16
|
+
export declare const setElementAtrributes: (element: Element, attributes: Record<string, unknown>) => void;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getSliderResponsiveParams = exports.DEFAULT_SLIDE_BREAKPOINTS = void 0;
|
|
3
|
+
exports.setElementAtrributes = exports.useMemoized = exports.getSliderResponsiveParams = exports.DEFAULT_SLIDE_BREAKPOINTS = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const isEqual_1 = tslib_1.__importDefault(require("lodash/isEqual"));
|
|
5
7
|
const pickBy_1 = tslib_1.__importDefault(require("lodash/pickBy"));
|
|
6
8
|
const constants_1 = require("../../constants");
|
|
7
9
|
const models_1 = require("./models");
|
|
@@ -29,3 +31,13 @@ function getSliderResponsiveParams({ contentLength, slidesToShow, mobileFullscre
|
|
|
29
31
|
}, {});
|
|
30
32
|
}
|
|
31
33
|
exports.getSliderResponsiveParams = getSliderResponsiveParams;
|
|
34
|
+
const useMemoized = (value) => {
|
|
35
|
+
const [memoizedValue, setMemoizedValue] = (0, react_1.useState)(value);
|
|
36
|
+
(0, react_1.useEffect)(() => {
|
|
37
|
+
setMemoizedValue((memoized) => value && typeof value === 'object' && (0, isEqual_1.default)(memoized, value) ? memoized : value);
|
|
38
|
+
}, [value]);
|
|
39
|
+
return memoizedValue;
|
|
40
|
+
};
|
|
41
|
+
exports.useMemoized = useMemoized;
|
|
42
|
+
const setElementAtrributes = (element, attributes) => Object.entries(attributes).forEach(([attribute, value]) => element.setAttribute(attribute, String(value)));
|
|
43
|
+
exports.setElementAtrributes = setElementAtrributes;
|