recipe.astro (8868B)
1 --- 2 import type { Props } from "@astrojs/starlight/props"; 3 import { Recipe as CooklangRecipe } from "@cooklang/cooklang-ts"; 4 import Default from "@astrojs/starlight/components/MarkdownContent.astro"; 5 import InfoBox from './infobox.astro'; 6 7 const frontmatter = Astro.props.entry.data; 8 const isRecipe = frontmatter.recipe; 9 const recipe = new CooklangRecipe(Astro.props.entry.body, { 10 defaultIngredientAmount: "etwas", 11 }); 12 const meta_array = Array.from(Object.entries(recipe.metadata)); 13 const contains_source = meta_array.find(([key, _value]) => "source" === key.toLowerCase() || "quelle" === key.toLowerCase()); 14 const source_url_raw = (contains_source ?? [])[1] ?? ""; 15 const source_url = source_url_raw.replace("<", "").replace(">", ""); 16 const infos: { 17 title?: string, 18 text: string 19 }[] | undefined = frontmatter.info; 20 const description = frontmatter.description; 21 --- 22 { 23 isRecipe ? ( 24 <Default {...Astro.props}> 25 <article vocab="https://schema.org/" typeof="Recipe"> 26 {description && <><p class="whitespace-pre-line">{description}</p><hr/></>} 27 <p class="no-margin-children"> 28 {(contains_source?.length ?? 0) > 0 ? ( 29 <><b>Quelle: </b>{source_url.includes("http") ? <a href={source_url}>{source_url}</a> : <span>{source_url}</span>}<br/></> 30 ) : ( 31 <><b>Quelle: </b>Keine Quelle angegeben</><br/> 32 )} 33 34 { 35 (contains_source && meta_array.length >= 2) || !contains_source && meta_array.length >= 1 ? 36 ( 37 Array.from(Object.entries(recipe.metadata)).map( 38 ([key, value]) => 39 key.toLowerCase() === "source" || key.toLowerCase() === "quelle" ? null : ( 40 <> 41 <b class="capitalize">{key}</b>: 42 {value.replace("%", " ")}<br/> 43 </> 44 ), 45 ) 46 ) : 47 ( 48 <></> 49 ) 50 } 51 </p> 52 53 <div class="grid-container"> 54 <div class="box p-4 border border-zinc-300 rounded min-w-[10rem] w-full md:w-auto relative"> 55 <h2 id="zutaten">Zutaten</h2> 56 <ul> 57 {recipe.ingredients.map((ingredient) => ( 58 <li> 59 {["etwas"].includes(String(ingredient.quantity)) ? ( 60 <span property="recipeIngredient"> 61 {ingredient.quantity} {ingredient.name} 62 </span> 63 ) : ( 64 <span property="recipeIngredient"> 65 {ingredient.name} — {ingredient.quantity}{" "} 66 {ingredient.units} 67 </span> 68 )} 69 </li> 70 ))} 71 </ul> 72 </div> 73 74 {recipe.cookwares.length > 0 ? ( 75 <div class="box p-4 border border-zinc-300 rounded min-w-[10rem] w-full md:w-auto relative"> 76 <h2 id="geraete">Hilfsmittel</h2> 77 <ul> 78 {recipe.cookwares.map((cookware) => { 79 if (cookware.quantity === 1) { 80 return ( 81 <li> 82 {cookware.name} 83 </li> 84 ) 85 } else { 86 return ( 87 <li> 88 {cookware.quantity} {cookware.name} 89 </li> 90 ) 91 } 92 93 })} 94 </ul> 95 </div>) : null} 96 </div> 97 98 { 99 ((infos?.length ?? 0) > 0) ? ( 100 infos?.map((info) => ( 101 <InfoBox info={info}></InfoBox> 102 )) 103 ): null 104 } 105 106 <h2 id="zubereitung">Zubereitung</h2> 107 <ol property="recipeInstructions"> 108 {recipe.steps.map((step) => ( 109 <li class="leading-loose"> 110 {step.map((text: any) => { 111 if (text.type === "text") { 112 return text.value; 113 } else if (text.type === "ingredient") { 114 return ( 115 <span class="rounded-full px-2 py-1 bg-green-200 text-black whitespace-nowrap"> 116 {text.quantity === "etwas" ? ( 117 <>{text.name}</> 118 ) : ( 119 <> 120 {text.quantity} {text.units}{" "} 121 {text.name} 122 </> 123 )} 124 </span> 125 ); 126 } else if (text.type === "cookware") { 127 if (text.quantity === 1) { 128 return ( 129 <span class="rounded-full px-2 py-1 bg-cyan-200 text-black whitespace-nowrap"> 130 {text.name} 131 </span> 132 ); 133 }else { 134 return ( 135 <span class="rounded-full px-2 py-1 bg-cyan-200 text-black whitespace-nowrap"> 136 {text.quantity} {text.name} 137 </span> 138 ); 139 } 140 } else if (text.type === "timer") { 141 return ( 142 <span class="rounded-full px-2 py-1 bg-red-200 text-black whitespace-nowrap"> 143 { 144 text.units == "minutes" ? ( 145 <meta property="performTime" content={"PT"+text.quanity+"M"} /> 146 ): <></> 147 } 148 149 {text.quantity} {text.units} 150 </span> 151 ); 152 } else { 153 return "Error"; 154 } 155 })} 156 </li> 157 ))} 158 </ol> 159 </article> 160 </Default> 161 <style> 162 .grid-container { 163 display: grid; 164 grid-auto-flow: dense; 165 gap: 1rem; 166 grid-template-columns: repeat(auto-fit, minmax(min(100%, 100px), 1fr)); 167 container-type: inline-size; 168 } 169 170 @media only screen and (max-width: 600px) { 171 .grid-container { 172 grid-template-rows: repeat(auto-fit, minmax(min(100%, 100px), 1fr)); 173 grid-template-columns: 1fr; 174 } 175 } 176 177 .box { 178 display: grid; 179 grid-row: span 2; 180 grid-template-rows: subgrid; 181 margin: 0 !important; 182 } 183 184 .no-margin-children > * { 185 margin: 0 !important; 186 } 187 </style> 188 ) : ( 189 <Default {...Astro.props}> 190 <slot /> 191 </Default> 192 ) 193 }