45 lines
1.9 KiB
TypeScript
45 lines
1.9 KiB
TypeScript
"use client";
|
|
|
|
import dynamic from "next/dynamic";
|
|
import type { ComponentProps } from "react";
|
|
import type { RehypeRewriteOptions } from "rehype-rewrite";
|
|
|
|
// Markdown editing/rendering touches the DOM directly and has no
|
|
// server-render value, so both pieces are loaded client-side only and kept
|
|
// out of the initial board bundle. Shared here so every place that needs
|
|
// markdown (group notes, to-do details) uses the exact same editor/preview
|
|
// widgets instead of each declaring its own dynamic import.
|
|
const RawMDEditor = dynamic(() => import("@uiw/react-md-editor"), { ssr: false });
|
|
|
|
// `preview` only seeds the editor's initial mode -- @uiw/react-md-editor
|
|
// owns it as internal state after that, flipped by the edit/live/preview
|
|
// toolbar buttons. Defaulting it to "preview" opens every editor rendered
|
|
// instead of raw markdown, while the toolbar still lets someone drop into
|
|
// "edit" (or the split "live" view) when they want granular control.
|
|
export function MDEditor(props: ComponentProps<typeof RawMDEditor>) {
|
|
return <RawMDEditor preview="preview" {...props} />;
|
|
}
|
|
|
|
const RawMarkdownPreview = dynamic(
|
|
() => import("@uiw/react-md-editor").then((mod) => mod.default.Markdown),
|
|
{ ssr: false }
|
|
);
|
|
|
|
// Rewrites every rendered <a> to open in a new tab -- clicking a link in a
|
|
// note/to-do preview would otherwise navigate the whole app away from the
|
|
// board. `noopener noreferrer` keeps the new tab from being able to reach
|
|
// back into this one via `window.opener`.
|
|
const openLinksInNewTab: RehypeRewriteOptions["rewrite"] = (node) => {
|
|
if (node.type === "element" && node.tagName === "a") {
|
|
node.properties = {
|
|
...node.properties,
|
|
target: "_blank",
|
|
rel: ["noopener", "noreferrer"],
|
|
};
|
|
}
|
|
};
|
|
|
|
export function MarkdownPreview(props: ComponentProps<typeof RawMarkdownPreview>) {
|
|
return <RawMarkdownPreview {...props} rehypeRewrite={openLinksInNewTab} />;
|
|
}
|