initial commit
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
105
src/admin/components/RichEditor.tsx
Normal file
105
src/admin/components/RichEditor.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import { useMemo, useRef, useCallback } from 'react'
|
||||
import ReactQuill from 'react-quill-new'
|
||||
import 'react-quill-new/dist/quill.snow.css'
|
||||
|
||||
const Quill = ReactQuill.Quill
|
||||
|
||||
if (!(Quill as any).__bohaRegistered) {
|
||||
const Font = Quill.import('attributors/class/font') as any
|
||||
Font.whitelist = [
|
||||
'arial', 'tahoma', 'verdana', 'georgia', 'times-new-roman',
|
||||
'courier-new', 'trebuchet-ms', 'impact', 'comic-sans-ms',
|
||||
'lucida-console', 'palatino-linotype', 'garamond'
|
||||
]
|
||||
Quill.register(Font, true)
|
||||
|
||||
const SizeStyle = Quill.import('attributors/style/size') as any
|
||||
SizeStyle.whitelist = [
|
||||
'8px', '9px', '10px', '11px', '12px', '14px', '16px',
|
||||
'18px', '20px', '24px', '28px', '32px', '36px', '48px'
|
||||
]
|
||||
Quill.register(SizeStyle, true)
|
||||
;(Quill as any).__bohaRegistered = true
|
||||
}
|
||||
|
||||
const Font = Quill.import('attributors/class/font') as any
|
||||
const SIZE_WHITELIST = [
|
||||
'8px', '9px', '10px', '11px', '12px', '14px', '16px',
|
||||
'18px', '20px', '24px', '28px', '32px', '36px', '48px'
|
||||
]
|
||||
|
||||
const COLORS = [
|
||||
'#000000', '#1a1a1a', '#333333', '#555555', '#777777', '#999999', '#bbbbbb', '#dddddd', '#ffffff',
|
||||
'#de3a3a', '#e57373', '#c62828',
|
||||
'#1565c0', '#42a5f5', '#0d47a1',
|
||||
'#2e7d32', '#66bb6a', '#1b5e20',
|
||||
'#f57f17', '#ffca28', '#e65100',
|
||||
'#6a1b9a', '#ab47bc', '#4a148c',
|
||||
'#00695c', '#26a69a', '#004d40',
|
||||
'#37474f', '#78909c', '#263238',
|
||||
]
|
||||
|
||||
const TOOLBAR = [
|
||||
[{ font: Font.whitelist }],
|
||||
[{ size: SIZE_WHITELIST }],
|
||||
['bold', 'italic', 'underline', 'strike'],
|
||||
[{ color: COLORS }, { background: COLORS }],
|
||||
[{ list: 'ordered' }, { list: 'bullet' }],
|
||||
[{ indent: '-1' }, { indent: '+1' }],
|
||||
[{ align: [] }],
|
||||
['link'],
|
||||
['clean']
|
||||
]
|
||||
|
||||
const FORMATS = [
|
||||
'font', 'size',
|
||||
'bold', 'italic', 'underline', 'strike',
|
||||
'color', 'background',
|
||||
'list', 'indent', 'align',
|
||||
'link'
|
||||
]
|
||||
|
||||
interface RichEditorProps {
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
placeholder?: string
|
||||
minHeight?: string
|
||||
}
|
||||
|
||||
export default function RichEditor({
|
||||
value,
|
||||
onChange,
|
||||
placeholder = 'Obsah...',
|
||||
minHeight = '120px'
|
||||
}: RichEditorProps) {
|
||||
const quillRef = useRef<ReactQuill>(null)
|
||||
const lastValueRef = useRef(value)
|
||||
|
||||
const modules = useMemo(() => ({
|
||||
toolbar: TOOLBAR,
|
||||
clipboard: {
|
||||
matchVisual: false,
|
||||
},
|
||||
}), [])
|
||||
|
||||
const handleChange = useCallback((content: string, _delta: any, source: string) => {
|
||||
if (source !== 'user') return
|
||||
if (content === lastValueRef.current) return
|
||||
lastValueRef.current = content
|
||||
onChange(content)
|
||||
}, [onChange])
|
||||
|
||||
return (
|
||||
<div className="rich-editor" style={{ '--re-min-height': minHeight } as React.CSSProperties}>
|
||||
<ReactQuill
|
||||
ref={quillRef}
|
||||
theme="snow"
|
||||
value={value || ''}
|
||||
onChange={handleChange}
|
||||
modules={modules}
|
||||
formats={FORMATS}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user