Compare commits
219 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
8896df9f1f | 2 years ago |
|
|
10bca448dc | 2 years ago |
|
|
7a2423568a | 2 years ago |
|
|
ccb265cb2e | 2 years ago |
|
|
3152372c39 | 2 years ago |
|
|
4e43b9aea8 | 2 years ago |
|
|
0c5a1aac4f | 2 years ago |
|
|
fc3a3c052c | 2 years ago |
|
|
888ac35156 | 2 years ago |
|
|
2b9b93ef31 | 2 years ago |
|
|
1335d34b07 | 2 years ago |
|
|
42623654bb | 2 years ago |
|
|
c0d06f8ef9 | 2 years ago |
|
|
2dba409aa7 | 2 years ago |
|
|
8dce04b35d | 2 years ago |
|
|
642e3809dc | 2 years ago |
|
|
c41acef4ce | 2 years ago |
|
|
af912dca10 | 2 years ago |
|
|
d06425df94 | 2 years ago |
|
|
0e0f24454e | 2 years ago |
|
|
2a95f32aeb | 2 years ago |
|
|
bef03ff120 | 2 years ago |
|
|
bd218b8888 | 2 years ago |
|
|
2d321ff6a2 | 2 years ago |
|
|
d4c4bd6e81 | 2 years ago |
|
|
0b2c38871a | 2 years ago |
|
|
9f0c779246 | 2 years ago |
|
|
02c72a87f1 | 2 years ago |
|
|
f70fe23926 | 2 years ago |
|
|
5e0b262839 | 2 years ago |
|
|
590ce1bb90 | 2 years ago |
|
|
806a2519b6 | 2 years ago |
|
|
efce351342 | 2 years ago |
|
|
f4b96c4668 | 2 years ago |
|
|
77f71b852b | 2 years ago |
|
|
93e2716817 | 2 years ago |
|
|
1edd818afd | 2 years ago |
|
|
01786d947a | 2 years ago |
|
|
f9823d191f | 2 years ago |
|
|
d9d4de4072 | 2 years ago |
|
|
63f7c01e09 | 2 years ago |
|
|
0170d0fb06 | 2 years ago |
|
|
e5552e4d8b | 2 years ago |
|
|
c67bc5a22c | 2 years ago |
|
|
247a08902d | 2 years ago |
|
|
8c88cb7517 | 2 years ago |
|
|
551460fc14 | 2 years ago |
|
|
cc595acfd4 | 2 years ago |
|
|
541a042597 | 2 years ago |
|
|
1b7a2eb111 | 2 years ago |
|
|
e1d82fc86e | 2 years ago |
|
|
17e8794399 | 3 years ago |
|
|
c90d4fc769 | 3 years ago |
|
|
90f7568a2a | 3 years ago |
|
|
b163307892 | 3 years ago |
|
|
1e7ffc7366 | 3 years ago |
|
|
cf8515fe9e | 3 years ago |
|
|
3a786f97c9 | 3 years ago |
|
|
403690369d | 3 years ago |
|
|
98bb7e24c1 | 3 years ago |
|
|
a055f9df80 | 3 years ago |
|
|
4c4b594b3c | 3 years ago |
|
|
d9bd7e68fa | 3 years ago |
|
|
767d7794c3 | 3 years ago |
|
|
4a53d56881 | 3 years ago |
|
|
472949baad | 3 years ago |
|
|
795a8237f7 | 3 years ago |
|
|
27b91ac3a0 | 3 years ago |
|
|
1e0a2cfc2d | 3 years ago |
|
|
86934cf6b8 | 3 years ago |
|
|
e188c32fcd | 3 years ago |
|
|
e1c8ff491e | 3 years ago |
|
|
4129e2730a | 3 years ago |
|
|
46b80b8e6c | 3 years ago |
|
|
66a53765dc | 3 years ago |
|
|
fcaa0015e5 | 3 years ago |
|
|
fe18150ee7 | 3 years ago |
|
|
aa41e833c7 | 3 years ago |
|
|
83880792cc | 3 years ago |
|
|
ca35f3d238 | 3 years ago |
|
|
877747322b | 3 years ago |
|
|
727aaece68 | 3 years ago |
|
|
2b3427800c | 3 years ago |
|
|
00ce4f76ab | 3 years ago |
|
|
9990d8eec6 | 3 years ago |
|
|
96ab8c588f | 3 years ago |
|
|
df1ac5508d | 3 years ago |
|
|
d6112d0db1 | 3 years ago |
|
|
ec60b7c1bc | 3 years ago |
|
|
4153cd8444 | 3 years ago |
|
|
e3332c4e3f | 3 years ago |
|
|
0697dbf072 | 3 years ago |
|
|
061b09d1c4 | 3 years ago |
|
|
e546a77f99 | 3 years ago |
|
|
e202aa1d30 | 3 years ago |
|
|
3d1d9e05cd | 3 years ago |
|
|
a56c44521b | 3 years ago |
|
|
ea00f09c7e | 3 years ago |
|
|
d783ef64e0 | 3 years ago |
|
|
ac6383bff9 | 3 years ago |
|
|
0750331e6a | 3 years ago |
|
|
27b2449711 | 3 years ago |
|
|
b0b7ab6073 | 3 years ago |
|
|
f31a83629d | 3 years ago |
|
|
5815744de4 | 3 years ago |
|
|
6a286d46dd | 3 years ago |
|
|
603f098b12 | 3 years ago |
|
|
e83c4032a9 | 3 years ago |
|
|
786a11281b | 3 years ago |
|
|
9b8242a712 | 3 years ago |
|
|
942a895004 | 3 years ago |
|
|
c9868effeb | 3 years ago |
|
|
bd71c5ef0c | 3 years ago |
|
|
62173b545b | 3 years ago |
|
|
282c652c63 | 3 years ago |
|
|
3c7e8c3a75 | 3 years ago |
|
|
370c165905 | 3 years ago |
|
|
1081406926 | 3 years ago |
|
|
7a5301f891 | 3 years ago |
|
|
4e681d01b2 | 3 years ago |
|
|
5edf656d1f | 3 years ago |
|
|
437f7d4d70 | 3 years ago |
|
|
a93f2e4c6c | 3 years ago |
|
|
27f7c3f092 | 3 years ago |
|
|
89e07e70e6 | 3 years ago |
|
|
b0c9b2a6c9 | 3 years ago |
|
|
7681462c83 | 3 years ago |
|
|
d5cf526725 | 3 years ago |
|
|
0f067862f9 | 3 years ago |
|
|
c0a594d7c8 | 3 years ago |
|
|
a69fbe6c4b | 3 years ago |
|
|
6a8d3f9200 | 3 years ago |
|
|
9205c61454 | 3 years ago |
|
|
b7026e08bb | 3 years ago |
|
|
e357fa8d6c | 3 years ago |
|
|
d5e2964e19 | 3 years ago |
|
|
44a550a67d | 3 years ago |
|
|
a910921bde | 3 years ago |
|
|
3795025519 | 3 years ago |
|
|
5f93cc4717 | 3 years ago |
|
|
c4403ea089 | 3 years ago |
|
|
e48b890fd7 | 3 years ago |
|
|
5eeaacde4c | 3 years ago |
|
|
b59c5ce832 | 3 years ago |
|
|
9376098b61 | 3 years ago |
|
|
27002909a5 | 3 years ago |
|
|
8a46f0a363 | 3 years ago |
|
|
b6c77febac | 3 years ago |
|
|
e43cbc2c02 | 3 years ago |
|
|
f335295dae | 3 years ago |
|
|
355f9fab5d | 3 years ago |
|
|
d97c10e56b | 3 years ago |
|
|
5b999d0657 | 3 years ago |
|
|
c4b304b031 | 3 years ago |
|
|
7feed32c37 | 3 years ago |
|
|
e50e3c1d8c | 3 years ago |
|
|
b80ced0877 | 3 years ago |
|
|
5c43a69c13 | 3 years ago |
|
|
5a38dfdc57 | 3 years ago |
|
|
8859004e6c | 3 years ago |
|
|
452f713c49 | 3 years ago |
|
|
7f1634b3e9 | 3 years ago |
|
|
090d28c1fd | 3 years ago |
|
|
e967ee24ed | 3 years ago |
|
|
0e8e83634c | 3 years ago |
|
|
912ccd452d | 3 years ago |
|
|
9646f24c03 | 3 years ago |
|
|
7e3c8b5473 | 3 years ago |
|
|
32a1720e9f | 3 years ago |
|
|
2103b69fea | 3 years ago |
|
|
7d1d37c6b5 | 3 years ago |
|
|
7b3b0fe2c0 | 3 years ago |
|
|
8d069a4242 | 3 years ago |
|
|
ba23f5258d | 3 years ago |
|
|
eafdc35971 | 3 years ago |
|
|
d6d89c3e90 | 3 years ago |
|
|
7b3e3b521d | 3 years ago |
|
|
633e729cce | 3 years ago |
|
|
d5bc16f083 | 3 years ago |
|
|
953fc449cf | 3 years ago |
|
|
459c39a0ea | 3 years ago |
|
|
59766a9601 | 3 years ago |
|
|
6b19ad2ec3 | 3 years ago |
|
|
0e68373893 | 3 years ago |
|
|
02ca2e1400 | 3 years ago |
|
|
f33146c376 | 3 years ago |
|
|
6002643f36 | 3 years ago |
|
|
af5be4bfab | 3 years ago |
|
|
ae93db8c8a | 3 years ago |
|
|
ffbca0cf2f | 3 years ago |
|
|
f2f883de62 | 3 years ago |
|
|
cd9c35b301 | 3 years ago |
|
|
749491521a | 3 years ago |
|
|
eb4a368ad1 | 3 years ago |
|
|
62281019f1 | 3 years ago |
|
|
9bf9f65f51 | 3 years ago |
|
|
40369dcded | 3 years ago |
|
|
d0bcb49a29 | 3 years ago |
|
|
a5757e44fe | 3 years ago |
|
|
6dad0f3758 | 3 years ago |
|
|
cab081346e | 3 years ago |
|
|
7155a80a29 | 3 years ago |
|
|
93328cd9eb | 3 years ago |
|
|
07c928ab97 | 3 years ago |
|
|
a47ccbbe3e | 3 years ago |
|
|
6d88197ac3 | 3 years ago |
|
|
d5adc2cd7e | 3 years ago |
|
|
f02325c470 | 3 years ago |
|
|
f2c12bed8c | 3 years ago |
|
|
e8dac0541d | 3 years ago |
|
|
8956dbc169 | 3 years ago |
|
|
bf52b61309 | 3 years ago |
|
|
a9925f7bb8 | 3 years ago |
|
|
790c049ea5 | 3 years ago |
|
|
a7bd8c392e | 3 years ago |
|
|
e66d8147fa | 3 years ago |
|
|
42a9f916d9 | 3 years ago |
|
|
7cdfd41fe2 | 3 years ago |
|
|
6ed5fea40d | 3 years ago |
@ -0,0 +1 @@ |
|||||||
|
google.com, pub-6802442215403184, DIRECT, f08c47fec0942fa0 |
||||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 793 B |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 802 B |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
@ -0,0 +1,12 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<browserconfig> |
||||||
|
<msapplication> |
||||||
|
<tile> |
||||||
|
<square70x70logo src="/mstile-70x70.png"/> |
||||||
|
<square144x144logo src="/mstile-144x144.png"/> |
||||||
|
<square150x150logo src="/mstile-150x150.png"/> |
||||||
|
<square310x310logo src="/mstile-310x310.png"/> |
||||||
|
<TileColor>#da532c</TileColor> |
||||||
|
</tile> |
||||||
|
</msapplication> |
||||||
|
</browserconfig> |
||||||
|
After Width: | Height: | Size: 477 B |
|
After Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,19 @@ |
|||||||
|
{ |
||||||
|
"name": "", |
||||||
|
"short_name": "", |
||||||
|
"icons": [ |
||||||
|
{ |
||||||
|
"src": "/android-chrome-192x192.png", |
||||||
|
"sizes": "192x192", |
||||||
|
"type": "image/png" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"src": "/android-chrome-512x512.png", |
||||||
|
"sizes": "512x512", |
||||||
|
"type": "image/png" |
||||||
|
} |
||||||
|
], |
||||||
|
"theme_color": "#ffffff", |
||||||
|
"background_color": "#ffffff", |
||||||
|
"display": "standalone" |
||||||
|
} |
||||||
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 598 B After Width: | Height: | Size: 884 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
@ -0,0 +1,12 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<browserconfig> |
||||||
|
<msapplication> |
||||||
|
<tile> |
||||||
|
<square70x70logo src="/mstile-70x70.png"/> |
||||||
|
<square144x144logo src="/mstile-144x144.png"/> |
||||||
|
<square150x150logo src="/mstile-150x150.png"/> |
||||||
|
<square310x310logo src="/mstile-310x310.png"/> |
||||||
|
<TileColor>#da532c</TileColor> |
||||||
|
</tile> |
||||||
|
</msapplication> |
||||||
|
</browserconfig> |
||||||
|
After Width: | Height: | Size: 477 B |
|
After Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,19 @@ |
|||||||
|
{ |
||||||
|
"name": "", |
||||||
|
"short_name": "", |
||||||
|
"icons": [ |
||||||
|
{ |
||||||
|
"src": "/android-chrome-192x192.png", |
||||||
|
"sizes": "192x192", |
||||||
|
"type": "image/png" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"src": "/android-chrome-512x512.png", |
||||||
|
"sizes": "512x512", |
||||||
|
"type": "image/png" |
||||||
|
} |
||||||
|
], |
||||||
|
"theme_color": "#ffffff", |
||||||
|
"background_color": "#ffffff", |
||||||
|
"display": "standalone" |
||||||
|
} |
||||||
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 568 B |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 624 B |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 229 B |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 908 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 451 B |
|
After Width: | Height: | Size: 755 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 329 B |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 2.0 KiB |
@ -1,15 +1,28 @@ |
|||||||
<!DOCTYPE html> |
<!DOCTYPE html> |
||||||
<html lang="en"> |
<html lang="en"> |
||||||
|
|
||||||
<head> |
<head> |
||||||
<title></title> |
<title></title> |
||||||
</head> |
</head> |
||||||
|
|
||||||
<body> |
<body> |
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/oidc-client/1.11.5/oidc-client.min.js" integrity="sha512-pGtU1n/6GJ8fu6bjYVGIOT9Dphaw5IWPwVlqkpvVgqBxFkvdNbytUh0H8AP15NYF777P4D3XEeA/uDWFCpSQ1g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> |
<script src="https://cdnjs.cloudflare.com/ajax/libs/oidc-client/1.11.5/oidc-client.min.js" |
||||||
|
integrity="sha512-pGtU1n/6GJ8fu6bjYVGIOT9Dphaw5IWPwVlqkpvVgqBxFkvdNbytUh0H8AP15NYF777P4D3XEeA/uDWFCpSQ1g==" |
||||||
|
crossorigin="anonymous" referrerpolicy="no-referrer"></script> |
||||||
<script> |
<script> |
||||||
new Oidc.UserManager().signinSilentCallback() |
new Oidc.UserManager().signinSilentCallback() |
||||||
.catch((err) => { |
// обновляем рефреш токен в локалсторадже |
||||||
console.error('OIDC: silent refresh callback error', err); |
// так как safari не дает доступ к кукам |
||||||
}); |
.then(() => { |
||||||
|
const refreshToken = localStorage.getItem('refresh_token'); |
||||||
|
if (refreshToken) { |
||||||
|
localStorage.setItem('refresh_token', new URLSearchParams(document.location.search).get('refresh_token')); |
||||||
|
} |
||||||
|
}) |
||||||
|
.catch((err) => { |
||||||
|
console.error('OIDC: silent refresh callback error', err); |
||||||
|
}); |
||||||
</script> |
</script> |
||||||
</body> |
</body> |
||||||
</html> |
|
||||||
|
</html> |
||||||
@ -0,0 +1,163 @@ |
|||||||
|
import { useEffect, useState } from 'react' |
||||||
|
|
||||||
|
import ReactGA from 'react-ga' |
||||||
|
|
||||||
|
import { updateAdsView } from 'requests' |
||||||
|
|
||||||
|
import { useToggle } from 'hooks' |
||||||
|
|
||||||
|
import { getLocalStorageItem, isMatchPage } from 'helpers' |
||||||
|
|
||||||
|
import { |
||||||
|
device, |
||||||
|
COUNTRY, |
||||||
|
} from 'config' |
||||||
|
|
||||||
|
import { useMatchPageStore } from 'features/MatchPage/store' |
||||||
|
|
||||||
|
import type { AdComponentType } from './index' |
||||||
|
|
||||||
|
import { checkVideo } from '../../helpers' |
||||||
|
import { |
||||||
|
adsViews, |
||||||
|
EventGA, |
||||||
|
ViewsType, |
||||||
|
} from '../../types' |
||||||
|
|
||||||
|
const countryCode = getLocalStorageItem(COUNTRY) |
||||||
|
|
||||||
|
export const useAd = ({ ad }: AdComponentType) => { |
||||||
|
const isMatch = isMatchPage() |
||||||
|
|
||||||
|
const [isOpenAd, setIsOpenAd] = useState(isMatch) |
||||||
|
const [isNeedToShow, setIsNeedToShow] = useState(isMatch) |
||||||
|
const [shownTime, setShownTime] = useState(0) |
||||||
|
|
||||||
|
const { isFullscreen } = useMatchPageStore() |
||||||
|
|
||||||
|
const views = getLocalStorageItem(adsViews) as ViewsType |
||||||
|
const { |
||||||
|
duration, |
||||||
|
frequency, |
||||||
|
id, |
||||||
|
media, |
||||||
|
name, |
||||||
|
time_close, |
||||||
|
} = ad |
||||||
|
|
||||||
|
const { |
||||||
|
close, |
||||||
|
isOpen: isOpenCloseBtn, |
||||||
|
open: showCloseBtn, |
||||||
|
} = useToggle() |
||||||
|
|
||||||
|
const isNeedBanner = Number(views?.HOME) % frequency === 0 |
||||||
|
const isVideo = checkVideo(media.url) |
||||||
|
const currentAdsTime = duration - shownTime |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (!isFullscreen) { |
||||||
|
if (currentAdsTime === 0) { |
||||||
|
setShownTime(0) |
||||||
|
} else { |
||||||
|
const stopWatch = setInterval(() => { |
||||||
|
setShownTime((prev) => prev + 1) |
||||||
|
}, 1000) |
||||||
|
return () => clearInterval(stopWatch) |
||||||
|
} |
||||||
|
} |
||||||
|
return undefined |
||||||
|
}, [ |
||||||
|
isFullscreen, |
||||||
|
currentAdsTime, |
||||||
|
]) |
||||||
|
|
||||||
|
const handleClose = () => { |
||||||
|
setIsOpenAd(false) |
||||||
|
isMatch && setIsNeedToShow(false) |
||||||
|
sendBannerClickEvent(EventGA.CLOSE) |
||||||
|
} |
||||||
|
|
||||||
|
const sendBannerClickEvent = (event: EventGA) => { |
||||||
|
ReactGA.initialize('Advertisement') |
||||||
|
ReactGA.event({ |
||||||
|
action: event, |
||||||
|
category: 'Advertisement', |
||||||
|
label: `${name}_${countryCode ?? ''}_${device}`, |
||||||
|
value: id, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
setShownTime(0) |
||||||
|
|
||||||
|
if (isMatch) { |
||||||
|
const interval = setInterval(() => { |
||||||
|
setIsNeedToShow(true) |
||||||
|
setIsOpenAd(true) |
||||||
|
}, frequency * 1000) |
||||||
|
|
||||||
|
return () => clearInterval(interval) |
||||||
|
} |
||||||
|
|
||||||
|
setIsNeedToShow(isNeedBanner) |
||||||
|
return setIsOpenAd(isNeedBanner) |
||||||
|
}, [ |
||||||
|
frequency, |
||||||
|
isNeedToShow, |
||||||
|
views?.HOME, |
||||||
|
isNeedBanner, |
||||||
|
isMatch, |
||||||
|
]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (isFullscreen || !isOpenAd) return undefined |
||||||
|
|
||||||
|
const timeoutCloseAd = setTimeout(handleClose, currentAdsTime * 1000) |
||||||
|
return () => { |
||||||
|
clearTimeout(timeoutCloseAd) |
||||||
|
} |
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [ |
||||||
|
isNeedToShow, |
||||||
|
isOpenAd, |
||||||
|
isFullscreen, |
||||||
|
currentAdsTime, |
||||||
|
]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
close() |
||||||
|
const timeoutCloseBtn = time_close && setTimeout(showCloseBtn, time_close * 1000) |
||||||
|
return () => { |
||||||
|
time_close && clearTimeout(timeoutCloseBtn) |
||||||
|
} |
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [ |
||||||
|
isNeedToShow, |
||||||
|
isOpenAd, |
||||||
|
views?.HOME, |
||||||
|
]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (!isNeedToShow || (!isMatch && !isNeedBanner)) return |
||||||
|
|
||||||
|
(async () => { |
||||||
|
await updateAdsView({ adv_id: id }) |
||||||
|
})() |
||||||
|
sendBannerClickEvent(EventGA.DISPLAY) |
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [ |
||||||
|
id, |
||||||
|
isNeedToShow, |
||||||
|
isMatch, |
||||||
|
]) |
||||||
|
|
||||||
|
return { |
||||||
|
handleClose, |
||||||
|
isNeedToShow, |
||||||
|
isOpenAd, |
||||||
|
isOpenCloseBtn, |
||||||
|
isVideo, |
||||||
|
sendBannerClickEvent, |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,91 @@ |
|||||||
|
import type { MouseEvent } from 'react' |
||||||
|
import { memo } from 'react' |
||||||
|
|
||||||
|
import type { AdType } from 'requests' |
||||||
|
|
||||||
|
import { useLexicsStore } from 'features/LexicsStore' |
||||||
|
|
||||||
|
import { useAd } from './hooks' |
||||||
|
|
||||||
|
import { EventGA } from '../../types' |
||||||
|
|
||||||
|
import { |
||||||
|
AdImg, |
||||||
|
AdVideo, |
||||||
|
AdWrapper, |
||||||
|
LinkWrapper, |
||||||
|
AdsCloseButton, |
||||||
|
} from './styled' |
||||||
|
|
||||||
|
export type AdComponentType = { |
||||||
|
ad: AdType, |
||||||
|
} |
||||||
|
export const AdComponent = memo(({ ad }: AdComponentType) => { |
||||||
|
const { |
||||||
|
link, |
||||||
|
media, |
||||||
|
position, |
||||||
|
} = ad |
||||||
|
|
||||||
|
const { |
||||||
|
handleClose, |
||||||
|
isNeedToShow, |
||||||
|
isOpenAd, |
||||||
|
isOpenCloseBtn, |
||||||
|
isVideo, |
||||||
|
sendBannerClickEvent, |
||||||
|
} = useAd({ ad }) |
||||||
|
|
||||||
|
const { suffix } = useLexicsStore() |
||||||
|
|
||||||
|
const close = (e: MouseEvent<HTMLButtonElement>) => { |
||||||
|
e.stopPropagation() |
||||||
|
handleClose() |
||||||
|
} |
||||||
|
|
||||||
|
const onLinkClick = () => { |
||||||
|
link && sendBannerClickEvent(EventGA.CLICK) |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
position && isOpenAd && isNeedToShow |
||||||
|
? ( |
||||||
|
<AdWrapper |
||||||
|
position={position.id} |
||||||
|
isOpenAd={isOpenAd} |
||||||
|
> |
||||||
|
{isOpenCloseBtn && ( |
||||||
|
<AdsCloseButton |
||||||
|
onClick={close} |
||||||
|
size={12} |
||||||
|
position={position.id} |
||||||
|
/> |
||||||
|
)} |
||||||
|
<LinkWrapper |
||||||
|
href={link} |
||||||
|
target='_blank' |
||||||
|
rel='noreferrer' |
||||||
|
onClick={onLinkClick} |
||||||
|
> |
||||||
|
{isVideo |
||||||
|
? ( |
||||||
|
<AdVideo |
||||||
|
muted={isVideo} |
||||||
|
autoPlay={isVideo} |
||||||
|
loop={isVideo} |
||||||
|
src={media.url} |
||||||
|
position={position.id} |
||||||
|
/> |
||||||
|
) |
||||||
|
: ( |
||||||
|
<AdImg |
||||||
|
src={media.url} |
||||||
|
position={position.id} |
||||||
|
alt={`name_${suffix}`} |
||||||
|
/> |
||||||
|
)} |
||||||
|
</LinkWrapper> |
||||||
|
</AdWrapper> |
||||||
|
) : null |
||||||
|
) |
||||||
|
}) |
||||||
@ -0,0 +1,109 @@ |
|||||||
|
import styled, { css } from 'styled-components/macro' |
||||||
|
|
||||||
|
import includes from 'lodash/includes' |
||||||
|
|
||||||
|
import { CloseButton } from 'features/PopupComponents' |
||||||
|
|
||||||
|
import { |
||||||
|
MATCH_ADS, |
||||||
|
PLAYER_ADS, |
||||||
|
VIEW_ADS, |
||||||
|
} from '../../types' |
||||||
|
|
||||||
|
type Props = { |
||||||
|
position: number, |
||||||
|
} |
||||||
|
|
||||||
|
const header = [7, 8, 9] |
||||||
|
|
||||||
|
const chooseStyle = (type: number) => { |
||||||
|
switch (true) { |
||||||
|
case VIEW_ADS.COLUMN === type: |
||||||
|
return 'grid-row: 1 / 3; img {max-height: none;}' |
||||||
|
case VIEW_ADS.ROW === type: |
||||||
|
return 'grid-column: 1 / 3' |
||||||
|
case VIEW_ADS.SQUARE === type: |
||||||
|
return 'grid-row: 1 / 3; grid-column: 1 / 3; img {max-height: none;}' |
||||||
|
case VIEW_ADS.SECOND_COLUMN === type: |
||||||
|
return 'grid-column: 2 / 3; grid-row: 1 / 1' |
||||||
|
case VIEW_ADS.SECOND_ROW === type: |
||||||
|
return 'grid-column: 1 / 2; grid-row: 2 / 3;' |
||||||
|
case MATCH_ADS.PLAYS_TOP === type: |
||||||
|
return 'margin-left: 14px; height: 48px' |
||||||
|
case MATCH_ADS.PLAYS_BOTTOM === type: |
||||||
|
return 'grid-row: 4; margin-bottom: 12px; height: 48px;' |
||||||
|
case PLAYER_ADS.LEFT_BOTTOM === type: |
||||||
|
return css` |
||||||
|
height: auto;
|
||||||
|
width: 42.4%;
|
||||||
|
bottom: 100px;
|
||||||
|
left: 20px;
|
||||||
|
` |
||||||
|
case PLAYER_ADS.CENTER_BOTTOM === type: |
||||||
|
return css` |
||||||
|
height: 18.3%;
|
||||||
|
width: 81.3%;
|
||||||
|
bottom: 100px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%); |
||||||
|
` |
||||||
|
case PLAYER_ADS.RIGHT === type: |
||||||
|
return css` |
||||||
|
height: 87.2%;
|
||||||
|
width: 18.3%;
|
||||||
|
bottom: 90px;
|
||||||
|
right: 18px; |
||||||
|
` |
||||||
|
case PLAYER_ADS.FULL_SCREEN === type: |
||||||
|
return 'bottom: 0; left: 0;' |
||||||
|
default: |
||||||
|
return '' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const AdImg = styled.img<Props>` |
||||||
|
width: 100%; |
||||||
|
min-height: ${({ position }) => (!includes(header, position) && '100%')}; |
||||||
|
max-height: ${({ position }) => (includes(header, position) ? '13rem' : '100%')}; |
||||||
|
cursor: pointer; |
||||||
|
border-radius: 3px; |
||||||
|
` |
||||||
|
|
||||||
|
export const AdVideo = styled.video<Props>` |
||||||
|
object-fit: contain; |
||||||
|
width: 100%; |
||||||
|
cursor: pointer; |
||||||
|
max-height: ${({ position }) => (includes(header, position) ? '283px' : '100%')}; |
||||||
|
background-color: black; |
||||||
|
border-radius: 3px; |
||||||
|
` |
||||||
|
|
||||||
|
export const AdWrapper = styled.div<Props & {isOpenAd: boolean}>` |
||||||
|
position: ${({ position }) => (includes(PLAYER_ADS, position) ? 'absolute' : 'relative')}; |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
z-index: 1; |
||||||
|
|
||||||
|
${({ position }) => chooseStyle(position)}; |
||||||
|
|
||||||
|
display: ${({ isOpenAd }) => (isOpenAd ? '' : 'none')}; |
||||||
|
` |
||||||
|
|
||||||
|
export const AdsCloseButton = styled(CloseButton)<Props>` |
||||||
|
position: absolute; |
||||||
|
right: ${({ position }) => (position === PLAYER_ADS.FULL_SCREEN ? '10px' : '0')}; |
||||||
|
top: ${({ position }) => (position === PLAYER_ADS.FULL_SCREEN ? '10px' : '0')}; |
||||||
|
background: none; |
||||||
|
border-radius: 0; |
||||||
|
z-index: 2; |
||||||
|
cursor: pointer; |
||||||
|
color: #9B9B9B; |
||||||
|
width: 28px; |
||||||
|
height: 28px; |
||||||
|
|
||||||
|
:hover { |
||||||
|
background: none; |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
export const LinkWrapper = styled.a`` |
||||||
@ -0,0 +1,79 @@ |
|||||||
|
import type { MouseEvent } from 'react' |
||||||
|
|
||||||
|
import includes from 'lodash/includes' |
||||||
|
|
||||||
|
import type { AdType } from 'requests' |
||||||
|
|
||||||
|
import { useLexicsStore } from 'features/LexicsStore' |
||||||
|
|
||||||
|
import { useAd } from '../AdComponent/hooks' |
||||||
|
import { EventGA, PLAYER_MOBILE_FULL_SCREEN } from '../../types' |
||||||
|
|
||||||
|
import { |
||||||
|
AdsCloseButton, |
||||||
|
Img, |
||||||
|
MobileAdWrapper, |
||||||
|
Video, |
||||||
|
} from './styled' |
||||||
|
|
||||||
|
type MobileAdTypes = { |
||||||
|
ad: AdType, |
||||||
|
} |
||||||
|
|
||||||
|
export const MobileAd = ({ ad }: MobileAdTypes) => { |
||||||
|
const { |
||||||
|
link, |
||||||
|
media, |
||||||
|
position, |
||||||
|
} = ad |
||||||
|
|
||||||
|
const { suffix } = useLexicsStore() |
||||||
|
|
||||||
|
const { |
||||||
|
handleClose, |
||||||
|
isNeedToShow, |
||||||
|
isOpenAd, |
||||||
|
isOpenCloseBtn, |
||||||
|
isVideo, |
||||||
|
sendBannerClickEvent, |
||||||
|
} = useAd({ ad }) |
||||||
|
|
||||||
|
const close = (e: MouseEvent<HTMLButtonElement>) => { |
||||||
|
e.stopPropagation() |
||||||
|
handleClose() |
||||||
|
} |
||||||
|
|
||||||
|
const onLinkClick = () => { |
||||||
|
if (link) { |
||||||
|
sendBannerClickEvent(EventGA.CLICK) |
||||||
|
window.open(link, '_blank') |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
position && isOpenAd && isNeedToShow ? ( |
||||||
|
<MobileAdWrapper |
||||||
|
position={position.id} |
||||||
|
onClick={onLinkClick} |
||||||
|
> |
||||||
|
{isOpenCloseBtn |
||||||
|
&& ( |
||||||
|
<AdsCloseButton |
||||||
|
position={position.id} |
||||||
|
onClick={close} |
||||||
|
size={includes(PLAYER_MOBILE_FULL_SCREEN, position.id) ? 12 : 8} |
||||||
|
/> |
||||||
|
)} |
||||||
|
{isVideo |
||||||
|
? <Video position={position.id} src={media.url} /> |
||||||
|
: ( |
||||||
|
<Img |
||||||
|
position={position.id} |
||||||
|
src={media.url} |
||||||
|
alt={`name_${suffix}`} |
||||||
|
/> |
||||||
|
)} |
||||||
|
</MobileAdWrapper> |
||||||
|
) : null |
||||||
|
) |
||||||
|
} |
||||||
@ -0,0 +1,115 @@ |
|||||||
|
import styled, { css } from 'styled-components/macro' |
||||||
|
|
||||||
|
import includes from 'lodash/includes' |
||||||
|
|
||||||
|
import { CloseButton } from 'features/PopupComponents' |
||||||
|
|
||||||
|
import { |
||||||
|
MATCH_ADS, |
||||||
|
PLAYER_MOBILE_FULL_SCREEN, |
||||||
|
PLAYER_MOBILE_ADS, |
||||||
|
MATCH_PAGE_MOBILE_ADS, |
||||||
|
VIEW_ADS, |
||||||
|
} from '../../types' |
||||||
|
|
||||||
|
type Props = { |
||||||
|
position: number, |
||||||
|
} |
||||||
|
|
||||||
|
const chooseStyle = (type: number) => { |
||||||
|
switch (type) { |
||||||
|
case MATCH_ADS.PLAYS_BOTTOM_MOBILE: |
||||||
|
return css` |
||||||
|
grid-row: 4;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
height: 48px; |
||||||
|
` |
||||||
|
case PLAYER_MOBILE_ADS: |
||||||
|
return css` |
||||||
|
position: absolute; |
||||||
|
width: 92%;
|
||||||
|
bottom: 50px;
|
||||||
|
left: 15px; |
||||||
|
` |
||||||
|
case PLAYER_MOBILE_FULL_SCREEN.VERTICAL_FULL_SCREEN: |
||||||
|
case PLAYER_MOBILE_FULL_SCREEN.HORIZONTAL_FULL_SCREEN: |
||||||
|
return css` |
||||||
|
position: absolute; |
||||||
|
top: 0; |
||||||
|
left: 0; |
||||||
|
height: 100%; |
||||||
|
padding: 5px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.7); |
||||||
|
` |
||||||
|
case VIEW_ADS.MOBILE_IN_COLLAPSE_FOOTER: |
||||||
|
case VIEW_ADS.MOBILE_IN_COLLAPSE_HEADER: |
||||||
|
return 'margin-top: 6px' |
||||||
|
default: |
||||||
|
return '' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const MobileAdWrapper = styled.div<Props>` |
||||||
|
position: relative; |
||||||
|
width: 100%; |
||||||
|
z-index: ${({ position }) => (includes(PLAYER_MOBILE_FULL_SCREEN, position) ? '101' : '4')}; |
||||||
|
|
||||||
|
${({ position }) => chooseStyle(position)}; |
||||||
|
` |
||||||
|
|
||||||
|
export const AdsCloseButton = styled(CloseButton)<Props>` |
||||||
|
position: absolute; |
||||||
|
right: ${({ position }) => (includes(PLAYER_MOBILE_FULL_SCREEN, position) ? '-5px' : '-10px')}; |
||||||
|
top: ${({ position }) => (includes(PLAYER_MOBILE_FULL_SCREEN, position) ? '15px' : '10px')}; |
||||||
|
background: none; |
||||||
|
border-radius: 0; |
||||||
|
transform: translate(-50%, -50%); |
||||||
|
z-index: 2; |
||||||
|
color: #9B9B9B; |
||||||
|
` |
||||||
|
|
||||||
|
export const Img = styled.img<Props>` |
||||||
|
border-radius: 2px; |
||||||
|
width: 100%; |
||||||
|
|
||||||
|
object-fit: ${({ position }) => { |
||||||
|
switch (true) { |
||||||
|
case position === PLAYER_MOBILE_FULL_SCREEN.VERTICAL_FULL_SCREEN: |
||||||
|
return 'fill' |
||||||
|
case position === MATCH_ADS.PLAYS_TOP_MOBILE: |
||||||
|
return 'contain' |
||||||
|
default: |
||||||
|
return 'cover' |
||||||
|
} |
||||||
|
}}; |
||||||
|
|
||||||
|
height: ${({ position }) => { |
||||||
|
switch (true) { |
||||||
|
case position === 10: |
||||||
|
return '50px' |
||||||
|
case includes(MATCH_PAGE_MOBILE_ADS, position): |
||||||
|
return '100%' |
||||||
|
default: |
||||||
|
return '75px' |
||||||
|
} |
||||||
|
}} |
||||||
|
` |
||||||
|
|
||||||
|
export const Video = styled.video<Props>` |
||||||
|
max-height: 100%; |
||||||
|
object-fit: cover; |
||||||
|
min-width: 100%; |
||||||
|
height: ${({ position }) => (position === 10 ? '50px' : '75px')}; |
||||||
|
border-radius: 2px; |
||||||
|
|
||||||
|
height: ${({ position }) => { |
||||||
|
switch (true) { |
||||||
|
case position === 10: |
||||||
|
return '50px' |
||||||
|
case position === MATCH_ADS.PLAYS_TOP_MOBILE: |
||||||
|
return '48px' |
||||||
|
default: |
||||||
|
return '75px' |
||||||
|
} |
||||||
|
}} |
||||||
|
` |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
import type { AdResponse, AdsListType } from 'requests' |
||||||
|
|
||||||
|
export const calcMaxAdDurationAds = (advertisements: AdResponse) => { |
||||||
|
const allAds = Object.values(advertisements) |
||||||
|
|
||||||
|
const combineAds = allAds.reduce((result, currentAd) => { |
||||||
|
result.push(...currentAd) |
||||||
|
|
||||||
|
return result |
||||||
|
}, [] as AdsListType) |
||||||
|
|
||||||
|
const maxDuration = combineAds |
||||||
|
.reduce((result, { duration }) => Math.max(result, duration), 0) |
||||||
|
|
||||||
|
return maxDuration |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
export * from './isVideo' |
||||||
@ -0,0 +1,2 @@ |
|||||||
|
const regexp = /^https?:\/\/\S+(?:mp4)$/ |
||||||
|
export const checkVideo = (url: string) => regexp.test(url) |
||||||
@ -0,0 +1,65 @@ |
|||||||
|
import { useMemo } from 'react' |
||||||
|
|
||||||
|
import { useQuery } from 'react-query' |
||||||
|
|
||||||
|
import { useRecoilState } from 'recoil' |
||||||
|
|
||||||
|
import { isMobileDevice, querieKeys } from 'config' |
||||||
|
|
||||||
|
import { getAds } from 'requests' |
||||||
|
|
||||||
|
import { isMatchPage } from 'helpers/isMatchPage' |
||||||
|
import { useLang } from 'features/LexicsStore/hooks/useLang' |
||||||
|
import { useAuthStore } from 'features/AuthStore' |
||||||
|
|
||||||
|
import { |
||||||
|
DeviceType, |
||||||
|
PageType, |
||||||
|
} from './types' |
||||||
|
import { calcMaxAdDurationAds } from './helpers/calcMaxDurationAds' |
||||||
|
import { adsStore } from '../../pages/HighlightsPage/storeHighlightsAtoms' |
||||||
|
|
||||||
|
type Props = { |
||||||
|
matchId?: number, |
||||||
|
sportType?: number, |
||||||
|
tournamentId?: number, |
||||||
|
} |
||||||
|
|
||||||
|
export const useAds = ({ |
||||||
|
matchId, |
||||||
|
sportType, |
||||||
|
tournamentId, |
||||||
|
}: Props) => { |
||||||
|
const [ads, setAds] = useRecoilState(adsStore) |
||||||
|
const { lang } = useLang() |
||||||
|
const { user } = useAuthStore() |
||||||
|
|
||||||
|
useQuery({ |
||||||
|
enabled: isMatchPage() ? (!!user && !!tournamentId) : !!user, |
||||||
|
queryFn: async () => { |
||||||
|
const adsList = await getAds({ |
||||||
|
client_type: isMobileDevice ? DeviceType.MOBILE : DeviceType.WEB, |
||||||
|
language: lang, |
||||||
|
type_id: isMatchPage() ? PageType.MATCH : PageType.HOME, |
||||||
|
...isMatchPage() && { |
||||||
|
matches: [{ |
||||||
|
match_id: matchId, |
||||||
|
sport_id: sportType, |
||||||
|
}], |
||||||
|
tournaments: [{ |
||||||
|
sport_id: sportType, |
||||||
|
tournament_id: tournamentId, |
||||||
|
}], |
||||||
|
}, |
||||||
|
}) |
||||||
|
adsList && setAds(adsList) |
||||||
|
return adsList |
||||||
|
}, |
||||||
|
queryKey: [querieKeys.ads, matchId], |
||||||
|
staleTime: useMemo(() => Math.max(calcMaxAdDurationAds(ads), 60 * 1000), [ads]), |
||||||
|
}) |
||||||
|
|
||||||
|
return { |
||||||
|
ads, |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
import type { AdType } from 'requests' |
||||||
|
|
||||||
|
import { isMobileDevice } from 'config' |
||||||
|
|
||||||
|
import type { AdsPropsType } from './types' |
||||||
|
import { AdComponent } from './components/AdComponent' |
||||||
|
import { MobileAd } from './components/MobileAd' |
||||||
|
|
||||||
|
import { |
||||||
|
HeaderWrapAd, |
||||||
|
} from './styled' |
||||||
|
|
||||||
|
export const HeaderAds = ({ ads }: AdsPropsType) => ( |
||||||
|
ads?.length ? ( |
||||||
|
<HeaderWrapAd column={ads?.length}> |
||||||
|
{ads.map((ad: AdType) => ( |
||||||
|
!isMobileDevice ? ( |
||||||
|
<AdComponent |
||||||
|
ad={ad} |
||||||
|
key={ad.id} |
||||||
|
/> |
||||||
|
) : ( |
||||||
|
<MobileAd |
||||||
|
ad={ad} |
||||||
|
key={ad.id} |
||||||
|
/> |
||||||
|
) |
||||||
|
))} |
||||||
|
</HeaderWrapAd> |
||||||
|
) : null |
||||||
|
) |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
import styled, { css } from 'styled-components/macro' |
||||||
|
|
||||||
|
import { isMobileDevice } from 'config' |
||||||
|
|
||||||
|
export const HeaderWrapAd = styled.div<{column: number}>` |
||||||
|
width: 100%; |
||||||
|
margin-bottom: 0.7rem; |
||||||
|
display: grid; |
||||||
|
grid-column-gap: 0.9rem; |
||||||
|
grid-template-columns: ${({ column }) => (column > 1 ? `repeat(${column},${16.3 * 6 / column}%)` : 'repeat(1, 98.7%)')}; |
||||||
|
|
||||||
|
${isMobileDevice && css` |
||||||
|
padding: 0 0.71rem; |
||||||
|
grid-template-columns: none; |
||||||
|
`}}
|
||||||
|
` |
||||||
@ -0,0 +1,59 @@ |
|||||||
|
import type { AdsListType } from 'requests' |
||||||
|
|
||||||
|
export enum PageType { |
||||||
|
HOME = 1, |
||||||
|
MATCH = 2, |
||||||
|
} |
||||||
|
|
||||||
|
export enum DeviceType { |
||||||
|
MOBILE = 'mobile', |
||||||
|
WEB = 'web' |
||||||
|
} |
||||||
|
|
||||||
|
export type ViewsType = Partial<Record<keyof typeof PageType, number>> |
||||||
|
|
||||||
|
export enum EventGA { |
||||||
|
CLICK = 'banner_click', |
||||||
|
CLOSE = 'banner_close', |
||||||
|
DISPLAY = 'banner_display' |
||||||
|
} |
||||||
|
|
||||||
|
export enum VIEW_ADS { |
||||||
|
ROW = 4, |
||||||
|
COLUMN = 5, |
||||||
|
SQUARE = 6, |
||||||
|
SECOND_COLUMN = 2, |
||||||
|
SECOND_ROW = 3, |
||||||
|
MOBILE_IN_COLLAPSE_HEADER = 12, |
||||||
|
MOBILE_IN_COLLAPSE_FOOTER = 25 |
||||||
|
} |
||||||
|
|
||||||
|
export const HEADER_MOBILE_ADS = [10, 11] |
||||||
|
|
||||||
|
export enum MATCH_ADS { |
||||||
|
WATCH_TOP = 13, |
||||||
|
PLAYS_TOP = 14, |
||||||
|
PLAYS_BOTTOM = 15, |
||||||
|
PLAYS_TOP_MOBILE = 16, |
||||||
|
PLAYS_BOTTOM_MOBILE = 17, |
||||||
|
} |
||||||
|
|
||||||
|
export enum PLAYER_ADS { |
||||||
|
LEFT_BOTTOM = 18, |
||||||
|
CENTER_BOTTOM = 19, |
||||||
|
RIGHT = 20, |
||||||
|
FULL_SCREEN = 21, |
||||||
|
} |
||||||
|
|
||||||
|
export const PLAYER_MOBILE_ADS = 22 |
||||||
|
|
||||||
|
export enum PLAYER_MOBILE_FULL_SCREEN { |
||||||
|
VERTICAL_FULL_SCREEN = 23, |
||||||
|
HORIZONTAL_FULL_SCREEN = 24, |
||||||
|
} |
||||||
|
|
||||||
|
export const MATCH_PAGE_MOBILE_ADS = [16, 17, 22, 23, 24] |
||||||
|
|
||||||
|
export type AdsPropsType = Record<'ads', AdsListType | undefined> |
||||||
|
|
||||||
|
export const adsViews = 'adsViews' |
||||||
@ -0,0 +1,72 @@ |
|||||||
|
import type { Props, State } from '../types' |
||||||
|
import { |
||||||
|
createVariableSizingTransformationSet, |
||||||
|
createClones, |
||||||
|
createDefaultTransformationSet, |
||||||
|
getElementDimensions, |
||||||
|
getItemsCount, |
||||||
|
getItemsOffset, |
||||||
|
getTransitionProperty, |
||||||
|
getTranslate3dProperty, |
||||||
|
getItemsInSlide, |
||||||
|
} from './elements' |
||||||
|
import { getActiveIndex, getStartIndex } from './math' |
||||||
|
|
||||||
|
export const calculateInitialState = (props: Props, el: HTMLElement | null): State => { |
||||||
|
const { |
||||||
|
activeIndex: propsActiveIndex = 0, |
||||||
|
animationDuration = 0, |
||||||
|
infinite = false, |
||||||
|
variableSizing = false, |
||||||
|
} = props |
||||||
|
const clones = createClones(props) |
||||||
|
const transition = getTransitionProperty() |
||||||
|
const itemsCount = getItemsCount(props) |
||||||
|
const itemsOffset = getItemsOffset(props) |
||||||
|
const itemsInSlide = getItemsInSlide(itemsCount, props) |
||||||
|
const startIndex = getStartIndex(propsActiveIndex, itemsCount) |
||||||
|
const activeIndex = getActiveIndex({ |
||||||
|
infinite, |
||||||
|
itemsCount, |
||||||
|
startIndex, |
||||||
|
}) |
||||||
|
const { width: listWidth } = getElementDimensions(el) |
||||||
|
|
||||||
|
const transformationSet = variableSizing |
||||||
|
? createVariableSizingTransformationSet({ |
||||||
|
el, |
||||||
|
infinite, |
||||||
|
listWidth, |
||||||
|
props, |
||||||
|
}).coords |
||||||
|
: createDefaultTransformationSet({ |
||||||
|
children: clones, |
||||||
|
infinite, |
||||||
|
itemsInSlide, |
||||||
|
listWidth, |
||||||
|
props, |
||||||
|
}).coords |
||||||
|
|
||||||
|
const translate3d = getTranslate3dProperty(activeIndex, { |
||||||
|
infinite, |
||||||
|
itemsInSlide, |
||||||
|
itemsOffset, |
||||||
|
transformationSet, |
||||||
|
variableSizing, |
||||||
|
}) |
||||||
|
|
||||||
|
return { |
||||||
|
activeIndex, |
||||||
|
animationDuration, |
||||||
|
clones, |
||||||
|
infinite, |
||||||
|
itemsCount, |
||||||
|
itemsInSlide, |
||||||
|
itemsOffset, |
||||||
|
listWidth, |
||||||
|
transformationSet, |
||||||
|
transition, |
||||||
|
translate3d, |
||||||
|
variableSizing, |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,274 @@ |
|||||||
|
import type { CSSProperties, ReactNode } from 'react' |
||||||
|
import { Children } from 'react' |
||||||
|
|
||||||
|
import type { |
||||||
|
Transformations, |
||||||
|
ItemCoords, |
||||||
|
Props, |
||||||
|
State, |
||||||
|
Transition, |
||||||
|
} from '../types' |
||||||
|
import { mapPartialCoords, mapPositionCoords } from './mappers' |
||||||
|
import { getShiftIndex } from './math' |
||||||
|
|
||||||
|
export const getSlides = ({ children }: Props) => Children.toArray(children) |
||||||
|
|
||||||
|
export const getItemsCount = (props: Props) => getSlides(props).length |
||||||
|
|
||||||
|
export const getItemsOffset = ({ infinite }: Props) => (infinite ? 1 : 0) |
||||||
|
|
||||||
|
export const createClones = (props: Props) => { |
||||||
|
const slides = getSlides(props) |
||||||
|
|
||||||
|
if (!props.infinite) return slides |
||||||
|
|
||||||
|
const itemsCount = getItemsCount(props) |
||||||
|
const itemsOffset = getItemsOffset(props) |
||||||
|
const itemsInSlide = getItemsInSlide(itemsCount, props) |
||||||
|
const cursor = Math.min(itemsInSlide, itemsCount) + itemsOffset |
||||||
|
|
||||||
|
const clonesAfter = slides.slice(0, cursor) |
||||||
|
const clonesBefore = slides.slice(-cursor) |
||||||
|
|
||||||
|
if (itemsOffset && itemsInSlide === itemsCount) { |
||||||
|
const afterOffsetClone = slides[0] |
||||||
|
const [beforeOffsetClone] = slides.slice(-1) |
||||||
|
|
||||||
|
clonesBefore.unshift(beforeOffsetClone) |
||||||
|
clonesAfter.push(afterOffsetClone) |
||||||
|
} |
||||||
|
|
||||||
|
return clonesBefore.concat(slides, clonesAfter) |
||||||
|
} |
||||||
|
|
||||||
|
type CreateVariableSizingTransformationSetArgs = { |
||||||
|
el: HTMLElement | null, |
||||||
|
infinite: boolean, |
||||||
|
listWidth: number, |
||||||
|
props: Props, |
||||||
|
} |
||||||
|
|
||||||
|
export const createVariableSizingTransformationSet = ({ |
||||||
|
el, |
||||||
|
infinite, |
||||||
|
listWidth, |
||||||
|
props, |
||||||
|
}: CreateVariableSizingTransformationSetArgs) => { |
||||||
|
let content = 0 |
||||||
|
let partial = true |
||||||
|
let coords: Array<ItemCoords> = [] |
||||||
|
|
||||||
|
const { spaceBetween = 0 } = props |
||||||
|
|
||||||
|
const children: Array<HTMLElement | Element> = Array.from(el?.children || []) |
||||||
|
|
||||||
|
coords = children.reduce<Array<ItemCoords>>(( |
||||||
|
acc, |
||||||
|
child, |
||||||
|
i, |
||||||
|
) => { |
||||||
|
let position = 0 |
||||||
|
const previewsChildCursor = i - 1 |
||||||
|
const previewsChild = acc[previewsChildCursor] |
||||||
|
const { width = 0 } = getElementDimensions(child?.firstChild as HTMLElement) |
||||||
|
|
||||||
|
content += width + spaceBetween |
||||||
|
partial = listWidth >= content |
||||||
|
|
||||||
|
if (previewsChild) { |
||||||
|
position = previewsChildCursor === 0 |
||||||
|
? previewsChild.width + spaceBetween |
||||||
|
: previewsChild.width + previewsChild.position + spaceBetween |
||||||
|
} |
||||||
|
|
||||||
|
acc.push({ position, width }) |
||||||
|
return acc |
||||||
|
}, []) |
||||||
|
|
||||||
|
if (!infinite) { |
||||||
|
if (partial) { |
||||||
|
coords = mapPartialCoords(coords) |
||||||
|
} else { |
||||||
|
const position = content - listWidth |
||||||
|
coords = mapPositionCoords(coords, position) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
content, |
||||||
|
coords, |
||||||
|
partial, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
type CreateDefaultTransformationSetArgs = { |
||||||
|
children: Array<ReactNode>, |
||||||
|
infinite: boolean, |
||||||
|
itemsInSlide: number, |
||||||
|
listWidth: number, |
||||||
|
props: Props, |
||||||
|
} |
||||||
|
|
||||||
|
export const createDefaultTransformationSet = ({ |
||||||
|
children, |
||||||
|
infinite, |
||||||
|
itemsInSlide, |
||||||
|
listWidth, |
||||||
|
props, |
||||||
|
}: CreateDefaultTransformationSetArgs): Transformations => { |
||||||
|
let content = 0 |
||||||
|
let partial = true |
||||||
|
let coords: Array<ItemCoords> = [] |
||||||
|
|
||||||
|
const { spaceBetween = 0 } = props |
||||||
|
|
||||||
|
const width = getItemWidth({ |
||||||
|
galleryWidth: listWidth, |
||||||
|
itemsInSlide, |
||||||
|
props, |
||||||
|
}) |
||||||
|
|
||||||
|
coords = children.reduce<Array<ItemCoords>>(( |
||||||
|
acc, |
||||||
|
_, |
||||||
|
i, |
||||||
|
) => { |
||||||
|
let position = 0 |
||||||
|
const previewsChild = acc[i - 1] |
||||||
|
|
||||||
|
content += width + spaceBetween |
||||||
|
partial = listWidth >= content |
||||||
|
|
||||||
|
if (previewsChild) { |
||||||
|
position = width + spaceBetween + previewsChild.position || 0 |
||||||
|
} |
||||||
|
|
||||||
|
acc.push({ position, width }) |
||||||
|
return acc |
||||||
|
}, []) |
||||||
|
|
||||||
|
if (!infinite) { |
||||||
|
if (partial) { |
||||||
|
coords = mapPartialCoords(coords) |
||||||
|
} else { |
||||||
|
const position = content - listWidth |
||||||
|
coords = mapPositionCoords(coords, position) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
content, |
||||||
|
coords, |
||||||
|
partial, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
type GetItemWidthArgs = { |
||||||
|
galleryWidth: number, |
||||||
|
itemsInSlide: number, |
||||||
|
props: Props, |
||||||
|
} |
||||||
|
|
||||||
|
export const getItemWidth = ({ |
||||||
|
galleryWidth, |
||||||
|
itemsInSlide, |
||||||
|
props: { spaceBetween = 0 }, |
||||||
|
}: GetItemWidthArgs) => (itemsInSlide > 0 |
||||||
|
? (galleryWidth - spaceBetween * (itemsInSlide - 1)) / itemsInSlide |
||||||
|
: galleryWidth) |
||||||
|
|
||||||
|
export const getElementDimensions = (element: HTMLElement | null) => { |
||||||
|
const { height, width } = element?.getBoundingClientRect() || { height: 0, width: 0 } |
||||||
|
|
||||||
|
return { height, width } |
||||||
|
} |
||||||
|
|
||||||
|
export const getTransitionProperty = (options?: Transition): string => { |
||||||
|
const { animationDuration = 0, animationTimingFunction = 'ease' } = options || {} |
||||||
|
return `transform ${animationDuration}ms ${animationTimingFunction} 0ms` |
||||||
|
} |
||||||
|
|
||||||
|
export const getListElementStyles = ( |
||||||
|
{ translate3d }: Partial<State>, |
||||||
|
currentStyles: CSSProperties, |
||||||
|
): CSSProperties => { |
||||||
|
const transform = `translate3d(${-(translate3d || 0)}px, 0, 0)` |
||||||
|
|
||||||
|
return { ...currentStyles, transform } |
||||||
|
} |
||||||
|
|
||||||
|
export const getItemStyles = (index: number, state: State): CSSProperties => { |
||||||
|
const { transformationSet } = state |
||||||
|
const { width } = transformationSet[index] || {} |
||||||
|
|
||||||
|
return { |
||||||
|
width, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const getTranslate3dProperty = (nextIndex: number, state: Partial<State>) => { |
||||||
|
let cursor = nextIndex |
||||||
|
|
||||||
|
const { |
||||||
|
infinite, |
||||||
|
itemsInSlide = 0, |
||||||
|
itemsOffset = 0, |
||||||
|
transformationSet = [], |
||||||
|
} = state |
||||||
|
|
||||||
|
if (infinite) { |
||||||
|
cursor = nextIndex + getShiftIndex(itemsInSlide, itemsOffset) |
||||||
|
} |
||||||
|
|
||||||
|
return (transformationSet[cursor] || {}).position || 0 |
||||||
|
} |
||||||
|
|
||||||
|
export const isDisplayedItem = (i = 0, state: State) => { |
||||||
|
const { |
||||||
|
activeIndex, |
||||||
|
infinite, |
||||||
|
itemsInSlide, |
||||||
|
itemsOffset, |
||||||
|
variableSizing, |
||||||
|
} = state |
||||||
|
|
||||||
|
const shiftIndex = getShiftIndex(itemsInSlide, itemsOffset) |
||||||
|
|
||||||
|
if (variableSizing && infinite) { |
||||||
|
return i - shiftIndex === activeIndex + itemsOffset |
||||||
|
} |
||||||
|
|
||||||
|
const index = activeIndex + shiftIndex |
||||||
|
|
||||||
|
if (!infinite) { |
||||||
|
return i >= activeIndex && i < index |
||||||
|
} |
||||||
|
|
||||||
|
return i >= index && i < index + itemsInSlide |
||||||
|
} |
||||||
|
|
||||||
|
export const getItemsInSlide = (itemsCount: number, props: Props) => { |
||||||
|
let itemsInSlide = 1 |
||||||
|
|
||||||
|
const { |
||||||
|
breakpoints = {}, |
||||||
|
infinite, |
||||||
|
variableSizing, |
||||||
|
} = props |
||||||
|
|
||||||
|
if (variableSizing) { |
||||||
|
return infinite ? itemsCount : itemsInSlide |
||||||
|
} |
||||||
|
|
||||||
|
const configKeys = Object.keys(breakpoints) |
||||||
|
|
||||||
|
configKeys.forEach((key) => { |
||||||
|
if (Number(key) <= window.innerWidth) { |
||||||
|
const { items, itemsFit = 'fill' } = breakpoints[key] |
||||||
|
|
||||||
|
itemsInSlide = itemsFit === 'contain' ? items : Math.min(items, itemsCount) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
return itemsInSlide || 1 |
||||||
|
} |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
export * from './common' |
||||||
|
export * from './elements' |
||||||
|
export * from './math' |
||||||
|
export * from './mappers' |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
import type { ItemCoords } from '../types' |
||||||
|
|
||||||
|
export const mapPartialCoords = (coords: Array<ItemCoords>) => ( |
||||||
|
coords.map(({ width }) => ({ position: 0, width })) |
||||||
|
) |
||||||
|
|
||||||
|
export const mapPositionCoords = (coords: Array<ItemCoords>, position = 0) => coords.map((item) => { |
||||||
|
if (item.position > position) { |
||||||
|
return { ...item, position } |
||||||
|
} |
||||||
|
return item |
||||||
|
}) |
||||||
@ -0,0 +1,44 @@ |
|||||||
|
import type { ItemCoords } from '../types' |
||||||
|
|
||||||
|
export const getShiftIndex = (itemsInSlide = 0, itemsOffset = 0) => itemsInSlide + itemsOffset |
||||||
|
|
||||||
|
export const getStartIndex = (index = 0, itemsCount = 0) => { |
||||||
|
if (itemsCount) { |
||||||
|
if (index >= itemsCount) { |
||||||
|
return itemsCount - 1 |
||||||
|
} |
||||||
|
|
||||||
|
if (index > 0) { |
||||||
|
return index |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
export const getActiveIndex = ({ |
||||||
|
infinite = false, |
||||||
|
itemsCount = 0, |
||||||
|
startIndex = 0, |
||||||
|
}) => (infinite ? startIndex : getStartIndex(startIndex, itemsCount)) |
||||||
|
|
||||||
|
export const getUpdateSlidePositionIndex = (activeIndex: number, itemsCount: number) => { |
||||||
|
if (activeIndex < 0) return itemsCount - 1 |
||||||
|
if (activeIndex >= itemsCount) return 0 |
||||||
|
|
||||||
|
return activeIndex |
||||||
|
} |
||||||
|
|
||||||
|
export const shouldRecalculateSlideIndex = (activeIndex: number, itemsCount: number) => ( |
||||||
|
activeIndex < 0 || activeIndex >= itemsCount |
||||||
|
) |
||||||
|
|
||||||
|
export const shouldCancelSlideAnimation = (activeIndex: number, itemsCount: number) => ( |
||||||
|
activeIndex < 0 || activeIndex >= itemsCount |
||||||
|
) |
||||||
|
|
||||||
|
export const getTransformationItemIndex = ( |
||||||
|
transformationSet: Array<ItemCoords> = [], |
||||||
|
position = 0, |
||||||
|
) => transformationSet.findIndex((item) => item.position >= Math.abs(position)) |
||||||
|
|
||||||
@ -0,0 +1,160 @@ |
|||||||
|
import { |
||||||
|
useRef, |
||||||
|
useEffect, |
||||||
|
useLayoutEffect, |
||||||
|
type ReactNode, |
||||||
|
} from 'react' |
||||||
|
|
||||||
|
import { KEYBOARD_KEYS } from 'config' |
||||||
|
|
||||||
|
import { useEventListener, useObjectState } from 'hooks' |
||||||
|
|
||||||
|
import type { State, Props } from './types' |
||||||
|
import * as Utils from './helpers' |
||||||
|
import { ListItem } from './styled' |
||||||
|
|
||||||
|
export const useCarousel = (props: Props) => { |
||||||
|
const [state, setState] = useObjectState<State>(Utils.calculateInitialState(props, null)) |
||||||
|
const isAnimationDisabledRef = useRef(false) |
||||||
|
const slideEndTimeoutIdRef = useRef<number | null>(null) |
||||||
|
const listElementRef = useRef<HTMLUListElement>(null) |
||||||
|
const rootElementRef = useRef<HTMLDivElement>(null) |
||||||
|
|
||||||
|
const { |
||||||
|
activeIndex, |
||||||
|
animationDuration, |
||||||
|
clones, |
||||||
|
itemsCount, |
||||||
|
itemsInSlide, |
||||||
|
transition, |
||||||
|
translate3d, |
||||||
|
} = state |
||||||
|
|
||||||
|
const { |
||||||
|
animationTimingFunction, |
||||||
|
infinite, |
||||||
|
onSlideChange, |
||||||
|
useKeyboardNavigation, |
||||||
|
} = props |
||||||
|
|
||||||
|
const listElementStyles = Utils.getListElementStyles({ translate3d }, { transition }) |
||||||
|
|
||||||
|
const clearSlideEndTimeout = () => { |
||||||
|
slideEndTimeoutIdRef.current && clearTimeout(slideEndTimeoutIdRef.current) |
||||||
|
slideEndTimeoutIdRef.current = null |
||||||
|
} |
||||||
|
|
||||||
|
const slidePrev = () => { |
||||||
|
const newActiveIndex = activeIndex - 1 |
||||||
|
|
||||||
|
handleSlideTo(newActiveIndex) |
||||||
|
} |
||||||
|
|
||||||
|
const slideNext = () => { |
||||||
|
const newActiveIndex = activeIndex + 1 |
||||||
|
|
||||||
|
handleSlideTo(newActiveIndex) |
||||||
|
} |
||||||
|
|
||||||
|
const handleUpdateSlidePosition = (index: number) => { |
||||||
|
const newTranslate3d = Utils.getTranslate3dProperty(index, state) |
||||||
|
const newTransition = Utils.getTransitionProperty({ animationDuration: 0 }) |
||||||
|
|
||||||
|
setState({ |
||||||
|
activeIndex: index, |
||||||
|
transition: newTransition, |
||||||
|
translate3d: newTranslate3d, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
const handleBeforeSlideEnd = (index: number) => { |
||||||
|
if (Utils.shouldRecalculateSlideIndex(index, itemsCount)) { |
||||||
|
const nextIndex = Utils.getUpdateSlidePositionIndex(index, itemsCount) |
||||||
|
handleUpdateSlidePosition(nextIndex) |
||||||
|
} |
||||||
|
|
||||||
|
isAnimationDisabledRef.current = false |
||||||
|
} |
||||||
|
|
||||||
|
const handleSlideTo = (newActiveIndex = 0) => { |
||||||
|
if ( |
||||||
|
isAnimationDisabledRef.current |
||||||
|
|| newActiveIndex === activeIndex |
||||||
|
|| (!infinite && Utils.shouldCancelSlideAnimation(newActiveIndex, itemsCount)) |
||||||
|
) return |
||||||
|
|
||||||
|
isAnimationDisabledRef.current = true |
||||||
|
clearSlideEndTimeout() |
||||||
|
|
||||||
|
const newTranslate3d = Utils.getTranslate3dProperty(newActiveIndex, state) |
||||||
|
|
||||||
|
const newTransition = Utils.getTransitionProperty({ |
||||||
|
animationDuration, |
||||||
|
animationTimingFunction, |
||||||
|
}) |
||||||
|
|
||||||
|
onSlideChange?.(newActiveIndex) |
||||||
|
|
||||||
|
setState({ |
||||||
|
activeIndex: newActiveIndex, |
||||||
|
transition: newTransition, |
||||||
|
translate3d: newTranslate3d, |
||||||
|
}) |
||||||
|
|
||||||
|
slideEndTimeoutIdRef.current = setTimeout( |
||||||
|
() => handleBeforeSlideEnd(newActiveIndex), |
||||||
|
animationDuration, |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
const renderItem = (item: ReactNode, index: number) => { |
||||||
|
const styles = Utils.getItemStyles(index, state) |
||||||
|
|
||||||
|
return ( |
||||||
|
<ListItem |
||||||
|
key={index} |
||||||
|
style={styles} |
||||||
|
aria-hidden={!Utils.isDisplayedItem(index, state)} |
||||||
|
> |
||||||
|
{item} |
||||||
|
</ListItem> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
useEventListener({ |
||||||
|
callback: (e) => { |
||||||
|
if (!useKeyboardNavigation) return |
||||||
|
if (e.key === KEYBOARD_KEYS.ArrowLeft) slidePrev() |
||||||
|
if (e.key === KEYBOARD_KEYS.ArrowRight) slideNext() |
||||||
|
}, |
||||||
|
event: 'keydown', |
||||||
|
}) |
||||||
|
|
||||||
|
useLayoutEffect(() => { |
||||||
|
const setInitialState = () => { |
||||||
|
const initialState = Utils.calculateInitialState(props, listElementRef.current) |
||||||
|
|
||||||
|
setState(initialState) |
||||||
|
} |
||||||
|
|
||||||
|
setInitialState() |
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [props.activeIndex]) |
||||||
|
|
||||||
|
useEffect(() => () => { |
||||||
|
slideEndTimeoutIdRef.current && clearTimeout(slideEndTimeoutIdRef.current) |
||||||
|
}, []) |
||||||
|
|
||||||
|
return { |
||||||
|
activeIndex, |
||||||
|
clones, |
||||||
|
itemsCount, |
||||||
|
itemsInSlide, |
||||||
|
listElementRef, |
||||||
|
listElementStyles, |
||||||
|
renderItem, |
||||||
|
rootElementRef, |
||||||
|
slideNext, |
||||||
|
slidePrev, |
||||||
|
} |
||||||
|
} |
||||||