150 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			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,
 | |
| });
 |