Bladeren bron

Add Mobile And Table Screens

Francesco Baccetti 4 jaren geleden
bovenliggende
commit
a9020bd4d7

+ 1 - 0
package.json

@@ -95,6 +95,7 @@
     "react-dom": "^16.13.1",
     "react-glider": "^2.0.2",
     "react-player": "^2.2.0",
+    "react-responsive": "^8.1.0",
     "react-scripts": "3.4.1",
     "react-spring": "^8.0.27",
     "storybook-addon-jsx": "^7.1.15",

+ 1 - 1
src/components/ChannelGallery.tsx

@@ -7,7 +7,7 @@ import { spacing } from '@/shared/theme'
 import { ChannelFields } from '@/api/queries/__generated__/ChannelFields'
 
 type ChannelGalleryProps = {
-  title: string
+  title?: string
   action?: string
   channels?: ChannelFields[]
   loading?: boolean

+ 25 - 0
src/components/ChannelGrid.tsx

@@ -0,0 +1,25 @@
+import React from 'react'
+import styled from '@emotion/styled'
+
+import { ChannelFields } from '@/api/queries/__generated__/ChannelFields'
+import { Grid } from '@/shared/components'
+import ChannelPreview from './ChannelPreviewWithNavigation'
+
+const StyledChannelPreview = styled(ChannelPreview)`
+  margin: 0 auto;
+`
+
+type ChannelGridProps = {
+  channels: ChannelFields[]
+} & React.ComponentProps<typeof Grid>
+
+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} />
+      ))}
+    </Grid>
+  )
+}
+export default ChannelGrid

+ 6 - 2
src/components/VideoBestMatch/VideoBestMatch.style.tsx

@@ -1,5 +1,5 @@
 import styled from '@emotion/styled'
-import { colors } from '@/shared/theme'
+import { colors, breakpoints as bp } from '@/shared/theme'
 
 export const Container = styled.div`
   color: ${colors.gray[300]};
@@ -8,8 +8,12 @@ export const Container = styled.div`
 
 export const Content = styled.div`
   display: grid;
-  grid-template-columns: 650px 1fr;
+  grid-template-columns: minmax(50%, 650px) 1fr;
   grid-column-gap: 24px;
+
+  @media (max-width: ${bp.mobile}) {
+    grid-template-columns: 1fr;
+  }
 `
 export const Poster = styled.img`
   width: 100%;

+ 1 - 1
src/components/VideoGallery.tsx

@@ -9,7 +9,7 @@ import { VideoFields } from '@/api/queries/__generated__/VideoFields'
 import { spacing } from '@/shared/theme'
 
 type VideoGalleryProps = {
-  title: string
+  title?: string
   action?: string
   videos?: VideoFields[]
   loading?: boolean

+ 1 - 0
src/components/index.ts

@@ -8,3 +8,4 @@ export { default as VideoBestMatch } from './VideoBestMatch'
 export { default as VideoGrid } from './VideoGrid'
 export { default as VideoPreview } from './VideoPreviewWithNavigation'
 export { default as ChannelPreview } from './ChannelPreviewWithNavigation'
+export { default as ChannelGrid } from './ChannelGrid'

+ 2 - 3
src/shared/theme/breakpoints.ts

@@ -1,5 +1,4 @@
 export default {
-  small: '21rem',
-  medium: '48rem',
-  large: '72rem',
+  mobile: '480px',
+  tablet: '1156px',
 }

+ 10 - 1
src/views/ChannelView/ChannelView.style.tsx

@@ -1,5 +1,6 @@
 import styled from '@emotion/styled'
 import { Avatar } from '@/shared/components'
+import { fluidRange } from 'polished'
 import theme from '@/shared/theme'
 
 type ChannelHeaderProps = {
@@ -19,10 +20,14 @@ export const TitleSection = styled.div`
   display: flex;
   align-items: center;
   padding-top: ${theme.sizes.b10}px;
+  @media (max-width: ${theme.breakpoints.mobile}) {
+    flex-direction: column;
+    align-items: start;
+  }
 `
 export const Title = styled.h1`
-  font-size: ${theme.typography.sizes.h2};
   font-weight: bold;
+  ${fluidRange({ prop: 'fontSize', fromSize: '34px', toSize: '40px' })};
   max-width: 320px;
   display: inline-block;
   margin: 0;
@@ -38,4 +43,8 @@ export const StyledAvatar = styled(Avatar)`
   width: 136px;
   height: 136px;
   margin-right: ${theme.sizes.b6}px;
+  @media (max-width: ${theme.breakpoints.mobile}) {
+    width: 128px;
+    height: 128px;
+  }
 `

+ 18 - 8
src/views/SearchView.tsx

@@ -7,15 +7,16 @@ import { useQuery } from '@apollo/client'
 import { SEARCH } from '@/api/queries'
 import { Search, SearchVariables } from '@/api/queries/__generated__/Search'
 import { TabsMenu } from '@/shared/components'
-import { VideoGrid, ChannelGallery, VideoBestMatch } from '@/components'
+import { VideoGrid, ChannelGallery, VideoBestMatch, VideoGallery } from '@/components'
 import routes from '@/config/routes'
+import ChannelGrid from '@/components/ChannelGrid'
 
 type SearchViewProps = {
   search?: string
 } & RouteComponentProps
 const tabs = ['all results', 'videos', 'channels']
 
