search page styling

This commit is contained in:
Jelle van Snik 2022-02-13 18:49:03 +01:00
parent 01d18aa459
commit e75fcd3002
15 changed files with 378 additions and 29 deletions

View file

@ -0,0 +1,134 @@
import { ButtonControlProps, ButtonControl } from "./ButtonControl";
import { Icon, Icons } from "components/Icon";
import React, {
useRef,
Ref,
Dispatch,
SetStateAction,
MouseEventHandler,
KeyboardEvent,
SyntheticEvent,
useEffect,
useState,
} from "react";
import { Backdrop, useBackdrop } from "components/layout/Backdrop";
export interface DropdownButtonProps extends ButtonControlProps {
icon: Icons;
open: boolean;
setOpen: Dispatch<SetStateAction<boolean>>;
selectedItem: string;
setSelectedItem: Dispatch<SetStateAction<string>>;
options: Array<OptionItem>;
}
export interface OptionProps {
option: OptionItem;
onClick: MouseEventHandler<HTMLDivElement>;
tabIndex?: number;
}
export interface OptionItem {
id: string;
name: string;
icon: Icons;
}
function Option({ option, onClick, tabIndex }: OptionProps) {
return (
<div
className="text-denim-700 h-10 px-4 py-2 text-left cursor-pointer flex items-center space-x-2 hover:text-white transition-colors"
onClick={onClick}
tabIndex={tabIndex}
>
<Icon icon={option.icon} />
<input type="radio" className="hidden" id={option.id} />
<label htmlFor={option.id} className="cursor-pointer ">
<div className="item">{option.name}</div>
</label>
</div>
);
}
export const DropdownButton = React.forwardRef<
HTMLDivElement,
DropdownButtonProps
>((props, ref) => {
const [setBackdrop, backdropProps, highlightedProps] = useBackdrop();
const [delayedSelectedId, setDelayedSelectedId] = useState(
props.selectedItem
);
useEffect(() => {
let id: NodeJS.Timeout;
if (props.open) {
setDelayedSelectedId(props.selectedItem);
} else {
id = setTimeout(() => {
setDelayedSelectedId(props.selectedItem);
}, 200);
}
return () => {
if (id) clearTimeout(id);
};
}, [props.open]);
const selectedItem: OptionItem = props.options.find(
(opt) => opt.id === props.selectedItem
) || { id: "movie", name: "movie", icon: Icons.ARROW_LEFT };
useEffect(() => {
setBackdrop(props.open);
}, [props.open]);
const onOptionClick = (e: SyntheticEvent, option: OptionItem) => {
e.stopPropagation();
props.setSelectedItem(option.id);
props.setOpen(false);
};
return (
<div className="w-full sm:w-auto min-w-[140px]">
<div
ref={ref}
className="relative w-full sm:w-auto"
{...highlightedProps}
>
<ButtonControl
{...props}
className="flex items-center justify-center sm:justify-left px-4 py-2 space-x-2 bg-bink-200 relative z-20 hover:bg-bink-300 text-white h-10 rounded-[20px] w-full"
>
<Icon icon={selectedItem.icon} />
<span className="flex-1">{selectedItem.name}</span>
<Icon
icon={Icons.CHEVRON_DOWN}
className={`transition-transform ${props.open ? "rotate-180" : ""}`}
/>
</ButtonControl>
<div
className={`absolute pt-[40px] top-0 duration-200 transition-all w-full rounded-[20px] z-10 bg-denim-300 ${
props.open
? "opacity-100 max-h-60 block"
: "opacity-0 max-h-0 invisible"
}`}
>
{props.options
.filter((opt) => opt.id != delayedSelectedId)
.map((opt) => (
<Option
option={opt}
key={opt.id}
onClick={(e) => onOptionClick(e, opt)}
tabIndex={
props.open ? 0 : undefined
} /*onKeyPress={active ? handleOptionKeyPress(opt, i) : undefined}*/
/>
))}
</div>
</div>
<Backdrop onClick={() => props.setOpen(false)} {...backdropProps} />
</div>
);
});

View file

