From 1cf062f326ed55b9d84479ae75bdcc1a6adff727 Mon Sep 17 00:00:00 2001 From: smfahim25 Date: Sun, 2 Feb 2025 16:37:06 +0600 Subject: [PATCH] added all panel and solved canvas issue when claer and also added zoom functionallity --- src/App.css | 5 + src/App.jsx | 2 + src/components/ActionButtons.jsx | 9 +- src/components/Canvas/Canvas.jsx | 227 +++++-- src/components/CanvasSetting.jsx | 573 +++++++++--------- .../EachComponent/CustomShape/CustomShape.jsx | 111 ++-- .../Customization/LockObject.jsx | 30 +- .../Customization/OpacityCustomization.jsx | 52 +- .../EachComponent/Icons/AllIcons.jsx | 230 +++---- .../RoundedShapes/RoundedShape.jsx | 160 ++--- .../EachComponent/Shapes/PlainShapes.jsx | 446 +++++++++----- src/components/EachComponent/UploadImage.jsx | 562 ++++++++--------- src/components/ObjectShortcut.jsx | 1 + src/components/Panel/CanvasPanel.jsx | 29 + src/components/Panel/CommonPanel.jsx | 36 -- src/components/Panel/EditorPanel.jsx | 30 + src/components/Panel/FlipPanel.jsx | 39 ++ src/components/Panel/GroupObjectPanel.jsx | 36 ++ src/components/Panel/IconPanel.jsx | 30 + src/components/Panel/ImagePanel.jsx | 29 + src/components/Panel/PositionPanel.jsx | 29 + src/components/Panel/ShadowPanel.jsx | 29 + src/components/Panel/ShapePanel.jsx | 47 ++ src/components/Panel/StrokePanel.jsx | 12 +- src/components/Panel/TopBar.jsx | 165 ++++- src/components/Panel/UploadPanel.jsx | 30 + 26 files changed, 1875 insertions(+), 1074 deletions(-) create mode 100644 src/components/Panel/CanvasPanel.jsx create mode 100644 src/components/Panel/FlipPanel.jsx create mode 100644 src/components/Panel/GroupObjectPanel.jsx create mode 100644 src/components/Panel/IconPanel.jsx create mode 100644 src/components/Panel/ImagePanel.jsx create mode 100644 src/components/Panel/PositionPanel.jsx create mode 100644 src/components/Panel/ShadowPanel.jsx create mode 100644 src/components/Panel/ShapePanel.jsx create mode 100644 src/components/Panel/UploadPanel.jsx diff --git a/src/App.css b/src/App.css index 55d480c..3bd0fa3 100644 --- a/src/App.css +++ b/src/App.css @@ -16,3 +16,8 @@ -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ } + +.\[\&_svg\]\:size-4 svg { + width: 1.3rem !important; + height: 1.3rem !important; +} diff --git a/src/App.jsx b/src/App.jsx index d878806..a2b196e 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -15,6 +15,7 @@ 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"; +import CanvasCapture from "./components/CanvasCapture"; function App() { useEffect(() => { @@ -100,6 +101,7 @@ function App() { {activeObject && } + ); diff --git a/src/components/ActionButtons.jsx b/src/components/ActionButtons.jsx index d4e6491..ea45bed 100644 --- a/src/components/ActionButtons.jsx +++ b/src/components/ActionButtons.jsx @@ -1,4 +1,5 @@ import CanvasContext from "./Context/canvasContext/CanvasContext"; +import OpenContext from "./Context/openContext/OpenContext"; import { Button } from "./ui/button"; import { Select, @@ -28,6 +29,7 @@ const aspectRatios = [ ]; export function ActionButtons() { + const { setCaptureOpen } = useContext(OpenContext); const { setCanvasRatio, canvasRatio } = useContext(CanvasContext); const handleRatioChange = (newRatio) => { setCanvasRatio(newRatio); @@ -54,7 +56,12 @@ export function ActionButtons() { -
+
{ + setCaptureOpen(true); + }} + > - - -
- - - - - -
-
- -
- - - -
-
- -
- -
- - - -
-
- - {/* opacity */} -
- - { - const newOpacity = value[0]; // Extract slider value - adjustBackgroundOpacity(newOpacity); // Adjust Fabric.js background opacity - }} - /> -
-
- - - - {/* canvas size customization (width/height) */} -
-
- - { - if (canvasWidth > parseInt(e.target.value)) { - handleChange('width', parseInt(e.target.value, 10)); - } - }} - /> -
- -
- - { - if (canvasHeight > parseInt(e.target.value)) { - handleChange('height', parseInt(e.target.value, 10)); - } - }} - /> -
-
-
-
- - ) + // Update canvas dimensions + if (key === "width") { + canvas.setWidth(value); // Update canvas width + } else if (key === "height") { + canvas.setHeight(value); // Update canvas height } - return screenWidth <= 768 ? ( - - {content()} - - ) : ( -
- {content()} -
+ // Adjust the background image to fit the updated canvas + const bgImage = canvas.backgroundImage; + if (bgImage) { + const canvasWidth = canvas.width; + const canvasHeight = canvas.height; + + // Calculate scaling factors for width and height + const scaleX = canvasWidth / bgImage.width; + const scaleY = canvasHeight / bgImage.height; + + // Choose the larger scale to cover the entire canvas + const scale = Math.max(scaleX, scaleY); + + // Apply the scale and center the image + bgImage.scaleX = scale; + bgImage.scaleY = scale; + bgImage.left = 0; // Align left + bgImage.top = 0; // Align top + + // Mark the background image as needing an update + canvas.setBackgroundImage(bgImage, canvas.renderAll.bind(canvas)); + } else { + canvas.renderAll(); // Render if no background image + } + }; + + const setBackgroundImage = (e) => { + const file = e.target.files[0]; + if (file) { + const blobUrl = URL.createObjectURL(file); + + // Create an image element + const imgElement = new Image(); + imgElement.src = blobUrl; + + imgElement.onload = () => { + // Create a fabric.Image instance + const img = new fabric.Image(imgElement, { + scaleX: canvas.width / imgElement.width, + scaleY: canvas.height / imgElement.height, + }); + + // Set the background image on the canvas + canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas)); + + // Revoke the object URL to free memory + URL.revokeObjectURL(blobUrl); + }; + + imgElement.onerror = () => { + console.error("Failed to load the image."); + URL.revokeObjectURL(blobUrl); + }; + } + }; + + const setBackgroundOverlayImage = (e) => { + const file = e.target.files[0]; + if (file) { + const blobUrl = URL.createObjectURL(file); + + // Create an image element + const imgElement = new Image(); + imgElement.src = blobUrl; + + imgElement.onload = () => { + const img = new fabric.Image(imgElement, { + scaleX: canvas.width / imgElement.width, + scaleY: canvas.height / imgElement.height, + }); + + // Use setOverlayImage method to add the image as an overlay + canvas.setOverlayImage(img, () => { + canvas.renderAll(); + }); + + // Revoke the object URL after image is loaded and set + URL.revokeObjectURL(blobUrl); + }; + } + }; + + const adjustBackgroundOpacity = (value) => { + if (canvas) { + if (canvas.backgroundImage) { + // Update the opacity of the background image if it exists + canvas.backgroundImage.set("opacity", value); + } else if (canvas.overlayImage) { + // Update the opacity of the overlay image if it exists + canvas.overlayImage.set("opacity", value); + } else { + console.error("No background or overlay image found on the canvas."); + return; + } + + // Re-render the canvas to apply changes + canvas.renderAll(); + } else { + console.error("Canvas is not initialized."); + } + }; + + const removeBackgroundImage = () => { + if (canvas) { + canvas.backgroundImage = null; + canvas.renderAll(); + } + if (bgImgRef.current) { + bgImgRef.current.value = ""; + } + }; + + const removeBackgroundOverlayImage = () => { + if (canvas) { + canvas.overlayImage = null; + canvas.renderAll(); + } + }; + + const rndValue = { + valueX: 0, + valueY: 20, + width: 250, + height: 0, + minWidth: 250, + maxWidth: 300, + minHeight: 0, + maxHeight: 400, + bound: "parent", + }; + const content = () => { + return ( + + + Canvas Setting{" "} + {" "} + + + +
+ + + + +
+
+ +
+ + + +
+
+ +
+ +
+ + + +
+
+ + {/* opacity */} +
+ + { + const newOpacity = value[0]; // Extract slider value + adjustBackgroundOpacity(newOpacity); // Adjust Fabric.js background opacity + }} + /> +
+
+ + + + {/* canvas size customization (width/height) */} +
+
+ + { + if (canvasWidth > parseInt(e.target.value)) { + handleChange("width", parseInt(e.target.value, 10)); + } + }} + /> +
+ +
+ + { + if (canvasHeight > parseInt(e.target.value)) { + handleChange("height", parseInt(e.target.value, 10)); + } + }} + /> +
+
+
+
+
); -} + }; -export default CanvasSetting \ No newline at end of file + return screenWidth <= 768 ? ( + {content()} + ) : ( +
{content()}
+ ); +}; + +export default CanvasSetting; diff --git a/src/components/EachComponent/CustomShape/CustomShape.jsx b/src/components/EachComponent/CustomShape/CustomShape.jsx index 2615965..7b071ea 100644 --- a/src/components/EachComponent/CustomShape/CustomShape.jsx +++ b/src/components/EachComponent/CustomShape/CustomShape.jsx @@ -1,68 +1,69 @@ -import ActiveObjectContext from '@/components/Context/activeObject/ObjectContext'; -import CanvasContext from '@/components/Context/canvasContext/CanvasContext'; -import { useContext } from 'react' -import { shapes } from './shapes'; -import { fabric } from 'fabric'; +import ActiveObjectContext from "@/components/Context/activeObject/ObjectContext"; +import CanvasContext from "@/components/Context/canvasContext/CanvasContext"; +import { useContext } from "react"; +import { shapes } from "./shapes"; +import { fabric } from "fabric"; const CustomShape = () => { - const { canvas } = useContext(CanvasContext); - const { setActiveObject } = useContext(ActiveObjectContext); + const { canvas } = useContext(CanvasContext); + const { setActiveObject } = useContext(ActiveObjectContext); - const addShape = (each) => { - // Load the SVG from the imported file - fabric.loadSVGFromURL(each, (objects, options) => { - const svgGroup = fabric.util.groupSVGElements(objects, options); + const addShape = (each) => { + // Load the SVG from the imported file + fabric.loadSVGFromURL(each, (objects, options) => { + const svgGroup = fabric.util.groupSVGElements(objects, options); - // Calculate canvas center - const centerX = canvas.getWidth() / 2; - const centerY = canvas.getHeight() / 2; + // Calculate canvas center + const centerX = canvas.getWidth() / 2; + const centerY = canvas.getHeight() / 2; - // Set properties for centering the SVG - svgGroup.set({ - left: centerX, // Center horizontally - top: centerY, // Center vertically - originX: 'center', // Set the origin to the center - originY: 'center', - fill: "#f09b0a", - scaleX: 1, - scaleY: 1, - }); + // Set properties for centering the SVG + svgGroup.set({ + left: centerX, // Center horizontally + top: centerY, // Center vertically + originX: "center", // Set the origin to the center + originY: "center", + fill: "#f09b0a", + scaleX: 1, + scaleY: 1, + strokeWidth: 0, + }); - // Add SVG to the canvas - canvas.add(svgGroup); - canvas.setActiveObject(svgGroup); + // Add SVG to the canvas + canvas.add(svgGroup); + canvas.setActiveObject(svgGroup); - // Update the active object state - setActiveObject(svgGroup); + // Update the active object state + setActiveObject(svgGroup); - // Render the canvas - canvas.renderAll(); - }); - }; + // Render the canvas + canvas.renderAll(); + }); + }; - return ( + return ( +
addShape()} + > + {shapes.map((each) => (
addShape()} + key={each.shape} + className="relative aspect-square flex items-center justify-center bg-secondary rounded-md cursor-pointer" + onClick={(e) => { + e.stopPropagation(); + addShape(each.source); + }} > - {shapes.map((each) => ( -
{ - e.stopPropagation() - addShape(each.source) - }} - > - {`Shape -
- ))} + {`Shape
- ) -} + ))} +
+ ); +}; -export default CustomShape \ No newline at end of file +export default CustomShape; diff --git a/src/components/EachComponent/Customization/LockObject.jsx b/src/components/EachComponent/Customization/LockObject.jsx index 4f83377..5d9e9c9 100644 --- a/src/components/EachComponent/Customization/LockObject.jsx +++ b/src/components/EachComponent/Customization/LockObject.jsx @@ -4,6 +4,7 @@ import { Button } from "@/components/ui/button"; import { useToast } from "@/hooks/use-toast"; import { useContext, useEffect, useState } from "react"; import { Lock, Unlock } from "lucide-react"; +import { Tooltip } from "react-tooltip"; const LockObject = () => { const { canvas } = useContext(CanvasContext); @@ -51,19 +52,22 @@ const LockObject = () => { return (
- + + + +
); }; diff --git a/src/components/EachComponent/Customization/OpacityCustomization.jsx b/src/components/EachComponent/Customization/OpacityCustomization.jsx index a1d2e84..e30fb26 100644 --- a/src/components/EachComponent/Customization/OpacityCustomization.jsx +++ b/src/components/EachComponent/Customization/OpacityCustomization.jsx @@ -10,6 +10,7 @@ import { import { useContext, useEffect, useState } from "react"; import { BsTransparency } from "react-icons/bs"; import { Button } from "@/components/ui/button"; +import { Tooltip } from "react-tooltip"; const OpacityCustomization = () => { const { activeObject } = useContext(ActiveObjectContext); @@ -31,30 +32,35 @@ const OpacityCustomization = () => { }; return ( - - - - - -
-
- - - {Math.round(opacity * 100)}% - +
+ + + + + + + +
+
+ + + {Math.round(opacity * 100)}% + +
+ adjustBackgroundOpacity(value[0])} + />
- adjustBackgroundOpacity(value[0])} - /> -
- - + + + +
); }; diff --git a/src/components/EachComponent/Icons/AllIcons.jsx b/src/components/EachComponent/Icons/AllIcons.jsx index c08e5d6..75e6013 100644 --- a/src/components/EachComponent/Icons/AllIcons.jsx +++ b/src/components/EachComponent/Icons/AllIcons.jsx @@ -4,133 +4,141 @@ import { Card, CardDescription, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import * as lucideIcons from "lucide-react"; import CanvasContext from "@/components/Context/canvasContext/CanvasContext"; -import { fabric } from 'fabric'; +import { fabric } from "fabric"; import ActiveObjectContext from "@/components/Context/activeObject/ObjectContext"; import { useToast } from "@/hooks/use-toast"; const AllIconsPage = () => { - const [search, setSearch] = useState(""); - const { canvas } = useContext(CanvasContext); - const { setActiveObject } = useContext(ActiveObjectContext); + const [search, setSearch] = useState(""); + const { canvas } = useContext(CanvasContext); + const { setActiveObject } = useContext(ActiveObjectContext); - const { toast } = useToast(); + const { toast } = useToast(); - // Assume icons is already defined as shown previously, and filtered is created based on the search query - const icons = Object.entries(lucideIcons)?.filter(([name, Icon]) => - !name.includes("Icon") && Icon?.$$typeof - ); + // Assume icons is already defined as shown previously, and filtered is created based on the search query + const icons = Object.entries(lucideIcons)?.filter( + ([name, Icon]) => !name.includes("Icon") && Icon?.$$typeof + ); - const filtered = icons.filter(([name, Icon]) => - name.toLowerCase().includes(search.toLowerCase()) - ); + const filtered = icons.filter(([name, Icon]) => + name.toLowerCase().includes(search.toLowerCase()) + ); - const handleSearch = (e) => { - setSearch(e.target.value); - }; + const handleSearch = (e) => { + setSearch(e.target.value); + }; - const handleIcon = (e) => { - // Check if the target is an SVG or path - if (e.target.tagName.toLowerCase() === 'svg') { - // Serialize the SVG element to a string and pass it - const svgString = new XMLSerializer().serializeToString(e.target); - handleAddIcon(svgString); + const handleIcon = (e) => { + // Check if the target is an SVG or path + if (e.target.tagName.toLowerCase() === "svg") { + // Serialize the SVG element to a string and pass it + const svgString = new XMLSerializer().serializeToString(e.target); + handleAddIcon(svgString); + } else { + toast({ + title: "Invalid Choice", + description: "The target is a path element! Select the full icon.", + variant: "destructive", + }); + } + }; + + const handleAddIcon = (svgString) => { + if (!canvas) { + console.error("Canvas is not initialized."); + return; + } + + if (!svgString) { + console.error("Failed to retrieve SVG string from the icon."); + return; + } + + fabric.loadSVGFromString(svgString, (objects, options) => { + if (!objects || !options) { + console.error("Failed to parse SVG."); + return; + } + + // Recursively set fill color for all objects + const setFillColor = (obj, color) => { + if (obj.type === "group" && obj._objects) { + obj._objects.forEach((child) => setFillColor(child, color)); } else { - toast({ - title: "Invalid Choice", - description: "The target is a path element! Select the full icon.", - variant: "destructive", - }) + obj.set("stroke", color); } - }; + }; - const handleAddIcon = (svgString) => { - if (!canvas) { - console.error("Canvas is not initialized."); - return; - } + objects.forEach((obj) => setFillColor(obj, "#FFA500")); // Set fill color to orange - if (!svgString) { - console.error("Failed to retrieve SVG string from the icon."); - return; - } + const iconGroup = fabric.util.groupSVGElements(objects, options); + iconGroup.set({ + left: canvas.width / 2, + top: canvas.height / 2, + originX: "center", + originY: "center", + scaleX: 6, + scaleY: 6, + }); - fabric.loadSVGFromString(svgString, (objects, options) => { - if (!objects || !options) { - console.error("Failed to parse SVG."); - return; - } - - // Recursively set fill color for all objects - const setFillColor = (obj, color) => { - if (obj.type === 'group' && obj._objects) { - obj._objects.forEach((child) => setFillColor(child, color)); - } else { - obj.set('stroke', color); - } - }; - - objects.forEach((obj) => setFillColor(obj, '#FFA500')); // Set fill color to orange - - const iconGroup = fabric.util.groupSVGElements(objects, options); - iconGroup.set({ - left: canvas.width / 2, - top: canvas.height / 2, - originX: 'center', - originY: 'center', - scaleX: 6, - scaleY: 6, - }); - - canvas.add(iconGroup); - canvas.setActiveObject(iconGroup); - setActiveObject(iconGroup); - canvas.renderAll(); - }); - }; - - // Cell component for rendering each icon - const Cell = ({ columnIndex, rowIndex, style }) => { - const index = rowIndex * 3 + columnIndex; // Adjust columns as needed (3 columns in this case) - if (index >= filtered.length) return null; // Handle out-of-bounds index - const [name, Icon] = filtered[index]; - // Define cell-specific styles - return ( -
-
- -

{name}

-
-
- ); - }; + canvas.add(iconGroup); + canvas.setActiveObject(iconGroup); + setActiveObject(iconGroup); + canvas.renderAll(); + }); + }; + // Cell component for rendering each icon + const Cell = ({ columnIndex, rowIndex, style }) => { + const index = rowIndex * 3 + columnIndex; // Adjust columns as needed (3 columns in this case) + if (index >= filtered.length) return null; // Handle out-of-bounds index + const [name, Icon] = filtered[index]; + // Define cell-specific styles return ( - - All Icons - All copyright (c) for Lucide are held by Lucide Contributors 2022. - - - - {Cell} - - - - ) +
+
+ +

