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"
|
alt="Describe the image"
|
||||||
caption="Optional caption"
|
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",
|
name: "PullQuote",
|
||||||
|
|||||||
@@ -73,6 +73,27 @@ const mdxAutocompleteSuggestions: MdeAutocompleteSuggestion[] = [
|
|||||||
group: 'Component',
|
group: 'Component',
|
||||||
trigger: '<',
|
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',
|
label: 'PullQuote',
|
||||||
value: `<PullQuote>\n${AUTOCOMPLETE_CURSOR_MARKER}\n</PullQuote>`,
|
value: `<PullQuote>\n${AUTOCOMPLETE_CURSOR_MARKER}\n</PullQuote>`,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export default async function CvPage() {
|
|||||||
components={mdxComponents}
|
components={mdxComponents}
|
||||||
options={{
|
options={{
|
||||||
mdxOptions: {
|
mdxOptions: {
|
||||||
format: "md",
|
format: "mdx",
|
||||||
remarkPlugins: [remarkGfm],
|
remarkPlugins: [remarkGfm],
|
||||||
rehypePlugins: [rehypeHighlight],
|
rehypePlugins: [rehypeHighlight],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default async function ProjectsPage() {
|
|||||||
components={mdxComponents}
|
components={mdxComponents}
|
||||||
options={{
|
options={{
|
||||||
mdxOptions: {
|
mdxOptions: {
|
||||||
format: "md",
|
format: "mdx",
|
||||||
remarkPlugins: [remarkGfm],
|
remarkPlugins: [remarkGfm],
|
||||||
rehypePlugins: [rehypeHighlight],
|
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" />;
|
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 }) {
|
function PullQuote({ children }: { children: ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<blockquote className="border-primary my-8 border-l-4 pl-5 text-xl leading-8 font-medium">
|
<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 }) {
|
function Paragraph({ children }: { children: ReactNode }) {
|
||||||
const containsBlockComponent = Children.toArray(children).some(
|
const containsBlockComponent = Children.toArray(children).some(
|
||||||
@@ -153,9 +186,12 @@ export const mdxComponents = {
|
|||||||
Badge,
|
Badge,
|
||||||
ButtonLink,
|
ButtonLink,
|
||||||
Callout,
|
Callout,
|
||||||
|
Column,
|
||||||
Figure,
|
Figure,
|
||||||
img: Img,
|
img: Img,
|
||||||
|
Layout,
|
||||||
Lead,
|
Lead,
|
||||||
PullQuote,
|
PullQuote,
|
||||||
|
Row,
|
||||||
TagList,
|
TagList,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user