diff --git a/components/Button.tsx b/components/Button.tsx index f1b80a0..1ef05a4 100644 --- a/components/Button.tsx +++ b/components/Button.tsx @@ -6,7 +6,7 @@ export function Button(props: JSX.HTMLAttributes) { +
+

{props.count}

- +
); } diff --git a/islands/IngredientsList.tsx b/islands/IngredientsList.tsx new file mode 100644 index 0000000..7488468 --- /dev/null +++ b/islands/IngredientsList.tsx @@ -0,0 +1,78 @@ +import { Signal } from "@preact/signals"; +import type { + Ingredient, + IngredientGroup, + Ingredients, +} from "../lib/recipes.ts"; +import { FunctionalComponent } from "preact"; + +type IngredientsProps = { + ingredients: Ingredients; +}; + +const Ingredient = ( + { ingredient, amount, key = "", portion = 1 }: { + ingredient: Ingredient; + amount: Signal; + key?: string | number; + portion?: number; + }, +) => { + const { type, amount: _amount, unit } = ingredient; + + const finalAmount = (typeof _amount === "number" && amount) + ? (_amount / portion) * (amount?.value || 1) + : ""; + + return ( + + + {finalAmount + (typeof unit === "string" ? unit : "")} + + {type} + + ); +}; + +export const IngredientsList: FunctionalComponent< + { ingredients: Ingredients; amount: Signal; portion?: number } +> = ( + { ingredients, amount, portion }, +) => { + return ( + + + {ingredients.map((item, index) => { + if ("name" in item) { + // Render IngredientGroup + const { name, ingredients: groupIngredients } = + item as IngredientGroup; + + return ( + <> + + + + {groupIngredients.map((item, index) => { + // Render Ingredient + return ( + + ); + })} + + ); + } else { + return ( + + ); + } + })} + +
{name}
+ ); +}; diff --git a/lib/documents.ts b/lib/documents.ts index c74c2ca..d7be8a1 100644 --- a/lib/documents.ts +++ b/lib/documents.ts @@ -1,6 +1,9 @@ import { unified } from "npm:unified"; import remarkParse from "npm:remark-parse"; import remarkFrontmatter from "https://esm.sh/remark-frontmatter@4"; +import remarkRehype from "https://esm.sh/remark-rehype"; +import rehypeSanitize from "https://esm.sh/rehype-sanitize"; +import rehypeStringify from "https://esm.sh/rehype-stringify"; import { parse } from "https://deno.land/std@0.194.0/yaml/mod.ts"; import * as cache from "@lib/cache/documents.ts"; @@ -53,6 +56,17 @@ export function parseDocument(doc: string) { .parse(doc); } +export function renderMarkdown(doc: string) { + const out = unified() + .use(remarkParse) + .use(remarkRehype) + .use(rehypeSanitize) + .use(rehypeStringify) + .processSync(doc); + + return String(out); +} + export type ParsedDocument = ReturnType; export type DocumentChild = ParsedDocument["children"][number]; diff --git a/lib/recipes.ts b/lib/recipes.ts index 1a87d03..bbcaa55 100644 --- a/lib/recipes.ts +++ b/lib/recipes.ts @@ -4,6 +4,7 @@ import { getTextOfRange, parseDocument, parseFrontmatter, + renderMarkdown, } from "@lib/documents.ts"; import { parseIngredient } from "npm:parse-ingredient"; @@ -163,8 +164,8 @@ export function parseRecipe(original: string, id: string): Recipe { id, meta, name, - description, + description: description ? renderMarkdown(description) : "", ingredients, - preparation, + preparation: preparation ? renderMarkdown(preparation) : "", }; } diff --git a/routes/api/images/index.ts b/routes/api/images/index.ts index 50eddd5..620af89 100644 --- a/routes/api/images/index.ts +++ b/routes/api/images/index.ts @@ -10,6 +10,7 @@ import * as cache from "@lib/cache/image.ts"; await initializeImageMagick(); async function getRemoteImage(image: string) { + console.log("[api/image] fetching", { image }); const sourceRes = await fetch(image); if (!sourceRes.ok) { return "Error retrieving image from URL."; @@ -131,8 +132,6 @@ export const handler = async ( mediaType: remoteImage.mediaType, }); - console.log("[api/image] stored image in cache"); - return new Response(modifiedImage, { headers: { "Content-Type": remoteImage.mediaType, diff --git a/routes/recipes/[name].tsx b/routes/recipes/[name].tsx index 6b89c3c..70e2bee 100644 --- a/routes/recipes/[name].tsx +++ b/routes/recipes/[name].tsx @@ -1,9 +1,11 @@ import { Handlers, PageProps } from "$fresh/server.ts"; -import { IngredientsList } from "@components/IngredientsList.tsx"; +import { IngredientsList } from "@islands/IngredientsList.tsx"; import { RecipeHero } from "@components/RecipeHero.tsx"; import { MainLayout } from "@components/layouts/main.tsx"; import { Recipe } from "@lib/recipes.ts"; import { getRecipe } from "../api/recipes/[name].ts"; +import Counter from "@islands/Counter.tsx"; +import { useSignal } from "@preact/signals"; export const handler: Handlers = { async GET(_, ctx) { @@ -13,13 +15,31 @@ export const handler: Handlers = { }; export default function Greet(props: PageProps) { + const recipe = props.data; + + const portion = recipe.meta?.portion; + const amount = useSignal(portion || 1); + return ( - +
-

Ingredients

- +
+

Ingredients

+ {portion && } +
+

Preparation

+
+          {recipe.preparation}
+        
); diff --git a/static/global.css b/static/global.css index 67b393b..42739e6 100644 --- a/static/global.css +++ b/static/global.css @@ -2,6 +2,9 @@ body { background: #1F1F1F; padding: 0px 20px; } +pre { + font-family:Work Sans; +} .noisy-gradient::after { content: "";