205 lines
6.1 KiB
TypeScript
205 lines
6.1 KiB
TypeScript
import React, { ReactElement } from "react";
|
|
import PropTypes from "prop-types";
|
|
import { isEmpty, isNil } from "lodash";
|
|
|
|
interface ICardProps {
|
|
orientation: string;
|
|
imageUrl?: string;
|
|
hasDetails?: boolean;
|
|
title?: React.ReactNode;
|
|
children?: React.ReactNode;
|
|
borderColorClass?: string;
|
|
backgroundColor?: string;
|
|
cardState?: "wanted" | "delete" | "scraped" | "uncompressed" | "imported" | "missing";
|
|
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
|
cardContainerStyle?: React.CSSProperties;
|
|
imageStyle?: React.CSSProperties;
|
|
}
|
|
|
|
const getCardStateClass = (cardState?: string): string => {
|
|
switch (cardState) {
|
|
case "wanted":
|
|
return "bg-card-wanted";
|
|
case "delete":
|
|
return "bg-card-delete";
|
|
case "scraped":
|
|
return "bg-card-scraped";
|
|
case "uncompressed":
|
|
return "bg-card-uncompressed";
|
|
case "imported":
|
|
return "bg-card-imported";
|
|
case "missing":
|
|
return "bg-card-missing";
|
|
default:
|
|
return "";
|
|
}
|
|
};
|
|
|
|
const renderCard = (props: ICardProps): ReactElement => {
|
|
switch (props.orientation) {
|
|
case "horizontal":
|
|
return (
|
|
<div className="card-container">
|
|
<div className="card generic-card">
|
|
<div className="is-horizontal">
|
|
<div className="card-image">
|
|
<img
|
|
style={props.imageStyle}
|
|
src={props.imageUrl}
|
|
alt="Placeholder image"
|
|
className="cropped-image"
|
|
/>
|
|
</div>
|
|
{props.hasDetails && (
|
|
<div className="card-content">{props.children}</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
case "vertical":
|
|
return (
|
|
<div onClick={props.onClick}>
|
|
<div className="generic-card" style={props.cardContainerStyle}>
|
|
<div
|
|
className={
|
|
!isNil(props.borderColorClass)
|
|
? `${props.borderColorClass}`
|
|
: ""
|
|
}
|
|
>
|
|
<div
|
|
className={
|
|
props.hasDetails
|
|
? "partial-rounded-card-image"
|
|
: "rounded-card-image"
|
|
}
|
|
>
|
|
<figure>
|
|
<img
|
|
src={props.imageUrl}
|
|
style={props.imageStyle}
|
|
alt="Placeholder image"
|
|
/>
|
|
</figure>
|
|
</div>
|
|
{props.hasDetails && (
|
|
<div
|
|
className="card-content"
|
|
style={{ backgroundColor: props.backgroundColor }}
|
|
>
|
|
{!isNil(props.title) ? (
|
|
<div className="card-title is-size-8 is-family-secondary">
|
|
{props.title}
|
|
</div>
|
|
) : null}
|
|
{props.children}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
case "vertical-2":
|
|
return (
|
|
<div className={`block rounded-md max-w-64 h-fit shadow-md shadow-white-400 ${getCardStateClass(props.cardState) || "bg-gray-200 dark:bg-slate-500"}`}>
|
|
<div className="relative">
|
|
{props.imageUrl ? (
|
|
<img
|
|
alt="Home"
|
|
src={props.imageUrl}
|
|
className="rounded-t-md object-cover"
|
|
/>
|
|
) : (
|
|
<div className="rounded-t-md h-48 bg-gray-100 dark:bg-slate-600" />
|
|
)}
|
|
{props.cardState === "missing" && (
|
|
<div className="absolute inset-0 flex items-center justify-center rounded-t-md bg-card-missing/70">
|
|
<i className="icon-[solar--file-corrupted-outline] w-16 h-16 text-red-500" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{props.title ? (
|
|
<div className="px-3 pt-3 mb-2">
|
|
<dd className="text-sm text-slate-500 dark:text-black">
|
|
{props.title}
|
|
</dd>
|
|
</div>
|
|
) : null}
|
|
|
|
{props.hasDetails ? (
|
|
<div className="px-2">
|
|
<>{props.children}</>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
);
|
|
|
|
case "horizontal-small":
|
|
return (
|
|
<>
|
|
<div className={`flex flex-row justify-start align-top gap-3 h-fit rounded-md shadow-md shadow-white-400 ${getCardStateClass(props.cardState) || "bg-slate-200"}`}>
|
|
{/* thumbnail */}
|
|
<div className="rounded-md overflow-hidden">
|
|
<img src={props.imageUrl} className="object-cover h-20 w-20" />
|
|
</div>
|
|
{/* details */}
|
|
<div className="w-fit h-fit pl-1 pr-2 py-1">
|
|
<p className="text-sm">{props.title}</p>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
|
|
case "horizontal-medium":
|
|
return (
|
|
<>
|
|
<div className={`flex flex-row items-center align-top gap-3 h-fit p-2 rounded-md shadow-md shadow-white-400 ${getCardStateClass(props.cardState) || "bg-slate-200"}`}>
|
|
{/* thumbnail */}
|
|
<div className="rounded-md overflow-hidden">
|
|
<img src={props.imageUrl} />
|
|
</div>
|
|
{/* details */}
|
|
<div className="pl-1 pr-2 py-1">
|
|
<p className="text-sm">{props.title}</p>
|
|
{props.hasDetails && <>{props.children}</>}
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
|
|
case "cover-only":
|
|
return (
|
|
<>
|
|
{/* thumbnail */}
|
|
<div className="rounded-lg shadow-lg overflow-hidden w-fit h-fit">
|
|
<img src={props.imageUrl} />
|
|
</div>
|
|
</>
|
|
);
|
|
case "card-with-info-panel":
|
|
return (
|
|
<>
|
|
<div className="flex flex-row">
|
|
{/* thumbnail */}
|
|
<div className="rounded-md overflow-hidden w-fit h-fit">
|
|
<img src={props.imageUrl} />
|
|
</div>
|
|
{/* myata-dyata */}
|
|
</div>
|
|
</>
|
|
);
|
|
|
|
default:
|
|
return <></>;
|
|
}
|
|
};
|
|
|
|
export const Card = React.memo(
|
|
(props: ICardProps): ReactElement => renderCard(props),
|
|
);
|
|
|
|
export default Card;
|