@alfalab/core-components-gallery 7.2.3-alfasans → 7.3.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 (285) hide show
  1. package/Component.js +51 -7
  2. package/Component.js.map +1 -1
  3. package/components/bottom-button/index.css +5 -5
  4. package/components/bottom-button/index.module.css.js +1 -1
  5. package/components/buttons/index.css +5 -5
  6. package/components/buttons/index.d.ts +3 -1
  7. package/components/buttons/index.js +49 -12
  8. package/components/buttons/index.js.map +1 -1
  9. package/components/buttons/index.module.css.js +1 -1
  10. package/components/header/Component.js +1 -0
  11. package/components/header/Component.js.map +1 -1
  12. package/components/header/index.css +3 -2
  13. package/components/header/index.module.css.js +1 -1
  14. package/components/header/index.module.css.js.map +1 -1
  15. package/components/header-info-block/Component.js +2 -1
  16. package/components/header-info-block/Component.js.map +1 -1
  17. package/components/header-info-block/index.css +4 -4
  18. package/components/header-info-block/index.module.css.js +1 -1
  19. package/components/header-mobile/index.css +6 -6
  20. package/components/header-mobile/index.module.css.js +1 -1
  21. package/components/image-preview/Component.js +2 -1
  22. package/components/image-preview/Component.js.map +1 -1
  23. package/components/image-preview/index.css +20 -20
  24. package/components/image-preview/index.module.css.js +1 -1
  25. package/components/image-preview/index.module.css.js.map +1 -1
  26. package/components/image-viewer/hooks.js +3 -2
  27. package/components/image-viewer/hooks.js.map +1 -1
  28. package/components/image-viewer/index.css +30 -31
  29. package/components/image-viewer/index.module.css.js +1 -1
  30. package/components/image-viewer/index.module.css.js.map +1 -1
  31. package/components/image-viewer/single.js +1 -0
  32. package/components/image-viewer/single.js.map +1 -1
  33. package/components/image-viewer/slide.js +3 -2
  34. package/components/image-viewer/slide.js.map +1 -1
  35. package/components/image-viewer/video/index.css +22 -10
  36. package/components/image-viewer/video/index.js +50 -15
  37. package/components/image-viewer/video/index.js.map +1 -1
  38. package/components/image-viewer/video/index.module.css.js +1 -1
  39. package/components/image-viewer/video/index.module.css.js.map +1 -1
  40. package/components/info-bar/Component.js +1 -0
  41. package/components/info-bar/Component.js.map +1 -1
  42. package/components/info-bar/index.css +3 -3
  43. package/components/info-bar/index.module.css.js +1 -1
  44. package/components/navigation-bar/Component.js +56 -26
  45. package/components/navigation-bar/Component.js.map +1 -1
  46. package/components/navigation-bar/index.css +21 -11
  47. package/components/navigation-bar/index.module.css.js +1 -1
  48. package/components/navigation-bar/index.module.css.js.map +1 -1
  49. package/components/subtitles/index.css +3 -3
  50. package/components/subtitles/index.module.css.js +1 -1
  51. package/constants.d.ts +1 -1
  52. package/constants.js +1 -1
  53. package/constants.js.map +1 -1
  54. package/cssm/Component.js +51 -7
  55. package/cssm/Component.js.map +1 -1
  56. package/cssm/components/buttons/index.d.ts +3 -1
  57. package/cssm/components/buttons/index.js +54 -17
  58. package/cssm/components/buttons/index.js.map +1 -1
  59. package/cssm/components/header/Component.js +1 -0
  60. package/cssm/components/header/Component.js.map +1 -1
  61. package/cssm/components/header/index.module.css +2 -1
  62. package/cssm/components/header-info-block/Component.js +2 -1
  63. package/cssm/components/header-info-block/Component.js.map +1 -1
  64. package/cssm/components/image-preview/Component.js +2 -1
  65. package/cssm/components/image-preview/Component.js.map +1 -1
  66. package/cssm/components/image-preview/index.module.css +3 -3
  67. package/cssm/components/image-viewer/hooks.js +3 -2
  68. package/cssm/components/image-viewer/hooks.js.map +1 -1
  69. package/cssm/components/image-viewer/index.module.css +2 -3
  70. package/cssm/components/image-viewer/single.js +1 -0
  71. package/cssm/components/image-viewer/single.js.map +1 -1
  72. package/cssm/components/image-viewer/slide.js +3 -2
  73. package/cssm/components/image-viewer/slide.js.map +1 -1
  74. package/cssm/components/image-viewer/video/index.js +50 -15
  75. package/cssm/components/image-viewer/video/index.js.map +1 -1
  76. package/cssm/components/image-viewer/video/index.module.css +15 -3
  77. package/cssm/components/info-bar/Component.js +1 -0
  78. package/cssm/components/info-bar/Component.js.map +1 -1
  79. package/cssm/components/navigation-bar/Component.js +56 -26
  80. package/cssm/components/navigation-bar/Component.js.map +1 -1
  81. package/cssm/components/navigation-bar/index.module.css +13 -3
  82. package/cssm/constants.d.ts +1 -1
  83. package/cssm/constants.js +1 -1
  84. package/cssm/constants.js.map +1 -1
  85. package/cssm/hooks/use-custom-subtitles.js +57 -11
  86. package/cssm/hooks/use-custom-subtitles.js.map +1 -1
  87. package/cssm/index.js +1 -0
  88. package/cssm/index.js.map +1 -1
  89. package/cssm/types.d.ts +4 -2
  90. package/cssm/utils/index.d.ts +1 -0
  91. package/cssm/utils/index.js +2 -0
  92. package/cssm/utils/index.js.map +1 -1
  93. package/cssm/utils/modern-download.d.ts +6 -0
  94. package/cssm/utils/modern-download.js +113 -0
  95. package/cssm/utils/modern-download.js.map +1 -0
  96. package/esm/Component.js +51 -7
  97. package/esm/Component.js.map +1 -1
  98. package/esm/components/bottom-button/index.css +5 -5
  99. package/esm/components/bottom-button/index.module.css.js +1 -1
  100. package/esm/components/buttons/index.css +5 -5
  101. package/esm/components/buttons/index.d.ts +3 -1
  102. package/esm/components/buttons/index.js +50 -13
  103. package/esm/components/buttons/index.js.map +1 -1
  104. package/esm/components/buttons/index.module.css.js +1 -1
  105. package/esm/components/header/Component.js +1 -0
  106. package/esm/components/header/Component.js.map +1 -1
  107. package/esm/components/header/index.css +3 -2
  108. package/esm/components/header/index.module.css.js +1 -1
  109. package/esm/components/header/index.module.css.js.map +1 -1
  110. package/esm/components/header-info-block/Component.js +2 -1
  111. package/esm/components/header-info-block/Component.js.map +1 -1
  112. package/esm/components/header-info-block/index.css +4 -4
  113. package/esm/components/header-info-block/index.module.css.js +1 -1
  114. package/esm/components/header-mobile/index.css +6 -6
  115. package/esm/components/header-mobile/index.module.css.js +1 -1
  116. package/esm/components/image-preview/Component.js +2 -1
  117. package/esm/components/image-preview/Component.js.map +1 -1
  118. package/esm/components/image-preview/index.css +20 -20
  119. package/esm/components/image-preview/index.module.css.js +1 -1
  120. package/esm/components/image-preview/index.module.css.js.map +1 -1
  121. package/esm/components/image-viewer/hooks.js +3 -2
  122. package/esm/components/image-viewer/hooks.js.map +1 -1
  123. package/esm/components/image-viewer/index.css +30 -31
  124. package/esm/components/image-viewer/index.module.css.js +1 -1
  125. package/esm/components/image-viewer/index.module.css.js.map +1 -1
  126. package/esm/components/image-viewer/single.js +1 -0
  127. package/esm/components/image-viewer/single.js.map +1 -1
  128. package/esm/components/image-viewer/slide.js +3 -2
  129. package/esm/components/image-viewer/slide.js.map +1 -1
  130. package/esm/components/image-viewer/video/index.css +22 -10
  131. package/esm/components/image-viewer/video/index.js +51 -16
  132. package/esm/components/image-viewer/video/index.js.map +1 -1
  133. package/esm/components/image-viewer/video/index.module.css.js +1 -1
  134. package/esm/components/image-viewer/video/index.module.css.js.map +1 -1
  135. package/esm/components/info-bar/Component.js +1 -0
  136. package/esm/components/info-bar/Component.js.map +1 -1
  137. package/esm/components/info-bar/index.css +3 -3
  138. package/esm/components/info-bar/index.module.css.js +1 -1
  139. package/esm/components/navigation-bar/Component.js +56 -25
  140. package/esm/components/navigation-bar/Component.js.map +1 -1
  141. package/esm/components/navigation-bar/index.css +21 -11
  142. package/esm/components/navigation-bar/index.module.css.js +1 -1
  143. package/esm/components/navigation-bar/index.module.css.js.map +1 -1
  144. package/esm/components/subtitles/index.css +3 -3
  145. package/esm/components/subtitles/index.module.css.js +1 -1
  146. package/esm/constants.d.ts +1 -1
  147. package/esm/constants.js +1 -1
  148. package/esm/constants.js.map +1 -1
  149. package/esm/hooks/use-custom-subtitles.js +57 -11
  150. package/esm/hooks/use-custom-subtitles.js.map +1 -1
  151. package/esm/index.css +9 -9
  152. package/esm/index.js +1 -0
  153. package/esm/index.js.map +1 -1
  154. package/esm/index.module.css.js +1 -1
  155. package/esm/types.d.ts +4 -2
  156. package/esm/utils/index.d.ts +1 -0
  157. package/esm/utils/index.js +1 -0
  158. package/esm/utils/index.js.map +1 -1
  159. package/esm/utils/modern-download.d.ts +6 -0
  160. package/esm/utils/modern-download.js +109 -0
  161. package/esm/utils/modern-download.js.map +1 -0
  162. package/hooks/use-custom-subtitles.js +57 -11
  163. package/hooks/use-custom-subtitles.js.map +1 -1
  164. package/index.css +9 -9
  165. package/index.js +1 -0
  166. package/index.js.map +1 -1
  167. package/index.module.css.js +1 -1
  168. package/modern/Component.js +50 -7
  169. package/modern/Component.js.map +1 -1
  170. package/modern/components/bottom-button/index.css +5 -5
  171. package/modern/components/bottom-button/index.module.css.js +1 -1
  172. package/modern/components/buttons/index.css +5 -5
  173. package/modern/components/buttons/index.d.ts +3 -1
  174. package/modern/components/buttons/index.js +51 -12
  175. package/modern/components/buttons/index.js.map +1 -1
  176. package/modern/components/buttons/index.module.css.js +1 -1
  177. package/modern/components/header/index.css +3 -2
  178. package/modern/components/header/index.module.css.js +1 -1
  179. package/modern/components/header/index.module.css.js.map +1 -1
  180. package/modern/components/header-info-block/Component.js +1 -1
  181. package/modern/components/header-info-block/Component.js.map +1 -1
  182. package/modern/components/header-info-block/index.css +4 -4
  183. package/modern/components/header-info-block/index.module.css.js +1 -1
  184. package/modern/components/header-mobile/index.css +6 -6
  185. package/modern/components/header-mobile/index.module.css.js +1 -1
  186. package/modern/components/image-preview/Component.js +1 -1
  187. package/modern/components/image-preview/Component.js.map +1 -1
  188. package/modern/components/image-preview/index.css +20 -20
  189. package/modern/components/image-preview/index.module.css.js +1 -1
  190. package/modern/components/image-preview/index.module.css.js.map +1 -1
  191. package/modern/components/image-viewer/hooks.js +3 -2
  192. package/modern/components/image-viewer/hooks.js.map +1 -1
  193. package/modern/components/image-viewer/index.css +30 -31
  194. package/modern/components/image-viewer/index.module.css.js +1 -1
  195. package/modern/components/image-viewer/index.module.css.js.map +1 -1
  196. package/modern/components/image-viewer/slide.js +2 -2
  197. package/modern/components/image-viewer/slide.js.map +1 -1
  198. package/modern/components/image-viewer/video/index.css +22 -10
  199. package/modern/components/image-viewer/video/index.js +47 -12
  200. package/modern/components/image-viewer/video/index.js.map +1 -1
  201. package/modern/components/image-viewer/video/index.module.css.js +1 -1
  202. package/modern/components/image-viewer/video/index.module.css.js.map +1 -1
  203. package/modern/components/info-bar/index.css +3 -3
  204. package/modern/components/info-bar/index.module.css.js +1 -1
  205. package/modern/components/navigation-bar/Component.js +55 -25
  206. package/modern/components/navigation-bar/Component.js.map +1 -1
  207. package/modern/components/navigation-bar/index.css +21 -11
  208. package/modern/components/navigation-bar/index.module.css.js +1 -1
  209. package/modern/components/navigation-bar/index.module.css.js.map +1 -1
  210. package/modern/components/subtitles/index.css +3 -3
  211. package/modern/components/subtitles/index.module.css.js +1 -1
  212. package/modern/constants.d.ts +1 -1
  213. package/modern/constants.js +1 -1
  214. package/modern/constants.js.map +1 -1
  215. package/modern/hooks/use-custom-subtitles.js +56 -9
  216. package/modern/hooks/use-custom-subtitles.js.map +1 -1
  217. package/modern/index.css +9 -9
  218. package/modern/index.module.css.js +1 -1
  219. package/modern/types.d.ts +4 -2
  220. package/modern/utils/index.d.ts +1 -0
  221. package/modern/utils/index.js +1 -0
  222. package/modern/utils/index.js.map +1 -1
  223. package/modern/utils/modern-download.d.ts +6 -0
  224. package/modern/utils/modern-download.js +76 -0
  225. package/modern/utils/modern-download.js.map +1 -0
  226. package/moderncssm/Component.js +50 -7
  227. package/moderncssm/Component.js.map +1 -1
  228. package/moderncssm/components/buttons/index.d.ts +3 -1
  229. package/moderncssm/components/buttons/index.js +51 -12
  230. package/moderncssm/components/buttons/index.js.map +1 -1
  231. package/moderncssm/components/header/index.module.css +1 -1
  232. package/moderncssm/components/header-info-block/Component.js +1 -1
  233. package/moderncssm/components/header-info-block/Component.js.map +1 -1
  234. package/moderncssm/components/image-preview/Component.js +1 -1
  235. package/moderncssm/components/image-preview/Component.js.map +1 -1
  236. package/moderncssm/components/image-preview/index.module.css +2 -2
  237. package/moderncssm/components/image-viewer/hooks.js +3 -2
  238. package/moderncssm/components/image-viewer/hooks.js.map +1 -1
  239. package/moderncssm/components/image-viewer/index.module.css +2 -2
  240. package/moderncssm/components/image-viewer/slide.js +2 -2
  241. package/moderncssm/components/image-viewer/slide.js.map +1 -1
  242. package/moderncssm/components/image-viewer/video/index.js +47 -12
  243. package/moderncssm/components/image-viewer/video/index.js.map +1 -1
  244. package/moderncssm/components/image-viewer/video/index.module.css +16 -3
  245. package/moderncssm/components/navigation-bar/Component.js +55 -25
  246. package/moderncssm/components/navigation-bar/Component.js.map +1 -1
  247. package/moderncssm/components/navigation-bar/index.module.css +14 -2
  248. package/moderncssm/constants.d.ts +1 -1
  249. package/moderncssm/constants.js +1 -1
  250. package/moderncssm/constants.js.map +1 -1
  251. package/moderncssm/hooks/use-custom-subtitles.js +56 -9
  252. package/moderncssm/hooks/use-custom-subtitles.js.map +1 -1
  253. package/moderncssm/types.d.ts +4 -2
  254. package/moderncssm/utils/index.d.ts +1 -0
  255. package/moderncssm/utils/index.js +1 -0
  256. package/moderncssm/utils/index.js.map +1 -1
  257. package/moderncssm/utils/modern-download.d.ts +6 -0
  258. package/moderncssm/utils/modern-download.js +76 -0
  259. package/moderncssm/utils/modern-download.js.map +1 -0
  260. package/package.json +12 -12
  261. package/src/Component.tsx +63 -6
  262. package/src/components/buttons/index.tsx +157 -62
  263. package/src/components/header/index.module.css +1 -1
  264. package/src/components/header-info-block/Component.tsx +2 -2
  265. package/src/components/image-preview/Component.tsx +1 -1
  266. package/src/components/image-preview/index.module.css +2 -2
  267. package/src/components/image-viewer/hooks.ts +3 -2
  268. package/src/components/image-viewer/index.module.css +2 -2
  269. package/src/components/image-viewer/slide.tsx +2 -1
  270. package/src/components/image-viewer/video/index.module.css +16 -3
  271. package/src/components/image-viewer/video/index.tsx +62 -13
  272. package/src/components/navigation-bar/Component.tsx +68 -27
  273. package/src/components/navigation-bar/index.module.css +12 -2
  274. package/src/constants.ts +1 -1
  275. package/src/hooks/use-custom-subtitles.ts +72 -12
  276. package/src/types.ts +4 -2
  277. package/src/utils/index.ts +1 -0
  278. package/src/utils/modern-download.ts +107 -0
  279. package/types.d.ts +4 -2
  280. package/utils/index.d.ts +1 -0
  281. package/utils/index.js +2 -0
  282. package/utils/index.js.map +1 -1
  283. package/utils/modern-download.d.ts +6 -0
  284. package/utils/modern-download.js +113 -0
  285. package/utils/modern-download.js.map +1 -0
