- Split admin.css (3228 lines) into 12 focused files: variables, base, forms, buttons, layout, components, tables, skeleton, datepicker, filemanager, pagination, responsive - Extracted shared styles from offers.css and dashboard.css into components.css and forms.css (offers-* → admin-* prefix) - Standardized naming: dash-kpi-* → admin-kpi-*, session-* → dash-session-*, rich-editor → admin-rich-editor - Deleted duplicate offers-tabs (using admin-tabs everywhere) - Deduplicated DatePicker and FileManager CSS (~360 lines removed) - Added 16 utility classes to base.css (font sizes, widths, gaps, margins) - Deleted empty admin.css Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
180 lines
3.1 KiB
TypeScript
180 lines
3.1 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="admin-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>
|
|
);
|
|
}
|