1
0
Fork 0
mirror of https://github.com/sussy-code/smov.git synced 2024-12-29 16:07:40 +01:00

Styled Loading and mediacard

Co-authored-by: William Oldham <wegg7250@gmail.com>
This commit is contained in:
Jelle van Snik 2022-02-16 22:38:37 +01:00
parent afb6958995
commit 5ddd8638ec
7 changed files with 119 additions and 27 deletions

View file

@ -5,6 +5,7 @@ export enum Icons {
EYE_SLASH = "eyeSlash",
ARROW_LEFT = "arrowLeft",
CHEVRON_DOWN = "chevronDown",
CHEVRON_RIGHT = "chevronRight",
CLAPPER_BOARD = "clapperBoard",
FILM = "film",
DRAGON = "dragon",
@ -21,7 +22,8 @@ const iconList = {
clock: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M256 512C114.6 512 0 397.4 0 256C0 114.6 114.6 0 256 0C397.4 0 512 114.6 512 256C512 397.4 397.4 512 256 512zM232 256C232 264 236 271.5 242.7 275.1L338.7 339.1C349.7 347.3 364.6 344.3 371.1 333.3C379.3 322.3 376.3 307.4 365.3 300L280 243.2V120C280 106.7 269.3 96 255.1 96C242.7 96 231.1 106.7 231.1 120L232 256z"/></svg>`,
eyeSlash: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M150.7 92.77C195 58.27 251.8 32 320 32C400.8 32 465.5 68.84 512.6 112.6C559.4 156 590.7 207.1 605.5 243.7C608.8 251.6 608.8 260.4 605.5 268.3C592.1 300.6 565.2 346.1 525.6 386.7L630.8 469.1C641.2 477.3 643.1 492.4 634.9 502.8C626.7 513.2 611.6 515.1 601.2 506.9L9.196 42.89C-1.236 34.71-3.065 19.63 5.112 9.196C13.29-1.236 28.37-3.065 38.81 5.112L150.7 92.77zM223.1 149.5L313.4 220.3C317.6 211.8 320 202.2 320 191.1C320 180.5 316.1 169.7 311.6 160.4C314.4 160.1 317.2 159.1 320 159.1C373 159.1 416 202.1 416 255.1C416 269.7 413.1 282.7 407.1 294.5L446.6 324.7C457.7 304.3 464 280.9 464 255.1C464 176.5 399.5 111.1 320 111.1C282.7 111.1 248.6 126.2 223.1 149.5zM320 480C239.2 480 174.5 443.2 127.4 399.4C80.62 355.1 49.34 304 34.46 268.3C31.18 260.4 31.18 251.6 34.46 243.7C44 220.8 60.29 191.2 83.09 161.5L177.4 235.8C176.5 242.4 176 249.1 176 255.1C176 335.5 240.5 400 320 400C338.7 400 356.6 396.4 373 389.9L446.2 447.5C409.9 467.1 367.8 480 320 480H320z"/></svg>`,
arrowLeft: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M9.375 233.4l128-128c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25L109.3 224H480c17.69 0 32 14.31 32 32s-14.31 32-32 32H109.3l73.38 73.38c12.5 12.5 12.5 32.75 0 45.25c-12.49 12.49-32.74 12.51-45.25 0l-128-128C-3.125 266.1-3.125 245.9 9.375 233.4z"/></svg>`,
chevronDown: `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>`,
chevronDown: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>`,
chevronRight: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right"><polyline points="9 18 15 12 9 6"></polyline></svg>`,
clapperBoard: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M326.1 160l127.4-127.4C451.7 32.39 449.9 32 448 32h-86.06l-128 128H326.1zM166.1 160l128-128H201.9l-128 128H166.1zM497.7 56.19L393.9 160H512V96C512 80.87 506.5 67.15 497.7 56.19zM134.1 32H64C28.65 32 0 60.65 0 96v64h6.062L134.1 32zM0 416c0 35.35 28.65 64 64 64h384c35.35 0 64-28.65 64-64V192H0V416z"/></svg>`,
film: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M463.1 32h-416C21.49 32-.0001 53.49-.0001 80v352c0 26.51 21.49 48 47.1 48h416c26.51 0 48-21.49 48-48v-352C511.1 53.49 490.5 32 463.1 32zM111.1 408c0 4.418-3.582 8-8 8H55.1c-4.418 0-8-3.582-8-8v-48c0-4.418 3.582-8 8-8h47.1c4.418 0 8 3.582 8 8L111.1 408zM111.1 280c0 4.418-3.582 8-8 8H55.1c-4.418 0-8-3.582-8-8v-48c0-4.418 3.582-8 8-8h47.1c4.418 0 8 3.582 8 8V280zM111.1 152c0 4.418-3.582 8-8 8H55.1c-4.418 0-8-3.582-8-8v-48c0-4.418 3.582-8 8-8h47.1c4.418 0 8 3.582 8 8L111.1 152zM351.1 400c0 8.836-7.164 16-16 16H175.1c-8.836 0-16-7.164-16-16v-96c0-8.838 7.164-16 16-16h160c8.836 0 16 7.162 16 16V400zM351.1 208c0 8.836-7.164 16-16 16H175.1c-8.836 0-16-7.164-16-16v-96c0-8.838 7.164-16 16-16h160c8.836 0 16 7.162 16 16V208zM463.1 408c0 4.418-3.582 8-8 8h-47.1c-4.418 0-7.1-3.582-7.1-8l0-48c0-4.418 3.582-8 8-8h47.1c4.418 0 8 3.582 8 8V408zM463.1 280c0 4.418-3.582 8-8 8h-47.1c-4.418 0-8-3.582-8-8v-48c0-4.418 3.582-8 8-8h47.1c4.418 0 8 3.582 8 8V280zM463.1 152c0 4.418-3.582 8-8 8h-47.1c-4.418 0-8-3.582-8-8l0-48c0-4.418 3.582-8 7.1-8h47.1c4.418 0 8 3.582 8 8V152z"/></svg>`,
dragon: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M18.43 255.8L192 224L100.8 292.6C90.67 302.8 97.8 320 112 320h222.7c-9.499-26.5-14.75-54.5-14.75-83.38V194.2L200.3 106.8C176.5 90.88 145 92.75 123.3 111.2l-117.5 116.4C-6.562 238 2.436 258 18.43 255.8zM575.2 289.9l-100.7-50.25c-16.25-8.125-26.5-24.75-26.5-43V160h63.99l28.12 22.62C546.1 188.6 554.2 192 562.7 192h30.1c11.1 0 23.12-6.875 28.5-17.75l14.37-28.62c5.374-10.87 4.25-23.75-2.999-33.5l-74.49-99.37C552.1 4.75 543.5 0 533.5 0H296C288.9 0 285.4 8.625 290.4 13.62L351.1 64L292.4 88.75c-5.874 3-5.874 11.37 0 14.37L351.1 128l-.0011 108.6c0 72 35.99 139.4 95.99 179.4c-195.6 6.75-344.4 41-434.1 60.88c-8.124 1.75-13.87 9-13.87 17.38C.0463 504 8.045 512 17.79 512h499.1c63.24 0 119.6-47.5 122.1-110.8C642.3 354 617.1 310.9 575.2 289.9zM489.1 66.25l45.74 11.38c-2.75 11-12.5 18.88-24.12 18.25C497.7 95.25 484.8 83.38 489.1 66.25z"/></svg>`,

View file

@ -1,10 +1,22 @@
export function Loading() {
export interface LoadingProps {
text?: string;
className?: string;
}
export function Loading(props: LoadingProps) {
return (
<div className="flex items-center h-12 justify-center">
<div className="w-2 mx-1 h-2 rounded-full animate-loading-pin bg-denim-300"></div>
<div className="w-2 mx-1 h-2 rounded-full [animation-delay:150ms] animate-loading-pin bg-denim-300"></div>
<div className="w-2 mx-1 h-2 rounded-full [animation-delay:300ms] animate-loading-pin bg-denim-300"></div>
<div className="w-2 mx-1 h-2 rounded-full [animation-delay:450ms] animate-loading-pin bg-denim-300"></div>
<div className={props.className}>
<div className="flex flex-col items-center justify-center py-12">
<div className="flex h-12 items-center justify-center">
<div className="animate-loading-pin bg-denim-300 mx-1 h-2 w-2 rounded-full"></div>
<div className="animate-loading-pin bg-denim-300 mx-1 h-2 w-2 rounded-full [animation-delay:150ms]"></div>
<div className="animate-loading-pin bg-denim-300 mx-1 h-2 w-2 rounded-full [animation-delay:300ms]"></div>
<div className="animate-loading-pin bg-denim-300 mx-1 h-2 w-2 rounded-full [animation-delay:450ms]"></div>
</div>
{props.text && props.text.length ? (
<p className="mt-3 max-w-xs text-sm opacity-75">{props.text}</p>
) : null}
</div>
</div>
);
}

View file

@ -10,9 +10,9 @@ interface SectionHeadingProps {
export function SectionHeading(props: SectionHeadingProps) {
return (
<div className="mt-12">
<p className="flex items-center uppercase font-bold text-denim-700 mb-3">
<p className="text-denim-700 mb-4 flex items-center font-bold uppercase">
{props.icon ? (
<span className="text-xl mr-2">
<span className="mr-2 text-xl">
<Icon icon={props.icon} />
</span>
) : null}

View file

@ -1,30 +1,90 @@
import { GetProviderFromId, MWMedia, MWMediaType } from "scrapers";
import { Link } from "react-router-dom";
import { Icon, Icons } from "components/Icon";
export interface MediaCardProps {
media: MWMedia;
watchedPercentage: Number;
linkable?: boolean;
}
function MediaCardContent({ media, watchedPercentage }: MediaCardProps) {
export interface MediaMetaProps {
content: string[];
}
function MediaMeta(props: MediaMetaProps) {
return (
<>
<p>{media.title} ({GetProviderFromId(media.providerId)?.displayName})</p>
<p>{watchedPercentage}% watched</p>
<hr/>
</>
)
<p className="text-denim-700 text-xs font-semibold">
{props.content.map((item, index) => (
<span key={item}>
{index !== 0 ? (
<span className="mx-[0.6em] text-[1em]">&#9679;</span>
) : null}
{item}
</span>
))}
</p>
);
}
function MediaCardContent({
media,
linkable,
watchedPercentage,
}: MediaCardProps) {
const provider = GetProviderFromId(media.providerId);
if (!provider) {
return null;
}
return (
<article
className={`bg-denim-300 group relative mb-4 flex overflow-hidden rounded py-4 px-5 ${
linkable ? "hover:bg-denim-400" : ""
}`}
>
{/* progress background */}
{watchedPercentage > 0 ? (
<div className="absolute top-0 left-0 right-0 bottom-0">
<div
className="bg-bink-300 relative h-full bg-opacity-30"
style={{
width: `${watchedPercentage}%`,
}}
>
<div className="from-bink-400 absolute right-0 top-0 bottom-0 ml-auto w-40 bg-gradient-to-l to-transparent opacity-40" />
</div>
</div>
) : null}
<div className="relative flex flex-1">
{/* card content */}
<div className="flex-1">
<h1 className="mb-1 font-bold text-white">{media.title}</h1>
<MediaMeta content={[provider.displayName, provider.type]} />
</div>
{/* hoverable chevron */}
<div
className={`flex translate-x-3 items-center justify-end text-xl text-white opacity-0 transition-[opacity,transform] ${
linkable ? "group-hover:translate-x-0 group-hover:opacity-100" : ""
}`}
>
<Icon icon={Icons.CHEVRON_RIGHT} />
</div>
</div>
</article>
);
}
export function MediaCard(props: MediaCardProps) {
const provider = GetProviderFromId(props.media.providerId);
let link = "movie"
if (provider?.type === MWMediaType.SERIES)
link = "series";
let link = "movie";
if (provider?.type === MWMediaType.SERIES) link = "series";
return (
<Link to={`/media/${link}`}>
<MediaCardContent {...props} />
</Link>
)
const content = <MediaCardContent {...props} />;
if (!props.linkable) return <span>{content}</span>;
return <Link to={`/media/${link}`}>{content}</Link>;
}

View file

@ -6,5 +6,5 @@ export interface WatchedMediaCardProps {
}
export function WatchedMediaCard(props: WatchedMediaCardProps) {
return <MediaCard watchedPercentage={42} media={props.media} />
return <MediaCard watchedPercentage={72} media={props.media} linkable />;
}

View file

@ -6,3 +6,11 @@ html,
body {
@apply bg-denim-100 text-denim-700 font-open-sans min-h-screen;
}
#root {
display: flex;
justify-content: flex-start;
align-items: flex-start;
min-height: 100vh;
width: 100%;
}

View file

@ -30,8 +30,12 @@ export function SearchView() {
setResults(results);
}
const isLoading = search.searchQuery !== "" && results.length === 0;
const hasResult = results.length > 0;
return (
<ThinContainer>
{/* input section */}
<div className="mt-36 space-y-16 text-center">
<div className="space-y-4">
<Tagline>Because watching legally is boring</Tagline>
@ -43,14 +47,20 @@ export function SearchView() {
placeholder="What movie do you want to watch?"
/>
</div>
{results.length > 0 ? (
{/* results */}
{hasResult ? (
<SectionHeading title="Search results" icon={Icons.SEARCH}>
{results.map((v) => (
<WatchedMediaCard media={v} />
))}
</SectionHeading>
) : null}
{search.searchQuery !== "" && results.length === 0 ? <Loading /> : null}
{/* Loading icon */}
{isLoading ? (
<Loading className="my-12" text="Fetching your favourite shows..." />
) : null}
</ThinContainer>
);
}