feat(nextjs): Icon editor & refactor image editor

This commit is contained in:
shiva raj badu 2025-07-18 15:20:32 +05:45
parent 83dab9346b
commit 5798bcdbdf
4 changed files with 16 additions and 87 deletions

View file

@ -104,6 +104,7 @@ const IconsEditor = ({
side="right"
className="w-[400px]"
onOpenAutoFocus={(e) => e.preventDefault()}
onClick={(e) => e.stopPropagation()}
>
<SheetHeader>
<SheetTitle>Choose Icon</SheetTitle>
@ -112,6 +113,7 @@ const IconsEditor = ({
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
handleIconSearch();
}}
>
@ -122,6 +124,7 @@ const IconsEditor = ({
placeholder="Search icons..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onClick={(e) => e.stopPropagation()}
className="pl-10"
/>
</div>
@ -129,6 +132,7 @@ const IconsEditor = ({
type="submit"
variant="outline"
className="w-full text-semibold text-[#51459e]"
onClick={(e) => e.stopPropagation()}
>
Search
</Button>
@ -147,7 +151,10 @@ const IconsEditor = ({
{icons.map((iconSrc, idx) => (
<div
key={idx}
onClick={() => handleIconChange(iconSrc)}
onClick={(e) => {
e.stopPropagation();
handleIconChange(iconSrc);
}}
className="w-12 h-12 cursor-pointer group relative rounded-lg overflow-hidden hover:bg-gray-100 p-2"
>
<img

View file

@ -12,9 +12,8 @@ import { Textarea } from "@/components/ui/textarea";
import {
Wand2,
Upload,
Edit,
Move,
Maximize,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { useDispatch, useSelector } from "react-redux";
@ -28,20 +27,14 @@ import {
} from "@/store/slices/presentationGeneration";
import { getStaticFileUrl, ThemeImagePrompt } from "../utils/others";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import ToolTip from "@/components/ToolTip";
interface ImageEditorProps {
initialImage: string | null;
imageIdx?: number;
title: string;
slideIndex: number;
elementId: string;
className?: string;
promptContent?: string;
properties?: null | any;
@ -51,10 +44,7 @@ interface ImageEditorProps {
const ImageEditor = ({
initialImage,
imageIdx = 0,
className,
title,
slideIndex,
elementId,
promptContent,
properties,
onClose,
@ -66,9 +56,6 @@ const ImageEditor = ({
const searchParams = useSearchParams();
const [image, setImage] = useState(initialImage);
const [previewImages, setPreviewImages] = useState([initialImage]);
const [isEditorOpen, setIsEditorOpen] = useState(false);
const [isToolbarOpen, setIsToolbarOpen] = useState(false);
const [prompt, setPrompt] = useState<string>("");
const [isGenerating, setIsGenerating] = useState(false);
const [error, setError] = useState<string | null>(null);
@ -110,7 +97,7 @@ const ImageEditor = ({
!toolbarRef.current.contains(event.target as Node) &&
!popoverContentRef.current
) {
setIsToolbarOpen(false);
if (isFocusPointMode) {
// saveFocusPoint(); // Save focus point before closing
saveImageProperties(objectFit, focusPoint);
@ -125,16 +112,7 @@ const ImageEditor = ({
};
}, [isFocusPointMode, focusPoint]);
const handleImageClick = () => {
if (!isFocusPointMode) {
setIsToolbarOpen(true);
}
};
const handleOpenEditor = () => {
setIsToolbarOpen(false);
setIsEditorOpen(true);
};
const handleImageChange = (newImage: string) => {
setImage(newImage);
@ -145,7 +123,6 @@ const ImageEditor = ({
image: newImage,
})
);
setIsEditorOpen(false);
};
const handleFocusPointClick = (e: React.MouseEvent) => {
@ -600,7 +577,7 @@ const ImageEditor = ({
className="cursor-pointer group w-full h-full"
>
<img
src={getStaticFileUrl(uploadedImageUrl)}
src={uploadedImageUrl}
alt="Uploaded preview"
className="w-full h-full object-cover group-hover:scale-105 transition-transform"
/>

View file

@ -70,9 +70,7 @@ export const SmartEditableProvider: React.FC<SmartEditableProviderProps> = ({
dataPath: path,
props: {
slideIndex,
elementId: `image-${path.replace(/[^\w]/g, '-')}`,
initialImage: data.__image_url__,
title: imgElement.alt || 'Image',
promptContent: data.__image_prompt__ || '',
imageIdx: elements.filter(e => e.type === 'image').length
}
@ -155,55 +153,6 @@ export const SmartEditableProvider: React.FC<SmartEditableProviderProps> = ({
return getFilename(domSrc) === getFilename(dataSrc) && getFilename(domSrc) !== '';
};
// Add event delegation for clicks
const handleClick = (event: MouseEvent) => {
const target = event.target as HTMLElement;
if (target.tagName === 'IMG') {
const imgElement = target as HTMLImageElement;
const editableElement = editableElements.find(el => el.element === imgElement);
if (editableElement) {
event.preventDefault();
event.stopPropagation();
const rect = imgElement.getBoundingClientRect();
setActiveEditor({
type: editableElement.type,
element: imgElement,
props: editableElement.props,
rect
});
}
}
};
// Add hover effects
const handleMouseEnter = (event: MouseEvent) => {
const target = event.target as HTMLElement;
if (target.tagName === 'IMG') {
const imgElement = target as HTMLImageElement;
const isEditable = editableElements.some(el => el.element === imgElement);
if (isEditable) {
imgElement.style.cursor = 'pointer';
imgElement.style.filter = 'brightness(0.9)';
imgElement.style.transition = 'filter 0.2s ease';
}
}
};
const handleMouseLeave = (event: MouseEvent) => {
const target = event.target as HTMLElement;
if (target.tagName === 'IMG') {
const imgElement = target as HTMLImageElement;
const isEditable = editableElements.some(el => el.element === imgElement);
if (isEditable) {
imgElement.style.filter = '';
}
}
};
// Set up event listeners after elements are found
const timer = setTimeout(() => {
findEditableElements();
@ -211,11 +160,8 @@ export const SmartEditableProvider: React.FC<SmartEditableProviderProps> = ({
return () => {
clearTimeout(timer);
container.removeEventListener('click', handleClick);
container.removeEventListener('mouseenter', handleMouseEnter, true);
container.removeEventListener('mouseleave', handleMouseLeave, true);
};
}, [slideIndex, slideId, slideData, isEditMode, editableElements]);
}, [slideIndex, slideId, slideData, isEditMode]);
// Set up event listeners when editableElements change
useEffect(() => {
@ -298,7 +244,7 @@ export const SmartEditableProvider: React.FC<SmartEditableProviderProps> = ({
);
};
// Simple overlay component for editors
// overlay component for editors
const EditorOverlay: React.FC<{
activeEditor: {
type: 'image' | 'icon';
@ -320,7 +266,6 @@ const EditorOverlay: React.FC<{
onClose();
}
};
document.addEventListener('keydown', handleEscape);
document.addEventListener('click', handleClickOutside);

View file

@ -35,7 +35,7 @@ export async function POST(request: NextRequest) {
// Return the relative path that can be used in the frontend
return NextResponse.json({
success: true,
filePath: `/app/user_data/uploads/${filename}`
filePath: `${userDataDir}/uploads/${filename}`
});
} catch (error) {
console.error("Error saving image:", error);