fix: make recipe ingredinets interactive

This commit is contained in:
Max Richter
2025-11-02 20:01:01 +01:00
parent d4a7763b15
commit 098da12ac4
10 changed files with 29 additions and 23 deletions

View File

@@ -1,5 +1,5 @@
import { asset } from "$fresh/runtime.ts"; import { asset } from "$fresh/runtime.ts";
import * as CSS from "https://esm.sh/csstype@3.1.2"; import * as CSS from "csstype";
interface ResponsiveAttributes { interface ResponsiveAttributes {
srcset: string; srcset: string;

View File

@@ -26,11 +26,14 @@
"@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.5.1", "@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.5.1",
"@std/http": "jsr:@std/http@^1.0.12", "@std/http": "jsr:@std/http@^1.0.12",
"@std/yaml": "jsr:@std/yaml@^1.0.5", "@std/yaml": "jsr:@std/yaml@^1.0.5",
"csstype": "npm:csstype@^3.1.3",
"defuddle": "npm:defuddle@^0.6.6", "defuddle": "npm:defuddle@^0.6.6",
"drizzle-kit": "npm:drizzle-kit@^0.30.1", "drizzle-kit": "npm:drizzle-kit@^0.30.1",
"drizzle-orm": "npm:drizzle-orm@^0.38.3", "drizzle-orm": "npm:drizzle-orm@^0.38.3",
"fuzzysort": "npm:fuzzysort@^3.1.0", "fuzzysort": "npm:fuzzysort@^3.1.0",
"jsdom": "npm:jsdom@^24.1.3", "jsdom": "npm:jsdom@^24.1.3",
"moviedb-promise": "npm:moviedb-promise@^4.0.7",
"parse-ingredient": "npm:parse-ingredient@^1.3.1",
"playwright": "npm:playwright@^1.49.1", "playwright": "npm:playwright@^1.49.1",
"playwright-extra": "npm:playwright-extra@^4.3.6", "playwright-extra": "npm:playwright-extra@^4.3.6",
"preact": "https://esm.sh/preact@10.22.0", "preact": "https://esm.sh/preact@10.22.0",
@@ -42,6 +45,7 @@
"tailwindcss/": "npm:/tailwindcss@^3.4.17/", "tailwindcss/": "npm:/tailwindcss@^3.4.17/",
"tailwindcss/plugin": "npm:/tailwindcss@^3.4.17/plugin.js", "tailwindcss/plugin": "npm:/tailwindcss@^3.4.17/plugin.js",
"camelcase-css": "npm:camelcase-css", "camelcase-css": "npm:camelcase-css",
"thumbhash": "npm:thumbhash@^0.1.1",
"tsx": "npm:tsx@^4.19.2", "tsx": "npm:tsx@^4.19.2",
"yaml": "https://deno.land/std@0.197.0/yaml/mod.ts", "yaml": "https://deno.land/std@0.197.0/yaml/mod.ts",
"zod": "npm:zod@^3.24.1", "zod": "npm:zod@^3.24.1",

View File

@@ -43,13 +43,13 @@ const Ingredient = (
return ( return (
<tr key={key}> <tr key={key}>
<td class="pr-4 py-2"> <td class="pr-4 py-1">
{formatAmount(finalAmount || 0)} {formatAmount(finalAmount || 0)}
<span class="ml-0.5 opacity-50"> <span class="ml-0.5 opacity-50">
{formatUnit(unit, finalAmount || 0)} {formatUnit(unit, finalAmount || 0)}
</span> </span>
</td> </td>
<td class="px-4 py-2">{name}</td> <td class="px-4 py-1">{name}</td>
</tr> </tr>
); );
}; };
@@ -66,14 +66,10 @@ export const IngredientsList: FunctionalComponent<
return ( return (
<table class="w-full border-collapse table-auto"> <table class="w-full border-collapse table-auto">
<tbody> <tbody>
{ingredients.filter((s) => !!s?.length).map((item) => { {ingredients.map((item) => {
return ( return (
<div dangerouslySetInnerHTML={{ __html: renderMarkdown(item) }}> <Ingredient ingredient={item} amount={amount} portion={portion} />
</div>
); );
// return (
// <Ingredient ingredient={item} amount={amount} portion={portion} />
// );
})} })}
</tbody> </tbody>
</table> </table>

View File

@@ -69,7 +69,6 @@ export const SearchResultItem = (
) => { ) => {
const resourceType = resources[item?.content._type]; const resourceType = resources[item?.content._type];
const href = item?.path.replace("/resources", "").replace(/\.md$/, ""); const href = item?.path.replace("/resources", "").replace(/\.md$/, "");
console.log({ item, href });
return ( return (
<a <a
href={href} href={href}

View File

@@ -1,7 +1,7 @@
import { import {
parseIngredient, parseIngredient,
unitsOfMeasure as _unitsOfMeasure, unitsOfMeasure as _unitsOfMeasure,
} from "https://esm.sh/parse-ingredient@1.2.1"; } from "npm:parse-ingredient";
import { Ingredient, IngredientGroup } from "@lib/recipeSchema.ts"; import { Ingredient, IngredientGroup } from "@lib/recipeSchema.ts";
import { removeMarkdownFormatting } from "@lib/string.ts"; import { removeMarkdownFormatting } from "@lib/string.ts";

View File

@@ -1,4 +1,4 @@
import * as thumbhash from "https://esm.sh/thumbhash@0.1.1"; import * as thumbhash from "thumbhash";
export function generateThumbhash(buffer: Uint8Array, w: number, h: number) { export function generateThumbhash(buffer: Uint8Array, w: number, h: number) {
const hash = thumbhash.rgbaToThumbHash(w, h, buffer); const hash = thumbhash.rgbaToThumbHash(w, h, buffer);

View File

@@ -5,7 +5,7 @@ import {
MovieResultsResponse, MovieResultsResponse,
ShowResponse, ShowResponse,
TvResultsResponse, TvResultsResponse,
} from "https://esm.sh/moviedb-promise@3.4.1"; } from "moviedb-promise";
import { createCache } from "@lib/cache.ts"; import { createCache } from "@lib/cache.ts";
const moviedb = new MovieDb(Deno.env.get("TMDB_API_KEY") || ""); const moviedb = new MovieDb(Deno.env.get("TMDB_API_KEY") || "");

View File

@@ -24,10 +24,10 @@ export default async function Greet(
return ctx.renderNotFound(); return ctx.renderNotFound();
} }
const { author = "", datePublished = "" } = movie.content; const { author = "", datePublished = "",reviewBody } = movie.content;
const content = renderMarkdown( const content = renderMarkdown(
removeImage(movie.content.reviewBody || "", movie.content.image), removeImage(reviewBody || "", movie.content.image),
); );
return ( return (
@@ -57,7 +57,7 @@ export default async function Greet(
title: author?.name, title: author?.name,
href: `/?q=${encodeURIComponent(author?.name)}`, href: `/?q=${encodeURIComponent(author?.name)}`,
}, },
datePublished.toString(), date.toString(),
]} ]}
> >
{movie.content.reviewRating && ( {movie.content.reviewRating && (

View File

@@ -3,7 +3,6 @@ import { IngredientsList } from "@islands/IngredientsList.tsx";
import { MainLayout } from "@components/layouts/main.tsx"; import { MainLayout } from "@components/layouts/main.tsx";
import Counter from "@islands/Counter.tsx"; import Counter from "@islands/Counter.tsx";
import { Signal, useSignal } from "@preact/signals"; import { Signal, useSignal } from "@preact/signals";
import { Recipe } from "@lib/recipeSchema.ts";
import { RedirectSearchHandler } from "@islands/Search.tsx"; import { RedirectSearchHandler } from "@islands/Search.tsx";
import { KMenu } from "@islands/KMenu.tsx"; import { KMenu } from "@islands/KMenu.tsx";
import PageHero from "@components/PageHero.tsx"; import PageHero from "@components/PageHero.tsx";
@@ -12,8 +11,12 @@ import { renderMarkdown } from "@lib/markdown.ts";
import { isValidRecipe } from "@lib/recipeSchema.ts"; import { isValidRecipe } from "@lib/recipeSchema.ts";
import { MetaTags } from "@components/MetaTags.tsx"; import { MetaTags } from "@components/MetaTags.tsx";
import { fetchResource } from "@lib/marka/index.ts"; import { fetchResource } from "@lib/marka/index.ts";
import { RecipeResource } from "@lib/marka/schema.ts";
import { parseIngredients } from "@lib/parseIngredient.ts";
export const handler: Handlers<{ recipe: Recipe; session: unknown } | null> = { export const handler: Handlers<
{ recipe: RecipeResource; session: unknown } | null
> = {
async GET(_, ctx) { async GET(_, ctx) {
try { try {
const recipe = await fetchResource(`recipes/${ctx.params.name}.md`); const recipe = await fetchResource(`recipes/${ctx.params.name}.md`);
@@ -31,7 +34,11 @@ function ValidRecipe({
recipe, recipe,
amount, amount,
portion, portion,
}: { recipe: Recipe; amount: Signal<number>; portion: number }) { }: { recipe: RecipeResource; amount: Signal<number>; portion: number }) {
const ingredients = parseIngredients(
recipe.content.recipeIngredient?.join("\n"),
);
return ( return (
<> <>
<div class="flex items-center gap-8"> <div class="flex items-center gap-8">
@@ -39,7 +46,7 @@ function ValidRecipe({
{portion && <Counter count={amount} />} {portion && <Counter count={amount} />}
</div> </div>
<IngredientsList <IngredientsList
ingredients={recipe.content.recipeIngredient} ingredients={ingredients}
amount={amount} amount={amount}
portion={portion} portion={portion}
/> />

View File

@@ -23,14 +23,14 @@ export const handler: Handlers<{ serie: ReviewResource; session: unknown }> = {
}; };
export default function Greet( export default function Greet(
props: PageProps<{ serie: Series; session: Record<string, string> }>, props: PageProps<{ serie: ReviewResource; session: Record<string, string> }>,
) { ) {
const { serie, session } = props.data; const { serie, session } = props.data;
const { author = "", date = "" } = serie?.content || {}; const { author = "", date = "", reviewBody } = serie?.content || {};
const content = renderMarkdown( const content = renderMarkdown(
removeImage(serie.description || "", serie.content?.image), removeImage(reviewBody, serie.image?.url),
); );
return ( return (