Files
app/src/admin/components/RichEditor.tsx
BOHA 9e6ce4359a fix: use RichEditor with readOnly prop instead of raw HTML for locked/invalidated offers
RichEditor now supports readOnly prop — hides toolbar and disables
editing via ReactQuill's built-in readOnly. Content renders with
proper Quill CSS (list margins, indentation, fonts) instead of
broken browser defaults from dangerouslySetInnerHTML.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 11:23:34 +01:00

109 lines
2.9 KiB
TypeScript

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
readOnly?: boolean
}
export default function RichEditor({
value,
onChange,
placeholder = 'Obsah...',
minHeight = '120px',
readOnly = false,
}: RichEditorProps) {
const quillRef = useRef<ReactQuill>(null)
const lastValueRef = useRef(value)
const modules = useMemo(() => ({
toolbar: readOnly ? false : TOOLBAR,
clipboard: {
matchVisual: false,
},
}), [readOnly])
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}
readOnly={readOnly}
/>
</div>
)
}