diff --git a/package-lock.json b/package-lock.json index 62927b4..d4d19ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-dropzone": "^14.3.5", + "react-icons": "^5.4.0", "react-image-file-resizer": "^0.4.8", "react-rnd": "^10.4.13", "react-window": "^1.8.10", @@ -7128,6 +7129,15 @@ "react": ">= 16.8 || 18.0.0" } }, + "node_modules/react-icons": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.4.0.tgz", + "integrity": "sha512-7eltJxgVt7X64oHh6wSWNwwbKTCtMfK35hcjvJS0yxEAhPM8oUKdS3+kqaW1vicIltw+kR2unHaa12S9pPALoQ==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-image-file-resizer": { "version": "0.4.8", "resolved": "https://registry.npmjs.org/react-image-file-resizer/-/react-image-file-resizer-0.4.8.tgz", diff --git a/package.json b/package.json index fbb6e1e..b4403e6 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-dropzone": "^14.3.5", + "react-icons": "^5.4.0", "react-image-file-resizer": "^0.4.8", "react-rnd": "^10.4.13", "react-window": "^1.8.10", diff --git a/src/App.jsx b/src/App.jsx index e12cd48..d878806 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,19 +1,20 @@ -import { useContext, useEffect, useState } from "react"; +import { useContext, useEffect } from "react"; import "./App.css"; // import Canvas from "./components/Canvas"; import WebFont from "webfontloader"; -import Header from "./components/Layouts/Header"; -import SheetRightPanel from "./components/Layouts/SheetRightPanel"; -import SheetLeftPanel from "./components/Layouts/SheetLeftPanel"; -import CanvasCapture from "./components/CanvasCapture"; -import { Toaster } from "./components/ui/toaster"; +// import Header from "./components/Layouts/Header"; +// import SheetRightPanel from "./components/Layouts/SheetRightPanel"; +// import SheetLeftPanel from "./components/Layouts/SheetLeftPanel"; +// import CanvasCapture from "./components/CanvasCapture"; +// import { Toaster } from "./components/ui/toaster"; import { Sidebar } from "./components/Layouts/LeftSidebar"; -import { Canvas } from "./components/Panel/Canvas"; -import TextPanel from "./components/Panel/TextPanel"; +// import TextPanel from "./components/Panel/TextPanel"; import { TopBar } from "./components/Panel/TopBar"; import { ActionButtons } from "./components/ActionButtons"; import EditorPanel from "./components/Panel/EditorPanel"; import CanvasContext from "./components/Context/canvasContext/CanvasContext"; +import Canvas from "./components/Canvas/Canvas"; +import ActiveObjectContext from "./components/Context/activeObject/ObjectContext"; function App() { useEffect(() => { @@ -75,7 +76,7 @@ function App() { }, []); const { selectedPanel } = useContext(CanvasContext); - const [hasSelectedObject, setHasSelectedObject] = useState(true); + const { activeObject } = useContext(ActiveObjectContext); return ( //
@@ -96,7 +97,7 @@ function App() { {selectedPanel !== "" && }
- + {activeObject && }
diff --git a/src/components/Panel/Canvas.jsx b/src/components/Canvas/Canvas.jsx similarity index 77% rename from src/components/Panel/Canvas.jsx rename to src/components/Canvas/Canvas.jsx index 283448e..c2993a3 100644 --- a/src/components/Panel/Canvas.jsx +++ b/src/components/Canvas/Canvas.jsx @@ -3,8 +3,9 @@ import { AspectRatio } from "@/components/ui/aspect-ratio"; import OpenContext from "../Context/openContext/OpenContext"; import CanvasContext from "../Context/canvasContext/CanvasContext"; import { Card, CardContent } from "../ui/card"; +import ActiveObjectContext from "../Context/activeObject/ObjectContext"; -export function Canvas() { +export default function Canvas() { const { setLeftPanelOpen, setRightPanelOpen, @@ -24,6 +25,36 @@ export function Canvas() { setScreenWidth, } = useContext(CanvasContext); + const { activeObject, setActiveObject } = useContext(ActiveObjectContext); + + useEffect(() => { + if (!canvas) return; // Ensure canvas is available + + // Event handler for mouse down + const handleMouseDown = (event) => { + const target = event.target; // Get the clicked target + const activeObject = canvas.getActiveObject(); // Get the active object + + if (target) { + if (target.type === "group") { + setActiveObject(activeObject); + } else { + setActiveObject(activeObject); + } + } else { + setActiveObject(activeObject); + } + }; + + // Attach the event listener + canvas.on("mouse:down", handleMouseDown); + + // Cleanup function to remove the event listener + return () => { + canvas.off("mouse:down", handleMouseDown); // Remove the listener on unmount + }; + }, [canvas, setActiveObject]); + useEffect(() => { import("fabric").then((fabricModule) => { window.fabric = fabricModule.fabric; @@ -127,7 +158,11 @@ export function Canvas() { }, []); return ( - + { - const [activeObject, setActiveObject] = useState(null); - return ( - - {children} - - ) -} +const ObjectProvider = ({ children }) => { + const [activeObject, setActiveObject] = useState(null); + + return ( + + {children} + + ); +}; + +export default ObjectProvider; diff --git a/src/components/Context/canvasContext/CanvasContextProvider.jsx b/src/components/Context/canvasContext/CanvasContextProvider.jsx index 6d7ef57..f808269 100644 --- a/src/components/Context/canvasContext/CanvasContextProvider.jsx +++ b/src/components/Context/canvasContext/CanvasContextProvider.jsx @@ -9,6 +9,7 @@ const CanvasContextProvider = ({ children }) => { const [screenWidth, setScreenWidth] = useState(0); const [canvasRatio, setCanvasRatio] = useState("4:3"); const [selectedPanel, setSelectedPanel] = useState(""); + const [textColor, setTextColor] = useState(null); const fabricCanvasRef = useRef(null); return ( @@ -21,6 +22,8 @@ const CanvasContextProvider = ({ children }) => { canvasHeight, canvasRatio, setCanvasRatio, + textColor, + setTextColor, selectedPanel, setSelectedPanel, setCanvasHeight, diff --git a/src/components/EachComponent/ApplyColor.jsx b/src/components/EachComponent/ApplyColor.jsx index c16e48f..662b25c 100644 --- a/src/components/EachComponent/ApplyColor.jsx +++ b/src/components/EachComponent/ApplyColor.jsx @@ -1,289 +1,358 @@ -import { useContext, useEffect, useState } from 'react' -import ActiveObjectContext from '../Context/activeObject/ObjectContext'; -import CanvasContext from '../Context/canvasContext/CanvasContext'; -import { fabric } from 'fabric'; -import { debounce } from "lodash"; -import { Separator } from '../ui/separator'; -import { Label } from '../ui/label'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'; -import { Input } from '../ui/input'; -import { Card } from '../ui/card'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs'; -import { Button } from '../ui/button'; -import CollapsibleComponent from './Customization/CollapsibleComponent'; +import { useCallback, useContext, useEffect, useState } from "react"; +import ActiveObjectContext from "../Context/activeObject/ObjectContext"; +import CanvasContext from "../Context/canvasContext/CanvasContext"; +import { fabric } from "fabric"; +import { Separator } from "../ui/separator"; +import { Label } from "../ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "../ui/select"; +import { Input } from "../ui/input"; +import { Card } from "../ui/card"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs"; +import CollapsibleComponent from "./Customization/CollapsibleComponent"; const ApplyColor = () => { - const [colorField, setColorField] = useState("fill"); - const [fillColor, setFillColor] = useState(""); - const [backgroundColor, setBackgroundColor] = useState(""); - const [gradientFillColors, setGradientFillColors] = useState({ - color1: "#f97316", - color2: "#e26286", - }); - const [colorType, setColorType] = useState("color"); // 'color' or 'gradient' - const [gradientDirection, setGradientDirection] = useState("top-to-bottom"); - // get values from context - const { activeObject } = useContext(ActiveObjectContext); - const { canvas } = useContext(CanvasContext); + const [colorField, setColorField] = useState("fill"); + const [fillColor, setFillColor] = useState(""); + const [backgroundColor, setBackgroundColor] = useState(""); + const [gradientFillColors, setGradientFillColors] = useState({ + color1: "#f97316", + color2: "#e26286", + }); + const [colorType, setColorType] = useState("color"); // 'color' or 'gradient' + const [gradientDirection, setGradientDirection] = useState("top-to-bottom"); - // to get previous values from active object - useEffect(() => { - const handleObjectStyle = (object) => { - if (object.fill) { - if (typeof object.fill === "string") { - setColorType("color"); - } else if (object.fill instanceof fabric.Gradient) { - setColorType("gradient"); - } - } + // Get values from context + const { activeObject, setActiveObject } = useContext(ActiveObjectContext); + const { canvas, setTextColor } = useContext(CanvasContext); - // Handle solid colors - if (object.fill && !object.fill.colorStops) { - setFillColor(object.fill); // Solid fill - } - if (object.backgroundColor) { - setBackgroundColor(object.backgroundColor); // Solid background color - } - - // Handle gradients - if (object.fill?.colorStops) { - setGradientFillColors({ - color1: object.fill.colorStops[0]?.color || "", - color2: object.fill.colorStops[1]?.color || "", - }); - } - }; - - const processGroupObjects = (group) => { - group._objects.forEach((obj) => { - if (obj.type === "group") { - processGroupObjects(obj); // Recursively handle nested groups - } else { - handleObjectStyle(obj); // Apply styles to child objects - } - }); - }; - - if (activeObject) { - if (activeObject.type === "group") { - processGroupObjects(activeObject); // Process all objects in the group - } else { - handleObjectStyle(activeObject); // Handle single object - } + // To get previous values from active object + useEffect(() => { + const handleObjectStyle = (object) => { + if (object.fill) { + if (typeof object.fill === "string") { + setColorType("color"); + } else if (object.fill instanceof fabric.Gradient) { + setColorType("gradient"); } - }, [activeObject, colorField]); + } - const handleSolidColorChange = debounce((newColor) => { - if (colorField === "fill") { - setFillColor(newColor) - } - else { - setBackgroundColor(newColor) - } - }, 100); + // Handle solid colors + if (object.fill && !object.fill.colorStops) { + setFillColor(object.fill); // Solid fill + } + if (object.backgroundColor) { + setBackgroundColor(object.backgroundColor); // Solid background color + } - // Common debounce handler for updating colors - const debouncedSetGradientColors = debounce((key, value) => { - if (colorField === "fill") { - setGradientFillColors((prev) => ({ - ...prev, - [key]: value, - })) - } - }, 300); // Adjust debounce delay as needed - - const handleColorChange = (key) => (e) => { - const newColor = e.target.value; - debouncedSetGradientColors(key, newColor); + // Handle gradients + if (object.fill?.colorStops) { + setGradientFillColors({ + color1: object.fill.colorStops[0]?.color || "", + color2: object.fill.colorStops[1]?.color || "", + }); + } }; - // Apply color/gradient style to the selected object on the canvas - const applyColor = () => { - const applyStyleToObject = (object, style) => { - if (colorType === "color") { - if (colorField === "fill" && object.fill !== style.fill) { - object.set("fill", style.fill); - } + const processGroupObjects = (group) => { + group._objects.forEach((obj) => { + if (obj.type === "group") { + processGroupObjects(obj); // Recursively handle nested groups + } else { + handleObjectStyle(obj); // Apply styles to child objects + } + }); + }; - if (colorField === "background" && object.backgroundColor !== style.backgroundColor) { - object.set("backgroundColor", style.backgroundColor); - } - } - - if (colorType === "gradient" && colorField === "fill") { - const width = object?.width || 0; - const height = object?.height || 0; - - const coords = { - "top-to-bottom": { x1: 0, y1: 0, x2: 0, y2: height }, - "bottom-to-top": { x1: 0, y1: height, x2: 0, y2: 0 }, - "left-to-right": { x1: 0, y1: 0, x2: width, y2: 0 }, - "right-to-left": { x1: width, y1: 0, x2: 0, y2: 0 }, - }; - - const directionCoords = coords[gradientDirection]; - const gradient = new fabric.Gradient({ - type: "linear", - gradientUnits: "pixels", - coords: directionCoords, - colorStops: [ - { offset: 0, color: gradientFillColors.color1 }, - { offset: 1, color: gradientFillColors.color2 }, - ], - }); - - object.set("fill", gradient); - } - }; - - const applyStyleRecursively = (object, style) => { - if (object.type === "group" && object._objects) { - // If the object is a group, iterate through its children - object._objects.forEach((child) => applyStyleRecursively(child, style)); - } else { - // If the object is not a group, apply the style directly - applyStyleToObject(object, style); - } - }; - - const style = { - fill: fillColor, - backgroundColor: backgroundColor, - }; - - applyStyleRecursively(activeObject, style); - - // Trigger re-render for the canvas - canvas.renderAll(); + if (activeObject) { + if (activeObject.type === "group") { + processGroupObjects(activeObject); // Process all objects in the group + } else { + handleObjectStyle(activeObject); // Handle single object + } } + }, [activeObject, colorField]); - const content = () => { - return ( -
-
-
-
- - -
+ // Apply color/gradient style to the selected object on the canvas + const applyColor = useCallback(() => { + const applyStyleToObject = (object, style) => { + if (colorType === "color") { + if (colorField === "fill" && object.fill !== style.fill) { + object.set("fill", style.fill); + } - setColorType(value)}> - - Solid Color - Gradient - + if ( + colorField === "background" && + object.backgroundColor !== style.backgroundColor + ) { + object.set("backgroundColor", style.backgroundColor); + } + } - -
- -
- handleSolidColorChange(e.target.value)} - className="w-12 h-12 p-1 rounded-md" - /> - handleSolidColorChange(e.target.value)} - className="flex-grow" - /> -
-
+ if (colorType === "gradient" && colorField === "fill") { + const width = object?.width || 0; + const height = object?.height || 0; -
-
+ const coords = { + "top-to-bottom": { x1: 0, y1: 0, x2: 0, y2: height }, + "bottom-to-top": { x1: 0, y1: height, x2: 0, y2: 0 }, + "left-to-right": { x1: 0, y1: 0, x2: width, y2: 0 }, + "right-to-left": { x1: width, y1: 0, x2: 0, y2: 0 }, + }; - -
- - -
-
- -
- - -
-
-
- -
- - -
-
-
-
-
-
+ const directionCoords = coords[gradientDirection]; + const gradient = new fabric.Gradient({ + type: "linear", + gradientUnits: "pixels", + coords: directionCoords, + colorStops: [ + { offset: 0, color: gradientFillColors.color1 }, + { offset: 1, color: gradientFillColors.color2 }, + ], + }); - -
-
- ) + object.set("fill", gradient); + } + }; + + const applyStyleRecursively = (object, style) => { + if (object.type === "group" && object._objects) { + // If the object is a group, iterate through its children + object._objects.forEach((child) => applyStyleRecursively(child, style)); + } else { + // If the object is not a group, apply the style directly + applyStyleToObject(object, style); + } + }; + + const style = { + fill: fillColor, + backgroundColor: backgroundColor, + }; + + if (activeObject) { + applyStyleRecursively(activeObject, style); + setActiveObject(activeObject); + setTextColor(style); + canvas.renderAll(); } + }, [ + activeObject, + fillColor, + backgroundColor, + gradientFillColors, + colorType, + gradientDirection, + setActiveObject, + canvas, + colorField, + setTextColor, + ]); + // Watch for changes in color-related states and apply them instantly + useEffect(() => { + applyColor(); + }, [ + applyColor, // Now included in dependencies + fillColor, + backgroundColor, + gradientFillColors, + colorType, + gradientDirection, + ]); + + const handleSolidColorChange = (newColor) => { + if (colorField === "fill") { + setFillColor(newColor); + } else { + setBackgroundColor(newColor); + } + }; + + const handleGradientColorChange = (key, value) => { + setGradientFillColors((prev) => ({ + ...prev, + [key]: value, + })); + }; + + const content = () => { return ( -
- - - {content()} - - - -
- ) -} +
+
+
+
+ + +
-export default ApplyColor \ No newline at end of file + setColorType(value)} + > + + Solid Color + + Gradient + + + + +
+ +
+ handleSolidColorChange(e.target.value)} + className="w-12 h-12 p-1 rounded-md" + /> + handleSolidColorChange(e.target.value)} + className="flex-grow" + /> +
+
+ +
+
+ + +
+ + +
+
+ +
+ + handleGradientColorChange("color1", e.target.value) + } + className="w-12 h-12 p-1 rounded-md" + /> + + handleGradientColorChange("color1", e.target.value) + } + className="flex-grow" + /> +
+
+
+ +
+ + handleGradientColorChange("color2", e.target.value) + } + className="w-12 h-12 p-1 rounded-md" + /> + + handleGradientColorChange("color2", e.target.value) + } + className="flex-grow" + /> +
+
+
+
+
+
+
+
+ ); + }; + + return ( +
+ + + {content()} + + + +
+ ); +}; + +export default ApplyColor; diff --git a/src/components/EachComponent/Customization/CollapsibleComponent.jsx b/src/components/EachComponent/Customization/CollapsibleComponent.jsx index edd626a..180835f 100644 --- a/src/components/EachComponent/Customization/CollapsibleComponent.jsx +++ b/src/components/EachComponent/Customization/CollapsibleComponent.jsx @@ -18,7 +18,7 @@ const CollapsibleComponent = ({ children, text }) => { }, [text]); return ( - +
{ const { canvas } = useContext(CanvasContext); @@ -51,8 +50,7 @@ const LockObject = () => { }; return ( - -

{!isLocked ? "Lock" : "Unlock"} Object

+
- +
); }; diff --git a/src/components/EachComponent/Customization/OpacityCustomization.jsx b/src/components/EachComponent/Customization/OpacityCustomization.jsx index e8d3c04..a1d2e84 100644 --- a/src/components/EachComponent/Customization/OpacityCustomization.jsx +++ b/src/components/EachComponent/Customization/OpacityCustomization.jsx @@ -1,46 +1,61 @@ -import ActiveObjectContext from '@/components/Context/activeObject/ObjectContext'; -import CanvasContext from '@/components/Context/canvasContext/CanvasContext'; -import { Label } from '@/components/ui/label'; -import { Slider } from '@/components/ui/slider'; -import { useContext, useEffect, useState } from 'react' +import ActiveObjectContext from "@/components/Context/activeObject/ObjectContext"; +import CanvasContext from "@/components/Context/canvasContext/CanvasContext"; +import { Label } from "@/components/ui/label"; +import { Slider } from "@/components/ui/slider"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { useContext, useEffect, useState } from "react"; +import { BsTransparency } from "react-icons/bs"; +import { Button } from "@/components/ui/button"; const OpacityCustomization = () => { - const { activeObject } = useContext(ActiveObjectContext); - const { canvas } = useContext(CanvasContext); + const { activeObject } = useContext(ActiveObjectContext); + const { canvas } = useContext(CanvasContext); + const [opacity, setOpacity] = useState(0); - const [opacity, setOpacity] = useState(0); + useEffect(() => { + if (activeObject) { + setOpacity(activeObject?.opacity); + } + }, [activeObject]); - useEffect(() => { - if (activeObject) { - setOpacity(activeObject?.opacity); - } - }, [activeObject]) + const adjustBackgroundOpacity = (newOpacity) => { + setOpacity(newOpacity); + if (activeObject) { + activeObject.set("opacity", newOpacity); + canvas.renderAll(); + } + }; - const adjustBackgroundOpacity = (value) => { - setOpacity(value); - if (activeObject) { - activeObject.set("opacity", opacity); // Update the opacity - canvas.renderAll(); // Re-render the canvas - } - }; - - return ( -
- - { - const newOpacity = value[0]; // Extract slider value - adjustBackgroundOpacity(newOpacity); // Adjust Fabric.js background opacity - }} - /> + return ( + + + + + +
+
+ + + {Math.round(opacity * 100)}% + +
+ adjustBackgroundOpacity(value[0])} + />
- ) -} +
+
+ ); +}; -export default OpacityCustomization \ No newline at end of file +export default OpacityCustomization; diff --git a/src/components/EachComponent/Customization/StrokeCustomization.jsx b/src/components/EachComponent/Customization/StrokeCustomization.jsx index cfd73e8..ccef96c 100644 --- a/src/components/EachComponent/Customization/StrokeCustomization.jsx +++ b/src/components/EachComponent/Customization/StrokeCustomization.jsx @@ -47,7 +47,7 @@ const StrokeCustomization = () => { } } if (object.strokeWidth) { - setStrokeWidth(object.strokeWidth || 0); + setStrokeWidth(0); } }; diff --git a/src/components/EachComponent/Customization/TextCustomization.jsx b/src/components/EachComponent/Customization/TextCustomization.jsx index 354c2fd..c0f545c 100644 --- a/src/components/EachComponent/Customization/TextCustomization.jsx +++ b/src/components/EachComponent/Customization/TextCustomization.jsx @@ -1,7 +1,7 @@ import ActiveObjectContext from "@/components/Context/activeObject/ObjectContext"; import CanvasContext from "@/components/Context/canvasContext/CanvasContext"; import { Button } from "@/components/ui/button"; -import { Card, CardContent } from "@/components/ui/card"; + import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { @@ -12,9 +12,12 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { Slider } from "@/components/ui/slider"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { useContext, useEffect, useState } from "react"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { useCallback, useContext, useEffect, useState } from "react"; import { AlignLeft, AlignCenter, @@ -23,8 +26,11 @@ import { Italic, Underline, Strikethrough, + Minus, + Plus, } from "lucide-react"; -import CollapsibleComponent from "./CollapsibleComponent"; +import { RiLineHeight } from "react-icons/ri"; +import { Slider } from "@/components/ui/slider"; const fonts = [ "Roboto", @@ -79,9 +85,8 @@ const fonts = [ ]; const TextCustomization = () => { - // get values from context const { activeObject } = useContext(ActiveObjectContext); - const { canvas } = useContext(CanvasContext); + const { canvas, setSelectedPanel, textColor } = useContext(CanvasContext); const [text, setText] = useState(""); const [fontFamily, setFontFamily] = useState("Arial"); @@ -93,7 +98,7 @@ const TextCustomization = () => { const [underline, setUnderline] = useState(false); const [linethrough, setLinethrough] = useState(false); const [textAlign, setTextAlign] = useState("left"); - const [previewText, setPreviewText] = useState(""); + const [fillColor, setFillColor] = useState("black"); useEffect(() => { if (activeObject?.type === "i-text") { @@ -107,18 +112,21 @@ const TextCustomization = () => { setUnderline(activeObject?.underline || false); setLinethrough(activeObject?.linethrough || false); setTextAlign(activeObject?.textAlign || "left"); - setPreviewText(activeObject?.text || ""); + setFillColor(textColor?.fill || "black"); } - }, [activeObject]); + }, [activeObject, textColor]); - const updateActiveObject = (properties) => { - if (activeObject?.type === "i-text") { - activeObject.set(properties); - canvas?.renderAll(); - } - }; + const updateActiveObject = useCallback( + (properties) => { + if (activeObject?.type === "i-text") { + activeObject.set(properties); + canvas?.renderAll(); + } + }, + [activeObject, canvas] + ); // Add dependencies - const applyChanges = () => { + const applyChanges = useCallback(() => { updateActiveObject({ text, fontFamily, @@ -131,12 +139,29 @@ const TextCustomization = () => { linethrough, textAlign, }); - }; + }, [ + text, + fontFamily, + fontSize, + fontStyle, + fontWeight, + lineHeight, + charSpacing, + underline, + linethrough, + textAlign, + updateActiveObject, // Add this dependency + ]); - const handleTextChange = (newText) => { - setText(newText); - setPreviewText(newText); - }; + // Automatically apply changes when state updates + useEffect(() => { + if (activeObject?.type === "i-text") { + applyChanges(); + } + }, [ + applyChanges, // Now included in dependencies + activeObject?.type, // Track active object type + ]); const handleFontFamilyChange = (newFontFamily) => { setFontFamily(newFontFamily); @@ -151,204 +176,217 @@ const TextCustomization = () => { }; const handleFontStyleChange = () => { - const newFontStyle = fontStyle === "normal" ? "italic" : "normal"; - setFontStyle(newFontStyle); + setFontStyle(fontStyle === "normal" ? "italic" : "normal"); }; const handleFontWeightChange = () => { - const newFontWeight = fontWeight === "normal" ? "bold" : "normal"; - setFontWeight(newFontWeight); + setFontWeight(fontWeight === "normal" ? "bold" : "normal"); }; const handleLineHeightChange = (newLineHeight) => { - setLineHeight(newLineHeight); + setLineHeight(parseFloat(newLineHeight)); }; const handleCharSpacingChange = (newCharSpacing) => { - setCharSpacing(newCharSpacing); + setCharSpacing(parseInt(newCharSpacing)); }; const handleUnderlineChange = () => { - const newUnderline = !underline; - setUnderline(newUnderline); + setUnderline(!underline); }; const handleLinethroughChange = () => { - const newLinethrough = !linethrough; - setLinethrough(newLinethrough); + setLinethrough(!linethrough); }; const content = () => { if (!(activeObject?.type === "i-text")) { - return ( -
- - -

- Select a text object to customize -

-
-
-
- ); - } else { - return ( - - - - Text - Style - - - -
- - handleTextChange(e.target.value)} - /> -
-
- - -
-
- -
- handleFontSizeChange(value)} - min={8} - max={72} - step={1} - className="flex-grow" - /> - - handleFontSizeChange(parseInt(e.target.value, 10) || 16) - } - className="w-16" - /> -
-
-
- -
- -
- - - -
-
-
- -
- - - - -
-
-
- - handleLineHeightChange(value)} - min={0.5} - max={3} - step={0.1} - /> -
-
- - handleCharSpacingChange(value)} - min={-20} - max={100} - step={1} - /> -
-
-
-
- ); + return
; } - }; - return ( - - - {content()} - -
- {previewText && ( + return ( +
+ {/* New Toolbar Design */} +
+ {/* Font Family Select */} + + + {/* Font Size Controls */} +
+ + { + const numericValue = e.target.value.replace(/\D/g, ""); + handleFontSizeChange(parseInt(numericValue) || 12); + }} + className="w-12 h-8 border-0 text-center" + /> + +
+ + {/* Vertical Separator */} +
+ + {/* Text Formatting Controls */} +
+ + + + + + + + + +
+ + {/* Vertical Separator */} +
+ + {/* Spacing Controls */} + + + + + +
+
+ +
+ handleLineHeightChange(value)} + min={0.5} + max={3} + step={0.1} + /> +
+
+
+ +
+ + handleCharSpacingChange(value) + } + min={-20} + max={100} + step={1} + /> +
+
+
+
+
+
+ + {/* Text Input */} + {/*
+ + handleTextChange(e.target.value)} + /> +
*/} + + {/* Preview */} + {/* {previewText && (

Preview:

{ {previewText}

- )} + )} */} - + */}
- - ); + ); + }; + + return
{content()}
; }; export default TextCustomization; diff --git a/src/components/Panel/ColorPanel.jsx b/src/components/Panel/ColorPanel.jsx new file mode 100644 index 0000000..e8d7900 --- /dev/null +++ b/src/components/Panel/ColorPanel.jsx @@ -0,0 +1,29 @@ +import { useContext } from "react"; +import ApplyColor from "../EachComponent/ApplyColor"; +import { Button } from "../ui/button"; +import CanvasContext from "../Context/canvasContext/CanvasContext"; +import { X } from "lucide-react"; +import { ScrollArea } from "../ui/scroll-area"; + +const ColorPanel = () => { + const { setSelectedPanel } = useContext(CanvasContext); + return ( +
+
+

Color

+ +
+ + + +
+ ); +}; + +export default ColorPanel; diff --git a/src/components/Panel/CommonPanel.jsx b/src/components/Panel/CommonPanel.jsx index 252c0c3..c319a54 100644 --- a/src/components/Panel/CommonPanel.jsx +++ b/src/components/Panel/CommonPanel.jsx @@ -5,23 +5,31 @@ import StrokeCustomization from "../EachComponent/Customization/StrokeCustomizat import PositionCustomization from "../EachComponent/Customization/PositionCustomization"; import { Card } from "../ui/card"; import CollapsibleComponent from "../EachComponent/Customization/CollapsibleComponent"; -import OpacityCustomization from "../EachComponent/Customization/OpacityCustomization"; import FlipCustomization from "../EachComponent/Customization/FlipCustomization"; import RotateCustomization from "../EachComponent/Customization/RotateCustomization"; import SkewCustomization from "../EachComponent/Customization/SkewCustomization"; import ScaleObjects from "../EachComponent/Customization/ScaleObjects"; import ShadowCustomization from "../EachComponent/Customization/ShadowCustomization"; import AddImageIntoShape from "../EachComponent/Customization/AddImageIntoShape"; +import ApplyColor from "../EachComponent/ApplyColor"; const CommonPanel = () => { const { canvas } = useContext(CanvasContext); const activeObject = canvas?.getActiveObject(); + const activeObjectType = activeObject?.type; + const hasClipPath = !!activeObject?.clipPath; const customClipPath = activeObject?.isClipPath; + return (
+ {/* Apply fill and background color */} + {activeObjectType !== "image" && !hasClipPath && !customClipPath && ( + + )} + {/* Apply stroke and stroke color */} {!customClipPath && ( <> @@ -37,9 +45,8 @@ const CommonPanel = () => { {/* Controls for opacity, flip, and rotation */} - +
-
diff --git a/src/components/Panel/EditorPanel.jsx b/src/components/Panel/EditorPanel.jsx index ab44e28..d28260b 100644 --- a/src/components/Panel/EditorPanel.jsx +++ b/src/components/Panel/EditorPanel.jsx @@ -1,6 +1,7 @@ import { useContext } from "react"; import CanvasContext from "../Context/canvasContext/CanvasContext"; import TextPanel from "./TextPanel"; +import ColorPanel from "./ColorPanel"; const EditorPanel = () => { const { selectedPanel } = useContext(CanvasContext); @@ -9,6 +10,8 @@ const EditorPanel = () => { switch (selectedPanel) { case "text": return ; + case "color": + return ; default: return; } @@ -17,7 +20,7 @@ const EditorPanel = () => { return ( <> {selectedPanel !== "" && ( -
+
{renderPanel()}
)} diff --git a/src/components/Panel/TextPanel.jsx b/src/components/Panel/TextPanel.jsx index d375311..cdcc761 100644 --- a/src/components/Panel/TextPanel.jsx +++ b/src/components/Panel/TextPanel.jsx @@ -1,21 +1,25 @@ import { Button } from "../ui/Button"; import { X } from "lucide-react"; import { ScrollArea } from "../ui/scroll-area"; -import { useContext } from "react"; +import { useContext, useEffect, useState } from "react"; import CanvasContext from "../Context/canvasContext/CanvasContext"; import ActiveObjectContext from "../Context/activeObject/ObjectContext"; import { fabric } from "fabric"; import CommonPanel from "./CommonPanel"; -import TextCustomization from "../EachComponent/Customization/TextCustomization"; export default function TextPanel() { - const { canvas } = useContext(CanvasContext); - const { setActiveObject } = useContext(ActiveObjectContext); - const activeObject = canvas?.getActiveObject(); - const activeObjectType = activeObject?.type; - const hasClipPath = !!activeObject?.clipPath; - const customClipPath = activeObject?.isClipPath; + const { canvas, setSelectedPanel } = useContext(CanvasContext); + const { activeObject, setActiveObject } = useContext(ActiveObjectContext); + const [open, setOpen] = useState(false); + + useEffect(() => { + if (activeObject) { + setOpen(true); + } else { + setOpen(false); + } + }, [activeObject]); const addText = () => { if (canvas) { const text = new fabric.IText("Editable Text", { @@ -37,12 +41,16 @@ export default function TextPanel() {

Text

-
- + - {activeObject ? ( -
- - -
- ) : ( + {!open ? (

No active object found

+ ) : ( +
+ +
)}
diff --git a/src/components/Panel/TopBar.jsx b/src/components/Panel/TopBar.jsx index a5c6b47..b278fad 100644 --- a/src/components/Panel/TopBar.jsx +++ b/src/components/Panel/TopBar.jsx @@ -1,13 +1,3 @@ -import { - AlignCenter, - AlignLeft, - AlignRight, - Bold, - Italic, - Lock, - Underline, -} from "lucide-react"; -import { Button } from "../ui/Button"; import { Select, SelectContent, @@ -15,87 +5,46 @@ import { SelectTrigger, SelectValue, } from "../ui/Select"; +import TextCustomization from "../EachComponent/Customization/TextCustomization"; +import LockObject from "../EachComponent/Customization/LockObject"; +import { ScrollArea, ScrollBar } from "../ui/scroll-area"; +import OpacityCustomization from "../EachComponent/Customization/OpacityCustomization"; +import CanvasContext from "../Context/canvasContext/CanvasContext"; +import { useContext } from "react"; -export function TopBar({ isVisible = false }) { - if (!isVisible) return null; - +export function TopBar() { + const { selectedPanel } = useContext(CanvasContext); return ( -
-
- +
+ +
+
+ +
+ +
-
- -
12
- + + +
- -
- -
- - - -
- -
- -
- - - -
- -
- - - - -
+ +
); } diff --git a/src/main.jsx b/src/main.jsx index 7b98f01..9c4bd2b 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,13 +1,12 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.jsx' -import CanvasContextProvider from './components/Context/canvasContext/CanvasContextProvider' -import ColorContextProvider from './components/Context/colorContext/ColorContextProvider' -import { ObjectProvider } from './components/Context/activeObject/ObjectProvider' -import OpenContextProvider from './components/Context/openContext/OpenContextProvider' +import { createRoot } from "react-dom/client"; +import "./index.css"; +import App from "./App.jsx"; +import CanvasContextProvider from "./components/Context/canvasContext/CanvasContextProvider"; +import ColorContextProvider from "./components/Context/colorContext/ColorContextProvider"; +import ObjectProvider from "./components/Context/activeObject/ObjectProvider"; +import OpenContextProvider from "./components/Context/openContext/OpenContextProvider"; -createRoot(document.getElementById('root')).render( +createRoot(document.getElementById("root")).render( // @@ -19,4 +18,4 @@ createRoot(document.getElementById('root')).render( // , -) +);