mirror of
https://github.com/r2r90/canvas-label.git
synced 2025-12-16 21:19:38 +00:00
adding canvas size button to sidebar - without fonctionality
This commit is contained in:
25
src/components/delete-shape-button.tsx
Normal file
25
src/components/delete-shape-button.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import React from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { deleteShape } from "@/store/app.slice";
|
||||
import { useAppDispatch } from "@/hooks";
|
||||
import { BsTrash3 } from "react-icons/bs";
|
||||
|
||||
type Props = {
|
||||
selectedItemId: string;
|
||||
};
|
||||
|
||||
export const DeleteShapeButton = ({ selectedItemId }: Props) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const deleteTextHandler = (selectedItemId: string) => {
|
||||
dispatch(deleteShape(selectedItemId));
|
||||
};
|
||||
return (
|
||||
<Button
|
||||
variant="destructive"
|
||||
color="cyan"
|
||||
onClick={() => deleteTextHandler(selectedItemId)}
|
||||
>
|
||||
<BsTrash3 />
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,32 @@
|
||||
import React from "react";
|
||||
import { DeleteShapeButton } from "@/components/delete-shape-button";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { TbFlipHorizontal, TbFlipVertical } from "react-icons/tb";
|
||||
import { Toggle } from "@/components/ui/toggle";
|
||||
import { useAppDispatch } from "@/hooks";
|
||||
import { updateImage } from "@/store/app.slice";
|
||||
|
||||
export function ImageToolbar() {
|
||||
return <div>Image toolbar</div>;
|
||||
export function ImageToolbar({ selectedItemId, currentImage }) {
|
||||
const dispatch = useAppDispatch();
|
||||
const flipImageVerticaly = (selectedItemId) => {
|
||||
dispatch(
|
||||
updateImage({
|
||||
id: selectedItemId,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const flipImageHorizontaly = () => {
|
||||
console.log("Horizontal Flip");
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Toggle onClick={flipImageVerticaly}>
|
||||
<TbFlipVertical />
|
||||
</Toggle>
|
||||
<Toggle>
|
||||
<TbFlipHorizontal onClick={flipImageHorizontaly} />
|
||||
</Toggle>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@ 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";
|
||||
|
||||
export function Sidebar() {
|
||||
return (
|
||||
<div className="flex h-full w-20 flex-col gap-2 p-2">
|
||||
<SizeSelect />
|
||||
<TextInput />
|
||||
<ImageInput />
|
||||
<SelectTemplate />
|
||||
|
||||
54
src/components/layout/sidebar/size-select.tsx
Normal file
54
src/components/layout/sidebar/size-select.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
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";
|
||||
|
||||
const SizeSelect = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleSizeSelect = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
onClick={() => setOpen(true)}
|
||||
variant="secondary"
|
||||
className="text-xl"
|
||||
>
|
||||
<SlSizeFullscreen />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent side="right" className="mt-4">
|
||||
<Card className="p-3">
|
||||
<CardHeader>
|
||||
<CardTitle>Sélectionnez la taille d'étiquette</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<Button>Petit Format</Button>
|
||||
<Button>Moyen Format</Button>
|
||||
<Button>Grand Format</Button>
|
||||
</div>
|
||||
|
||||
<CardFooter className="mt-8 justify-between">
|
||||
<Button variant="ghost" onClick={() => setOpen(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleSizeSelect}>Submit</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
export default SizeSelect;
|
||||
@@ -9,6 +9,7 @@ import { type TransformableTextProps } from "@/components/transformable-text";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useAppDispatch } from "@/hooks";
|
||||
import { deleteShape } from "@/store/app.slice";
|
||||
import { DeleteShapeButton } from "@/components/delete-shape-button";
|
||||
|
||||
type Props = {
|
||||
selectedItemId: string;
|
||||
@@ -22,9 +23,6 @@ export const TextToolbar = ({
|
||||
}: Props) => {
|
||||
const dispatch = useAppDispatch();
|
||||
if (!currentText) return null;
|
||||
const deleteTextHandler = (selectedItemId) => {
|
||||
dispatch(deleteShape(selectedItemId));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -53,9 +51,6 @@ export const TextToolbar = ({
|
||||
currentText={currentText}
|
||||
selectedItemId={selectedItemId}
|
||||
/>
|
||||
<Button onClick={() => deleteTextHandler(selectedItemId)}>
|
||||
Delete Text
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,6 +3,10 @@ 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";
|
||||
|
||||
export const Toolbar = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -27,7 +31,6 @@ export const Toolbar = () => {
|
||||
|
||||
return (
|
||||
<div className=" flex h-[5rem] w-full gap-6 border border-t-0 bg-white p-[1rem]">
|
||||
{currentImage && <ImageToolbar />}
|
||||
{currentText && (
|
||||
<TextToolbar
|
||||
currentText={currentText}
|
||||
@@ -35,6 +38,16 @@ export const Toolbar = () => {
|
||||
onTextColorChange={handleTextColorChange}
|
||||
/>
|
||||
)}
|
||||
{currentImage && (
|
||||
<ImageToolbar
|
||||
currentImage={currentImage}
|
||||
selectedItemId={selectedItemId}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Separator orientation="vertical" />
|
||||
|
||||
<DeleteShapeButton selectedItemId={selectedItemId} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,10 +3,10 @@ import type { TransformableTextProps } from "@/components/transformable-text";
|
||||
import type { PayloadAction } from "@reduxjs/toolkit";
|
||||
import type { ChangeEvent } from "react";
|
||||
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { createSlice, current } from "@reduxjs/toolkit";
|
||||
import { v1 } from "uuid";
|
||||
import type { TextConfig } from "konva/lib/shapes/Text";
|
||||
import { RootState } from "./store";
|
||||
import type { ImageConfig } from "konva/lib/shapes/Image";
|
||||
|
||||
const initialState = {
|
||||
selectedItemId: null as string | null,
|
||||
@@ -22,26 +22,30 @@ const defaultTextConfig = {
|
||||
fontFamily: "Roboto",
|
||||
};
|
||||
|
||||
const defaultImageConfig = {};
|
||||
|
||||
export const appSlice = createSlice({
|
||||
name: "app",
|
||||
initialState,
|
||||
reducers: {
|
||||
addText: (state, action: PayloadAction<{ initialValue: string }>) => {
|
||||
const textId = v1();
|
||||
state.texts.push({
|
||||
text: action.payload.initialValue,
|
||||
id: textId,
|
||||
...defaultTextConfig,
|
||||
});
|
||||
|
||||
console.log(current(state.texts));
|
||||
},
|
||||
|
||||
addImage: (state, action: PayloadAction<ChangeEvent<HTMLInputElement>>) => {
|
||||
const file = action.payload.target.files?.[0];
|
||||
if (!file) return;
|
||||
const imageId = v1();
|
||||
const imageUrl = URL.createObjectURL(file);
|
||||
state.images.push({ imageUrl, id: imageId });
|
||||
},
|
||||
|
||||
addText: (state, action: PayloadAction<{ initialValue: string }>) => {
|
||||
const textId = v1();
|
||||
console.log(state);
|
||||
state.texts.push({
|
||||
text: action.payload.initialValue,
|
||||
id: textId,
|
||||
...defaultTextConfig,
|
||||
});
|
||||
console.log(current(state.images));
|
||||
},
|
||||
|
||||
selectItem: (state, action: PayloadAction<string>) => {
|
||||
@@ -54,7 +58,7 @@ export const appSlice = createSlice({
|
||||
|
||||
updateText: (
|
||||
state,
|
||||
action: PayloadAction<{ id: string } & Partial<TextConfig>>,
|
||||
action: PayloadAction<{ id: string } & Partial<TransformableTextProps>>,
|
||||
) => {
|
||||
const textToUpdateIndex = state.texts.findIndex(
|
||||
(t) => t.id === action.payload.id,
|
||||
@@ -69,11 +73,28 @@ export const appSlice = createSlice({
|
||||
};
|
||||
},
|
||||
|
||||
updateImage: (
|
||||
state,
|
||||
action: PayloadAction<{ id: string } & Partial<TransformableImageProps>>,
|
||||
) => {
|
||||
const imageToUpdateIndex = state.images.findIndex(
|
||||
(img) => img.id === action.payload.id,
|
||||
);
|
||||
const imageToUpdate = state.images[imageToUpdateIndex];
|
||||
|
||||
if (!imageToUpdate) return;
|
||||
|
||||
state.images[imageToUpdateIndex] = {
|
||||
...imageToUpdate,
|
||||
...action.payload,
|
||||
};
|
||||
},
|
||||
|
||||
deleteShape: (state, action: PayloadAction<string>) => {
|
||||
console.log(state.texts);
|
||||
return {
|
||||
...state,
|
||||
texts: state.texts.filter((shape) => shape.id !== action.payload),
|
||||
images: state.images.filter((shape) => shape.id !== action.payload),
|
||||
};
|
||||
},
|
||||
},
|
||||
@@ -85,5 +106,6 @@ export const {
|
||||
selectItem,
|
||||
deselectItem,
|
||||
updateText,
|
||||
updateImage,
|
||||
deleteShape,
|
||||
} = appSlice.actions;
|
||||
|
||||
Reference in New Issue
Block a user