105 lines
4.0 KiB
TypeScript
105 lines
4.0 KiB
TypeScript
import { Chip } from "components/Chip";
|
|
import { Img } from "components/Img";
|
|
import { InsetBox } from "components/Containers/InsetBox";
|
|
import { Markdawn } from "components/Markdown/Markdawn";
|
|
import { GetContentTextQuery, UploadImageFragment } from "graphql/generated";
|
|
import { prettyInlineTitle, prettySlug, slugify } from "helpers/formatters";
|
|
import { ImageQuality } from "helpers/img";
|
|
import { filterHasAttributes } from "helpers/asserts";
|
|
import { useAtomGetter } from "helpers/atoms";
|
|
import { atoms } from "contexts/atoms";
|
|
import { useFormat } from "hooks/useFormat";
|
|
|
|
/*
|
|
* ╭─────────────╮
|
|
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
|
*/
|
|
|
|
interface Props {
|
|
pre_title?: string | null | undefined;
|
|
title: string | null | undefined;
|
|
subtitle?: string | null | undefined;
|
|
description?: string | null | undefined;
|
|
type?: NonNullable<
|
|
NonNullable<GetContentTextQuery["contents"]>["data"][number]["attributes"]
|
|
>["type"];
|
|
categories?: NonNullable<
|
|
NonNullable<GetContentTextQuery["contents"]>["data"][number]["attributes"]
|
|
>["categories"];
|
|
thumbnail?: UploadImageFragment | null | undefined;
|
|
|
|
languageSwitcher?: JSX.Element;
|
|
}
|
|
|
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
|
|
|
export const ThumbnailHeader = ({
|
|
pre_title,
|
|
title,
|
|
subtitle,
|
|
thumbnail,
|
|
type,
|
|
categories,
|
|
description,
|
|
languageSwitcher,
|
|
}: Props): JSX.Element => {
|
|
const { format } = useFormat();
|
|
const { showLightBox } = useAtomGetter(atoms.lightBox);
|
|
|
|
return (
|
|
<>
|
|
<div className="mb-12 grid place-items-center gap-12">
|
|
<div className="drop-shadow-lg shadow-shade">
|
|
{thumbnail ? (
|
|
<Img
|
|
className="cursor-pointer rounded-xl"
|
|
src={thumbnail}
|
|
quality={ImageQuality.Medium}
|
|
onClick={() => showLightBox([thumbnail])}
|
|
/>
|
|
) : (
|
|
<div className="aspect-[4/3] w-96 rounded-xl bg-light" />
|
|
)}
|
|
</div>
|
|
<div
|
|
id={slugify(prettyInlineTitle(pre_title ?? "", title, subtitle ?? ""))}
|
|
className="grid place-items-center text-center">
|
|
<p className="text-2xl">{pre_title}</p>
|
|
<h1 className="text-3xl">{title}</h1>
|
|
<h2 className="text-2xl">{subtitle}</h2>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flew-wrap flex flex-row place-content-center gap-8">
|
|
{type?.data?.attributes && (
|
|
<div className="flex flex-col place-items-center gap-2">
|
|
<h3 className="text-xl">{format("type", { count: 1 })}</h3>
|
|
<div className="flex flex-row flex-wrap">
|
|
<Chip
|
|
text={
|
|
type.data.attributes.titles?.[0]?.title ?? prettySlug(type.data.attributes.slug)
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{categories && categories.data.length > 0 && (
|
|
<div className="flex flex-col place-items-center gap-2">
|
|
<h3 className="text-xl">{format("category", { count: categories.data.length })}</h3>
|
|
<div className="flex flex-row flex-wrap place-content-center gap-2">
|
|
{filterHasAttributes(categories.data, ["attributes", "id"] as const).map(
|
|
(category) => (
|
|
<Chip key={category.id} text={category.attributes.name} />
|
|
)
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
{languageSwitcher}
|
|
</div>
|
|
{description && <InsetBox className="mt-8">{<Markdawn text={description} />}</InsetBox>}
|
|
</>
|
|
);
|
|
};
|