This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import markdownToText from "@helpers/markdownToText";
|
||||
import { markdownToText,readDuration } from "@helpers/markdown";
|
||||
import { Card } from "./card";
|
||||
import { useTranslatedPath, useTranslations } from "@i18n/utils";
|
||||
import Image from "@components/Image.astro";
|
||||
@@ -30,7 +30,7 @@ const link = translatePath(`/${collection}/${id.split("/")[0]}`);
|
||||
<Card
|
||||
classes={`grid gradient border-1 border-neutral overflow-hidden ${cover ? "grid-rows-[200px_1fr] xs:grid-rows-none xs:grid-cols-[1fr_200px]" : ""}`}>
|
||||
<Card.Content classes="px-8 py-7 order-last xs:order-first">
|
||||
{date && (<Card.Metadata date={date} />)}
|
||||
{(date || body)&& <Card.Metadata date={date} readDuration={readDuration(body)} />}
|
||||
<Card.Title classes="text-4xl flex items-center gap-2">
|
||||
{
|
||||
icon &&
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import markdownToText from "@helpers/markdownToText";
|
||||
import { markdownToText } from "@helpers/markdown";
|
||||
import { useTranslatedPath } from "@i18n/utils";
|
||||
import type { InferEntrySchema } from "astro:content";
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
export let date: string | Date;
|
||||
export let readDuration: string | undefined;
|
||||
|
||||
const toDate = (d: string | Date) =>
|
||||
typeof d === "string" ? new Date(d) : d;
|
||||
@@ -22,4 +23,8 @@
|
||||
<time class="text-sm text-neutral" datetime={iso(date)}
|
||||
>{formatDate(date)}</time>
|
||||
{/if}
|
||||
|
||||
{#if readDuration}
|
||||
<div class="text-sm text-neutral">{readDuration} mins read</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
25
src/components/resources/Article.astro
Normal file
25
src/components/resources/Article.astro
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
import * as memorium from "@helpers/memorium";
|
||||
import { markdownToHtml, markdownToText } from "@helpers/markdown";
|
||||
import Image from "@components/Image.astro";
|
||||
|
||||
const { resource } = Astro.props;
|
||||
const ingredients = resource?.content?.recipeIngredient || [];
|
||||
const instructions = resource?.content?.recipeInstructions || [];
|
||||
---
|
||||
|
||||
<h1 class="text-4xl">{resource?.content?.headline}</h1>
|
||||
<div>
|
||||
{
|
||||
resource?.content?.image && (
|
||||
<Image
|
||||
hash
|
||||
src={{ src: memorium.getImageUrl(resource.content.image) }}
|
||||
alt="Cover for {resource?.content?.name}"
|
||||
class="rounded-2xl overflow-hidden"
|
||||
pictureClass="rounded-2xl"
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div set:html={markdownToHtml(resource?.content?.articleBody)} />
|
||||
13
src/components/resources/Display.astro
Normal file
13
src/components/resources/Display.astro
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
import Recipe from "./Recipe.astro";
|
||||
import Article from "./Article.astro";
|
||||
const { resource } = Astro.props;
|
||||
const type = resource?.content?._type ?? "unknown";
|
||||
---
|
||||
|
||||
{type === "Recipe" && <Recipe resource={resource} />}
|
||||
{type === "Article" && <Article resource={resource} />}
|
||||
|
||||
{type === "unknown" && <div>
|
||||
<h3>Unknown resource type</h3>
|
||||
</div>}
|
||||
32
src/components/resources/Recipe.astro
Normal file
32
src/components/resources/Recipe.astro
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
import * as memorium from "@helpers/memorium";
|
||||
import { markdownToHtml, markdownToText } from "@helpers/markdown";
|
||||
import Image from "@components/Image.astro";
|
||||
|
||||
const { resource } = Astro.props
|
||||
const ingredients = resource?.content?.recipeIngredient || [];
|
||||
const instructions = resource?.content?.recipeInstructions || [];
|
||||
---
|
||||
|
||||
<h1 class="text-4xl">{resource?.content?.name}</h1>
|
||||
<div>
|
||||
{resource?.content?.image && <Image hash src={{src: memorium.getImageUrl(resource.content.image)}} alt="Cover for {resource?.content?.name}" class="rounded-2xl overflow-hidden" pictureClass="rounded-2xl" />}
|
||||
</div>
|
||||
<p>{resource?.content?.description}</p>
|
||||
<h2 class="text-2xl">Ingredients</h2>
|
||||
<ul>
|
||||
{
|
||||
ingredients.map((ingredient) => (
|
||||
<li set:html={markdownToHtml(ingredient)}/>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
|
||||
<h2 class="text-2xl">Steps</h2>
|
||||
<ol>
|
||||
{
|
||||
instructions.map((ingredient) => (
|
||||
<li set:html={markdownToHtml(ingredient)}/>
|
||||
))
|
||||
}
|
||||
</ol>
|
||||
@@ -1,8 +1,26 @@
|
||||
import MarkdownIt from "markdown-it";
|
||||
const parser = new MarkdownIt();
|
||||
|
||||
export default function markdownToText(markdown: string): string {
|
||||
if(!markdown) return ''
|
||||
export function readDuration(markdown: string): number | undefined {
|
||||
const words = markdown.split(" ")?.filter(Boolean)?.length;
|
||||
return words && Math.round(words / 250);
|
||||
}
|
||||
|
||||
export function markdownToHtml(markdown: string): string {
|
||||
const md = new MarkdownIt({
|
||||
html: false, // set to true only if you trust the source
|
||||
linkify: true,
|
||||
typographer: true,
|
||||
breaks: true,
|
||||
});
|
||||
|
||||
// Convert -> sanitize
|
||||
const unsafeHtml = md.render(markdown);
|
||||
return unsafeHtml;
|
||||
}
|
||||
|
||||
export function markdownToText(markdown: string): string {
|
||||
if (!markdown) return "";
|
||||
return parser
|
||||
.render(markdown)
|
||||
.split("\n")
|
||||
@@ -1,9 +1,10 @@
|
||||
---
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import { useTranslatedPath } from "@i18n/utils";
|
||||
import ResourceDisplay from "@components/resources/Display.astro";
|
||||
import * as memorium from "@helpers/memorium";
|
||||
import { resources as resourceTypes } from "../resources.ts";
|
||||
import markdownToText from "@helpers/markdownToText";
|
||||
import { markdownToText } from "@helpers/markdown";
|
||||
import Image from "@components/Image.astro";
|
||||
|
||||
const { resourceType, resourceName } = Astro?.params;
|
||||
@@ -35,9 +36,6 @@ export async function getStaticPaths() {
|
||||
const resource = await memorium.listResource(
|
||||
`${resourceType}/${resourceName}.md`,
|
||||
);
|
||||
|
||||
const ingredients = resource?.content?.recipeIngredient || [];
|
||||
const instructions = resource?.content?.recipeInstructions || [];
|
||||
---
|
||||
|
||||
<Layout title="Max Richter">
|
||||
@@ -49,7 +47,7 @@ const instructions = resource?.content?.recipeInstructions || [];
|
||||
</a>
|
||||
<div class="date opacity-50">
|
||||
{
|
||||
resource?.content.date?.toLocaleString("en-US", {
|
||||
resource?.content.datePublished?.toLocaleString("en-US", {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
@@ -57,28 +55,5 @@ const instructions = resource?.content?.recipeInstructions || [];
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <pre>{JSON.stringify(resource, null, 2)}</pre> -->
|
||||
<h1 class="text-4xl">{resource?.content?.name}</h1>
|
||||
<div>
|
||||
{resource?.content?.image && <Image hash src={{src: memorium.getImageUrl(resource.content.image)}} alt="Cover for {resource?.content?.name}" class="rounded-2xl overflow-hidden" pictureClass="rounded-2xl" />}
|
||||
</div>
|
||||
<p>{resource?.content?.description}</p>
|
||||
<h2 class="text-2xl">Ingredients</h2>
|
||||
<ul>
|
||||
{
|
||||
ingredients.map((ingredient) => (
|
||||
<li>{markdownToText(ingredient)}</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
|
||||
<h2 class="text-2xl">Steps</h2>
|
||||
<ol>
|
||||
{
|
||||
instructions.map((ingredient) => (
|
||||
<li>{markdownToText(ingredient)}</li>
|
||||
))
|
||||
}
|
||||
</ol>
|
||||
<ResourceDisplay resource={resource} />
|
||||
</Layout>
|
||||
|
||||
@@ -3,7 +3,7 @@ import Layout from "@layouts/Layout.astro";
|
||||
import HeroCard from "@components/HeroCard.astro";
|
||||
import * as memorium from "@helpers/memorium";
|
||||
import { resources as resourceTypes } from "../resources.ts";
|
||||
import markdownToText from "@helpers/markdownToText";
|
||||
import { markdownToText } from "@helpers/markdown";
|
||||
|
||||
const { resourceType } = Astro.params;
|
||||
|
||||
@@ -28,19 +28,25 @@ export async function getStaticPaths() {
|
||||
});
|
||||
}
|
||||
|
||||
function isValidResource(res) {
|
||||
if (res?.content?.name) return true;
|
||||
if (res?.content?.headline) return true;
|
||||
return false;
|
||||
}
|
||||
---
|
||||
|
||||
<Layout title="Max Richter">
|
||||
{
|
||||
resources.content
|
||||
.filter((res) => res && res?.content && res?.content?.name)
|
||||
.filter((res) => res && res?.content)
|
||||
.map((resource: any) => (
|
||||
<HeroCard
|
||||
post={{
|
||||
collection: "resources/" + resourceType,
|
||||
id: resource.name.replace(/\.md$/, ""),
|
||||
data: {
|
||||
title: resource.content.name,
|
||||
title: resource.content.name ?? resource.content.headline,
|
||||
date: resource?.content?.datePublished,
|
||||
cover: {
|
||||
src: memorium.getImageUrl(resource.content.image),
|
||||
},
|
||||
@@ -50,3 +56,4 @@ export async function getStaticPaths() {
|
||||
))
|
||||
}
|
||||
</Layout>
|
||||
</Layout>
|
||||
|
||||
@@ -19,15 +19,14 @@ type Resource = {
|
||||
// },
|
||||
// };
|
||||
|
||||
// const articles = {
|
||||
// id: "Articles",
|
||||
// collection,
|
||||
// body: "Articles saved",
|
||||
// data: {
|
||||
// title: "Articles",
|
||||
// icon: "📰",
|
||||
// },
|
||||
// };
|
||||
const articles = {
|
||||
id: "articles",
|
||||
collection,
|
||||
data: {
|
||||
title: "Articles",
|
||||
icon: "📰",
|
||||
},
|
||||
};
|
||||
|
||||
const recipes = {
|
||||
id: "recipes",
|
||||
@@ -58,4 +57,4 @@ const recipes = {
|
||||
// },
|
||||
// };
|
||||
|
||||
export const resources: Resource[] = [recipes];
|
||||
export const resources: Resource[] = [recipes, articles];
|
||||
|
||||
Reference in New Issue
Block a user