@@ -1,6 +1,6 @@
1
1
  import React, { useRef, useState, useContext, useCallback, useEffect } from 'react';
2
2
  import cn from 'classnames';
3
- import throttle from 'lodash/throttle';
3
+ import { isClient } from '@alfalab/core-components-shared/moderncssm';
4
4
  import { GalleryContext } from '../../context.js';
5
5
  import { getImageKey } from '../../utils/utils.js';
6
6
  import { TestIds } from '../../utils/constants.js';
@@ -8,11 +8,13 @@ import { ImagePreview } from '../image-preview/Component.js';
8
8
  import styles from './index.module.css';
9
9
 
10
10
  const MIN_SCROLL_STEP = 24;
11
- const SCROLL_SPEED = 2; // Коэффициент скорости прокрутки (можно настроить)
12
- const SCROLL_THRESHOLD = 150; // Расстояние от границы, при котором начинается прокрутка
11
+ const DRAG_CLICK_THRESHOLD = 5;
13
12
  const NavigationBar = () => {
14
13
  const containerRef = useRef(null);
15
- const [isNavMouseDowned, setIsNavMouseDowned] = useState(false);
14
+ const dragStartXRef = useRef(0);
15
+ const dragStartScrollLeftRef = useRef(0);
16
+ const suppressClickRef = useRef(false);
17
+ const [isDragging, setIsDragging] = useState(false);
16
18
  const { images, currentSlideIndex, setCurrentSlideIndex, getSwiper, setPlayingVideo, view } = useContext(GalleryContext);
17
19
  const swiper = getSwiper();
18
20
  const handlePreviewSelect = (index) => {
@@ -31,32 +33,57 @@ const NavigationBar = () => {
31
33
  });
32
34
  }
33
35
  }, []);
