@via-profit/ui-kit 0.5.0 → 0.7.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.
Files changed (311) hide show
  1. package/Accordion/AccordionActions.d.ts +1 -1
  2. package/Accordion/AccordionActions.js +2 -1
  3. package/Accordion/AccordionContainer.d.ts +1 -1
  4. package/Accordion/AccordionContainer.js +2 -14
  5. package/Accordion/AccordionContent.d.ts +1 -1
  6. package/Accordion/AccordionContent.js +5 -1
  7. package/Accordion/AccordionHeader.d.ts +2 -2
  8. package/Accordion/AccordionHeader.js +6 -2
  9. package/Accordion/index.d.ts +9 -9
  10. package/Accordion/index.js +18 -9
  11. package/Autocomplete/AutocompleteContainer.d.ts +2 -2
  12. package/Autocomplete/AutocompleteContainer.js +17 -7
  13. package/Autocomplete/AutocompleteItem.js +2 -1
  14. package/Autocomplete/context.d.ts +3 -3
  15. package/Autocomplete/index.d.ts +2 -2
  16. package/Avatar/AvatarContainer.d.ts +2 -2
  17. package/Avatar/AvatarPicture.d.ts +2 -2
  18. package/Avatar/index.js +1 -2
  19. package/Badge/BadgeBase.d.ts +1 -1
  20. package/Badge/BadgeContainer.d.ts +1 -1
  21. package/Badge/BadgeDeleteButton.d.ts +2 -2
  22. package/Badge/BadgeStyled.js +1 -1
  23. package/Badge/index.d.ts +1 -1
  24. package/Badge/index.js +1 -2
  25. package/Button/ButtonBase.d.ts +2 -2
  26. package/Button/ButtonContainer.d.ts +4 -4
  27. package/Button/ButtonContainer.js +22 -5
  28. package/Button/ButtonIconWrapper.d.ts +1 -1
  29. package/Button/ButtonStyled.d.ts +1 -1
  30. package/Button/ButtonTextWrapper.d.ts +1 -1
  31. package/Button/buttonVariants.d.ts +1 -1
  32. package/Button/buttonVariants.js +3 -2
  33. package/Button/index.d.ts +1 -1
  34. package/Button/index.js +1 -20
  35. package/ButtonGroup/index.js +4 -3
  36. package/CHANGELOG.md +49 -1
  37. package/Calendar/CalendarComponent.js +18 -9
  38. package/Calendar/CalendarControlButton.d.ts +2 -2
  39. package/Calendar/CalendarPaper.js +1 -1
  40. package/Calendar/CalendarWeekDayLabel.d.ts +2 -2
  41. package/Calendar/CalendarWeekRowButton.d.ts +1 -1
  42. package/Calendar/IconChevronDown.js +17 -7
  43. package/Calendar/index.d.ts +1 -1
  44. package/Calendar/index.js +17 -7
  45. package/Checkbox/CheckboxAsterisk.js +17 -7
  46. package/Checkbox/CheckboxBox.d.ts +2 -2
  47. package/Checkbox/CheckboxBox.js +1 -1
  48. package/Checkbox/CheckboxContainer.d.ts +2 -2
  49. package/Checkbox/CheckboxErrorText.js +17 -7
  50. package/Checkbox/index.d.ts +10 -10
  51. package/Checkbox/index.js +1 -2
  52. package/Checkbox/useCheckboxColor.d.ts +1 -1
  53. package/ClickOutside/index.js +5 -2
  54. package/DatePicker/index.d.ts +1 -1
  55. package/ErrorBoundary/RenderError.js +17 -7
  56. package/ErrorBoundary/index.d.ts +1 -1
  57. package/Grid/index.d.ts +17 -5
  58. package/Grid/index.js +7 -2
  59. package/Highlighted/index.js +1 -2
  60. package/LoadingIndicator/LoadingOverlay.js +17 -7
  61. package/LoadingIndicator/Spinner.js +17 -7
  62. package/LoadingIndicator/SpinnerCircle.d.ts +2 -3
  63. package/Menu/MenuContainer.d.ts +2 -2
  64. package/Menu/MenuContainer.js +19 -10
  65. package/Menu/MenuItem.js +5 -1
  66. package/Menu/MenuList.js +1 -1
  67. package/Menu/context.d.ts +1 -1
  68. package/Menu/index.d.ts +1 -1
  69. package/Modal/BaseModal/ModalInner.js +2 -0
  70. package/Modal/BaseModal/ModalWrapper.js +30 -37
  71. package/Modal/BaseModal/context.d.ts +1 -1
  72. package/Modal/BaseModal/index.js +1 -2
  73. package/Modal/ConfirmBox/ConfirmBoxContent.js +5 -1
  74. package/Modal/ConfirmBox/ConfirmBoxFooter.js +5 -1
  75. package/Modal/ConfirmBox/ConfirmBoxHeader.js +5 -1
  76. package/Modal/ConfirmBox/index.js +1 -2
  77. package/Modal/Dialog/DialogInner.js +5 -1
  78. package/Modal/Dialog/index.js +18 -9
  79. package/Modal/Drawer/DrawerCloseIcon.js +17 -7
  80. package/Modal/Drawer/DrawerFooter.js +5 -1
  81. package/Modal/Drawer/DrawerHeader.d.ts +4 -4
  82. package/Modal/Drawer/DrawerHeader.js +5 -1
  83. package/Modal/Drawer/index.js +18 -9
  84. package/Modal/MessageBox/MessageBoxContent.js +5 -1
  85. package/Modal/MessageBox/MessageBoxFooter.js +5 -1
  86. package/Modal/MessageBox/MessageBoxHeader.js +5 -1
  87. package/Modal/MessageBox/index.js +18 -9
  88. package/Modal/index.js +17 -7
  89. package/Pagination/PaginationContainer.d.ts +9 -0
  90. package/Pagination/PaginationContainer.js +34 -0
  91. package/Pagination/PaginationGap.d.ts +4 -0
  92. package/Pagination/PaginationGap.js +22 -0
  93. package/Pagination/PaginationIcons.d.ts +6 -0
  94. package/Pagination/PaginationIcons.js +21 -0
  95. package/Pagination/PaginationItem.d.ts +44 -0
  96. package/Pagination/PaginationItem.js +59 -0
  97. package/Pagination/getPaginationItems.d.ts +22 -0
  98. package/Pagination/getPaginationItems.js +42 -0
  99. package/Pagination/index.d.ts +104 -0
  100. package/Pagination/index.js +98 -0
  101. package/Pagination/usePagination.d.ts +105 -0
  102. package/Pagination/usePagination.js +66 -0
  103. package/PhoneField/CountryFlagComponent.d.ts +1 -1
  104. package/PhoneField/UnknownFlag.d.ts +0 -1
  105. package/PhoneField/flags/BY.js +17 -7
  106. package/PhoneField/flags/CN.js +17 -7
  107. package/PhoneField/flags/IL.js +17 -7
  108. package/PhoneField/flags/JP.js +17 -7
  109. package/PhoneField/flags/KZ.js +17 -7
  110. package/PhoneField/flags/RU.js +17 -7
  111. package/PhoneField/flags/UA.js +17 -7
  112. package/PhoneField/flags/US.js +17 -7
  113. package/PhoneField/flags/Unknown.js +17 -7
  114. package/PhoneField/templates.d.ts +2 -2
  115. package/Popper/Popper.js +1 -2
  116. package/Radio/RadioBox.d.ts +1 -1
  117. package/Radio/RadioBox.js +1 -1
  118. package/Radio/RadioContainer.d.ts +2 -2
  119. package/Radio/index.d.ts +5 -5
  120. package/Radio/index.js +1 -2
  121. package/Radio/useRadioColor.d.ts +1 -1
  122. package/RadioGroup/RadioGroupAsterisk.js +17 -7
  123. package/RadioGroup/RadioGroupErrorText.js +17 -7
  124. package/RadioGroup/RadioGroupItems.d.ts +1 -1
  125. package/RadioGroup/index.d.ts +11 -11
  126. package/RadioGroup/index.js +1 -2
  127. package/Selectbox/SelectboxButton.d.ts +4 -4
  128. package/Selectbox/SelectboxButton.js +25 -10
  129. package/Selectbox/SelectboxButtonWrapper.d.ts +2 -2
  130. package/Selectbox/SelectboxButtonWrapper.js +19 -10
  131. package/Selectbox/SelectboxMenu.d.ts +2 -2
  132. package/Selectbox/SelectboxMenu.js +17 -7
  133. package/Selectbox/index.d.ts +2 -2
  134. package/Selectbox/index.js +18 -9
  135. package/Slider/SliderRail.d.ts +1 -1
  136. package/Slider/SliderThumb.d.ts +1 -1
  137. package/Slider/SliderTrack.d.ts +1 -1
  138. package/Slider/index.js +1 -2
  139. package/Slider/useSliderColor.d.ts +1 -1
  140. package/Stack/index.d.ts +18 -6
  141. package/Stack/index.js +7 -2
  142. package/Surface/SurfaceContainer.d.ts +3 -3
  143. package/Surface/SurfaceContainer.js +1 -1
  144. package/Surface/SurfaceContent.d.ts +2 -2
  145. package/Surface/SurfaceContent.js +18 -11
  146. package/Surface/SurfaceFooter.d.ts +2 -2
  147. package/Surface/SurfaceFooter.js +5 -1
  148. package/Surface/SurfaceHeader.d.ts +2 -2
  149. package/Surface/SurfaceHeader.js +6 -2
  150. package/Surface/SurfaceSubheader.d.ts +2 -2
  151. package/Surface/SurfaceSubheader.js +6 -2
  152. package/Surface/index.d.ts +10 -10
  153. package/Surface/index.js +18 -9
  154. package/Swiper/Swiper.d.ts +83 -15
  155. package/Swiper/Swiper.js +285 -143
  156. package/Swiper/SwiperContainer.d.ts +2 -2
  157. package/Swiper/SwiperSlide.js +20 -7
  158. package/Swiper/SwiperSlidesRenderer.d.ts +4 -0
  159. package/Swiper/SwiperSlidesRenderer.js +19 -8
  160. package/Swiper/SwiperTrack.d.ts +22 -2
  161. package/Swiper/SwiperTrack.js +45 -12
  162. package/Swiper/SwiperWrapper.d.ts +5 -2
  163. package/Swiper/SwiperWrapper.js +22 -11
  164. package/Switch/SwitchAsterisk.js +17 -7
  165. package/Switch/SwitchContainer.d.ts +2 -2
  166. package/Switch/SwitchDot.d.ts +1 -1
  167. package/Switch/SwitchDot.js +2 -4
  168. package/Switch/SwitchErrorText.js +17 -7
  169. package/Switch/SwitchTrack.d.ts +1 -1
  170. package/Switch/index.d.ts +9 -9
  171. package/Switch/index.js +1 -2
  172. package/Switch/useSwitchColor.d.ts +1 -1
  173. package/Table/Table.d.ts +1 -1
  174. package/Table/Table.js +1 -1
  175. package/Table/TableCaption.js +5 -1
  176. package/Table/TableCell.js +6 -2
  177. package/Table/TableFooter.js +2 -1
  178. package/Table/TableHeaderCell.js +6 -2
  179. package/Tabs/Tab.js +4 -1
  180. package/Tabs/TabPanel.d.ts +1 -1
  181. package/Tabs/Tabs.d.ts +7 -7
  182. package/Tabs/Tabs.js +1 -2
  183. package/Tabs/useTabsColor.d.ts +1 -1
  184. package/TextArea/TextAreaIconWrapper.d.ts +3 -3
  185. package/TextArea/TextAreaIconWrapper.js +17 -7
  186. package/TextArea/TextAreaInput.js +23 -8
  187. package/TextArea/index.js +18 -9
  188. package/TextField/TextFieldContainer.js +17 -7
  189. package/TextField/TextFieldErrorText.js +17 -7
  190. package/TextField/TextFieldIconWrapper.d.ts +3 -3
  191. package/TextField/TextFieldIconWrapper.js +17 -7
  192. package/TextField/TextFieldInput.js +23 -8
  193. package/TextField/TextFieldInputWrapper.d.ts +5 -5
  194. package/TextField/TextFieldInputWrapper.js +22 -9
  195. package/TextField/TextFieldLabel.js +17 -7
  196. package/TextField/TextFieldLabelAsterisk.js +17 -7
  197. package/TextField/index.d.ts +1 -1
  198. package/TextField/index.js +18 -9
  199. package/ThemeProvider/createTheme.js +7 -3
  200. package/ThemeProvider/index.d.ts +45 -12
  201. package/ThemeProvider/index.js +1 -4
  202. package/ThemeProvider/tokens.d.ts +54 -3
  203. package/ThemeProvider/tokens.js +61 -3
  204. package/Toast/ToastCard.js +5 -2
  205. package/Toast/ToastContainer.js +1 -2
  206. package/Tooltip/TooltipContainer.js +2 -0
  207. package/Tooltip/index.js +3 -4
  208. package/Typography/Base.d.ts +2 -3
  209. package/Typography/Blockquote.d.ts +2 -3
  210. package/Typography/List.d.ts +6 -7
  211. package/Typography/Paragraph.d.ts +1 -1
  212. package/Typography/Paragraph.js +17 -7
  213. package/Typography/SectionDescription.d.ts +2 -3
  214. package/Typography/U.d.ts +2 -3
  215. package/esm/Accordion/AccordionActions.js +2 -1
  216. package/esm/Accordion/AccordionContainer.js +2 -14
  217. package/esm/Accordion/AccordionContent.js +5 -1
  218. package/esm/Accordion/AccordionHeader.js +7 -3
  219. package/esm/Accordion/index.js +1 -2
  220. package/esm/Autocomplete/AutocompleteItem.js +2 -1
  221. package/esm/Avatar/index.js +1 -2
  222. package/esm/Badge/BadgeStyled.js +2 -2
  223. package/esm/Badge/index.js +1 -2
  224. package/esm/Button/ButtonContainer.js +23 -6
  225. package/esm/Button/buttonVariants.js +3 -2
  226. package/esm/Button/index.js +1 -20
  227. package/esm/ButtonGroup/index.js +4 -3
  228. package/esm/Calendar/CalendarComponent.js +1 -2
  229. package/esm/Calendar/CalendarPaper.js +1 -1
  230. package/esm/Checkbox/CheckboxBox.js +2 -2
  231. package/esm/Checkbox/index.js +1 -2
  232. package/esm/ClickOutside/index.js +5 -2
  233. package/esm/Grid/index.js +7 -2
  234. package/esm/Highlighted/index.js +1 -2
  235. package/esm/Menu/MenuContainer.js +2 -3
  236. package/esm/Menu/MenuItem.js +5 -1
  237. package/esm/Menu/MenuList.js +1 -1
  238. package/esm/Modal/BaseModal/ModalInner.js +2 -0
  239. package/esm/Modal/BaseModal/ModalWrapper.js +30 -37
  240. package/esm/Modal/BaseModal/index.js +1 -2
  241. package/esm/Modal/ConfirmBox/ConfirmBoxContent.js +5 -1
  242. package/esm/Modal/ConfirmBox/ConfirmBoxFooter.js +5 -1
  243. package/esm/Modal/ConfirmBox/ConfirmBoxHeader.js +5 -1
  244. package/esm/Modal/ConfirmBox/index.js +1 -2
  245. package/esm/Modal/Dialog/DialogInner.js +5 -1
  246. package/esm/Modal/Dialog/index.js +1 -2
  247. package/esm/Modal/Drawer/DrawerFooter.js +5 -1
  248. package/esm/Modal/Drawer/DrawerHeader.js +5 -1
  249. package/esm/Modal/Drawer/index.js +1 -2
  250. package/esm/Modal/MessageBox/MessageBoxContent.js +5 -1
  251. package/esm/Modal/MessageBox/MessageBoxFooter.js +5 -1
  252. package/esm/Modal/MessageBox/MessageBoxHeader.js +5 -1
  253. package/esm/Modal/MessageBox/index.js +1 -2
  254. package/esm/Pagination/PaginationContainer.js +29 -0
  255. package/esm/Pagination/PaginationGap.js +17 -0
  256. package/esm/Pagination/PaginationIcons.js +15 -0
  257. package/esm/Pagination/PaginationItem.js +54 -0
  258. package/esm/Pagination/getPaginationItems.js +40 -0
  259. package/esm/Pagination/index.js +77 -0
  260. package/esm/Pagination/usePagination.js +61 -0
  261. package/esm/Popper/Popper.js +1 -2
  262. package/esm/Radio/RadioBox.js +2 -2
  263. package/esm/Radio/index.js +1 -2
  264. package/esm/RadioGroup/index.js +1 -2
  265. package/esm/Selectbox/SelectboxButton.js +8 -3
  266. package/esm/Selectbox/SelectboxButtonWrapper.js +2 -3
  267. package/esm/Selectbox/index.js +1 -2
  268. package/esm/Slider/index.js +1 -2
  269. package/esm/Stack/index.js +7 -2
  270. package/esm/Surface/SurfaceContainer.js +1 -1
  271. package/esm/Surface/SurfaceContent.js +18 -11
  272. package/esm/Surface/SurfaceFooter.js +5 -1
  273. package/esm/Surface/SurfaceHeader.js +6 -2
  274. package/esm/Surface/SurfaceSubheader.js +6 -2
  275. package/esm/Surface/index.js +1 -2
  276. package/esm/Swiper/Swiper.js +268 -136
  277. package/esm/Swiper/SwiperSlide.js +3 -0
  278. package/esm/Swiper/SwiperSlidesRenderer.js +2 -1
  279. package/esm/Swiper/SwiperTrack.js +28 -5
  280. package/esm/Swiper/SwiperWrapper.js +5 -4
  281. package/esm/Switch/SwitchDot.js +2 -4
  282. package/esm/Switch/index.js +1 -2
  283. package/esm/Table/Table.js +1 -1
  284. package/esm/Table/TableCaption.js +5 -1
  285. package/esm/Table/TableCell.js +6 -2
  286. package/esm/Table/TableFooter.js +2 -1
  287. package/esm/Table/TableHeaderCell.js +6 -2
  288. package/esm/Tabs/Tab.js +5 -2
  289. package/esm/Tabs/Tabs.js +1 -2
  290. package/esm/TextArea/TextAreaInput.js +6 -1
  291. package/esm/TextArea/index.js +1 -2
  292. package/esm/TextField/TextFieldInput.js +6 -1
  293. package/esm/TextField/TextFieldInputWrapper.js +5 -2
  294. package/esm/TextField/index.js +1 -2
  295. package/esm/ThemeProvider/createTheme.js +7 -3
  296. package/esm/ThemeProvider/index.js +0 -2
  297. package/esm/ThemeProvider/tokens.js +55 -2
  298. package/esm/Toast/ToastCard.js +6 -3
  299. package/esm/Toast/ToastContainer.js +1 -2
  300. package/esm/Tooltip/TooltipContainer.js +2 -0
  301. package/esm/Tooltip/index.js +3 -4
  302. package/esm/utils/getElementRef.js +13 -0
  303. package/package.json +46 -11
  304. package/utils/getElementRef.d.ts +8 -0
  305. package/utils/getElementRef.js +18 -0
  306. package/ThemeProvider/components.d.ts +0 -79
  307. package/ThemeProvider/components.js +0 -3
  308. package/ThemeProvider/useThemeProps.d.ts +0 -7
  309. package/ThemeProvider/useThemeProps.js +0 -34
  310. package/esm/ThemeProvider/components.js +0 -2
  311. package/esm/ThemeProvider/useThemeProps.js +0 -29
