update zIndex changes

This commit is contained in:
Artur AGH
2023-11-05 11:02:32 +01:00
parent df33d4a3b8
commit 88feafddc3
11 changed files with 202 additions and 73 deletions

View File

@@ -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>
);

View File

@@ -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({

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

View File

@@ -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">

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

View File

@@ -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>

View File

@@ -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>
);
}

View File

@@ -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>

View File

@@ -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"

View File

@@ -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";

View File

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