34
- const handlePreviewMouseDown = () => {
35
- setIsNavMouseDowned(true);
36
+ const handlePreviewMouseDown = (e) => {
37
+ if (e.button !== 0 || !containerRef.current) {
38
+ return;
39
+ }
40
+ dragStartXRef.current = e.clientX;
41
+ dragStartScrollLeftRef.current = containerRef.current.scrollLeft;
42
+ suppressClickRef.current = false;
43
+ setIsDragging(true);
36
44
  };
37
- const handlePreviewMouseUp = () => {
38
- setIsNavMouseDowned(false);
39
- if (containerRef.current) {
40
- containerRef.current.style.scrollBehavior = 'auto'; // Отключаем плавность для резкой остановки
45
+ const handlePreviewMouseUp = useCallback(() => {
46
+ setIsDragging(false);
47
+ }, []);
48
+ const dragTo = useCallback((clientX) => {
49
+ const container = containerRef.current;
50
+ if (!container) {
51
+ return;
52
+ }
53
+ const deltaX = clientX - dragStartXRef.current;
54
+ if (Math.abs(deltaX) > DRAG_CLICK_THRESHOLD) {
55
+ suppressClickRef.current = true;
41
56
  }
57
+ container.scrollLeft = dragStartScrollLeftRef.current - deltaX;
58
+ }, []);
59
+ const handleMouseNavigationMove = (e) => {
60
+ if (!isDragging) {
61
+ return;
62
+ }
63
+ dragTo(e.clientX);
42
64
  };
43
- const handleMouseNavigationMove = throttle((e) => {
44
- if (!containerRef.current || !isNavMouseDowned)
65
+ const handleClickCapture = (e) => {
66
+ if (!suppressClickRef.current) {
45
67
  return;
46
- const container = containerRef.current;
47
- const { width: containerWidth, left: containerLeft } = container.getBoundingClientRect();
48
- const mouseX = e.clientX;
49
- // Если курсор близко к правой границе
50
- if (mouseX > containerWidth + containerLeft - SCROLL_THRESHOLD) {
51
- const scrollValue = (mouseX - (containerWidth + containerLeft - SCROLL_THRESHOLD)) * SCROLL_SPEED;
52
- container.scrollBy({ left: scrollValue, behavior: 'smooth' });
53
68
  }
54
- // Если курсор близко к левой границе
55
- else if (mouseX < containerLeft + SCROLL_THRESHOLD) {
56
- const scrollValue = (mouseX - (containerLeft + SCROLL_THRESHOLD)) * SCROLL_SPEED;
57
- container.scrollBy({ left: scrollValue, behavior: 'smooth' });
69
+ e.preventDefault();
70
+ e.stopPropagation();
71
+ suppressClickRef.current = false;
72
+ };
73
+ useEffect(() => {
74
+ if (!isClient() || !isDragging) {
75
+ return undefined;
58
76
  }
59
- }, 150);
77
+ const handleDocumentMouseMove = (event) => {
78
+ dragTo(event.clientX);
79
+ };
80
+ document.addEventListener('mousemove', handleDocumentMouseMove);
81
+ document.addEventListener('mouseup', handlePreviewMouseUp);
82
+ return () => {
83
+ document.removeEventListener('mousemove', handleDocumentMouseMove);
84
+ document.removeEventListener('mouseup', handlePreviewMouseUp);
85
+ };
86
+ }, [dragTo, handlePreviewMouseUp, isDragging]);
60
87
  const handlePreviewPosition = useCallback((preview, containerWidth) => {
61
88
  const { right, left, x } = preview.getBoundingClientRect();
62
89
  if (view === 'mobile') {
@@ -82,7 +109,10 @@ const NavigationBar = () => {
82
109
  }, [currentSlideIndex, handlePreviewPosition, scroll]);
83
110
  return (
84
111
  // eslint-disable-next-line jsx-a11y/no-static-element-interactions
85
- React.createElement("div", { className: cn(styles.component, { [styles.mobile]: view === 'mobile' }), ref: containerRef, "data-test-id": TestIds.NAVIGATION_BAR, onMouseDown: handlePreviewMouseDown, onMouseUp: handlePreviewMouseUp, onMouseMove: handleMouseNavigationMove }, images.map((image, index) => {
112
+ React.createElement("div", { className: cn(styles.component, {
113
+ [styles.mobile]: view === 'mobile',
114
+ [styles.dragging]: isDragging,
115
+ }), ref: containerRef, "data-test-id": TestIds.NAVIGATION_BAR, onMouseDown: handlePreviewMouseDown, onMouseUp: handlePreviewMouseUp, onMouseMove: handleMouseNavigationMove, onClickCapture: handleClickCapture }, images.map((image, index) => {
86
116
  const active = index === currentSlideIndex;
87
117
  return (React.createElement(ImagePreview, { key: getImageKey(image, index), image: image, active: active, index: index, onSelect: handlePreviewSelect, className: cn(styles.preview, { [styles.mobile]: view === 'mobile' }) }));
88
118
  })));
@@ -1 +1 @@
1
- {"version":3,"file":"Component.js","sources":["../../../src/components/navigation-bar/Component.tsx"],"sourcesContent":["import React, { type FC, useCallback, useContext, useEffect, useRef, useState } from 'react';\nimport cn from 'classnames';\nimport throttle from 'lodash/throttle';\n\nimport { GalleryContext } from '../../context';\nimport { getImageKey, TestIds } from '../../utils';\nimport { ImagePreview } from '../image-preview';\n\nimport styles from './index.module.css';\n\nconst MIN_SCROLL_STEP = 24;\nconst SCROLL_SPEED = 2; // Коэффициент скорости прокрутки (можно настроить)\nconst SCROLL_THRESHOLD = 150; // Расстояние от границы, при котором начинается прокрутка\n\nexport const NavigationBar: FC = () => {\n const containerRef = useRef<HTMLDivElement>(null);\n\n const [isNavMouseDowned, setIsNavMouseDowned] = useState<boolean>(false);\n\n const { images, currentSlideIndex, setCurrentSlideIndex, getSwiper, setPlayingVideo, view } =\n useContext(GalleryContext);\n\n const swiper = getSwiper();\n\n const handlePreviewSelect = (index: number) => {\n setCurrentSlideIndex?.(index);\n\n if (swiper) {\n setPlayingVideo(true);\n swiper.slideTo(index);\n }\n };\n\n const scroll = useCallback((scrollValue: number) => {\n if (containerRef.current) {\n containerRef.current.scroll({\n top: 0,\n left: containerRef.current.scrollLeft + scrollValue,\n behavior: 'smooth',\n });\n }\n }, []);\n\n const handlePreviewMouseDown = () => {\n setIsNavMouseDowned(true);\n };\n\n const handlePreviewMouseUp = () => {\n setIsNavMouseDowned(false);\n if (containerRef.current) {\n containerRef.current.style.scrollBehavior = 'auto'; // Отключаем плавность для резкой остановки\n }\n };\n\n const handleMouseNavigationMove = throttle((e: React.MouseEvent<HTMLDivElement>) => {\n if (!containerRef.current || !isNavMouseDowned) return;\n\n const container = containerRef.current;\n const { width: containerWidth, left: containerLeft } = container.getBoundingClientRect();\n const mouseX = e.clientX;\n\n // Если курсор близко к правой границе\n if (mouseX > containerWidth + containerLeft - SCROLL_THRESHOLD) {\n const scrollValue =\n (mouseX - (containerWidth + containerLeft - SCROLL_THRESHOLD)) * SCROLL_SPEED;\n\n container.scrollBy({ left: scrollValue, behavior: 'smooth' });\n }\n // Если курсор близко к левой границе\n else if (mouseX < containerLeft + SCROLL_THRESHOLD) {\n const scrollValue = (mouseX - (containerLeft + SCROLL_THRESHOLD)) * SCROLL_SPEED;\n\n container.scrollBy({ left: scrollValue, behavior: 'smooth' });\n }\n }, 150);\n\n const handlePreviewPosition = useCallback(\n (preview: Element, containerWidth: number) => {\n const { right, left, x } = preview.getBoundingClientRect();\n\n if (view === 'mobile') {\n scroll(x - containerWidth / 2 + MIN_SCROLL_STEP); // Передвигаем в центр активное превью\n }\n\n if (right > containerWidth) {\n const scrollValue = right - containerWidth + MIN_SCROLL_STEP;\n\n scroll(scrollValue);\n } else if (left < 0) {\n const scrollValue = left - MIN_SCROLL_STEP;\n\n scroll(scrollValue);\n }\n },\n [scroll, view],\n );\n\n useEffect(() => {\n if (containerRef.current) {\n const { width: containerWidth } = containerRef.current.getBoundingClientRect();\n\n const activePreview = containerRef.current.children[currentSlideIndex];\n\n if (activePreview) {\n handlePreviewPosition(activePreview, containerWidth);\n }\n }\n }, [currentSlideIndex, handlePreviewPosition, scroll]);\n\n return (\n // eslint-disable-next-line jsx-a11y/no-static-element-interactions\n <div\n className={cn(styles.component, { [styles.mobile]: view === 'mobile' })}\n ref={containerRef}\n data-test-id={TestIds.NAVIGATION_BAR}\n onMouseDown={handlePreviewMouseDown}\n onMouseUp={handlePreviewMouseUp}\n onMouseMove={handleMouseNavigationMove}\n >\n {images.map((image, index) => {\n const active = index === currentSlideIndex;\n\n return (\n <ImagePreview\n key={getImageKey(image, index)}\n image={image}\n active={active}\n index={index}\n onSelect={handlePreviewSelect}\n className={cn(styles.preview, { [styles.mobile]: view === 'mobile' })}\n />\n );\n })}\n </div>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;AAUA,MAAM,eAAe,GAAG,EAAE;AAC1B,MAAM,YAAY,GAAG,CAAC,CAAC;AACvB,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAEtB,MAAM,aAAa,GAAO,MAAK;AAClC,IAAA,MAAM,YAAY,GAAG,MAAM,CAAiB,IAAI,CAAC;IAEjD,MAAM,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC;AAExE,IAAA,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,SAAS,EAAE,eAAe,EAAE,IAAI,EAAE,GACvF,UAAU,CAAC,cAAc,CAAC;AAE9B,IAAA,MAAM,MAAM,GAAG,SAAS,EAAE;AAE1B,IAAA,MAAM,mBAAmB,GAAG,CAAC,KAAa,KAAI;AAC1C,QAAA,oBAAoB,GAAG,KAAK,CAAC;QAE7B,IAAI,MAAM,EAAE;YACR,eAAe,CAAC,IAAI,CAAC;AACrB,YAAA,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;;AAE7B,KAAC;AAED,IAAA,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,WAAmB,KAAI;AAC/C,QAAA,IAAI,YAAY,CAAC,OAAO,EAAE;AACtB,YAAA,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC;AACxB,gBAAA,GAAG,EAAE,CAAC;AACN,gBAAA,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,UAAU,GAAG,WAAW;AACnD,gBAAA,QAAQ,EAAE,QAAQ;AACrB,aAAA,CAAC;;KAET,EAAE,EAAE,CAAC;IAEN,MAAM,sBAAsB,GAAG,MAAK;QAChC,mBAAmB,CAAC,IAAI,CAAC;AAC7B,KAAC;IAED,MAAM,oBAAoB,GAAG,MAAK;QAC9B,mBAAmB,CAAC,KAAK,CAAC;AAC1B,QAAA,IAAI,YAAY,CAAC,OAAO,EAAE;YACtB,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,GAAG,MAAM,CAAC;;AAE3D,KAAC;AAED,IAAA,MAAM,yBAAyB,GAAG,QAAQ,CAAC,CAAC,CAAmC,KAAI;AAC/E,QAAA,IAAI,CAAC,YAAY,CAAC,OAAO,IAAI,CAAC,gBAAgB;YAAE;AAEhD,QAAA,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO;AACtC,QAAA,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,SAAS,CAAC,qBAAqB,EAAE;AACxF,QAAA,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO;;QAGxB,IAAI,MAAM,GAAG,cAAc,GAAG,aAAa,GAAG,gBAAgB,EAAE;AAC5D,YAAA,MAAM,WAAW,GACb,CAAC,MAAM,IAAI,cAAc,GAAG,aAAa,GAAG,gBAAgB,CAAC,IAAI,YAAY;AAEjF,YAAA,SAAS,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;;;AAG5D,aAAA,IAAI,MAAM,GAAG,aAAa,GAAG,gBAAgB,EAAE;AAChD,YAAA,MAAM,WAAW,GAAG,CAAC,MAAM,IAAI,aAAa,GAAG,gBAAgB,CAAC,IAAI,YAAY;AAEhF,YAAA,SAAS,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;;KAEpE,EAAE,GAAG,CAAC;IAEP,MAAM,qBAAqB,GAAG,WAAW,CACrC,CAAC,OAAgB,EAAE,cAAsB,KAAI;AACzC,QAAA,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,qBAAqB,EAAE;AAE1D,QAAA,IAAI,IAAI,KAAK,QAAQ,EAAE;YACnB,MAAM,CAAC,CAAC,GAAG,cAAc,GAAG,CAAC,GAAG,eAAe,CAAC,CAAC;;AAGrD,QAAA,IAAI,KAAK,GAAG,cAAc,EAAE;AACxB,YAAA,MAAM,WAAW,GAAG,KAAK,GAAG,cAAc,GAAG,eAAe;YAE5D,MAAM,CAAC,WAAW,CAAC;;AAChB,aAAA,IAAI,IAAI,GAAG,CAAC,EAAE;AACjB,YAAA,MAAM,WAAW,GAAG,IAAI,GAAG,eAAe;YAE1C,MAAM,CAAC,WAAW,CAAC;;AAE3B,KAAC,EACD,CAAC,MAAM,EAAE,IAAI,CAAC,CACjB;IAED,SAAS,CAAC,MAAK;AACX,QAAA,IAAI,YAAY,CAAC,OAAO,EAAE;AACtB,YAAA,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,qBAAqB,EAAE;YAE9E,MAAM,aAAa,GAAG,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YAEtE,IAAI,aAAa,EAAE;AACf,gBAAA,qBAAqB,CAAC,aAAa,EAAE,cAAc,CAAC;;;KAG/D,EAAE,CAAC,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,CAAC,CAAC;IAEtD;;IAEI,KACI,CAAA,aAAA,CAAA,KAAA,EAAA,EAAA,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,KAAK,QAAQ,EAAE,CAAC,EACvE,GAAG,EAAE,YAAY,EAAA,cAAA,EACH,OAAO,CAAC,cAAc,EACpC,WAAW,EAAE,sBAAsB,EACnC,SAAS,EAAE,oBAAoB,EAC/B,WAAW,EAAE,yBAAyB,EAAA,EAErC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAI;AACzB,QAAA,MAAM,MAAM,GAAG,KAAK,KAAK,iBAAiB;QAE1C,QACI,oBAAC,YAAY,EAAA,EACT,GAAG,EAAE,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,EAC9B,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,mBAAmB,EAC7B,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,KAAK,QAAQ,EAAE,CAAC,EACvE,CAAA;KAET,CAAC,CACA;AAEd;;;;"}
1
+ {"version":3,"file":"Component.js","sources":["../../../src/components/navigation-bar/Component.tsx"],"sourcesContent":["import React, { type FC, useCallback, useContext, useEffect, useRef, useState } from 'react';\nimport cn from 'classnames';\n\nimport { isClient } from '@alfalab/core-components-shared';\n\nimport { GalleryContext } from '../../context';\nimport { getImageKey, TestIds } from '../../utils';\nimport { ImagePreview } from '../image-preview';\n\nimport styles from './index.module.css';\n\nconst MIN_SCROLL_STEP = 24;\nconst DRAG_CLICK_THRESHOLD = 5;\n\nexport const NavigationBar: FC = () => {\n const containerRef = useRef<HTMLDivElement>(null);\n const dragStartXRef = useRef<number>(0);\n const dragStartScrollLeftRef = useRef<number>(0);\n const suppressClickRef = useRef<boolean>(false);\n\n const [isDragging, setIsDragging] = useState<boolean>(false);\n\n const { images, currentSlideIndex, setCurrentSlideIndex, getSwiper, setPlayingVideo, view } =\n useContext(GalleryContext);\n\n const swiper = getSwiper();\n\n const handlePreviewSelect = (index: number) => {\n setCurrentSlideIndex?.(index);\n\n if (swiper) {\n setPlayingVideo(true);\n swiper.slideTo(index);\n }\n };\n\n const scroll = useCallback((scrollValue: number) => {\n if (containerRef.current) {\n containerRef.current.scroll({\n top: 0,\n left: containerRef.current.scrollLeft + scrollValue,\n behavior: 'smooth',\n });\n }\n }, []);\n\n const handlePreviewMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {\n if (e.button !== 0 || !containerRef.current) {\n return;\n }\n\n dragStartXRef.current = e.clientX;\n dragStartScrollLeftRef.current = containerRef.current.scrollLeft;\n suppressClickRef.current = false;\n setIsDragging(true);\n };\n\n const handlePreviewMouseUp = useCallback(() => {\n setIsDragging(false);\n }, []);\n\n const dragTo = useCallback((clientX: number) => {\n const container = containerRef.current;\n\n if (!container) {\n return;\n }\n\n const deltaX = clientX - dragStartXRef.current;\n\n if (Math.abs(deltaX) > DRAG_CLICK_THRESHOLD) {\n suppressClickRef.current = true;\n }\n\n container.scrollLeft = dragStartScrollLeftRef.current - deltaX;\n }, []);\n\n const handleMouseNavigationMove = (e: React.MouseEvent<HTMLDivElement>) => {\n if (!isDragging) {\n return;\n }\n\n dragTo(e.clientX);\n };\n\n const handleClickCapture = (e: React.MouseEvent<HTMLDivElement>) => {\n if (!suppressClickRef.current) {\n return;\n }\n\n e.preventDefault();\n e.stopPropagation();\n suppressClickRef.current = false;\n };\n\n useEffect(() => {\n if (!isClient() || !isDragging) {\n return undefined;\n }\n\n const handleDocumentMouseMove = (event: MouseEvent) => {\n dragTo(event.clientX);\n };\n\n document.addEventListener('mousemove', handleDocumentMouseMove);\n document.addEventListener('mouseup', handlePreviewMouseUp);\n\n return () => {\n document.removeEventListener('mousemove', handleDocumentMouseMove);\n document.removeEventListener('mouseup', handlePreviewMouseUp);\n };\n }, [dragTo, handlePreviewMouseUp, isDragging]);\n\n const handlePreviewPosition = useCallback(\n (preview: Element, containerWidth: number) => {\n const { right, left, x } = preview.getBoundingClientRect();\n\n if (view === 'mobile') {\n scroll(x - containerWidth / 2 + MIN_SCROLL_STEP); // Передвигаем в центр активное превью\n }\n\n if (right > containerWidth) {\n const scrollValue = right - containerWidth + MIN_SCROLL_STEP;\n\n scroll(scrollValue);\n } else if (left < 0) {\n const scrollValue = left - MIN_SCROLL_STEP;\n\n scroll(scrollValue);\n }\n },\n [scroll, view],\n );\n\n useEffect(() => {\n if (containerRef.current) {\n const { width: containerWidth } = containerRef.current.getBoundingClientRect();\n\n const activePreview = containerRef.current.children[currentSlideIndex];\n\n if (activePreview) {\n handlePreviewPosition(activePreview, containerWidth);\n }\n }\n }, [currentSlideIndex, handlePreviewPosition, scroll]);\n\n return (\n // eslint-disable-next-line jsx-a11y/no-static-element-interactions\n <div\n className={cn(styles.component, {\n [styles.mobile]: view === 'mobile',\n [styles.dragging]: isDragging,\n })}\n ref={containerRef}\n data-test-id={TestIds.NAVIGATION_BAR}\n onMouseDown={handlePreviewMouseDown}\n onMouseUp={handlePreviewMouseUp}\n onMouseMove={handleMouseNavigationMove}\n onClickCapture={handleClickCapture}\n >\n {images.map((image, index) => {\n const active = index === currentSlideIndex;\n\n return (\n <ImagePreview\n key={getImageKey(image, index)}\n image={image}\n active={active}\n index={index}\n onSelect={handlePreviewSelect}\n className={cn(styles.preview, { [styles.mobile]: view === 'mobile' })}\n />\n );\n })}\n </div>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;AAWA,MAAM,eAAe,GAAG,EAAE;AAC1B,MAAM,oBAAoB,GAAG,CAAC;AAEvB,MAAM,aAAa,GAAO,MAAK;AAClC,IAAA,MAAM,YAAY,GAAG,MAAM,CAAiB,IAAI,CAAC;AACjD,IAAA,MAAM,aAAa,GAAG,MAAM,CAAS,CAAC,CAAC;AACvC,IAAA,MAAM,sBAAsB,GAAG,MAAM,CAAS,CAAC,CAAC;AAChD,IAAA,MAAM,gBAAgB,GAAG,MAAM,CAAU,KAAK,CAAC;IAE/C,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC;AAE5D,IAAA,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,SAAS,EAAE,eAAe,EAAE,IAAI,EAAE,GACvF,UAAU,CAAC,cAAc,CAAC;AAE9B,IAAA,MAAM,MAAM,GAAG,SAAS,EAAE;AAE1B,IAAA,MAAM,mBAAmB,GAAG,CAAC,KAAa,KAAI;AAC1C,QAAA,oBAAoB,GAAG,KAAK,CAAC;QAE7B,IAAI,MAAM,EAAE;YACR,eAAe,CAAC,IAAI,CAAC;AACrB,YAAA,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;;AAE7B,KAAC;AAED,IAAA,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,WAAmB,KAAI;AAC/C,QAAA,IAAI,YAAY,CAAC,OAAO,EAAE;AACtB,YAAA,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC;AACxB,gBAAA,GAAG,EAAE,CAAC;AACN,gBAAA,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,UAAU,GAAG,WAAW;AACnD,gBAAA,QAAQ,EAAE,QAAQ;AACrB,aAAA,CAAC;;KAET,EAAE,EAAE,CAAC;AAEN,IAAA,MAAM,sBAAsB,GAAG,CAAC,CAAmC,KAAI;QACnE,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;YACzC;;AAGJ,QAAA,aAAa,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO;QACjC,sBAAsB,CAAC,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,UAAU;AAChE,QAAA,gBAAgB,CAAC,OAAO,GAAG,KAAK;QAChC,aAAa,CAAC,IAAI,CAAC;AACvB,KAAC;AAED,IAAA,MAAM,oBAAoB,GAAG,WAAW,CAAC,MAAK;QAC1C,aAAa,CAAC,KAAK,CAAC;KACvB,EAAE,EAAE,CAAC;AAEN,IAAA,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,OAAe,KAAI;AAC3C,QAAA,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO;QAEtC,IAAI,CAAC,SAAS,EAAE;YACZ;;AAGJ,QAAA,MAAM,MAAM,GAAG,OAAO,GAAG,aAAa,CAAC,OAAO;QAE9C,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,oBAAoB,EAAE;AACzC,YAAA,gBAAgB,CAAC,OAAO,GAAG,IAAI;;QAGnC,SAAS,CAAC,UAAU,GAAG,sBAAsB,CAAC,OAAO,GAAG,MAAM;KACjE,EAAE,EAAE,CAAC;AAEN,IAAA,MAAM,yBAAyB,GAAG,CAAC,CAAmC,KAAI;QACtE,IAAI,CAAC,UAAU,EAAE;YACb;;AAGJ,QAAA,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;AACrB,KAAC;AAED,IAAA,MAAM,kBAAkB,GAAG,CAAC,CAAmC,KAAI;AAC/D,QAAA,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE;YAC3B;;QAGJ,CAAC,CAAC,cAAc,EAAE;QAClB,CAAC,CAAC,eAAe,EAAE;AACnB,QAAA,gBAAgB,CAAC,OAAO,GAAG,KAAK;AACpC,KAAC;IAED,SAAS,CAAC,MAAK;AACX,QAAA,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,UAAU,EAAE;AAC5B,YAAA,OAAO,SAAS;;AAGpB,QAAA,MAAM,uBAAuB,GAAG,CAAC,KAAiB,KAAI;AAClD,YAAA,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;AACzB,SAAC;AAED,QAAA,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,uBAAuB,CAAC;AAC/D,QAAA,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,oBAAoB,CAAC;AAE1D,QAAA,OAAO,MAAK;AACR,YAAA,QAAQ,CAAC,mBAAmB,CAAC,WAAW,EAAE,uBAAuB,CAAC;AAClE,YAAA,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,oBAAoB,CAAC;AACjE,SAAC;KACJ,EAAE,CAAC,MAAM,EAAE,oBAAoB,EAAE,UAAU,CAAC,CAAC;IAE9C,MAAM,qBAAqB,GAAG,WAAW,CACrC,CAAC,OAAgB,EAAE,cAAsB,KAAI;AACzC,QAAA,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,qBAAqB,EAAE;AAE1D,QAAA,IAAI,IAAI,KAAK,QAAQ,EAAE;YACnB,MAAM,CAAC,CAAC,GAAG,cAAc,GAAG,CAAC,GAAG,eAAe,CAAC,CAAC;;AAGrD,QAAA,IAAI,KAAK,GAAG,cAAc,EAAE;AACxB,YAAA,MAAM,WAAW,GAAG,KAAK,GAAG,cAAc,GAAG,eAAe;YAE5D,MAAM,CAAC,WAAW,CAAC;;AAChB,aAAA,IAAI,IAAI,GAAG,CAAC,EAAE;AACjB,YAAA,MAAM,WAAW,GAAG,IAAI,GAAG,eAAe;YAE1C,MAAM,CAAC,WAAW,CAAC;;AAE3B,KAAC,EACD,CAAC,MAAM,EAAE,IAAI,CAAC,CACjB;IAED,SAAS,CAAC,MAAK;AACX,QAAA,IAAI,YAAY,CAAC,OAAO,EAAE;AACtB,YAAA,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,qBAAqB,EAAE;YAE9E,MAAM,aAAa,GAAG,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YAEtE,IAAI,aAAa,EAAE;AACf,gBAAA,qBAAqB,CAAC,aAAa,EAAE,cAAc,CAAC;;;KAG/D,EAAE,CAAC,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,CAAC,CAAC;IAEtD;;AAEI,IAAA,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EACI,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE;AAC5B,YAAA,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,KAAK,QAAQ;AAClC,YAAA,CAAC,MAAM,CAAC,QAAQ,GAAG,UAAU;AAChC,SAAA,CAAC,EACF,GAAG,EAAE,YAAY,kBACH,OAAO,CAAC,cAAc,EACpC,WAAW,EAAE,sBAAsB,EACnC,SAAS,EAAE,oBAAoB,EAC/B,WAAW,EAAE,yBAAyB,EACtC,cAAc,EAAE,kBAAkB,EAEjC,EAAA,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAI;AACzB,QAAA,MAAM,MAAM,GAAG,KAAK,KAAK,iBAAiB;QAE1C,QACI,oBAAC,YAAY,EAAA,EACT,GAAG,EAAE,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,EAC9B,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,mBAAmB,EAC7B,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,KAAK,QAAQ,EAAE,CAAC,EACvE,CAAA;KAET,CAAC,CACA;AAEd;;;;"}
@@ -8,15 +8,27 @@
8
8
  flex-shrink: 0;
9
9
  overflow-x: auto;
10
10
  box-sizing: border-box;
11
- padding: var(--gap-12) var(--gap-24);
11
+ padding: var(--gap-12) var(--gap-32);
12
12
  scrollbar-width: none;
13
13
  background-color: var(--color-static-neutral-0-inverted);
14
+ cursor: grab;
15
+ -webkit-user-select: none;
16
+ -moz-user-select: none;
17
+ user-select: none;
14
18
  }
15
19
 
16
20
  .component.mobile {
17
- padding: var(--gap-8) var(--gap-24);
21
+ padding: var(--gap-8);
18
22
  }
19
23
 
24
+ .component.dragging {
25
+ cursor: grabbing;
26
+ }
27
+
28
+ .component.dragging * {
29
+ cursor: grabbing;
30
+ }
31
+
20
32
  .component::-webkit-scrollbar {
21
33
  display: none;
22
34
  }
@@ -1,4 +1,4 @@
1
- export declare const SWIPE_THRESHOLD = -250;
1
+ export declare const SWIPE_THRESHOLD = 250;
2
2
  export declare const PREVIEW_WIDTH_MOBILE = 46;
3
3
  export declare const PREVIEW_HEIGHT_MOBILE = 46;
4
4
  export declare const PREVIEW_WIDTH_DESKTOP = 56;
@@ -1,4 +1,4 @@
1
- const SWIPE_THRESHOLD = -250;
1
+ const SWIPE_THRESHOLD = 250;
2
2
  const PREVIEW_WIDTH_MOBILE = 46;
3
3
  const PREVIEW_HEIGHT_MOBILE = 46;
4
4
  const PREVIEW_WIDTH_DESKTOP = 56;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sources":["../src/constants.ts"],"sourcesContent":["export const SWIPE_THRESHOLD = -250;\n\nexport const PREVIEW_WIDTH_MOBILE = 46;\nexport const PREVIEW_HEIGHT_MOBILE = 46;\n\nexport const PREVIEW_WIDTH_DESKTOP = 56;\nexport const PREVIEW_HEIGHT_DESKTOP = 56;\n\nexport const PREVIEW_VIDEO_MULTIPLIER = 12;\n"],"names":[],"mappings":"AAAa,MAAA,eAAe,GAAG;AAExB,MAAM,oBAAoB,GAAG;AAC7B,MAAM,qBAAqB,GAAG;AAE9B,MAAM,qBAAqB,GAAG;AAC9B,MAAM,sBAAsB,GAAG;AAE/B,MAAM,wBAAwB,GAAG;;;;"}
1
+ {"version":3,"file":"constants.js","sources":["../src/constants.ts"],"sourcesContent":["export const SWIPE_THRESHOLD = 250;\n\nexport const PREVIEW_WIDTH_MOBILE = 46;\nexport const PREVIEW_HEIGHT_MOBILE = 46;\n\nexport const PREVIEW_WIDTH_DESKTOP = 56;\nexport const PREVIEW_HEIGHT_DESKTOP = 56;\n\nexport const PREVIEW_VIDEO_MULTIPLIER = 12;\n"],"names":[],"mappings":"AAAO,MAAM,eAAe,GAAG;AAExB,MAAM,oBAAoB,GAAG;AAC7B,MAAM,qBAAqB,GAAG;AAE9B,MAAM,qBAAqB,GAAG;AAC9B,MAAM,sBAAsB,GAAG;AAE/B,MAAM,wBAAwB,GAAG;;;;"}
@@ -1,7 +1,7 @@
1
1
  import { useContext, useState, useEffect } from 'react';
2
2
  import { GalleryContext } from '../context.js';
3
3
 
4
- const SUBTITLES_ANIMATION_SPEED = 0.5;
4
+ const SUBTITLES_FADE_DURATION_SECONDS = 0.5;
5
5
  const useCustomSubtitles = () => {
6
6
  const { getCurrentImageMeta } = useContext(GalleryContext);
7
7
  const meta = getCurrentImageMeta();
@@ -9,23 +9,70 @@ const useCustomSubtitles = () => {
9
9
  const [currentSub, setCurrentSub] = useState('');
10
10
  const player = meta?.player?.current;
11
11
  useEffect(() => {
12
- const intervalId = setInterval(() => {
13
- if (!player) {
14
- return;
12
+ if (!player) {
13
+ return undefined;
14
+ }
15
+ const { textTracks } = player;
16
+ let textTrack;
17
+ let hideTimeoutId;
18
+ const clearHideTimeout = () => {
19
+ if (hideTimeoutId !== undefined) {
20
+ clearTimeout(hideTimeoutId);
21
+ hideTimeoutId = undefined;
15
22
  }
16
- const textTrack = [...(Array.from(player.textTracks) || [])].find((track) => track.kind === 'subtitles');
23
+ };
24
+ const syncSubtitles = () => {
25
+ clearHideTimeout();
17
26
  const activeCue = textTrack?.activeCues?.[0];
18
27
  if (!activeCue?.text) {
19
28
  setShowSub(false);
20
29
  return;
21
30
  }
22
- const isVisible = player?.currentTime >= activeCue.startTime &&
23
- player?.currentTime <= activeCue.endTime - SUBTITLES_ANIMATION_SPEED;
31
+ const fadeStartTime = activeCue.endTime - SUBTITLES_FADE_DURATION_SECONDS;
32
+ const isVisible = player.currentTime <= fadeStartTime;
24
33
  setCurrentSub(activeCue.text);
25
34
  setShowSub(isVisible);
26
- });
35
+ if (!isVisible || player.paused || player.playbackRate <= 0) {
36
+ return;
37
+ }
38
+ const timeUntilFadeMs = ((fadeStartTime - player.currentTime) / player.playbackRate) * 1000;
39
+ hideTimeoutId = setTimeout(() => {
40
+ setShowSub(false);
41
+ }, timeUntilFadeMs);
42
+ };
43
+ const connectTextTrack = () => {
44
+ const nextTextTrack = Array.from(textTracks).find((track) => track.kind === 'subtitles');
45
+ if (nextTextTrack === textTrack) {
46
+ syncSubtitles();
47
+ return;
48
+ }
49
+ textTrack?.removeEventListener('cuechange', syncSubtitles);
50
+ textTrack = nextTextTrack;
51
+ textTrack?.addEventListener('cuechange', syncSubtitles);
52
+ syncSubtitles();
53
+ };
54
+ player.addEventListener('play', syncSubtitles);
55
+ player.addEventListener('playing', syncSubtitles);
56
+ player.addEventListener('pause', clearHideTimeout);
57
+ player.addEventListener('waiting', clearHideTimeout);
58
+ player.addEventListener('seeking', clearHideTimeout);
59
+ player.addEventListener('seeked', syncSubtitles);
60
+ player.addEventListener('ratechange', syncSubtitles);
61
+ textTracks.addEventListener('addtrack', connectTextTrack);
62
+ textTracks.addEventListener('change', connectTextTrack);
63
+ connectTextTrack();
27
64
  return () => {
28
- clearInterval(intervalId);
65
+ clearHideTimeout();
66
+ textTrack?.removeEventListener('cuechange', syncSubtitles);
67
+ player.removeEventListener('play', syncSubtitles);
68
+ player.removeEventListener('playing', syncSubtitles);
69
+ player.removeEventListener('pause', clearHideTimeout);
70
+ player.removeEventListener('waiting', clearHideTimeout);
71
+ player.removeEventListener('seeking', clearHideTimeout);
72
+ player.removeEventListener('seeked', syncSubtitles);
73
+ player.removeEventListener('ratechange', syncSubtitles);
74
+ textTracks.removeEventListener('addtrack', connectTextTrack);
75
+ textTracks.removeEventListener('change', connectTextTrack);
29
76
  };
30
77
  }, [player]);
31
78
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"use-custom-subtitles.js","sources":["../../src/hooks/use-custom-subtitles.ts"],"sourcesContent":["import { useContext, useEffect, useState } from 'react';\n\nimport { GalleryContext } from '../context';\n\nconst SUBTITLES_ANIMATION_SPEED = 0.5;\n\nexport const useCustomSubtitles = () => {\n const { getCurrentImageMeta } = useContext(GalleryContext);\n\n const meta = getCurrentImageMeta();\n\n const [showSub, setShowSub] = useState(false);\n const [currentSub, setCurrentSub] = useState('');\n\n const player = meta?.player?.current;\n\n useEffect(() => {\n const intervalId = setInterval(() => {\n if (!player) {\n return;\n }\n\n const textTrack = [...(Array.from(player.textTracks) || [])].find(\n (track) => track.kind === 'subtitles',\n );\n const activeCue = textTrack?.activeCues?.[0] as VTTCue | undefined;\n\n if (!activeCue?.text) {\n setShowSub(false);\n\n return;\n }\n\n const isVisible =\n player?.currentTime >= activeCue.startTime &&\n player?.currentTime <= activeCue.endTime - SUBTITLES_ANIMATION_SPEED;\n\n setCurrentSub(activeCue.text);\n setShowSub(isVisible);\n });\n\n return () => {\n clearInterval(intervalId);\n };\n }, [player]);\n\n return {\n showSub,\n currentSub,\n };\n};\n"],"names":[],"mappings":";;;AAIA,MAAM,yBAAyB,GAAG,GAAG;AAE9B,MAAM,kBAAkB,GAAG,MAAK;IACnC,MAAM,EAAE,mBAAmB,EAAE,GAAG,UAAU,CAAC,cAAc,CAAC;AAE1D,IAAA,MAAM,IAAI,GAAG,mBAAmB,EAAE;IAElC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;IAC7C,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC;AAEhD,IAAA,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO;IAEpC,SAAS,CAAC,MAAK;AACX,QAAA,MAAM,UAAU,GAAG,WAAW,CAAC,MAAK;YAChC,IAAI,CAAC,MAAM,EAAE;gBACT;;AAGJ,YAAA,MAAM,SAAS,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAC7D,CAAC,KAAK,KAAK,KAAK,CAAC,IAAI,KAAK,WAAW,CACxC;YACD,MAAM,SAAS,GAAG,SAAS,EAAE,UAAU,GAAG,CAAC,CAAuB;AAElE,YAAA,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE;gBAClB,UAAU,CAAC,KAAK,CAAC;gBAEjB;;YAGJ,MAAM,SAAS,GACX,MAAM,EAAE,WAAW,IAAI,SAAS,CAAC,SAAS;gBAC1C,MAAM,EAAE,WAAW,IAAI,SAAS,CAAC,OAAO,GAAG,yBAAyB;AAExE,YAAA,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC;YAC7B,UAAU,CAAC,SAAS,CAAC;AACzB,SAAC,CAAC;AAEF,QAAA,OAAO,MAAK;YACR,aAAa,CAAC,UAAU,CAAC;AAC7B,SAAC;AACL,KAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IAEZ,OAAO;QACH,OAAO;QACP,UAAU;KACb;AACL;;;;"}
1
+ {"version":3,"file":"use-custom-subtitles.js","sources":["../../src/hooks/use-custom-subtitles.ts"],"sourcesContent":["import { useContext, useEffect, useState } from 'react';\n\nimport { GalleryContext } from '../context';\n\nconst SUBTITLES_FADE_DURATION_SECONDS = 0.5;\n\nexport const useCustomSubtitles = () => {\n const { getCurrentImageMeta } = useContext(GalleryContext);\n\n const meta = getCurrentImageMeta();\n\n const [showSub, setShowSub] = useState(false);\n const [currentSub, setCurrentSub] = useState('');\n\n const player = meta?.player?.current;\n\n useEffect(() => {\n if (!player) {\n return undefined;\n }\n\n const { textTracks } = player;\n\n let textTrack: TextTrack | undefined;\n let hideTimeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const clearHideTimeout = () => {\n if (hideTimeoutId !== undefined) {\n clearTimeout(hideTimeoutId);\n hideTimeoutId = undefined;\n }\n };\n\n const syncSubtitles = () => {\n clearHideTimeout();\n\n const activeCue = textTrack?.activeCues?.[0] as VTTCue | undefined;\n\n if (!activeCue?.text) {\n setShowSub(false);\n\n return;\n }\n\n const fadeStartTime = activeCue.endTime - SUBTITLES_FADE_DURATION_SECONDS;\n const isVisible = player.currentTime <= fadeStartTime;\n\n setCurrentSub(activeCue.text);\n setShowSub(isVisible);\n\n if (!isVisible || player.paused || player.playbackRate <= 0) {\n return;\n }\n\n const timeUntilFadeMs =\n ((fadeStartTime - player.currentTime) / player.playbackRate) * 1000;\n\n hideTimeoutId = setTimeout(() => {\n setShowSub(false);\n }, timeUntilFadeMs);\n };\n\n const connectTextTrack = () => {\n const nextTextTrack = Array.from(textTracks).find(\n (track) => track.kind === 'subtitles',\n );\n\n if (nextTextTrack === textTrack) {\n syncSubtitles();\n\n return;\n }\n\n textTrack?.removeEventListener('cuechange', syncSubtitles);\n textTrack = nextTextTrack;\n textTrack?.addEventListener('cuechange', syncSubtitles);\n syncSubtitles();\n };\n\n player.addEventListener('play', syncSubtitles);\n player.addEventListener('playing', syncSubtitles);\n player.addEventListener('pause', clearHideTimeout);\n player.addEventListener('waiting', clearHideTimeout);\n player.addEventListener('seeking', clearHideTimeout);\n player.addEventListener('seeked', syncSubtitles);\n player.addEventListener('ratechange', syncSubtitles);\n textTracks.addEventListener('addtrack', connectTextTrack);\n textTracks.addEventListener('change', connectTextTrack);\n\n connectTextTrack();\n\n return () => {\n clearHideTimeout();\n textTrack?.removeEventListener('cuechange', syncSubtitles);\n player.removeEventListener('play', syncSubtitles);\n player.removeEventListener('playing', syncSubtitles);\n player.removeEventListener('pause', clearHideTimeout);\n player.removeEventListener('waiting', clearHideTimeout);\n player.removeEventListener('seeking', clearHideTimeout);\n player.removeEventListener('seeked', syncSubtitles);\n player.removeEventListener('ratechange', syncSubtitles);\n textTracks.removeEventListener('addtrack', connectTextTrack);\n textTracks.removeEventListener('change', connectTextTrack);\n };\n }, [player]);\n\n return {\n showSub,\n currentSub,\n };\n};\n"],"names":[],"mappings":"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"}
@@ -40,9 +40,11 @@ export type ImageMeta = {
40
40
  height: number;
41
41
  broken?: boolean;
42
42
  player?: never;
43
+ loaded?: boolean;
43
44
  } | {
44
- width?: never;
45
- height?: never;
45
+ width?: number;
46
+ height?: number;
46
47
  broken?: boolean;
47
48
  player: RefObject<HTMLVideoElement>;
49
+ loaded?: boolean;
48
50
  };
@@ -1,3 +1,4 @@
1
1
  export * from './split-filename';
2
2
  export * from './utils';
3
3
  export * from './constants';
4
+ export * from './modern-download';
@@ -1,4 +1,5 @@
1
1
  export { splitFilename } from './split-filename.js';
2
2
  export { PLACEHOLDER_HEIGHT, PLACEHOLDER_WIDTH, getImageAlt, getImageKey, isSmallImage, isVideo } from './utils.js';
3
3
  export { TestIds } from './constants.js';
4
+ export { downloadFile } from './modern-download.js';
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
@@ -0,0 +1,6 @@
1
+ export interface DownloadOptions {
2
+ url: string;
3
+ fileName?: string;
4
+ fileType?: string;
5
+ }
6
+ export declare const downloadFile: ({ url, fileName, fileType, }: DownloadOptions) => Promise<void>;
@@ -0,0 +1,76 @@
1
+ const downloadWithBlob = (blob, fileName) => {
2
+ const url = URL.createObjectURL(blob);
3
+ const link = document.createElement('a');
4
+ link.href = url;
5
+ link.download = fileName;
6
+ document.body.appendChild(link);
7
+ link.click();
8
+ document.body.removeChild(link);
9
+ setTimeout(() => URL.revokeObjectURL(url), 100);
10
+ };
11
+ const downloadWithLink = (url, fileName) => {
12
+ const link = document.createElement('a');
13
+ link.href = url;
14
+ link.download = fileName;
15
+ link.target = '_blank';
16
+ link.rel = 'noopener noreferrer';
17
+ document.body.appendChild(link);
18
+ link.click();
19
+ document.body.removeChild(link);
20
+ };
21
+ const canUseBlobDownload = () => typeof fetch === 'function' &&
22
+ typeof URL.createObjectURL === 'function' &&
23
+ typeof URL.revokeObjectURL === 'function';
24
+ const canUseFileShare = () => typeof File === 'function' &&
25
+ typeof navigator !== 'undefined' &&
26
+ typeof navigator.share === 'function' &&
27
+ typeof navigator.canShare === 'function';
28
+ const canShareFiles = (files) => canUseFileShare() && navigator.canShare({ files });
29
+ const getBlob = async (url) => {
30
+ const response = await fetch(url);
31
+ if (!response.ok) {
32
+ throw new Error('Network error');
33
+ }
34
+ return response.blob();
35
+ };
36
+ const isAbortError = (error) => typeof DOMException !== 'undefined' &&
37
+ error instanceof DOMException &&
38
+ error.name === 'AbortError';
39
+ const downloadFile = async ({ url, fileName = 'download', fileType, }) => {
40
+ if (!canUseBlobDownload()) {
41
+ downloadWithLink(url, fileName);
42
+ return;
43
+ }
44
+ if (canUseFileShare() && canShareFiles([new File([], fileName, { type: fileType })])) {
45
+ try {
46
+ const blob = await getBlob(url);
47
+ const file = new File([blob], fileName, { type: blob.type || fileType });
48
+ if (!canShareFiles([file])) {
49
+ downloadWithBlob(blob, fileName);
50
+ return;
51
+ }
52
+ await navigator.share({
53
+ files: [file],
54
+ title: `Скачать ${fileName}`,
55
+ });
56
+ return;
57
+ }
58
+ catch (error) {
59
+ if (isAbortError(error)) {
60
+ return;
61
+ }
62
+ downloadWithLink(url, fileName);
63
+ return;
64
+ }
65
+ }
66
+ try {
67
+ const blob = await getBlob(url);
68
+ downloadWithBlob(blob, fileName);
69
+ }
70
+ catch {
71
+ downloadWithLink(url, fileName);
72
+ }
73
+ };
74
+
75
+ export { downloadFile };
76
+ //# sourceMappingURL=modern-download.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modern-download.js","sources":["../../src/utils/modern-download.ts"],"sourcesContent":["const downloadWithBlob = (blob: Blob, fileName: string): void => {\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n\n link.href = url;\n link.download = fileName;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n setTimeout(() => URL.revokeObjectURL(url), 100);\n};\n\nconst downloadWithLink = (url: string, fileName: string): void => {\n const link = document.createElement('a');\n\n link.href = url;\n link.download = fileName;\n link.target = '_blank';\n link.rel = 'noopener noreferrer';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n};\n\nconst canUseBlobDownload = (): boolean =>\n typeof fetch === 'function' &&\n typeof URL.createObjectURL === 'function' &&\n typeof URL.revokeObjectURL === 'function';\n\nconst canUseFileShare = (): boolean =>\n typeof File === 'function' &&\n typeof navigator !== 'undefined' &&\n typeof navigator.share === 'function' &&\n typeof navigator.canShare === 'function';\n\nconst canShareFiles = (files: File[]): boolean =>\n canUseFileShare() && navigator.canShare({ files });\n\nconst getBlob = async (url: string): Promise<Blob> => {\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error('Network error');\n }\n\n return response.blob();\n};\n\nconst isAbortError = (error: unknown): boolean =>\n typeof DOMException !== 'undefined' &&\n error instanceof DOMException &&\n error.name === 'AbortError';\n\nexport interface DownloadOptions {\n url: string;\n fileName?: string;\n fileType?: string;\n}\n\nexport const downloadFile = async ({\n url,\n fileName = 'download',\n fileType,\n}: DownloadOptions): Promise<void> => {\n if (!canUseBlobDownload()) {\n downloadWithLink(url, fileName);\n\n return;\n }\n\n if (canUseFileShare() && canShareFiles([new File([], fileName, { type: fileType })])) {\n try {\n const blob = await getBlob(url);\n\n const file = new File([blob], fileName, { type: blob.type || fileType });\n\n if (!canShareFiles([file])) {\n downloadWithBlob(blob, fileName);\n\n return;\n }\n\n await navigator.share({\n files: [file],\n title: `Скачать ${fileName}`,\n });\n\n return;\n } catch (error) {\n if (isAbortError(error)) {\n return;\n }\n\n downloadWithLink(url, fileName);\n\n return;\n }\n }\n\n try {\n const blob = await getBlob(url);\n\n downloadWithBlob(blob, fileName);\n } catch {\n downloadWithLink(url, fileName);\n }\n};\n"],"names":[],"mappings":"AAAA,MAAM,gBAAgB,GAAG,CAAC,IAAU,EAAE,QAAgB,KAAU;IAC5D,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC;IACrC,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC;AAExC,IAAA,IAAI,CAAC,IAAI,GAAG,GAAG;AACf,IAAA,IAAI,CAAC,QAAQ,GAAG,QAAQ;AACxB,IAAA,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;IAC/B,IAAI,CAAC,KAAK,EAAE;AACZ,IAAA,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;AAC/B,IAAA,UAAU,CAAC,MAAM,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;AACnD,CAAC;AAED,MAAM,gBAAgB,GAAG,CAAC,GAAW,EAAE,QAAgB,KAAU;IAC7D,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC;AAExC,IAAA,IAAI,CAAC,IAAI,GAAG,GAAG;AACf,IAAA,IAAI,CAAC,QAAQ,GAAG,QAAQ;AACxB,IAAA,IAAI,CAAC,MAAM,GAAG,QAAQ;AACtB,IAAA,IAAI,CAAC,GAAG,GAAG,qBAAqB;AAChC,IAAA,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;IAC/B,IAAI,CAAC,KAAK,EAAE;AACZ,IAAA,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;AACnC,CAAC;AAED,MAAM,kBAAkB,GAAG,MACvB,OAAO,KAAK,KAAK,UAAU;AAC3B,IAAA,OAAO,GAAG,CAAC,eAAe,KAAK,UAAU;AACzC,IAAA,OAAO,GAAG,CAAC,eAAe,KAAK,UAAU;AAE7C,MAAM,eAAe,GAAG,MACpB,OAAO,IAAI,KAAK,UAAU;IAC1B,OAAO,SAAS,KAAK,WAAW;AAChC,IAAA,OAAO,SAAS,CAAC,KAAK,KAAK,UAAU;AACrC,IAAA,OAAO,SAAS,CAAC,QAAQ,KAAK,UAAU;AAE5C,MAAM,aAAa,GAAG,CAAC,KAAa,KAChC,eAAe,EAAE,IAAI,SAAS,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC;AAEtD,MAAM,OAAO,GAAG,OAAO,GAAW,KAAmB;AACjD,IAAA,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC;AAEjC,IAAA,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE;AACd,QAAA,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC;;AAGpC,IAAA,OAAO,QAAQ,CAAC,IAAI,EAAE;AAC1B,CAAC;AAED,MAAM,YAAY,GAAG,CAAC,KAAc,KAChC,OAAO,YAAY,KAAK,WAAW;AACnC,IAAA,KAAK,YAAY,YAAY;AAC7B,IAAA,KAAK,CAAC,IAAI,KAAK,YAAY;AAQlB,MAAA,YAAY,GAAG,OAAO,EAC/B,GAAG,EACH,QAAQ,GAAG,UAAU,EACrB,QAAQ,GACM,KAAmB;AACjC,IAAA,IAAI,CAAC,kBAAkB,EAAE,EAAE;AACvB,QAAA,gBAAgB,CAAC,GAAG,EAAE,QAAQ,CAAC;QAE/B;;IAGJ,IAAI,eAAe,EAAE,IAAI,aAAa,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE;AAClF,QAAA,IAAI;AACA,YAAA,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAE/B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,QAAQ,EAAE,CAAC;YAExE,IAAI,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE;AACxB,gBAAA,gBAAgB,CAAC,IAAI,EAAE,QAAQ,CAAC;gBAEhC;;YAGJ,MAAM,SAAS,CAAC,KAAK,CAAC;gBAClB,KAAK,EAAE,CAAC,IAAI,CAAC;gBACb,KAAK,EAAE,CAAW,QAAA,EAAA,QAAQ,CAAE,CAAA;AAC/B,aAAA,CAAC;YAEF;;QACF,OAAO,KAAK,EAAE;AACZ,YAAA,IAAI,YAAY,CAAC,KAAK,CAAC,EAAE;gBACrB;;AAGJ,YAAA,gBAAgB,CAAC,GAAG,EAAE,QAAQ,CAAC;YAE/B;;;AAIR,IAAA,IAAI;AACA,QAAA,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;AAE/B,QAAA,gBAAgB,CAAC,IAAI,EAAE,QAAQ,CAAC;;AAClC,IAAA,MAAM;AACJ,QAAA,gBAAgB,CAAC,GAAG,EAAE,QAAQ,CAAC;;AAEvC;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfalab/core-components-gallery",
3
- "version": "7.2.3-alfasans",
3
+ "version": "7.3.0",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "license": "MIT",
@@ -13,20 +13,20 @@
13
13
  "build": "rollup -c ../../tools/rollup/rollup.config.mjs --silent"
14
14
  },
15
15
  "dependencies": {
16
- "@alfalab/core-components-base-modal": "7.1.8-alfasans",
17
- "@alfalab/core-components-button": "13.2.2-alfasans",
18
- "@alfalab/core-components-icon-button": "8.0.10-alfasans",
19
- "@alfalab/core-components-icon-view": "5.0.4-alfasans",
20
- "@alfalab/core-components-mq": "6.0.7-alfasans",
21
- "@alfalab/core-components-spinner": "6.0.7-alfasans",
22
- "@alfalab/core-components-tooltip": "9.0.18-alfasans",
23
- "@alfalab/core-components-typography": "6.0.10-alfasans",
16
+ "@alfalab/core-components-base-modal": "^7.1.8",
17
+ "@alfalab/core-components-button": "^13.2.2",
18
+ "@alfalab/core-components-icon-button": "^8.0.10",
19
+ "@alfalab/core-components-icon-view": "^5.0.4",
20
+ "@alfalab/core-components-mq": "^6.0.7",
21
+ "@alfalab/core-components-shared": "^2.2.3",
22
+ "@alfalab/core-components-spinner": "^6.0.7",
23
+ "@alfalab/core-components-tooltip": "^9.0.18",
24
+ "@alfalab/core-components-typography": "^6.0.10",
24
25
  "@alfalab/hooks": "^1.17.0",
25
26
  "@alfalab/icons-glyph": "^2.260.0",
26
27
  "classnames": "^2.5.1",
27
28
  "element-closest": "^3.0.2",
28
29
  "hls.js": "^1.5.13",
29
- "lodash": "^4.17.21",
30
30
  "swiper": "^12.1.3",
31
31
  "tslib": "^2.4.0"
32
32
  },
@@ -38,6 +38,6 @@
38
38
  "access": "public",
39
39
  "directory": "dist"
40
40
  },
41
- "themesVersion": "15.2.1-alfasans",
42
- "varsVersion": "11.2.1-alfasans"
41
+ "themesVersion": "15.2.1",
42
+ "varsVersion": "11.2.1"
43
43
  }
package/src/Component.tsx CHANGED
@@ -88,6 +88,7 @@ export const Gallery: FC<GalleryProps> = ({
88
88
  const [mutedVideo, setMutedVideo] = useState<boolean>(DEFAULT_MUTED_VIDEO);
89
89
  const [playingVideo, setPlayingVideo] = useState<boolean>(DEFAULT_PLAYING_VIDEO);
90
90
  const [hideNavigation, setHideNavigation] = useState<boolean>(DEFAULT_HIDE_NAVIGATION);
91
+ const [swipeY, setSwipeY] = useState<number>(0);
91
92
 
92
93
  const isDesktop = useIsDesktop();
93
94
 
@@ -200,6 +201,12 @@ export const Gallery: FC<GalleryProps> = ({
200
201
  }
201
202
  }, [uncontrolled, currentSlideIndex, swiper]);
202
203
 
204
+ useEffect(() => {
205
+ if (!open) {
206
+ setSwipeY(0);
207
+ }
208
+ }, [open]);
209
+
203
210
  useEffect(() => {
204
211
  document.addEventListener('keydown', handleKeyDown);
205
212
 
@@ -209,14 +216,24 @@ export const Gallery: FC<GalleryProps> = ({
209
216
  }, [handleKeyDown]);
210
217
 
211
218
  useEffect(() => {
212
- let startY: number;
219
+ if (!open) {
220
+ return undefined;
221
+ }
222
+
223
+ let startX = 0;
224
+ let startY = 0;
225
+ let lockedDirection: 'horizontal' | 'vertical' | null = null;
226
+ const directionLockThreshold = 10;
227
+
213
228
  const abortController = new AbortController();
214
229
  const { signal } = abortController;
215
230
 
216
231
  document.addEventListener(
217
232
  'touchstart',
218
233
  (e) => {
234
+ startX = e.touches[0].clientX;
219
235
  startY = e.touches[0].clientY;
236
+ lockedDirection = null;
220
237
  },
221
238
  { signal },
222
239
  );
@@ -224,21 +241,57 @@ export const Gallery: FC<GalleryProps> = ({
224
241
  document.addEventListener(
225
242
  'touchmove',
226
243
  (e) => {
227
- const endY = e.changedTouches[0].clientY;
228
- const deltaY = startY - endY;
244
+ const currentX = e.touches[0].clientX;
245
+ const currentY = e.touches[0].clientY;
246
+ const deltaX = currentX - startX;
247
+ const deltaY = currentY - startY;
248
+
249
+ if (!lockedDirection) {
250
+ const absX = Math.abs(deltaX);
251
+ const absY = Math.abs(deltaY);
252
+
253
+ if (absX > absY && absX > directionLockThreshold) {
254
+ lockedDirection = 'horizontal';
255
+ } else if (absY > absX && absY > directionLockThreshold) {
256
+ lockedDirection = 'vertical';
257
+ } else {
258
+ return;
259
+ }
260
+ }
261
+
262
+ if (lockedDirection === 'horizontal') {
263
+ return;
264
+ }
229
265
 
230
- // Если свайп вниз, закрываем галерею
231
- if (deltaY < SWIPE_THRESHOLD) {
266
+ if (deltaY <= 0) {
267
+ setSwipeY(0);
268
+
269
+ return;
270
+ }
271
+
272
+ setSwipeY(deltaY);
273
+
274
+ if (deltaY > SWIPE_THRESHOLD) {
275
+ setSwipeY(0);
232
276
  onClose();
233
277
  }
234
278
  },
235
279
  { signal },
236
280
  );
237
281
 
282
+ document.addEventListener(
283
+ 'touchend',
284
+ () => {
285
+ lockedDirection = null;
286
+ setSwipeY(0);
287
+ },
288
+ { signal },
289
+ );
290
+
238
291
  return () => {
239
292
  abortController.abort();
240
293
  };
241
- }, [onClose]);
294
+ }, [onClose, open]);
242
295
 
243
296
  const singleSlide = images.length === 1;
244
297
 
@@ -282,6 +335,10 @@ export const Gallery: FC<GalleryProps> = ({
282
335
  onUnmount={onUnmount}
283
336
  >
284
337
  <div
338
+ data-content-area={true}
339
+ style={{
340
+ transform: `translateY(${swipeY}px)`,
341
+ }}
285
342
  className={cn(styles.container, {
286
343
  [styles.mobile]: !isDesktop,
287
344
  })}