refactor: make components more modular

would help in implementing name route

also did some stylistic changes
This commit is contained in:
zyachel
2023-04-15 20:56:15 +05:30
parent 8ce02d0236
commit 18ca98fd4a
43 changed files with 757 additions and 796 deletions

View File

@@ -1,13 +1,8 @@
import { Fragment } from 'react';
import Image from 'next/future/image';
import Link from 'next/link';
import { CardBasic } from 'src/components/card';
import { Basic } from 'src/interfaces/shared/title';
import {
formatNumber,
formatTime,
getProxiedIMDbImgUrl,
modifyIMDbImg,
} from 'src/utils/helpers';
import { formatNumber, formatTime } from 'src/utils/helpers';
import styles from 'src/styles/modules/components/title/basic.module.scss';
type Props = {
@@ -23,135 +18,92 @@ const Basic = ({ data, className }: Props) => {
: data.releaseYear?.start;
return (
<section
// role is valid but not known to jsx-a11y
// aria-description={`basic info for '${data.title}'`}
// style={{ backgroundImage: data.poster && `url(${data.poster?.url})` }}
<CardBasic
className={`${styles.container} ${className}`}
image={data.poster?.url}
title={data.title}
>
<div
className={styles.imageContainer}
style={{
backgroundImage:
data.poster &&
`url(${getProxiedIMDbImgUrl(modifyIMDbImg(data.poster.url, 300))})`,
}}
>
{data.poster ? (
<Image
className={styles.image}
src={modifyIMDbImg(data.poster.url)}
alt={data.poster.caption}
priority
fill
sizes='300px'
/>
) : (
<svg className={styles.image__NA}>
<use href='/svg/sprite.svg#icon-image-slash' />
</svg>
<ul className={styles.meta} aria-label='quick facts'>
{data.status && data.status.id !== 'released' && (
<li className={styles.meta__text}>{data.status.text}</li>
)}
</div>
<div className={styles.info}>
<h1 className={`${styles.title} heading heading__primary`}>
{data.title}
</h1>
<ul className={styles.meta} aria-label='quick facts'>
{data.status && data.status.id !== 'released' && (
<li className={styles.meta__text}>{data.status.text}</li>
)}
<li className={styles.meta__text}>{data.type.name}</li>
{data.releaseYear && (
<li className={styles.meta__text}>{releaseTime}</li>
)}
{data.ceritficate && (
<li className={styles.meta__text}>{data.ceritficate}</li>
)}
{data.runtime && (
<li className={styles.meta__text}>{formatTime(data.runtime)}</li>
)}
</ul>
<div className={styles.ratings}>
{data.ratings.avg && (
<>
<p className={styles.rating}>
<span className={styles.rating__num}>{data.ratings.avg}</span>
<svg className={styles.rating__icon}>
<use href='/svg/sprite.svg#icon-rating'></use>
</svg>
<span className={styles.rating__text}> Avg. rating</span>
</p>
<p className={styles.rating}>
<span className={styles.rating__num}>
{formatNumber(data.ratings.numVotes)}
</span>
<svg className={styles.rating__icon}>
<use href='/svg/sprite.svg#icon-like-dislike'></use>
</svg>
<span className={styles.rating__text}> No. of votes</span>
</p>
</>
)}
{data.ranking && (
<li className={styles.meta__text}>{data.type.name}</li>
{data.releaseYear && <li className={styles.meta__text}>{releaseTime}</li>}
{data.ceritficate && <li className={styles.meta__text}>{data.ceritficate}</li>}
{data.runtime && <li className={styles.meta__text}>{formatTime(data.runtime)}</li>}
</ul>
<div className={styles.ratings}>
{data.ratings.avg && (
<>
<p className={styles.rating}>
<span className={styles.rating__num}>
{formatNumber(data.ranking.position)}
</span>
<span className={styles.rating__num}>{data.ratings.avg}</span>
<svg className={styles.rating__icon}>
<use href='/svg/sprite.svg#icon-graph-rising'></use>
<use href='/svg/sprite.svg#icon-rating'></use>
</svg>
<span className={styles.rating__text}>
{' '}
Popularity (
<span className={styles.rating__sub}>
{data.ranking.direction === 'UP'
? `\u2191${formatNumber(data.ranking.change)}`
: data.ranking.direction === 'DOWN'
? `\u2193${formatNumber(data.ranking.change)}`
: ''}
</span>
)
</span>
<span className={styles.rating__text}> Avg. rating</span>
</p>
)}
</div>
{!!data.genres.length && (
<p className={styles.genres}>
<span className={styles.genres__heading}>Genres: </span>
{data.genres.map((genre, i) => (
<Fragment key={genre.id}>
{i > 0 && ', '}
<Link href={`/search/title?genres=${genre.id}`}>
<a className={styles.link}>{genre.text}</a>
</Link>
</Fragment>
))}
<p className={styles.rating}>
<span className={styles.rating__num}>{formatNumber(data.ratings.numVotes)}</span>
<svg className={styles.rating__icon}>
<use href='/svg/sprite.svg#icon-like-dislike'></use>
</svg>
<span className={styles.rating__text}> No. of votes</span>
</p>
</>
)}
{data.ranking && (
<p className={styles.rating}>
<span className={styles.rating__num}>{formatNumber(data.ranking.position)}</span>
<svg className={styles.rating__icon}>
<use href='/svg/sprite.svg#icon-graph-rising'></use>
</svg>
<span className={styles.rating__text}>
{' '}
Popularity (
<span className={styles.rating__sub}>
{data.ranking.direction === 'UP'
? `\u2191${formatNumber(data.ranking.change)}`
: data.ranking.direction === 'DOWN'
? `\u2193${formatNumber(data.ranking.change)}`
: ''}
</span>
)
</span>
</p>
)}
{
<p className={styles.overview}>
<span className={styles.overview__heading}>Plot: </span>
<span className={styles.overview__text}>{data.plot || '-'}</span>
</p>
}
{data.primaryCrew.map(crewType => (
<p className={styles.crewType} key={crewType.type.id}>
<span className={styles.crewType__heading}>
{`${crewType.type.category}: `}
</span>
{crewType.crew.map((crew, i) => (
<Fragment key={crew.id}>
{i > 0 && ', '}
<Link href={`/name/${crew.id}`}>
<a className={styles.link}>{crew.name}</a>
</Link>
</Fragment>
))}
</p>
))}
</div>
</section>
{!!data.genres.length && (
<p className={styles.genres}>
<span className={styles.genres__heading}>Genres: </span>
{data.genres.map((genre, i) => (
<Fragment key={genre.id}>
{i > 0 && ', '}
<Link href={`/search/title?genres=${genre.id}`}>
<a className={styles.link}>{genre.text}</a>
</Link>
</Fragment>
))}
</p>
)}
<p className={styles.overview}>
<span className={styles.overview__heading}>Plot: </span>
<span className={styles.overview__text}>{data.plot || '-'}</span>
</p>
{data.primaryCrew.map(crewType => (
<p className={styles.crewType} key={crewType.type.id}>
<span className={styles.crewType__heading}>{`${crewType.type.category}: `}</span>
{crewType.crew.map((crew, i) => (
<Fragment key={crew.id}>
{i > 0 && ', '}
<Link href={`/name/${crew.id}`}>
<a className={styles.link}>{crew.name}</a>
</Link>
</Fragment>
))}
</p>
))}
</CardBasic>
);
};

View File

@@ -1,7 +1,5 @@
import Image from 'next/future/image';
import Link from 'next/link';
import { CardCast } from 'src/components/card';
import { Cast } from 'src/interfaces/shared/title';
import { modifyIMDbImg } from 'src/utils/helpers';
import styles from 'src/styles/modules/components/title/cast.module.scss';
type Props = {
@@ -10,46 +8,25 @@ type Props = {
};
const Cast = ({ className, cast }: Props) => {
if (!cast.length) return <></>;
if (!cast.length) return null;
return (
<section className={`${className} ${styles.container}`}>
<h2 className='heading heading__secondary'>Cast</h2>
<ul className={styles.cast}>
{cast.map(member => (
<li key={member.id} className={styles.member}>
<div className={styles.member__imgContainer}>
{member.image ? (
<Image
src={modifyIMDbImg(member.image, 400)}
alt=''
fill
className={styles.member__img}
sizes='200px'
/>
) : (
<svg className={styles.member__imgNA}>
<use href='/svg/sprite.svg#icon-image-slash' />
</svg>
)}
</div>
<div className={styles.member__textContainer}>
<p>
<Link href={`/name/${member.id}`}>
<a className={styles.member__name}>{member.name}</a>
</Link>
</p>
<p className={styles.member__role}>
{member.characters?.join(', ')}
{member.attributes && (
<span> ({member.attributes.join(', ')})</span>
)}
</p>
</div>
</li>
<CardCast
key={member.id}
link={`/name/${member.id}`}
name={member.name}
image={member.image}
characters={member.characters}
attributes={member.attributes}
/>
))}
</ul>
</section>
);
};
export default Cast;

View File

@@ -1,90 +0,0 @@
import Image from 'next/future/image';
import Link from 'next/link';
import { Media } from 'src/interfaces/shared/title';
import { getProxiedIMDbImgUrl, modifyIMDbImg } from 'src/utils/helpers';
import styles from 'src/styles/modules/components/title/media.module.scss';
type Props = {
className: string;
media: Media;
};
const Media = ({ className, media }: Props) => {
return (
<div className={`${className} ${styles.media}`}>
{(media.trailer || !!media.videos.total) && (
<section className={styles.videos}>
<h2 className='heading heading__secondary'>Videos</h2>
<div className={styles.videos__container}>
{media.trailer && (
<div className={styles.trailer}>
<video
aria-label='trailer video'
// it's a relatively new tag. hence jsx-all1 complains
aria-description={media.trailer.caption}
controls
playsInline
poster={getProxiedIMDbImgUrl(
modifyIMDbImg(media.trailer.thumbnail)
)}
className={styles.trailer__video}
preload='none'
>
{media.trailer.urls.map(source => (
<source
key={source.url}
type={source.mimeType}
src={getProxiedIMDbImgUrl(source.url)}
data-res={source.resolution}
/>
))}
</video>
</div>
)}
{!!media.videos.total &&
media.videos.videos.map(video => (
<Link href={`/video/${video.id}`} key={video.id}>
<a className={styles.video}>
<Image
className={styles.video__img}
src={modifyIMDbImg(video.thumbnail)}
alt=''
fill
sizes='400px'
/>
<p className={styles.video__caption}>
{video.caption} ({video.runtime}s)
</p>
</a>
</Link>
))}
</div>
</section>
)}
{!!media.images.total && (
<section className={styles.images}>
<h2 className='heading heading__secondary'>Images</h2>
<div className={styles.images__container}>
{media.images.images.map(image => (
<figure key={image.id} className={styles.image}>
<Image
className={styles.image__img}
src={modifyIMDbImg(image.url)}
alt=''
fill
sizes='400px'
/>
<figcaption className={styles.image__caption}>
{image.caption.plainText}
</figcaption>
</figure>
))}
</div>
</section>
)}
</div>
);
};
export default Media;

View File

@@ -1,7 +1,5 @@
import Image from 'next/future/image';
import Link from 'next/link';
import { CardTitle } from 'src/components/card';
import { MoreLikeThis } from 'src/interfaces/shared/title';
import { formatNumber, modifyIMDbImg } from 'src/utils/helpers';
import styles from 'src/styles/modules/components/title/more-like-this.module.scss';
type Props = {
@@ -10,52 +8,22 @@ type Props = {
};
const MoreLikeThis = ({ className, data }: Props) => {
if (!data.length) return <></>;
if (!data.length) return null;
return (
<section className={`${className} ${styles.morelikethis}`}>
<h2 className='heading heading__secondary'>More like this</h2>
<ul className={styles.container}>
{data.map(title => (
<li key={title.id}>
<Link href={`/title/${title.id}`}>
<a className={styles.item}>
<div className={styles.item__imgContainer}>
{title.poster ? (
<Image
src={modifyIMDbImg(title.poster.url, 400)}
alt=''
fill
className={styles.item__img}
sizes='200px'
/>
) : (
<svg className={styles.item__imgNA}>
<use href='/svg/sprite.svg#icon-image-slash' />
</svg>
)}
</div>
<div className={styles.item__textContainer}>
<h3 className={`heading ${styles.item__heading}`}>
{title.title}
</h3>
{title.ratings.avg && (
<p className={styles.item__rating}>
<span className={styles.item__ratingNum}>
{title.ratings.avg}
</span>
<svg className={styles.item__ratingIcon}>
<use href='/svg/sprite.svg#icon-rating'></use>
</svg>
<span>
({formatNumber(title.ratings.numVotes)} votes)
</span>
</p>
)}
</div>
</a>
</Link>
</li>
<CardTitle
key={title.id}
link={`/title/${title.id}`}
name={title.title}
titleType={title.type.text}
image={title.poster?.url}
year={title.releaseYear}
ratings={title.ratings}
/>
))}
</ul>
</section>

View File

@@ -1,9 +1,6 @@
import Basic from './Basic';
import Cast from './Cast';
import DidYouKnow from './DidYouKnow';
import Info from './Info';
import Media from './Media';
import MoreLikeThis from './MoreLikeThis';
import Reviews from './Reviews';
export { Basic, Cast, DidYouKnow, Info, Media, MoreLikeThis, Reviews };
export { default as Basic } from './Basic';
export { default as Cast } from './Cast';
export { default as DidYouKnow } from './DidYouKnow';
export { default as Info } from './Info';
export { default as MoreLikeThis } from './MoreLikeThis';
export { default as Reviews } from './Reviews';