mdx layout components
This commit is contained in:
@@ -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<T extends FieldValues>(params: { control: Control<T>, name: Path<T>, label: string, dataColorMode: "dark"|"light" }) {
|
||||
return (
|
||||
<FormField
|
||||
control={params.control}
|
||||
name={params.name}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{params.label}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<MDEditor
|
||||
value={field.value ? field.value : ""}
|
||||
onChange={field.onChange}
|
||||
data-color-mode={params.dataColorMode}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -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: `<Layout>
|
||||
<Row>
|
||||
<Column>
|
||||
|
||||
- First list item
|
||||
- Second list item
|
||||
|
||||
</Column>
|
||||
<Column>
|
||||
|
||||
- Another list
|
||||
- Side by side
|
||||
|
||||
</Column>
|
||||
</Row>
|
||||
</Layout>`,
|
||||
},
|
||||
{
|
||||
name: "PullQuote",
|
||||
|
||||
@@ -73,6 +73,27 @@ const mdxAutocompleteSuggestions: MdeAutocompleteSuggestion[] = [
|
||||
group: 'Component',
|
||||
trigger: '<',
|
||||
},
|
||||
{
|
||||
label: 'Layout',
|
||||
value: `<Layout>\n<Row>\n<Column>\n${AUTOCOMPLETE_CURSOR_MARKER}\n</Column>\n<Column>\n\n</Column>\n</Row>\n</Layout>`,
|
||||
detail: 'Flex layout wrapper with side-by-side, wrapping columns.',
|
||||
group: 'Component',
|
||||
trigger: '<',
|
||||
},
|
||||
{
|
||||
label: 'Row',
|
||||
value: `<Row>\n<Column>\n${AUTOCOMPLETE_CURSOR_MARKER}\n</Column>\n<Column>\n\n</Column>\n</Row>`,
|
||||
detail: 'Side-by-side columns that wrap when narrow.',
|
||||
group: 'Component',
|
||||
trigger: '<',
|
||||
},
|
||||
{
|
||||
label: 'Column',
|
||||
value: `<Column>\n${AUTOCOMPLETE_CURSOR_MARKER}\n</Column>`,
|
||||
detail: 'Vertically stacked column within a Row.',
|
||||
group: 'Component',
|
||||
trigger: '<',
|
||||
},
|
||||
{
|
||||
label: 'PullQuote',
|
||||
value: `<PullQuote>\n${AUTOCOMPLETE_CURSOR_MARKER}\n</PullQuote>`,
|
||||
|
||||
@@ -24,7 +24,7 @@ export default async function CvPage() {
|
||||
components={mdxComponents}
|
||||
options={{
|
||||
mdxOptions: {
|
||||
format: "md",
|
||||
format: "mdx",
|
||||
remarkPlugins: [remarkGfm],
|
||||
rehypePlugins: [rehypeHighlight],
|
||||
},
|
||||
|
||||
@@ -23,7 +23,7 @@ export default async function ProjectsPage() {
|
||||
components={mdxComponents}
|
||||
options={{
|
||||
mdxOptions: {
|
||||
format: "md",
|
||||
format: "mdx",
|
||||
remarkPlugins: [remarkGfm],
|
||||
rehypePlugins: [rehypeHighlight],
|
||||
},
|
||||
|
||||
@@ -111,6 +111,31 @@ function Img({ src, alt, title }: { src: string; alt?: string; title?: string })
|
||||
return <img src={src} alt={alt ?? ""} className="w-full rounded-md border object-cover" />;
|
||||
}
|
||||
|
||||
// Composable, border-less flex layout primitives for MDX.
|
||||
//
|
||||
// <Layout> — section wrapper, stacks its children with spacing
|
||||
// <Row> — lays children side by side, wraps when too narrow
|
||||
// <Column> — 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 <div className="my-6 flex flex-col gap-6 [&>*]:my-0">{children}</div>;
|
||||
}
|
||||
|
||||
function Row({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<div className="flex flex-wrap gap-x-8 gap-y-4 [&>*]:mt-0 [&>*]:min-w-[16rem] [&>*]:flex-1">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Column({ children }: { children: ReactNode }) {
|
||||
return <div className="flex min-w-0 flex-col gap-2 [&>*]:my-0">{children}</div>;
|
||||
}
|
||||
|
||||
function PullQuote({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<blockquote className="border-primary my-8 border-l-4 pl-5 text-xl leading-8 font-medium">
|
||||
@@ -135,7 +160,15 @@ function ExternalLink(props: ComponentPropsWithoutRef<"a">) {
|
||||
);
|
||||
}
|
||||
|
||||
const blockComponents = new Set<unknown>([Callout, Figure, PullQuote, TagList]);
|
||||
const blockComponents = new Set<unknown>([
|
||||
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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user