+ {name} +

+
+
+ ); + }; + + return ( + + + All Icons + + + All copyright (c) for Lucide are held by Lucide Contributors 2022. + + + + + {Cell} + + + + ); }; export default AllIconsPage; - - diff --git a/src/components/EachComponent/RoundedShapes/RoundedShape.jsx b/src/components/EachComponent/RoundedShapes/RoundedShape.jsx index 371b9e6..e512138 100644 --- a/src/components/EachComponent/RoundedShapes/RoundedShape.jsx +++ b/src/components/EachComponent/RoundedShapes/RoundedShape.jsx @@ -1,85 +1,95 @@ -import React, { useContext } from 'react' -import { ArrowBigRight, Diamond, Hexagon, Octagon, Pentagon, Sparkle, Square, Star, Triangle } from 'lucide-react' +import React, { useContext } from "react"; +import { + ArrowBigRight, + Diamond, + Hexagon, + Octagon, + Pentagon, + Sparkle, + Square, + Star, + Triangle, +} from "lucide-react"; import ReactDOMServer from "react-dom/server"; -import { fabric } from 'fabric'; -import CanvasContext from '@/components/Context/canvasContext/CanvasContext'; -import ActiveObjectContext from '@/components/Context/activeObject/ObjectContext'; -import { Card } from '@/components/ui/card'; -import { Separator } from '@/components/ui/separator'; +import { fabric } from "fabric"; +import CanvasContext from "@/components/Context/canvasContext/CanvasContext"; +import ActiveObjectContext from "@/components/Context/activeObject/ObjectContext"; +import { Card } from "@/components/ui/card"; +import { Separator } from "@/components/ui/separator"; const RoundedShape = () => { - const { canvas } = useContext(CanvasContext); - const { setActiveObject } = useContext(ActiveObjectContext); + const { canvas } = useContext(CanvasContext); + const { setActiveObject } = useContext(ActiveObjectContext); - const shapes = [ - { icon: , name: 'Arrow' }, - { icon: , name: 'Diamond' }, - { icon: , name: 'Hexagon' }, - { icon: , name: 'Octagon' }, - { icon: , name: 'Pentagon' }, - { icon: , name: 'Sparkle' }, - { icon: , name: 'Square' }, - { icon: , name: 'Star' }, - { icon: , name: 'Triangle' }, - ]; + const shapes = [ + { icon: , name: "Arrow" }, + { icon: , name: "Diamond" }, + { icon: , name: "Hexagon" }, + { icon: , name: "Octagon" }, + { icon: , name: "Pentagon" }, + { icon: , name: "Sparkle" }, + { icon: , name: "Square" }, + { icon: , name: "Star" }, + { icon: , name: "Triangle" }, + ]; - const addObject = (icon) => { - if (!canvas) { - console.error("Canvas is not initialized."); - return; - } - const svgString = ReactDOMServer.renderToStaticMarkup(icon); + const addObject = (icon) => { + if (!canvas) { + console.error("Canvas is not initialized."); + return; + } + const svgString = ReactDOMServer.renderToStaticMarkup(icon); - if (!svgString) { - console.error("Failed to retrieve SVG string from icon."); - return; - } - // Load SVG onto the Fabric.js canvas - fabric.loadSVGFromString(svgString, (objects, options) => { - if (!objects || !options) { - console.error("Failed to parse SVG."); - return; - } + if (!svgString) { + console.error("Failed to retrieve SVG string from icon."); + return; + } + // Load SVG onto the Fabric.js canvas + fabric.loadSVGFromString(svgString, (objects, options) => { + if (!objects || !options) { + console.error("Failed to parse SVG."); + return; + } - const iconGroup = fabric.util.groupSVGElements(objects, options); - iconGroup.set({ - left: canvas.width / 2, - top: canvas.height / 2, - originX: 'center', - originY: 'center', - fill: "#f09b0a", - scaleX: 6, - scaleY: 6, - strokeWidth: 0, - stroke: "#ffffff" - }); - canvas.add(iconGroup); - canvas.setActiveObject(iconGroup); - setActiveObject(iconGroup) - canvas.renderAll(); - }); - }; + const iconGroup = fabric.util.groupSVGElements(objects, options); + iconGroup.set({ + left: canvas.width / 2, + top: canvas.height / 2, + originX: "center", + originY: "center", + fill: "#f09b0a", + scaleX: 6, + scaleY: 6, + strokeWidth: 0, + stroke: "#ffffff", + }); + canvas.add(iconGroup); + canvas.setActiveObject(iconGroup); + setActiveObject(iconGroup); + canvas.renderAll(); + }); + }; - return ( - -

Rounded Shapes

- -
- {shapes.map((shape, index) => ( - - ))} + return ( + +

Rounded Shapes

+ +
+ {shapes.map((shape, index) => ( + + ))} +
+
+ ); +}; -export default RoundedShape \ No newline at end of file +export default RoundedShape; diff --git a/src/components/EachComponent/Shapes/PlainShapes.jsx b/src/components/EachComponent/Shapes/PlainShapes.jsx index 87a17c2..f62eed3 100644 --- a/src/components/EachComponent/Shapes/PlainShapes.jsx +++ b/src/components/EachComponent/Shapes/PlainShapes.jsx @@ -1,174 +1,326 @@ -import ActiveObjectContext from '@/components/Context/activeObject/ObjectContext'; -import CanvasContext from '@/components/Context/canvasContext/CanvasContext'; -import React, { useContext } from 'react' +import ActiveObjectContext from "@/components/Context/activeObject/ObjectContext"; +import CanvasContext from "@/components/Context/canvasContext/CanvasContext"; +import React, { useContext } from "react"; import ReactDOMServer from "react-dom/server"; -import { fabric } from 'fabric'; -import { Card } from '@/components/ui/card'; -import { Separator } from '@/components/ui/separator'; -import { Badge, Circle, Heart, Shield } from 'lucide-react'; +import { fabric } from "fabric"; +import { Card } from "@/components/ui/card"; +import { Separator } from "@/components/ui/separator"; +import { Badge, Circle, Heart, Shield } from "lucide-react"; const PlainShapes = () => { - const { canvas } = useContext(CanvasContext); - const { setActiveObject } = useContext(ActiveObjectContext); + const { canvas } = useContext(CanvasContext); + const { setActiveObject } = useContext(ActiveObjectContext); - const shapes = [ - { - icon: - - , name: 'Arrow' - }, + const shapes = [ + { + icon: ( + + + + ), + name: "Arrow", + }, - { icon: , name: 'Badge' }, - { icon: , name: 'Circle' }, + { icon: , name: "Badge" }, + { icon: , name: "Circle" }, - { icon: , name: 'Club' }, + { + icon: ( + + + + ), + name: "Club", + }, - { - icon: - - , name: 'Cross' - }, + { + icon: ( + + + + ), + name: "Cross", + }, - { - icon: - - , name: 'Diamond' - }, + { + icon: ( + + + + ), + name: "Diamond", + }, - { icon: , name: 'Heart' }, + { icon: , name: "Heart" }, - { - icon: - - , name: 'Hexagon' - }, + { + icon: ( + + + + ), + name: "Hexagon", + }, - { - icon: - - , name: 'Line' - }, + { + icon: ( + + + + ), + name: "Line", + }, - { - icon: - - , name: 'Octagon' - }, + { + icon: ( + + + + ), + name: "Octagon", + }, - { - icon: - - , name: 'Pentagon' - }, + { + icon: ( + + + + ), + name: "Pentagon", + }, - { - icon: - - , name: 'Rectangle' - }, + { + icon: ( + + + + ), + name: "Rectangle", + }, - { - icon: - - , name: 'Right Triangle' - }, + { + icon: ( + + + + ), + name: "Right Triangle", + }, + { + icon: , + name: "Shield", + }, - { - icon: , name: 'Shield' - }, + { + icon: ( + + + + ), + name: "Rectangle Square", + }, - { - icon: - - , name: 'Rectangle Square' - }, + { + icon: ( + + + + ), + name: "Star", + }, + { + icon: ( + + + + ), + name: "Triangle", + }, - { - icon: - - , name: 'Star' - }, + { + icon: ( + + + + ), + name: "Rectangle Vertical", + }, + ]; - { - icon: - - , name: 'Triangle' - }, + const addObject = (icon, name) => { + if (!canvas) { + console.error("Canvas is not initialized."); + return; + } - { - icon: - - , name: 'Rectangle Vertical' - }, + const svgString = ReactDOMServer.renderToStaticMarkup(icon); - ]; - - const addObject = (icon, name) => { - if (!canvas) { - console.error("Canvas is not initialized."); - return; - } - - const svgString = ReactDOMServer.renderToStaticMarkup(icon); - - if (!svgString) { - console.error("Failed to retrieve SVG string from icon."); - return; - } - // Load SVG onto the Fabric.js canvas - fabric.loadSVGFromString(svgString, (objects, options) => { - if (!objects || !options) { - console.error("Failed to parse SVG."); - return; - } - const iconGroup = fabric.util.groupSVGElements(objects, options); - iconGroup.set({ - left: canvas.width / 2, - top: canvas.height / 2, - originX: 'center', - originY: 'center', - fill: "#f09b0a", - scaleX: 6, - scaleY: 6, - strokeWidth: 0, - // rx: 0, - // x: 0, - // y: 0, - }); - if (name === "Line") { - iconGroup.set({ - strokeWidth: 2, - }) - } - canvas.add(iconGroup); - canvas.setActiveObject(iconGroup); - setActiveObject(iconGroup) - canvas.renderAll(); + if (!svgString) { + console.error("Failed to retrieve SVG string from icon."); + return; + } + // Load SVG onto the Fabric.js canvas + fabric.loadSVGFromString(svgString, (objects, options) => { + if (!objects || !options) { + console.error("Failed to parse SVG."); + return; + } + const iconGroup = fabric.util.groupSVGElements(objects, options); + iconGroup.set({ + left: canvas.width / 2, + top: canvas.height / 2, + originX: "center", + originY: "center", + fill: "#f09b0a", + scaleX: 6, + scaleY: 6, + strokeWidth: 0, + // rx: 0, + // x: 0, + // y: 0, + }); + if (name === "Line") { + iconGroup.set({ + strokeWidth: 2, }); - }; + } + canvas.add(iconGroup); + canvas.setActiveObject(iconGroup); + setActiveObject(iconGroup); + canvas.renderAll(); + }); + }; - return ( - -

Plain Shapes

- -
- {shapes.map((shape, index) => ( - - ))} + return ( + +

Plain Shapes

+ +
+ {shapes.map((shape, index) => ( + + ))} +
+
+ ); +}; -export default PlainShapes \ No newline at end of file +export default PlainShapes; diff --git a/src/components/EachComponent/UploadImage.jsx b/src/components/EachComponent/UploadImage.jsx index a4c84c3..310bb38 100644 --- a/src/components/EachComponent/UploadImage.jsx +++ b/src/components/EachComponent/UploadImage.jsx @@ -1,288 +1,308 @@ -import { useContext, useRef, useState } from 'react' -import CanvasContext from '../Context/canvasContext/CanvasContext' -import { Button } from '@/components/ui/button' -import { fabric } from 'fabric' -import { ImageIcon, Trash2 } from 'lucide-react' -import { Label } from '@/components/ui/label' -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' -import Resizer from "react-image-file-resizer" -import { Slider } from '@/components/ui/slider' -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' -import { useDropzone } from 'react-dropzone' -import ImageCustomization from './Customization/ImageCustomization' -import { Separator } from '../ui/separator' -import ActiveObjectContext from '../Context/activeObject/ObjectContext' +import { useContext, useRef, useState } from "react"; +import CanvasContext from "../Context/canvasContext/CanvasContext"; +import { Button } from "@/components/ui/button"; +import { fabric } from "fabric"; +import { ImageIcon, Trash2 } from "lucide-react"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import Resizer from "react-image-file-resizer"; +import { Slider } from "@/components/ui/slider"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { useDropzone } from "react-dropzone"; +import ImageCustomization from "./Customization/ImageCustomization"; +import { Separator } from "../ui/separator"; +import ActiveObjectContext from "../Context/activeObject/ObjectContext"; const UploadImage = () => { - const { canvas } = useContext(CanvasContext); - const [width, setWidth] = useState(1080); - const [height, setHeight] = useState(1080); - const [quality, setQuality] = useState(100); - const [rotation, setRotation] = useState("0"); - const [format, setFormat] = useState('JPEG'); - const fileInputRef = useRef(null); + const { canvas } = useContext(CanvasContext); + const [width, setWidth] = useState(1080); + const [height, setHeight] = useState(1080); + const [quality, setQuality] = useState(100); + const [rotation, setRotation] = useState("0"); + const [format, setFormat] = useState("JPEG"); + const fileInputRef = useRef(null); - const { activeObject, setActiveObject } = useContext(ActiveObjectContext); + const { activeObject, setActiveObject } = useContext(ActiveObjectContext); - const [file, setFile] = useState(null); - const [preview, setPreview] = useState(null); + const [file, setFile] = useState(null); + const [preview, setPreview] = useState(null); - const { getRootProps, getInputProps, isDragActive } = useDropzone({ - accept: { - 'image/*': ['.jpeg', '.png', '.gif', '.jpg', '.webp', '.svg'] - }, - // maxSize: 5 * 1024 * 1024, // 5MB max file size - multiple: false, - onDrop: (acceptedFiles) => { - if (!acceptedFiles.length) { - console.error("No files were dropped."); - return; - } - const selectedFile = acceptedFiles[0]; - // Create a preview URL - const blobUrl = URL.createObjectURL(selectedFile); - setFile(selectedFile); - setPreview(blobUrl); + const { getRootProps, getInputProps, isDragActive } = useDropzone({ + accept: { + "image/*": [".jpeg", ".png", ".gif", ".jpg", ".webp", ".svg"], + }, + // maxSize: 5 * 1024 * 1024, // 5MB max file size + multiple: false, + onDrop: (acceptedFiles) => { + if (!acceptedFiles.length) { + console.error("No files were dropped."); + return; + } + const selectedFile = acceptedFiles[0]; + // Create a preview URL + const blobUrl = URL.createObjectURL(selectedFile); + setFile(selectedFile); + setPreview(blobUrl); - if (selectedFile.type === "image/svg+xml") { - addImageToCanvas(selectedFile); - URL.revokeObjectURL(blobUrl); - } else { - const imgElement = new Image(); - imgElement.src = blobUrl; + if (selectedFile.type === "image/svg+xml") { + addImageToCanvas(selectedFile); + URL.revokeObjectURL(blobUrl); + } else { + const imgElement = new Image(); + imgElement.src = blobUrl; - imgElement.onload = () => { - if (imgElement.width > 1080) { - handleResize(selectedFile, (compressedFile) => { - addImageToCanvas(compressedFile); - }); - } else { - addImageToCanvas(selectedFile); - } - URL.revokeObjectURL(blobUrl); // Clean up - }; + imgElement.onload = () => { + if (imgElement.width > 1080) { + handleResize(selectedFile, (compressedFile) => { + addImageToCanvas(compressedFile); + }); + } else { + addImageToCanvas(selectedFile); + } + URL.revokeObjectURL(blobUrl); // Clean up + }; - imgElement.onerror = () => { - console.error("Failed to load image."); - URL.revokeObjectURL(blobUrl); // Clean up - }; - } - } + imgElement.onerror = () => { + console.error("Failed to load image."); + URL.revokeObjectURL(blobUrl); // Clean up + }; + } + }, + }); + + const removeFile = () => { + // Revoke the object URL to free up memory + if (preview) { + URL.revokeObjectURL(preview); + } + setFile(null); + setPreview(null); + if (fileInputRef.current) { + fileInputRef.current.value = ""; + } + if (activeObject?.type === "image") { + canvas.remove(activeObject); + setActiveObject(null); + canvas.renderAll(); + } + }; + + const handleResize = (file, callback) => { + Resizer.imageFileResizer( + file, + width, + height, + format, + quality, + parseInt(rotation), + (resizedFile) => { + callback(resizedFile); + }, + "file" + ); + }; + + const addImageToCanvas = (file) => { + const blobUrl = URL.createObjectURL(file); + fabric.Image.fromURL(blobUrl, (img) => { + img.set({ + left: canvas.width / 4, + top: canvas.height / 4, + scaleX: 0.5, + scaleY: 0.5, + }); + canvas.add(img); + canvas.setActiveObject(img); + // Update the active object state + setActiveObject(img); + canvas.renderAll(); + URL.revokeObjectURL(blobUrl); }); + }; - const removeFile = () => { - // Revoke the object URL to free up memory - if (preview) { - URL.revokeObjectURL(preview) - } - setFile(null) - setPreview(null) - if (fileInputRef.current) { - fileInputRef.current.value = "" - } - if (activeObject?.type === "image") { - canvas.remove(activeObject); - setActiveObject(null); - canvas.renderAll(); - } - } + return ( + + + Image Upload & Editing + + Upload, resize, and apply effects to your images + + + + + + Upload + Effects + - const handleResize = (file, callback) => { - Resizer.imageFileResizer( - file, - width, - height, - format, - quality, - parseInt(rotation), - (resizedFile) => { - callback(resizedFile) - }, - 'file', - ) - } + {/* Uploads */} + +
+
+
+ + setWidth(value[0])} + /> +
+
+ + setHeight(value[0])} + /> +
+
+ {format === "JPEG" && ( +
+ + setQuality(value[0])} + /> +
+ )} - const addImageToCanvas = (file) => { - const blobUrl = URL.createObjectURL(file) - fabric.Image.fromURL(blobUrl, (img) => { - img.set({ - left: canvas.width / 4, - top: canvas.height / 4, - scaleX: 0.5, - scaleY: 0.5, - }) - canvas.add(img) - canvas.setActiveObject(img); - // Update the active object state - setActiveObject(img); - canvas.renderAll(); - URL.revokeObjectURL(blobUrl); - }) - } +
+
+ + +
+
+ + +
+
- return ( - - - Image Upload & Editing - Upload, resize, and apply effects to your images - - - - - Upload - Effects - - - {/* Uploads */} - -
-
-
- - setWidth(value[0])} - /> -
-
- - setHeight(value[0])} - /> -
-
- {format === "JPEG" && ( -
- - setQuality(value[0])} - /> -
- )} - -
-
- - -
-
- - -
-
- - {/* upload image */} - { - !preview && -
- - -
- -
- -

- {isDragActive - ? 'Drop file here' - : 'Drag \'n\' drop an image, or click to select a file' - } -

-

- (Max 5MB, image files only) -

-
-
-
-
-
- } - - {/* preview image */} - {preview && ( - - -
- { - file?.type === "image/svg+xml" ? - Your browser does not support SVG, no preview available for SVG. - : - Uploaded image - } - - -
-

{file?.name}

- -
-
-
-
- )} + {/* upload image */} + {!preview && ( +
+ + +
+ +
+ +

+ {isDragActive + ? "Drop file here" + : "Drag 'n' drop an image, or click to select a file"} +

+

+ (Max 5MB, image files only) +

- +
+
+
+
+ )} - {/* Effects */} - - - - - - - ) -} -export default UploadImage + {/* preview image */} + {preview && ( + + +
+ {file?.type === "image/svg+xml" ? ( + + Your browser does not support SVG, no preview + available for SVG. + + ) : ( + Uploaded image + )} + +
+

+ {file?.name} +

+ +
+
+
+
+ )} +
+
+ {/* Effects */} + + + +
+
+
+ ); +}; +export default UploadImage; diff --git a/src/components/ObjectShortcut.jsx b/src/components/ObjectShortcut.jsx index adbc276..c99018e 100644 --- a/src/components/ObjectShortcut.jsx +++ b/src/components/ObjectShortcut.jsx @@ -176,6 +176,7 @@ export const ObjectShortcut = ({ value }) => { const clearCanvas = () => { canvas.clear(); canvas.renderAll(); + canvas.setBackgroundColor("#ffffff", canvas.renderAll.bind(canvas)); setActiveObject(null); }; diff --git a/src/components/Panel/CanvasPanel.jsx b/src/components/Panel/CanvasPanel.jsx new file mode 100644 index 0000000..8f56b39 --- /dev/null +++ b/src/components/Panel/CanvasPanel.jsx @@ -0,0 +1,29 @@ +import { useContext } from "react"; +import CanvasContext from "../Context/canvasContext/CanvasContext"; +import { Button } from "../ui/button"; +import { ScrollArea } from "../ui/scroll-area"; +import { X } from "lucide-react"; +import CanvasSetting from "../CanvasSetting"; + +const CanvasPanel = () => { + const { setSelectedPanel } = useContext(CanvasContext); + return ( +
+
+

Canvas Settings

+ +
+ + + +
+ ); +}; + +export default CanvasPanel; diff --git a/src/components/Panel/CommonPanel.jsx b/src/components/Panel/CommonPanel.jsx index c319a54..57e6f2e 100644 --- a/src/components/Panel/CommonPanel.jsx +++ b/src/components/Panel/CommonPanel.jsx @@ -1,15 +1,7 @@ import { useContext } from "react"; import CanvasContext from "../Context/canvasContext/CanvasContext"; -import SelectObjectFromGroup from "../EachComponent/Customization/SelectObjectFromGroup"; -import StrokeCustomization from "../EachComponent/Customization/StrokeCustomization"; -import PositionCustomization from "../EachComponent/Customization/PositionCustomization"; -import { Card } from "../ui/card"; -import CollapsibleComponent from "../EachComponent/Customization/CollapsibleComponent"; -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"; @@ -23,45 +15,17 @@ const CommonPanel = () => { return (
- - {/* Apply fill and background color */} {activeObjectType !== "image" && !hasClipPath && !customClipPath && ( )} - {/* Apply stroke and stroke color */} - {!customClipPath && ( - <> - - - )} - - {activeObject?.type !== "group" && ( - <> - - - )} - - {/* Controls for opacity, flip, and rotation */} - - -
- - -
-
-
- {/* Skew Customization */} {/* Scale Objects */} - {/* Shadow Customization */} - - {/* Add image into shape */}
diff --git a/src/components/Panel/EditorPanel.jsx b/src/components/Panel/EditorPanel.jsx index 10e01f2..18f9449 100644 --- a/src/components/Panel/EditorPanel.jsx +++ b/src/components/Panel/EditorPanel.jsx @@ -2,6 +2,16 @@ import { useContext } from "react"; import CanvasContext from "../Context/canvasContext/CanvasContext"; import TextPanel from "./TextPanel"; import ColorPanel from "./ColorPanel"; +import ShapePanel from "./ShapePanel"; +import IconPanel from "./IconPanel"; +import UploadPanel from "./UploadPanel"; +import StrokePanel from "./StrokePanel"; +import ShadowPanel from "./ShadowPanel"; +import FlipPanel from "./FlipPanel"; +import PositionPanel from "./PositionPanel"; +import ImagePanel from "./ImagePanel"; +import GroupObjectPanel from "./GroupObjectPanel"; +import CanvasPanel from "./CanvasPanel"; const EditorPanel = () => { const { selectedPanel } = useContext(CanvasContext); @@ -10,8 +20,28 @@ const EditorPanel = () => { switch (selectedPanel) { case "text": return ; + case "shape": + return ; + case "icon": + return ; + case "upload": + return ; case "color": return ; + case "stroke": + return ; + case "shadow": + return ; + case "flip": + return ; + case "position": + return ; + case "image-insert": + return ; + case "group-obj": + return ; + case "canvas": + return ; default: return; } diff --git a/src/components/Panel/FlipPanel.jsx b/src/components/Panel/FlipPanel.jsx new file mode 100644 index 0000000..7bfb8fd --- /dev/null +++ b/src/components/Panel/FlipPanel.jsx @@ -0,0 +1,39 @@ +import { useContext } from "react"; +import CanvasContext from "../Context/canvasContext/CanvasContext"; +import { Button } from "../ui/button"; +import { ScrollArea } from "../ui/scroll-area"; +import { X } from "lucide-react"; +import { Card } from "../ui/card"; +import CollapsibleComponent from "../EachComponent/Customization/CollapsibleComponent"; +import FlipCustomization from "../EachComponent/Customization/FlipCustomization"; +import RotateCustomization from "../EachComponent/Customization/RotateCustomization"; + +const FlipPanel = () => { + const { setSelectedPanel } = useContext(CanvasContext); + return ( +
+
+

Flip & Rotate

+ +
+ + + +
+ + +
+
+
+
+
+ ); +}; + +export default FlipPanel; diff --git a/src/components/Panel/GroupObjectPanel.jsx b/src/components/Panel/GroupObjectPanel.jsx new file mode 100644 index 0000000..0bdd55c --- /dev/null +++ b/src/components/Panel/GroupObjectPanel.jsx @@ -0,0 +1,36 @@ +import { useContext } from "react"; +import CanvasContext from "../Context/canvasContext/CanvasContext"; +import { Button } from "../ui/button"; +import { ScrollArea } from "../ui/scroll-area"; +import SelectObjectFromGroup from "../EachComponent/Customization/SelectObjectFromGroup"; +import { X } from "lucide-react"; +import SkewCustomization from "../EachComponent/Customization/SkewCustomization"; +import ScaleObjects from "../EachComponent/Customization/ScaleObjects"; + +const GroupObjectPanel = () => { + const { setSelectedPanel } = useContext(CanvasContext); + return ( +
+
+

Group Object

+ +
+ + + {/* Skew Customization */} + + + {/* Scale Objects */} + + +
+ ); +}; + +export default GroupObjectPanel; diff --git a/src/components/Panel/IconPanel.jsx b/src/components/Panel/IconPanel.jsx new file mode 100644 index 0000000..99be08b --- /dev/null +++ b/src/components/Panel/IconPanel.jsx @@ -0,0 +1,30 @@ +import { useContext } from "react"; +import { Button } from "../ui/button"; +import CanvasContext from "../Context/canvasContext/CanvasContext"; +import { X } from "lucide-react"; +import { ScrollArea } from "../ui/scroll-area"; +import AllIconsPage from "../EachComponent/Icons/AllIcons"; + +const IconPanel = () => { + const { setSelectedPanel } = useContext(CanvasContext); + return ( +
+
+

Icons

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

Image

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

Position Controller

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

Shadow Color

+ +
+ + + +
+ ); +}; + +export default ShadowPanel; diff --git a/src/components/Panel/ShapePanel.jsx b/src/components/Panel/ShapePanel.jsx new file mode 100644 index 0000000..d837b7f --- /dev/null +++ b/src/components/Panel/ShapePanel.jsx @@ -0,0 +1,47 @@ +import { useContext } from "react"; +import CanvasContext from "../Context/canvasContext/CanvasContext"; +import { Button } from "../ui/button"; +import { ScrollArea } from "../ui/scroll-area"; +import { X } from "lucide-react"; +import { Separator } from "../ui/separator"; +import CustomShape from "../EachComponent/CustomShape/CustomShape"; +import RoundedShape from "../EachComponent/RoundedShapes/RoundedShape"; +import PlainShapes from "../EachComponent/Shapes/PlainShapes"; + +const ShapePanel = () => { + const { setSelectedPanel } = useContext(CanvasContext); + return ( +
+
+

Shape

+ +
+ + +

Custom Shapes

+ +
+
+ +
+ +
+ +
+ +
+ +
+
+
+
+ ); +}; + +export default ShapePanel; diff --git a/src/components/Panel/StrokePanel.jsx b/src/components/Panel/StrokePanel.jsx index e8d7900..e8833d6 100644 --- a/src/components/Panel/StrokePanel.jsx +++ b/src/components/Panel/StrokePanel.jsx @@ -1,16 +1,16 @@ 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"; +import StrokeCustomization from "../EachComponent/Customization/StrokeCustomization"; -const ColorPanel = () => { +const StrokePanel = () => { const { setSelectedPanel } = useContext(CanvasContext); return (
-

Color

+

Stroke Color

- - + +
); }; -export default ColorPanel; +export default StrokePanel; diff --git a/src/components/Panel/TopBar.jsx b/src/components/Panel/TopBar.jsx index a200883..383c396 100644 --- a/src/components/Panel/TopBar.jsx +++ b/src/components/Panel/TopBar.jsx @@ -5,9 +5,23 @@ import OpacityCustomization from "../EachComponent/Customization/OpacityCustomiz import CanvasContext from "../Context/canvasContext/CanvasContext"; import { useContext } from "react"; import { ObjectShortcut } from "../ObjectShortcut"; +import ActiveObjectContext from "../Context/activeObject/ObjectContext"; +import { Button } from "../ui/button"; +import { ImagePlus } from "lucide-react"; +import { RxBorderWidth } from "react-icons/rx"; +import { LuFlipVertical } from "react-icons/lu"; +import { SlTarget } from "react-icons/sl"; +import { RiShadowLine } from "react-icons/ri"; +import { FaRegObjectGroup } from "react-icons/fa"; +import { Tooltip } from "react-tooltip"; export function TopBar() { - const { selectedPanel } = useContext(CanvasContext); + const { activeObject } = useContext(ActiveObjectContext); + const { selectedPanel, setSelectedPanel, textColor } = + useContext(CanvasContext); + const activeObjectType = activeObject?.type; + const hasClipPath = !!activeObject?.clipPath; + const customClipPath = activeObject?.isClipPath; return (
+
+ + + + +
+ {activeObjectType !== "image" && + activeObject?.type !== "i-text" && + !hasClipPath && + !customClipPath && ( + + + + )} + + {!customClipPath && ( + + + + )} + {(activeObject || activeObject.type === "group") && ( + + + + )} +
+ +
@@ -33,6 +188,14 @@ export function TopBar() {
+ + + + + + + +
); } diff --git a/src/components/Panel/UploadPanel.jsx b/src/components/Panel/UploadPanel.jsx new file mode 100644 index 0000000..faeaff5 --- /dev/null +++ b/src/components/Panel/UploadPanel.jsx @@ -0,0 +1,30 @@ +import { useContext } from "react"; +import CanvasContext from "../Context/canvasContext/CanvasContext"; +import { X } from "lucide-react"; +import { Button } from "../ui/button"; +import { ScrollArea } from "../ui/scroll-area"; +import UploadImage from "../EachComponent/UploadImage"; + +const UploadPanel = () => { + const { setSelectedPanel } = useContext(CanvasContext); + return ( +
+
+

Upload

+ +
+ + + + +
+ ); +}; + +export default UploadPanel;