Add GitHub-style Markdown editor to PublishModal
This commit is contained in:
parent
659789b0dc
commit
758d5c0207
157
src/components/ChangelogEditor.tsx
Normal file
157
src/components/ChangelogEditor.tsx
Normal file
@ -0,0 +1,157 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { theme } from "antd";
|
||||
import { commands, type ICommand } from "@uiw/react-md-editor";
|
||||
import "@uiw/react-md-editor/markdown-editor.css";
|
||||
import "@uiw/react-markdown-preview/markdown.css";
|
||||
|
||||
// @uiw/react-md-editor accesses browser APIs on import — skip SSR
|
||||
const MDEditor = dynamic(() => import("@uiw/react-md-editor"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type EditorMode = "edit" | "preview";
|
||||
|
||||
interface ChangelogEditorProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GitHub-style Code / Preview tabs
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function ModeTab({
|
||||
label,
|
||||
active,
|
||||
onClick,
|
||||
}: {
|
||||
label: string;
|
||||
active: boolean;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
const { token } = theme.useToken();
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
display: "inline-block",
|
||||
padding: `2px ${token.paddingSM}px`,
|
||||
fontSize: token.fontSizeSM,
|
||||
fontWeight: active ? 600 : 400,
|
||||
color: active ? token.colorPrimary : token.colorTextSecondary,
|
||||
borderBottom: active
|
||||
? `2px solid ${token.colorPrimary}`
|
||||
: "2px solid transparent",
|
||||
cursor: "pointer",
|
||||
userSelect: "none",
|
||||
}}
|
||||
onClick={onClick}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function buildTabCommand(
|
||||
key: string,
|
||||
label: string,
|
||||
active: boolean,
|
||||
onSwitch: () => void,
|
||||
): ICommand {
|
||||
return {
|
||||
name: key,
|
||||
keyCommand: key,
|
||||
icon: <ModeTab label={label} active={active} onClick={onSwitch} />,
|
||||
buttonProps: null,
|
||||
execute: (state, api, dispatch) => {
|
||||
dispatch?.({ preview: key === "preview" ? "preview" : "edit" });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Formatting commands, like GitHub's toolbar — hidden in Preview mode
|
||||
const FORMAT_COMMANDS: ICommand[] = [
|
||||
commands.bold,
|
||||
commands.italic,
|
||||
commands.strikethrough,
|
||||
commands.divider,
|
||||
commands.link,
|
||||
commands.code,
|
||||
commands.codeBlock,
|
||||
commands.image,
|
||||
commands.divider,
|
||||
commands.unorderedListCommand,
|
||||
commands.orderedListCommand,
|
||||
commands.checkedListCommand,
|
||||
commands.quote,
|
||||
commands.table,
|
||||
];
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Component
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function ChangelogEditor({ value, onChange }: ChangelogEditorProps) {
|
||||
const { token } = theme.useToken();
|
||||
const [mode, setMode] = useState<EditorMode>("edit");
|
||||
|
||||
const toolbarCommands: ICommand[] =
|
||||
mode === "edit"
|
||||
? [
|
||||
buildTabCommand("code", "Code", true, () => setMode("edit")),
|
||||
buildTabCommand("preview", "Preview", false, () => setMode("preview")),
|
||||
commands.divider,
|
||||
...FORMAT_COMMANDS,
|
||||
]
|
||||
: [
|
||||
buildTabCommand("code", "Code", false, () => setMode("edit")),
|
||||
buildTabCommand("preview", "Preview", true, () => setMode("preview")),
|
||||
];
|
||||
|
||||
return (
|
||||
<div
|
||||
data-color-mode="light"
|
||||
style={{
|
||||
// Map the editor's GitHub-flavored CSS variables onto Ant Design tokens
|
||||
// so the preview/toolbar visually merge into the surrounding UI.
|
||||
["--md-editor-background-color" as string]: token.colorBgContainer,
|
||||
["--md-editor-box-shadow-color" as string]: token.colorBorder,
|
||||
["--md-editor-font-family" as string]: token.fontFamily,
|
||||
["--color-fg-default" as string]: token.colorText,
|
||||
["--color-fg-muted" as string]: token.colorTextSecondary,
|
||||
["--color-canvas-default" as string]: token.colorBgContainer,
|
||||
["--color-canvas-subtle" as string]: token.colorFillQuaternary,
|
||||
["--color-border-default" as string]: token.colorBorder,
|
||||
["--color-border-muted" as string]: token.colorBorderSecondary,
|
||||
["--color-neutral-muted" as string]: token.colorFillSecondary,
|
||||
["--color-accent-fg" as string]: token.colorPrimary,
|
||||
["--color-accent-emphasis" as string]: token.colorPrimaryActive,
|
||||
}}
|
||||
>
|
||||
<MDEditor
|
||||
value={value}
|
||||
onChange={(val) => onChange(val ?? "")}
|
||||
preview={mode}
|
||||
height={520}
|
||||
minHeight={400}
|
||||
maxHeight={900}
|
||||
visibleDragbar={false}
|
||||
textareaProps={{
|
||||
placeholder: "输入 Markdown 格式的更新日志…",
|
||||
}}
|
||||
commands={toolbarCommands}
|
||||
extraCommands={[]}
|
||||
style={{
|
||||
borderRadius: token.borderRadius,
|
||||
fontFamily: token.fontFamily,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -25,6 +25,7 @@ import { Template } from "@/lib/utils/template";
|
||||
import type { McVersionEntry } from "@/lib/utils/mcVersion";
|
||||
import { computeVersionRanges } from "@/lib/utils/mcVersion";
|
||||
import { getModrinthMcVersions, getCurseForgeMcVersions } from "@/services/meta";
|
||||
import { ChangelogEditor } from "./ChangelogEditor";
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
@ -384,21 +385,7 @@ export function PublishModal({
|
||||
<Text strong style={{ display: "block", marginBottom: token.marginSM }}>
|
||||
Changelog (Markdown)
|
||||
</Text>
|
||||
{/* TODO: Replace with @uiw/react-md-editor */}
|
||||
<textarea
|
||||
style={{
|
||||
width: "100%",
|
||||
minHeight: 160,
|
||||
border: `1px solid ${token.colorBorder}`,
|
||||
borderRadius: token.borderRadius,
|
||||
padding: token.paddingSM,
|
||||
fontFamily: token.fontFamilyCode ?? "monospace",
|
||||
fontSize: token.fontSizeSM,
|
||||
}}
|
||||
placeholder="输入 Markdown 格式的更新日志…"
|
||||
value={changelog}
|
||||
onChange={(e) => setChangelog(e.target.value)}
|
||||
/>
|
||||
<ChangelogEditor value={changelog} onChange={setChangelog} />
|
||||
</div>
|
||||
|
||||
{/* Version type */}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user