-const VideosHeader = styled.h5`
+const SectionHeader = styled.h5`
   margin: 0 0 ${spacing.m};
   font-size: ${typography.sizes.h5};
 `
@@ -46,15 +47,24 @@ const SearchView: React.FC<SearchViewProps> = ({ search = '' }) => {
   return (
     <Container>
       <TabsMenu tabs={tabs} onSelectTab={setSelectedIndex} initialIndex={0} />
-      {bestMatch && <VideoBestMatch video={bestMatch} onClick={() => navigate(routes.video(bestMatch.id))} />}
-      {videos.length > 0 && (selectedIndex === 0 || selectedIndex === 1) && (
+      {bestMatch && selectedIndex === 0 && (
+        <VideoBestMatch video={bestMatch} onClick={() => navigate(routes.video(bestMatch.id))} />
+      )}
+      {videos.length > 0 && selectedIndex !== 2 && (
         <div>
-          <VideosHeader>Videos</VideosHeader>
-          <VideoGrid videos={videos} />
+          <SectionHeader>Videos</SectionHeader>
+          {selectedIndex === 0 ? <VideoGallery videos={videos} /> : <VideoGrid videos={videos} />}
         </div>
       )}
-      {channels.length > 0 && (selectedIndex === 0 || selectedIndex === 2) && (
-        <ChannelGallery title="Channels" action="See all" loading={loading} channels={channels} />
+      {channels.length > 0 && selectedIndex !== 1 && (
+        <div>
+          <SectionHeader>Channels</SectionHeader>
+          {selectedIndex === 0 ? (
+            <ChannelGallery channels={channels} />
+          ) : (
+            <ChannelGrid channels={channels} repeat="fill" />
+          )}
+        </div>
       )}
     </Container>
   )

+ 5 - 1
src/views/VideoView/VideoView.style.tsx

@@ -1,5 +1,5 @@
 import styled from '@emotion/styled'
-import { ChannelAvatar } from '@/shared/components'
+import { ChannelAvatar, VideoPlayer } from '@/shared/components'
 import theme from '@/shared/theme'
 
 export const Container = styled.div`
@@ -58,3 +58,7 @@ export const MoreVideosHeader = styled.h5`
   margin: 0 0 ${theme.spacing.m};
   font-size: ${theme.typography.sizes.h5};
 `
+
+export const StyledVideoPlayer = styled(VideoPlayer)`
+  width: min(1250px, 100%);
+`

+ 2 - 1
src/views/VideoView/VideoView.tsx

@@ -9,6 +9,7 @@ import {
   MoreVideosHeader,
   PlayerContainer,
   StyledChannelAvatar,
+  StyledVideoPlayer,
   Title,
   TitleActionsContainer,
 } from './VideoView.style'
@@ -67,7 +68,7 @@ const VideoView: React.FC<RouteComponentProps> = () => {
   return (
     <Container>
       <PlayerContainer>
-        <VideoPlayer src={data.video.media.location} height={700} autoplay />
+        <StyledVideoPlayer src={data.video.media.location} autoplay fluid />
       </PlayerContainer>
       <InfoContainer>
         <TitleActionsContainer>

+ 27 - 0
yarn.lock

@@ -6215,6 +6215,11 @@ css-loader@^3.0.0:
     schema-utils "^2.7.0"
     semver "^6.3.0"
 
+css-mediaquery@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/css-mediaquery/-/css-mediaquery-0.1.2.tgz#6a2c37344928618631c54bd33cedd301da18bea0"
+  integrity sha1-aiw3NEkoYYYxxUvTPO3TAdoYvqA=
+
 css-prefers-color-scheme@^3.1.1:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz#6f830a2714199d4f0d0d0bb8a27916ed65cff1f4"
@@ -9082,6 +9087,11 @@ hyperlinker@^1.0.0:
   resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e"
   integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==
 
+hyphenate-style-name@^1.0.0:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d"
+  integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==
+
 iconv-lite@0.4.24, iconv-lite@^0.4.24:
   version "0.4.24"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@@ -11268,6 +11278,13 @@ markdown-to-jsx@^6.11.4:
     prop-types "^15.6.2"
     unquote "^1.1.0"
 
+matchmediaquery@^0.3.0:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/matchmediaquery/-/matchmediaquery-0.3.1.tgz#8247edc47e499ebb7c58f62a9ff9ccf5b815c6d7"
+  integrity sha512-Hlk20WQHRIm9EE9luN1kjRjYXAQToHOIAHPJn9buxBwuhfTHoKUcX+lXBbxc85DVQfXYbEQ4HcwQdd128E3qHQ==
+  dependencies:
+    css-mediaquery "^0.1.2"
+
 material-colors@^1.2.1:
   version "1.2.6"
   resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46"
@@ -14151,6 +14168,16 @@ react-popper@^1.3.7:
     typed-styles "^0.0.7"
     warning "^4.0.2"
 
+react-responsive@^8.1.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/react-responsive/-/react-responsive-8.1.0.tgz#afcc2293c46a37b1e7926ff7fef66bcb147e7cba"
+  integrity sha512-U8Nv2/ZWACIw/fAE9XNPbc2Xo33X5q1bcCASc2SufvJ9ifB+o/rokfogfznSVcvS22hN1rafGi0uZD6GiVFEHw==
+  dependencies:
+    hyphenate-style-name "^1.0.0"
+    matchmediaquery "^0.3.0"
+    prop-types "^15.6.1"
+    shallow-equal "^1.1.0"
+
 react-scripts@3.4.1:
   version "3.4.1"
   resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-3.4.1.tgz#f551298b5c71985cc491b9acf3c8e8c0ae3ada0a"