toolbar added

This commit is contained in:
Artur AGH
2023-10-03 15:57:10 +02:00
parent 55cd3f963d
commit 13e4e0a1a7
9 changed files with 181 additions and 87 deletions

View File

@@ -14,77 +14,63 @@ import TransformableText, {
import { Button } from "./ui/button"; import { Button } from "./ui/button";
import { Input } from "./ui/input"; import { Input } from "./ui/input";
import { useAppDispatch, useAppSelector } from "@/hooks"; 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 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 deselectHandler = (e) => {
dispatch(checkDeselect(e));
const selectItem = (id: string) => dispatch(appSlice.actions.selectItem(id))
const [selectedImageId, selectImage] = useState<string | null>(null);
const [selectedTextId, selectText] = useState<string | null>(null);
const [inputText, setInputText] = useState("");
const [images, setImages] = useState<TransformableImageProps["imageProps"][]>(
[],
);
const [texts, setTexts] = useState<TransformableTextProps["textProps"][]>([]);
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputText(e.target.value);
}; };
return ( return (
<main className="flex"> <div className="flex h-screen w-full flex-col items-center justify-between">
<Toolbar />
<Stage width={window.innerWidth} height={window.innerHeight}> <Stage
className="bg-white"
width={600}
height={500}
onTouchStart={deselectHandler}
onMouseDown={deselectHandler}
>
<Layer> <Layer>
{images.map((image) => { {images.map((image) => {
return ( return (
<TransformableImage <TransformableImage
onSelect={() => selectItem(image.imageId)} onSelect={() => selectItem(image.id)}
isSelected={image.imageId === selectedItemId} isSelected={image.id === selectedItemId}
onChange={(newAttrs) => { onChange={(newAttrs) => {
setImages( images.map((i) => (i.id === image.id ? newAttrs : i));
images.map((i) =>
i.imageId === image.imageId ? newAttrs : i,
),
);
}} }}
imageProps={image} imageProps={image}
key={image.imageId} key={image.id}
/> />
); );
})} })}
{texts.map((text) => { {texts.map((text) => {
return ( return (
<TransformableText <TransformableText
onSelect={() => selectItem(text.textId)} onSelect={() => selectItem(text.id)}
isSelected={text.textId === selectedItemId} isSelected={text.id === selectedItemId}
onChange={(newAttrs) => { onChange={(newAttrs) => {
setTexts( texts.map((t) => (t.id === text.id ? newAttrs : t));
texts.map((t) => (t.textId === text.textId ? newAttrs : t)),
);
}} }}
textProps={text} textProps={text}
key={text.textId} key={text.id}
/> />
); );
})} })}
</Layer> </Layer>
</Stage> </Stage>
</main> </div>
); );
}; };

View File

@@ -1,5 +1,6 @@
import { ReactNode } from "react"; import { ReactNode } from "react";
import { Sidebar } from "./sidebar"; import { Sidebar } from "./sidebar";
import { Navbar } from "./navbar";
type Props = { type Props = {
children: ReactNode; children: ReactNode;
@@ -7,9 +8,12 @@ type Props = {
export const Layout = ({ children }: Props) => { export const Layout = ({ children }: Props) => {
return ( return (
<div className="flex h-full "> <>
<Sidebar {} /> <Navbar />
<main>{children}</main> <div className="flex h-full ">
</div> <Sidebar />
<main className="h-full w-full bg-slate-300">{children}</main>
</div>
</>
); );
}; };

View File

@@ -0,0 +1,7 @@
export const Navbar = () => {
return (
<div>
Navbar
</div>
)
}

View File

@@ -1,21 +1,30 @@
import { addImage, addText } from "@/store/app.slice";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import { Input } from "../ui/input"; 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({ export function Sidebar() {
handleImageUploaded, const dispatch = useAppDispatch();
handleInputChange,
handleTextAdd, const [inputText, setInputText] = useState("");
inputText,
}: Props) { const handleImageUploaded = (e: ChangeEvent<HTMLInputElement>) => {
dispatch(addImage(e));
};
const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
setInputText(e.target.value);
};
console.log(inputText);
const handleTextAdd = () => dispatch(addText({initialValue: inputText}));
return ( return (
<div className="flex flex-col "> <div className="h-full flex flex-col ">
<Input <Input
type="file" type="file"
className="m-[2rem] w-auto " className="m-[2rem] w-auto "

View File

@@ -0,0 +1,51 @@
import { useAppDispatch, useAppSelector } from "@/hooks";
import { updateText } from "@/store/app.slice";
export const Toolbar = () => {
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 (
<div className="flex gap-6">
<button onClick={handleFontStyleToggle("italic")}>Italic</button>
<button onClick={handleFontStyleToggle("bold")}>Bold</button>
</div>
);
};
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();
}
};

View File

@@ -6,17 +6,21 @@ import { api } from "@/utils/api";
import "@/styles/globals.css"; import "@/styles/globals.css";
import { Layout } from "@/components/layout/layout"; import { Layout } from "@/components/layout/layout";
import { Provider } from "react-redux";
import { store } from "@/store/store";
const MyApp: AppType<{ session: Session | null }> = ({ const MyApp: AppType<{ session: Session | null }> = ({
Component, Component,
pageProps: { session, ...pageProps }, pageProps: { session, ...pageProps },
}) => { }) => {
return ( return (
<SessionProvider session={session}> <Provider store={store}>
<Layout> <SessionProvider session={session}>
<Component {...pageProps} /> <Layout>
</Layout> <Component {...pageProps} />
</SessionProvider> </Layout>
</SessionProvider>
</Provider>
); );
}; };

View File

@@ -1,9 +1,12 @@
import { TransformableImageProps } from "@/components/transformable-image"; import type { TransformableImageProps } from "@/components/transformable-image";
import { TransformableTextProps } from "@/components/transformable-text"; import type { TransformableTextProps } from "@/components/transformable-text";
import { PayloadAction, createSlice } from "@reduxjs/toolkit"; import type { PayloadAction } from "@reduxjs/toolkit";
import { KonvaEventObject } from "konva/lib/Node"; import type { KonvaEventObject } from "konva/lib/Node";
import { ChangeEvent } from "react"; import type { ChangeEvent } from "react";
import { createSlice } from "@reduxjs/toolkit";
import { v1 } from "uuid"; import { v1 } from "uuid";
import { TextConfig } from "konva/lib/shapes/Text";
const initialState = { const initialState = {
selectedItemId: null as string | null, selectedItemId: null as string | null,
@@ -25,6 +28,7 @@ export const appSlice = createSlice({
}, },
addText: (state, action: PayloadAction<{ initialValue: string }>) => { addText: (state, action: PayloadAction<{ initialValue: string }>) => {
const textId = v1(); const textId = v1();
state.texts.push({ text: action.payload.initialValue, id: textId }); state.texts.push({ text: action.payload.initialValue, id: textId });
}, },
@@ -43,5 +47,20 @@ export const appSlice = createSlice({
state.selectedItemId = null; state.selectedItemId = null;
} }
}, },
updateText: (
state,
action: PayloadAction<Omit<TextConfig, "id"> & { 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;

View File

@@ -74,3 +74,15 @@
@apply bg-background text-foreground; @apply bg-background text-foreground;
} }
} }
:root {
height: 100%;
}
body {
height: 100%;
}
#__next {
height: 100%;
}

2
todo.md Normal file
View File

@@ -0,0 +1,2 @@
[ ] image toolbar
[ ] text toolbar