@alfalab/core-components-gallery 7.2.3 → 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 +2 -2
  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,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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"}
package/modern/index.css CHANGED
@@ -11,7 +11,7 @@
11
11
  --sat: env(safe-area-inset-top, var(--gap-0));
12
12
  --sab: env(safe-area-inset-bottom, var(--gap-0));
13
13
  }
14
- .gallery__container_1ncrj {
14
+ .gallery__container_1kzqo {
15
15
  position: relative;
16
16
  overflow-x: hidden;
17
17
  overflow-y: auto;
@@ -25,17 +25,17 @@
25
25
  padding-top: var(--sat);
26
26
  padding-bottom: var(--sab);
27
27
  }
28
- .gallery__container_1ncrj.gallery__mobile_1ncrj {
28
+ .gallery__container_1kzqo.gallery__mobile_1kzqo {
29
29
  overflow: hidden;
30
30
  }
31
- .gallery__modal_1ncrj {
31
+ .gallery__modal_1kzqo {
32
32
  flex-grow: 1;
33
33
  width: 100vw;
34
34
  height: 100vh;
35
35
  height: 100dvh;
36
36
  background: transparent;
37
37
  }
38
- .gallery__navigationVideo_1ncrj {
38
+ .gallery__navigationVideo_1kzqo {
39
39
  z-index: 3;
40
40
  width: 100%;
41
41
  position: absolute;
@@ -44,18 +44,18 @@
44
44
  transition: transform 0.3s ease-in-out;
45
45
  text-align: center;
46
46
  }
47
- .gallery__hide_1ncrj {
47
+ .gallery__hide_1kzqo {
48
48
  transform: translateY(114px);
49
49
  }
50
- .gallery__hideInfo_1ncrj {
50
+ .gallery__hideInfo_1kzqo {
51
51
  transform: translateY(48px);
52
52
  }
53
- .gallery__bottomButton_1ncrj {
53
+ .gallery__bottomButton_1kzqo {
54
54
  width: calc(100% - var(--gap-32));
55
55
  margin: var(--gap-16);
56
56
  background-color: var(--color-static-neutral-translucent-500-inverted);
57
57
  }
58
- .gallery__subtitles_1ncrj {
58
+ .gallery__subtitles_1kzqo {
59
59
  padding: var(--gap-0) var(--gap-32);
60
60
  margin-bottom: var(--gap-16);
61
61
  width: 100%;
@@ -66,6 +66,6 @@
66
66
  opacity 500ms ease-in-out,
67
67
  margin-bottom 270ms ease-in-out;
68
68
  }
69
- .gallery__hideSubtitles_1ncrj {
69
+ .gallery__hideSubtitles_1kzqo {
70
70
  opacity: 0;
71
71
  }
@@ -1,6 +1,6 @@
1
1
  import './index.css';
2
2
 
3
- const styles = {"container":"gallery__container_1ncrj","mobile":"gallery__mobile_1ncrj","modal":"gallery__modal_1ncrj","navigationVideo":"gallery__navigationVideo_1ncrj","hide":"gallery__hide_1ncrj","hideInfo":"gallery__hideInfo_1ncrj","bottomButton":"gallery__bottomButton_1ncrj"};
3
+ const styles = {"container":"gallery__container_1kzqo","mobile":"gallery__mobile_1kzqo","modal":"gallery__modal_1kzqo","navigationVideo":"gallery__navigationVideo_1kzqo","hide":"gallery__hide_1kzqo","hideInfo":"gallery__hideInfo_1kzqo","bottomButton":"gallery__bottomButton_1kzqo"};
4
4
 
5
5
  export { styles as default };
6
6
  //# sourceMappingURL=index.module.css.js.map
package/modern/types.d.ts CHANGED
@@ -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":"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"}
@@ -33,6 +33,7 @@ const Gallery = ({ open, images, slideIndex, initialSlide = 0, loop = true, onCl
33
33
  const [mutedVideo, setMutedVideo] = useState(DEFAULT_MUTED_VIDEO);
34
34
  const [playingVideo, setPlayingVideo] = useState(DEFAULT_PLAYING_VIDEO);
35
35
  const [hideNavigation, setHideNavigation] = useState(DEFAULT_HIDE_NAVIGATION);
36
+ const [swipeY, setSwipeY] = useState(0);
36
37
  const isDesktop = useIsDesktop();
37
38
  const isCurrentVideo = !!imagesMeta[currentSlideIndex]?.player?.current;
38
39
  const bottomButton = images[currentSlideIndex]?.bottomButton;
@@ -111,6 +112,11 @@ const Gallery = ({ open, images, slideIndex, initialSlide = 0, loop = true, onCl
111
112
  swiper?.slideTo(currentSlideIndex);
112
113
  }
113
114
  }, [uncontrolled, currentSlideIndex, swiper]);
115
+ useEffect(() => {
116
+ if (!open) {
117
+ setSwipeY(0);
118
+ }
119
+ }, [open]);
114
120
  useEffect(() => {
115
121
  document.addEventListener('keydown', handleKeyDown);
116
122
  return () => {
@@ -118,24 +124,59 @@ const Gallery = ({ open, images, slideIndex, initialSlide = 0, loop = true, onCl
118
124
  };
119
125
  }, [handleKeyDown]);
120
126
  useEffect(() => {
121
- let startY;
127
+ if (!open) {
128
+ return undefined;
129
+ }
130
+ let startX = 0;
131
+ let startY = 0;
132
+ let lockedDirection = null;
133
+ const directionLockThreshold = 10;
122
134
  const abortController = new AbortController();
123
135
  const { signal } = abortController;
124
136
  document.addEventListener('touchstart', (e) => {
137
+ startX = e.touches[0].clientX;
125
138
  startY = e.touches[0].clientY;
139
+ lockedDirection = null;
126
140
  }, { signal });
127
141
  document.addEventListener('touchmove', (e) => {
128
- const endY = e.changedTouches[0].clientY;
129
- const deltaY = startY - endY;
130
- // Если свайп вниз, закрываем галерею
131
- if (deltaY < SWIPE_THRESHOLD) {
142
+ const currentX = e.touches[0].clientX;
143
+ const currentY = e.touches[0].clientY;
144
+ const deltaX = currentX - startX;
145
+ const deltaY = currentY - startY;
146
+ if (!lockedDirection) {
147
+ const absX = Math.abs(deltaX);
148
+ const absY = Math.abs(deltaY);
149
+ if (absX > absY && absX > directionLockThreshold) {
150
+ lockedDirection = 'horizontal';
151
+ }
152
+ else if (absY > absX && absY > directionLockThreshold) {
153
+ lockedDirection = 'vertical';
154
+ }
155
+ else {
156
+ return;
157
+ }
158
+ }
159
+ if (lockedDirection === 'horizontal') {
160
+ return;
161
+ }
162
+ if (deltaY <= 0) {
163
+ setSwipeY(0);
164
+ return;
165
+ }
166
+ setSwipeY(deltaY);
167
+ if (deltaY > SWIPE_THRESHOLD) {
168
+ setSwipeY(0);
132
169
  onClose();
133
170
  }
134
171
  }, { signal });
172
+ document.addEventListener('touchend', () => {
173
+ lockedDirection = null;
174
+ setSwipeY(0);
175
+ }, { signal });
135
176
  return () => {
136
177
  abortController.abort();
137
178
  };
138
- }, [onClose]);
179
+ }, [onClose, open]);
139
180
  const singleSlide = images.length === 1;
140
181
  const showNavigationBar = !singleSlide && !fullScreen;
141
182
  // eslint-disable-next-line react/jsx-no-constructed-context-values
@@ -167,7 +208,9 @@ const Gallery = ({ open, images, slideIndex, initialSlide = 0, loop = true, onCl
167
208
  };
168
209
  return (React.createElement(GalleryContext.Provider, { value: galleryContext },
169
210
  React.createElement(BaseModal, { open: open, className: cn(styles.modal, popupClassName), onEscapeKeyDown: handleEscapeKeyDown, Backdrop: Backdrop, onUnmount: onUnmount },
170
- React.createElement("div", { className: cn(styles.container, {
211
+ React.createElement("div", { "data-content-area": true, style: {
212
+ transform: `translateY(${swipeY}px)`,
213
+ }, className: cn(styles.container, {
171
214
  [styles.mobile]: !isDesktop,
172
215
  }) },
173
216
  isDesktop ? React.createElement(Header, null) : React.createElement(HeaderMobile, null),
@@ -1 +1 @@
1
- {"version":3,"file":"Component.js","sources":["../src/Component.tsx"],"sourcesContent":["import React, { type FC, type MouseEvent, useCallback, useEffect, useState } from 'react';\nimport cn from 'classnames';\nimport type SwiperCore from 'swiper';\n\nimport { BaseModal } from '@alfalab/core-components-base-modal';\nimport { useIsDesktop } from '@alfalab/core-components-mq';\n\nimport { BottomButton } from './components/bottom-button';\nimport { Single } from './components/image-viewer/single';\nimport { Subtitles } from './components/subtitles';\nimport { Header, HeaderMobile, ImageViewer, InfoBar, NavigationBar } from './components';\nimport { SWIPE_THRESHOLD } from './constants';\nimport { GalleryContext } from './context';\nimport { type GalleryImage, type ImageMeta } from './types';\n\nimport styles from './index.module.css';\n\nexport type GalleryProps = {\n /**\n * Управление видимостью\n */\n open: boolean;\n\n /**\n * Массив изображений\n */\n images: GalleryImage[];\n\n /**\n * Зациклить галерею\n * @default true\n */\n loop?: boolean;\n\n /**\n * Индекс открытого изображение\n * @default 0\n */\n initialSlide?: number;\n\n /**\n * Обработчик закрытия\n */\n onClose: () => void;\n\n /**\n * Индекс текущего изображения\n */\n slideIndex?: number;\n\n /**\n * Обработчик изменения текущего изображения\n */\n onSlideIndexChange?: (index: number) => void;\n\n /**\n * Дополнительный класс для попапа\n */\n popupClassName?: string;\n};\n\nconst DEFAULT_FULL_SCREEN = false;\nconst DEFAULT_MUTED_VIDEO = true;\nconst DEFAULT_PLAYING_VIDEO = true;\nconst DEFAULT_HIDE_NAVIGATION = false;\n\nconst Backdrop = () => null;\n\nexport const Gallery: FC<GalleryProps> = ({\n open,\n images,\n slideIndex,\n initialSlide = 0,\n loop = true,\n onClose,\n onSlideIndexChange,\n popupClassName,\n}) => {\n const currentSlideIndexState = useState(initialSlide);\n const uncontrolled = slideIndex === undefined;\n const [currentSlideIndex, setCurrentSlideIndex] = uncontrolled\n ? currentSlideIndexState\n : [slideIndex, onSlideIndexChange];\n\n const [swiper, setSwiper] = useState<SwiperCore>();\n const [imagesMeta, setImagesMeta] = useState<ImageMeta[]>([]);\n const [fullScreen, setFullScreen] = useState<boolean>(DEFAULT_FULL_SCREEN);\n const [mutedVideo, setMutedVideo] = useState<boolean>(DEFAULT_MUTED_VIDEO);\n const [playingVideo, setPlayingVideo] = useState<boolean>(DEFAULT_PLAYING_VIDEO);\n const [hideNavigation, setHideNavigation] = useState<boolean>(DEFAULT_HIDE_NAVIGATION);\n\n const isDesktop = useIsDesktop();\n\n const isCurrentVideo = !!imagesMeta[currentSlideIndex]?.player?.current;\n\n const bottomButton = images[currentSlideIndex]?.bottomButton;\n\n const slideTo = useCallback(\n (index: number) => {\n if (images[index]) {\n setCurrentSlideIndex?.(index);\n\n if (swiper) {\n setPlayingVideo(true);\n swiper.slideTo(index);\n }\n }\n },\n [images, setCurrentSlideIndex, swiper],\n );\n\n const slideNext = useCallback(() => {\n const lastIndex = images.length - 1;\n\n let nextIndex = currentSlideIndex + 1;\n\n if (nextIndex >= images.length) {\n nextIndex = loop ? 0 : lastIndex;\n }\n\n slideTo(nextIndex);\n }, [images.length, loop, currentSlideIndex, slideTo]);\n\n const slidePrev = useCallback(() => {\n const lastIndex = images.length - 1;\n\n let nextIndex = currentSlideIndex - 1;\n\n if (nextIndex < 0) {\n nextIndex = loop ? lastIndex : 0;\n }\n\n slideTo(nextIndex);\n }, [images.length, loop, currentSlideIndex, slideTo]);\n\n const setImageMeta = useCallback((meta: ImageMeta, index: number) => {\n setImagesMeta((prevImagesMeta) => {\n const nextImagesMeta = prevImagesMeta.slice();\n\n nextImagesMeta[index] = meta;\n\n return nextImagesMeta;\n });\n }, []);\n\n const handleBottomButtonClick = useCallback(\n (e: MouseEvent) => {\n e.stopPropagation();\n if (bottomButton?.onClick) {\n bottomButton.onClick(e);\n }\n },\n [bottomButton],\n );\n\n const handleClose = useCallback(() => {\n onClose();\n\n if (uncontrolled) {\n setCurrentSlideIndex?.(initialSlide);\n }\n\n setFullScreen(false);\n }, [initialSlide, onClose, setCurrentSlideIndex, uncontrolled]);\n\n const handleEscapeKeyDown = () => {\n if (fullScreen) {\n setFullScreen(false);\n } else {\n handleClose();\n }\n };\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent) => {\n if (!open || fullScreen) {\n return;\n }\n\n switch (event.key) {\n case 'ArrowLeft':\n slidePrev();\n break;\n case 'ArrowRight':\n slideNext();\n break;\n }\n },\n [fullScreen, open, slideNext, slidePrev],\n );\n\n const onUnmount = useCallback(() => {\n setPlayingVideo(DEFAULT_PLAYING_VIDEO);\n setMutedVideo(DEFAULT_MUTED_VIDEO);\n }, [setPlayingVideo]);\n\n useEffect(() => {\n if (!uncontrolled && !swiper?.destroyed) {\n swiper?.slideTo(currentSlideIndex);\n }\n }, [uncontrolled, currentSlideIndex, swiper]);\n\n useEffect(() => {\n document.addEventListener('keydown', handleKeyDown);\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n };\n }, [handleKeyDown]);\n\n useEffect(() => {\n let startY: number;\n const abortController = new AbortController();\n const { signal } = abortController;\n\n document.addEventListener(\n 'touchstart',\n (e) => {\n startY = e.touches[0].clientY;\n },\n { signal },\n );\n\n document.addEventListener(\n 'touchmove',\n (e) => {\n const endY = e.changedTouches[0].clientY;\n const deltaY = startY - endY;\n\n // Если свайп вниз, закрываем галерею\n if (deltaY < SWIPE_THRESHOLD) {\n onClose();\n }\n },\n { signal },\n );\n\n return () => {\n abortController.abort();\n };\n }, [onClose]);\n\n const singleSlide = images.length === 1;\n\n const showNavigationBar = !singleSlide && !fullScreen;\n\n // eslint-disable-next-line react/jsx-no-constructed-context-values\n const galleryContext: GalleryContext = {\n view: isDesktop ? 'desktop' : 'mobile',\n singleSlide,\n currentSlideIndex,\n images,\n imagesMeta,\n fullScreen,\n initialSlide: uncontrolled ? initialSlide : currentSlideIndex,\n setFullScreen,\n playingVideo,\n setPlayingVideo,\n mutedVideo,\n setMutedVideo,\n hideNavigation,\n setHideNavigation,\n setImageMeta,\n slideNext,\n slidePrev,\n slideTo,\n getSwiper: () => swiper,\n setSwiper,\n onClose: handleClose,\n setCurrentSlideIndex,\n getCurrentImage: () => images[currentSlideIndex],\n getCurrentImageMeta: () => imagesMeta[currentSlideIndex],\n };\n\n return (\n <GalleryContext.Provider value={galleryContext}>\n <BaseModal\n open={open}\n className={cn(styles.modal, popupClassName)}\n onEscapeKeyDown={handleEscapeKeyDown}\n Backdrop={Backdrop}\n onUnmount={onUnmount}\n >\n <div\n className={cn(styles.container, {\n [styles.mobile]: !isDesktop,\n })}\n >\n {isDesktop ? <Header /> : <HeaderMobile />}\n {images.length === 1 ? <Single /> : <ImageViewer />}\n <nav\n className={cn({\n [styles.navigationVideo]: isCurrentVideo && !isDesktop,\n [styles.hide]: showNavigationBar && hideNavigation && !isDesktop,\n [styles.hideInfo]: !showNavigationBar && hideNavigation && !isDesktop,\n })}\n >\n {!isDesktop && <Subtitles />}\n {isCurrentVideo && !isDesktop && bottomButton && (\n <BottomButton\n bottomButton={bottomButton}\n onClick={handleBottomButtonClick}\n className={styles.bottomButton}\n />\n )}\n\n {showNavigationBar && <NavigationBar />}\n {!isDesktop && <InfoBar />}\n </nav>\n </div>\n </BaseModal>\n </GalleryContext.Provider>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Component.js","sources":["../src/Component.tsx"],"sourcesContent":["import React, { type FC, type MouseEvent, useCallback, useEffect, useState } from 'react';\nimport cn from 'classnames';\nimport type SwiperCore from 'swiper';\n\nimport { BaseModal } from '@alfalab/core-components-base-modal';\nimport { useIsDesktop } from '@alfalab/core-components-mq';\n\nimport { BottomButton } from './components/bottom-button';\nimport { Single } from './components/image-viewer/single';\nimport { Subtitles } from './components/subtitles';\nimport { Header, HeaderMobile, ImageViewer, InfoBar, NavigationBar } from './components';\nimport { SWIPE_THRESHOLD } from './constants';\nimport { GalleryContext } from './context';\nimport { type GalleryImage, type ImageMeta } from './types';\n\nimport styles from './index.module.css';\n\nexport type GalleryProps = {\n /**\n * Управление видимостью\n */\n open: boolean;\n\n /**\n * Массив изображений\n */\n images: GalleryImage[];\n\n /**\n * Зациклить галерею\n * @default true\n */\n loop?: boolean;\n\n /**\n * Индекс открытого изображение\n * @default 0\n */\n initialSlide?: number;\n\n /**\n * Обработчик закрытия\n */\n onClose: () => void;\n\n /**\n * Индекс текущего изображения\n */\n slideIndex?: number;\n\n /**\n * Обработчик изменения текущего изображения\n */\n onSlideIndexChange?: (index: number) => void;\n\n /**\n * Дополнительный класс для попапа\n */\n popupClassName?: string;\n};\n\nconst DEFAULT_FULL_SCREEN = false;\nconst DEFAULT_MUTED_VIDEO = true;\nconst DEFAULT_PLAYING_VIDEO = true;\nconst DEFAULT_HIDE_NAVIGATION = false;\n\nconst Backdrop = () => null;\n\nexport const Gallery: FC<GalleryProps> = ({\n open,\n images,\n slideIndex,\n initialSlide = 0,\n loop = true,\n onClose,\n onSlideIndexChange,\n popupClassName,\n}) => {\n const currentSlideIndexState = useState(initialSlide);\n const uncontrolled = slideIndex === undefined;\n const [currentSlideIndex, setCurrentSlideIndex] = uncontrolled\n ? currentSlideIndexState\n : [slideIndex, onSlideIndexChange];\n\n const [swiper, setSwiper] = useState<SwiperCore>();\n const [imagesMeta, setImagesMeta] = useState<ImageMeta[]>([]);\n const [fullScreen, setFullScreen] = useState<boolean>(DEFAULT_FULL_SCREEN);\n const [mutedVideo, setMutedVideo] = useState<boolean>(DEFAULT_MUTED_VIDEO);\n const [playingVideo, setPlayingVideo] = useState<boolean>(DEFAULT_PLAYING_VIDEO);\n const [hideNavigation, setHideNavigation] = useState<boolean>(DEFAULT_HIDE_NAVIGATION);\n const [swipeY, setSwipeY] = useState<number>(0);\n\n const isDesktop = useIsDesktop();\n\n const isCurrentVideo = !!imagesMeta[currentSlideIndex]?.player?.current;\n\n const bottomButton = images[currentSlideIndex]?.bottomButton;\n\n const slideTo = useCallback(\n (index: number) => {\n if (images[index]) {\n setCurrentSlideIndex?.(index);\n\n if (swiper) {\n setPlayingVideo(true);\n swiper.slideTo(index);\n }\n }\n },\n [images, setCurrentSlideIndex, swiper],\n );\n\n const slideNext = useCallback(() => {\n const lastIndex = images.length - 1;\n\n let nextIndex = currentSlideIndex + 1;\n\n if (nextIndex >= images.length) {\n nextIndex = loop ? 0 : lastIndex;\n }\n\n slideTo(nextIndex);\n }, [images.length, loop, currentSlideIndex, slideTo]);\n\n const slidePrev = useCallback(() => {\n const lastIndex = images.length - 1;\n\n let nextIndex = currentSlideIndex - 1;\n\n if (nextIndex < 0) {\n nextIndex = loop ? lastIndex : 0;\n }\n\n slideTo(nextIndex);\n }, [images.length, loop, currentSlideIndex, slideTo]);\n\n const setImageMeta = useCallback((meta: ImageMeta, index: number) => {\n setImagesMeta((prevImagesMeta) => {\n const nextImagesMeta = prevImagesMeta.slice();\n\n nextImagesMeta[index] = meta;\n\n return nextImagesMeta;\n });\n }, []);\n\n const handleBottomButtonClick = useCallback(\n (e: MouseEvent) => {\n e.stopPropagation();\n if (bottomButton?.onClick) {\n bottomButton.onClick(e);\n }\n },\n [bottomButton],\n );\n\n const handleClose = useCallback(() => {\n onClose();\n\n if (uncontrolled) {\n setCurrentSlideIndex?.(initialSlide);\n }\n\n setFullScreen(false);\n }, [initialSlide, onClose, setCurrentSlideIndex, uncontrolled]);\n\n const handleEscapeKeyDown = () => {\n if (fullScreen) {\n setFullScreen(false);\n } else {\n handleClose();\n }\n };\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent) => {\n if (!open || fullScreen) {\n return;\n }\n\n switch (event.key) {\n case 'ArrowLeft':\n slidePrev();\n break;\n case 'ArrowRight':\n slideNext();\n break;\n }\n },\n [fullScreen, open, slideNext, slidePrev],\n );\n\n const onUnmount = useCallback(() => {\n setPlayingVideo(DEFAULT_PLAYING_VIDEO);\n setMutedVideo(DEFAULT_MUTED_VIDEO);\n }, [setPlayingVideo]);\n\n useEffect(() => {\n if (!uncontrolled && !swiper?.destroyed) {\n swiper?.slideTo(currentSlideIndex);\n }\n }, [uncontrolled, currentSlideIndex, swiper]);\n\n useEffect(() => {\n if (!open) {\n setSwipeY(0);\n }\n }, [open]);\n\n useEffect(() => {\n document.addEventListener('keydown', handleKeyDown);\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n };\n }, [handleKeyDown]);\n\n useEffect(() => {\n if (!open) {\n return undefined;\n }\n\n let startX = 0;\n let startY = 0;\n let lockedDirection: 'horizontal' | 'vertical' | null = null;\n const directionLockThreshold = 10;\n\n const abortController = new AbortController();\n const { signal } = abortController;\n\n document.addEventListener(\n 'touchstart',\n (e) => {\n startX = e.touches[0].clientX;\n startY = e.touches[0].clientY;\n lockedDirection = null;\n },\n { signal },\n );\n\n document.addEventListener(\n 'touchmove',\n (e) => {\n const currentX = e.touches[0].clientX;\n const currentY = e.touches[0].clientY;\n const deltaX = currentX - startX;\n const deltaY = currentY - startY;\n\n if (!lockedDirection) {\n const absX = Math.abs(deltaX);\n const absY = Math.abs(deltaY);\n\n if (absX > absY && absX > directionLockThreshold) {\n lockedDirection = 'horizontal';\n } else if (absY > absX && absY > directionLockThreshold) {\n lockedDirection = 'vertical';\n } else {\n return;\n }\n }\n\n if (lockedDirection === 'horizontal') {\n return;\n }\n\n if (deltaY <= 0) {\n setSwipeY(0);\n\n return;\n }\n\n setSwipeY(deltaY);\n\n if (deltaY > SWIPE_THRESHOLD) {\n setSwipeY(0);\n onClose();\n }\n },\n { signal },\n );\n\n document.addEventListener(\n 'touchend',\n () => {\n lockedDirection = null;\n setSwipeY(0);\n },\n { signal },\n );\n\n return () => {\n abortController.abort();\n };\n }, [onClose, open]);\n\n const singleSlide = images.length === 1;\n\n const showNavigationBar = !singleSlide && !fullScreen;\n\n // eslint-disable-next-line react/jsx-no-constructed-context-values\n const galleryContext: GalleryContext = {\n view: isDesktop ? 'desktop' : 'mobile',\n singleSlide,\n currentSlideIndex,\n images,\n imagesMeta,\n fullScreen,\n initialSlide: uncontrolled ? initialSlide : currentSlideIndex,\n setFullScreen,\n playingVideo,\n setPlayingVideo,\n mutedVideo,\n setMutedVideo,\n hideNavigation,\n setHideNavigation,\n setImageMeta,\n slideNext,\n slidePrev,\n slideTo,\n getSwiper: () => swiper,\n setSwiper,\n onClose: handleClose,\n setCurrentSlideIndex,\n getCurrentImage: () => images[currentSlideIndex],\n getCurrentImageMeta: () => imagesMeta[currentSlideIndex],\n };\n\n return (\n <GalleryContext.Provider value={galleryContext}>\n <BaseModal\n open={open}\n className={cn(styles.modal, popupClassName)}\n onEscapeKeyDown={handleEscapeKeyDown}\n Backdrop={Backdrop}\n onUnmount={onUnmount}\n >\n <div\n data-content-area={true}\n style={{\n transform: `translateY(${swipeY}px)`,\n }}\n className={cn(styles.container, {\n [styles.mobile]: !isDesktop,\n })}\n >\n {isDesktop ? <Header /> : <HeaderMobile />}\n {images.length === 1 ? <Single /> : <ImageViewer />}\n <nav\n className={cn({\n [styles.navigationVideo]: isCurrentVideo && !isDesktop,\n [styles.hide]: showNavigationBar && hideNavigation && !isDesktop,\n [styles.hideInfo]: !showNavigationBar && hideNavigation && !isDesktop,\n })}\n >\n {!isDesktop && <Subtitles />}\n {isCurrentVideo && !isDesktop && bottomButton && (\n <BottomButton\n bottomButton={bottomButton}\n onClick={handleBottomButtonClick}\n className={styles.bottomButton}\n />\n )}\n\n {showNavigationBar && <NavigationBar />}\n {!isDesktop && <InfoBar />}\n </nav>\n </div>\n </BaseModal>\n </GalleryContext.Provider>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -11,7 +11,9 @@ export declare const Pause: FC<Props>;
11
11
  export declare const ExitFullscreen: FC<Props>;
12
12
  export declare const MuteVideo: FC<Props>;
13
13
  export declare const UnmuteVideo: FC<Props>;
14
- export declare const Download: FC<Props>;
14
+ export declare const Download: FC<Props & {
15
+ href?: string;
16
+ }>;
15
17
  export declare const Share: FC<Props>;
16
18
  export declare const Exit: FC<Props>;
17
19
  export {};
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import cn from 'classnames';
3
3
  import { IconButton } from '@alfalab/core-components-icon-button/moderncssm';
4
+ import { useIsDesktop } from '@alfalab/core-components-mq/moderncssm';
4
5
  import { TooltipDesktop } from '@alfalab/core-components-tooltip/moderncssm/desktop';
5
6
  import { ArrowLeftMIcon } from '@alfalab/icons-glyph/ArrowLeftMIcon';
6
7
  import { ArrowsInwardMIcon } from '@alfalab/icons-glyph/ArrowsInwardMIcon';
@@ -12,23 +13,61 @@ import { PointerDownMIcon } from '@alfalab/icons-glyph/PointerDownMIcon';
12
13
  import { ShareMIcon } from '@alfalab/icons-glyph/ShareMIcon';
13
14
  import { SoundCrossMIcon } from '@alfalab/icons-glyph/SoundCrossMIcon';
14
15
  import { SoundMIcon } from '@alfalab/icons-glyph/SoundMIcon';
16
+ import { isVideo } from '../../utils/utils.js';
17
+ import { downloadFile } from '../../utils/modern-download.js';
15
18
  import styles from './index.module.css';
16
19
 
17
- const Fullscreen = ({ buttonRef, ...restProps }) => (React.createElement(TooltipDesktop, { trigger: 'hover', position: 'bottom', content: '\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0432 \u043F\u043E\u043B\u043D\u043E\u044D\u043A\u0440\u0430\u043D\u043D\u043E\u043C \u0440\u0435\u0436\u0438\u043C\u0435', fallbackPlacements: ['bottom-end'] },
18
- React.createElement(IconButton, { ...restProps, ref: buttonRef, icon: ArrowsOutwardMIcon, "aria-label": '\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0432 \u043F\u043E\u043B\u043D\u043E\u044D\u043A\u0440\u0430\u043D\u043D\u043E\u043C \u0440\u0435\u0436\u0438\u043C\u0435', className: styles.iconButton })));
20
+ const Fullscreen = ({ buttonRef, ...restProps }) => {
21
+ const isDesktop = useIsDesktop();
22
+ const iconButton = (React.createElement(IconButton, { ...restProps, ref: buttonRef, icon: ArrowsOutwardMIcon, "aria-label": '\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0432 \u043F\u043E\u043B\u043D\u043E\u044D\u043A\u0440\u0430\u043D\u043D\u043E\u043C \u0440\u0435\u0436\u0438\u043C\u0435', className: styles.iconButton }));
23
+ return isDesktop ? (React.createElement(TooltipDesktop, { view: 'hint', colors: 'inverted', trigger: 'hover', position: 'bottom', content: '\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0432 \u043F\u043E\u043B\u043D\u043E\u044D\u043A\u0440\u0430\u043D\u043D\u043E\u043C \u0440\u0435\u0436\u0438\u043C\u0435', fallbackPlacements: ['bottom-end'] }, iconButton)) : (iconButton);
24
+ };
19
25
  const BackArrow = ({ buttonRef, ...restProps }) => (React.createElement(IconButton, { ...restProps, ref: buttonRef, icon: ArrowLeftMIcon, "aria-label": '\u0412\u0435\u0440\u043D\u0443\u0442\u044C\u0441\u044F \u043D\u0430\u0437\u0430\u0434', className: styles.iconButton }));
20
26
  const Play = ({ buttonRef, className, ...restProps }) => (React.createElement(IconButton, { ...restProps, ref: buttonRef, icon: PlayCompactMIcon, "aria-label": '\u041F\u0440\u043E\u0438\u0433\u0440\u0430\u0442\u044C \u0432\u0438\u0434\u0435\u043E', className: cn(styles.iconButton, className) }));
21
27
  const Pause = ({ buttonRef, className, ...restProps }) => (React.createElement(IconButton, { ...restProps, ref: buttonRef, icon: PauseCompactMIcon, "aria-label": '\u041F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u043F\u0430\u0443\u0437\u0443 \u043D\u0430 \u0432\u0438\u0434\u0435\u043E', className: cn(styles.iconButton, className) }));
22
- const ExitFullscreen = ({ buttonRef, ...restProps }) => (React.createElement(TooltipDesktop, { trigger: 'hover', position: 'bottom', content: '\u0412\u044B\u0439\u0442\u0438 \u0438\u0437 \u043F\u043E\u043B\u043D\u043E\u044D\u043A\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0440\u0435\u0436\u0438\u043C\u0430', fallbackPlacements: ['bottom-end'] },
23
- React.createElement(IconButton, { ...restProps, ref: buttonRef, icon: ArrowsInwardMIcon, "aria-label": '\u0412\u044B\u0439\u0442\u0438 \u0438\u0437 \u043F\u043E\u043B\u043D\u043E\u044D\u043A\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0440\u0435\u0436\u0438\u043C\u0430', className: styles.iconButton })));
24
- const MuteVideo = ({ buttonRef, className, ...restProps }) => (React.createElement(TooltipDesktop, { trigger: 'hover', position: 'bottom', content: '\u0412\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0437\u0432\u0443\u043A', fallbackPlacements: ['bottom-end'], targetClassName: className },
25
- React.createElement(IconButton, { ...restProps, ref: buttonRef, icon: SoundMIcon, "aria-label": '\u0412\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0437\u0432\u0443\u043A', className: styles.iconButton })));
26
- const UnmuteVideo = ({ buttonRef, className, ...restProps }) => (React.createElement(TooltipDesktop, { trigger: 'hover', position: 'bottom', content: '\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0437\u0432\u0443\u043A', fallbackPlacements: ['bottom-end'], targetClassName: className },
27
- React.createElement(IconButton, { ...restProps, ref: buttonRef, icon: SoundCrossMIcon, "aria-label": '\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0437\u0432\u0443\u043A', className: styles.iconButton })));
28
- const Download = (props) => (React.createElement(TooltipDesktop, { trigger: 'hover', position: 'bottom', content: '\u0421\u043A\u0430\u0447\u0430\u0442\u044C', fallbackPlacements: ['bottom-end'] },
29
- React.createElement(IconButton, { ...props, icon: PointerDownMIcon, "aria-label": '\u0421\u043A\u0430\u0447\u0430\u0442\u044C', className: styles.iconButton })));
30
- const Share = (props) => (React.createElement(TooltipDesktop, { trigger: 'hover', position: 'bottom', content: '\u041F\u043E\u0434\u0435\u043B\u0438\u0442\u044C\u0441\u044F', fallbackPlacements: ['bottom-end'] },
31
- React.createElement(IconButton, { ...props, icon: ShareMIcon, "aria-label": '\u041F\u043E\u0434\u0435\u043B\u0438\u0442\u044C\u0441\u044F', className: styles.iconButton })));
28
+ const ExitFullscreen = ({ buttonRef, ...restProps }) => {
29
+ const isDesktop = useIsDesktop();
30
+ const iconButton = (React.createElement(IconButton, { ...restProps, ref: buttonRef, icon: ArrowsInwardMIcon, "aria-label": '\u0412\u044B\u0439\u0442\u0438 \u0438\u0437 \u043F\u043E\u043B\u043D\u043E\u044D\u043A\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0440\u0435\u0436\u0438\u043C\u0430', className: styles.iconButton }));
31
+ return isDesktop ? (React.createElement(TooltipDesktop, { view: 'hint', colors: 'inverted', trigger: 'hover', position: 'bottom', content: '\u0412\u044B\u0439\u0442\u0438 \u0438\u0437 \u043F\u043E\u043B\u043D\u043E\u044D\u043A\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0440\u0435\u0436\u0438\u043C\u0430', fallbackPlacements: ['bottom-end'] }, iconButton)) : (iconButton);
32
+ };
33
+ const MuteVideo = ({ buttonRef, className, ...restProps }) => {
34
+ const isDesktop = useIsDesktop();
35
+ const iconButton = (React.createElement(IconButton, { ...restProps, ref: buttonRef, icon: SoundMIcon, "aria-label": '\u0412\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0437\u0432\u0443\u043A', className: styles.iconButton }));
36
+ return isDesktop ? (React.createElement(TooltipDesktop, { view: 'hint', colors: 'inverted', trigger: 'hover', position: 'bottom', content: '\u0412\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0437\u0432\u0443\u043A', fallbackPlacements: ['bottom-end'], targetClassName: className }, iconButton)) : (iconButton);
37
+ };
38
+ const UnmuteVideo = ({ buttonRef, className, ...restProps }) => {
39
+ const isDesktop = useIsDesktop();
40
+ const iconButton = (React.createElement(IconButton, { ...restProps, ref: buttonRef, icon: SoundCrossMIcon, "aria-label": '\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0437\u0432\u0443\u043A', className: styles.iconButton }));
41
+ return isDesktop ? (React.createElement(TooltipDesktop, { view: 'hint', colors: 'inverted', trigger: 'hover', position: 'bottom', content: '\u0412\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0437\u0432\u0443\u043A', fallbackPlacements: ['bottom-end'], targetClassName: className }, iconButton)) : (iconButton);
42
+ };
43
+ const Download = ({ href, ...props }) => {
44
+ const isDesktop = useIsDesktop();
45
+ const handleMobileVideoDownload = async (e) => {
46
+ e.preventDefault();
47
+ if (!href)
48
+ return;
49
+ const fileName = props.download?.toString() || 'video';
50
+ const fileType = isVideo(href) ? 'video/*' : undefined;
51
+ await downloadFile({
52
+ url: href,
53
+ fileName,
54
+ fileType,
55
+ });
56
+ };
57
+ const iconButtonProps = {
58
+ ...props,
59
+ icon: PointerDownMIcon,
60
+ 'aria-label': 'Скачать',
61
+ className: styles.iconButton,
62
+ };
63
+ const iconButton = !isDesktop && isVideo(href) ? (React.createElement(IconButton, { ...iconButtonProps, onClick: handleMobileVideoDownload })) : (React.createElement(IconButton, { ...iconButtonProps, href: href }));
64
+ return isDesktop ? (React.createElement(TooltipDesktop, { view: 'hint', colors: 'inverted', trigger: 'hover', position: 'bottom', content: '\u0421\u043A\u0430\u0447\u0430\u0442\u044C', fallbackPlacements: ['bottom-end'] }, iconButton)) : (iconButton);
65
+ };
66
+ const Share = (props) => {
67
+ const isDesktop = useIsDesktop();
68
+ const iconButton = (React.createElement(IconButton, { ...props, icon: ShareMIcon, "aria-label": '\u041F\u043E\u0434\u0435\u043B\u0438\u0442\u044C\u0441\u044F', className: styles.iconButton }));
69
+ return isDesktop ? (React.createElement(TooltipDesktop, { view: 'hint', colors: 'inverted', trigger: 'hover', position: 'bottom', content: '\u041F\u043E\u0434\u0435\u043B\u0438\u0442\u044C\u0441\u044F', fallbackPlacements: ['bottom-end'] }, iconButton)) : (iconButton);
70
+ };
32
71
  const Exit = (props) => (React.createElement(IconButton, { ...props, icon: CrossMIcon, "aria-label": '\u0417\u0430\u043A\u0440\u044B\u0442\u044C', className: styles.iconButton }));
33
72
 
34
73
  export { BackArrow, Download, Exit, ExitFullscreen, Fullscreen, MuteVideo, Pause, Play, Share, UnmuteVideo };