kontenhumas-fe/components/editor/simple-editor.tsx

143 lines
3.8 KiB
TypeScript

'use client';
import { useState, useRef, useEffect } from 'react';
import { Button } from '@/components/ui/button';
import {
Bold,
Italic,
Underline,
List,
ListOrdered,
Quote,
Link,
Undo,
Redo,
Type,
AlignLeft,
AlignCenter,
AlignRight
} from 'lucide-react';
interface SimpleEditorProps {
data?: string;
onChange?: (data: string) => void;
placeholder?: string;
className?: string;
disabled?: boolean;
}
export default function SimpleEditor({
data = '',
onChange,
placeholder = 'Start typing...',
className = '',
disabled = false
}: SimpleEditorProps) {
const [content, setContent] = useState(data);
const editorRef = useRef<HTMLDivElement>(null);
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
setIsMounted(true);
}, []);
useEffect(() => {
setContent(data);
}, [data]);
const handleInput = () => {
if (editorRef.current) {
const html = editorRef.current.innerHTML;
setContent(html);
onChange?.(html);
}
};
const execCommand = (command: string, value?: string) => {
document.execCommand(command, false, value);
editorRef.current?.focus();
handleInput();
};
const insertLink = () => {
const url = prompt('Enter URL:');
if (url) {
execCommand('createLink', url);
}
};
const toolbarButtons = [
{ command: 'bold', icon: Bold, label: 'Bold' },
{ command: 'italic', icon: Italic, label: 'Italic' },
{ command: 'underline', icon: Underline, label: 'Underline' },
{ command: 'insertUnorderedList', icon: List, label: 'Bullet List' },
{ command: 'insertOrderedList', icon: ListOrdered, label: 'Numbered List' },
{ command: 'formatBlock', icon: Quote, label: 'Quote', value: 'blockquote' },
{ command: 'justifyLeft', icon: AlignLeft, label: 'Align Left' },
{ command: 'justifyCenter', icon: AlignCenter, label: 'Align Center' },
{ command: 'justifyRight', icon: AlignRight, label: 'Align Right' },
{ command: 'undo', icon: Undo, label: 'Undo' },
{ command: 'redo', icon: Redo, label: 'Redo' },
];
if (!isMounted) {
return (
<div className={`flex items-center justify-center p-8 border border-gray-200 rounded-lg ${className}`}>
<div className="text-gray-500">Loading editor...</div>
</div>
);
}
return (
<div className={`border border-gray-200 rounded-lg overflow-hidden ${className}`}>
{/* Toolbar */}
<div className="flex flex-wrap items-center gap-1 p-2 bg-gray-50 border-b border-gray-200">
{toolbarButtons.map((button) => {
const IconComponent = button.icon;
return (
<Button
key={button.command}
variant="ghost"
size="sm"
onClick={() => {
if (button.command === 'createLink') {
insertLink();
} else {
execCommand(button.command, button.value);
}
}}
className="h-8 w-8 p-0"
disabled={disabled}
title={button.label}
>
<IconComponent className="h-4 w-4" />
</Button>
);
})}
<Button
variant="ghost"
size="sm"
onClick={insertLink}
className="h-8 w-8 p-0"
disabled={disabled}
title="Insert Link"
>
<Link className="h-4 w-4" />
</Button>
</div>
{/* Editor Content */}
<div
ref={editorRef}
contentEditable={!disabled}
onInput={handleInput}
className="min-h-[200px] p-4 focus:outline-none"
style={{ minHeight: '200px' }}
dangerouslySetInnerHTML={{ __html: content }}
data-placeholder={placeholder}
suppressContentEditableWarning={true}
/>
</div>
);
}