added fucntionality for text

This commit is contained in:
smfahim25 2025-01-27 17:33:55 +06:00
parent 954ac950b0
commit 79ca662a08
18 changed files with 826 additions and 656 deletions

10
package-lock.json generated
View file

@ -31,6 +31,7 @@
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-dropzone": "^14.3.5", "react-dropzone": "^14.3.5",
"react-icons": "^5.4.0",
"react-image-file-resizer": "^0.4.8", "react-image-file-resizer": "^0.4.8",
"react-rnd": "^10.4.13", "react-rnd": "^10.4.13",
"react-window": "^1.8.10", "react-window": "^1.8.10",
@ -7128,6 +7129,15 @@
"react": ">= 16.8 || 18.0.0" "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": { "node_modules/react-image-file-resizer": {
"version": "0.4.8", "version": "0.4.8",
"resolved": "https://registry.npmjs.org/react-image-file-resizer/-/react-image-file-resizer-0.4.8.tgz", "resolved": "https://registry.npmjs.org/react-image-file-resizer/-/react-image-file-resizer-0.4.8.tgz",

View file

@ -33,6 +33,7 @@
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-dropzone": "^14.3.5", "react-dropzone": "^14.3.5",
"react-icons": "^5.4.0",
"react-image-file-resizer": "^0.4.8", "react-image-file-resizer": "^0.4.8",
"react-rnd": "^10.4.13", "react-rnd": "^10.4.13",
"react-window": "^1.8.10", "react-window": "^1.8.10",

View file

@ -1,19 +1,20 @@
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect } from "react";
import "./App.css"; import "./App.css";
// import Canvas from "./components/Canvas"; // import Canvas from "./components/Canvas";
import WebFont from "webfontloader"; import WebFont from "webfontloader";
import Header from "./components/Layouts/Header"; // import Header from "./components/Layouts/Header";
import SheetRightPanel from "./components/Layouts/SheetRightPanel"; // import SheetRightPanel from "./components/Layouts/SheetRightPanel";
import SheetLeftPanel from "./components/Layouts/SheetLeftPanel"; // import SheetLeftPanel from "./components/Layouts/SheetLeftPanel";
import CanvasCapture from "./components/CanvasCapture"; // import CanvasCapture from "./components/CanvasCapture";
import { Toaster } from "./components/ui/toaster"; // import { Toaster } from "./components/ui/toaster";
import { Sidebar } from "./components/Layouts/LeftSidebar"; 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 { TopBar } from "./components/Panel/TopBar";
import { ActionButtons } from "./components/ActionButtons"; import { ActionButtons } from "./components/ActionButtons";
import EditorPanel from "./components/Panel/EditorPanel"; import EditorPanel from "./components/Panel/EditorPanel";
import CanvasContext from "./components/Context/canvasContext/CanvasContext"; import CanvasContext from "./components/Context/canvasContext/CanvasContext";
import Canvas from "./components/Canvas/Canvas";
import ActiveObjectContext from "./components/Context/activeObject/ObjectContext";
function App() { function App() {
useEffect(() => { useEffect(() => {
@ -75,7 +76,7 @@ function App() {
}, []); }, []);
const { selectedPanel } = useContext(CanvasContext); const { selectedPanel } = useContext(CanvasContext);
const [hasSelectedObject, setHasSelectedObject] = useState(true); const { activeObject } = useContext(ActiveObjectContext);
return ( return (
// <div className="relative flex flex-col h-screen overflow-hidden"> // <div className="relative flex flex-col h-screen overflow-hidden">
@ -96,7 +97,7 @@ function App() {
<Sidebar /> <Sidebar />
{selectedPanel !== "" && <EditorPanel />} {selectedPanel !== "" && <EditorPanel />}
<div className="flex-1 relative"> <div className="flex-1 relative">
<TopBar isVisible={hasSelectedObject} /> {activeObject && <TopBar />}
<ActionButtons /> <ActionButtons />
<Canvas /> <Canvas />
</div> </div>

View file

@ -3,8 +3,9 @@ import { AspectRatio } from "@/components/ui/aspect-ratio";
import OpenContext from "../Context/openContext/OpenContext"; import OpenContext from "../Context/openContext/OpenContext";
import CanvasContext from "../Context/canvasContext/CanvasContext"; import CanvasContext from "../Context/canvasContext/CanvasContext";
import { Card, CardContent } from "../ui/card"; import { Card, CardContent } from "../ui/card";
import ActiveObjectContext from "../Context/activeObject/ObjectContext";
export function Canvas() { export default function Canvas() {
const { const {
setLeftPanelOpen, setLeftPanelOpen,
setRightPanelOpen, setRightPanelOpen,
@ -24,6 +25,36 @@ export function Canvas() {
setScreenWidth, setScreenWidth,
} = useContext(CanvasContext); } = 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(() => { useEffect(() => {
import("fabric").then((fabricModule) => { import("fabric").then((fabricModule) => {
window.fabric = fabricModule.fabric; window.fabric = fabricModule.fabric;
@ -127,7 +158,11 @@ export function Canvas() {
}, []); }, []);
return ( 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"> <CardContent className="p-0 space-y-2">
<AspectRatio <AspectRatio
ratio={getRatioValue(canvasRatio)} ratio={getRatioValue(canvasRatio)}

View file

@ -1,11 +1,14 @@
import { useState } from 'react' import { useState } from "react";
import ActiveObjectContext from './ObjectContext' import ActiveObjectContext from "./ObjectContext";
export const ObjectProvider = ({ children }) => { const ObjectProvider = ({ children }) => {
const [activeObject, setActiveObject] = useState(null); const [activeObject, setActiveObject] = useState(null);
return ( return (
<ActiveObjectContext.Provider value={{ activeObject, setActiveObject }}> <ActiveObjectContext.Provider value={{ activeObject, setActiveObject }}>
{children} {children}
</ActiveObjectContext.Provider> </ActiveObjectContext.Provider>
) );
} };
export default ObjectProvider;

View file

@ -9,6 +9,7 @@ const CanvasContextProvider = ({ children }) => {
const [screenWidth, setScreenWidth] = useState(0); const [screenWidth, setScreenWidth] = useState(0);
const [canvasRatio, setCanvasRatio] = useState("4:3"); const [canvasRatio, setCanvasRatio] = useState("4:3");
const [selectedPanel, setSelectedPanel] = useState(""); const [selectedPanel, setSelectedPanel] = useState("");
const [textColor, setTextColor] = useState(null);
const fabricCanvasRef = useRef(null); const fabricCanvasRef = useRef(null);
return ( return (
@ -21,6 +22,8 @@ const CanvasContextProvider = ({ children }) => {
canvasHeight, canvasHeight,
canvasRatio, canvasRatio,
setCanvasRatio, setCanvasRatio,
textColor,
setTextColor,
selectedPanel, selectedPanel,
setSelectedPanel, setSelectedPanel,
setCanvasHeight, setCanvasHeight,

View file

@ -1,16 +1,20 @@
import { useContext, useEffect, useState } from 'react' import { useCallback, useContext, useEffect, useState } from "react";
import ActiveObjectContext from '../Context/activeObject/ObjectContext'; import ActiveObjectContext from "../Context/activeObject/ObjectContext";
import CanvasContext from '../Context/canvasContext/CanvasContext'; import CanvasContext from "../Context/canvasContext/CanvasContext";
import { fabric } from 'fabric'; import { fabric } from "fabric";
import { debounce } from "lodash"; import { Separator } from "../ui/separator";
import { Separator } from '../ui/separator'; import { Label } from "../ui/label";
import { Label } from '../ui/label'; import {
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'; Select,
import { Input } from '../ui/input'; SelectContent,
import { Card } from '../ui/card'; SelectItem,
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs'; SelectTrigger,
import { Button } from '../ui/button'; SelectValue,
import CollapsibleComponent from './Customization/CollapsibleComponent'; } 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 ApplyColor = () => {
const [colorField, setColorField] = useState("fill"); const [colorField, setColorField] = useState("fill");
@ -22,11 +26,12 @@ const ApplyColor = () => {
}); });
const [colorType, setColorType] = useState("color"); // 'color' or 'gradient' const [colorType, setColorType] = useState("color"); // 'color' or 'gradient'
const [gradientDirection, setGradientDirection] = useState("top-to-bottom"); const [gradientDirection, setGradientDirection] = useState("top-to-bottom");
// get values from context
const { activeObject } = useContext(ActiveObjectContext);
const { canvas } = useContext(CanvasContext);
// to get previous values from active object // Get values from context
const { activeObject, setActiveObject } = useContext(ActiveObjectContext);
const { canvas, setTextColor } = useContext(CanvasContext);
// To get previous values from active object
useEffect(() => { useEffect(() => {
const handleObjectStyle = (object) => { const handleObjectStyle = (object) => {
if (object.fill) { if (object.fill) {
@ -73,39 +78,18 @@ const ApplyColor = () => {
} }
}, [activeObject, colorField]); }, [activeObject, colorField]);
const handleSolidColorChange = debounce((newColor) => {
if (colorField === "fill") {
setFillColor(newColor)
}
else {
setBackgroundColor(newColor)
}
}, 100);
// 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);
};
// Apply color/gradient style to the selected object on the canvas // Apply color/gradient style to the selected object on the canvas
const applyColor = () => { const applyColor = useCallback(() => {
const applyStyleToObject = (object, style) => { const applyStyleToObject = (object, style) => {
if (colorType === "color") { if (colorType === "color") {
if (colorField === "fill" && object.fill !== style.fill) { if (colorField === "fill" && object.fill !== style.fill) {
object.set("fill", style.fill); object.set("fill", style.fill);
} }
if (colorField === "background" && object.backgroundColor !== style.backgroundColor) { if (
colorField === "background" &&
object.backgroundColor !== style.backgroundColor
) {
object.set("backgroundColor", style.backgroundColor); object.set("backgroundColor", style.backgroundColor);
} }
} }
@ -151,11 +135,51 @@ const ApplyColor = () => {
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
}; };
if (activeObject) {
applyStyleRecursively(activeObject, style); applyStyleRecursively(activeObject, style);
setActiveObject(activeObject);
// Trigger re-render for the canvas setTextColor(style);
canvas.renderAll(); 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 = () => { const content = () => {
return ( return (
@ -164,7 +188,10 @@ const ApplyColor = () => {
<div className="space-y-2"> <div className="space-y-2">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="colorField">Color Field</Label> <Label htmlFor="colorField">Color Field</Label>
<Select value={colorField} onValueChange={(value) => setColorField(value)}> <Select
value={colorField}
onValueChange={(value) => setColorField(value)}
>
<SelectTrigger id="colorField"> <SelectTrigger id="colorField">
<SelectValue placeholder="Select color field type" /> <SelectValue placeholder="Select color field type" />
</SelectTrigger> </SelectTrigger>
@ -175,10 +202,18 @@ const ApplyColor = () => {
</Select> </Select>
</div> </div>
<Tabs value={colorType} onValueChange={(value) => setColorType(value)}> <Tabs
value={colorType}
onValueChange={(value) => setColorType(value)}
>
<TabsList className="grid w-full grid-cols-2"> <TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="color">Solid Color</TabsTrigger> <TabsTrigger value="color">Solid Color</TabsTrigger>
<TabsTrigger value="gradient" disabled={colorField === 'background'}>Gradient</TabsTrigger> <TabsTrigger
value="gradient"
disabled={colorField === "background"}
>
Gradient
</TabsTrigger>
</TabsList> </TabsList>
<TabsContent value="color" className="space-y-4"> <TabsContent value="color" className="space-y-4">
@ -188,36 +223,55 @@ const ApplyColor = () => {
<Input <Input
id="solidColor" id="solidColor"
type="color" type="color"
value={colorField === "fill" ? fillColor : backgroundColor} value={
colorField === "fill" ? fillColor : backgroundColor
}
onChange={(e) => handleSolidColorChange(e.target.value)} onChange={(e) => handleSolidColorChange(e.target.value)}
className="w-12 h-12 p-1 rounded-md" className="w-12 h-12 p-1 rounded-md"
/> />
<Input <Input
type="text" type="text"
value={colorField === "fill" ? fillColor : backgroundColor} value={
colorField === "fill" ? fillColor : backgroundColor
}
onChange={(e) => handleSolidColorChange(e.target.value)} onChange={(e) => handleSolidColorChange(e.target.value)}
className="flex-grow" className="flex-grow"
/> />
</div> </div>
</div> </div>
<div className="h-24 rounded-md" style={{ <div
backgroundColor: colorField === "fill" ? fillColor : backgroundColor, className="h-24 rounded-md"
}}></div> style={{
backgroundColor:
colorField === "fill" ? fillColor : backgroundColor,
}}
></div>
</TabsContent> </TabsContent>
<TabsContent value="gradient" className="space-y-4"> <TabsContent value="gradient" className="space-y-4">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="gradientDirection">Direction</Label> <Label htmlFor="gradientDirection">Direction</Label>
<Select value={gradientDirection} onValueChange={setGradientDirection}> <Select
value={gradientDirection}
onValueChange={setGradientDirection}
>
<SelectTrigger id="gradientDirection"> <SelectTrigger id="gradientDirection">
<SelectValue placeholder="Select direction" /> <SelectValue placeholder="Select direction" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="top-to-bottom">Top to Bottom</SelectItem> <SelectItem value="top-to-bottom">
<SelectItem value="bottom-to-top">Bottom to Top</SelectItem> Top to Bottom
<SelectItem value="left-to-right">Left to Right</SelectItem> </SelectItem>
<SelectItem value="right-to-left">Right to Left</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> </SelectContent>
</Select> </Select>
</div> </div>
@ -228,13 +282,17 @@ const ApplyColor = () => {
id="gradientColor1" id="gradientColor1"
type="color" type="color"
value={gradientFillColors.color1} value={gradientFillColors.color1}
onChange={handleColorChange('color1')} onChange={(e) =>
handleGradientColorChange("color1", e.target.value)
}
className="w-12 h-12 p-1 rounded-md" className="w-12 h-12 p-1 rounded-md"
/> />
<Input <Input
type="text" type="text"
value={gradientFillColors.color1} value={gradientFillColors.color1}
onChange={handleColorChange('color1')} onChange={(e) =>
handleGradientColorChange("color1", e.target.value)
}
className="flex-grow" className="flex-grow"
/> />
</div> </div>
@ -246,44 +304,55 @@ const ApplyColor = () => {
id="gradientColor2" id="gradientColor2"
type="color" type="color"
value={gradientFillColors.color2} value={gradientFillColors.color2}
onChange={handleColorChange('color2')} onChange={(e) =>
handleGradientColorChange("color2", e.target.value)
}
className="w-12 h-12 p-1 rounded-md" className="w-12 h-12 p-1 rounded-md"
/> />
<Input <Input
type="text" type="text"
value={gradientFillColors.color2} value={gradientFillColors.color2}
onChange={handleColorChange('color2')} onChange={(e) =>
handleGradientColorChange("color2", e.target.value)
}
className="flex-grow" className="flex-grow"
/> />
</div> </div>
</div> </div>
<div className="h-24 rounded-md" style={{ <div
background: `linear-gradient(${gradientDirection === 'top-to-bottom' ? 'to bottom' : className="h-24 rounded-md"
gradientDirection === 'bottom-to-top' ? 'to top' : style={{
gradientDirection === 'left-to-right' ? 'to right' : background: `linear-gradient(${
'to left' gradientDirection === "top-to-bottom"
}, ${gradientFillColors.color1}, ${gradientFillColors.color2})`, ? "to bottom"
}}></div> : gradientDirection === "bottom-to-top"
? "to top"
: gradientDirection === "left-to-right"
? "to right"
: "to left"
}, ${gradientFillColors.color1}, ${
gradientFillColors.color2
})`,
}}
></div>
</TabsContent> </TabsContent>
</Tabs> </Tabs>
</div> </div>
<Button className="my-2" onClick={() => { applyColor() }}>Apply</Button>
</div> </div>
</div> </div>
) );
} };
return ( return (
<div> <div>
<Card className="p-2"> <Card className="border-0 shadow-none">
<CollapsibleComponent text={"Color Control"}> <CollapsibleComponent text={"Color Control"}>
{content()} {content()}
</CollapsibleComponent> </CollapsibleComponent>
</Card> </Card>
<Separator className="my-2" /> <Separator className="my-2" />
</div> </div>
) );
} };
export default ApplyColor export default ApplyColor;

View file

@ -18,7 +18,7 @@ const CollapsibleComponent = ({ children, text }) => {
}, [text]); }, [text]);
return ( return (
<Collapsible open={isOpen} onOpenChange={setIsOpen}> <Collapsible open={isOpen}>
<CollapsibleTrigger asChild> <CollapsibleTrigger asChild>
<div <div
className={`flex items-center justify-between cursor-pointer ${ className={`flex items-center justify-between cursor-pointer ${

View file

@ -4,7 +4,6 @@ import { Button } from "@/components/ui/button";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { Lock, Unlock } from "lucide-react"; import { Lock, Unlock } from "lucide-react";
import { Card } from "@/components/ui/card";
const LockObject = () => { const LockObject = () => {
const { canvas } = useContext(CanvasContext); const { canvas } = useContext(CanvasContext);
@ -51,8 +50,7 @@ const LockObject = () => {
}; };
return ( return (
<Card className="shadow-none border-0"> <div className="shadow-none border-0">
<h2 className="font-bold">{!isLocked ? "Lock" : "Unlock"} Object</h2>
<Button <Button
onClick={toggleLock} onClick={toggleLock}
variant="outline" variant="outline"
@ -66,7 +64,7 @@ const LockObject = () => {
<Lock className="h-4 w-4" /> <Lock className="h-4 w-4" />
)} )}
</Button> </Button>
</Card> </div>
); );
}; };

View file

@ -1,46 +1,61 @@
import ActiveObjectContext from '@/components/Context/activeObject/ObjectContext'; import ActiveObjectContext from "@/components/Context/activeObject/ObjectContext";
import CanvasContext from '@/components/Context/canvasContext/CanvasContext'; import CanvasContext from "@/components/Context/canvasContext/CanvasContext";
import { Label } from '@/components/ui/label'; import { Label } from "@/components/ui/label";
import { Slider } from '@/components/ui/slider'; import { Slider } from "@/components/ui/slider";
import { useContext, useEffect, useState } from 'react' 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 OpacityCustomization = () => {
const { activeObject } = useContext(ActiveObjectContext); const { activeObject } = useContext(ActiveObjectContext);
const { canvas } = useContext(CanvasContext); const { canvas } = useContext(CanvasContext);
const [opacity, setOpacity] = useState(0); const [opacity, setOpacity] = useState(0);
useEffect(() => { useEffect(() => {
if (activeObject) { if (activeObject) {
setOpacity(activeObject?.opacity); setOpacity(activeObject?.opacity);
} }
}, [activeObject]) }, [activeObject]);
const adjustBackgroundOpacity = (value) => { const adjustBackgroundOpacity = (newOpacity) => {
setOpacity(value); setOpacity(newOpacity);
if (activeObject) { if (activeObject) {
activeObject.set("opacity", opacity); // Update the opacity activeObject.set("opacity", newOpacity);
canvas.renderAll(); // Re-render the canvas canvas.renderAll();
} }
}; };
return ( return (
<div className='grid gap-2 pt-1'> <Popover>
<Label> <PopoverTrigger asChild>
Adjust Opacity: <Button variant="ghost" size="icon" className="h-8 w-8">
</Label> <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 <Slider
value={[opacity]} value={[opacity]}
min={0.0} min={0}
max={1.0} max={1}
step={0.01} // Step size for fine control step={0.01}
onValueChange={(value) => { onValueChange={(value) => adjustBackgroundOpacity(value[0])}
const newOpacity = value[0]; // Extract slider value
adjustBackgroundOpacity(newOpacity); // Adjust Fabric.js background opacity
}}
/> />
</div> </div>
) </PopoverContent>
} </Popover>
);
};
export default OpacityCustomization export default OpacityCustomization;

View file

@ -47,7 +47,7 @@ const StrokeCustomization = () => {
} }
} }
if (object.strokeWidth) { if (object.strokeWidth) {
setStrokeWidth(object.strokeWidth || 0); setStrokeWidth(0);
} }
}; };

View file

@ -1,7 +1,7 @@
import ActiveObjectContext from "@/components/Context/activeObject/ObjectContext"; import ActiveObjectContext from "@/components/Context/activeObject/ObjectContext";
import CanvasContext from "@/components/Context/canvasContext/CanvasContext"; import CanvasContext from "@/components/Context/canvasContext/CanvasContext";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { import {
@ -12,9 +12,12 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Slider } from "@/components/ui/slider"; import {
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; Popover,
import { useContext, useEffect, useState } from "react"; PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { useCallback, useContext, useEffect, useState } from "react";
import { import {
AlignLeft, AlignLeft,
AlignCenter, AlignCenter,
@ -23,8 +26,11 @@ import {
Italic, Italic,
Underline, Underline,
Strikethrough, Strikethrough,
Minus,
Plus,
} from "lucide-react"; } from "lucide-react";
import CollapsibleComponent from "./CollapsibleComponent"; import { RiLineHeight } from "react-icons/ri";
import { Slider } from "@/components/ui/slider";
const fonts = [ const fonts = [
"Roboto", "Roboto",
@ -79,9 +85,8 @@ const fonts = [
]; ];
const TextCustomization = () => { const TextCustomization = () => {
// get values from context
const { activeObject } = useContext(ActiveObjectContext); const { activeObject } = useContext(ActiveObjectContext);
const { canvas } = useContext(CanvasContext); const { canvas, setSelectedPanel, textColor } = useContext(CanvasContext);
const [text, setText] = useState(""); const [text, setText] = useState("");
const [fontFamily, setFontFamily] = useState("Arial"); const [fontFamily, setFontFamily] = useState("Arial");
@ -93,7 +98,7 @@ const TextCustomization = () => {
const [underline, setUnderline] = useState(false); const [underline, setUnderline] = useState(false);
const [linethrough, setLinethrough] = useState(false); const [linethrough, setLinethrough] = useState(false);
const [textAlign, setTextAlign] = useState("left"); const [textAlign, setTextAlign] = useState("left");
const [previewText, setPreviewText] = useState(""); const [fillColor, setFillColor] = useState("black");
useEffect(() => { useEffect(() => {
if (activeObject?.type === "i-text") { if (activeObject?.type === "i-text") {
@ -107,18 +112,21 @@ const TextCustomization = () => {
setUnderline(activeObject?.underline || false); setUnderline(activeObject?.underline || false);
setLinethrough(activeObject?.linethrough || false); setLinethrough(activeObject?.linethrough || false);
setTextAlign(activeObject?.textAlign || "left"); setTextAlign(activeObject?.textAlign || "left");
setPreviewText(activeObject?.text || ""); setFillColor(textColor?.fill || "black");
} }
}, [activeObject]); }, [activeObject, textColor]);
const updateActiveObject = (properties) => { const updateActiveObject = useCallback(
(properties) => {
if (activeObject?.type === "i-text") { if (activeObject?.type === "i-text") {
activeObject.set(properties); activeObject.set(properties);
canvas?.renderAll(); canvas?.renderAll();
} }
}; },
[activeObject, canvas]
); // Add dependencies
const applyChanges = () => { const applyChanges = useCallback(() => {
updateActiveObject({ updateActiveObject({
text, text,
fontFamily, fontFamily,
@ -131,12 +139,29 @@ const TextCustomization = () => {
linethrough, linethrough,
textAlign, textAlign,
}); });
}; }, [
text,
fontFamily,
fontSize,
fontStyle,
fontWeight,
lineHeight,
charSpacing,
underline,
linethrough,
textAlign,
updateActiveObject, // Add this dependency
]);
const handleTextChange = (newText) => { // Automatically apply changes when state updates
setText(newText); useEffect(() => {
setPreviewText(newText); if (activeObject?.type === "i-text") {
}; applyChanges();
}
}, [
applyChanges, // Now included in dependencies
activeObject?.type, // Track active object type
]);
const handleFontFamilyChange = (newFontFamily) => { const handleFontFamilyChange = (newFontFamily) => {
setFontFamily(newFontFamily); setFontFamily(newFontFamily);
@ -151,80 +176,50 @@ const TextCustomization = () => {
}; };
const handleFontStyleChange = () => { const handleFontStyleChange = () => {
const newFontStyle = fontStyle === "normal" ? "italic" : "normal"; setFontStyle(fontStyle === "normal" ? "italic" : "normal");
setFontStyle(newFontStyle);
}; };
const handleFontWeightChange = () => { const handleFontWeightChange = () => {
const newFontWeight = fontWeight === "normal" ? "bold" : "normal"; setFontWeight(fontWeight === "normal" ? "bold" : "normal");
setFontWeight(newFontWeight);
}; };
const handleLineHeightChange = (newLineHeight) => { const handleLineHeightChange = (newLineHeight) => {
setLineHeight(newLineHeight); setLineHeight(parseFloat(newLineHeight));
}; };
const handleCharSpacingChange = (newCharSpacing) => { const handleCharSpacingChange = (newCharSpacing) => {
setCharSpacing(newCharSpacing); setCharSpacing(parseInt(newCharSpacing));
}; };
const handleUnderlineChange = () => { const handleUnderlineChange = () => {
const newUnderline = !underline; setUnderline(!underline);
setUnderline(newUnderline);
}; };
const handleLinethroughChange = () => { const handleLinethroughChange = () => {
const newLinethrough = !linethrough; setLinethrough(!linethrough);
setLinethrough(newLinethrough);
}; };
const content = () => { const content = () => {
if (!(activeObject?.type === "i-text")) { if (!(activeObject?.type === "i-text")) {
return ( return <div></div>;
<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"> return (
<div className="space-y-1"> <div className="space-y-4">
<Label htmlFor="text-content">Text Content</Label> {/* New Toolbar Design */}
<Input <div className="flex w-full items-center space-x-2 rounded-lg p-1 bg-white">
id="text-content" {/* Font Family Select */}
value={text} <Select value={fontFamily} onValueChange={handleFontFamilyChange}>
onChange={(e) => handleTextChange(e.target.value)} <SelectTrigger className="min-w-[140px] h-8">
/>
</div>
<div className="space-y-1">
<Label>Font Family</Label>
<Select
value={fontFamily}
onValueChange={handleFontFamilyChange}
>
<SelectTrigger>
<SelectValue placeholder="Select a font" /> <SelectValue placeholder="Select a font" />
</SelectTrigger> </SelectTrigger>
<SelectContent className="h-[250px]"> <SelectContent className="h-[250px]">
<SelectGroup> <SelectGroup>
{fonts.map((font) => ( {fonts.map((font) => (
<SelectItem <SelectItem
style={{ fontFamily: font }}
key={font} key={font}
value={font} value={font}
style={{ fontFamily: font }}
> >
{font} {font}
</SelectItem> </SelectItem>
@ -232,91 +227,126 @@ const TextCustomization = () => {
</SelectGroup> </SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div>
<div className="space-y-1"> {/* Font Size Controls */}
<Label>Font Size</Label> <div className="flex items-center border rounded-md">
<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 <Button
variant={textAlign === "left" ? "default" : "outline"} 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" size="icon"
onClick={() => handleTextAlignChange("left")} onClick={() => handleTextAlignChange("left")}
> >
<AlignLeft className="h-4 w-4" /> <AlignLeft className="h-4 w-4" />
</Button> </Button>
<Button <Button
variant={textAlign === "center" ? "default" : "outline"} variant={textAlign === "center" ? "secondary" : "ghost"}
size="icon" size="icon"
onClick={() => handleTextAlignChange("center")} onClick={() => handleTextAlignChange("center")}
> >
<AlignCenter className="h-4 w-4" /> <AlignCenter className="h-4 w-4" />
</Button> </Button>
<Button <Button
variant={textAlign === "right" ? "default" : "outline"} variant={textAlign === "right" ? "secondary" : "ghost"}
size="icon" size="icon"
onClick={() => handleTextAlignChange("right")} onClick={() => handleTextAlignChange("right")}
> >
<AlignRight className="h-4 w-4" /> <AlignRight className="h-4 w-4" />
</Button> </Button>
</div>
</div>
<div className="flex items-center justify-between">
<Label>Text Style</Label>
<div className="flex space-x-1">
<Button <Button
variant={fontWeight === "bold" ? "default" : "outline"} variant={fontWeight === "bold" ? "secondary" : "ghost"}
size="icon" size="icon"
className="h-8 w-8"
onClick={handleFontWeightChange} onClick={handleFontWeightChange}
> >
<Bold className="h-4 w-4" /> <Bold className="h-4 w-4" />
</Button> </Button>
<Button <Button
variant={fontStyle === "italic" ? "default" : "outline"} variant={underline ? "secondary" : "ghost"}
size="icon"
onClick={handleFontStyleChange}
>
<Italic className="h-4 w-4" />
</Button>
<Button
variant={underline ? "default" : "outline"}
size="icon" size="icon"
className="h-8 w-8"
onClick={handleUnderlineChange} onClick={handleUnderlineChange}
> >
<Underline className="h-4 w-4" /> <Underline className="h-4 w-4" />
</Button> </Button>
<Button <Button
variant={linethrough ? "default" : "outline"} variant={fontStyle === "italic" ? "secondary" : "ghost"}
size="icon" 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} onClick={handleLinethroughChange}
> >
<Strikethrough className="h-4 w-4" /> <Strikethrough className="h-4 w-4" />
</Button> </Button>
</div> </div>
</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"> <div className="space-y-2">
<Label>Line Height</Label> <Label>Line Spacing</Label>
<div className="flex items-center space-x-2">
<Slider <Slider
value={[lineHeight]} value={[lineHeight]}
onValueChange={([value]) => handleLineHeightChange(value)} onValueChange={([value]) => handleLineHeightChange(value)}
@ -325,30 +355,38 @@ const TextCustomization = () => {
step={0.1} step={0.1}
/> />
</div> </div>
</div>
<div className="space-y-2"> <div className="space-y-2">
<Label>Character Spacing</Label> <Label>Letter Spacing</Label>
<div className="flex items-center space-x-2">
<Slider <Slider
value={[charSpacing]} value={[charSpacing]}
onValueChange={([value]) => handleCharSpacingChange(value)} onValueChange={([value]) =>
handleCharSpacingChange(value)
}
min={-20} min={-20}
max={100} max={100}
step={1} step={1}
/> />
</div> </div>
</TabsContent> </div>
</Tabs> </div>
</CardContent> </PopoverContent>
); </Popover>
} </div>
};
return ( {/* Text Input */}
<Card className="p-2"> {/* <div className="space-y-2">
<CollapsibleComponent text={"Text Control"}> <Label htmlFor="text-content">Text Content</Label>
{content()} <Input
</CollapsibleComponent> id="text-content"
<div className="mt-4 space-y-4"> value={text}
{previewText && ( onChange={(e) => handleTextChange(e.target.value)}
/>
</div> */}
{/* Preview */}
{/* {previewText && (
<div className="p-4 border rounded-md overflow-hidden"> <div className="p-4 border rounded-md overflow-hidden">
<p className="font-bold mb-2">Preview:</p> <p className="font-bold mb-2">Preview:</p>
<p <p
@ -369,14 +407,17 @@ const TextCustomization = () => {
{previewText} {previewText}
</p> </p>
</div> </div>
)} )} */}
<Button onClick={applyChanges} className="w-full"> {/* Apply Changes Button */}
{/* <Button onClick={applyChanges} className="w-full">
Apply Changes Apply Changes
</Button> </Button> */}
</div> </div>
</Card>
); );
};
return <div className="">{content()}</div>;
}; };
export default TextCustomization; export default TextCustomization;

View 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;

View file

@ -5,23 +5,31 @@ import StrokeCustomization from "../EachComponent/Customization/StrokeCustomizat
import PositionCustomization from "../EachComponent/Customization/PositionCustomization"; import PositionCustomization from "../EachComponent/Customization/PositionCustomization";
import { Card } from "../ui/card"; import { Card } from "../ui/card";
import CollapsibleComponent from "../EachComponent/Customization/CollapsibleComponent"; import CollapsibleComponent from "../EachComponent/Customization/CollapsibleComponent";
import OpacityCustomization from "../EachComponent/Customization/OpacityCustomization";
import FlipCustomization from "../EachComponent/Customization/FlipCustomization"; import FlipCustomization from "../EachComponent/Customization/FlipCustomization";
import RotateCustomization from "../EachComponent/Customization/RotateCustomization"; import RotateCustomization from "../EachComponent/Customization/RotateCustomization";
import SkewCustomization from "../EachComponent/Customization/SkewCustomization"; import SkewCustomization from "../EachComponent/Customization/SkewCustomization";
import ScaleObjects from "../EachComponent/Customization/ScaleObjects"; import ScaleObjects from "../EachComponent/Customization/ScaleObjects";
import ShadowCustomization from "../EachComponent/Customization/ShadowCustomization"; import ShadowCustomization from "../EachComponent/Customization/ShadowCustomization";
import AddImageIntoShape from "../EachComponent/Customization/AddImageIntoShape"; import AddImageIntoShape from "../EachComponent/Customization/AddImageIntoShape";
import ApplyColor from "../EachComponent/ApplyColor";
const CommonPanel = () => { const CommonPanel = () => {
const { canvas } = useContext(CanvasContext); const { canvas } = useContext(CanvasContext);
const activeObject = canvas?.getActiveObject(); const activeObject = canvas?.getActiveObject();
const activeObjectType = activeObject?.type;
const hasClipPath = !!activeObject?.clipPath;
const customClipPath = activeObject?.isClipPath; const customClipPath = activeObject?.isClipPath;
return ( return (
<div> <div>
<div className="space-y-5"> <div className="space-y-5">
<SelectObjectFromGroup /> <SelectObjectFromGroup />
{/* Apply fill and background color */}
{activeObjectType !== "image" && !hasClipPath && !customClipPath && (
<ApplyColor />
)}
{/* Apply stroke and stroke color */} {/* Apply stroke and stroke color */}
{!customClipPath && ( {!customClipPath && (
<> <>
@ -37,9 +45,8 @@ const CommonPanel = () => {
{/* Controls for opacity, flip, and rotation */} {/* Controls for opacity, flip, and rotation */}
<Card className="shadow-none border-0"> <Card className="shadow-none border-0">
<CollapsibleComponent text={"Opacity, Flip, Rotate Control"}> <CollapsibleComponent text={"Flip, Rotate Control"}>
<div className="space-y-2"> <div className="space-y-2">
<OpacityCustomization />
<FlipCustomization /> <FlipCustomization />
<RotateCustomization /> <RotateCustomization />
</div> </div>

View file

@ -1,6 +1,7 @@
import { useContext } from "react"; import { useContext } from "react";
import CanvasContext from "../Context/canvasContext/CanvasContext"; import CanvasContext from "../Context/canvasContext/CanvasContext";
import TextPanel from "./TextPanel"; import TextPanel from "./TextPanel";
import ColorPanel from "./ColorPanel";
const EditorPanel = () => { const EditorPanel = () => {
const { selectedPanel } = useContext(CanvasContext); const { selectedPanel } = useContext(CanvasContext);
@ -9,6 +10,8 @@ const EditorPanel = () => {
switch (selectedPanel) { switch (selectedPanel) {
case "text": case "text":
return <TextPanel />; return <TextPanel />;
case "color":
return <ColorPanel />;
default: default:
return; return;
} }
@ -17,7 +20,7 @@ const EditorPanel = () => {
return ( return (
<> <>
{selectedPanel !== "" && ( {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()} {renderPanel()}
</div> </div>
)} )}

View file

@ -1,21 +1,25 @@
import { Button } from "../ui/Button"; import { Button } from "../ui/Button";
import { X } from "lucide-react"; import { X } from "lucide-react";
import { ScrollArea } from "../ui/scroll-area"; import { ScrollArea } from "../ui/scroll-area";
import { useContext } from "react"; import { useContext, useEffect, useState } from "react";
import CanvasContext from "../Context/canvasContext/CanvasContext"; import CanvasContext from "../Context/canvasContext/CanvasContext";
import ActiveObjectContext from "../Context/activeObject/ObjectContext"; import ActiveObjectContext from "../Context/activeObject/ObjectContext";
import { fabric } from "fabric"; import { fabric } from "fabric";
import CommonPanel from "./CommonPanel"; import CommonPanel from "./CommonPanel";
import TextCustomization from "../EachComponent/Customization/TextCustomization";
export default function TextPanel() { export default function TextPanel() {
const { canvas } = useContext(CanvasContext); const { canvas, setSelectedPanel } = useContext(CanvasContext);
const { setActiveObject } = useContext(ActiveObjectContext); const { activeObject, setActiveObject } = useContext(ActiveObjectContext);
const activeObject = canvas?.getActiveObject();
const activeObjectType = activeObject?.type;
const hasClipPath = !!activeObject?.clipPath;
const customClipPath = activeObject?.isClipPath;
const [open, setOpen] = useState(false);
useEffect(() => {
if (activeObject) {
setOpen(true);
} else {
setOpen(false);
}
}, [activeObject]);
const addText = () => { const addText = () => {
if (canvas) { if (canvas) {
const text = new fabric.IText("Editable Text", { const text = new fabric.IText("Editable Text", {
@ -37,12 +41,16 @@ export default function TextPanel() {
<div> <div>
<div className="flex justify-between items-center p-4 border-b"> <div className="flex justify-between items-center p-4 border-b">
<h2 className="text-lg font-semibold">Text</h2> <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" /> <X className="h-4 w-4" />
</Button> </Button>
</div> </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 <Button
className="w-full bg-[#FF2B85] hover:bg-[#FF2B85] text-white rounded-[10px] mb-6 h-12 font-medium text-xl" className="w-full bg-[#FF2B85] hover:bg-[#FF2B85] text-white rounded-[10px] mb-6 h-12 font-medium text-xl"
onClick={() => { onClick={() => {
@ -77,15 +85,14 @@ export default function TextPanel() {
/> />
</svg> </svg>
</Button> </Button>
{activeObject ? ( {!open ? (
<div className="space-y-4">
<CommonPanel />
<TextCustomization />
</div>
) : (
<p className="text-sm font-semibold text-center"> <p className="text-sm font-semibold text-center">
No active object found No active object found
</p> </p>
) : (
<div className="space-y-4">
<CommonPanel />
</div>
)} )}
</ScrollArea> </ScrollArea>
</div> </div>

View file

@ -1,13 +1,3 @@
import {
AlignCenter,
AlignLeft,
AlignRight,
Bold,
Italic,
Lock,
Underline,
} from "lucide-react";
import { Button } from "../ui/Button";
import { import {
Select, Select,
SelectContent, SelectContent,
@ -15,70 +5,29 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "../ui/Select"; } 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 }) { export function TopBar() {
if (!isVisible) return null; const { selectedPanel } = useContext(CanvasContext);
return ( return (
<div className="absolute top-4 left-[40%] -translate-x-1/2 z-40"> <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 className="bg-white rounded-[16px] shadow-sm px-4 py-2 flex items-center gap-2">
<Select defaultValue="Albert Sans"> <div>
<SelectTrigger className="w-[140px] h-9"> <TextCustomization />
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="Albert Sans">Albert Sans</SelectItem>
<SelectItem value="Arial">Arial</SelectItem>
<SelectItem value="Helvetica">Helvetica</SelectItem>
</SelectContent>
</Select>
<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>
</div> </div>
<OpacityCustomization />
<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" /> <div className="h-4 w-px bg-border mx-2" />
<Select defaultValue="position"> <Select defaultValue="position">
@ -92,10 +41,10 @@ export function TopBar({ isVisible = false }) {
</SelectContent> </SelectContent>
</Select> </Select>
<Button variant="ghost" size="icon" className="h-9 w-9"> <LockObject />
<Lock className="h-4 w-4" />
</Button>
</div> </div>
<ScrollBar orientation="horizontal" />
</ScrollArea>
</div> </div>
); );
} }

View file

@ -1,13 +1,12 @@
import { StrictMode } from 'react' import { createRoot } from "react-dom/client";
import { createRoot } from 'react-dom/client' import "./index.css";
import './index.css' import App from "./App.jsx";
import App from './App.jsx' import CanvasContextProvider from "./components/Context/canvasContext/CanvasContextProvider";
import CanvasContextProvider from './components/Context/canvasContext/CanvasContextProvider' import ColorContextProvider from "./components/Context/colorContext/ColorContextProvider";
import ColorContextProvider from './components/Context/colorContext/ColorContextProvider' import ObjectProvider from "./components/Context/activeObject/ObjectProvider";
import { ObjectProvider } from './components/Context/activeObject/ObjectProvider' import OpenContextProvider from "./components/Context/openContext/OpenContextProvider";
import OpenContextProvider from './components/Context/openContext/OpenContextProvider'
createRoot(document.getElementById('root')).render( createRoot(document.getElementById("root")).render(
// <StrictMode> // <StrictMode>
<CanvasContextProvider> <CanvasContextProvider>
<ColorContextProvider> <ColorContextProvider>
@ -19,4 +18,4 @@ createRoot(document.getElementById('root')).render(
</ColorContextProvider> </ColorContextProvider>
</CanvasContextProvider> </CanvasContextProvider>
// </StrictMode>, // </StrictMode>,
) );