canvas layout change, customization alignment changed
This commit is contained in:
parent
ac870250d5
commit
29a30904f1
13 changed files with 482 additions and 351 deletions
|
|
@ -8,6 +8,7 @@ import { Card, CardContent } from './ui/card';
|
|||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './ui/tooltip';
|
||||
import { Button } from './ui/button';
|
||||
import { Separator } from './ui/separator';
|
||||
import { ObjectShortcut } from './ObjectShortcut';
|
||||
|
||||
const aspectRatios = [
|
||||
{ value: "1:1", label: "Square (1:1)" },
|
||||
|
|
@ -28,10 +29,10 @@ const aspectRatios = [
|
|||
];
|
||||
|
||||
export function AspectCanvas() {
|
||||
const { setLeftPanelOpen, setRightPanelOpen, setOpenPanel, setCaptureOpen, setOpenSetting, openObjectPanel, setOpenObjectPanel, rightPanelOpen } = useContext(OpenContext);
|
||||
const { setLeftPanelOpen, setRightPanelOpen, setOpenPanel, setCaptureOpen, setOpenSetting, setOpenObjectPanel, rightPanelOpen } = useContext(OpenContext);
|
||||
const [selectedRatio, setSelectedRatio] = useState("4:3");
|
||||
|
||||
const { canvasRef, canvas, setCanvas, fabricCanvasRef, setCanvasHeight, setCanvasWidth } = useContext(CanvasContext);
|
||||
const { canvasRef, canvas, setCanvas, fabricCanvasRef, setCanvasHeight, setCanvasWidth, setScreenWidth } = useContext(CanvasContext);
|
||||
|
||||
useEffect(() => {
|
||||
import('fabric').then((fabricModule) => {
|
||||
|
|
@ -80,6 +81,8 @@ export function AspectCanvas() {
|
|||
}
|
||||
}
|
||||
|
||||
setScreenWidth(document.getElementById("root").offsetWidth);
|
||||
|
||||
// Handle responsive behavior for panels
|
||||
if (document.getElementById("root").offsetWidth <= 640) {
|
||||
setLeftPanelOpen(false);
|
||||
|
|
@ -87,9 +90,9 @@ export function AspectCanvas() {
|
|||
}
|
||||
if (document.getElementById("root").offsetWidth > 640) {
|
||||
setOpenObjectPanel(false);
|
||||
setOpenSetting(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Initial setup
|
||||
updateCanvasSize();
|
||||
|
||||
|
|
@ -98,18 +101,7 @@ export function AspectCanvas() {
|
|||
|
||||
// Cleanup listener on unmount
|
||||
return () => window.removeEventListener('resize', updateCanvasSize);
|
||||
}, [
|
||||
setCanvasWidth,
|
||||
setCanvasHeight,
|
||||
selectedRatio,
|
||||
canvasRef,
|
||||
canvas,
|
||||
openObjectPanel,
|
||||
setLeftPanelOpen,
|
||||
setOpenObjectPanel,
|
||||
setRightPanelOpen,
|
||||
rightPanelOpen
|
||||
]);
|
||||
}, [setCanvasWidth, setCanvasHeight, selectedRatio, canvasRef, canvas, setLeftPanelOpen, setOpenObjectPanel, setRightPanelOpen, rightPanelOpen, setScreenWidth, setOpenSetting]);
|
||||
|
||||
useEffect(() => {
|
||||
if (window.fabric) {
|
||||
|
|
@ -138,10 +130,10 @@ export function AspectCanvas() {
|
|||
|
||||
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">
|
||||
<CardContent className="p-0 space-y-4">
|
||||
<div className='flex w-full flex-wrap items-center justify-between mx-auto gap-2'>
|
||||
<CardContent className="p-0 space-y-2">
|
||||
<div className='flex w-full flex-wrap items-center justify-between mx-auto'>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='flex gap-1'>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
|
|
@ -155,19 +147,22 @@ export function AspectCanvas() {
|
|||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<div className="flex justify-between gap-2 items-center">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="outline" size="icon" onClick={() => setOpenSetting(true)}>
|
||||
<Settings className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Open Settings</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<div className="flex justify-between gap-1 items-center">
|
||||
<div className="block xl:hidden lg:hidden md:hidden">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="outline" size="icon" onClick={() => setOpenSetting(true)}>
|
||||
<Settings className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Open Settings</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
|
|
@ -180,6 +175,7 @@ export function AspectCanvas() {
|
|||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
|
|
@ -201,12 +197,12 @@ export function AspectCanvas() {
|
|||
<TooltipTrigger asChild>
|
||||
<div className="w-full">
|
||||
<Select onValueChange={handleRatioChange} value={selectedRatio}>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectTrigger className="w-full text-xs font-bold">
|
||||
<SelectValue placeholder="Select aspect ratio" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{aspectRatios.map((ratio) => (
|
||||
<SelectItem key={ratio.value} value={ratio.value}>
|
||||
<SelectItem key={ratio.value} value={ratio.value} className="text-xs font-bold">
|
||||
{ratio.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
|
|
@ -223,6 +219,10 @@ export function AspectCanvas() {
|
|||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ObjectShortcut value={"default"} />
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<AspectRatio ratio={getRatioValue(selectedRatio)} className="overflow-y-scroll shadow-red-200 overflow-x-hidden shadow-lg rounded-lg border-2 border-primary/10 transition-all duration-300 ease-in-out hover:shadow-xl scrollbar-hide">
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { ScrollArea } from './ui/scroll-area';
|
|||
import RndComponent from './Layouts/RndComponent';
|
||||
|
||||
const CanvasSetting = () => {
|
||||
const { canvas, canvasHeight, canvasWidth } = useContext(CanvasContext);
|
||||
const { canvas, canvasHeight, canvasWidth, screenWidth } = useContext(CanvasContext);
|
||||
const { setOpenSetting } = useContext(OpenContext);
|
||||
const bgImgRef = useRef(null);
|
||||
|
||||
|
|
@ -168,17 +168,18 @@ const CanvasSetting = () => {
|
|||
maxHeight: 400,
|
||||
bound: "parent"
|
||||
}
|
||||
|
||||
return (
|
||||
<RndComponent value={rndValue}>
|
||||
<Card className="px-2 py-2">
|
||||
<CardTitle className="flex items-center flex-wrap justify-between gap-1">Canvas Setting <Button className="rnd-escape" variant="secondary" onClick={() => setOpenSetting(false)}><X /></Button> </CardTitle>
|
||||
<Separator className="mt-4" />
|
||||
const content = () => {
|
||||
return (
|
||||
<Card className="xl:p-0 lg:p-0 md:p-0 p-2">
|
||||
<CardTitle className="flex items-center flex-wrap justify-between gap-1 xl:hidden lg:hidden md:hidden">Canvas Setting <Button className="rnd-escape" variant="secondary" onClick={() => setOpenSetting(false)}><X /></Button> </CardTitle>
|
||||
<Separator className="mt-4 block xl:hidden lg:hidden md:hidden" />
|
||||
<ScrollArea className="h-[400px] xl:h-fit lg:h-fit md:h-fit">
|
||||
<div className='rnd-escape'>
|
||||
|
||||
<ColorComponent />
|
||||
|
||||
<Separator className="mt-2" />
|
||||
|
||||
<div className='flex flex-col my-2 gap-2 rnd-escape'>
|
||||
<div>
|
||||
<Label>Background:</Label>
|
||||
|
|
@ -215,12 +216,10 @@ const CanvasSetting = () => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<Separator className="mt-4" />
|
||||
|
||||
{/* opacity */}
|
||||
<div className='flex flex-col gap-2 rnd-escape'>
|
||||
<div className='flex flex-col gap-2 rnd-escape mt-2'>
|
||||
<Label>
|
||||
Adjust Opacity:
|
||||
Background Opacity:
|
||||
</Label>
|
||||
<Slider
|
||||
defaultValue={[1.0]} // Default value, you can set it to 0.0 or another value
|
||||
|
|
@ -274,8 +273,18 @@ const CanvasSetting = () => {
|
|||
</div>
|
||||
</ScrollArea>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
return screenWidth <= 768 ? (
|
||||
<RndComponent value={rndValue}>
|
||||
{content()}
|
||||
</RndComponent>
|
||||
)
|
||||
) : (
|
||||
<div>
|
||||
{content()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CanvasSetting
|
||||
|
|
@ -1,104 +1,134 @@
|
|||
import { useContext, useEffect } from 'react'
|
||||
import { Label } from './ui/label';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
|
||||
import { Input } from './ui/input';
|
||||
import ColorContext from './Context/colorContext/ColorContext';
|
||||
import CanvasContext from './Context/canvasContext/CanvasContext';
|
||||
import { fabric } from 'fabric';
|
||||
import { useContext, useEffect, useState } from 'react'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import ColorContext from './Context/colorContext/ColorContext'
|
||||
import CanvasContext from './Context/canvasContext/CanvasContext'
|
||||
import { fabric } from 'fabric'
|
||||
|
||||
const ColorComponent = () => {
|
||||
const { canvas } = useContext(CanvasContext);
|
||||
const {
|
||||
backgroundType,
|
||||
setBackgroundType,
|
||||
solidColor,
|
||||
gradientColors,
|
||||
setSolidColor,
|
||||
setGradientColors,
|
||||
gradientDirection,
|
||||
setGradientDirection,
|
||||
setDirectionCoords,
|
||||
directionCoords,
|
||||
} = useContext(ColorContext);
|
||||
const { canvas } = useContext(CanvasContext)
|
||||
const { backgroundType, setBackgroundType, solidColor, gradientColors, setSolidColor, setGradientColors, gradientDirection, setGradientDirection, setDirectionCoords,
|
||||
} = useContext(ColorContext)
|
||||
|
||||
const applySolidColor = (color) => {
|
||||
setSolidColor(color);
|
||||
};
|
||||
|
||||
const applyGradient = () => {
|
||||
const width = canvas?.width || 0;
|
||||
const height = canvas?.height || 0;
|
||||
|
||||
// Define coordinates for linear gradient
|
||||
const linearCoords = {
|
||||
"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 = linearCoords[gradientDirection];
|
||||
if (directionCoords) {
|
||||
setDirectionCoords(directionCoords);
|
||||
} else {
|
||||
console.error(`Invalid gradient direction: ${gradientDirection}`);
|
||||
}
|
||||
};
|
||||
const [previewBackgroundType, setPreviewBackgroundType] = useState(backgroundType)
|
||||
const [previewSolidColor, setPreviewSolidColor] = useState(solidColor)
|
||||
const [previewGradientColors, setPreviewGradientColors] = useState(gradientColors)
|
||||
const [previewGradientDirection, setPreviewGradientDirection] = useState(gradientDirection)
|
||||
|
||||
useEffect(() => {
|
||||
if (gradientDirection && backgroundType === "gradient") {
|
||||
applyGradient();
|
||||
}
|
||||
}, [gradientDirection, backgroundType]);
|
||||
setPreviewBackgroundType(backgroundType)
|
||||
setPreviewSolidColor(solidColor)
|
||||
setPreviewGradientColors(gradientColors)
|
||||
setPreviewGradientDirection(gradientDirection)
|
||||
}, [backgroundType, solidColor, gradientColors, gradientDirection])
|
||||
|
||||
useEffect(() => {
|
||||
const object = canvas?.getActiveObject();
|
||||
if (!object) {
|
||||
if (canvas && solidColor && backgroundType === "color") {
|
||||
canvas.backgroundColor = solidColor;
|
||||
canvas.renderAll();
|
||||
if (previewBackgroundType === "gradient" && canvas) {
|
||||
const width = canvas.width || 0
|
||||
const height = canvas.height || 0
|
||||
|
||||
const linearCoords = {
|
||||
"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 },
|
||||
}
|
||||
if (canvas && directionCoords && gradientColors && backgroundType === "gradient") {
|
||||
|
||||
const coords = linearCoords[previewGradientDirection]
|
||||
if (coords) {
|
||||
setDirectionCoords(coords)
|
||||
}
|
||||
}
|
||||
}, [previewBackgroundType, previewGradientDirection, canvas, setDirectionCoords])
|
||||
|
||||
const applyChanges = () => {
|
||||
setBackgroundType(previewBackgroundType)
|
||||
setSolidColor(previewSolidColor)
|
||||
setGradientColors(previewGradientColors)
|
||||
setGradientDirection(previewGradientDirection)
|
||||
applyToCanvas()
|
||||
}
|
||||
|
||||
const applyToCanvas = () => {
|
||||
if (!canvas) return
|
||||
|
||||
if (previewBackgroundType === "color") {
|
||||
canvas.backgroundColor = previewSolidColor
|
||||
} else if (previewBackgroundType === "gradient") {
|
||||
const width = canvas.width || 0
|
||||
const height = canvas.height || 0
|
||||
|
||||
const linearCoords = {
|
||||
"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 coords = linearCoords[previewGradientDirection]
|
||||
if (coords) {
|
||||
setDirectionCoords(coords)
|
||||
const gradient = new fabric.Gradient({
|
||||
type: "linear",
|
||||
gradientUnits: "pixels",
|
||||
coords: directionCoords,
|
||||
coords: coords,
|
||||
colorStops: [
|
||||
{ offset: 0, color: gradientColors.color1 },
|
||||
{ offset: 1, color: gradientColors.color2 },
|
||||
{ offset: 0, color: previewGradientColors.color1 },
|
||||
{ offset: 1, color: previewGradientColors.color2 },
|
||||
],
|
||||
});
|
||||
canvas.backgroundColor = gradient;
|
||||
canvas.renderAll();
|
||||
})
|
||||
canvas.backgroundColor = gradient
|
||||
}
|
||||
if (canvas && gradientColors && backgroundType === "radial") {
|
||||
const gradient = new fabric.Gradient({
|
||||
type: "radial",
|
||||
gradientUnits: "pixels",
|
||||
coords: {
|
||||
x1: canvas.width / 2,
|
||||
y1: canvas.height / 2,
|
||||
r1: 0,
|
||||
x2: canvas.width / 2,
|
||||
y2: canvas.height / 2,
|
||||
r2: Math.min(canvas.width, canvas.height) / 2,
|
||||
},
|
||||
colorStops: [
|
||||
{ offset: 0, color: gradientColors.color1 },
|
||||
{ offset: 1, color: gradientColors.color2 },
|
||||
],
|
||||
});
|
||||
canvas.backgroundColor = gradient;
|
||||
canvas.renderAll();
|
||||
} else if (previewBackgroundType === "radial") {
|
||||
const gradient = new fabric.Gradient({
|
||||
type: "radial",
|
||||
gradientUnits: "pixels",
|
||||
coords: {
|
||||
x1: canvas.width / 2,
|
||||
y1: canvas.height / 2,
|
||||
r1: 0,
|
||||
x2: canvas.width / 2,
|
||||
y2: canvas.height / 2,
|
||||
r2: Math.min(canvas.width, canvas.height) / 2,
|
||||
},
|
||||
colorStops: [
|
||||
{ offset: 0, color: previewGradientColors.color1 },
|
||||
{ offset: 1, color: previewGradientColors.color2 },
|
||||
],
|
||||
})
|
||||
canvas.backgroundColor = gradient
|
||||
}
|
||||
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
const getPreviewStyle = () => {
|
||||
if (previewBackgroundType === "color") {
|
||||
return { backgroundColor: previewSolidColor }
|
||||
} else if (previewBackgroundType === "gradient") {
|
||||
const direction = {
|
||||
'top-to-bottom': '180deg',
|
||||
'bottom-to-top': '0deg',
|
||||
'left-to-right': '90deg',
|
||||
'right-to-left': '270deg'
|
||||
}[previewGradientDirection]
|
||||
return {
|
||||
backgroundImage: `linear-gradient(${direction}, ${previewGradientColors.color1}, ${previewGradientColors.color2})`
|
||||
}
|
||||
} else if (previewBackgroundType === "radial") {
|
||||
return {
|
||||
backgroundImage: `radial-gradient(circle, ${previewGradientColors.color1}, ${previewGradientColors.color2})`
|
||||
}
|
||||
}
|
||||
}, [gradientColors, directionCoords, solidColor, canvas, backgroundType]);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex flex-wrap my-2 gap-2'>
|
||||
<div className='flex gap-2 flex-wrap mb-auto'>
|
||||
<div className='flex flex-col gap-4 p-1'>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<Label>Background Type:</Label>
|
||||
<Select value={backgroundType} onValueChange={setBackgroundType}>
|
||||
<Select value={previewBackgroundType} onValueChange={setPreviewBackgroundType}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select background type" />
|
||||
</SelectTrigger>
|
||||
|
|
@ -110,24 +140,22 @@ const ColorComponent = () => {
|
|||
</Select>
|
||||
</div>
|
||||
|
||||
{backgroundType === "color" ? (
|
||||
{previewBackgroundType === "color" ? (
|
||||
<div className='flex flex-col gap-2'>
|
||||
<Label>Solid Color:</Label>
|
||||
<Input
|
||||
type="color"
|
||||
value={solidColor}
|
||||
onChange={(e) => applySolidColor(e.target.value)}
|
||||
value={previewSolidColor}
|
||||
onChange={(e) => setPreviewSolidColor(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
) : backgroundType === "gradient" ? (
|
||||
) : previewBackgroundType === "gradient" ? (
|
||||
<div className='grid grid-cols-1 gap-2'>
|
||||
<div className='flex flex-wrap gap-2'>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<Label>Direction:</Label>
|
||||
<Select
|
||||
value={gradientDirection}
|
||||
onValueChange={(value) => {
|
||||
setGradientDirection(value);
|
||||
}}
|
||||
value={previewGradientDirection}
|
||||
onValueChange={setPreviewGradientDirection}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select direction" />
|
||||
|
|
@ -141,26 +169,26 @@ const ColorComponent = () => {
|
|||
</Select>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2 flex-wrap'>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<Label>Color 1:</Label>
|
||||
<Input
|
||||
type="color"
|
||||
value={gradientColors.color1}
|
||||
value={previewGradientColors.color1}
|
||||
onChange={(e) =>
|
||||
setGradientColors((prev) => ({
|
||||
setPreviewGradientColors((prev) => ({
|
||||
...prev,
|
||||
color1: e.target.value,
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex flex-wrap gap-2'>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<Label>Color 2:</Label>
|
||||
<Input
|
||||
type="color"
|
||||
value={gradientColors.color2}
|
||||
value={previewGradientColors.color2}
|
||||
onChange={(e) =>
|
||||
setGradientColors((prev) => ({
|
||||
setPreviewGradientColors((prev) => ({
|
||||
...prev,
|
||||
color2: e.target.value,
|
||||
}))}
|
||||
|
|
@ -168,27 +196,27 @@ const ColorComponent = () => {
|
|||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className='flex flex-wrap gap-2'>
|
||||
<div className='flex gap-2 flex-wrap'>
|
||||
<div className='grid grid-cols-1 gap-2'>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<Label>Color 1:</Label>
|
||||
<Input
|
||||
type="color"
|
||||
value={gradientColors.color1}
|
||||
value={previewGradientColors.color1}
|
||||
onChange={(e) =>
|
||||
setGradientColors((prev) => ({
|
||||
setPreviewGradientColors((prev) => ({
|
||||
...prev,
|
||||
color1: e.target.value,
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex flex-wrap gap-2'>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<Label>Color 2:</Label>
|
||||
<Input
|
||||
type="color"
|
||||
value={gradientColors.color2}
|
||||
value={previewGradientColors.color2}
|
||||
onChange={(e) =>
|
||||
setGradientColors((prev) => ({
|
||||
setPreviewGradientColors((prev) => ({
|
||||
...prev,
|
||||
color2: e.target.value,
|
||||
}))}
|
||||
|
|
@ -196,8 +224,21 @@ const ColorComponent = () => {
|
|||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColorComponent;
|
||||
<div>
|
||||
<Label>Preview:</Label>
|
||||
<div
|
||||
className='w-full h-24 rounded-md mt-2'
|
||||
style={getPreviewStyle()}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<Button onClick={applyChanges}>
|
||||
Apply Changes
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ColorComponent
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ const CanvasContextProvider = ({ children }) => {
|
|||
const [canvas, setCanvas] = useState(null);
|
||||
const [canvasHeight, setCanvasHeight] = useState(0);
|
||||
const [canvasWidth, setCanvasWidth] = useState(0);
|
||||
const [screenWidth, setScreenWidth] = useState(0);
|
||||
const fabricCanvasRef = useRef(null);
|
||||
|
||||
return (
|
||||
<CanvasContext.Provider value={{ canvasRef, canvas, setCanvas, fabricCanvasRef, canvasHeight, setCanvasHeight, canvasWidth, setCanvasWidth }}>
|
||||
<CanvasContext.Provider value={{ canvasRef, canvas, setCanvas, fabricCanvasRef, canvasHeight, setCanvasHeight, canvasWidth, setCanvasWidth, screenWidth, setScreenWidth }}>
|
||||
{children}
|
||||
</CanvasContext.Provider>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import ColorContext from './ColorContext'
|
|||
|
||||
const ColorContextProvider = ({ children }) => {
|
||||
const [backgroundType, setBackgroundType] = useState("color"); // 'color' or 'gradient'
|
||||
const [solidColor, setSolidColor] = useState("#ffffff");
|
||||
const [solidColor, setSolidColor] = useState("#FFA500");
|
||||
const [gradientColors, setGradientColors] = useState({
|
||||
color1: "#ffffff",
|
||||
color2: "#e26286",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,21 @@
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
||||
import { ChevronUp, ChevronDown } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const CollapsibleComponent = ({ children, text }) => {
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
const [isOpen, setIsOpen] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Check if the text prop is "Canvas Setting" and set isOpen to false
|
||||
if (text === "Canvas Setting") {
|
||||
setIsOpen(false);
|
||||
}
|
||||
else {
|
||||
setIsOpen(true)
|
||||
}
|
||||
}, [text])
|
||||
|
||||
return (
|
||||
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
|
||||
<CollapsibleTrigger asChild>
|
||||
|
|
|
|||
|
|
@ -114,11 +114,11 @@ const AllIconsPage = () => {
|
|||
onChange={handleSearch}
|
||||
className="border p-2 mb-0 w-full"
|
||||
/>
|
||||
<Card className="flex items-center justify-center py-1">
|
||||
<Card className="flex items-center justify-center rounded-none p-1">
|
||||
<Grid
|
||||
columnCount={4}
|
||||
columnWidth={50}
|
||||
height={380}
|
||||
height={330}
|
||||
rowCount={Math.ceil(filtered.length / 4)}
|
||||
rowHeight={70}
|
||||
width={240}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useCallback, useContext, useState } from "react"
|
||||
import { useContext, useState } from "react"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from './ui/card';
|
||||
import { Button } from './ui/button';
|
||||
import { BringToFront, CopyPlus, GroupIcon, PencilRuler, Save, Settings, Shapes, SquareX, Store, Trash2, UngroupIcon, Upload, ChevronDown, ChevronUp, MoveIcon, Type, ImageDown, X } from 'lucide-react';
|
||||
import { PencilRuler, Save, Settings, Shapes, SquareX, Store, Upload, ChevronDown, ChevronUp, Type, ImageDown, X } from 'lucide-react';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './ui/tooltip';
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from './ui/collapsible';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';
|
||||
|
|
@ -10,6 +10,7 @@ import CanvasContext from './Context/canvasContext/CanvasContext';
|
|||
import ActiveObjectContext from './Context/activeObject/ObjectContext';
|
||||
import { fabric } from 'fabric';
|
||||
import RndComponent from './Layouts/RndComponent';
|
||||
import { ObjectShortcut } from "./ObjectShortcut";
|
||||
|
||||
export function EditPanel() {
|
||||
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||
|
|
@ -17,108 +18,7 @@ export function EditPanel() {
|
|||
|
||||
const { canvas } = useContext(CanvasContext);
|
||||
|
||||
const { setActiveObject, activeObject } = useContext(ActiveObjectContext);
|
||||
|
||||
const groupSelectedObjects = () => {
|
||||
const activeObjects = canvas.getActiveObjects(); // Get selected objects
|
||||
if (activeObjects.length > 1) {
|
||||
|
||||
canvas.discardActiveObject();
|
||||
|
||||
const group = new fabric.Group(activeObjects, {
|
||||
left: canvas?.width / 2,
|
||||
top: canvas?.height / 2,
|
||||
originX: "center",
|
||||
originY: "center",
|
||||
selectable: true, // Allow group selection
|
||||
subTargetCheck: true, // Allow individual object selection
|
||||
hasControls: true, // Enable resizing/movement of the group
|
||||
})
|
||||
canvas.remove(...activeObjects);
|
||||
canvas.add(group);
|
||||
canvas.setActiveObject(group);
|
||||
setActiveObject(group);
|
||||
canvas.renderAll();
|
||||
} else {
|
||||
console.log("Select at least two objects")
|
||||
}
|
||||
};
|
||||
|
||||
const ungroupSelectedObjects = () => {
|
||||
const activeObject = canvas.getActiveObject();
|
||||
if (activeObject && activeObject.type === "group") {
|
||||
// Get the group
|
||||
const group = activeObject;
|
||||
canvas.discardActiveObject();
|
||||
// Remove the group from the canvas
|
||||
canvas.remove(group);
|
||||
setActiveObject(null);
|
||||
|
||||
// Iterate through each object in the group
|
||||
group._objects.forEach((object) => {
|
||||
// Calculate the absolute position based on the group's transformation
|
||||
const objLeft = object.left * group.scaleX + group.left;
|
||||
const objTop = object.top * group.scaleY + group.top;
|
||||
|
||||
// Reset transformations and positions
|
||||
object.set({
|
||||
left: objLeft,
|
||||
top: objTop,
|
||||
scaleX: object.scaleX * group.scaleX, // Adjust scale based on group
|
||||
scaleY: object.scaleY * group.scaleY, // Adjust scale based on group
|
||||
angle: object.angle + group.angle, // Adjust rotation
|
||||
hasControls: true, // Allow resizing
|
||||
selectable: true, // Make selectable
|
||||
group: null, // Remove group reference
|
||||
originX: "center",
|
||||
originY: "center",
|
||||
});
|
||||
|
||||
// Update coordinates
|
||||
object.setCoords();
|
||||
|
||||
// Add the object back to the canvas
|
||||
canvas.add(object);
|
||||
});
|
||||
|
||||
// Clear the selection
|
||||
canvas.discardActiveObject();
|
||||
|
||||
// Render the canvas to reflect changes
|
||||
canvas.renderAll();
|
||||
}
|
||||
};
|
||||
|
||||
// Bring Selected Object to Front
|
||||
const bringToFront = () => {
|
||||
if (activeObject) {
|
||||
activeObject.bringToFront();
|
||||
canvas.renderAll();
|
||||
}
|
||||
};
|
||||
|
||||
// Remove Selected Element
|
||||
const removeSelected = useCallback(() => {
|
||||
const activeObject = canvas?.getActiveObject();
|
||||
|
||||
const allObjects = canvas?.getObjects();
|
||||
|
||||
const textObjects = allObjects.filter((obj) => obj.type === 'textbox' || obj.type === 'text' ||
|
||||
obj.type === 'i-text');
|
||||
|
||||
if (activeObject) {
|
||||
canvas.remove(activeObject);
|
||||
setActiveObject(null);
|
||||
}
|
||||
if (activeObject && textObjects?.length === 1) {
|
||||
canvas.remove(activeObject);
|
||||
setActiveObject(null);
|
||||
}
|
||||
if (activeObject.length > 1) {
|
||||
canvas.remove(...activeObject);
|
||||
setActiveObject(null);
|
||||
}
|
||||
}, [canvas, setActiveObject]);
|
||||
const { setActiveObject } = useContext(ActiveObjectContext);
|
||||
|
||||
const saveCanvasState = () => {
|
||||
// Get the JSON representation of all objects
|
||||
|
|
@ -155,17 +55,6 @@ export function EditPanel() {
|
|||
// loadCanvasState(canvasState);
|
||||
};
|
||||
|
||||
// duplicating current objects
|
||||
const duplicating = () => {
|
||||
// Clone the active object to create a true deep copy
|
||||
activeObject.clone((clonedObject) => {
|
||||
// Add the cloned object to the canvas
|
||||
clonedObject.set("left", clonedObject?.left + 30)
|
||||
canvas.add(clonedObject);
|
||||
canvas.renderAll();
|
||||
});
|
||||
}
|
||||
|
||||
// for clear canvas
|
||||
const clearCanvas = () => {
|
||||
canvas.clear();
|
||||
|
|
@ -227,57 +116,7 @@ export function EditPanel() {
|
|||
</TabsList>
|
||||
|
||||
<TabsContent value="edit" className="mt-2">
|
||||
<TooltipProvider>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<ActionButton
|
||||
icon={<GroupIcon className="h-4 w-4" />}
|
||||
label="Group"
|
||||
onClick={groupSelectedObjects}
|
||||
tooltipContent={
|
||||
<div className="text-sm">
|
||||
<p className="font-semibold mb-1">Group selected objects</p>
|
||||
<p>To select multiple objects:</p>
|
||||
<ol className="list-decimal list-inside mt-1">
|
||||
<li>Hold down the Shift key</li>
|
||||
<li>Click and drag with the left mouse button to select objects</li>
|
||||
<li>Release the Shift key and mouse button</li>
|
||||
</ol>
|
||||
<p className="mt-1">Then click this button to group the selected objects.</p>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<ActionButton
|
||||
icon={<UngroupIcon className="h-4 w-4" />}
|
||||
label="Ungroup"
|
||||
onClick={ungroupSelectedObjects}
|
||||
tooltipContent="Ungroup selected objects"
|
||||
/>
|
||||
<ActionButton
|
||||
icon={<CopyPlus className="h-4 w-4" />}
|
||||
label="Duplicate"
|
||||
onClick={duplicating}
|
||||
tooltipContent="Duplicate selected objects"
|
||||
/>
|
||||
<ActionButton
|
||||
icon={<BringToFront className="h-4 w-4" />}
|
||||
label="To Front"
|
||||
onClick={bringToFront}
|
||||
tooltipContent="Bring selected objects to front"
|
||||
/>
|
||||
<ActionButton
|
||||
icon={<MoveIcon className="h-4 w-4" />}
|
||||
label="Move"
|
||||
onClick={() => { }}
|
||||
tooltipContent="Move selected objects"
|
||||
/>
|
||||
<ActionButton
|
||||
icon={<Trash2 className="h-4 w-4" />}
|
||||
label="Remove"
|
||||
onClick={removeSelected}
|
||||
tooltipContent="Remove selected objects"
|
||||
/>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
<ObjectShortcut value={"edit"} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="add" className="mt-2">
|
||||
|
|
@ -343,12 +182,14 @@ export function EditPanel() {
|
|||
onClick={saveCanvasState}
|
||||
tooltipContent="Save current canvas state"
|
||||
/>
|
||||
<ActionButton
|
||||
icon={<Settings className="h-4 w-4" />}
|
||||
label="Settings"
|
||||
onClick={() => setOpenSetting(true)}
|
||||
tooltipContent="Open canvas settings"
|
||||
/>
|
||||
<div className="block xl:hidden lg:hidden md:hidden">
|
||||
<ActionButton
|
||||
icon={<Settings className="h-4 w-4" />}
|
||||
label="Settings"
|
||||
onClick={() => setOpenSetting(true)}
|
||||
tooltipContent="Open canvas settings"
|
||||
/>
|
||||
</div>
|
||||
<ActionButton
|
||||
icon={<SquareX className="h-4 w-4" />}
|
||||
label="Clear"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const RndComponent = ({ children, value }) => {
|
|||
x: valueX,
|
||||
y: valueY,
|
||||
width: width,
|
||||
height: 'auto',
|
||||
height: height,
|
||||
}}
|
||||
minWidth={minWidth}
|
||||
maxWidth={maxWidth}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ const SheetLeftPanel = () => {
|
|||
|
||||
<Separator className="my-2" />
|
||||
|
||||
<TabsContent value="shapes" className="mt-2 h-[530px] overflow-y-scroll scrollbar-thin scrollbar-thumb-secondary scrollbar-track-white">
|
||||
<TabsContent value="shapes" className="mt-2 h-[450px] overflow-y-scroll scrollbar-thin scrollbar-thumb-secondary scrollbar-track-white">
|
||||
<AddShapes />
|
||||
</TabsContent>
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ const SheetLeftPanel = () => {
|
|||
<AllIconsPage />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="image" className="mt-2 h-[530px] overflow-y-scroll px-1 scrollbar-thin scrollbar-thumb-secondary scrollbar-track-white">
|
||||
<TabsContent value="image" className="mt-2 h-[450px] overflow-y-scroll px-1 scrollbar-thin scrollbar-thumb-secondary scrollbar-track-white">
|
||||
<UploadImage />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ import { X } from "lucide-react";
|
|||
import { useContext, useEffect, useState } from "react";
|
||||
import ActiveObjectContext from "../Context/activeObject/ObjectContext";
|
||||
import OpenContext from "../Context/openContext/OpenContext";
|
||||
import CanvasSetting from "../CanvasSetting";
|
||||
import CollapsibleComponent from "../EachComponent/Customization/CollapsibleComponent";
|
||||
import { Card } from "../ui/card";
|
||||
|
||||
const SheetRightPanel = () => {
|
||||
const { rightPanelOpen, setRightPanelOpen } = useContext(OpenContext)
|
||||
|
|
@ -43,7 +46,7 @@ const SheetRightPanel = () => {
|
|||
|
||||
return (
|
||||
<Sheet onOpenChange={handleOpenChange} open={rightPanelOpen} modal={false}>
|
||||
<SheetContent className="w-[300px] top-[60px] overflow-y-scroll scrollbar-thin scrollbar-thumb-secondary scrollbar-track-white">
|
||||
<SheetContent className="w-[300px] top-[60px]">
|
||||
<SheetHeader>
|
||||
<SheetTitle className="text-left flex items-center gap-1 flex-wrap justify-between">
|
||||
<SheetDescription>Edit Customization</SheetDescription>
|
||||
|
|
@ -52,16 +55,30 @@ const SheetRightPanel = () => {
|
|||
</Button>
|
||||
</SheetTitle>
|
||||
|
||||
<SheetDescription className="text-left">
|
||||
<SheetDescription className="text-left text-xs">
|
||||
Customize each shapes, and text as per your choice.
|
||||
</SheetDescription>
|
||||
|
||||
</SheetHeader>
|
||||
<div className="my-2 mb-14">
|
||||
<Separator className="my-2" />
|
||||
{
|
||||
open ? <CustomizeShape /> : <p className='text-sm font-semibold'>No active object found</p>
|
||||
}
|
||||
|
||||
<Separator className="my-2" />
|
||||
|
||||
<div className="h-[500px] overflow-y-scroll">
|
||||
<div>
|
||||
<Card className="p-2 mx-1">
|
||||
<CollapsibleComponent text={"Canvas Setting"}>
|
||||
<div className="mt-2">
|
||||
<CanvasSetting />
|
||||
</div>
|
||||
</CollapsibleComponent>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="my-2">
|
||||
<Separator className="my-2" />
|
||||
{
|
||||
open ? <CustomizeShape /> : <p className='text-sm font-semibold'>No active object found</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const ObjectPanel = () => {
|
|||
valueY: 20,
|
||||
width: 250,
|
||||
height: 0,
|
||||
minWidth: 250,
|
||||
minWidth: 280,
|
||||
maxWidth: 300,
|
||||
minHeight: 0,
|
||||
maxHeight: 500,
|
||||
|
|
@ -31,7 +31,7 @@ const ObjectPanel = () => {
|
|||
|
||||
return (
|
||||
<RndComponent value={rndValue}>
|
||||
<Card className="w-full shadow-lg">
|
||||
<Card className="w-full shadow-lg px-1">
|
||||
<CardHeader className="p-1 mr-12">
|
||||
<div className="flex items-center justify-between">
|
||||
<Button className="rnd-escape" variant={"ghost"} size={"sm"} onClick={() => setOpenObjectPanel(false)}><X /></Button>
|
||||
|
|
@ -48,7 +48,7 @@ const ObjectPanel = () => {
|
|||
</CollapsibleTrigger>
|
||||
|
||||
<CollapsibleContent>
|
||||
<ScrollArea className="h-[500px] xl:h-fit lg:h-fit md:h-fit">
|
||||
<div className="h-[450px] xl:h-fit lg:h-fit md:h-fit overflow-y-scroll px-1">
|
||||
<Tabs className="w-full h-fit"
|
||||
value={tabValue}
|
||||
onValueChange={(value) => setTabValue(value)} // Sync tab state with context
|
||||
|
|
@ -101,18 +101,18 @@ const ObjectPanel = () => {
|
|||
|
||||
{/* All shapes */}
|
||||
<TabsContent value="shapes" className="mt-2">
|
||||
<Card className="p-1">
|
||||
<div>
|
||||
<CardTitle className="p-1">Shapes </CardTitle>
|
||||
<Separator className="my-2" />
|
||||
<ScrollArea className="h-[450px]">
|
||||
<AddShapes />
|
||||
</ScrollArea>
|
||||
</Card>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
{/* Upload images */}
|
||||
<TabsContent value="images" className="mt-2">
|
||||
<Card className="p-1">
|
||||
<div>
|
||||
<CardTitle className="p-1">Upload</CardTitle>
|
||||
<Separator className="my-2" />
|
||||
<ScrollArea className="h-[450px]">
|
||||
|
|
@ -120,23 +120,23 @@ const ObjectPanel = () => {
|
|||
<UploadImage />
|
||||
</Card>
|
||||
</ScrollArea>
|
||||
</Card>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
{/* Customization */}
|
||||
<TabsContent value="customize" className="mt-2">
|
||||
<Card className="p-1">
|
||||
<div>
|
||||
<CardTitle className="p-1">Object customization </CardTitle>
|
||||
<Separator className="my-2" />
|
||||
<ScrollArea className="h-[450px]">
|
||||
<CustomizeShape />
|
||||
</ScrollArea>
|
||||
</Card>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
</Tabs>
|
||||
|
||||
</ScrollArea>
|
||||
</div>
|
||||
|
||||
</CollapsibleContent>
|
||||
|
||||
|
|
|
|||
211
src/components/ObjectShortcut.jsx
Normal file
211
src/components/ObjectShortcut.jsx
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
import { useCallback, useContext } from 'react'
|
||||
import CanvasContext from './Context/canvasContext/CanvasContext';
|
||||
import ActiveObjectContext from './Context/activeObject/ObjectContext';
|
||||
import { fabric } from 'fabric';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './ui/tooltip';
|
||||
import { Button } from './ui/button';
|
||||
import { BringToFront, CopyPlus, GroupIcon, SquareX, Trash2, UngroupIcon } from 'lucide-react';
|
||||
|
||||
export const ObjectShortcut = ({ value }) => {
|
||||
const { canvas } = useContext(CanvasContext);
|
||||
const { setActiveObject, activeObject } = useContext(ActiveObjectContext);
|
||||
|
||||
const groupSelectedObjects = () => {
|
||||
const activeObjects = canvas.getActiveObjects(); // Get selected objects
|
||||
if (activeObjects.length > 1) {
|
||||
|
||||
canvas.discardActiveObject();
|
||||
|
||||
const group = new fabric.Group(activeObjects, {
|
||||
left: canvas?.width / 2,
|
||||
top: canvas?.height / 2,
|
||||
originX: "center",
|
||||
originY: "center",
|
||||
selectable: true, // Allow group selection
|
||||
subTargetCheck: true, // Allow individual object selection
|
||||
hasControls: true, // Enable resizing/movement of the group
|
||||
})
|
||||
canvas.remove(...activeObjects);
|
||||
canvas.add(group);
|
||||
canvas.setActiveObject(group);
|
||||
setActiveObject(group);
|
||||
canvas.renderAll();
|
||||
} else {
|
||||
console.log("Select at least two objects")
|
||||
}
|
||||
};
|
||||
|
||||
const ungroupSelectedObjects = () => {
|
||||
const activeObject = canvas.getActiveObject();
|
||||
if (activeObject && activeObject.type === "group") {
|
||||
// Get the group
|
||||
const group = activeObject;
|
||||
canvas.discardActiveObject();
|
||||
// Remove the group from the canvas
|
||||
canvas.remove(group);
|
||||
setActiveObject(null);
|
||||
|
||||
// Iterate through each object in the group
|
||||
group._objects.forEach((object) => {
|
||||
// Calculate the absolute position based on the group's transformation
|
||||
const objLeft = object.left * group.scaleX + group.left;
|
||||
const objTop = object.top * group.scaleY + group.top;
|
||||
|
||||
// Reset transformations and positions
|
||||
object.set({
|
||||
left: objLeft,
|
||||
top: objTop,
|
||||
scaleX: object.scaleX * group.scaleX, // Adjust scale based on group
|
||||
scaleY: object.scaleY * group.scaleY, // Adjust scale based on group
|
||||
angle: object.angle + group.angle, // Adjust rotation
|
||||
hasControls: true, // Allow resizing
|
||||
selectable: true, // Make selectable
|
||||
group: null, // Remove group reference
|
||||
originX: "center",
|
||||
originY: "center",
|
||||
});
|
||||
|
||||
// Update coordinates
|
||||
object.setCoords();
|
||||
|
||||
// Add the object back to the canvas
|
||||
canvas.add(object);
|
||||
});
|
||||
|
||||
// Clear the selection
|
||||
canvas.discardActiveObject();
|
||||
|
||||
// Render the canvas to reflect changes
|
||||
canvas.renderAll();
|
||||
}
|
||||
};
|
||||
|
||||
// Bring Selected Object to Front
|
||||
const bringToFront = () => {
|
||||
if (activeObject) {
|
||||
activeObject.bringToFront();
|
||||
canvas.renderAll();
|
||||
}
|
||||
};
|
||||
|
||||
// Remove Selected Element
|
||||
const removeSelected = useCallback(() => {
|
||||
const activeObject = canvas?.getActiveObject();
|
||||
|
||||
const allObjects = canvas?.getObjects();
|
||||
|
||||
const textObjects = allObjects.filter((obj) => obj.type === 'textbox' || obj.type === 'text' ||
|
||||
obj.type === 'i-text');
|
||||
|
||||
if (activeObject) {
|
||||
canvas.remove(activeObject);
|
||||
setActiveObject(null);
|
||||
}
|
||||
if (activeObject && textObjects?.length === 1) {
|
||||
canvas.remove(activeObject);
|
||||
setActiveObject(null);
|
||||
}
|
||||
if (activeObject.length > 1) {
|
||||
canvas.remove(...activeObject);
|
||||
setActiveObject(null);
|
||||
}
|
||||
}, [canvas, setActiveObject]);
|
||||
|
||||
// duplicating current objects
|
||||
const duplicating = () => {
|
||||
// Clone the active object to create a true deep copy
|
||||
activeObject.clone((clonedObject) => {
|
||||
// Add the cloned object to the canvas
|
||||
clonedObject.set("left", clonedObject?.left + 30)
|
||||
canvas.add(clonedObject);
|
||||
canvas.renderAll();
|
||||
});
|
||||
}
|
||||
|
||||
// for clear canvas
|
||||
const clearCanvas = () => {
|
||||
canvas.clear();
|
||||
canvas.renderAll();
|
||||
setActiveObject(null);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TooltipProvider>
|
||||
<div className={`grid grid-cols-3 gap-2 ${value === "default" ? "xl:grid-cols-6 lg:grid-cols-6 md:grid-cols-6" : "xl:grid-cols-3 lg:grid-cols-3 md:grid-cols-3"}`}>
|
||||
<ActionButton
|
||||
icon={<GroupIcon className="h-4 w-4" />}
|
||||
label="Group"
|
||||
onClick={groupSelectedObjects}
|
||||
tooltipContent={
|
||||
<div className="text-sm">
|
||||
<p className="font-semibold mb-1">Group selected objects</p>
|
||||
<p>To select multiple objects:</p>
|
||||
<ol className="list-decimal list-inside mt-1">
|
||||
<li>Hold down the Shift key</li>
|
||||
<li>Click and drag with the left mouse button to select objects</li>
|
||||
<li>Release the Shift key and mouse button</li>
|
||||
</ol>
|
||||
<p className="mt-1">Then click this button to group the selected objects.</p>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
<ActionButton
|
||||
icon={<UngroupIcon className="h-4 w-4" />}
|
||||
label="Ungroup"
|
||||
onClick={ungroupSelectedObjects}
|
||||
tooltipContent="Ungroup selected objects"
|
||||
/>
|
||||
|
||||
<ActionButton
|
||||
icon={<CopyPlus className="h-4 w-4" />}
|
||||
label="Duplicate"
|
||||
onClick={duplicating}
|
||||
tooltipContent="Duplicate selected objects"
|
||||
/>
|
||||
|
||||
<ActionButton
|
||||
icon={<BringToFront className="h-4 w-4" />}
|
||||
label="To Front"
|
||||
onClick={bringToFront}
|
||||
tooltipContent="Bring selected objects to front"
|
||||
/>
|
||||
|
||||
<ActionButton
|
||||
icon={<Trash2 className="h-4 w-4" />}
|
||||
label="Remove"
|
||||
onClick={removeSelected}
|
||||
tooltipContent="Remove selected objects"
|
||||
/>
|
||||
|
||||
<ActionButton
|
||||
icon={<SquareX className="h-4 w-4" />}
|
||||
label="Clear"
|
||||
onClick={clearCanvas}
|
||||
tooltipContent="Clear entire canvas"
|
||||
/>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
function ActionButton({ icon, label, onClick, tooltipContent }) {
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="outline" size="md" className="w-full" onClick={onClick}>
|
||||
<div className="flex items-center gap-1 p-1">
|
||||
{icon}
|
||||
<span className="text-[10px] font-bold">{label}</span>
|
||||
</div>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" align="center" className="max-w-xs">
|
||||
<p>{tooltipContent}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue