mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 10:42:57 +00:00
dev day #75
This commit is contained in:
@@ -78,6 +78,7 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
|
||||
const onChangeRef = useRef(onChange);
|
||||
const selectedImageIdRef = useRef<string | null>(null);
|
||||
const selectImageByIdRef = useRef<(id: string) => void>(() => {});
|
||||
const toolbarDragRef = useRef<{ active: boolean; startX: number; startY: number; startLeft: number; startTop: number }>({ active: false, startX: 0, startY: 0, startLeft: 0, startTop: 0 });
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
|
||||
// Ensure component is mounted before rendering Quill
|
||||
@@ -401,6 +402,7 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
|
||||
let startX = 0;
|
||||
let startY = 0;
|
||||
let startWidth = 0;
|
||||
let rafId = 0;
|
||||
|
||||
const createResizeHandle = (img: HTMLImageElement) => {
|
||||
removeResizeHandle();
|
||||
@@ -672,23 +674,13 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
|
||||
const scrollTop = editor.root.scrollTop;
|
||||
const scrollLeft = editor.root.scrollLeft;
|
||||
|
||||
// Position toolbar to the right of the image, or left if not enough space
|
||||
// Place toolbar close to the image (top-right corner inside the image area when possible)
|
||||
const toolbarWidth = 380;
|
||||
const spaceOnRight = window.innerWidth - rect.right;
|
||||
const positionRight = spaceOnRight > toolbarWidth + 20;
|
||||
|
||||
let leftPos = positionRight
|
||||
? rect.right - editorRect.left + scrollLeft + 10
|
||||
: rect.left - editorRect.left + scrollLeft - toolbarWidth - 10;
|
||||
|
||||
// Ensure toolbar is visible horizontally
|
||||
leftPos = Math.max(10, Math.min(leftPos, editorRect.width - toolbarWidth - 10));
|
||||
|
||||
// Position vertically aligned with top of image
|
||||
let topPos = rect.top - editorRect.top + scrollTop;
|
||||
|
||||
// Ensure toolbar is visible vertically
|
||||
topPos = Math.max(10, topPos);
|
||||
const margin = 8;
|
||||
let leftPos = rect.left - editorRect.left + scrollLeft + (rect.width > toolbarWidth + margin ? (rect.width - toolbarWidth - margin) : margin);
|
||||
leftPos = Math.max(margin, Math.min(leftPos, editorRect.width - toolbarWidth - margin));
|
||||
let topPos = rect.top - editorRect.top + scrollTop + margin;
|
||||
topPos = Math.max(margin, topPos);
|
||||
|
||||
setToolbarPosition({
|
||||
top: topPos,
|
||||
@@ -895,16 +887,18 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
|
||||
|
||||
// Handle scroll to update resize handle position
|
||||
const handleScroll = () => {
|
||||
if (selectedImage && resizeHandle) {
|
||||
const rect = selectedImage.getBoundingClientRect();
|
||||
if (!selectedImage || !resizeHandle) return;
|
||||
if (rafId) cancelAnimationFrame(rafId);
|
||||
rafId = requestAnimationFrame(() => {
|
||||
const rect = selectedImage!.getBoundingClientRect();
|
||||
const editorRect = editor.root.getBoundingClientRect();
|
||||
const scrollTop = editor.root.scrollTop;
|
||||
const scrollLeft = editor.root.scrollLeft;
|
||||
resizeHandle.style.left = `${rect.left - editorRect.left + scrollLeft}px`;
|
||||
resizeHandle.style.top = `${rect.top - editorRect.top + scrollTop}px`;
|
||||
resizeHandle.style.width = `${rect.width}px`;
|
||||
resizeHandle.style.height = `${rect.height}px`;
|
||||
}
|
||||
resizeHandle!.style.left = `${rect.left - editorRect.left + scrollLeft}px`;
|
||||
resizeHandle!.style.top = `${rect.top - editorRect.top + scrollTop}px`;
|
||||
resizeHandle!.style.width = `${rect.width}px`;
|
||||
resizeHandle!.style.height = `${rect.height}px`;
|
||||
});
|
||||
};
|
||||
|
||||
// Prevent default drag behavior on images
|
||||
@@ -934,6 +928,7 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
|
||||
document.removeEventListener('keydown', handleKeyDown);
|
||||
window.removeEventListener('resize', handleScroll);
|
||||
document.removeEventListener('scroll', handleScroll, true);
|
||||
if (rafId) cancelAnimationFrame(rafId);
|
||||
removeResizeHandle();
|
||||
deselectImage();
|
||||
};
|
||||
@@ -1047,8 +1042,6 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
|
||||
const editor = quillRef.current?.getEditor();
|
||||
if (editor) {
|
||||
onChangeRef.current(editor.root.innerHTML);
|
||||
// Force overlay reposition
|
||||
try { editor.root.dispatchEvent(new Event('scroll')); } catch {}
|
||||
}
|
||||
reselectAfterContentUpdate();
|
||||
|
||||
@@ -1111,7 +1104,6 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
|
||||
setManualWidth(finalWidth.toString());
|
||||
if (editor) {
|
||||
onChangeRef.current(editor.root.innerHTML);
|
||||
try { editor.root.dispatchEvent(new Event('scroll')); } catch {}
|
||||
}
|
||||
// Keep selection active for subsequent operations (e.g., 50% → 75%)
|
||||
reselectAfterContentUpdate();
|
||||
@@ -1132,7 +1124,6 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
|
||||
setManualWidth('');
|
||||
if (editor) {
|
||||
onChangeRef.current(editor.root.innerHTML);
|
||||
try { editor.root.dispatchEvent(new Event('scroll')); } catch {}
|
||||
}
|
||||
reselectAfterContentUpdate();
|
||||
toast({ title: 'Šířka resetována', status: 'info', duration: 1200 });
|
||||
@@ -1190,7 +1181,7 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
|
||||
let cleaned = DOMPurify.sanitize(content, {
|
||||
USE_PROFILES: { html: true },
|
||||
ADD_TAGS: ['iframe'],
|
||||
ADD_ATTR: ['target', 'rel', 'allow', 'allowfullscreen', 'style', 'data-filters'],
|
||||
ADD_ATTR: ['target', 'rel', 'allow', 'allowfullscreen', 'style', 'data-filters', 'data-img-id'],
|
||||
});
|
||||
|
||||
// Replace white and very light colors with dark colors for visibility
|
||||
@@ -1236,7 +1227,7 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
|
||||
borderWidth="1px"
|
||||
borderColor={borderColor}
|
||||
borderRadius="md"
|
||||
overflow="hidden"
|
||||
overflow="visible"
|
||||
bg={bgColor}
|
||||
sx={{
|
||||
'.ql-toolbar': {
|
||||
@@ -1477,7 +1468,33 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
|
||||
>
|
||||
<VStack align="stretch" spacing={3}>
|
||||
{/* Toolbar Header */}
|
||||
<HStack justify="space-between">
|
||||
<HStack
|
||||
justify="space-between"
|
||||
onMouseDown={(e) => {
|
||||
if (e.button !== 0) return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toolbarDragRef.current.active = true;
|
||||
toolbarDragRef.current.startX = e.clientX;
|
||||
toolbarDragRef.current.startY = e.clientY;
|
||||
toolbarDragRef.current.startLeft = toolbarPosition.left;
|
||||
toolbarDragRef.current.startTop = toolbarPosition.top;
|
||||
const onMove = (ev: MouseEvent) => {
|
||||
if (!toolbarDragRef.current.active) return;
|
||||
const dx = ev.clientX - toolbarDragRef.current.startX;
|
||||
const dy = ev.clientY - toolbarDragRef.current.startY;
|
||||
setToolbarPosition((pos) => ({ top: Math.max(0, toolbarDragRef.current.startTop + dy), left: Math.max(0, toolbarDragRef.current.startLeft + dx) }));
|
||||
};
|
||||
const onUp = () => {
|
||||
toolbarDragRef.current.active = false;
|
||||
document.removeEventListener('mousemove', onMove);
|
||||
document.removeEventListener('mouseup', onUp);
|
||||
};
|
||||
document.addEventListener('mousemove', onMove);
|
||||
document.addEventListener('mouseup', onUp);
|
||||
}}
|
||||
cursor="move"
|
||||
>
|
||||
<HStack spacing={2}>
|
||||
<Settings size={16} />
|
||||
<Text fontWeight="bold" fontSize="sm">Úprava obrázku</Text>
|
||||
|
||||
Reference in New Issue
Block a user