inital working version

This commit is contained in:
Stefan Hardegger
2025-07-22 21:49:40 +02:00
parent bebb799784
commit 59d29dceaf
98 changed files with 8027 additions and 856 deletions

View File

@@ -0,0 +1,184 @@
'use client';
import { useState, useRef } from 'react';
import { Textarea } from '../ui/Input';
import Button from '../ui/Button';
interface RichTextEditorProps {
value: string;
onChange: (value: string) => void;
placeholder?: string;
error?: string;
}
export default function RichTextEditor({
value,
onChange,
placeholder = 'Write your story here...',
error
}: RichTextEditorProps) {
const [viewMode, setViewMode] = useState<'visual' | 'html'>('visual');
const [htmlValue, setHtmlValue] = useState(value);
const previewRef = useRef<HTMLDivElement>(null);
const handleVisualChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const plainText = e.target.value;
// Convert plain text to basic HTML paragraphs
const htmlContent = plainText
.split('\n\n')
.filter(paragraph => paragraph.trim())
.map(paragraph => `<p>${paragraph.replace(/\n/g, '<br>')}</p>`)
.join('\n');
onChange(htmlContent);
setHtmlValue(htmlContent);
};
const handleHtmlChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const html = e.target.value;
setHtmlValue(html);
onChange(html);
};
const getPlainText = (html: string): string => {
// Simple HTML to plain text conversion
return html
.replace(/<\/p>/gi, '\n\n')
.replace(/<br\s*\/?>/gi, '\n')
.replace(/<[^>]*>/g, '')
.replace(/\n{3,}/g, '\n\n')
.trim();
};
const formatText = (tag: string) => {
if (viewMode === 'visual') {
// For visual mode, we'll just show formatting helpers
// In a real implementation, you'd want a proper WYSIWYG editor
return;
}
const textarea = document.querySelector('textarea') as HTMLTextAreaElement;
if (!textarea) return;
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const selectedText = htmlValue.substring(start, end);
if (selectedText) {
const beforeText = htmlValue.substring(0, start);
const afterText = htmlValue.substring(end);
const formattedText = `<${tag}>${selectedText}</${tag}>`;
const newValue = beforeText + formattedText + afterText;
setHtmlValue(newValue);
onChange(newValue);
}
};
return (
<div className="space-y-2">
{/* Toolbar */}
<div className="flex items-center justify-between p-2 theme-card border theme-border rounded-t-lg">
<div className="flex items-center gap-2">
<Button
type="button"
size="sm"
variant="ghost"
onClick={() => setViewMode('visual')}
className={viewMode === 'visual' ? 'theme-accent-bg text-white' : ''}
>
Visual
</Button>
<Button
type="button"
size="sm"
variant="ghost"
onClick={() => setViewMode('html')}
className={viewMode === 'html' ? 'theme-accent-bg text-white' : ''}
>
HTML
</Button>
</div>
{viewMode === 'html' && (
<div className="flex items-center gap-1">
<Button
type="button"
size="sm"
variant="ghost"
onClick={() => formatText('strong')}
title="Bold"
>
<strong>B</strong>
</Button>
<Button
type="button"
size="sm"
variant="ghost"
onClick={() => formatText('em')}
title="Italic"
>
<em>I</em>
</Button>
<Button
type="button"
size="sm"
variant="ghost"
onClick={() => formatText('p')}
title="Paragraph"
>
P
</Button>
</div>
)}
</div>
{/* Editor */}
<div className="border theme-border rounded-b-lg overflow-hidden">
{viewMode === 'visual' ? (
<Textarea
value={getPlainText(value)}
onChange={handleVisualChange}
placeholder={placeholder}
rows={12}
className="border-0 rounded-none focus:ring-0"
/>
) : (
<Textarea
value={htmlValue}
onChange={handleHtmlChange}
placeholder="<p>Write your HTML content here...</p>"
rows={12}
className="border-0 rounded-none focus:ring-0 font-mono text-sm"
/>
)}
</div>
{/* Preview for HTML mode */}
{viewMode === 'html' && value && (
<div className="space-y-2">
<h4 className="text-sm font-medium theme-header">Preview:</h4>
<div
ref={previewRef}
className="p-4 border theme-border rounded-lg theme-card max-h-40 overflow-y-auto"
dangerouslySetInnerHTML={{ __html: value }}
/>
</div>
)}
{error && (
<p className="text-sm text-red-600 dark:text-red-400">{error}</p>
)}
<div className="text-xs theme-text">
<p>
<strong>Visual mode:</strong> Write in plain text, paragraphs will be automatically formatted.
</p>
<p>
<strong>HTML mode:</strong> Write HTML directly for advanced formatting.
Allowed tags: p, br, strong, em, ul, ol, li, h1-h6, blockquote.
</p>
</div>
</div>
);
}