mirror of
https://github.com/r2r90/canvas-label.git
synced 2025-12-16 21:19:38 +00:00
update zIndex changes
This commit is contained in:
@@ -1,24 +1,20 @@
|
||||
import { TransformableImage } from "@/components/transformable-image";
|
||||
|
||||
import type { KonvaEventObject } from "konva/lib/Node";
|
||||
|
||||
import { Layer, Stage } from "react-konva";
|
||||
import { Layer, Rect, Stage } from "react-konva";
|
||||
import TransformableText from "./transformable-text";
|
||||
import { useAppDispatch, useAppSelector } from "@/hooks";
|
||||
import { appSlice, deselectItem } from "@/store/app.slice";
|
||||
import { TextToolbar } from "./text-toolbar";
|
||||
import { useEffect, useState } from "react";
|
||||
import { EditableText } from "@/components/editable-resizable-text";
|
||||
import { ImageToolbar } from "@/components/image-toolbar";
|
||||
import { Toolbar } from "@/components/toolbar";
|
||||
|
||||
const Canvas = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const stage = useAppSelector((state) => state.app.stage);
|
||||
const selectedItemId = useAppSelector((state) => state.app.selectedItemId);
|
||||
const selectItem = (id: string) => dispatch(appSlice.actions.selectItem(id));
|
||||
const texts = useAppSelector((state) => state.app.texts);
|
||||
const images = useAppSelector((state) => state.app.images);
|
||||
const backgroundRects = useAppSelector((state) => state.app.backgroundRects);
|
||||
|
||||
const deselectHandler = (
|
||||
e: KonvaEventObject<MouseEvent> | KonvaEventObject<TouchEvent>,
|
||||
@@ -32,9 +28,6 @@ const Canvas = () => {
|
||||
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [isTransforming, setIsTransforming] = useState(false);
|
||||
const [text, setText] = useState("Click to resize. Double click to edit.");
|
||||
const [width, setWidth] = useState(200);
|
||||
const [height, setHeight] = useState(200);
|
||||
const [selected, setSelected] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -61,8 +54,8 @@ const Canvas = () => {
|
||||
|
||||
<Stage
|
||||
className="m-[3rem] bg-white"
|
||||
width={600}
|
||||
height={500}
|
||||
width={stage.width}
|
||||
height={stage.height}
|
||||
onTouchStart={deselectHandler}
|
||||
onMouseDown={deselectHandler}
|
||||
>
|
||||
@@ -94,23 +87,20 @@ const Canvas = () => {
|
||||
);
|
||||
})}
|
||||
|
||||
{/*<EditableText*/}
|
||||
{/* x={20}*/}
|
||||
{/* y={40}*/}
|
||||
{/* text={text}*/}
|
||||
{/* width={width}*/}
|
||||
{/* height={height}*/}
|
||||
{/* onResize={(newWidth, newHeight) => {*/}
|
||||
{/* setWidth(newWidth);*/}
|
||||
{/* setHeight(newHeight);*/}
|
||||
{/* }}*/}
|
||||
{/* isEditing={isEditing}*/}
|
||||
{/* isTransforming={isTransforming}*/}
|
||||
{/* onToggleEdit={toggleEdit}*/}
|
||||
{/* onToggleTransform={toggleTransforming}*/}
|
||||
{/* onChange={setText}*/}
|
||||
{/*/>*/}
|
||||
{backgroundRects.map((rect, i) => {
|
||||
return (
|
||||
<Rect
|
||||
key={i}
|
||||
width={rect.width}
|
||||
height={rect.width}
|
||||
fill={rect.fill}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Layer>
|
||||
{/* <Layer>
|
||||
<Text zIndex={10} x={30} y={200} fontSize={48} text="Hello" />
|
||||
</Layer>*/}
|
||||
</Stage>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -9,7 +9,9 @@ import { Separator } from "@/components/ui/separator";
|
||||
export function ImageToolbar({ selectedItemId, currentImage }) {
|
||||
const dispatch = useAppDispatch();
|
||||
const flipImageVerticaly = () => {
|
||||
const editOffsetY = currentImage.height / 2;
|
||||
const currentScale = currentImage.scaleY;
|
||||
let editOffsetY = currentImage.height;
|
||||
if (currentScale < 0) editOffsetY = 0;
|
||||
const editScaleY = -1 * currentImage.scaleY;
|
||||
dispatch(
|
||||
updateImage({
|
||||
@@ -21,7 +23,9 @@ export function ImageToolbar({ selectedItemId, currentImage }) {
|
||||
};
|
||||
|
||||
const flipImageHorizontaly = () => {
|
||||
const editOffsetX = currentImage.width / 2;
|
||||
const currentScale = currentImage.scaleX;
|
||||
let editOffsetX = currentImage.width;
|
||||
if (currentScale < 0) editOffsetX = 0;
|
||||
const editScaleX = -1 * currentImage.scaleX;
|
||||
dispatch(
|
||||
updateImage({
|
||||
|
||||
74
src/components/layout/sidebar/background-select.tsx
Normal file
74
src/components/layout/sidebar/background-select.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { RxLayout } from "react-icons/rx";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { useAppDispatch } from "@/hooks";
|
||||
import { selectBackground } from "@/store/app.slice";
|
||||
|
||||
export const BackgroundSelect = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleBackgroundSelect = (color: string) => {
|
||||
dispatch(selectBackground(color));
|
||||
};
|
||||
|
||||
const handleSizeSelect = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
onClick={() => setOpen(true)}
|
||||
variant="outline"
|
||||
className="text-xl"
|
||||
>
|
||||
<RxLayout />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent side="right" className="mt-4">
|
||||
<Card className="p-3">
|
||||
<CardHeader className="mb-2 p-2 text-center">
|
||||
<CardTitle>Fond</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
<div
|
||||
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-2 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary"
|
||||
onClick={() => handleBackgroundSelect("red")}
|
||||
>
|
||||
Red
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="paypal"
|
||||
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary"
|
||||
onClick={() => handleBackgroundSelect("blue")}
|
||||
>
|
||||
Blue
|
||||
</Label>
|
||||
</div>
|
||||
<div>
|
||||
<Label
|
||||
htmlFor="apple"
|
||||
className="flex flex-col items-center justify-between rounded-md border-2 border-muted bg-popover p-4 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary"
|
||||
onClick={() => handleBackgroundSelect("green")}
|
||||
>
|
||||
Green
|
||||
</Label>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
@@ -6,11 +6,11 @@ import {
|
||||
} from "@/components/ui/popover";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PiImageDuotone } from "react-icons/pi";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { addImage } from "@/store/app.slice";
|
||||
import { useAppDispatch } from "@/hooks";
|
||||
import { Card, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { RxImage } from "react-icons/rx";
|
||||
|
||||
function ImageInput() {
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -32,8 +32,8 @@ function ImageInput() {
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="secondary" className="text-xl">
|
||||
<PiImageDuotone />
|
||||
<Button variant="outline" className="text-xl">
|
||||
<RxImage />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent side="right">
|
||||
|
||||
31
src/components/layout/sidebar/layers.tsx
Normal file
31
src/components/layout/sidebar/layers.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { RxStack } from "react-icons/rx";
|
||||
|
||||
export const Layers = () => {
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="outline" className="text-xl">
|
||||
<RxStack />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent side="right">
|
||||
<Card className="p-2">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-center">Layer</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-2">
|
||||
<div className="flex h-[2rem] flex-col items-center justify-between rounded-md border-2 border-muted bg-popover"></div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
@@ -1,12 +1,11 @@
|
||||
import React, { ChangeEvent } from "react";
|
||||
import React from "react";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PiFrameCornersDuotone, PiImageDuotone } from "react-icons/pi";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { PiFrameCornersDuotone } from "react-icons/pi";
|
||||
import { useAppDispatch } from "@/hooks";
|
||||
import { Card, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
|
||||
@@ -25,7 +24,7 @@ export function SelectTemplate() {
|
||||
|
||||
<Card className="p-2">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-center">Choisissez un cadre</CardTitle>
|
||||
<CardTitle className="text-center">Cadre</CardTitle>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</PopoverContent>
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { TextInput } from "@/components/layout/sidebar/text-input";
|
||||
import ImageInput from "@/components/layout/sidebar/image-input";
|
||||
import { SelectTemplate } from "@/components/layout/sidebar/select-template";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useAppDispatch } from "@/hooks";
|
||||
import SizeSelect from "@/components/layout/sidebar/size-select";
|
||||
import { BackgroundSelect } from "@/components/layout/sidebar/background-select";
|
||||
import { Layers } from "@/components/layout/sidebar/layers";
|
||||
|
||||
export function Sidebar() {
|
||||
return (
|
||||
<div className="flex h-full w-20 flex-col gap-2 p-2">
|
||||
<div className="inline-flex h-full w-20 flex-col items-center justify-center gap-2 p-2">
|
||||
<SizeSelect />
|
||||
<TextInput />
|
||||
<ImageInput />
|
||||
<SelectTemplate />
|
||||
{/*<SelectTemplate />*/}
|
||||
<BackgroundSelect />
|
||||
<Layers />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,15 +5,22 @@ import {
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { PiTextT } from "react-icons/pi";
|
||||
import { Card, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { addText } from "@/store/app.slice";
|
||||
import { SlSizeFullscreen } from "react-icons/sl";
|
||||
import { RxMove } from "react-icons/rx";
|
||||
import { useAppDispatch } from "@/hooks";
|
||||
import { updateStage } from "@/store/app.slice";
|
||||
|
||||
const SizeSelect = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleStageSizeSelect = (
|
||||
width: number | undefined,
|
||||
height: number | undefined,
|
||||
) => {
|
||||
dispatch(updateStage({ width, height }));
|
||||
};
|
||||
|
||||
const handleSizeSelect = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
@@ -22,10 +29,10 @@ const SizeSelect = () => {
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
onClick={() => setOpen(true)}
|
||||
variant="secondary"
|
||||
variant="outline"
|
||||
className="text-xl"
|
||||
>
|
||||
<SlSizeFullscreen />
|
||||
<RxMove />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent side="right" className="mt-4">
|
||||
@@ -35,17 +42,23 @@ const SizeSelect = () => {
|
||||
</CardHeader>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<Button>Petit Format</Button>
|
||||
<Button>Moyen Format</Button>
|
||||
<Button>Grand Format</Button>
|
||||
<Button onClick={() => handleStageSizeSelect(350, 400)}>
|
||||
Petit Format
|
||||
</Button>
|
||||
<Button onClick={() => handleStageSizeSelect(500, 500)}>
|
||||
Moyen Format
|
||||
</Button>
|
||||
<Button onClick={() => handleStageSizeSelect(800, 600)}>
|
||||
Grand Format
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<CardFooter className="mt-8 justify-between">
|
||||
<Button variant="ghost" onClick={() => setOpen(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleSizeSelect}>Submit</Button>
|
||||
</CardFooter>
|
||||
{/*<CardFooter className="mt-8 justify-between">*/}
|
||||
{/* <Button variant="ghost" onClick={() => setOpen(false)}>*/}
|
||||
{/* Cancel*/}
|
||||
{/* </Button>*/}
|
||||
{/* <Button onClick={handleSizeSelect}>Submit</Button>*/}
|
||||
{/*</CardFooter>*/}
|
||||
</Card>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
@@ -8,10 +8,8 @@ import { Textarea } from "@/components/ui/textarea";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { addText } from "@/store/app.slice";
|
||||
import { useAppDispatch } from "@/hooks";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { PiTextT } from "react-icons/pi";
|
||||
import { Card, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { RxText } from "react-icons/rx";
|
||||
|
||||
export function TextInput() {
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -30,16 +28,16 @@ export function TextInput() {
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
onClick={() => setOpen(true)}
|
||||
variant="secondary"
|
||||
variant="outline"
|
||||
className="text-xl"
|
||||
>
|
||||
<PiTextT />
|
||||
<RxText />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent side="right" className="mt-4">
|
||||
<Card className="p-3">
|
||||
<CardHeader>
|
||||
<CardTitle>Ajouter votre text</CardTitle>
|
||||
<CardHeader className="pt-2">
|
||||
<CardTitle className="text-center">Text</CardTitle>
|
||||
</CardHeader>
|
||||
<Textarea
|
||||
id="description"
|
||||
|
||||
@@ -3,8 +3,6 @@ import { ImageToolbar } from "@/components/image-toolbar";
|
||||
import { TextToolbar } from "@/components/text-toolbar";
|
||||
import { useAppDispatch, useAppSelector } from "@/hooks";
|
||||
import { updateText } from "@/store/app.slice";
|
||||
import { BsTrash3 } from "react-icons/bs";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { DeleteShapeButton } from "@/components/delete-shape-button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
import type { TransformableImageProps } from "@/components/transformable-image";
|
||||
import type { TransformableTextProps } from "@/components/transformable-text";
|
||||
import type { PayloadAction } from "@reduxjs/toolkit";
|
||||
import type { ChangeEvent } from "react";
|
||||
|
||||
import { createSlice, current } from "@reduxjs/toolkit";
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { v1 } from "uuid";
|
||||
import type { TextConfig } from "konva/lib/shapes/Text";
|
||||
import type { ImageConfig } from "konva/lib/shapes/Image";
|
||||
import { type RectConfig } from "konva/lib/shapes/Rect";
|
||||
|
||||
const initialState = {
|
||||
stage: { width: 500, height: 500 },
|
||||
selectedItemId: null as string | null,
|
||||
images: [] as TransformableImageProps["imageProps"][],
|
||||
texts: [] as TransformableTextProps["textProps"][],
|
||||
backgroundRects: [] as RectConfig[],
|
||||
};
|
||||
|
||||
type InitialState = typeof initialState;
|
||||
|
||||
const defaultTextConfig = {
|
||||
fontSize: 16,
|
||||
align: "center",
|
||||
fontFamily: "Roboto",
|
||||
zIndex: 3,
|
||||
};
|
||||
|
||||
const defaultImageConfig = {
|
||||
@@ -30,6 +28,7 @@ const defaultImageConfig = {
|
||||
offsetY: 0,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
zIndex: 1,
|
||||
};
|
||||
|
||||
export const appSlice = createSlice({
|
||||
@@ -45,6 +44,19 @@ export const appSlice = createSlice({
|
||||
});
|
||||
},
|
||||
|
||||
selectBackground: (state, action: PayloadAction<string>) => {
|
||||
state.backgroundRects = [];
|
||||
state.backgroundRects.push({
|
||||
x: 0,
|
||||
y: 0,
|
||||
stroke: "black",
|
||||
z_index: 0,
|
||||
width: state.stage.width,
|
||||
height: state.stage.height,
|
||||
fill: action.payload,
|
||||
});
|
||||
},
|
||||
|
||||
addImage: (
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
@@ -74,6 +86,13 @@ export const appSlice = createSlice({
|
||||
state.selectedItemId = null;
|
||||
},
|
||||
|
||||
updateStage: (
|
||||
state,
|
||||
action: PayloadAction<{ width?: number; height?: number }>,
|
||||
) => {
|
||||
state.stage = { ...state.stage, ...action.payload };
|
||||
},
|
||||
|
||||
updateText: (
|
||||
state,
|
||||
action: PayloadAction<{ id: string } & Partial<TransformableTextProps>>,
|
||||
@@ -126,4 +145,6 @@ export const {
|
||||
updateText,
|
||||
updateImage,
|
||||
deleteShape,
|
||||
updateStage,
|
||||
selectBackground,
|
||||
} = appSlice.actions;
|
||||
|
||||
Reference in New Issue
Block a user