@gravity-ui/page-constructor 5.27.1-alpha.1 → 5.27.1-alpha.2
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/HeaderSlider/HeaderSlider.js +2 -2
- package/build/cjs/blocks/HeaderSlider/schema.js +1 -1
- package/build/cjs/blocks/Slider/Arrow/Arrow.css +22 -21
- package/build/cjs/blocks/Slider/Arrow/Arrow.d.ts +5 -2
- package/build/cjs/blocks/Slider/Arrow/Arrow.js +5 -4
- package/build/cjs/blocks/Slider/Slider.css +545 -329
- package/build/cjs/blocks/Slider/Slider.d.ts +6 -7
- package/build/cjs/blocks/Slider/Slider.js +40 -282
- package/build/cjs/blocks/Slider/i18n/en.json +1 -1
- package/build/cjs/blocks/Slider/i18n/ru.json +1 -1
- package/build/cjs/blocks/Slider/models.d.ts +2 -2
- package/build/cjs/blocks/Slider/models.js +1 -1
- package/build/cjs/blocks/Slider/schema.d.ts +18 -1
- package/build/cjs/blocks/Slider/schema.js +9 -0
- package/build/cjs/blocks/Slider/utils.d.ts +7 -27
- package/build/cjs/blocks/Slider/utils.js +26 -108
- package/build/cjs/blocks/{SliderNew → SliderOld}/Arrow/Arrow.css +24 -25
- package/build/cjs/blocks/SliderOld/Arrow/Arrow.d.ts +9 -0
- package/build/cjs/blocks/SliderOld/Arrow/Arrow.js +13 -0
- package/build/cjs/blocks/SliderOld/SliderOld.css +703 -0
- package/build/cjs/blocks/SliderOld/SliderOld.d.ts +17 -0
- package/build/cjs/blocks/SliderOld/SliderOld.js +301 -0
- package/build/cjs/blocks/{SliderNew → SliderOld}/i18n/en.json +1 -1
- package/build/cjs/blocks/{SliderNew → SliderOld}/i18n/index.js +1 -1
- package/build/cjs/blocks/{SliderNew → SliderOld}/i18n/ru.json +1 -1
- package/build/cjs/blocks/{SliderNew → SliderOld}/models.d.ts +2 -2
- package/build/cjs/blocks/{SliderNew → SliderOld}/models.js +1 -1
- package/build/cjs/blocks/{SliderNew → SliderOld}/schema.d.ts +5 -21
- package/build/cjs/blocks/{SliderNew → SliderOld}/schema.js +6 -14
- package/build/cjs/blocks/SliderOld/utils.d.ts +36 -0
- package/build/cjs/blocks/SliderOld/utils.js +125 -0
- package/build/cjs/blocks/index.d.ts +1 -0
- package/build/cjs/blocks/index.js +3 -1
- package/build/cjs/blocks/validators.d.ts +1 -0
- package/build/cjs/blocks/validators.js +1 -0
- package/build/cjs/components/FullscreenImage/FullscreenImage.css +3 -3
- package/build/cjs/components/FullscreenImage/FullscreenImage.js +2 -2
- package/build/cjs/components/Media/Image/Image.js +2 -2
- package/build/cjs/components/Title/TitleItem.d.ts +1 -1
- package/build/cjs/constructor-items.d.ts +2 -2
- package/build/cjs/constructor-items.js +2 -4
- package/build/cjs/editor/data/templates/{slider-new-block.json → slider-old-block.json} +2 -2
- package/build/cjs/grid/Col/Col.d.ts +1 -1
- package/build/cjs/grid/Row/Row.d.ts +1 -1
- package/build/cjs/models/constructor-items/blocks.d.ts +15 -14
- package/build/cjs/models/constructor-items/blocks.js +2 -2
- package/build/cjs/schema/constants.d.ts +0 -1
- package/build/cjs/schema/constants.js +3 -1
- package/build/cjs/schema/validators/blocks.d.ts +2 -2
- package/build/cjs/schema/validators/blocks.js +2 -2
- package/build/cjs/text-transform/config.js +1 -1
- package/build/esm/blocks/HeaderSlider/HeaderSlider.js +1 -1
- package/build/esm/blocks/HeaderSlider/schema.js +2 -2
- package/build/esm/blocks/Slider/Arrow/Arrow.css +22 -21
- package/build/esm/blocks/Slider/Arrow/Arrow.d.ts +5 -2
- package/build/esm/blocks/Slider/Arrow/Arrow.js +5 -4
- package/build/esm/blocks/Slider/Slider.css +545 -329
- package/build/esm/blocks/Slider/Slider.d.ts +6 -7
- package/build/esm/blocks/Slider/Slider.js +42 -284
- package/build/esm/blocks/Slider/i18n/en.json +1 -1
- package/build/esm/blocks/Slider/i18n/ru.json +1 -1
- package/build/esm/blocks/Slider/models.d.ts +2 -2
- package/build/esm/blocks/Slider/models.js +1 -1
- package/build/esm/blocks/Slider/schema.d.ts +18 -1
- package/build/esm/blocks/Slider/schema.js +9 -0
- package/build/esm/blocks/Slider/utils.d.ts +7 -27
- package/build/esm/blocks/Slider/utils.js +24 -103
- package/build/esm/blocks/{SliderNew → SliderOld}/Arrow/Arrow.css +24 -25
- package/build/esm/blocks/SliderOld/Arrow/Arrow.d.ts +10 -0
- package/build/esm/blocks/SliderOld/Arrow/Arrow.js +11 -0
- package/build/esm/blocks/SliderOld/SliderOld.css +703 -0
- package/build/esm/blocks/SliderOld/SliderOld.d.ts +18 -0
- package/build/esm/blocks/SliderOld/SliderOld.js +297 -0
- package/build/esm/blocks/{SliderNew → SliderOld}/i18n/en.json +1 -1
- package/build/esm/blocks/{SliderNew → SliderOld}/i18n/index.js +1 -1
- package/build/esm/blocks/{SliderNew → SliderOld}/i18n/ru.json +1 -1
- package/build/esm/blocks/{SliderNew → SliderOld}/models.d.ts +2 -2
- package/build/esm/blocks/{SliderNew → SliderOld}/models.js +1 -1
- package/build/esm/blocks/{SliderNew → SliderOld}/schema.d.ts +5 -21
- package/build/esm/blocks/{SliderNew → SliderOld}/schema.js +5 -13
- package/build/esm/blocks/SliderOld/utils.d.ts +36 -0
- package/build/esm/blocks/SliderOld/utils.js +115 -0
- package/build/esm/blocks/index.d.ts +1 -0
- package/build/esm/blocks/index.js +1 -0
- package/build/esm/blocks/validators.d.ts +1 -0
- package/build/esm/blocks/validators.js +1 -0
- package/build/esm/components/FullscreenImage/FullscreenImage.css +3 -3
- package/build/esm/components/FullscreenImage/FullscreenImage.js +1 -1
- package/build/esm/components/Media/Image/Image.js +1 -1
- package/build/esm/components/Title/TitleItem.d.ts +1 -1
- package/build/esm/constructor-items.d.ts +2 -2
- package/build/esm/constructor-items.js +3 -5
- package/build/esm/editor/data/templates/{slider-new-block.json → slider-old-block.json} +2 -2
- package/build/esm/grid/Col/Col.d.ts +1 -1
- package/build/esm/grid/Row/Row.d.ts +1 -1
- package/build/esm/models/constructor-items/blocks.d.ts +15 -14
- package/build/esm/models/constructor-items/blocks.js +2 -2
- package/build/esm/schema/constants.d.ts +0 -1
- package/build/esm/schema/constants.js +4 -2
- package/build/esm/schema/validators/blocks.d.ts +2 -2
- package/build/esm/schema/validators/blocks.js +2 -2
- package/build/esm/text-transform/config.js +1 -1
- package/package.json +2 -2
- package/server/models/constructor-items/blocks.d.ts +15 -14
- package/server/models/constructor-items/blocks.js +2 -2
- package/server/text-transform/config.js +1 -1
- package/widget/index.js +1 -1
- package/build/cjs/blocks/SliderNew/Arrow/Arrow.d.ts +0 -12
- package/build/cjs/blocks/SliderNew/Arrow/Arrow.js +0 -14
- package/build/cjs/blocks/SliderNew/Slider.css +0 -919
- package/build/cjs/blocks/SliderNew/Slider.d.ts +0 -14
- package/build/cjs/blocks/SliderNew/Slider.js +0 -57
- package/build/cjs/blocks/SliderNew/utils.d.ts +0 -16
- package/build/cjs/blocks/SliderNew/utils.js +0 -43
- package/build/cjs/blocks/unstable.d.ts +0 -1
- package/build/cjs/blocks/unstable.js +0 -8
- package/build/esm/blocks/SliderNew/Arrow/Arrow.d.ts +0 -13
- package/build/esm/blocks/SliderNew/Arrow/Arrow.js +0 -12
- package/build/esm/blocks/SliderNew/Slider.css +0 -919
- package/build/esm/blocks/SliderNew/Slider.d.ts +0 -15
- package/build/esm/blocks/SliderNew/Slider.js +0 -53
- package/build/esm/blocks/SliderNew/utils.d.ts +0 -16
- package/build/esm/blocks/SliderNew/utils.js +0 -36
- package/build/esm/blocks/unstable.d.ts +0 -1
- package/build/esm/blocks/unstable.js +0 -1
- /package/build/cjs/blocks/{SliderNew → Slider}/useSlider.d.ts +0 -0
- /package/build/cjs/blocks/{SliderNew → Slider}/useSlider.js +0 -0
- /package/build/cjs/blocks/{SliderNew → Slider}/useSliderPagination.d.ts +0 -0
- /package/build/cjs/blocks/{SliderNew → Slider}/useSliderPagination.js +0 -0
- /package/build/cjs/blocks/{SliderNew → SliderOld}/i18n/index.d.ts +0 -0
- /package/build/cjs/blocks/{Slider → SliderOld}/slick.css +0 -0
- /package/build/esm/blocks/{SliderNew → Slider}/useSlider.d.ts +0 -0
- /package/build/esm/blocks/{SliderNew → Slider}/useSlider.js +0 -0
- /package/build/esm/blocks/{SliderNew → Slider}/useSliderPagination.d.ts +0 -0
- /package/build/esm/blocks/{SliderNew → Slider}/useSliderPagination.js +0 -0
- /package/build/esm/blocks/{SliderNew → SliderOld}/i18n/index.d.ts +0 -0
- /package/build/esm/blocks/{Slider → SliderOld}/slick.css +0 -0
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { PropsWithChildren } from 'react';
|
|
2
|
+
import { Swiper } from 'swiper/react';
|
|
3
3
|
import { ClassNameProps, Refable, SliderProps as SliderParams } from '../../models';
|
|
4
4
|
import './Slider.css';
|
|
5
|
-
|
|
5
|
+
import 'swiper/swiper-bundle.css';
|
|
6
|
+
export interface SliderProps extends Omit<SliderParams, 'children'>, Partial<Pick<Swiper, 'onSlideChange' | 'onSlideChangeTransitionStart' | 'onSlideChangeTransitionEnd' | 'onActiveIndexChange' | 'onBreakpoint'>>, Refable<HTMLDivElement>, ClassNameProps {
|
|
6
7
|
type?: string;
|
|
7
8
|
anchorId?: string;
|
|
8
|
-
onAfterChange?: (index: number) => void;
|
|
9
|
-
onBeforeChange?: (current: number, next: number) => void;
|
|
10
9
|
dotsClassName?: string;
|
|
11
10
|
blockClassName?: string;
|
|
12
11
|
arrowSize?: number;
|
|
13
|
-
|
|
12
|
+
initialSlide?: number;
|
|
14
13
|
}
|
|
15
|
-
export declare const SliderBlock: (
|
|
14
|
+
export declare const SliderBlock: ({ animated, title, description, type, anchorId, arrows, adaptive, autoplay: autoplayMs, dots, initialSlide, className, dotsClassName, disclaimer, children, blockClassName, arrowSize, slidesToShow, onSlideChange, onSlideChangeTransitionStart, onSlideChangeTransitionEnd, onActiveIndexChange, onBreakpoint, }: PropsWithChildren<SliderProps>) => JSX.Element;
|
|
16
15
|
export default SliderBlock;
|
|
@@ -1,295 +1,53 @@
|
|
|
1
|
-
import React, { Fragment
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import get from 'lodash/get';
|
|
5
|
-
import noop from 'lodash/noop';
|
|
6
|
-
import SlickSlider from 'react-slick';
|
|
1
|
+
import React, { Fragment } from 'react';
|
|
2
|
+
import SwiperCore, { A11y, Autoplay, Pagination } from 'swiper';
|
|
3
|
+
import { Swiper, SwiperSlide } from 'swiper/react';
|
|
7
4
|
import Anchor from '../../components/Anchor/Anchor';
|
|
8
5
|
import AnimateBlock from '../../components/AnimateBlock/AnimateBlock';
|
|
9
|
-
import OutsideClick from '../../components/OutsideClick/OutsideClick';
|
|
10
6
|
import Title from '../../components/Title/Title';
|
|
11
|
-
import {
|
|
12
|
-
import { MobileContext } from '../../context/mobileContext';
|
|
13
|
-
import { SSRContext } from '../../context/ssrContext';
|
|
14
|
-
import { StylesContext } from '../../context/stylesContext/StylesContext';
|
|
15
|
-
import useFocus from '../../hooks/useFocus';
|
|
16
|
-
import { SliderType, } from '../../models';
|
|
7
|
+
import { SliderType } from '../../models';
|
|
17
8
|
import { block } from '../../utils';
|
|
18
9
|
import Arrow from './Arrow/Arrow';
|
|
19
10
|
import { i18n } from './i18n';
|
|
20
|
-
import {
|
|
11
|
+
import { useSlider } from './useSlider';
|
|
12
|
+
import { useSliderPagination } from './useSliderPagination';
|
|
21
13
|
import './Slider.css';
|
|
14
|
+
import 'swiper/swiper-bundle.css';
|
|
22
15
|
const b = block('SliderBlock');
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const isMobile = useContext(MobileContext);
|
|
31
|
-
const [breakpoint, setBreakpoint] = useState(BREAKPOINTS.xl);
|
|
32
|
-
const sliderId = useUniqId();
|
|
33
|
-
const disclosedChildren = useMemo(() => discloseAllNestedChildren(children, sliderId), [children, sliderId]);
|
|
34
|
-
const childrenCount = disclosedChildren.length;
|
|
35
|
-
const isAutoplayEnabled = autoplaySpeed !== undefined && autoplaySpeed > 0;
|
|
36
|
-
const isUserInteractionRef = useRef(false);
|
|
37
|
-
const [slidesToShow] = useState(getSlidesToShowWithDefaults({
|
|
38
|
-
contentLength: childrenCount,
|
|
39
|
-
breakpoints: props.slidesToShow,
|
|
40
|
-
mobileFullscreen: Boolean(props.type && Object.values(SliderType).includes(props.type)),
|
|
41
|
-
}));
|
|
42
|
-
const slidesToShowCount = getSlidesToShowCount(slidesToShow);
|
|
43
|
-
const slidesCountByBreakpoint = getSlidesCountByBreakpoint(breakpoint, slidesToShow);
|
|
44
|
-
const [currentIndex, setCurrentIndex] = useState(initialIndex);
|
|
45
|
-
const [childStyles, setChildStyles] = useState({});
|
|
46
|
-
const [slider, setSlider] = useState();
|
|
47
|
-
const prevIndexRef = useRef(0);
|
|
48
|
-
const autoplayTimeId = useRef();
|
|
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
|
-
};
|
|
54
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
55
|
-
const onResize = useCallback(debounce(() => {
|
|
56
|
-
if (!slider) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
const newBreakpoint = get(slider, 'state.breakpoint') || BREAKPOINTS.xl;
|
|
60
|
-
if (newBreakpoint !== breakpoint) {
|
|
61
|
-
setBreakpoint(newBreakpoint);
|
|
62
|
-
setCurrentIndex(0);
|
|
63
|
-
slider.slickGoTo(0);
|
|
64
|
-
}
|
|
65
|
-
}, 100), [slider, breakpoint]);
|
|
66
|
-
const scrollLastSlide = useCallback((current) => {
|
|
67
|
-
const lastSlide = childrenCount - slidesToShowCount;
|
|
68
|
-
if (isAutoplayEnabled && lastSlide === current) {
|
|
69
|
-
// Slick doesn't support autoplay with no infinity scroll
|
|
70
|
-
autoplayTimeId.current = setTimeout(() => {
|
|
71
|
-
if (slider) {
|
|
72
|
-
slider.slickGoTo(0, false);
|
|
73
|
-
slider.slickPause();
|
|
74
|
-
}
|
|
75
|
-
setTimeout(() => {
|
|
76
|
-
if (slider) {
|
|
77
|
-
slider.slickPlay();
|
|
78
|
-
}
|
|
79
|
-
}, 500);
|
|
80
|
-
}, autoplaySpeed);
|
|
81
|
-
}
|
|
82
|
-
}, [autoplaySpeed, childrenCount, isAutoplayEnabled, slider, slidesToShowCount]);
|
|
83
|
-
useEffect(() => {
|
|
84
|
-
if (hasFocus && autoplayTimeId.current) {
|
|
85
|
-
clearTimeout(autoplayTimeId.current);
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
scrollLastSlide(currentIndex);
|
|
89
|
-
}
|
|
90
|
-
}, [currentIndex, hasFocus, scrollLastSlide]);
|
|
91
|
-
useEffect(() => {
|
|
92
|
-
onResize();
|
|
93
|
-
window.addEventListener('resize', onResize, { passive: true });
|
|
94
|
-
return () => window.removeEventListener('resize', onResize);
|
|
95
|
-
}, [onResize]);
|
|
96
|
-
const handleArrowClick = (direction) => {
|
|
97
|
-
let nextIndex;
|
|
98
|
-
if (direction === 'right') {
|
|
99
|
-
nextIndex =
|
|
100
|
-
currentIndex === childrenCount - slidesCountByBreakpoint ? 0 : currentIndex + 1;
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
nextIndex =
|
|
104
|
-
currentIndex === 0 ? childrenCount - slidesCountByBreakpoint : currentIndex - 1;
|
|
105
|
-
}
|
|
106
|
-
if (slider) {
|
|
107
|
-
slider.slickGoTo(nextIndex);
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
const onBeforeChange = useCallback((current, next) => {
|
|
111
|
-
if (handleBeforeChange) {
|
|
112
|
-
handleBeforeChange(current, next);
|
|
113
|
-
}
|
|
114
|
-
prevIndexRef.current = current;
|
|
115
|
-
setCurrentIndex(Math.ceil(next));
|
|
116
|
-
}, [handleBeforeChange]);
|
|
117
|
-
const onAfterChange = useCallback((current) => {
|
|
118
|
-
if (handleAfterChange) {
|
|
119
|
-
handleAfterChange(current);
|
|
120
|
-
}
|
|
121
|
-
if (autoplayTimeId.current) {
|
|
122
|
-
clearTimeout(autoplayTimeId.current);
|
|
123
|
-
}
|
|
124
|
-
if (!hasFocus) {
|
|
125
|
-
scrollLastSlide(current);
|
|
126
|
-
}
|
|
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) => {
|
|
140
|
-
const nextIndex = index > currentIndex ? index + 1 - slidesCountByBreakpoint : index;
|
|
141
|
-
if (slider) {
|
|
142
|
-
slider.slickGoTo(nextIndex);
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
const barSlidesCount = childrenCount - slidesCountByBreakpoint + 1;
|
|
146
|
-
const barPosition = (DOT_GAP + DOT_WIDTH) * currentIndex;
|
|
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,
|
|
16
|
+
SwiperCore.use([Autoplay, A11y, Pagination]);
|
|
17
|
+
export const SliderBlock = ({ animated, title, description, type, anchorId, arrows = true, adaptive, autoplay: autoplayMs, dots = true, initialSlide = 0, className, dotsClassName, disclaimer, children, blockClassName, arrowSize, slidesToShow, onSlideChange, onSlideChangeTransitionStart, onSlideChangeTransitionEnd, onActiveIndexChange, onBreakpoint, }) => {
|
|
18
|
+
const { autoplay, isLocked, childrenCount, breakpoints, onSwiper, onPrev, onNext, setIsLocked } = useSlider({
|
|
19
|
+
slidesToShow,
|
|
20
|
+
children,
|
|
21
|
+
type,
|
|
22
|
+
autoplayMs,
|
|
153
23
|
});
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
currentSlideNumber = index + 1;
|
|
183
|
-
}
|
|
184
|
-
return currentSlideNumber;
|
|
185
|
-
};
|
|
186
|
-
const isVisibleSlide = (index) => {
|
|
187
|
-
const currentIndexDiff = index - currentIndex;
|
|
188
|
-
const result = slidesCountByBreakpoint > 0 &&
|
|
189
|
-
0 <= currentIndexDiff &&
|
|
190
|
-
currentIndexDiff < slidesCountByBreakpoint;
|
|
191
|
-
return result;
|
|
192
|
-
};
|
|
193
|
-
const renderDot = (index) => {
|
|
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)));
|
|
206
|
-
};
|
|
207
|
-
const renderNavigation = () => {
|
|
208
|
-
if (childrenCount <= slidesCountByBreakpoint || !dots || childrenCount === 1) {
|
|
209
|
-
return null;
|
|
210
|
-
}
|
|
211
|
-
const dotsList = Array(childrenCount)
|
|
212
|
-
.fill(null)
|
|
213
|
-
.map((_item, index) => renderDot(index));
|
|
214
|
-
dotsList.splice(currentIndex, 0, renderAccessibleBar(currentIndex));
|
|
215
|
-
return (React.createElement("div", { className: b('dots', dotsClassName) },
|
|
216
|
-
React.createElement("ul", Object.assign({ className: b('dots-list'), role: "menu", "aria-label": i18n('pagination-label') }, rovingListProps),
|
|
217
|
-
renderBar(),
|
|
218
|
-
dotsList)));
|
|
219
|
-
};
|
|
220
|
-
const renderDisclaimer = () => {
|
|
221
|
-
return disclaimer ? (React.createElement("div", { className: b('disclaimer', { size: disclaimer.size || 'm' }) }, disclaimer.text)) : null;
|
|
222
|
-
};
|
|
223
|
-
const renderSlider = () => {
|
|
224
|
-
/* Disable adding of width in inline styles when SSR to prevent overriding of default styles */
|
|
225
|
-
/* Calculate appropriate breakpoint for mobile devices with user agent */
|
|
226
|
-
const variableWidth = isServer && isMobile;
|
|
227
|
-
const settings = {
|
|
228
|
-
ref: (slickSlider) => setSlider(slickSlider),
|
|
229
|
-
className: slick(null, className),
|
|
230
|
-
arrows,
|
|
231
|
-
variableWidth,
|
|
232
|
-
infinite: false,
|
|
233
|
-
speed: 1000,
|
|
234
|
-
adaptiveHeight: adaptive,
|
|
235
|
-
autoplay: isAutoplayEnabled,
|
|
236
|
-
autoplaySpeed,
|
|
237
|
-
slidesToShow: slidesToShowCount,
|
|
238
|
-
slidesToScroll: 1,
|
|
239
|
-
responsive: getSliderResponsiveParams(slidesToShow),
|
|
240
|
-
beforeChange: onBeforeChange,
|
|
241
|
-
afterChange: onAfterChange,
|
|
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 })),
|
|
245
|
-
lazyLoad,
|
|
246
|
-
accessibility: false,
|
|
247
|
-
};
|
|
248
|
-
return (React.createElement(OutsideClick, { onOutsideClick: isMobile ? unsetFocus : noop },
|
|
249
|
-
React.createElement(SlickSlider, Object.assign({}, settings), disclosedChildren),
|
|
250
|
-
React.createElement("div", { className: b('footer') },
|
|
251
|
-
renderDisclaimer(),
|
|
252
|
-
renderNavigation())));
|
|
253
|
-
};
|
|
254
|
-
return (React.createElement(StylesContext.Provider, { value: Object.assign(Object.assign({}, childStyles), { setStyles: setChildStyles }) },
|
|
255
|
-
React.createElement("div", { className: b({
|
|
256
|
-
'align-left': childrenCount < slidesCountByBreakpoint,
|
|
257
|
-
'one-slide': childrenCount === 1,
|
|
258
|
-
'only-arrows': !(title === null || title === void 0 ? void 0 : title.text) && !description && arrows,
|
|
259
|
-
mobile: isMobile,
|
|
260
|
-
type,
|
|
261
|
-
}, blockClassName) },
|
|
262
|
-
anchorId && React.createElement(Anchor, { id: anchorId }),
|
|
263
|
-
React.createElement(Title, { title: title, subtitle: description, className: b('header', { 'no-description': !description }) }),
|
|
264
|
-
React.createElement(AnimateBlock, { className: b('animate-slides'), animate: animated }, renderSlider()))));
|
|
24
|
+
const isA11yControlHidden = Boolean(autoplay);
|
|
25
|
+
const controlTabIndex = isA11yControlHidden ? -1 : 0;
|
|
26
|
+
const paginationProps = useSliderPagination({
|
|
27
|
+
enabled: dots,
|
|
28
|
+
isA11yControlHidden,
|
|
29
|
+
controlTabIndex,
|
|
30
|
+
bulletClass: b('dot', dotsClassName),
|
|
31
|
+
bulletActiveClass: b('dot_active'),
|
|
32
|
+
paginationLabel: i18n('pagination-label'),
|
|
33
|
+
});
|
|
34
|
+
return (React.createElement("div", { className: b({
|
|
35
|
+
'one-slide': childrenCount === 1,
|
|
36
|
+
'only-arrows': !(title === null || title === void 0 ? void 0 : title.text) && !description && arrows,
|
|
37
|
+
'without-dots': !dots || isLocked,
|
|
38
|
+
type,
|
|
39
|
+
}, blockClassName) },
|
|
40
|
+
anchorId && React.createElement(Anchor, { id: anchorId }),
|
|
41
|
+
React.createElement(Title, { title: title, subtitle: description, className: b('header', { 'no-description': !description }) }),
|
|
42
|
+
React.createElement(AnimateBlock, { className: b('animate-slides'), animate: animated },
|
|
43
|
+
React.createElement(Swiper, Object.assign({ className: b('slider', className), onSwiper: onSwiper, speed: 1000, autoplay: autoplay, autoHeight: adaptive, initialSlide: initialSlide, 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: {
|
|
44
|
+
slideLabelMessage: '',
|
|
45
|
+
paginationBulletMessage: i18n('dot-label', { index: '{{index}}' }),
|
|
46
|
+
} }, paginationProps), React.Children.map(children, (elem, index) => (React.createElement(SwiperSlide, { className: b('slide'), key: index }, ({ isVisible }) => (React.createElement("div", { className: b('slide-item'), "aria-hidden": !isA11yControlHidden && !isVisible }, elem)))))),
|
|
47
|
+
arrows && !isLocked && (React.createElement(Fragment, null,
|
|
48
|
+
React.createElement("div", { "aria-hidden": isA11yControlHidden },
|
|
49
|
+
React.createElement(Arrow, { className: b('arrow', { prev: true }), type: "left", transparent: type === SliderType.HeaderCard, onClick: onPrev, size: arrowSize, extraProps: { tabIndex: controlTabIndex } }),
|
|
50
|
+
React.createElement(Arrow, { className: b('arrow', { next: true }), type: "right", transparent: type === SliderType.HeaderCard, onClick: onNext, size: arrowSize, extraProps: { tabIndex: controlTabIndex } })))),
|
|
51
|
+
React.createElement("div", { className: b('footer') }, disclaimer ? (React.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))));
|
|
265
52
|
};
|
|
266
|
-
function getSlideId(sliderId, index) {
|
|
267
|
-
return `slider-${sliderId}-child-${index}`;
|
|
268
|
-
}
|
|
269
|
-
// TODO remove this and rework PriceDetailed CLOUDFRONT-12230
|
|
270
|
-
function discloseAllNestedChildren(children, sliderId) {
|
|
271
|
-
if (!children) {
|
|
272
|
-
return [];
|
|
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
|
-
};
|
|
279
|
-
return React.Children.map(children, (child) => {
|
|
280
|
-
var _a;
|
|
281
|
-
if (child) {
|
|
282
|
-
// TODO: if child has 'items' then 'items' determinate like nested children for Slider.
|
|
283
|
-
const nestedChildren = (_a = child.props.data) === null || _a === void 0 ? void 0 : _a.items;
|
|
284
|
-
if (nestedChildren) {
|
|
285
|
-
return nestedChildren.map((nestedChild) => {
|
|
286
|
-
return wrapped(React.cloneElement(child, {
|
|
287
|
-
data: Object.assign(Object.assign({}, child.props.data), { items: [nestedChild] }),
|
|
288
|
-
}));
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
return child && wrapped(child);
|
|
293
|
-
}).filter(Boolean);
|
|
294
|
-
}
|
|
295
53
|
export default SliderBlock;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export declare enum SliderBreakpointNames {
|
|
2
|
+
Xs = "xs",
|
|
2
3
|
Sm = "sm",
|
|
3
4
|
Md = "md",
|
|
4
|
-
Lg = "lg"
|
|
5
|
-
Xl = "xl"
|
|
5
|
+
Lg = "lg"
|
|
6
6
|
}
|
|
7
7
|
export type SliderBreakpointParams = Record<SliderBreakpointNames, number>;
|
|
8
8
|
export type SlidesToShow = Partial<SliderBreakpointParams> | number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export var SliderBreakpointNames;
|
|
2
2
|
(function (SliderBreakpointNames) {
|
|
3
|
+
SliderBreakpointNames["Xs"] = "xs";
|
|
3
4
|
SliderBreakpointNames["Sm"] = "sm";
|
|
4
5
|
SliderBreakpointNames["Md"] = "md";
|
|
5
6
|
SliderBreakpointNames["Lg"] = "lg";
|
|
6
|
-
SliderBreakpointNames["Xl"] = "xl";
|
|
7
7
|
})(SliderBreakpointNames || (SliderBreakpointNames = {}));
|
|
@@ -11,6 +11,15 @@ export declare const SliderProps: {
|
|
|
11
11
|
autoplay: {
|
|
12
12
|
type: string;
|
|
13
13
|
};
|
|
14
|
+
type: {
|
|
15
|
+
type: string;
|
|
16
|
+
};
|
|
17
|
+
adaptive: {
|
|
18
|
+
type: string;
|
|
19
|
+
};
|
|
20
|
+
arrowSize: {
|
|
21
|
+
type: string;
|
|
22
|
+
};
|
|
14
23
|
animated: {
|
|
15
24
|
animated: {
|
|
16
25
|
type: string;
|
|
@@ -126,6 +135,15 @@ export declare const SliderBlock: {
|
|
|
126
135
|
autoplay: {
|
|
127
136
|
type: string;
|
|
128
137
|
};
|
|
138
|
+
type: {
|
|
139
|
+
type: string;
|
|
140
|
+
};
|
|
141
|
+
adaptive: {
|
|
142
|
+
type: string;
|
|
143
|
+
};
|
|
144
|
+
arrowSize: {
|
|
145
|
+
type: string;
|
|
146
|
+
};
|
|
129
147
|
animated: {
|
|
130
148
|
animated: {
|
|
131
149
|
type: string;
|
|
@@ -227,7 +245,6 @@ export declare const SliderBlock: {
|
|
|
227
245
|
};
|
|
228
246
|
};
|
|
229
247
|
};
|
|
230
|
-
type: {};
|
|
231
248
|
when: {
|
|
232
249
|
type: string;
|
|
233
250
|
};
|
|
@@ -53,6 +53,15 @@ export const SliderProps = {
|
|
|
53
53
|
autoplay: {
|
|
54
54
|
type: 'number',
|
|
55
55
|
},
|
|
56
|
+
type: {
|
|
57
|
+
type: 'string',
|
|
58
|
+
},
|
|
59
|
+
adaptive: {
|
|
60
|
+
type: 'boolean',
|
|
61
|
+
},
|
|
62
|
+
arrowSize: {
|
|
63
|
+
type: 'number',
|
|
64
|
+
},
|
|
56
65
|
animated: AnimatableProps,
|
|
57
66
|
slidesToShow: sliderSizesObject,
|
|
58
67
|
disclaimer: DisclaimerProps,
|
|
@@ -1,36 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SwiperOptions } from 'swiper/types/swiper-options';
|
|
2
|
+
import { SlidesToShow } from './models';
|
|
2
3
|
export declare const DEFAULT_SLIDE_BREAKPOINTS: {
|
|
3
|
-
xl: number;
|
|
4
4
|
lg: number;
|
|
5
5
|
md: number;
|
|
6
6
|
sm: number;
|
|
7
|
+
xs: number;
|
|
7
8
|
};
|
|
8
9
|
export interface GetSlidesToShowParams {
|
|
9
10
|
contentLength: number;
|
|
10
|
-
|
|
11
|
+
slidesToShow?: SlidesToShow;
|
|
11
12
|
mobileFullscreen?: boolean;
|
|
12
13
|
}
|
|
13
|
-
export declare
|
|
14
|
-
export declare
|
|
15
|
-
|
|
16
|
-
xl: number;
|
|
17
|
-
lg: number;
|
|
18
|
-
md: number;
|
|
19
|
-
};
|
|
20
|
-
export declare function getSliderResponsiveParams(breakpoints: SliderBreakpointParams): {
|
|
21
|
-
breakpoint: number;
|
|
22
|
-
settings: {
|
|
23
|
-
slidesToShow: number;
|
|
24
|
-
};
|
|
25
|
-
}[];
|
|
26
|
-
export declare function getSlidesCountByBreakpoint(breakpoint: number, breakpoints: SliderBreakpointParams): number;
|
|
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
|
-
};
|
|
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,115 +1,36 @@
|
|
|
1
|
-
import { useEffect,
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import isEqual from 'lodash/isEqual';
|
|
2
3
|
import pickBy from 'lodash/pickBy';
|
|
3
4
|
import { BREAKPOINTS } from '../../constants';
|
|
4
5
|
import { SliderBreakpointNames } from './models';
|
|
5
6
|
export const DEFAULT_SLIDE_BREAKPOINTS = {
|
|
6
|
-
[SliderBreakpointNames.
|
|
7
|
-
[SliderBreakpointNames.Lg]: 2,
|
|
7
|
+
[SliderBreakpointNames.Lg]: 3,
|
|
8
8
|
[SliderBreakpointNames.Md]: 2,
|
|
9
|
-
[SliderBreakpointNames.Sm]:
|
|
9
|
+
[SliderBreakpointNames.Sm]: 2,
|
|
10
|
+
[SliderBreakpointNames.Xs]: 1.15,
|
|
10
11
|
};
|
|
11
|
-
|
|
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
|
-
};
|
|
43
|
-
export function getSlidesToShowWithDefaults({ contentLength, breakpoints, mobileFullscreen, }) {
|
|
12
|
+
export function getSliderResponsiveParams({ contentLength, slidesToShow, mobileFullscreen, }) {
|
|
44
13
|
let result;
|
|
45
|
-
if (typeof
|
|
46
|
-
result = Object.keys(DEFAULT_SLIDE_BREAKPOINTS).reduce((acc, breakpointName) => (Object.assign(Object.assign({}, acc), { [breakpointName]:
|
|
14
|
+
if (typeof slidesToShow === 'number') {
|
|
15
|
+
result = Object.keys(DEFAULT_SLIDE_BREAKPOINTS).reduce((acc, breakpointName) => (Object.assign(Object.assign({}, acc), { [breakpointName]: slidesToShow })), {});
|
|
47
16
|
}
|
|
48
17
|
else {
|
|
49
|
-
result =
|
|
18
|
+
result = slidesToShow || DEFAULT_SLIDE_BREAKPOINTS;
|
|
50
19
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
settings: { slidesToShow },
|
|
57
|
-
}));
|
|
58
|
-
}
|
|
59
|
-
export function getSlidesCountByBreakpoint(breakpoint, breakpoints) {
|
|
60
|
-
const breakpointName = BREAKPOINT_NAMES_BY_VALUES[breakpoint];
|
|
61
|
-
return Math.floor(breakpoints[breakpointName]);
|
|
62
|
-
}
|
|
63
|
-
export function getSlidesToShowCount(breakpoints) {
|
|
64
|
-
return Math.floor(Math.max(...Object.values(breakpoints)));
|
|
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
|
-
},
|
|
20
|
+
const showCount = Object.assign(Object.assign(Object.assign({}, DEFAULT_SLIDE_BREAKPOINTS), pickBy(result, (value) => !isNaN(value))), { xs: !mobileFullscreen && contentLength > 1 ? DEFAULT_SLIDE_BREAKPOINTS.xs : 1 });
|
|
21
|
+
return Object.entries(showCount).reduce((res, [breakpointName, value]) => {
|
|
22
|
+
// eslint-disable-next-line no-param-reassign
|
|
23
|
+
res[BREAKPOINTS[breakpointName] + 1] = {
|
|
24
|
+
slidesPerView: value,
|
|
80
25
|
};
|
|
81
|
-
|
|
82
|
-
|
|
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 };
|
|
26
|
+
return res;
|
|
27
|
+
}, {});
|
|
115
28
|
}
|
|
29
|
+
export const useMemoized = (value) => {
|
|
30
|
+
const [memoizedValue, setMemoizedValue] = useState(value);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
setMemoizedValue((memoized) => value && typeof value === 'object' && isEqual(memoized, value) ? memoized : value);
|
|
33
|
+
}, [value]);
|
|
34
|
+
return memoizedValue;
|
|
35
|
+
};
|
|
36
|
+
export const setElementAtrributes = (element, attributes) => Object.entries(attributes).forEach(([attribute, value]) => element.setAttribute(attribute, String(value)));
|