@ -9,7 +9,7 @@ export function IconButton(props: IconButtonProps) {
return ( return (
<ButtonControl <ButtonControl
{...props} {...props}
className="flex items-center px-4 py-2 space-x-2 bg-pink-900 text-white rounded-full" className="flex items-center px-4 py-2 space-x-2 bg-bink-200 hover:bg-bink-300 text-white rounded-full"
> >
<Icon icon={props.icon} /> <Icon icon={props.icon} />
<span>{props.children}</span> <span>{props.children}</span>

View file

@ -4,10 +4,15 @@ export enum Icons {
CLOCK = "clock", CLOCK = "clock",
EYE_SLASH = "eyeSlash", EYE_SLASH = "eyeSlash",
ARROW_LEFT = "arrowLeft", ARROW_LEFT = "arrowLeft",
CHEVRON_DOWN = "chevronDown",
CLAPPER_BOARD = "clapperBoard",
FILM = "film",
DRAGON = "dragon",
} }
export interface IconProps { export interface IconProps {
icon: Icons; icon: Icons;
className?: string;
} }
const iconList = { const iconList = {
@ -16,8 +21,17 @@ 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>`, 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>`, 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>`, 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>`,
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>`,
};
export function Icon(props: IconProps) { export function Icon(props: IconProps) {
return <span dangerouslySetInnerHTML={{ __html: iconList[props.icon] }} />; return (
<span
dangerouslySetInnerHTML={{ __html: iconList[props.icon] }}
className={props.className}
/>
);
} }

View file

@ -1,10 +1,12 @@
import { IconButton } from "./Buttons/IconButton"; import { DropdownButton } from "./Buttons/DropdownButton";
import { Icons } from "./Icon"; import { Icons } from "./Icon";
import { import {
TextInputControl, TextInputControl,
TextInputControlPropsNoLabel, TextInputControlPropsNoLabel,
} from "./TextInputs/TextInputControl"; } from "./TextInputs/TextInputControl";
import { useState, useRef, useEffect } from "react";
export interface SearchBarProps extends TextInputControlPropsNoLabel { export interface SearchBarProps extends TextInputControlPropsNoLabel {
buttonText?: string; buttonText?: string;
onClick?: () => void; onClick?: () => void;
@ -12,17 +14,61 @@ export interface SearchBarProps extends TextInputControlPropsNoLabel {
} }
export function SearchBarInput(props: SearchBarProps) { export function SearchBarInput(props: SearchBarProps) {
const [dropdownOpen, setDropdownOpen] = useState(false);
const [dropdownSelected, setDropdownSelected] = useState("movie");
const dropdownRef = useRef<any>();
const handleClick = (e: MouseEvent) => {
if (dropdownRef.current?.contains(e.target as Node)) {
// inside click
return;
}
// outside click
closeDropdown();
};
const closeDropdown = () => {
setDropdownOpen(false);
};
return ( return (
<div className="flex items-center space-x-4 pl-8 pr-2 py-2 bg-dink-500 rounded-full"> <div className="flex flex-col sm:flex-row items-center gap-4 px-4 py-4 sm:pl-8 sm:pr-2 sm:py-2 bg-denim-300 rounded-[28px] hover:bg-denim-400 focus-within:bg-denim-400 transition-colors">
<TextInputControl <TextInputControl
onChange={props.onChange} onChange={props.onChange}
value={props.value} value={props.value}
className="placeholder-dink-150 bg-transparent flex-1 focus:outline-none text-white" className="placeholder-denim-700 w-full bg-transparent flex-1 focus:outline-none text-white"
placeholder={props.placeholder} placeholder={props.placeholder}
/> />
<IconButton icon={Icons.SEARCH} onClick={props.onClick}>
<DropdownButton
icon={Icons.SEARCH}
open={dropdownOpen}
setOpen={setDropdownOpen}
selectedItem={dropdownSelected}
setSelectedItem={setDropdownSelected}
options={[
{
id: "movie",
name: "Movie",
icon: Icons.FILM,
},
{
id: "series",
name: "Series",
icon: Icons.CLAPPER_BOARD,
},
{
id: "anime",
name: "Anime",
icon: Icons.DRAGON,
},
]}
onClick={() => setDropdownOpen((old) => !old)}
ref={dropdownRef}
>
{props.buttonText || "Search"} {props.buttonText || "Search"}
</IconButton> </DropdownButton>
</div> </div>
); );
} }

View file

@ -0,0 +1,7 @@
export interface TaglineProps {
children?: React.ReactNode;
}
export function Tagline(props: TaglineProps) {
return <p className="font-bold text-bink-600">{props.children}</p>;
}

View file

@ -0,0 +1,7 @@
export interface TitleProps {
children?: React.ReactNode;
}
export function Title(props: TitleProps) {
return <h1 className="text-4xl font-bold text-white">{props.children}</h1>;
}

View file

@ -0,0 +1,66 @@
import { useFade } from "hooks/useFade";
import { useEffect, useState } from "react";
interface BackdropProps {
onClick?: (e: MouseEvent) => void;
onBackdropHide?: () => void;
active?: boolean;
}
export function useBackdrop(): [
(state: boolean) => void,
BackdropProps,
{ style: any }
] {
const [backdrop, setBackdropState] = useState(false);
const [isHighlighted, setisHighlighted] = useState(false);
const setBackdrop = (state: boolean) => {
setBackdropState(state);
if (state) setisHighlighted(true);
};
const backdropProps: BackdropProps = {
active: backdrop,
onBackdropHide() {
setisHighlighted(false);
},
};
const highlightedProps = {
style: isHighlighted
? {
zIndex: "1000",
position: "relative",
}
: {},
};
return [setBackdrop, backdropProps, highlightedProps];
}
export function Backdrop(props: BackdropProps) {
const clickEvent = props.onClick || ((e: MouseEvent) => {});
const animationEvent = props.onBackdropHide || (() => {});
const [isVisible, setVisible, fadeProps] = useFade();
useEffect(() => {
setVisible(!!props.active);
}, [props.active]);
useEffect(() => {
if (!isVisible) animationEvent();
}, [isVisible]);
if (!isVisible) return null;
return (
<div
className={`fixed h-screen z-[999] top-0 left-0 right-0 bg-black bg-opacity-50 transition-opacity opacity-100 ${
!isVisible ? "opacity-0" : ""
}`}
{...fadeProps}
onClick={(e) => clickEvent(e.nativeEvent)}
></div>
);
}

View file

@ -0,0 +1,10 @@
export function Loading() {
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>
);
}

View file

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

View file

@ -1,12 +1,16 @@
import { ReactNode } from "react"; import { ReactNode } from "react";
interface ThinContainerProps { interface ThinContainerProps {
classNames?: string, classNames?: string;
children?: ReactNode, children?: ReactNode;
} }
export function ThinContainer(props: ThinContainerProps) { export function ThinContainer(props: ThinContainerProps) {
return (<div className={`max-w-[600px] mx-auto ${props.classNames || ""}`}> return (
{props.children} <div
</div>) className={`max-w-[600px] mx-auto px-2 sm:px-0 ${props.classNames || ""}`}
>
{props.children}
</div>
);
} }

View file

17
src/hooks/useFade.css Normal file
View file

@ -0,0 +1,17 @@
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}

27
src/hooks/useFade.ts Normal file
View file

@ -0,0 +1,27 @@
import React, { useEffect, useState } from "react";
import './useFade.css'
export const useFade = (initial: boolean = false): [boolean, React.Dispatch<React.SetStateAction<boolean>>, any] => {
const [show, setShow] = useState<boolean>(initial);
const [isVisible, setVisible] = useState<boolean>(show);
// Update visibility when show changes
useEffect(() => {
if (show) setVisible(true);
}, [show]);
// When the animation finishes, set visibility to false
const onAnimationEnd = () => {
if (!show) setVisible(false);
};
const style = { animation: `${show ? "fadeIn" : "fadeOut"} .3s` };
// These props go on the fading DOM element
const fadeProps = {
style,
onAnimationEnd
};
return [isVisible, setShow, fadeProps];
};

View file

@ -5,6 +5,9 @@ import { useState } from "react";
import { ThinContainer } from "components/layout/ThinContainer"; import { ThinContainer } from "components/layout/ThinContainer";
import { SectionHeading } from "components/layout/SectionHeading"; import { SectionHeading } from "components/layout/SectionHeading";
import { Icons } from "components/Icon"; import { Icons } from "components/Icon";
import { Loading } from "components/layout/Loading";
import { Tagline } from "components/Text/Tagline";
import { Title } from "components/Text/Title";
export function SearchView() { export function SearchView() {
const [results, setResults] = useState<MWMedia[]>([]); const [results, setResults] = useState<MWMedia[]>([]);
@ -22,12 +25,8 @@ export function SearchView() {
<ThinContainer> <ThinContainer>
<div className="mt-36 text-center space-y-16"> <div className="mt-36 text-center space-y-16">
<div className="space-y-4"> <div className="space-y-4">
<p className="font-bold text-bink"> <Tagline>Because watching legally is boring</Tagline>
Because watching legally is boring <Title>What movie do you want to watch?</Title>
</p>
<h1 className="text-4xl font-bold">
What movie do you want to watch?
</h1>
</div> </div>
<SearchBarInput <SearchBarInput
onChange={setSearch} onChange={setSearch}
@ -41,6 +40,7 @@ export function SearchView() {
<WatchedMediaCard media={v} /> <WatchedMediaCard media={v} />
))} ))}
</SectionHeading> </SectionHeading>
<Loading />
</ThinContainer> </ThinContainer>
); );
} }

View file

@ -2,20 +2,37 @@ module.exports = {
content: ["./src/**/*.{js,jsx,ts,tsx}"], content: ["./src/**/*.{js,jsx,ts,tsx}"],
theme: { theme: {
extend: { extend: {
/* colors */
colors: { colors: {
"dink-900": "#131119", "bink-100": "#432449",
"dink-500": "#252037", "bink-200": "#412B57",
"dink-400": "#231F34", "bink-300": "#533670",
"dink-300": "#70688B", "bink-400": "#714C97",
"dink-200": "#3A364D", "bink-500": "#8D66B5",
"dink-150": "#8F87AB", "bink-600": "#A87FD1",
"dink-100": "#393447", "bink-700": "#CD97D6",
bink: "#D588E3", "denim-100": "#131119",
"pink-900": "#412B57", "denim-200": "#1E1A29",
"denim-300": "#282336",
"denim-400": "#322D43",
"denim-500": "#433D55",
"denim-600": "#5A5370",
"denim-700": "#817998",
}, },
/* fonts */
fontFamily: { fontFamily: {
"open-sans": "'Open Sans'", "open-sans": "'Open Sans'",
}, },
/* animations */
keyframes: {
"loading-pin": {
"0%, 40%, 100%": { height: "0.5em", "background-color": "#282336" },
"20%": { height: "1em", "background-color": "white" },
},
},
animation: { "loading-pin": "loading-pin 1.8s ease-in-out infinite" },
}, },
}, },
plugins: [], plugins: [],