added fucntionality for text
This commit is contained in:
parent
954ac950b0
commit
79ca662a08
18 changed files with 826 additions and 656 deletions
10
package-lock.json
generated
10
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
21
src/App.jsx
21
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 (
|
||||
// <div className="relative flex flex-col h-screen overflow-hidden">
|
||||
|
|
@ -96,7 +97,7 @@ function App() {
|
|||
<Sidebar />
|
||||
{selectedPanel !== "" && <EditorPanel />}
|
||||
<div className="flex-1 relative">
|
||||
<TopBar isVisible={hasSelectedObject} />
|
||||
{activeObject && <TopBar />}
|
||||
<ActionButtons />
|
||||
<Canvas />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<Card className="w-full max-w-3xl p-2 my-4 overflow-y-scroll scrollbar-thin scrollbar-thumb-secondary scrollbar-track-background rounded-none flex-1 flex flex-col mt-24 mx-auto bg-white pl-5 pb-5 pt-5 border-0 shadow-none">
|
||||
<Card
|
||||
className={`w-full max-w-3xl p-2 my-4 overflow-y-scroll scrollbar-thin scrollbar-thumb-secondary scrollbar-track-background rounded-none flex-1 flex flex-col ${
|
||||
activeObject ? "mt-5" : "mt-20"
|
||||
} mx-auto bg-white pl-5 pb-5 pt-5 border-0 shadow-none`}
|
||||
>
|
||||
<CardContent className="p-0 space-y-2">
|
||||
<AspectRatio
|
||||
ratio={getRatioValue(canvasRatio)}
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
import { useState } from 'react'
|
||||
import ActiveObjectContext from './ObjectContext'
|
||||
import { useState } from "react";
|
||||
import ActiveObjectContext from "./ObjectContext";
|
||||
|
||||
export const ObjectProvider = ({ children }) => {
|
||||
const [activeObject, setActiveObject] = useState(null);
|
||||
return (
|
||||
<ActiveObjectContext.Provider value={{ activeObject, setActiveObject }}>
|
||||
{children}
|
||||
</ActiveObjectContext.Provider>
|
||||
)
|
||||
}
|
||||
const ObjectProvider = ({ children }) => {
|
||||
const [activeObject, setActiveObject] = useState(null);
|
||||
|
||||
return (
|
||||
<ActiveObjectContext.Provider value={{ activeObject, setActiveObject }}>
|
||||
{children}
|
||||
</ActiveObjectContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default ObjectProvider;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div>
|
||||
<div className="grid">
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="colorField">Color Field</Label>
|
||||
<Select value={colorField} onValueChange={(value) => setColorField(value)}>
|
||||
<SelectTrigger id="colorField">
|
||||
<SelectValue placeholder="Select color field type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="fill">Fill</SelectItem>
|
||||
<SelectItem value="background">Background</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
// 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);
|
||||
}
|
||||
|
||||
<Tabs value={colorType} onValueChange={(value) => setColorType(value)}>
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="color">Solid Color</TabsTrigger>
|
||||
<TabsTrigger value="gradient" disabled={colorField === 'background'}>Gradient</TabsTrigger>
|
||||
</TabsList>
|
||||
if (
|
||||
colorField === "background" &&
|
||||
object.backgroundColor !== style.backgroundColor
|
||||
) {
|
||||
object.set("backgroundColor", style.backgroundColor);
|
||||
}
|
||||
}
|
||||
|
||||
<TabsContent value="color" className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="solidColor">Color</Label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Input
|
||||
id="solidColor"
|
||||
type="color"
|
||||
value={colorField === "fill" ? fillColor : backgroundColor}
|
||||
onChange={(e) => handleSolidColorChange(e.target.value)}
|
||||
className="w-12 h-12 p-1 rounded-md"
|
||||
/>
|
||||
<Input
|
||||
type="text"
|
||||
value={colorField === "fill" ? fillColor : backgroundColor}
|
||||
onChange={(e) => handleSolidColorChange(e.target.value)}
|
||||
className="flex-grow"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
if (colorType === "gradient" && colorField === "fill") {
|
||||
const width = object?.width || 0;
|
||||
const height = object?.height || 0;
|
||||
|
||||
<div className="h-24 rounded-md" style={{
|
||||
backgroundColor: colorField === "fill" ? fillColor : backgroundColor,
|
||||
}}></div>
|
||||
</TabsContent>
|
||||
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 },
|
||||
};
|
||||
|
||||
<TabsContent value="gradient" className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="gradientDirection">Direction</Label>
|
||||
<Select value={gradientDirection} onValueChange={setGradientDirection}>
|
||||
<SelectTrigger id="gradientDirection">
|
||||
<SelectValue placeholder="Select direction" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="top-to-bottom">Top to Bottom</SelectItem>
|
||||
<SelectItem value="bottom-to-top">Bottom to Top</SelectItem>
|
||||
<SelectItem value="left-to-right">Left to Right</SelectItem>
|
||||
<SelectItem value="right-to-left">Right to Left</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="gradientColor1">Color 1</Label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Input
|
||||
id="gradientColor1"
|
||||
type="color"
|
||||
value={gradientFillColors.color1}
|
||||
onChange={handleColorChange('color1')}
|
||||
className="w-12 h-12 p-1 rounded-md"
|
||||
/>
|
||||
<Input
|
||||
type="text"
|
||||
value={gradientFillColors.color1}
|
||||
onChange={handleColorChange('color1')}
|
||||
className="flex-grow"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="gradientColor2">Color 2</Label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Input
|
||||
id="gradientColor2"
|
||||
type="color"
|
||||
value={gradientFillColors.color2}
|
||||
onChange={handleColorChange('color2')}
|
||||
className="w-12 h-12 p-1 rounded-md"
|
||||
/>
|
||||
<Input
|
||||
type="text"
|
||||
value={gradientFillColors.color2}
|
||||
onChange={handleColorChange('color2')}
|
||||
className="flex-grow"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-24 rounded-md" style={{
|
||||
background: `linear-gradient(${gradientDirection === 'top-to-bottom' ? 'to bottom' :
|
||||
gradientDirection === 'bottom-to-top' ? 'to top' :
|
||||
gradientDirection === 'left-to-right' ? 'to right' :
|
||||
'to left'
|
||||
}, ${gradientFillColors.color1}, ${gradientFillColors.color2})`,
|
||||
}}></div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
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 },
|
||||
],
|
||||
});
|
||||
|
||||
<Button className="my-2" onClick={() => { applyColor() }}>Apply</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
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 (
|
||||
<div>
|
||||
<Card className="p-2">
|
||||
<CollapsibleComponent text={"Color Control"}>
|
||||
{content()}
|
||||
</CollapsibleComponent>
|
||||
</Card>
|
||||
<Separator className="my-2" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div>
|
||||
<div className="grid">
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="colorField">Color Field</Label>
|
||||
<Select
|
||||
value={colorField}
|
||||
onValueChange={(value) => setColorField(value)}
|
||||
>
|
||||
<SelectTrigger id="colorField">
|
||||
<SelectValue placeholder="Select color field type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="fill">Fill</SelectItem>
|
||||
<SelectItem value="background">Background</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
export default ApplyColor
|
||||
<Tabs
|
||||
value={colorType}
|
||||
onValueChange={(value) => setColorType(value)}
|
||||
>
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="color">Solid Color</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="gradient"
|
||||
disabled={colorField === "background"}
|
||||
>
|
||||
Gradient
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="color" className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="solidColor">Color</Label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Input
|
||||
id="solidColor"
|
||||
type="color"
|
||||
value={
|
||||
colorField === "fill" ? fillColor : backgroundColor
|
||||
}
|
||||
onChange={(e) => handleSolidColorChange(e.target.value)}
|
||||
className="w-12 h-12 p-1 rounded-md"
|
||||
/>
|
||||
<Input
|
||||
type="text"
|
||||
value={
|
||||
colorField === "fill" ? fillColor : backgroundColor
|
||||
}
|
||||
onChange={(e) => handleSolidColorChange(e.target.value)}
|
||||
className="flex-grow"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="h-24 rounded-md"
|
||||
style={{
|
||||
backgroundColor:
|
||||
colorField === "fill" ? fillColor : backgroundColor,
|
||||
}}
|
||||
></div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="gradient" className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="gradientDirection">Direction</Label>
|
||||
<Select
|
||||
value={gradientDirection}
|
||||
onValueChange={setGradientDirection}
|
||||
>
|
||||
<SelectTrigger id="gradientDirection">
|
||||
<SelectValue placeholder="Select direction" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="top-to-bottom">
|
||||
Top to Bottom
|
||||
</SelectItem>
|
||||
<SelectItem value="bottom-to-top">
|
||||
Bottom to Top
|
||||
</SelectItem>
|
||||
<SelectItem value="left-to-right">
|
||||
Left to Right
|
||||
</SelectItem>
|
||||
<SelectItem value="right-to-left">
|
||||
Right to Left
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="gradientColor1">Color 1</Label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Input
|
||||
id="gradientColor1"
|
||||
type="color"
|
||||
value={gradientFillColors.color1}
|
||||
onChange={(e) =>
|
||||
handleGradientColorChange("color1", e.target.value)
|
||||
}
|
||||
className="w-12 h-12 p-1 rounded-md"
|
||||
/>
|
||||
<Input
|
||||
type="text"
|
||||
value={gradientFillColors.color1}
|
||||
onChange={(e) =>
|
||||
handleGradientColorChange("color1", e.target.value)
|
||||
}
|
||||
className="flex-grow"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="gradientColor2">Color 2</Label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Input
|
||||
id="gradientColor2"
|
||||
type="color"
|
||||
value={gradientFillColors.color2}
|
||||
onChange={(e) =>
|
||||
handleGradientColorChange("color2", e.target.value)
|
||||
}
|
||||
className="w-12 h-12 p-1 rounded-md"
|
||||
/>
|
||||
<Input
|
||||
type="text"
|
||||
value={gradientFillColors.color2}
|
||||
onChange={(e) =>
|
||||
handleGradientColorChange("color2", e.target.value)
|
||||
}
|
||||
className="flex-grow"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="h-24 rounded-md"
|
||||
style={{
|
||||
background: `linear-gradient(${
|
||||
gradientDirection === "top-to-bottom"
|
||||
? "to bottom"
|
||||
: gradientDirection === "bottom-to-top"
|
||||
? "to top"
|
||||
: gradientDirection === "left-to-right"
|
||||
? "to right"
|
||||
: "to left"
|
||||
}, ${gradientFillColors.color1}, ${
|
||||
gradientFillColors.color2
|
||||
})`,
|
||||
}}
|
||||
></div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card className="border-0 shadow-none">
|
||||
<CollapsibleComponent text={"Color Control"}>
|
||||
{content()}
|
||||
</CollapsibleComponent>
|
||||
</Card>
|
||||
<Separator className="my-2" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApplyColor;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const CollapsibleComponent = ({ children, text }) => {
|
|||
}, [text]);
|
||||
|
||||
return (
|
||||
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
|
||||
<Collapsible open={isOpen}>
|
||||
<CollapsibleTrigger asChild>
|
||||
<div
|
||||
className={`flex items-center justify-between cursor-pointer ${
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ 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 { Card } from "@/components/ui/card";
|
||||
|
||||
const LockObject = () => {
|
||||
const { canvas } = useContext(CanvasContext);
|
||||
|
|
@ -51,8 +50,7 @@ const LockObject = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Card className="shadow-none border-0">
|
||||
<h2 className="font-bold">{!isLocked ? "Lock" : "Unlock"} Object</h2>
|
||||
<div className="shadow-none border-0">
|
||||
<Button
|
||||
onClick={toggleLock}
|
||||
variant="outline"
|
||||
|
|
@ -66,7 +64,7 @@ const LockObject = () => {
|
|||
<Lock className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className='grid gap-2 pt-1'>
|
||||
<Label>
|
||||
Adjust Opacity:
|
||||
</Label>
|
||||
<Slider
|
||||
value={[opacity]}
|
||||
min={0.0}
|
||||
max={1.0}
|
||||
step={0.01} // Step size for fine control
|
||||
onValueChange={(value) => {
|
||||
const newOpacity = value[0]; // Extract slider value
|
||||
adjustBackgroundOpacity(newOpacity); // Adjust Fabric.js background opacity
|
||||
}}
|
||||
/>
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8">
|
||||
<BsTransparency className="h-4 w-4" size={20} />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-64 mt-3">
|
||||
<div className="grid gap-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-sm font-medium">Opacity</Label>
|
||||
<span className="text-sm text-gray-500">
|
||||
{Math.round(opacity * 100)}%
|
||||
</span>
|
||||
</div>
|
||||
<Slider
|
||||
value={[opacity]}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.01}
|
||||
onValueChange={(value) => adjustBackgroundOpacity(value[0])}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
export default OpacityCustomization
|
||||
export default OpacityCustomization;
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ const StrokeCustomization = () => {
|
|||
}
|
||||
}
|
||||
if (object.strokeWidth) {
|
||||
setStrokeWidth(object.strokeWidth || 0);
|
||||
setStrokeWidth(0);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="mt-2">
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<p className="text-center text-gray-500">
|
||||
Select a text object to customize
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<CardContent className="p-2">
|
||||
<Tabs defaultValue="text" className="w-full">
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="text">Text</TabsTrigger>
|
||||
<TabsTrigger value="style">Style</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="text" className="space-y-1">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="text-content">Text Content</Label>
|
||||
<Input
|
||||
id="text-content"
|
||||
value={text}
|
||||
onChange={(e) => handleTextChange(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label>Font Family</Label>
|
||||
<Select
|
||||
value={fontFamily}
|
||||
onValueChange={handleFontFamilyChange}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select a font" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="h-[250px]">
|
||||
<SelectGroup>
|
||||
{fonts.map((font) => (
|
||||
<SelectItem
|
||||
style={{ fontFamily: font }}
|
||||
key={font}
|
||||
value={font}
|
||||
>
|
||||
{font}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label>Font Size</Label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Slider
|
||||
value={[fontSize]}
|
||||
onValueChange={([value]) => handleFontSizeChange(value)}
|
||||
min={8}
|
||||
max={72}
|
||||
step={1}
|
||||
className="flex-grow"
|
||||
/>
|
||||
<Input
|
||||
type="number"
|
||||
value={fontSize}
|
||||
onChange={(e) =>
|
||||
handleFontSizeChange(parseInt(e.target.value, 10) || 16)
|
||||
}
|
||||
className="w-16"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="style" className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label>Text Alignment</Label>
|
||||
<div className="flex space-x-1">
|
||||
<Button
|
||||
variant={textAlign === "left" ? "default" : "outline"}
|
||||
size="icon"
|
||||
onClick={() => handleTextAlignChange("left")}
|
||||
>
|
||||
<AlignLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={textAlign === "center" ? "default" : "outline"}
|
||||
size="icon"
|
||||
onClick={() => handleTextAlignChange("center")}
|
||||
>
|
||||
<AlignCenter className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={textAlign === "right" ? "default" : "outline"}
|
||||
size="icon"
|
||||
onClick={() => handleTextAlignChange("right")}
|
||||
>
|
||||
<AlignRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<Label>Text Style</Label>
|
||||
<div className="flex space-x-1">
|
||||
<Button
|
||||
variant={fontWeight === "bold" ? "default" : "outline"}
|
||||
size="icon"
|
||||
onClick={handleFontWeightChange}
|
||||
>
|
||||
<Bold className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={fontStyle === "italic" ? "default" : "outline"}
|
||||
size="icon"
|
||||
onClick={handleFontStyleChange}
|
||||
>
|
||||
<Italic className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={underline ? "default" : "outline"}
|
||||
size="icon"
|
||||
onClick={handleUnderlineChange}
|
||||
>
|
||||
<Underline className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={linethrough ? "default" : "outline"}
|
||||
size="icon"
|
||||
onClick={handleLinethroughChange}
|
||||
>
|
||||
<Strikethrough className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Line Height</Label>
|
||||
<Slider
|
||||
value={[lineHeight]}
|
||||
onValueChange={([value]) => handleLineHeightChange(value)}
|
||||
min={0.5}
|
||||
max={3}
|
||||
step={0.1}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Character Spacing</Label>
|
||||
<Slider
|
||||
value={[charSpacing]}
|
||||
onValueChange={([value]) => handleCharSpacingChange(value)}
|
||||
min={-20}
|
||||
max={100}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</CardContent>
|
||||
);
|
||||
return <div></div>;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="p-2">
|
||||
<CollapsibleComponent text={"Text Control"}>
|
||||
{content()}
|
||||
</CollapsibleComponent>
|
||||
<div className="mt-4 space-y-4">
|
||||
{previewText && (
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* New Toolbar Design */}
|
||||
<div className="flex w-full items-center space-x-2 rounded-lg p-1 bg-white">
|
||||
{/* Font Family Select */}
|
||||
<Select value={fontFamily} onValueChange={handleFontFamilyChange}>
|
||||
<SelectTrigger className="min-w-[140px] h-8">
|
||||
<SelectValue placeholder="Select a font" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="h-[250px]">
|
||||
<SelectGroup>
|
||||
{fonts.map((font) => (
|
||||
<SelectItem
|
||||
key={font}
|
||||
value={font}
|
||||
style={{ fontFamily: font }}
|
||||
>
|
||||
{font}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{/* Font Size Controls */}
|
||||
<div className="flex items-center border rounded-md">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-none"
|
||||
onClick={() => handleFontSizeChange(Math.max(8, fontSize - 1))}
|
||||
>
|
||||
<Minus className="h-4 w-4" />
|
||||
</Button>
|
||||
<Input
|
||||
type="text"
|
||||
value={fontSize}
|
||||
onChange={(e) => {
|
||||
const numericValue = e.target.value.replace(/\D/g, "");
|
||||
handleFontSizeChange(parseInt(numericValue) || 12);
|
||||
}}
|
||||
className="w-12 h-8 border-0 text-center"
|
||||
/>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-none"
|
||||
onClick={() => handleFontSizeChange(Math.min(72, fontSize + 1))}
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Vertical Separator */}
|
||||
<div className="h-6 w-px bg-gray-200" />
|
||||
|
||||
{/* Text Formatting Controls */}
|
||||
<div className="flex items-center space-x-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="relative"
|
||||
onClick={() => setSelectedPanel("color")}
|
||||
>
|
||||
<div className="relative">
|
||||
<span className="text-lg font-semibold">A</span>
|
||||
<div
|
||||
className="absolute -bottom-0.5 -left-1 right-0 h-1 w-5 transition-all duration-200"
|
||||
style={{ backgroundColor: fillColor }}
|
||||
/>
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant={textAlign === "left" ? "secondary" : "ghost"}
|
||||
size="icon"
|
||||
onClick={() => handleTextAlignChange("left")}
|
||||
>
|
||||
<AlignLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={textAlign === "center" ? "secondary" : "ghost"}
|
||||
size="icon"
|
||||
onClick={() => handleTextAlignChange("center")}
|
||||
>
|
||||
<AlignCenter className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={textAlign === "right" ? "secondary" : "ghost"}
|
||||
size="icon"
|
||||
onClick={() => handleTextAlignChange("right")}
|
||||
>
|
||||
<AlignRight className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={fontWeight === "bold" ? "secondary" : "ghost"}
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={handleFontWeightChange}
|
||||
>
|
||||
<Bold className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={underline ? "secondary" : "ghost"}
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={handleUnderlineChange}
|
||||
>
|
||||
<Underline className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={fontStyle === "italic" ? "secondary" : "ghost"}
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={handleFontStyleChange}
|
||||
>
|
||||
<Italic className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={linethrough ? "secondary" : "ghost"}
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={handleLinethroughChange}
|
||||
>
|
||||
<Strikethrough className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Vertical Separator */}
|
||||
<div className="h-6 w-px bg-gray-200" />
|
||||
|
||||
{/* Spacing Controls */}
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8">
|
||||
<RiLineHeight className="h-4 w-4" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-44 mt-3">
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label>Line Spacing</Label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Slider
|
||||
value={[lineHeight]}
|
||||
onValueChange={([value]) => handleLineHeightChange(value)}
|
||||
min={0.5}
|
||||
max={3}
|
||||
step={0.1}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Letter Spacing</Label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Slider
|
||||
value={[charSpacing]}
|
||||
onValueChange={([value]) =>
|
||||
handleCharSpacingChange(value)
|
||||
}
|
||||
min={-20}
|
||||
max={100}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
{/* Text Input */}
|
||||
{/* <div className="space-y-2">
|
||||
<Label htmlFor="text-content">Text Content</Label>
|
||||
<Input
|
||||
id="text-content"
|
||||
value={text}
|
||||
onChange={(e) => handleTextChange(e.target.value)}
|
||||
/>
|
||||
</div> */}
|
||||
|
||||
{/* Preview */}
|
||||
{/* {previewText && (
|
||||
<div className="p-4 border rounded-md overflow-hidden">
|
||||
<p className="font-bold mb-2">Preview:</p>
|
||||
<p
|
||||
|
|
@ -369,14 +407,17 @@ const TextCustomization = () => {
|
|||
{previewText}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
)} */}
|
||||
|
||||
<Button onClick={applyChanges} className="w-full">
|
||||
{/* Apply Changes Button */}
|
||||
{/* <Button onClick={applyChanges} className="w-full">
|
||||
Apply Changes
|
||||
</Button>
|
||||
</Button> */}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
return <div className="">{content()}</div>;
|
||||
};
|
||||
|
||||
export default TextCustomization;
|
||||
|
|
|
|||
29
src/components/Panel/ColorPanel.jsx
Normal file
29
src/components/Panel/ColorPanel.jsx
Normal file
|
|
@ -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 (
|
||||
<div>
|
||||
<div className="flex justify-between items-center p-4 border-b">
|
||||
<h2 className="text-lg font-semibold">Color</h2>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setSelectedPanel("")}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<ScrollArea className="lg:h-[calc(90vh-190px)] xl:h-[calc(100vh-190px)] px-4 py-4">
|
||||
<ApplyColor />
|
||||
</ScrollArea>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColorPanel;
|
||||
|
|
@ -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 (
|
||||
<div>
|
||||
<div className="space-y-5">
|
||||
<SelectObjectFromGroup />
|
||||
|
||||
{/* Apply fill and background color */}
|
||||
{activeObjectType !== "image" && !hasClipPath && !customClipPath && (
|
||||
<ApplyColor />
|
||||
)}
|
||||
|
||||
{/* Apply stroke and stroke color */}
|
||||
{!customClipPath && (
|
||||
<>
|
||||
|
|
@ -37,9 +45,8 @@ const CommonPanel = () => {
|
|||
|
||||
{/* Controls for opacity, flip, and rotation */}
|
||||
<Card className="shadow-none border-0">
|
||||
<CollapsibleComponent text={"Opacity, Flip, Rotate Control"}>
|
||||
<CollapsibleComponent text={"Flip, Rotate Control"}>
|
||||
<div className="space-y-2">
|
||||
<OpacityCustomization />
|
||||
<FlipCustomization />
|
||||
<RotateCustomization />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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 <TextPanel />;
|
||||
case "color":
|
||||
return <ColorPanel />;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
|
@ -17,7 +20,7 @@ const EditorPanel = () => {
|
|||
return (
|
||||
<>
|
||||
{selectedPanel !== "" && (
|
||||
<div className="w-80 h-[calc(100vh-32px)] bg-background rounded-xl shadow-lg mx-4 my-4">
|
||||
<div className="w-80 lg:h-[calc(90vh-100px)] xl:h-[calc(100vh-100px)] bg-background rounded-xl shadow-lg mx-4 my-auto">
|
||||
{renderPanel()}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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() {
|
|||
<div>
|
||||
<div className="flex justify-between items-center p-4 border-b">
|
||||
<h2 className="text-lg font-semibold">Text</h2>
|
||||
<Button variant="ghost" size="icon">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setSelectedPanel("")}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<ScrollArea className="h-[calc(100vh-115px)] px-4 py-4">
|
||||
<ScrollArea className="lg:h-[calc(90vh-190px)] xl:h-[calc(100vh-190px)] px-4 py-4">
|
||||
<Button
|
||||
className="w-full bg-[#FF2B85] hover:bg-[#FF2B85] text-white rounded-[10px] mb-6 h-12 font-medium text-xl"
|
||||
onClick={() => {
|
||||
|
|
@ -77,15 +85,14 @@ export default function TextPanel() {
|
|||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
{activeObject ? (
|
||||
<div className="space-y-4">
|
||||
<CommonPanel />
|
||||
<TextCustomization />
|
||||
</div>
|
||||
) : (
|
||||
{!open ? (
|
||||
<p className="text-sm font-semibold text-center">
|
||||
No active object found
|
||||
</p>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
<CommonPanel />
|
||||
</div>
|
||||
)}
|
||||
</ScrollArea>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="absolute top-4 left-[40%] -translate-x-1/2 z-40">
|
||||
<div className="bg-white rounded-[16px] shadow-sm px-4 py-2 flex items-center gap-2">
|
||||
<Select defaultValue="Albert Sans">
|
||||
<SelectTrigger className="w-[140px] h-9">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Albert Sans">Albert Sans</SelectItem>
|
||||
<SelectItem value="Arial">Arial</SelectItem>
|
||||
<SelectItem value="Helvetica">Helvetica</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div>
|
||||
<ScrollArea
|
||||
className={`absolute top-2 lg:left-[20%] ${
|
||||
selectedPanel !== ""
|
||||
? "lg:w-[600px] xl:w-[820px] xl:left-[40%]"
|
||||
: "w-[70%] xl:left-[50%]"
|
||||
} -translate-x-1/2 z-40 scrollbar-hide`}
|
||||
>
|
||||
<div className="bg-white rounded-[16px] shadow-sm px-4 py-2 flex items-center gap-2">
|
||||
<div>
|
||||
<TextCustomization />
|
||||
</div>
|
||||
<OpacityCustomization />
|
||||
<div className="h-4 w-px bg-border mx-2" />
|
||||
|
||||
<div className="flex items-center border rounded-lg h-9">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-9 w-9 rounded-none rounded-l-lg"
|
||||
>
|
||||
-
|
||||
</Button>
|
||||
<div className="px-3 py-1">12</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-9 w-9 rounded-none rounded-r-lg"
|
||||
>
|
||||
+
|
||||
</Button>
|
||||
<Select defaultValue="position">
|
||||
<SelectTrigger className="w-[100px] h-9">
|
||||
<SelectValue placeholder="Position" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="position">Position</SelectItem>
|
||||
<SelectItem value="front">Bring to Front</SelectItem>
|
||||
<SelectItem value="back">Send to Back</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<LockObject />
|
||||
</div>
|
||||
|
||||
<div className="h-4 w-px bg-border mx-2" />
|
||||
|
||||
<div className="flex items-center gap-0.5">
|
||||
<Button variant="ghost" size="icon" className="h-9 w-9">
|
||||
<Bold className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" className="h-9 w-9">
|
||||
<Italic className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" className="h-9 w-9">
|
||||
<Underline className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="h-4 w-px bg-border mx-2" />
|
||||
|
||||
<div className="flex items-center gap-0.5">
|
||||
<Button variant="ghost" size="icon" className="h-9 w-9">
|
||||
<AlignLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" className="h-9 w-9">
|
||||
<AlignCenter className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" className="h-9 w-9">
|
||||
<AlignRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="h-4 w-px bg-border mx-2" />
|
||||
|
||||
<Select defaultValue="position">
|
||||
<SelectTrigger className="w-[100px] h-9">
|
||||
<SelectValue placeholder="Position" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="position">Position</SelectItem>
|
||||
<SelectItem value="front">Bring to Front</SelectItem>
|
||||
<SelectItem value="back">Send to Back</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<Button variant="ghost" size="icon" className="h-9 w-9">
|
||||
<Lock className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<ScrollBar orientation="horizontal" />
|
||||
</ScrollArea>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
19
src/main.jsx
19
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(
|
||||
// <StrictMode>
|
||||
<CanvasContextProvider>
|
||||
<ColorContextProvider>
|
||||
|
|
@ -19,4 +18,4 @@ createRoot(document.getElementById('root')).render(
|
|||
</ColorContextProvider>
|
||||
</CanvasContextProvider>
|
||||
// </StrictMode>,
|
||||
)
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue