MDXEditor setup

This commit is contained in:
gom-by
2025-02-19 17:53:38 +01:00
parent 70cce97c6a
commit 965f107921
3 changed files with 3253 additions and 147 deletions

View File

@@ -1,144 +1,77 @@
"use client";
import { Textarea } from "@/components/ui/textarea";
import { useEffect, useRef, useState } from "react";
import { marked } from "marked";
import DOMPurify from "isomorphic-dompurify";
import { useEffect, useState, useRef } from "react";
import { Button } from "@/components/ui/button";
import { Bold, Italic, Strikethrough, Underline } from "lucide-react";
enum Command {
Italic = "*",
Bold = "**",
Strikethrough = "~~",
Underline = "__",
}
import { MDXEditor } from "@mdxeditor/editor"; // Assuming react-mdx-editor or similar
import DOMPurify from "isomorphic-dompurify";
export default function NewBlog() {
const ref = useRef<HTMLTextAreaElement>(null);
const [text, setText] = useState("");
const [cursor, setCursor] = useState<{ line: number; column: number }>({
line: 0,
column: 0,
});
const [selection, setSelection] = useState<{
start: number;
end: number;
} | null>(null);
const getCursorPosition = (
event: React.ChangeEvent<HTMLTextAreaElement>,
) => {
const textarea = event.target;
const text = textarea.value;
const cursorPos = textarea.selectionStart;
const lines = text.substring(0, cursorPos).split("\n");
const line = lines.length; // Current line number (1-based)
const column = lines[lines.length - 1].length + 1; // Current column (1-based)
// Function to update the cursor position
const getCursorPosition = (newText: string) => {
const cursorPos = newText.length;
const lines = newText.substring(0, cursorPos).split("\n");
const line = lines.length; // Current line number
const column = lines[lines.length - 1].length + 1; // Current column number
setCursor({ line, column });
};
const onSelect = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
const { selectionStart, selectionEnd } = event.currentTarget;
if (selectionStart === selectionEnd) return;
setSelection({ start: selectionStart, end: selectionEnd });
};
useEffect(() => {
setSelection(null);
}, [text]);
const moveCursor = (newPos: number) => {
if (!ref.current) return;
ref.current.selectionEnd = newPos;
ref.current.focus();
};
const execCommand = (command: Command, symetry: boolean = true) => {
if (selection) {
const selectedText = text.substring(selection.start, selection.end);
const pre = text.slice(0, selection.start);
const post = text.slice(selection.end);
const newSelectedText = `${command}${selectedText}${symetry ? command : ""}`;
setText(pre + newSelectedText + post);
return;
}
const execCommand = (command: string) => {
const pre = text.slice(0, cursor.column);
const post = text.slice(cursor.column);
if (!symetry) setText(pre + command + post);
else {
const t = pre + command + command + post;
setText(t);
}
console.log(pre.length + command.length);
moveCursor(cursor.column + 2);
setText(pre + command + post);
getCursorPosition(pre + command + post); // Update cursor position after change
};
const sanitized = DOMPurify.sanitize(marked(text, { async: false }));
// Sanitize the MDX text before rendering
const sanitized = DOMPurify.sanitize(text);
return (
<section className="flex">
<div>
<section className="flex m-5">
<div className="flex-1">
<div className="flex gap-2 mb-2 border-b pb-2">
<Button
variant="outline"
size="icon"
onClick={() => execCommand(Command.Bold)}
>
<Button variant="outline" size="icon" onClick={() => execCommand("**")}>
<Bold size={16} />
</Button>
<Button
variant="outline"
size="icon"
onClick={() => execCommand(Command.Italic)}
>
<Button variant="outline" size="icon" onClick={() => execCommand("*")}>
<Italic size={16} />
</Button>
<Button
variant="outline"
size="icon"
onClick={() => execCommand(Command.Underline)}
>
<Button variant="outline" size="icon" onClick={() => execCommand("__")}>
<Underline size={16} />
</Button>
<Button
variant="outline"
size="icon"
onClick={() => execCommand(Command.Strikethrough)}
>
<Button variant="outline" size="icon" onClick={() => execCommand("~~")}>
<Strikethrough size={16} />
</Button>
{/*<Button variant="outline" size="icon" onClick={handleLink}>
<Link size={16} />
</Button> */}
</div>
<Textarea
ref={ref}
value={text}
onSelect={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
getCursorPosition(e);
onSelect(e);
}}
onChange={(e) => setText(e.currentTarget.value)}
{/* Using MDXEditor instead of a basic Textarea */}
<MDXEditor
onChange={setText}
onBlur={() => getCursorPosition(text)}
markdown="ok"
/>
<div>
Line: {cursor.line}; Column: {cursor.column}
<br />
Selection: Start {selection?.start} End {selection?.end}
</div>
</div>
<div
className="mt-4 p-2 bg-gray-100 border rounded-md text-sm text-black"
dangerouslySetInnerHTML={{
// @ts-ignore
__html: sanitized,
}}
></div>
/>
</section>
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@
},
"dependencies": {
"@hookform/resolvers": "^3.10.0",
"@mdxeditor/editor": "^3.23.2",
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-avatar": "^1.1.2",
"@radix-ui/react-checkbox": "^1.1.3",