diff --git a/src/components/image-editing-tools/image-opacity-tool.tsx b/src/components/image-editing-tools/image-opacity-tool.tsx new file mode 100644 index 0000000..7601058 --- /dev/null +++ b/src/components/image-editing-tools/image-opacity-tool.tsx @@ -0,0 +1,59 @@ +import React from "react"; +import { Label } from "@/components/ui/label"; +import { Slider } from "@/components/ui/slider"; +import { useAppDispatch } from "@/hooks"; +import { updateImage, updateText } from "@/store/app.slice"; +import { HoverCard, HoverCardTrigger } from "@/components/ui/hover-card"; +import { type ImageConfig } from "konva/lib/shapes/Image"; +import { TransformableImageProps } from "@/components/transformable-image"; + +type Props = { + currentImage: ImageConfig; + selectedItemId: string; +}; + +export default function ImageOpacityTool({ + selectedItemId, + currentImage, +}: Props) { + const dispatch = useAppDispatch(); + + const value = currentImage.opacity ?? 1; + + const handleOpacityChange = (e) => { + if (!selectedItemId) return; + dispatch( + updateImage({ + id: selectedItemId, + opacity: e, + }), + ); + }; + + return ( + + + + + Opacity + + {value} + + + { + handleOpacityChange(e[0]); + }} + className="[&_[role=slider]]:h-4 [&_[role=slider]]:w-4" + aria-label="opacity" + /> + + + + ); +} diff --git a/src/components/image-toolbar.tsx b/src/components/image-toolbar.tsx index ac5f237..8a76b37 100644 --- a/src/components/image-toolbar.tsx +++ b/src/components/image-toolbar.tsx @@ -1,32 +1,49 @@ 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"; +import ImageOpacityTool from "@/components/image-editing-tools/image-opacity-tool"; +import { Separator } from "@/components/ui/separator"; export function ImageToolbar({ selectedItemId, currentImage }) { const dispatch = useAppDispatch(); - const flipImageVerticaly = (selectedItemId) => { + const flipImageVerticaly = () => { + const editOffsetY = currentImage.height / 2; + const editScaleY = -1 * currentImage.scaleY; dispatch( updateImage({ id: selectedItemId, + offsetY: editOffsetY, + scaleY: editScaleY, }), ); }; const flipImageHorizontaly = () => { - console.log("Horizontal Flip"); + const editOffsetX = currentImage.width / 2; + const editScaleX = -1 * currentImage.scaleX; + dispatch( + updateImage({ + id: selectedItemId, + offsetX: editOffsetX, + scaleX: editScaleX, + }), + ); }; return ( <> - + flipImageHorizontaly()}> - + flipImageVerticaly()} /> + + > ); } diff --git a/src/components/layout/sidebar/image-input.tsx b/src/components/layout/sidebar/image-input.tsx index 8d07149..f8fc39d 100644 --- a/src/components/layout/sidebar/image-input.tsx +++ b/src/components/layout/sidebar/image-input.tsx @@ -1,4 +1,4 @@ -import React, { ChangeEvent } from "react"; +import React, { type ChangeEvent } from "react"; import { Popover, PopoverContent, @@ -16,7 +16,17 @@ function ImageInput() { const dispatch = useAppDispatch(); const handleImageUploaded = (e: ChangeEvent) => { - dispatch(addImage(e)); + const file = e.target.files?.[0]; + if (!file) return; + const img = document.createElement("img"); + const imageUrl = URL.createObjectURL(file); + img.onload = () => { + dispatch(addImage({ imageUrl, width: img.width, height: img.height })); + img.remove(); + }; + img.src = imageUrl; + img.style.cssText = "display: none;"; + document.body.appendChild(img); }; return ( diff --git a/src/store/app.slice.ts b/src/store/app.slice.ts index e0655a7..563b3b2 100644 --- a/src/store/app.slice.ts +++ b/src/store/app.slice.ts @@ -22,7 +22,15 @@ const defaultTextConfig = { fontFamily: "Roboto", }; -const defaultImageConfig = {}; +const defaultImageConfig = { + x: 50, + y: 50, + opacity: 1, + offsetX: 0, + offsetY: 0, + scaleX: 1, + scaleY: 1, +}; export const appSlice = createSlice({ name: "app", @@ -35,17 +43,27 @@ export const appSlice = createSlice({ id: textId, ...defaultTextConfig, }); - - console.log(current(state.texts)); }, - addImage: (state, action: PayloadAction>) => { - const file = action.payload.target.files?.[0]; + addImage: ( + state, + action: PayloadAction<{ + imageUrl: string; + width: number; + height: number; + }>, + ) => { + const file = action.payload; if (!file) return; const imageId = v1(); - const imageUrl = URL.createObjectURL(file); - state.images.push({ imageUrl, id: imageId }); - console.log(current(state.images)); + + state.images.push({ + imageUrl: action.payload.imageUrl, + id: imageId, + width: action.payload.width, + height: action.payload.height, + ...defaultImageConfig, + }); }, selectItem: (state, action: PayloadAction) => {