Browse Source

Correct Aspect Ratio Of Images And Implement New Resizing Policy

Francesco Baccetti 4 years ago
parent
commit
b037be41b9

+ 3 - 0
package.json

@@ -123,5 +123,8 @@
     },
     "clearMocks": true,
     "coverageDirectory": ".coverage"
+  },
+  "devDependencies": {
+    "@types/react-responsive": "^8.0.2"
   }
 }

+ 1 - 1
src/components/ChannelGrid.tsx

@@ -17,7 +17,7 @@ const ChannelGrid: React.FC<ChannelGridProps> = ({ channels, ...gridProps }) =>
   return (
     <Grid {...gridProps}>
       {channels.map((c) => (
-        <StyledChannelPreview key={c.id} id={c.id} views={c.totalViews} name={c.handle} avatarURL={c.avatarPhotoURL} />
+        <StyledChannelPreview key={c.id} id={c.id} name={c.handle} avatarURL={c.avatarPhotoURL} />
       ))}
     </Grid>
   )

+ 1 - 0
src/components/VideoGrid.tsx

@@ -7,6 +7,7 @@ import VideoPreview from './VideoPreviewWithNavigation'
 
 const StyledVideoPreview = styled(VideoPreview)`
   margin: 0 auto;
+  width: 100%;
 `
 
 type VideoGridProps = {

+ 32 - 8
src/shared/components/Grid/Grid.tsx

@@ -1,21 +1,42 @@
 import React from 'react'
 import styled from '@emotion/styled'
 import useResizeObserver from 'use-resize-observer'
+import { useMediaQuery } from 'react-responsive'
 import { spacing } from '../../theme'
 
-const Container = styled.div<GridProps>`
+const toPx = (n: number | string) => (typeof n === 'number' ? `${n}px` : n)
+
+const LARGE_VIEWPORT_BREAKPOINT = toPx(2000)
+
+type ContainerProps = {
+  gap: number | string
+  gridTemplateColumns: string
+}
+
+const Container = styled.div<ContainerProps>`
   display: grid;
-  gap: ${(props) => props.gap};
-  grid-template-columns: repeat(${(props) => `auto-${props.repeat}`}, minmax(min(270px, 100%), 1fr));
+  gap: ${(props) => toPx(props.gap)};
+  grid-template-columns: ${(props) => props.gridTemplateColumns};
 `
 
 type GridProps = {
   gap?: number | string
-  repeat?: 'fit' | 'fill'
   className?: string
+  maxColumns?: number
+  minWidth?: number | string
+  repeat?: 'fit' | 'fill'
   onResize?: (sizes: number[]) => void
 }
-const Grid: React.FC<GridProps> = ({ children, className, gap = spacing.xl, repeat = 'fit', onResize, ...props }) => {
+
+const Grid: React.FC<GridProps> = ({
+  className,
+  gap = spacing.xl,
+  onResize,
+  repeat = 'fit',
+  maxColumns = 6,
+  minWidth = 300,
+  ...props
+}) => {
   const { ref: gridRef } = useResizeObserver<HTMLDivElement>({
     onResize: () => {
       if (onResize && gridRef.current) {
@@ -25,11 +46,14 @@ const Grid: React.FC<GridProps> = ({ children, className, gap = spacing.xl, repe
       }
     },
   })
+  const isLargeViewport = useMediaQuery({ query: `(min-width: ${LARGE_VIEWPORT_BREAKPOINT})` })
+
+  const gridTemplateColumns = isLargeViewport
+    ? `repeat(${maxColumns}, 1fr)`
+    : `repeat(auto-${repeat}, minmax(min(${toPx(minWidth)}, 100%), 1fr))`
 
   return (
-    <Container {...props} repeat={repeat} className={className} ref={gridRef} gap={gap}>
-      {children}
-    </Container>
+    <Container {...props} className={className} ref={gridRef} gap={gap} gridTemplateColumns={gridTemplateColumns} />
   )
 }
 export default Grid

+ 5 - 4
src/shared/components/Placeholder/Placeholder.tsx

@@ -1,6 +1,7 @@
 import { keyframes } from '@emotion/core'
 import styled from '@emotion/styled'
 import { colors } from '@/shared/theme'
+import { darken } from 'polished'
 
 type PlaceholderProps = {
   width?: string | number
@@ -11,11 +12,11 @@ type PlaceholderProps = {
 const getPropValue = (v: string | number) => (typeof v === 'string' ? v : `${v}px`)
 
 const pulse = keyframes`
-  0, 100% {
-    opacity: 1
+  0, 100% { 
+    background-color: ${colors.gray[400]}
   }
   50% {
-    opacity: 0.5
+    background-color: ${darken(0.15, colors.gray[400])}
   }
 `
 const Placeholder = styled.div<PlaceholderProps>`
@@ -23,7 +24,7 @@ const Placeholder = styled.div<PlaceholderProps>`
   height: ${({ height = '100%' }) => getPropValue(height)};
   border-radius: ${({ rounded = false }) => (rounded ? '100%' : '0')};
   background-color: ${colors.gray['400']};
-  animation: ${pulse} 1s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+  animation: ${pulse} 0.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
 `
 
 export default Placeholder

+ 3 - 0
src/shared/components/VideoPreview/VideoPreview.styles.tsx

@@ -13,6 +13,9 @@ type ChannelProps = {
 
 export const CoverImage = styled.img<CoverImageProps>`
   display: block;
+  position: absolute;
+  top: 0;
+  left: 0;
   width: 100%;
   height: 100%;
   background-color: ${colors.gray['400']};

+ 3 - 5
src/shared/components/VideoPreview/VideoPreviewBase.styles.tsx

@@ -20,19 +20,17 @@ const fadeIn = keyframes`
   }
 `
 export const CoverContainer = styled.div`
+  position: relative;
   width: 100%;
-  height: 190px;
-
+  height: 0;
+  padding-top: 56.25%;
   transition-property: box-shadow, transform;
   transition-duration: 0.4s;
   transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
-
-  position: relative;
   animation: ${fadeIn} 0.5s ease-in;
 `
 
 export const Container = styled.article<ContainerProps>`
-  max-width: ${MAX_VIDEO_PREVIEW_WIDTH};
   color: ${colors.gray[300]};
   cursor: ${({ clickable }) => (clickable ? 'pointer' : 'auto')};
   display: inline-block;

+ 8 - 1
src/shared/components/VideoPreview/VideoPreviewBase.tsx

@@ -35,7 +35,7 @@ const VideoPreviewBase: React.FC<VideoPreviewBaseProps> = ({
 }) => {
   const clickable = !!onClick
 
-  const coverPlaceholder = <Placeholder />
+  const coverPlaceholder = <CoverPlaceholder />
   const channelAvatarPlaceholder = <Placeholder rounded />
   const titlePlaceholder = <Placeholder height="18px" width="60%" />
   const channelNamePlaceholder = <SpacedPlaceholder height="12px" width="60%" />
@@ -59,5 +59,12 @@ const VideoPreviewBase: React.FC<VideoPreviewBaseProps> = ({
 const SpacedPlaceholder = styled(Placeholder)`
   margin-top: 6px;
 `
+const CoverPlaceholder = styled(Placeholder)`
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+`
 
 export default VideoPreviewBase

+ 7 - 0
yarn.lock

@@ -2917,6 +2917,13 @@
   dependencies:
     "@types/react" "*"
 
+"@types/react-responsive@^8.0.2":
+  version "8.0.2"
+  resolved "https://registry.yarnpkg.com/@types/react-responsive/-/react-responsive-8.0.2.tgz#959fdc32f72e38b807e32be617a74be2c315081b"
+  integrity sha512-DTvm3Hb77v0hme7L4GYzRjLQqlZP+zNImFBzdKbSH7CsQ5c7QebGnSQX2Xf3BaA0rm/TQE57eFMhMGLcMe/A9w==
+  dependencies:
+    "@types/react" "*"
+
 "@types/react-syntax-highlighter@11.0.4":
   version "11.0.4"
   resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.4.tgz#d86d17697db62f98046874f62fdb3e53a0bbc4cd"