@asgerami/zemenay-blog
Version:
Plug-and-play blog system for Next.js - Get a fully functional blog running in minutes with zero configuration
67 lines (66 loc) • 7 kB
JavaScript
;
"use client";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RichTextEditor = RichTextEditor;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("@tiptap/react");
const starter_kit_1 = __importDefault(require("@tiptap/starter-kit"));
const extension_placeholder_1 = __importDefault(require("@tiptap/extension-placeholder"));
const extension_image_1 = __importDefault(require("@tiptap/extension-image"));
const react_2 = require("react");
const ImageUpload_1 = require("./ImageUpload");
function RichTextEditor({ content, onChange, placeholder = "Start writing your blog post...", className = "", }) {
const [showImageUpload, setShowImageUpload] = (0, react_2.useState)(false);
const [uploadedImages, setUploadedImages] = (0, react_2.useState)([]);
const handleImageUploaded = (image, publicUrl) => {
setUploadedImages((prev) => [...prev, { image, publicUrl }]);
};
const handleImageInsert = (publicUrl, altText) => {
if (editor) {
editor.chain().focus().setImage({ src: publicUrl, alt: altText }).run();
setShowImageUpload(false);
}
};
const handleImageError = (error) => {
alert(`Image upload error: ${error}`);
};
const editor = (0, react_1.useEditor)({
extensions: [
starter_kit_1.default,
extension_placeholder_1.default.configure({
placeholder,
}),
extension_image_1.default.configure({
inline: true,
allowBase64: false,
HTMLAttributes: {
class: "editor-image",
},
}),
],
content,
onUpdate: ({ editor }) => {
onChange(editor.getHTML());
},
editorProps: {
attributes: {
class: "prose prose-sm sm:prose lg:prose-lg xl:prose-2xl mx-auto focus:outline-none min-h-[200px] p-4",
},
},
});
// Update editor content when prop changes
(0, react_2.useEffect)(() => {
if (editor && content !== editor.getHTML()) {
editor.commands.setContent(content);
}
}, [content, editor]);
if (!editor) {
return (0, jsx_runtime_1.jsx)("div", { className: "animate-pulse bg-gray-200 h-48 rounded" });
}
return ((0, jsx_runtime_1.jsxs)("div", { className: `rich-text-editor ${className}`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "editor-toolbar", children: [(0, jsx_runtime_1.jsxs)("div", { className: "toolbar-group", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => editor.chain().focus().toggleBold().run(), className: editor.isActive("bold") ? "active" : "", title: "Bold", children: (0, jsx_runtime_1.jsx)("strong", { children: "B" }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => editor.chain().focus().toggleItalic().run(), className: editor.isActive("italic") ? "active" : "", title: "Italic", children: (0, jsx_runtime_1.jsx)("em", { children: "I" }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => editor.chain().focus().toggleStrike().run(), className: editor.isActive("strike") ? "active" : "", title: "Strikethrough", children: (0, jsx_runtime_1.jsx)("s", { children: "S" }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "toolbar-group", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => editor.chain().focus().toggleHeading({ level: 1 }).run(), className: editor.isActive("heading", { level: 1 }) ? "active" : "", title: "Heading 1", children: "H1" }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => editor.chain().focus().toggleHeading({ level: 2 }).run(), className: editor.isActive("heading", { level: 2 }) ? "active" : "", title: "Heading 2", children: "H2" }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => editor.chain().focus().toggleHeading({ level: 3 }).run(), className: editor.isActive("heading", { level: 3 }) ? "active" : "", title: "Heading 3", children: "H3" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "toolbar-group", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => editor.chain().focus().toggleBulletList().run(), className: editor.isActive("bulletList") ? "active" : "", title: "Bullet List", children: "\u2022 List" }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => editor.chain().focus().toggleOrderedList().run(), className: editor.isActive("orderedList") ? "active" : "", title: "Numbered List", children: "1. List" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "toolbar-group", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => editor.chain().focus().toggleBlockquote().run(), className: editor.isActive("blockquote") ? "active" : "", title: "Quote", children: "\" Quote" }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => editor.chain().focus().toggleCodeBlock().run(), className: editor.isActive("codeBlock") ? "active" : "", title: "Code Block", children: "</>" })] }), (0, jsx_runtime_1.jsx)("div", { className: "toolbar-group", children: (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setShowImageUpload(!showImageUpload), className: showImageUpload ? "active" : "", title: "Insert Image", children: "\uD83D\uDDBC\uFE0F" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "toolbar-group", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => editor.chain().focus().undo().run(), disabled: !editor.can().undo(), title: "Undo", children: "\u21B6" }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => editor.chain().focus().redo().run(), disabled: !editor.can().redo(), title: "Redo", children: "\u21B7" })] })] }), showImageUpload && ((0, jsx_runtime_1.jsxs)("div", { className: "image-upload-panel", children: [(0, jsx_runtime_1.jsx)("h4", { children: "Upload Image" }), (0, jsx_runtime_1.jsx)(ImageUpload_1.ImageUpload, { onImageUploaded: handleImageUploaded, onError: handleImageError }), uploadedImages.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "uploaded-images", children: [(0, jsx_runtime_1.jsx)("h5", { children: "Recently Uploaded" }), uploadedImages.map(({ image, publicUrl }, index) => ((0, jsx_runtime_1.jsx)(ImageUpload_1.ImagePreview, { image: image, publicUrl: publicUrl, onInsert: () => handleImageInsert(publicUrl, image.alt_text || image.original_name), onRemove: () => {
setUploadedImages((prev) => prev.filter((_, i) => i !== index));
} }, `${image.id}-${index}`)))] }))] })), (0, jsx_runtime_1.jsx)("div", { className: "editor-content", children: (0, jsx_runtime_1.jsx)(react_1.EditorContent, { editor: editor }) })] }));
}