This commit is contained in:
Tomas Dvorak
2025-10-17 17:39:11 +02:00
parent 35d0954afd
commit e9a63073e5
61 changed files with 3824 additions and 1061 deletions
@@ -71,7 +71,6 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
}) => {
const toast = useToast();
const quillRef = useRef<ReactQuill | null>(null);
const [editorMode, setEditorMode] = useState<'rich' | 'html'>('rich');
// Crop modal state
const [cropOpen, setCropOpen] = useState(false);
@@ -113,8 +112,8 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
[{ color: [] }, { background: [] }],
[{ list: 'ordered' }, { list: 'bullet' }],
[{ align: [] }],
['link', 'image', 'video'],
['blockquote', 'code-block'],
['link', 'image'],
['blockquote'],
['clean'],
],
basic: [
@@ -369,11 +368,22 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
}
}
// Show toolbar and position it
// Show toolbar and position it above the image
const rect = img.getBoundingClientRect();
const editorRect = editor.root.getBoundingClientRect();
const scrollTop = editor.root.scrollTop;
const toolbarHeight = 400; // Approximate toolbar height
// Calculate position relative to editor, accounting for scroll
let topPos = rect.top - editorRect.top + scrollTop - 60;
// If toolbar would go above visible area, position it below the image
if (topPos < scrollTop) {
topPos = rect.bottom - editorRect.top + scrollTop + 10;
}
setToolbarPosition({
top: rect.top - editorRect.top - 50,
top: topPos,
left: rect.left - editorRect.left,
});
setShowImageToolbar(true);
@@ -533,55 +543,52 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
return (
<Box>
{/* Editor Controls */}
{!readOnly && (
<HStack mb={2} spacing={2} justify="space-between" flexWrap="wrap">
<ButtonGroup size="sm" isAttached variant="outline">
<Button
leftIcon={<Type size={16} />}
variant={editorMode === 'rich' ? 'solid' : 'outline'}
colorScheme={editorMode === 'rich' ? 'blue' : 'gray'}
onClick={() => setEditorMode('rich')}
>
Editor
</Button>
<Button
leftIcon={<Code size={16} />}
variant={editorMode === 'html' ? 'solid' : 'outline'}
colorScheme={editorMode === 'html' ? 'blue' : 'gray'}
onClick={() => setEditorMode('html')}
>
HTML
</Button>
</ButtonGroup>
{editorMode === 'rich' && onImageUpload && (
<Button
size="sm"
leftIcon={<ImageIcon size={16} />}
colorScheme="purple"
onClick={handleImageUpload}
>
Vložit obrázek
</Button>
)}
{!readOnly && onImageUpload && (
<HStack mb={2} spacing={2} justify="flex-start" flexWrap="wrap">
<Button
size="sm"
leftIcon={<ImageIcon size={16} />}
colorScheme="purple"
onClick={handleImageUpload}
>
Vložit obrázek
</Button>
<Text fontSize="xs" color="gray.500">
nebo použijte tlačítko obrázku v nástrojové liště
</Text>
</HStack>
)}
{editorMode === 'rich' ? (
<Box
position="relative"
borderWidth="1px"
borderColor={borderColor}
borderRadius="md"
overflow="hidden"
bg={bgColor}
sx={{
<Box
position="relative"
borderWidth="1px"
borderColor={borderColor}
borderRadius="md"
overflow="hidden"
bg={bgColor}
sx={{
'.ql-toolbar': {
borderBottom: '1px solid',
borderColor: borderColor,
bg: hoverBg,
display: 'flex',
flexWrap: 'wrap',
gap: '4px',
padding: '12px',
'& button': {
color: 'gray.700 !important',
width: '32px !important',
height: '32px !important',
borderRadius: '6px',
transition: 'all 0.2s',
'&:hover': {
background: 'rgba(49, 130, 206, 0.1) !important',
transform: 'scale(1.05)',
},
'&.ql-active': {
background: 'rgba(49, 130, 206, 0.2) !important',
color: '#3182ce !important',
},
},
'& .ql-stroke': {
stroke: 'gray.700 !important',
@@ -589,6 +596,29 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
'& .ql-fill': {
fill: 'gray.700 !important',
},
'& .ql-active .ql-stroke': {
stroke: '#3182ce !important',
},
'& .ql-active .ql-fill': {
fill: '#3182ce !important',
},
'& .ql-picker': {
color: 'gray.700 !important',
},
'& .ql-picker-label': {
borderRadius: '6px',
padding: '4px 8px',
transition: 'all 0.2s',
'&:hover': {
background: 'rgba(49, 130, 206, 0.1) !important',
},
},
'& .ql-picker-options': {
background: 'white',
borderRadius: '8px',
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
padding: '8px',
},
},
'.ql-container': {
fontSize: '16px',
@@ -601,6 +631,8 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
overflowY: 'auto',
bg: 'white !important',
color: 'gray.800 !important',
padding: '16px',
lineHeight: '1.6',
'&::-webkit-scrollbar': {
width: '8px',
},
@@ -611,6 +643,27 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
bg: 'gray.400',
borderRadius: '4px',
},
'h1': {
fontSize: '2em !important',
fontWeight: 'bold !important',
marginTop: '0.67em !important',
marginBottom: '0.67em !important',
lineHeight: '1.2 !important',
},
'h2': {
fontSize: '1.5em !important',
fontWeight: 'bold !important',
marginTop: '0.83em !important',
marginBottom: '0.83em !important',
lineHeight: '1.3 !important',
},
'h3': {
fontSize: '1.17em !important',
fontWeight: 'bold !important',
marginTop: '1em !important',
marginBottom: '1em !important',
lineHeight: '1.4 !important',
},
img: {
cursor: 'pointer',
maxWidth: '100%',
@@ -652,26 +705,8 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
}}
/>
</Box>
) : (
<Box
as="textarea"
value={value}
onChange={(e: any) => onChange(e.target.value)}
fontFamily="mono"
fontSize="sm"
p={4}
borderWidth="1px"
borderColor={borderColor}
borderRadius="md"
bg={bgColor}
resize="vertical"
minH={height}
maxH="70vh"
width="100%"
/>
)}
{!readOnly && editorMode === 'rich' && (
{!readOnly && (
<Text fontSize="xs" color="gray.500" mt={2}>
💡 Tip: Klikněte na obrázek pro výběr a úpravu. Používejte nástrojovou lištu pro filtry a transformace.
</Text>
@@ -684,14 +719,16 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
top={`${toolbarPosition.top}px`}
left={`${toolbarPosition.left}px`}
bg={toolbarBg}
borderWidth="1px"
borderColor={toolbarBorder}
borderWidth="2px"
borderColor="blue.400"
borderRadius="lg"
boxShadow="lg"
p={3}
zIndex={1500}
minW="320px"
maxW="400px"
boxShadow="2xl"
p={4}
zIndex={9999}
minW="340px"
maxW="420px"
pointerEvents="auto"
onClick={(e) => e.stopPropagation()}
>
<VStack align="stretch" spacing={3}>
{/* Toolbar Header */}
@@ -894,7 +931,7 @@ const CustomRichEditor: React.FC<CustomRichEditorProps> = ({
>
<img
ref={imgRef as any}
src={cropSrc}
src={cropSrc || ''}
alt="Crop preview"
style={{
maxWidth: '100%',