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

@@ -0,0 +1,29 @@
import type { ReactNode, ElementType, ComponentPropsWithoutRef } from 'react';
import styles from 'src/styles/modules/components/card/card.module.scss';
// ensuring that other attributes to <Card/> are correct based on the value of 'as' prop.
// a cheap implementation of as prop found in libraries like CharkaUI or MaterialUI.
type Props<T extends ElementType> = {
children: ReactNode;
as?: T | 'section';
hoverable?: true;
} & ComponentPropsWithoutRef<T>;
const Card = <T extends ElementType = 'li'>({
children,
as,
hoverable,
className,
...rest
}: Props<T>) => {
const Component = as ?? 'li';
const classNames = `${hoverable ? styles.hoverable : ''} ${styles.card} ${className}`;
return (
<Component className={classNames} {...rest}>
{children}
</Component>
);
};
export default Card;

View File

@@ -0,0 +1,45 @@
import { ComponentPropsWithoutRef, CSSProperties, ReactNode } from 'react';
import Image from 'next/future/image';
import Card from './Card';
import { getProxiedIMDbImgUrl, modifyIMDbImg } from 'src/utils/helpers';
import styles from 'src/styles/modules/components/card/card-basic.module.scss';
type Props = {
children: ReactNode;
className?: string;
image?: string;
title: string;
} & ComponentPropsWithoutRef<'section'>;
const CardBasic = ({ image, children, className, title, ...rest }: Props) => {
const style: CSSProperties = {
backgroundImage: image && `url(${getProxiedIMDbImgUrl(modifyIMDbImg(image, 300))})`,
};
return (
<Card as='section' className={`${styles.container} ${className}`} {...rest}>
<div className={styles.imageContainer} style={style}>
{image ? (
<Image
className={styles.image}
src={modifyIMDbImg(image)}
alt=''
priority
fill
sizes='300px'
/>
) : (
<svg className={styles.imageNA}>
<use href='/svg/sprite.svg#icon-image-slash' />
</svg>
)}
</div>
<div className={styles.info}>
<h1 className={`${styles.title} heading heading__primary`}>{title}</h1>
{children}
</div>
</Card>
);
};
export default CardBasic;

View File

@@ -0,0 +1,51 @@
import Card from './Card';
import styles from 'src/styles/modules/components/card/card-cast.module.scss';
import { ComponentPropsWithoutRef, ReactNode } from 'react';
import Link from 'next/link';
import Image from 'next/future/image';
import { modifyIMDbImg } from 'src/utils/helpers';
type Props = {
link: string;
name: string;
characters: string[] | null;
attributes: string[] | null;
image?: string | null;
children?: ReactNode;
} & ComponentPropsWithoutRef<'li'>;
const CardCast = ({ link, name, image, children, characters, attributes, ...rest }: Props) => {
return (
<Card hoverable {...rest}>
<Link href={link}>
<a className={styles.item}>
<div className={styles.imgContainer}>
{image ? (
<Image
src={modifyIMDbImg(image, 400)}
alt=''
fill
className={styles.img}
sizes='200px'
/>
) : (
<svg className={styles.imgNA}>
<use href='/svg/sprite.svg#icon-image-slash' />
</svg>
)}
</div>
<div className={styles.textContainer}>
<p className={`heading ${styles.name}`}>{name}</p>
<p className={styles.role}>
{characters?.join(', ')}
{attributes && <span> ({attributes.join(', ')})</span>}
</p>
{children}
</div>
</a>
</Link>
</Card>
);
};
export default CardCast;

View File

@@ -0,0 +1,42 @@
import { ComponentPropsWithoutRef, ReactNode } from 'react';
import Link from 'next/link';
import Image from 'next/future/image';
import Card from './Card';
import { modifyIMDbImg } from 'src/utils/helpers';
import styles from 'src/styles/modules/components/card/card-result.module.scss';
type Props = {
link: string;
name: string;
image?: string;
showImage?: true;
children?: ReactNode;
} & ComponentPropsWithoutRef<'li'>;
const CardResult = ({ link, name, image, showImage, children, ...rest }: Props) => {
let ImageComponent = null;
if (showImage)
ImageComponent = image ? (
<Image src={modifyIMDbImg(image, 400)} alt='' fill className={styles.img} sizes='200px' />
) : (
<svg className={styles.imgNA}>
<use href='/svg/sprite.svg#icon-image-slash' />
</svg>
);
return (
<Card hoverable {...rest}>
<Link href={link}>
<a className={`${styles.item} ${!showImage && styles.sansImage}`}>
<div className={styles.imgContainer}>{ImageComponent}</div>
<div className={styles.info}>
<p className={`heading ${styles.heading}`}>{name}</p>
{children}
</div>
</a>
</Link>
</Card>
);
};
export default CardResult;

View File

@@ -0,0 +1,63 @@
import Card from './Card';
import styles from 'src/styles/modules/components/card/card-title.module.scss';
import { ComponentPropsWithoutRef, ReactNode } from 'react';
import Link from 'next/link';
import Image from 'next/future/image';
import { formatNumber, modifyIMDbImg } from 'src/utils/helpers';
type Props = {
link: string;
name: string;
titleType: string;
year?: { start: number; end: number | null };
ratings?: { avg: number | null; numVotes: number };
image?: string;
children?: ReactNode;
} & ComponentPropsWithoutRef<'li'>;
const CardTitle = ({ link, name, year, image, ratings, titleType, children, ...rest }: Props) => {
const years = year?.end ? `${year.start}-${year.end}` : year?.start;
return (
<Card hoverable {...rest}>
<Link href={link}>
<a className={styles.item}>
<div className={styles.imgContainer}>
{image ? (
<Image
src={modifyIMDbImg(image, 400)}
alt=''
fill
className={styles.img}
sizes='200px'
/>
) : (
<svg className={styles.imgNA}>
<use href='/svg/sprite.svg#icon-image-slash' />
</svg>
)}
</div>
<div className={styles.textContainer}>
<p className={`heading ${styles.name}`}>{name}</p>
<p>
<span>{titleType}</span>
<span>{years && ` (${years})`}</span>
</p>
{ratings?.avg && (
<p className={styles.rating}>
<span className={styles.ratingNum}>{ratings.avg}</span>
<svg className={styles.ratingIcon}>
<use href='/svg/sprite.svg#icon-rating'></use>
</svg>
<span> ({formatNumber(ratings.numVotes)} votes)</span>
</p>
)}
{children}
</div>
</a>
</Link>
</Card>
);
};
export default CardTitle;

View File

@@ -0,0 +1,5 @@
export { default as Card } from './Card';
export { default as CardTitle } from './CardTitle';
export { default as CardBasic } from './CardBasic';
export { default as CardCast } from './CardCast';
export { default as CardResult } from './CardResult';