@@ -14,47 +14,66 @@ import Container from './SwiperContainer';
14
14
  import Wrapper from './SwiperWrapper';
15
15
  import Track from './SwiperTrack';
16
16
  import SwiperSlidesRenderer from './SwiperSlidesRenderer';
17
- import useThemeProps from '../ThemeProvider/useThemeProps';
18
17
  export * from './SwiperSlide';
19
18
  const defaultSlideLabel = (index, total) => `${index + 1} / ${total}`;
19
+ const prefersReducedMotion = () => typeof window !== 'undefined' &&
20
+ typeof window.matchMedia === 'function' &&
21
+ window.matchMedia('(prefers-reduced-motion: reduce)').matches;
20
22
  export const Swiper = React.forwardRef((props, ref) => {
21
- const _a = useThemeProps('Swiper', props), { children, dragThreshold = 240, snap = true, draggable = true, overrides, initialIndex = 0, infinite = false, onSlideChange, autoplay = false, autoplayInterval = 3000, pauseOnHover = true, threshold = 20, resistance = true, slidesPerView = 1, keyboardControl = true, slideLabel = defaultSlideLabel } = _a, restProps = __rest(_a, ["children", "dragThreshold", "snap", "draggable", "overrides", "initialIndex", "infinite", "onSlideChange", "autoplay", "autoplayInterval", "pauseOnHover", "threshold", "resistance", "slidesPerView", "keyboardControl", "slideLabel"]);
23
+ const { children, dragThreshold = 240, snap = true, draggable: draggableProp = true, overrides, initialIndex = 0, infinite = false, onSlideChange, autoplay = false, autoplayInterval = 3000, pauseOnHover = true, threshold = 20, resistance = true, slidesPerView: slidesPerViewProp = 1, centered = false, direction = 'horizontal', effect = 'slide', speed = 300, autoScroll, keyboardControl: keyboardControlProp = true, slideLabel = defaultSlideLabel } = props, restProps = __rest(props, ["children", "dragThreshold", "snap", "draggable", "overrides", "initialIndex", "infinite", "onSlideChange", "autoplay", "autoplayInterval", "pauseOnHover", "threshold", "resistance", "slidesPerView", "centered", "direction", "effect", "speed", "autoScroll", "keyboardControl", "slideLabel"]);
24
+ const isFade = effect === 'fade';
25
+ const isTicker = typeof autoScroll === 'number' && autoScroll > 0 && !isFade;
26
+ const isVertical = direction === 'vertical';
27
+ const slidesPerView = isFade ? 1 : Math.max(slidesPerViewProp, 0.1);
28
+ const draggable = draggableProp && !isTicker;
29
+ const keyboardControl = keyboardControlProp && !isTicker;
30
+ // The current slide is moved to the middle of the view by this number of slides
31
+ const centerShift = centered && !isFade ? (slidesPerView - 1) / 2 : 0;
22
32
  // #region Slides
23
- const slides = React.useMemo(() => {
33
+ const { slides, clones } = React.useMemo(() => {
24
34
  // toArray skips null/false and keeps the user keys (prefixed), so the slides are not remounted
25
35
  // when a slide is added to the start
26
36
  const childrenSlides = React.Children.toArray(children).filter(React.isValidElement);
27
- if (infinite) {
28
- if (childrenSlides.length < 2) {
29
- console.error('To infinite loop counts of the slides must be greater than 2');
30
- return childrenSlides;
31
- }
32
- if (childrenSlides.length < slidesPerView + 1) {
33
- console.error(`For infinite loop with slidesPerView=${slidesPerView}, need at least ${slidesPerView + 1} slides`);
34
- return childrenSlides;
35
- }
36
- // Используем стабильные ключи без Date.now()
37
- const headClones = childrenSlides.slice(-slidesPerView).map((slide, i) => React.cloneElement(slide, {
38
- key: `clone-head-${i}`,
39
- 'data-clone': 'head',
40
- }));
41
- const tailClones = childrenSlides.slice(0, slidesPerView).map((slide, i) => React.cloneElement(slide, {
42
- key: `clone-tail-${i}`,
43
- 'data-clone': 'tail',
44
- }));
45
- return [...headClones, ...childrenSlides, ...tailClones];
46
- }
47
- return childrenSlides;
48
- }, [children, infinite, slidesPerView]);
37
+ // The fade effect does not move the track, the loop does not need the copies
38
+ if (!(infinite || isTicker) || isFade) {
39
+ return { slides: childrenSlides, clones: 0 };
40
+ }
41
+ // The copies on each side cover the whole view (also its partially visible slides)
42
+ // and the part of the view in front of the centered slide
43
+ const count = Math.ceil(slidesPerView) + Math.ceil(centerShift);
44
+ if (childrenSlides.length < Math.max(2, count)) {
45
+ console.error(`For infinite loop with slidesPerView=${slidesPerView}${centered ? ' and centered' : ''}, need at least ${Math.max(2, count)} slides`);
46
+ return { slides: childrenSlides, clones: 0 };
47
+ }
48
+ const headClones = childrenSlides.slice(-count).map((slide, i) => React.cloneElement(slide, {
49
+ key: `clone-head-${i}`,
50
+ 'data-clone': 'head',
51
+ }));
52
+ const tailClones = childrenSlides.slice(0, count).map((slide, i) => React.cloneElement(slide, {
53
+ key: `clone-tail-${i}`,
54
+ 'data-clone': 'tail',
55
+ }));
56
+ return { slides: [...headClones, ...childrenSlides, ...tailClones], clones: count };
57
+ }, [children, infinite, isTicker, isFade, slidesPerView, centerShift, centered]);
58
+ // The infinite loop by the copies of the slides (slide effect and ticker)
59
+ const isLoop = clones > 0;
60
+ // The fade effect loops by the index alone
61
+ const isFadeLoop = isFade && infinite && slides.length > 1;
49
62
  const total = slides.length;
50
- const realSlidesCount = infinite ? total - 2 * slidesPerView : total;
51
- const maxIndex = Math.max(0, total - slidesPerView);
63
+ const realSlidesCount = total - 2 * clones;
64
+ // Without the loop the last position shows the last slide at the end of the view
65
+ // (in the middle of the view when centered). With a fractional slidesPerView it is not a whole
66
+ // slide: the index goes up to the next whole number and the track stops at the last position
67
+ const lastPosition = isFade || centerShift > 0 ? Math.max(0, total - 1) : Math.max(0, total - slidesPerView);
68
+ const maxIndex = Math.ceil(lastPosition - 0.001);
69
+ // The index while the track is between the slides (drag, free mode) without the loop
70
+ const maxFreeIndex = Math.floor(lastPosition + 0.001);
52
71
  // #region States
53
72
  const [index, setIndex] = React.useState(() => {
54
- if (!infinite) {
73
+ if (!isLoop) {
55
74
  return Math.max(0, Math.min(initialIndex, maxIndex));
56
75
  }
57
- return initialIndex + slidesPerView;
76
+ return initialIndex + clones;
58
77
  });
59
78
  const [offset, setOffset] = React.useState(0);
60
79
  // The long decelerating transition after a free swipe (snap=false)
@@ -68,8 +87,8 @@ export const Swiper = React.forwardRef((props, ref) => {
68
87
  const isPaused = isPausedByApi || isHovered || isFocused;
69
88
  // #region Refs
70
89
  const dragging = React.useRef(false);
71
- const startX = React.useRef(0);
72
- const lastX = React.useRef(0);
90
+ const startCoord = React.useRef(0);
91
+ const lastCoord = React.useRef(0);
73
92
  const lastTime = React.useRef(0);
74
93
  const velocity = React.useRef(0);
75
94
  const wrapperRef = React.useRef(null);
@@ -77,12 +96,13 @@ export const Swiper = React.forwardRef((props, ref) => {
77
96
  const isAnimating = React.useRef(false);
78
97
  const mounted = React.useRef(true);
79
98
  const suppressClick = React.useRef(false);
99
+ const getCoord = React.useCallback((e) => (isVertical ? e.clientY : e.clientX), [isVertical]);
80
100
  // #region Real Index
81
101
  const realIndex = React.useMemo(() => {
82
- if (!infinite) {
102
+ if (!isLoop) {
83
103
  return index;
84
104
  }
85
- const realIndex = index - slidesPerView;
105
+ const realIndex = index - clones;
86
106
  if (realIndex < 0) {
87
107
  return realSlidesCount + realIndex;
88
108
  }
@@ -91,7 +111,7 @@ export const Swiper = React.forwardRef((props, ref) => {
91
111
  }
92
112
  // In the infinite mode any slide can be the first visible one, so there is no clamp
93
113
  return realIndex;
94
- }, [index, infinite, slidesPerView, realSlidesCount]);
114
+ }, [index, isLoop, clones, realSlidesCount]);
95
115
  const prevRealIndex = React.useRef(realIndex);
96
116
  // #region Effects
97
117
  React.useEffect(() => {
@@ -104,18 +124,18 @@ export const Swiper = React.forwardRef((props, ref) => {
104
124
  if (!mounted.current)
105
125
  return;
106
126
  // The slides were removed: keep the index inside the allowed range
107
- const lastIndex = infinite ? total - slidesPerView : maxIndex;
127
+ const lastIndex = isLoop ? total - clones : maxIndex;
108
128
  if (index > lastIndex) {
109
- setIndex(infinite ? slidesPerView : maxIndex);
129
+ setIndex(isLoop ? clones : maxIndex);
110
130
  setOffset(0);
111
131
  }
112
- }, [index, infinite, total, slidesPerView, maxIndex]);
132
+ }, [index, isLoop, total, clones, maxIndex]);
113
133
  // #region Normalization
114
134
  const normalizeIndex = React.useCallback(() => {
115
- if (!infinite || !mounted.current)
135
+ if (!isLoop || !mounted.current)
116
136
  return;
117
- const firstRealIndex = slidesPerView;
118
- const lastRealIndex = total - slidesPerView - 1;
137
+ const firstRealIndex = clones;
138
+ const lastRealIndex = total - clones - 1;
119
139
  if (index < firstRealIndex || index > lastRealIndex) {
120
140
  // A clone position shows the same slides as the real position shifted by the slides count.
121
141
  // Not always the first/last real slide: after several fast clicks the index may be deeper in the clones
@@ -135,7 +155,7 @@ export const Swiper = React.forwardRef((props, ref) => {
135
155
  });
136
156
  });
137
157
  }
138
- }, [index, infinite, slidesPerView, total, realSlidesCount]);
158
+ }, [index, isLoop, clones, total, realSlidesCount]);
139
159
  // #region Navigation API
140
160
  const indexRef = React.useRef(index);
141
161
  React.useEffect(() => {
@@ -144,13 +164,13 @@ export const Swiper = React.forwardRef((props, ref) => {
144
164
  const goToIndex = React.useCallback((i) => {
145
165
  // In the infinite mode the index is normalized after the transition, so wait for it.
146
166
  // In the regular mode the transition is simply retargeted
147
- if (infinite && isAnimating.current)
167
+ if (isLoop && isAnimating.current)
148
168
  return;
149
169
  let targetIndex = i;
150
- if (infinite) {
151
- targetIndex = i + slidesPerView;
152
- const minIndex = slidesPerView;
153
- const maxIndex = slidesPerView + realSlidesCount - 1;
170
+ if (isLoop) {
171
+ targetIndex = i + clones;
172
+ const minIndex = clones;
173
+ const maxIndex = clones + realSlidesCount - 1;
154
174
  targetIndex = Math.max(minIndex, Math.min(maxIndex, targetIndex));
155
175
  }
156
176
  else {
@@ -164,17 +184,20 @@ export const Swiper = React.forwardRef((props, ref) => {
164
184
  setOffset(0);
165
185
  setIndex(targetIndex);
166
186
  isAnimating.current = true;
167
- }, [infinite, slidesPerView, realSlidesCount, maxIndex]);
187
+ }, [isLoop, clones, realSlidesCount, maxIndex]);
168
188
  const next = React.useCallback(() => {
169
189
  setDisableAnimation(false);
170
190
  setIsMomentum(false);
171
191
  setOffset(0);
172
192
  setIndex(prev => {
173
193
  let nextIndex;
174
- if (infinite) {
194
+ if (isLoop) {
175
195
  // The last position is the first tail clone: it mirrors the first real slide.
176
196
  // Further positions would show the empty space when slidesPerView > 1
177
- nextIndex = Math.min(prev + 1, total - slidesPerView);
197
+ nextIndex = Math.min(prev + 1, total - clones);
198
+ }
199
+ else if (isFadeLoop) {
200
+ nextIndex = (prev + 1) % total;
178
201
  }
179
202
  else {
180
203
  nextIndex = Math.min(prev + 1, maxIndex);
@@ -186,50 +209,61 @@ export const Swiper = React.forwardRef((props, ref) => {
186
209
  isAnimating.current = true;
187
210
  return nextIndex;
188
211
  });
189
- }, [infinite, maxIndex, total, slidesPerView]);
212
+ }, [isLoop, isFadeLoop, maxIndex, total, clones]);
190
213
  const prev = React.useCallback(() => {
191
214
  setDisableAnimation(false);
192
215
  setIsMomentum(false);
193
216
  setOffset(0);
194
217
  setIndex(prev => {
195
- const nextIndex = Math.max(prev - 1, 0);
218
+ const nextIndex = isFadeLoop ? (prev - 1 + total) % total : Math.max(prev - 1, 0);
196
219
  if (nextIndex === prev) {
197
220
  return prev;
198
221
  }
199
222
  isAnimating.current = true;
200
223
  return nextIndex;
201
224
  });
202
- }, []);
225
+ }, [isFadeLoop, total]);
203
226
  // #region Position
204
- // The position is the distance in pixels from the start of the track to the left edge of the view.
205
- // The state keeps it as index (whole slides) + offset (pixels), the track is translated by both
206
- const getSlideWidth = React.useCallback(() => { var _a; return (((_a = wrapperRef.current) === null || _a === void 0 ? void 0 : _a.clientWidth) || 0) / slidesPerView; }, [slidesPerView]);
227
+ // The position is the distance in pixels from the start of the track to the start of the view
228
+ // without the centering shift. The state keeps it as index (whole slides) + offset (pixels)
229
+ const getSlideSize = React.useCallback(() => {
230
+ const wrapper = wrapperRef.current;
231
+ const size = (isVertical ? wrapper === null || wrapper === void 0 ? void 0 : wrapper.clientHeight : wrapper === null || wrapper === void 0 ? void 0 : wrapper.clientWidth) || 0;
232
+ return size / slidesPerView;
233
+ }, [isVertical, slidesPerView]);
207
234
  // The last allowed position. In the infinite mode it is the first tail clone
208
- const maxPosition = React.useCallback(() => (infinite ? total - slidesPerView : maxIndex) * getSlideWidth(), [infinite, total, slidesPerView, maxIndex, getSlideWidth]);
235
+ const maxPosition = React.useCallback(() => (isLoop ? total - clones : lastPosition) * getSlideSize(), [isLoop, total, clones, lastPosition, getSlideSize]);
236
+ // The index of the track: without the loop the track stops at the last position
237
+ // even if the index is the next whole number
238
+ const trackIndex = (isLoop ? index : Math.min(index, lastPosition)) - centerShift;
209
239
  // The position on the screen right now, also in the middle of a transition
210
240
  const readPosition = React.useCallback(() => {
211
241
  const track = trackRef.current;
242
+ const size = getSlideSize();
212
243
  if (track && typeof DOMMatrixReadOnly !== 'undefined') {
213
244
  const { transform } = getComputedStyle(track);
214
245
  if (transform && transform !== 'none') {
215
- return -new DOMMatrixReadOnly(transform).m41;
246
+ const matrix = new DOMMatrixReadOnly(transform);
247
+ return -(isVertical ? matrix.m42 : matrix.m41) + centerShift * size;
216
248
  }
217
249
  }
218
- return index * getSlideWidth() - offset;
219
- }, [index, offset, getSlideWidth]);
250
+ return (trackIndex + centerShift) * size - offset;
251
+ }, [trackIndex, centerShift, offset, getSlideSize, isVertical]);
220
252
  // Infinite mode: the same view is shown by the real slides and by the clones,
221
253
  // so the position is moved by the whole slides count when it comes close to the edge of the track
222
254
  const wrapPosition = React.useCallback((position) => {
223
- const width = getSlideWidth();
224
- const span = realSlidesCount * width;
225
- if (!infinite || width === 0)
255
+ const size = getSlideSize();
256
+ const span = realSlidesCount * size;
257
+ if (!isLoop || size === 0)
226
258
  return 0;
227
- if (position < width * 0.5)
259
+ if (position < size * 0.5)
228
260
  return span;
229
- if (position > maxPosition() - width * 0.5)
261
+ if (position > maxPosition() - size * 0.5)
230
262
  return -span;
231
263
  return 0;
232
- }, [infinite, realSlidesCount, getSlideWidth, maxPosition]);
264
+ }, [isLoop, realSlidesCount, getSlideSize, maxPosition]);
265
+ // Without the loop the index between the slides does not go past the last position
266
+ const clampFreeIndex = React.useCallback((i) => (isLoop ? i : Math.max(0, Math.min(maxFreeIndex, i))), [isLoop, maxFreeIndex]);
233
267
  const dragBasePosition = React.useRef(0);
234
268
  const dragAnchorIndex = React.useRef(0);
235
269
  const dragPosition = React.useRef(0);
@@ -240,49 +274,61 @@ export const Swiper = React.forwardRef((props, ref) => {
240
274
  // Only the main mouse button, the touch or the pen
241
275
  if (e.pointerType === 'mouse' && e.button !== 0)
242
276
  return;
243
- const width = getSlideWidth();
277
+ suppressClick.current = false;
278
+ dragging.current = true;
279
+ startCoord.current = getCoord(e);
280
+ lastCoord.current = getCoord(e);
281
+ lastTime.current = performance.now();
282
+ velocity.current = 0;
283
+ // The capture fails for a pointer that is already gone: the drag still works without it
284
+ try {
285
+ e.target.setPointerCapture(e.pointerId);
286
+ }
287
+ catch (_a) {
288
+ // nothing
289
+ }
290
+ // The fade does not move the track: the slide changes after the release
291
+ if (isFade)
292
+ return;
293
+ const size = getSlideSize();
244
294
  // Grab the track where it is now: a running transition stops under the finger instead of jumping
245
295
  let position = readPosition();
246
296
  position += wrapPosition(position);
247
- const anchor = width > 0 ? Math.round(position / width) : index;
297
+ const anchor = clampFreeIndex(size > 0 ? Math.round(position / size) : index);
248
298
  dragBasePosition.current = position;
249
299
  dragPosition.current = position;
250
300
  dragAnchorIndex.current = anchor;
251
- suppressClick.current = false;
252
- dragging.current = true;
253
301
  isAnimating.current = false;
254
302
  setDisableAnimation(true);
255
303
  setIsMomentum(false);
256
304
  setIsDragging(true);
257
305
  setIndex(anchor);
258
- setOffset(anchor * width - position);
259
- startX.current = e.clientX;
260
- lastX.current = e.clientX;
261
- lastTime.current = performance.now();
262
- velocity.current = 0;
263
- e.target.setPointerCapture(e.pointerId);
264
- }, [draggable, getSlideWidth, readPosition, wrapPosition, index]);
306
+ setOffset(anchor * size - position);
307
+ }, [draggable, isFade, getCoord, getSlideSize, readPosition, wrapPosition, clampFreeIndex, index]);
265
308
  const onPointerMove = React.useCallback((e) => {
266
309
  if (!dragging.current)
267
310
  return;
311
+ const coord = getCoord(e);
268
312
  const now = performance.now();
269
- const dx = e.clientX - lastX.current;
313
+ const delta = coord - lastCoord.current;
270
314
  const dt = now - lastTime.current;
271
315
  if (dt > 0) {
272
- velocity.current = dx / dt;
316
+ velocity.current = delta / dt;
273
317
  }
274
- lastX.current = e.clientX;
318
+ lastCoord.current = coord;
275
319
  lastTime.current = now;
276
- const width = getSlideWidth();
277
- let position = dragBasePosition.current - (e.clientX - startX.current);
320
+ if (isFade)
321
+ return;
322
+ const size = getSlideSize();
323
+ let position = dragBasePosition.current - (coord - startCoord.current);
278
324
  // Endless drag in the infinite mode: move the base together with the position
279
325
  const shift = wrapPosition(position);
280
326
  if (shift !== 0) {
281
327
  dragBasePosition.current += shift;
282
- dragAnchorIndex.current += Math.round(shift / width);
328
+ dragAnchorIndex.current += Math.round(shift / size);
283
329
  position += shift;
284
330
  }
285
- if (!infinite) {
331
+ if (!isLoop) {
286
332
  const max = maxPosition();
287
333
  if (position < 0) {
288
334
  position = resistance ? position * 0.3 : 0;
@@ -294,27 +340,55 @@ export const Swiper = React.forwardRef((props, ref) => {
294
340
  dragPosition.current = position;
295
341
  // snap: the index stays the same during the drag, the slide changes after the release.
296
342
  // free: the index follows the position, so onSlideChange reports the slide under the view
297
- const nextIndex = snap ? dragAnchorIndex.current : Math.round(position / width);
343
+ const nextIndex = snap
344
+ ? dragAnchorIndex.current
345
+ : clampFreeIndex(Math.round(position / size));
298
346
  setIndex(nextIndex);
299
- setOffset(nextIndex * width - position);
300
- }, [getSlideWidth, wrapPosition, infinite, maxPosition, resistance, snap]);
347
+ setOffset(nextIndex * size - position);
348
+ }, [
349
+ getCoord,
350
+ isFade,
351
+ getSlideSize,
352
+ wrapPosition,
353
+ isLoop,
354
+ maxPosition,
355
+ resistance,
356
+ snap,
357
+ clampFreeIndex,
358
+ ]);
301
359
  const onPointerUp = React.useCallback((e) => {
302
360
  if (!dragging.current)
303
361
  return;
304
- const width = getSlideWidth();
305
- const position = dragPosition.current;
306
- const delta = lastX.current - startX.current;
307
- // The pointer stopped before the release: no inertia
308
- const v = performance.now() - lastTime.current > 100 ? 0 : velocity.current;
309
- const lastIndex = infinite ? total - slidesPerView : maxIndex;
362
+ const delta = lastCoord.current - startCoord.current;
310
363
  // It was a drag, not a click: the click on a link or a button inside the slide must not fire
311
364
  if (Math.abs(delta) > threshold) {
312
365
  suppressClick.current = true;
313
366
  }
367
+ dragging.current = false;
368
+ try {
369
+ e.target.releasePointerCapture(e.pointerId);
370
+ }
371
+ catch (_a) {
372
+ // nothing
373
+ }
374
+ if (isFade) {
375
+ if (delta < -threshold) {
376
+ next();
377
+ }
378
+ else if (delta > threshold) {
379
+ prev();
380
+ }
381
+ return;
382
+ }
383
+ const size = getSlideSize();
384
+ const position = dragPosition.current;
385
+ // The pointer stopped before the release: no inertia
386
+ const v = performance.now() - lastTime.current > 100 ? 0 : velocity.current;
387
+ const lastIndex = isLoop ? total - clones : maxIndex;
314
388
  setDisableAnimation(false);
315
389
  if (snap) {
316
390
  const anchor = dragAnchorIndex.current;
317
- const displacement = position - anchor * width;
391
+ const displacement = position - anchor * size;
318
392
  const velocityFactor = Math.min(Math.abs(v) * 250, 0.65);
319
393
  const dynamicThreshold = Math.max(threshold, Math.min(dragThreshold * (1 - velocityFactor), dragThreshold));
320
394
  let nextIndex = anchor;
@@ -322,37 +396,42 @@ export const Swiper = React.forwardRef((props, ref) => {
322
396
  // At least one slide, more if the slide was dragged further
323
397
  nextIndex =
324
398
  displacement > 0
325
- ? Math.max(anchor + 1, Math.round(position / width))
326
- : Math.min(anchor - 1, Math.round(position / width));
399
+ ? Math.max(anchor + 1, Math.round(position / size))
400
+ : Math.min(anchor - 1, Math.round(position / size));
327
401
  }
328
402
  nextIndex = Math.max(0, Math.min(lastIndex, nextIndex));
329
403
  setIndex(nextIndex);
330
404
  setOffset(0);
331
405
  // A click without movement does not start a transition, so transitionend never comes
332
406
  // and the flag would block autoplay and goToIndex forever
333
- isAnimating.current = Math.abs(nextIndex * width - position) > 0.5;
407
+ const target = (isLoop ? nextIndex : Math.min(nextIndex, lastPosition)) * size;
408
+ isAnimating.current = Math.abs(target - position) > 0.5;
334
409
  }
335
410
  else {
336
411
  // Free mode: the track keeps moving by inertia and stops where it stops
337
- const projected = Math.max(0, Math.min(lastIndex * width, position - v * 300));
338
- const nextIndex = width > 0 ? Math.round(projected / width) : index;
412
+ const maxProjected = (isLoop ? lastIndex : lastPosition) * size;
413
+ const projected = Math.max(0, Math.min(maxProjected, position - v * 300));
414
+ const nextIndex = clampFreeIndex(size > 0 ? Math.round(projected / size) : index);
339
415
  setIsMomentum(true);
340
416
  setIndex(nextIndex);
341
- setOffset(nextIndex * width - projected);
417
+ setOffset(nextIndex * size - projected);
342
418
  isAnimating.current = Math.abs(projected - position) > 0.5;
343
419
  }
344
- dragging.current = false;
345
420
  setIsDragging(false);
346
- e.target.releasePointerCapture(e.pointerId);
347
421
  }, [
348
- getSlideWidth,
349
- infinite,
422
+ isFade,
423
+ next,
424
+ prev,
425
+ getSlideSize,
426
+ isLoop,
350
427
  total,
351
- slidesPerView,
428
+ clones,
352
429
  maxIndex,
430
+ lastPosition,
353
431
  threshold,
354
432
  snap,
355
433
  dragThreshold,
434
+ clampFreeIndex,
356
435
  index,
357
436
  ]);
358
437
  const onLostPointerCapture = React.useCallback((e) => {
@@ -364,7 +443,7 @@ export const Swiper = React.forwardRef((props, ref) => {
364
443
  const autoplayStep = React.useRef(() => undefined);
365
444
  autoplayStep.current = () => {
366
445
  // Without the infinite loop the autoplay returns to the first slide instead of stopping at the end
367
- if (!infinite && indexRef.current >= maxIndex) {
446
+ if (!isLoop && !isFadeLoop && indexRef.current >= maxIndex) {
368
447
  goToIndex(0);
369
448
  }
370
449
  else {
@@ -374,7 +453,7 @@ export const Swiper = React.forwardRef((props, ref) => {
374
453
  const pause = React.useCallback(() => setIsPausedByApi(true), []);
375
454
  const resume = React.useCallback(() => setIsPausedByApi(false), []);
376
455
  React.useEffect(() => {
377
- if (!autoplay || isPaused)
456
+ if (!autoplay || isTicker || isPaused)
378
457
  return undefined;
379
458
  const timer = setInterval(() => {
380
459
  if (!dragging.current && !isAnimating.current && mounted.current) {
@@ -382,13 +461,57 @@ export const Swiper = React.forwardRef((props, ref) => {
382
461
  }
383
462
  }, autoplayInterval);
384
463
  return () => clearInterval(timer);
385
- }, [autoplay, autoplayInterval, isPaused]);
464
+ }, [autoplay, isTicker, autoplayInterval, isPaused]);
465
+ // #region Ticker
466
+ // The continuous scroll writes the transform directly: 60 renders per second are not needed for it.
467
+ // The position is kept in slides, so it stays in place when the size of the swiper changes
468
+ const tickerPosition = React.useRef(null);
469
+ React.useEffect(() => {
470
+ const track = trackRef.current;
471
+ if (!isTicker || !isLoop || !track)
472
+ return undefined;
473
+ if (isPaused || prefersReducedMotion())
474
+ return undefined;
475
+ let frame = 0;
476
+ let last = performance.now();
477
+ const step = (now) => {
478
+ var _a;
479
+ const size = getSlideSize();
480
+ if (size > 0) {
481
+ let position = (_a = tickerPosition.current) !== null && _a !== void 0 ? _a : clones;
482
+ position += ((autoScroll || 0) * (now - last)) / 1000 / size;
483
+ if (position >= clones + realSlidesCount) {
484
+ position -= realSlidesCount;
485
+ }
486
+ tickerPosition.current = position;
487
+ const translate = -(position - centerShift) * size;
488
+ track.style.transition = 'none';
489
+ track.style.transform = isVertical
490
+ ? `translateY(${translate}px)`
491
+ : `translateX(${translate}px)`;
492
+ }
493
+ last = now;
494
+ frame = requestAnimationFrame(step);
495
+ };
496
+ frame = requestAnimationFrame(step);
497
+ return () => cancelAnimationFrame(frame);
498
+ }, [
499
+ isTicker,
500
+ isLoop,
501
+ isPaused,
502
+ autoScroll,
503
+ getSlideSize,
504
+ realSlidesCount,
505
+ clones,
506
+ centerShift,
507
+ isVertical,
508
+ ]);
386
509
  // #region Hover and focus
387
510
  // The autoplay pauses while the pointer is over the swiper (pauseOnHover)
388
511
  // and always while the focus is inside it, so the keyboard user is not interrupted
389
512
  React.useEffect(() => {
390
513
  const wrapper = wrapperRef.current;
391
- if (!autoplay || !wrapper)
514
+ if (!(autoplay || isTicker) || !wrapper)
392
515
  return undefined;
393
516
  const handleMouseEnter = () => pauseOnHover && setIsHovered(true);
394
517
  const handleMouseLeave = () => setIsHovered(false);
@@ -408,7 +531,7 @@ export const Swiper = React.forwardRef((props, ref) => {
408
531
  wrapper.removeEventListener('focusin', handleFocusIn);
409
532
  wrapper.removeEventListener('focusout', handleFocusOut);
410
533
  };
411
- }, [autoplay, pauseOnHover]);
534
+ }, [autoplay, isTicker, pauseOnHover]);
412
535
  // #region Keyboard
413
536
  const onKeyDown = React.useCallback((event) => {
414
537
  if (!keyboardControl)
@@ -417,15 +540,15 @@ export const Swiper = React.forwardRef((props, ref) => {
417
540
  const target = event.target;
418
541
  if (target.closest('input, textarea, select, [contenteditable="true"]'))
419
542
  return;
420
- if (event.key === 'ArrowLeft') {
543
+ if (event.key === (isVertical ? 'ArrowUp' : 'ArrowLeft')) {
421
544
  event.preventDefault();
422
545
  prev();
423
546
  }
424
- if (event.key === 'ArrowRight') {
547
+ if (event.key === (isVertical ? 'ArrowDown' : 'ArrowRight')) {
425
548
  event.preventDefault();
426
549
  next();
427
550
  }
428
- }, [keyboardControl, prev, next]);
551
+ }, [keyboardControl, isVertical, prev, next]);
429
552
  const onClickCapture = React.useCallback((event) => {
430
553
  if (suppressClick.current) {
431
554
  suppressClick.current = false;
@@ -444,14 +567,16 @@ export const Swiper = React.forwardRef((props, ref) => {
444
567
  const track = trackRef.current;
445
568
  if (!track)
446
569
  return;
570
+ // The fade changes the opacity of the slides, the event bubbles up to the track
571
+ const property = isFade ? 'opacity' : 'transform';
447
572
  const handleTransitionEnd = (e) => {
448
- if (e.propertyName !== 'transform')
573
+ if (e.propertyName !== property)
449
574
  return;
450
575
  isAnimating.current = false;
451
- normalizeIndex(); // <-- вот здесь
576
+ normalizeIndex();
452
577
  };
453
578
  const handleTransitionCancel = (e) => {
454
- if (e.propertyName !== 'transform')
579
+ if (e.propertyName !== property)
455
580
  return;
456
581
  isAnimating.current = false;
457
582
  };
@@ -461,7 +586,7 @@ export const Swiper = React.forwardRef((props, ref) => {
461
586
  track.removeEventListener('transitionend', handleTransitionEnd);
462
587
  track.removeEventListener('transitioncancel', handleTransitionCancel);
463
588
  };
464
- }, [normalizeIndex]);
589
+ }, [normalizeIndex, isFade]);
465
590
  // #region Mount
466
591
  React.useEffect(() => {
467
592
  mounted.current = true;
@@ -486,34 +611,41 @@ export const Swiper = React.forwardRef((props, ref) => {
486
611
  Wrapper: (overrides === null || overrides === void 0 ? void 0 : overrides.Wrapper) || Wrapper,
487
612
  }), [overrides]);
488
613
  const getSlideRealIndex = React.useCallback((domIndex) => {
489
- if (!infinite) {
614
+ if (!isLoop) {
490
615
  return domIndex;
491
616
  }
492
- const totalSlides = realSlidesCount;
493
- if (domIndex < slidesPerView) {
494
- return totalSlides - slidesPerView + domIndex;
617
+ if (domIndex < clones) {
618
+ return realSlidesCount - clones + domIndex;
495
619
  }
496
- if (domIndex >= total - slidesPerView) {
497
- return domIndex - (total - slidesPerView);
620
+ if (domIndex >= total - clones) {
621
+ return domIndex - (total - clones);
498
622
  }
499
- return domIndex - slidesPerView;
500
- }, [infinite, realSlidesCount, slidesPerView, total]);
623
+ return domIndex - clones;
624
+ }, [isLoop, realSlidesCount, clones, total]);
501
625
  // The slides at least partially in the view: they are not inert, so a partially visible slide
502
- // can be clicked. Between the slides (drag, free mode) one more slide is partially visible
503
- const slideWidth = wrapperRef.current ? wrapperRef.current.clientWidth / slidesPerView : 0;
504
- let firstVisibleDomIndex = index;
505
- let visibleCount = slidesPerView;
506
- if (slideWidth > 0 && Math.abs(offset) > 0.5) {
507
- const position = index * slideWidth - offset;
508
- firstVisibleDomIndex = Math.floor(position / slideWidth + 0.001);
509
- const lastVisibleDomIndex = Math.ceil((position + slidesPerView * slideWidth) / slideWidth - 0.001) - 1;
510
- visibleCount = lastVisibleDomIndex - firstVisibleDomIndex + 1;
626
+ // can be clicked. Between the slides (drag, free mode), with a fractional slidesPerView
627
+ // and with the centering one more slide is partially visible
628
+ const wrapper = wrapperRef.current;
629
+ const slideSize = wrapper
630
+ ? (isVertical ? wrapper.clientHeight : wrapper.clientWidth) / slidesPerView
631
+ : 0;
632
+ const viewPosition = slideSize > 0 ? trackIndex - offset / slideSize : trackIndex;
633
+ let firstVisibleDomIndex = Math.floor(viewPosition + 0.001);
634
+ let visibleCount = Math.ceil(viewPosition + slidesPerView - 0.001) - firstVisibleDomIndex;
635
+ if (isFade) {
636
+ firstVisibleDomIndex = index;
637
+ visibleCount = 1;
511
638
  }
512
639
  firstVisibleDomIndex = Math.max(0, Math.min(total - 1, firstVisibleDomIndex));
640
+ // The ticker moves without the state: all the slides are rendered
641
+ if (isTicker) {
642
+ firstVisibleDomIndex = clones;
643
+ visibleCount = realSlidesCount;
644
+ }
513
645
  return (React.createElement(overridesMap.Container, Object.assign({ role: "region", "aria-roledescription": "carousel" }, restProps),
514
- React.createElement(overridesMap.Wrapper, { ref: wrapperRef, draggable: draggable, slidesPerView: slidesPerView, tabIndex: keyboardControl ? 0 : undefined, "aria-live": autoplay && !isPaused ? 'off' : 'polite', onKeyDown: onKeyDown, onClickCapture: onClickCapture, onDragStart: onDragStart, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onLostPointerCapture: onLostPointerCapture },
515
- React.createElement(overridesMap.Track, { ref: trackRef, dragging: isDragging, index: index, offset: offset, disableAnimation: disableAnimation, momentum: isMomentum, slidesPerView: slidesPerView },
516
- React.createElement(SwiperSlidesRenderer, { slides: slides, slidesPerView: slidesPerView, realIndex: realIndex, firstVisibleIndex: getSlideRealIndex(firstVisibleDomIndex), visibleCount: visibleCount, realSlidesCount: realSlidesCount, getSlideRealIndex: getSlideRealIndex, slideLabel: slideLabel })))));
646
+ React.createElement(overridesMap.Wrapper, { ref: wrapperRef, draggable: draggable, slidesPerView: slidesPerView, vertical: isVertical, tabIndex: keyboardControl ? 0 : undefined, "aria-live": (autoplay || isTicker) && !isPaused ? 'off' : 'polite', onKeyDown: onKeyDown, onClickCapture: onClickCapture, onDragStart: onDragStart, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onLostPointerCapture: onLostPointerCapture },
647
+ React.createElement(overridesMap.Track, { ref: trackRef, dragging: isDragging, index: isFade ? 0 : trackIndex, offset: isFade ? 0 : offset, disableAnimation: disableAnimation, momentum: isMomentum, slidesPerView: slidesPerView, vertical: isVertical, effect: effect, speed: speed },
648
+ React.createElement(SwiperSlidesRenderer, { slides: slides, slidesPerView: slidesPerView, realIndex: realIndex, firstVisibleIndex: getSlideRealIndex(firstVisibleDomIndex), visibleCount: visibleCount, realSlidesCount: realSlidesCount, getSlideRealIndex: getSlideRealIndex, slideLabel: slideLabel, activeDomIndex: isFade ? index : null })))));
517
649
  });
518
650
  Swiper.displayName = 'Swiper';
519
651
  export default Swiper;