feat: add madeira blog post upgrade astro
This commit is contained in:
@@ -3,18 +3,14 @@ import markdownToText from "@helpers/markdownToText";
|
||||
import { Card } from "./card";
|
||||
import { useTranslatedPath, useTranslations } from "@i18n/utils";
|
||||
import Image from "@components/Image.astro";
|
||||
import type { ImageMetadata } from "astro";
|
||||
import type { InferEntrySchema } from "astro:content";
|
||||
|
||||
interface Props {
|
||||
post: {
|
||||
data: {
|
||||
title: string;
|
||||
icon?: string;
|
||||
cover?: ImageMetadata;
|
||||
};
|
||||
data: InferEntrySchema<"projects">;
|
||||
collection: string;
|
||||
slug: string;
|
||||
body: string;
|
||||
id: string;
|
||||
body?: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,13 +18,13 @@ const {
|
||||
data: { title, cover, icon },
|
||||
collection,
|
||||
body,
|
||||
slug,
|
||||
id,
|
||||
} = Astro.props.post;
|
||||
|
||||
const translatePath = useTranslatedPath(Astro.url);
|
||||
const t = useTranslations(Astro.url);
|
||||
|
||||
const link = translatePath(`/${collection}/${slug.split("/")[0]}`);
|
||||
const link = translatePath(`/${collection}/${id.split("/")[0]}`);
|
||||
---
|
||||
|
||||
<Card
|
||||
@@ -39,7 +35,7 @@ const link = translatePath(`/${collection}/${slug.split("/")[0]}`);
|
||||
{title}
|
||||
</Card.Title>
|
||||
<Card.Description>
|
||||
{markdownToText(body).slice(0, 200)}
|
||||
{markdownToText(body ?? "").slice(0, 200)}
|
||||
</Card.Description>
|
||||
<Card.ReadMoreButton link={link} text={t("read-more")} />
|
||||
</Card.Content>
|
||||
|
||||
@@ -141,6 +141,15 @@
|
||||
// console.log(ev);
|
||||
};
|
||||
|
||||
function formatExposureTime(num: string) {
|
||||
if (num.includes("/")) {
|
||||
const [a, b] = num.split("/");
|
||||
return `${a}/${b}s`;
|
||||
} else {
|
||||
return num + "s";
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const wrappers = Array.prototype.slice.call(
|
||||
document.querySelectorAll("picture > img"),
|
||||
@@ -168,7 +177,9 @@
|
||||
? exifData.FocalLength.replace(" mm", "mm")
|
||||
: "",
|
||||
"FNumber" in exifData ? exifData.FNumber : "",
|
||||
"ExposureTime" in exifData ? exifData.ExposureTime : "",
|
||||
"ExposureTime" in exifData
|
||||
? formatExposureTime(exifData.ExposureTime)
|
||||
: "",
|
||||
];
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -206,10 +217,11 @@
|
||||
<div class="controls">
|
||||
{#each images as _, i}
|
||||
<button
|
||||
aria-label={`Image ${i + 1}`}
|
||||
class:active={currentIndex === i}
|
||||
on:click={() => {
|
||||
currentIndex = i;
|
||||
}} />
|
||||
}}></button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
---
|
||||
import markdownToText from "@helpers/markdownToText";
|
||||
import { useTranslatedPath } from "@i18n/utils";
|
||||
import type { InferEntrySchema } from "astro:content";
|
||||
|
||||
const tp = useTranslatedPath(Astro.url);
|
||||
|
||||
interface Props {
|
||||
post: {
|
||||
data: {
|
||||
title: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
tags?: string[];
|
||||
};
|
||||
data: InferEntrySchema<"blog">;
|
||||
collection: string;
|
||||
body: string;
|
||||
slug: string;
|
||||
body?: string;
|
||||
id: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,15 +18,14 @@ const { post } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="rounded-diag-md border border-neutral p-4 overflow-hidden">
|
||||
<a href={tp(`/${post.collection}/${post.slug.split("/")[0]}`)}>
|
||||
<a href={tp(`/${post.collection}/${post.id.split("/")[0]}`)}>
|
||||
<h2
|
||||
class="text-2xl flex gap-2 items-center line-clamp text-ellipsis overflow-hidden"
|
||||
>
|
||||
class="text-2xl flex gap-2 items-center line-clamp text-ellipsis overflow-hidden">
|
||||
{post.data.icon && <img src={post.data.icon} class="h-6" />}
|
||||
{post.data.title}
|
||||
</h2>
|
||||
<p class="text-ellipsis overflow-hidden line-clamp-2">
|
||||
{post.data.description || markdownToText(post.body).slice(0, 200)}
|
||||
{post.data.description || markdownToText(post?.body || "").slice(0, 200)}
|
||||
</p>
|
||||
</a>
|
||||
{
|
||||
@@ -39,8 +34,7 @@ const { post } = Astro.props;
|
||||
{post.data.tags.map((tag) => (
|
||||
<a
|
||||
href={tp(`/tag/${tag}`)}
|
||||
class="text-xs border border-neutral p-2 rounded-md"
|
||||
>
|
||||
class="text-xs border border-neutral p-2 rounded-md">
|
||||
{tag}
|
||||
</a>
|
||||
))}
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
href={link}
|
||||
data-astro-prefetch
|
||||
class="bg-light p-2 text-s rounded-md px-4 flex flex-0 items-center gap-2 w-fit"
|
||||
>{text}<span class="i-tabler-arrow-right inline-block w-4 h-4" />
|
||||
>{text}<span class="i-tabler-arrow-right inline-block w-4 h-4"></span>
|
||||
</a>
|
||||
|
||||
@@ -5,14 +5,7 @@ import Title from './Title.svelte';
|
||||
import Description from './Description.svelte';
|
||||
import ReadMoreButton from './ReadMoreButton.svelte';
|
||||
|
||||
const Card = {
|
||||
...Wrapper,
|
||||
Image,
|
||||
Content,
|
||||
Title,
|
||||
Description,
|
||||
ReadMoreButton
|
||||
} as typeof Wrapper & {
|
||||
const Card = Wrapper as typeof Wrapper & {
|
||||
Image: typeof Image;
|
||||
Content: typeof Content;
|
||||
Title: typeof Title;
|
||||
@@ -20,4 +13,10 @@ const Card = {
|
||||
ReadMoreButton: typeof ReadMoreButton;
|
||||
}
|
||||
|
||||
Card.Image = Image;
|
||||
Card.Content = Content;
|
||||
Card.Title = Title;
|
||||
Card.Description = Description;
|
||||
Card.ReadMoreButton = ReadMoreButton;
|
||||
|
||||
export { Card };
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { glob } from 'astro/loaders';
|
||||
import { defineCollection, z, type ImageFunction } from 'astro:content';
|
||||
|
||||
const defaultSchema = ({ image }: { image: ImageFunction }) => z.object({
|
||||
title: z.string(),
|
||||
date: z.date(),
|
||||
cover: image().optional(),
|
||||
links: z.array(z.array(z.string())).optional(),
|
||||
coverAlt: z.string().optional(),
|
||||
toc: z.boolean().optional(),
|
||||
description: z.string().optional(),
|
||||
icon: z.string().optional(),
|
||||
draft: z.boolean().optional(),
|
||||
featured: z.boolean().optional(),
|
||||
tags: z.array(z.string()).optional(),
|
||||
_layout: z.enum(['normal', 'transparent']).optional(),
|
||||
})
|
||||
|
||||
|
||||
export const collections = {
|
||||
'blog': defineCollection({
|
||||
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/blog" }),
|
||||
schema: defaultSchema,
|
||||
}),
|
||||
"projects": defineCollection({
|
||||
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/projects" }),
|
||||
schema: defaultSchema,
|
||||
}),
|
||||
"photos": defineCollection({
|
||||
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/photos" }),
|
||||
schema: defaultSchema,
|
||||
})
|
||||
};
|
||||
@@ -1,26 +0,0 @@
|
||||
import { defineCollection, z } from 'astro:content';
|
||||
|
||||
const blogCollection = defineCollection({
|
||||
schema: ({ image }) => z.object({
|
||||
title: z.string(),
|
||||
date: z.date(),
|
||||
cover: image().refine((img) => img.width >= 720, {
|
||||
message: "Cover image must be at least 720 pixels wide!",
|
||||
}).optional(),
|
||||
links: z.array(z.array(z.string())).optional(),
|
||||
coverAlt: z.string().optional(),
|
||||
toc: z.boolean().optional(),
|
||||
description: z.string().optional(),
|
||||
icon: z.string().optional(),
|
||||
draft: z.boolean().optional(),
|
||||
featured: z.boolean().optional(),
|
||||
tags: z.array(z.string()).optional(),
|
||||
_layout: z.enum(['normal', 'transparent']).optional(),
|
||||
})
|
||||
});
|
||||
|
||||
export const collections = {
|
||||
'blog': blogCollection,
|
||||
"projects": blogCollection,
|
||||
"photos": blogCollection,
|
||||
};
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+8
-4
@@ -1,4 +1,4 @@
|
||||
import { defaultLocale, getLocale } from 'astro-i18n-aut';
|
||||
import { defaultLocale, getLocale, locales } from 'astro-i18n-aut';
|
||||
import { ui, defaultLang, showDefaultLang } from './ui';
|
||||
|
||||
export function useTranslatedPath(url: URL) {
|
||||
@@ -17,9 +17,13 @@ export function useTranslations(url: URL) {
|
||||
|
||||
export function parseSlug(id: string) {
|
||||
const splitPath = id.split('/');
|
||||
const split = splitPath.pop()?.split('.');
|
||||
const lang = split?.length === 2 ? defaultLocale : split?.[1];
|
||||
return [splitPath.join("/"), lang]
|
||||
const split = splitPath.pop()?.split('.').map(s => s.replace(/^index/, ""));
|
||||
for (const s of split || []) {
|
||||
if (locales[s]) {
|
||||
return [splitPath[0] ?? id, s]
|
||||
}
|
||||
}
|
||||
return [splitPath[0] ?? id, defaultLocale]
|
||||
}
|
||||
|
||||
export function filterCollection<T extends { id: string, data: { draft?: boolean, date?: Date } }>(collection: T[], locale: string): T[] {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import { getCollection, render } from "astro:content";
|
||||
import { getLocale } from "astro-i18n-aut";
|
||||
import { filterCollection, parseSlug } from "@i18n/utils";
|
||||
|
||||
const locale = getLocale(Astro.url);
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const pages = await getCollection("blog");
|
||||
const posts = await getCollection("blog");
|
||||
|
||||
const paths = pages.map((page) => {
|
||||
const [slug] = parseSlug(page.id);
|
||||
return { params: { slug }, props: { ...page } };
|
||||
const paths = posts.map((post) => {
|
||||
const [slug] = parseSlug(post.id);
|
||||
return { params: { slug }, props: { ...post } };
|
||||
});
|
||||
|
||||
return paths;
|
||||
@@ -28,7 +28,7 @@ if (!page) {
|
||||
});
|
||||
}
|
||||
|
||||
const { Content } = await page.render();
|
||||
const { Content } = await render(page);
|
||||
---
|
||||
|
||||
<Content />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import { getCollection, render } from "astro:content";
|
||||
import { getLocale } from "astro-i18n-aut";
|
||||
import { filterCollection, parseSlug } from "@i18n/utils";
|
||||
|
||||
@@ -19,6 +19,7 @@ export async function getStaticPaths() {
|
||||
const pages = await getCollection("photos");
|
||||
const page = filterCollection(pages, locale).find((page) => {
|
||||
const [slug] = parseSlug(page.id);
|
||||
console.log({ slug, id: page.id, params: Astro.params.slug, locale });
|
||||
return slug === Astro.params.slug;
|
||||
});
|
||||
|
||||
@@ -28,7 +29,7 @@ if (!page) {
|
||||
});
|
||||
}
|
||||
|
||||
const { Content } = await page.render();
|
||||
const { Content } = await render(page);
|
||||
---
|
||||
|
||||
<Content />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import { getCollection, render } from "astro:content";
|
||||
import { getLocale } from "astro-i18n-aut";
|
||||
import { filterCollection, parseSlug } from "@i18n/utils";
|
||||
|
||||
@@ -28,7 +28,7 @@ if (!page) {
|
||||
});
|
||||
}
|
||||
|
||||
const { Content } = await page.render();
|
||||
const { Content } = await render(page);
|
||||
---
|
||||
|
||||
<Content />
|
||||
|
||||
Reference in New Issue
Block a user