Files
latosa-escrima/frontend/app/(auth)/dashboard/blogs/new/page.tsx
2025-02-11 10:12:37 +01:00

145 lines
3.7 KiB
TypeScript

"use client";
import { Textarea } from "@/components/ui/textarea";
import { useEffect, useRef, useState } from "react";
import { marked } from "marked";
import DOMPurify from "isomorphic-dompurify";
import { Button } from "@/components/ui/button";
import { Bold, Italic, Strikethrough, Underline } from "lucide-react";
enum Command {
Italic = "*",
Bold = "**",
Strikethrough = "~~",
Underline = "__",
}
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)
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 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);
};
const sanitized = DOMPurify.sanitize(marked(text, { async: false }));
return (
<section className="flex">
<div>
<div className="flex gap-2 mb-2 border-b pb-2">
<Button
variant="outline"
size="icon"
onClick={() => execCommand(Command.Bold)}
>
<Bold size={16} />
</Button>
<Button
variant="outline"
size="icon"
onClick={() => execCommand(Command.Italic)}
>
<Italic size={16} />
</Button>
<Button
variant="outline"
size="icon"
onClick={() => execCommand(Command.Underline)}
>
<Underline size={16} />
</Button>
<Button
variant="outline"
size="icon"
onClick={() => execCommand(Command.Strikethrough)}
>
<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)}
/>
<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>
);
}