VideoGallery.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import styled from '@emotion/styled'
  2. import React, { useMemo } from 'react'
  3. import { VideoFieldsFragment } from '@/api/queries'
  4. import { Gallery, RankingNumberTile } from '@/shared/components'
  5. import { breakpointsOfGrid } from '@/shared/components/Grid'
  6. import { AvatarContainer } from '@/shared/components/VideoTileBase/VideoTileBase.styles'
  7. import { media } from '@/shared/theme'
  8. import { VideoTile } from './VideoTile'
  9. interface VideoFieldsWithProgress extends VideoFieldsFragment {
  10. progress?: number
  11. }
  12. type VideoWithIdAndProgress = {
  13. id: string
  14. progress?: number
  15. }
  16. type CustomVideosType = VideoFieldsWithProgress[] | VideoWithIdAndProgress[]
  17. type VideoGalleryProps = {
  18. title?: string
  19. videos?: CustomVideosType | null
  20. loading?: boolean
  21. removeButton?: boolean
  22. onRemoveButtonClick?: (id: string) => void
  23. onVideoNotFound?: (id: string) => void
  24. onVideoClick?: (id: string) => void
  25. hasRanking?: boolean
  26. seeAllUrl?: string
  27. className?: string
  28. }
  29. const PLACEHOLDERS_COUNT = 12
  30. const MIN_VIDEO_PREVIEW_WIDTH = 281
  31. const CAROUSEL_SMALL_BREAKPOINT = 688
  32. const FRACTIONAL_LEVEL = 1.3
  33. const FRACTIONAL_LEVEL_RANKING = 1.4
  34. export const VideoGallery: React.FC<VideoGalleryProps> = ({
  35. title,
  36. videos = [],
  37. loading,
  38. onVideoClick,
  39. removeButton,
  40. onRemoveButtonClick,
  41. onVideoNotFound,
  42. seeAllUrl,
  43. hasRanking = false,
  44. className,
  45. }) => {
  46. const breakpoints = useMemo(() => {
  47. return breakpointsOfGrid({
  48. breakpoints: 6,
  49. minItemWidth: 300,
  50. gridColumnGap: 24,
  51. viewportContainerDifference: 64,
  52. }).map((breakpoint, idx) => {
  53. if (breakpoint <= CAROUSEL_SMALL_BREAKPOINT && hasRanking) {
  54. return {
  55. breakpoint,
  56. settings: {
  57. slidesToShow: idx + FRACTIONAL_LEVEL,
  58. slidesToScroll: idx + 1,
  59. },
  60. }
  61. }
  62. return {
  63. breakpoint,
  64. settings: {
  65. slidesToShow: idx + (breakpoint <= CAROUSEL_SMALL_BREAKPOINT ? FRACTIONAL_LEVEL_RANKING : 1),
  66. slidesToScroll: idx + 1,
  67. },
  68. }
  69. })
  70. }, [hasRanking])
  71. if (loading === false && videos?.length === 0) {
  72. return null
  73. }
  74. const placeholderItems = Array.from({ length: loading || !videos?.length ? PLACEHOLDERS_COUNT : 0 }, () => ({
  75. id: undefined,
  76. progress: undefined,
  77. }))
  78. const createClickHandler = (id?: string) => () => id && onVideoClick && onVideoClick(id)
  79. const createRemoveButtonClickHandler = (id?: string) => () => id && onRemoveButtonClick && onRemoveButtonClick(id)
  80. const createNotFoundHandler = (id?: string) => () => id && onVideoNotFound && onVideoNotFound(id)
  81. return (
  82. <Gallery
  83. title={title}
  84. responsive={breakpoints}
  85. itemWidth={MIN_VIDEO_PREVIEW_WIDTH}
  86. dotsVisible
  87. seeAllUrl={seeAllUrl}
  88. className={className}
  89. >
  90. {[...(videos ? videos : []), ...placeholderItems]?.map((video, idx) =>
  91. hasRanking ? (
  92. <RankingNumberTile variant="video" rankingNumber={idx + 1} key={`${idx}-${video.id}`}>
  93. <StyledVideoTile
  94. id={video.id}
  95. progress={video?.progress}
  96. removeButton={video ? removeButton : false}
  97. onClick={createClickHandler(video.id)}
  98. onNotFound={createNotFoundHandler(video.id)}
  99. onRemoveButtonClick={createRemoveButtonClickHandler(video.id)}
  100. />
  101. </RankingNumberTile>
  102. ) : (
  103. <StyledVideoTile
  104. key={`${idx}-${video.id}`}
  105. id={video.id}
  106. progress={video?.progress}
  107. removeButton={video ? removeButton : false}
  108. onClick={createClickHandler(video.id)}
  109. onNotFound={createNotFoundHandler(video.id)}
  110. onRemoveButtonClick={createRemoveButtonClickHandler(video.id)}
  111. />
  112. )
  113. )}
  114. </Gallery>
  115. )
  116. }
  117. const StyledVideoTile = styled(VideoTile)`
  118. flex-shrink: 0;
  119. ${AvatarContainer} {
  120. display: none;
  121. ${media.medium} {
  122. display: block;
  123. }
  124. }
  125. `