From 13e4e0a1a7e73330d4221ead75238b919e3b6efa Mon Sep 17 00:00:00 2001 From: Artur AGH Date: Tue, 3 Oct 2023 15:57:10 +0200 Subject: [PATCH] toolbar added --- src/components/canvas.tsx | 70 +++++++++++++------------------ src/components/layout/layout.tsx | 12 ++++-- src/components/layout/navbar.tsx | 7 ++++ src/components/layout/sidebar.tsx | 35 ++++++++++------ src/components/toolbar.tsx | 51 ++++++++++++++++++++++ src/pages/_app.tsx | 14 ++++--- src/store/app.slice.ts | 29 ++++++++++--- src/styles/globals.css | 48 +++++++++++++-------- todo.md | 2 + 9 files changed, 181 insertions(+), 87 deletions(-) create mode 100644 src/components/layout/navbar.tsx create mode 100644 src/components/toolbar.tsx create mode 100644 todo.md diff --git a/src/components/canvas.tsx b/src/components/canvas.tsx index f188660..87b909e 100644 --- a/src/components/canvas.tsx +++ b/src/components/canvas.tsx @@ -14,77 +14,63 @@ import TransformableText, { import { Button } from "./ui/button"; import { Input } from "./ui/input"; import { useAppDispatch, useAppSelector } from "@/hooks"; -import { appSlice } from "@/store/app.slice"; +import { appSlice, checkDeselect } from "@/store/app.slice"; +import { Toolbar } from "./toolbar"; -// Provider * +// Provider * const Canvas = () => { + const dispatch = useAppDispatch(); - const dispatch = useAppDispatch() + 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 selectedItemId = useAppSelector((state) => state.app.selectedItemId) - - const selectItem = (id: string) => dispatch(appSlice.actions.selectItem(id)) - - const [selectedImageId, selectImage] = useState(null); - const [selectedTextId, selectText] = useState(null); - const [inputText, setInputText] = useState(""); - - const [images, setImages] = useState( - [], - ); - - const [texts, setTexts] = useState([]); - - - - const handleInputChange = (e: React.ChangeEvent) => { - setInputText(e.target.value); + const deselectHandler = (e) => { + dispatch(checkDeselect(e)); }; - - - return ( -
- - +
+ + {images.map((image) => { return ( selectItem(image.imageId)} - isSelected={image.imageId === selectedItemId} + onSelect={() => selectItem(image.id)} + isSelected={image.id === selectedItemId} onChange={(newAttrs) => { - setImages( - images.map((i) => - i.imageId === image.imageId ? newAttrs : i, - ), - ); + images.map((i) => (i.id === image.id ? newAttrs : i)); }} imageProps={image} - key={image.imageId} + key={image.id} /> ); })} {texts.map((text) => { return ( selectItem(text.textId)} - isSelected={text.textId === selectedItemId} + onSelect={() => selectItem(text.id)} + isSelected={text.id === selectedItemId} onChange={(newAttrs) => { - setTexts( - texts.map((t) => (t.textId === text.textId ? newAttrs : t)), - ); + texts.map((t) => (t.id === text.id ? newAttrs : t)); }} textProps={text} - key={text.textId} + key={text.id} /> ); })} -
+ ); }; diff --git a/src/components/layout/layout.tsx b/src/components/layout/layout.tsx index bee5424..6502465 100644 --- a/src/components/layout/layout.tsx +++ b/src/components/layout/layout.tsx @@ -1,5 +1,6 @@ import { ReactNode } from "react"; import { Sidebar } from "./sidebar"; +import { Navbar } from "./navbar"; type Props = { children: ReactNode; @@ -7,9 +8,12 @@ type Props = { export const Layout = ({ children }: Props) => { return ( -
- -
{children}
-
+ <> + +
+ +
{children}
+
+ ); }; diff --git a/src/components/layout/navbar.tsx b/src/components/layout/navbar.tsx new file mode 100644 index 0000000..919a54c --- /dev/null +++ b/src/components/layout/navbar.tsx @@ -0,0 +1,7 @@ +export const Navbar = () => { + return ( +
+ Navbar +
+ ) +} \ No newline at end of file diff --git a/src/components/layout/sidebar.tsx b/src/components/layout/sidebar.tsx index 68f2f07..6c0b53a 100644 --- a/src/components/layout/sidebar.tsx +++ b/src/components/layout/sidebar.tsx @@ -1,21 +1,30 @@ +import { addImage, addText } from "@/store/app.slice"; import { Button } from "../ui/button"; import { Input } from "../ui/input"; +import { useAppDispatch } from "@/hooks"; +import { ChangeEvent, useState } from "react"; -type Props = { - handleImageUploaded: any; - handleInputChange: any; - inputText: any; - handleTextAdd: any; -}; -export function Sidebar({ - handleImageUploaded, - handleInputChange, - handleTextAdd, - inputText, -}: Props) { +export function Sidebar() { + const dispatch = useAppDispatch(); + + const [inputText, setInputText] = useState(""); + + const handleImageUploaded = (e: ChangeEvent) => { + dispatch(addImage(e)); + }; + + const handleInputChange = (e: ChangeEvent) => { + setInputText(e.target.value); + }; + + console.log(inputText); + + + const handleTextAdd = () => dispatch(addText({initialValue: inputText})); + return ( -
+
{ + const dispatch = useAppDispatch(); + const selectedItemId = useAppSelector((state) => state.app.selectedItemId); + const texts = useAppSelector((state) => state.app.texts); + + const currentText = texts.find((t) => t.id === selectedItemId); + + console.log(currentText); + + if (!currentText) return null; + + const handleFontStyleToggle = (button: "bold" | "italic") => () => { + dispatch( + updateText({ + ...currentText, + fontStyle: getFontStyle( + currentText.fontStyle as string | undefined, + button, + ), + }), + ); + }; + + return ( +
+ + +
+ ); +}; + +const getFontStyle = ( + currentStyle: string | undefined, + buttonClicked: "bold" | "italic", +) => { + if (buttonClicked === "bold") { + if (currentStyle?.includes("bold")) { + return currentStyle?.replace("bold", "").trim(); + } + return ((currentStyle ?? "") + " bold").trim(); + } + if (buttonClicked === "italic") { + if (currentStyle?.includes("italic")) { + return currentStyle?.replace("italic", "").trim(); + } + return ((currentStyle ?? "") + " italic").trim(); + } +}; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 5356a65..0059781 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -6,17 +6,21 @@ import { api } from "@/utils/api"; import "@/styles/globals.css"; import { Layout } from "@/components/layout/layout"; +import { Provider } from "react-redux"; +import { store } from "@/store/store"; const MyApp: AppType<{ session: Session | null }> = ({ Component, pageProps: { session, ...pageProps }, }) => { return ( - - - - - + + + + + + + ); }; diff --git a/src/store/app.slice.ts b/src/store/app.slice.ts index 129e5bf..d04dcef 100644 --- a/src/store/app.slice.ts +++ b/src/store/app.slice.ts @@ -1,9 +1,12 @@ -import { TransformableImageProps } from "@/components/transformable-image"; -import { TransformableTextProps } from "@/components/transformable-text"; -import { PayloadAction, createSlice } from "@reduxjs/toolkit"; -import { KonvaEventObject } from "konva/lib/Node"; -import { ChangeEvent } from "react"; +import type { TransformableImageProps } from "@/components/transformable-image"; +import type { TransformableTextProps } from "@/components/transformable-text"; +import type { PayloadAction } from "@reduxjs/toolkit"; +import type { KonvaEventObject } from "konva/lib/Node"; +import type { ChangeEvent } from "react"; + +import { createSlice } from "@reduxjs/toolkit"; import { v1 } from "uuid"; +import { TextConfig } from "konva/lib/shapes/Text"; const initialState = { selectedItemId: null as string | null, @@ -25,6 +28,7 @@ export const appSlice = createSlice({ }, addText: (state, action: PayloadAction<{ initialValue: string }>) => { const textId = v1(); + state.texts.push({ text: action.payload.initialValue, id: textId }); }, @@ -43,5 +47,20 @@ export const appSlice = createSlice({ state.selectedItemId = null; } }, + + updateText: ( + state, + action: PayloadAction & { id: string }>, + ) => { + const textToUpdate = state.texts.findIndex( + (t) => t.id === action.payload.id, + ); + state.texts[textToUpdate] = action.payload; + }, }, }); + +export const { addImage, addText, selectItem, checkDeselect, updateText } = + appSlice.actions; + + diff --git a/src/styles/globals.css b/src/styles/globals.css index 6a75725..f577562 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -1,7 +1,7 @@ @tailwind base; @tailwind components; @tailwind utilities; - + @layer base { :root { --background: 0 0% 100%; @@ -9,63 +9,63 @@ --card: 0 0% 100%; --card-foreground: 222.2 84% 4.9%; - + --popover: 0 0% 100%; --popover-foreground: 222.2 84% 4.9%; - + --primary: 222.2 47.4% 11.2%; --primary-foreground: 210 40% 98%; - + --secondary: 210 40% 96.1%; --secondary-foreground: 222.2 47.4% 11.2%; - + --muted: 210 40% 96.1%; --muted-foreground: 215.4 16.3% 46.9%; - + --accent: 210 40% 96.1%; --accent-foreground: 222.2 47.4% 11.2%; - + --destructive: 0 84.2% 60.2%; --destructive-foreground: 210 40% 98%; --border: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%; --ring: 222.2 84% 4.9%; - + --radius: 0.5rem; } - + .dark { --background: 222.2 84% 4.9%; --foreground: 210 40% 98%; - + --card: 222.2 84% 4.9%; --card-foreground: 210 40% 98%; - + --popover: 222.2 84% 4.9%; --popover-foreground: 210 40% 98%; - + --primary: 210 40% 98%; --primary-foreground: 222.2 47.4% 11.2%; - + --secondary: 217.2 32.6% 17.5%; --secondary-foreground: 210 40% 98%; - + --muted: 217.2 32.6% 17.5%; --muted-foreground: 215 20.2% 65.1%; - + --accent: 217.2 32.6% 17.5%; --accent-foreground: 210 40% 98%; - + --destructive: 0 62.8% 30.6%; --destructive-foreground: 210 40% 98%; - + --border: 217.2 32.6% 17.5%; --input: 217.2 32.6% 17.5%; --ring: 212.7 26.8% 83.9%; } } - + @layer base { * { @apply border-border; @@ -73,4 +73,16 @@ body { @apply bg-background text-foreground; } +} + +:root { + height: 100%; +} +body { + height: 100%; +} + + +#__next { + height: 100%; } \ No newline at end of file diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..1c78435 --- /dev/null +++ b/todo.md @@ -0,0 +1,2 @@ +[ ] image toolbar +[ ] text toolbar