memorium/components/PageHero.tsx

150 lines
3.5 KiB
TypeScript

import { withSubComponents } from "@components/helpers/withSubComponents.ts";
import Image from "@components/Image.tsx";
import { IconExternalLink } from "@components/icons.tsx";
import { type ComponentChildren, createContext } from "preact";
import { IconArrowNarrowLeft } from "@components/icons.tsx";
import { IconEdit } from "@components/icons.tsx";
import { useContext } from "preact/hooks";
const HeroContext = createContext<{ image?: string; thumbnail?: string }>({
image: undefined,
thumbnail: undefined,
});
function Wrapper(
{ children, image, thumbnail }: {
children: ComponentChildren;
image?: string;
thumbnail?: string;
},
) {
return (
<div
class={`flex justify-between flex-col relative w-full ${
image ? "min-h-[400px]" : "min-h-[200px]"
} rounded-3xl overflow-hidden`}
>
<HeroContext.Provider value={{ image }}>
{image &&
(
<Image
fill
src={image}
thumbnail={thumbnail}
alt="Recipe Banner"
// style={{ objectPosition: "0% 25%" }}
class="absolute object-cover w-full h-full -z-10"
/>
)}
{children}
</HeroContext.Provider>
</div>
);
}
function Title(
{ children, link }: { children: ComponentChildren; link?: string },
) {
const ctx = useContext(HeroContext);
const OuterTag = link ? "a" : "div";
return (
<OuterTag
href={link}
class={`${
ctx.image ? "noisy-gradient" : ""
} after:opacity-90 flex gap-4 items-center ${ctx.image ? "pt-12" : ""}`}
>
<h2
class="flex gap-2 items-center text-4xl font-bold z-10"
style={{ color: ctx.image ? "#1F1F1F" : "white" }}
>
{children}
{link &&
<IconExternalLink />}
</h2>
</OuterTag>
);
}
function BackLink({ href }: { href: string }) {
return (
<a
class="px-4 py-2 bg-gray-300 text-gray-800 rounded-lg flex gap-1 items-center"
href={href}
>
<IconArrowNarrowLeft class="w-5 h-5" /> Back
</a>
);
}
function EditLink({ href }: { href: string }) {
const ctx = useContext(HeroContext);
return (
<a
class={`px-4 py-2 ${
ctx.image ? "bg-gray-300 text-gray-800" : "text-gray-200"
} rounded-lg flex gap-1 items-center`}
href={href}
>
<IconEdit class="w-5 h-5" />
</a>
);
}
function Header({ children }: { children: ComponentChildren }) {
return (
<div class="flex justify-between mx-8 mt-8">
{children}
</div>
);
}
function Subline(
{ entries, children }: {
children?: ComponentChildren;
entries: (string | { href: string; title: string })[];
},
) {
const ctx = useContext(HeroContext);
return (
<div
class={`relative flex items-center z-10 flex gap-5 font-sm text-light mt-3`}
style={{ color: ctx.image ? "#1F1F1F" : "white" }}
>
{children}
{entries.filter((s) =>
s && (typeof s === "string" ? s?.length > 1 : true)
).map((s) => {
if (typeof s === "string") {
return <span>{s}</span>;
} else {
return <a href={s.href}>{s.title}</a>;
}
})}
</div>
);
}
function Footer({ children }: { children: ComponentChildren }) {
const ctx = useContext(HeroContext);
return (
<div
class={`relative inset-x-0 py-4 px-8 ${ctx.image ? "py-8" : ""} `}
>
{children}
</div>
);
}
export default withSubComponents(Wrapper, {
EditLink,
BackLink,
Footer,
Subline,
Header,
Title,
});