From 81c60feed960ba0f7c3a2e062c7e274e69699bb9 Mon Sep 17 00:00:00 2001 From: Gregor Lohaus Date: Fri, 19 Jun 2026 14:48:38 +0200 Subject: [PATCH] mdx layout components --- src/app/_components/MdeFormField.tsx | 25 ------------ .../_components/MdxComponentReference.tsx | 20 ++++++++++ .../_components/useMdxEditorFieldProps.tsx | 21 ++++++++++ src/app/cv/page.tsx | 2 +- src/app/projects/page.tsx | 2 +- src/components/mdx-components.tsx | 38 ++++++++++++++++++- 6 files changed, 80 insertions(+), 28 deletions(-) delete mode 100644 src/app/_components/MdeFormField.tsx diff --git a/src/app/_components/MdeFormField.tsx b/src/app/_components/MdeFormField.tsx deleted file mode 100644 index 1e06a66..0000000 --- a/src/app/_components/MdeFormField.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import MDEditor from "@uiw/react-md-editor"; -import type { Control, FieldValues, Path } from "react-hook-form"; -import { FormControl, FormField, FormItem, FormLabel } from "~/components/ui/form"; -export default function MdeFormField(params: { control: Control, name: Path, label: string, dataColorMode: "dark"|"light" }) { - return ( - ( - - - {params.label} - - - - - - )} - /> - ) -} diff --git a/src/app/admin/_components/MdxComponentReference.tsx b/src/app/admin/_components/MdxComponentReference.tsx index e44f65a..2253f94 100644 --- a/src/app/admin/_components/MdxComponentReference.tsx +++ b/src/app/admin/_components/MdxComponentReference.tsx @@ -44,6 +44,26 @@ External resource alt="Describe the image" caption="Optional caption" />`, + }, + { + name: "Layout / Row / Column", + description: "Border-less flex layout. Columns sit side by side and wrap when narrow.", + code: ` + + + +- First list item +- Second list item + + + + +- Another list +- Side by side + + + +`, }, { name: "PullQuote", diff --git a/src/app/admin/_components/useMdxEditorFieldProps.tsx b/src/app/admin/_components/useMdxEditorFieldProps.tsx index b479d15..1edb793 100644 --- a/src/app/admin/_components/useMdxEditorFieldProps.tsx +++ b/src/app/admin/_components/useMdxEditorFieldProps.tsx @@ -73,6 +73,27 @@ const mdxAutocompleteSuggestions: MdeAutocompleteSuggestion[] = [ group: 'Component', trigger: '<', }, + { + label: 'Layout', + value: `\n\n\n${AUTOCOMPLETE_CURSOR_MARKER}\n\n\n\n\n\n`, + detail: 'Flex layout wrapper with side-by-side, wrapping columns.', + group: 'Component', + trigger: '<', + }, + { + label: 'Row', + value: `\n\n${AUTOCOMPLETE_CURSOR_MARKER}\n\n\n\n\n`, + detail: 'Side-by-side columns that wrap when narrow.', + group: 'Component', + trigger: '<', + }, + { + label: 'Column', + value: `\n${AUTOCOMPLETE_CURSOR_MARKER}\n`, + detail: 'Vertically stacked column within a Row.', + group: 'Component', + trigger: '<', + }, { label: 'PullQuote', value: `\n${AUTOCOMPLETE_CURSOR_MARKER}\n`, diff --git a/src/app/cv/page.tsx b/src/app/cv/page.tsx index caf9f82..4f09abb 100644 --- a/src/app/cv/page.tsx +++ b/src/app/cv/page.tsx @@ -24,7 +24,7 @@ export default async function CvPage() { components={mdxComponents} options={{ mdxOptions: { - format: "md", + format: "mdx", remarkPlugins: [remarkGfm], rehypePlugins: [rehypeHighlight], }, diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx index 8ea2bf4..7f8cc9b 100644 --- a/src/app/projects/page.tsx +++ b/src/app/projects/page.tsx @@ -23,7 +23,7 @@ export default async function ProjectsPage() { components={mdxComponents} options={{ mdxOptions: { - format: "md", + format: "mdx", remarkPlugins: [remarkGfm], rehypePlugins: [rehypeHighlight], }, diff --git a/src/components/mdx-components.tsx b/src/components/mdx-components.tsx index f86a445..05908a0 100644 --- a/src/components/mdx-components.tsx +++ b/src/components/mdx-components.tsx @@ -111,6 +111,31 @@ function Img({ src, alt, title }: { src: string; alt?: string; title?: string }) return {alt; } +// Composable, border-less flex layout primitives for MDX. +// +// — section wrapper, stacks its children with spacing +// — lays children side by side, wraps when too narrow +// — stacks its own children vertically +// +// Row/Column nest freely. A Row sizes its direct children into equal, +// flex-growing tracks with a min width, so two lists/Columns sit side by +// side and drop to stacked once they can't keep ~16rem each. +function Layout({ children }: { children: ReactNode }) { + return
{children}
; +} + +function Row({ children }: { children: ReactNode }) { + return ( +
+ {children} +
+ ); +} + +function Column({ children }: { children: ReactNode }) { + return
{children}
; +} + function PullQuote({ children }: { children: ReactNode }) { return (
@@ -135,7 +160,15 @@ function ExternalLink(props: ComponentPropsWithoutRef<"a">) { ); } -const blockComponents = new Set([Callout, Figure, PullQuote, TagList]); +const blockComponents = new Set([ + Callout, + Column, + Figure, + Layout, + PullQuote, + Row, + TagList, +]); function Paragraph({ children }: { children: ReactNode }) { const containsBlockComponent = Children.toArray(children).some( @@ -153,9 +186,12 @@ export const mdxComponents = { Badge, ButtonLink, Callout, + Column, Figure, img: Img, + Layout, Lead, PullQuote, + Row, TagList, };