Files
app/src/admin/components/RichEditor.tsx
BOHA d424ef8c15 refactor(css): scope Quill editor into styled() and delete offers.css
Phase 3 of the "fully MUI" cleanup.

- New src/admin/ui/RichText.tsx exports two theme-aware styled() wrappers:
  RichEditorRoot (the .admin-rich-editor Quill overrides) and RichTextView
  (the .admin-rich-text-view read-only output). All colors resolve via
  theme.vars; picker/tooltip shadows use theme.applyStyles for the
  light/dark difference; the active-button tint uses the primary channel.
- Dropped the ~170 dead font/size picker rules from the old stylesheet —
  the editor toolbar exposes neither picker. Kept the .ql-font-* content
  classes (shared by editor + view) so legacy stored HTML still renders.
- RichEditor uses <RichEditorRoot> (min-height still via --re-min-height);
  InvoiceDetail + OrderDetail use <RichTextView> for the sanitized output.
- offers.css deleted; its import removed from AdminApp.

tsc -b --noEmit and npm run build clean. Verified the editor in Chrome
(light + dark): toolbar, editor surface, text, icon strokes and min-height
all correct and theme-reactive.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 17:37:11 +02:00

124 lines
2.4 KiB
TypeScript

import { useMemo, useRef, useCallback, useLayoutEffect } from "react";
import ReactQuill from "react-quill-new";
import "react-quill-new/dist/quill.snow.css";
import { RichEditorRoot } from "../ui/RichText";
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 = [
["bold", "italic", "underline", "strike"],
[{ color: COLORS }, { background: COLORS }],
[{ list: "ordered" }, { list: "bullet" }],
[{ indent: "-1" }, { indent: "+1" }],
[{ align: [] }],
["link"],
["clean"],
];
const FORMATS = [
"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],
);
useLayoutEffect(() => {
if (!quillRef.current) return;
const editor = quillRef.current.getEditor();
editor.root.style.fontFamily = "Tahoma, sans-serif";
editor.root.style.fontSize = "14px";
}, []);
return (
<RichEditorRoot
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}
/>
</RichEditorRoot>
);
}