diff --git a/package.json b/package.json
index 06f30f7..6ab9eb1 100644
--- a/package.json
+++ b/package.json
@@ -24,8 +24,10 @@
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slider": "^1.1.2",
+ "@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toggle": "^1.0.3",
"@radix-ui/react-toggle-group": "^1.0.4",
+ "@radix-ui/react-toolbar": "^1.0.4",
"@radix-ui/themes": "^2.0.0",
"@reduxjs/toolkit": "^1.9.6",
"@remotion/google-fonts": "^4.0.51",
@@ -49,6 +51,7 @@
"react-dom": "18.2.0",
"react-icons": "^4.11.0",
"react-konva": "^18.2.10",
+ "react-konva-utils": "^1.0.5",
"react-redux": "^8.1.3",
"superjson": "^1.13.1",
"tailwind-merge": "^1.14.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 89d91aa..3e42ae5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -44,12 +44,18 @@ dependencies:
'@radix-ui/react-slider':
specifier: ^1.1.2
version: 1.1.2(@types/react-dom@18.2.14)(@types/react@18.2.31)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-tabs':
+ specifier: ^1.0.4
+ version: 1.0.4(@types/react-dom@18.2.14)(@types/react@18.2.31)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-toggle':
specifier: ^1.0.3
version: 1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.31)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-toggle-group':
specifier: ^1.0.4
version: 1.0.4(@types/react-dom@18.2.14)(@types/react@18.2.31)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-toolbar':
+ specifier: ^1.0.4
+ version: 1.0.4(@types/react-dom@18.2.14)(@types/react@18.2.31)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/themes':
specifier: ^2.0.0
version: 2.0.0(@types/react-dom@18.2.14)(@types/react@18.2.31)(react-dom@18.2.0)(react@18.2.0)
@@ -119,6 +125,9 @@ dependencies:
react-konva:
specifier: ^18.2.10
version: 18.2.10(konva@9.2.2)(react-dom@18.2.0)(react@18.2.0)
+ react-konva-utils:
+ specifier: ^1.0.5
+ version: 1.0.5(konva@9.2.2)(react-dom@18.2.0)(react@18.2.0)
react-redux:
specifier: ^8.1.3
version: 8.1.3(@types/react-dom@18.2.14)(@types/react@18.2.31)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1)
@@ -1558,6 +1567,33 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /@radix-ui/react-toolbar@1.0.4(@types/react-dom@18.2.14)(@types/react@18.2.31)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-tBgmM/O7a07xbaEkYJWYTXkIdU/1pW4/KZORR43toC/4XWyBCURK0ei9kMUdp+gTPPKBgYLxXmRSH1EVcIDp8Q==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.2
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-context': 1.0.1(@types/react@18.2.31)(react@18.2.0)
+ '@radix-ui/react-direction': 1.0.1(@types/react@18.2.31)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.31)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.14)(@types/react@18.2.31)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-separator': 1.0.3(@types/react-dom@18.2.14)(@types/react@18.2.31)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-toggle-group': 1.0.4(@types/react-dom@18.2.14)(@types/react@18.2.31)(react-dom@18.2.0)(react@18.2.0)
+ '@types/react': 18.2.31
+ '@types/react-dom': 18.2.14
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@radix-ui/react-tooltip@1.0.7(@types/react-dom@18.2.14)(@types/react@18.2.31)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==}
peerDependencies:
@@ -4126,6 +4162,20 @@ packages:
resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
dev: false
+ /react-konva-utils@1.0.5(konva@9.2.2)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-MQco0bre5ohm2lS34wAr/QJgT5PCnKbS3V1/aeYDldc8mq5X1UwcjxZWSL7YxGw3jQSHOm6XyX0YgLXQYUWBuQ==}
+ peerDependencies:
+ konva: ^8.3.5 || ^9.0.0
+ react: ^18.2.0
+ react-dom: ^18.2.0
+ dependencies:
+ konva: 9.2.2
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ react-konva: 18.2.10(konva@9.2.2)(react-dom@18.2.0)(react@18.2.0)
+ use-image: 1.1.1(react-dom@18.2.0)(react@18.2.0)
+ dev: false
+
/react-konva@18.2.10(konva@9.2.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-ohcX1BJINL43m4ynjZ24MxFI1syjBdrXhqVxYVDw2rKgr3yuS0x/6m1Y2Z4sl4T/gKhfreBx8KHisd0XC6OT1g==}
peerDependencies:
diff --git a/public/logo.png b/public/logo.png
new file mode 100644
index 0000000..26c9df0
Binary files /dev/null and b/public/logo.png differ
diff --git a/src/components/EditableText.tsx b/src/components/EditableText.tsx
new file mode 100644
index 0000000..4a46828
--- /dev/null
+++ b/src/components/EditableText.tsx
@@ -0,0 +1,56 @@
+import React, { ComponentPropsWithoutRef, CSSProperties } from "react";
+import { Html } from "react-konva-utils";
+
+function getStyle(
+ width: CSSProperties["width"],
+ height: CSSProperties["height"],
+) {
+ const isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
+ const baseStyle: CSSProperties = {
+ width: `${width}px`,
+ height: `${height}px`,
+ border: "none",
+ padding: "0px",
+ margin: "0px",
+ background: "none",
+ outline: "none",
+ resize: "none",
+ color: "black",
+ fontSize: "24px",
+ fontFamily: "sans-serif",
+ };
+ if (isFirefox) {
+ return baseStyle;
+ }
+ return {
+ ...baseStyle,
+ marginTop: "-4px",
+ };
+}
+
+export function EditableTextInput({
+ x,
+ y,
+ width,
+ height,
+ value,
+ onChange,
+ onKeyDown,
+}: {
+ x: number;
+ y: number;
+ width: CSSProperties["width"];
+ height: CSSProperties["height"];
+} & ComponentPropsWithoutRef<"textarea">) {
+ const style = getStyle(width, height);
+ return (
+
+
+
+ );
+}
diff --git a/src/components/canvas.tsx b/src/components/canvas.tsx
index 22a6667..49ffa07 100644
--- a/src/components/canvas.tsx
+++ b/src/components/canvas.tsx
@@ -6,7 +6,11 @@ import { Layer, Stage } from "react-konva";
import TransformableText from "./transformable-text";
import { useAppDispatch, useAppSelector } from "@/hooks";
import { appSlice, deselectItem } from "@/store/app.slice";
-import { Toolbar } from "./toolbar";
+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();
@@ -26,9 +30,35 @@ const Canvas = () => {
dispatch(deselectItem());
};
+ 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(() => {
+ if (!selected && isEditing) {
+ setIsEditing(false);
+ } else if (!selected && isTransforming) {
+ setIsTransforming(false);
+ }
+ }, [selected, isEditing, isTransforming]);
+
+ function toggleEdit() {
+ setIsEditing(!isEditing);
+ setSelected(!isEditing);
+ }
+
+ function toggleTransforming() {
+ setIsTransforming(!isTransforming);
+ setSelected(!isTransforming);
+ }
+
return (
+
{
/>
);
})}
+
+ {/* {*/}
+ {/* setWidth(newWidth);*/}
+ {/* setHeight(newHeight);*/}
+ {/* }}*/}
+ {/* isEditing={isEditing}*/}
+ {/* isTransforming={isTransforming}*/}
+ {/* onToggleEdit={toggleEdit}*/}
+ {/* onToggleTransform={toggleTransforming}*/}
+ {/* onChange={setText}*/}
+ {/*/>*/}
diff --git a/src/components/editable-resizable-text.tsx b/src/components/editable-resizable-text.tsx
new file mode 100644
index 0000000..215726c
--- /dev/null
+++ b/src/components/editable-resizable-text.tsx
@@ -0,0 +1,58 @@
+import React from "react";
+
+import { EditableTextInput } from "./EditableText";
+import TransformableText from "@/components/transformable-text";
+import { ResizableText } from "@/components/text";
+
+const RETURN_KEY = 13;
+const ESCAPE_KEY = 27;
+
+export function EditableText({
+ x,
+ y,
+ isEditing,
+ isTransforming,
+ onToggleEdit,
+ onToggleTransform,
+ onChange,
+ onResize,
+ text,
+ width,
+ height,
+}: any) {
+ function handleEscapeKeys(e: any) {
+ if ((e.keyCode === RETURN_KEY && !e.shiftKey) || e.keyCode === ESCAPE_KEY) {
+ onToggleEdit(e);
+ }
+ }
+
+ function handleTextChange(e: any) {
+ onChange(e.currentTarget.value);
+ }
+
+ if (isEditing) {
+ return (
+
+ );
+ }
+ return (
+
+ );
+}
diff --git a/src/components/image-toolbar.tsx b/src/components/image-toolbar.tsx
new file mode 100644
index 0000000..0c10c48
--- /dev/null
+++ b/src/components/image-toolbar.tsx
@@ -0,0 +1,5 @@
+import React from "react";
+
+export function ImageToolbar() {
+ return Image toolbar
;
+}
diff --git a/src/components/layout/layout.tsx b/src/components/layout/layout.tsx
index a222066..25114a0 100644
--- a/src/components/layout/layout.tsx
+++ b/src/components/layout/layout.tsx
@@ -1,5 +1,5 @@
import type { ReactNode } from "react";
-import { Sidebar } from "./sidebar";
+import { Sidebar } from "./sidebar/sidebar";
import { Navbar } from "./navbar";
type Props = {
diff --git a/src/components/layout/navbar.tsx b/src/components/layout/navbar.tsx
index 4878ef1..2e020da 100644
--- a/src/components/layout/navbar.tsx
+++ b/src/components/layout/navbar.tsx
@@ -1,6 +1,6 @@
import { UserNav } from "./user-nav";
import Image from "next/image";
-import logo from "@/assets/logo.png"
+import logo from "@/assets/logo.png";
export const Navbar = () => {
return (
diff --git a/src/components/layout/sidebar.tsx b/src/components/layout/sidebar.tsx
deleted file mode 100644
index 758bc74..0000000
--- a/src/components/layout/sidebar.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-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";
-
-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);
- };
-
- const handleTextAdd = () => dispatch(addText({ initialValue: inputText }));
-
- return (
-
- );
-}
diff --git a/src/components/layout/sidebar/image-input.tsx b/src/components/layout/sidebar/image-input.tsx
new file mode 100644
index 0000000..8d07149
--- /dev/null
+++ b/src/components/layout/sidebar/image-input.tsx
@@ -0,0 +1,44 @@
+import React, { ChangeEvent } from "react";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} 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";
+
+function ImageInput() {
+ const dispatch = useAppDispatch();
+
+ const handleImageUploaded = (e: ChangeEvent) => {
+ dispatch(addImage(e));
+ };
+
+ return (
+
+
+
+
+
+ {/**/}
+
+
+
+ Ajouter votre image
+
+
+
+
+
+
+ );
+}
+
+export default ImageInput;
diff --git a/src/components/layout/sidebar/select-template.tsx b/src/components/layout/sidebar/select-template.tsx
new file mode 100644
index 0000000..641f526
--- /dev/null
+++ b/src/components/layout/sidebar/select-template.tsx
@@ -0,0 +1,34 @@
+import React, { ChangeEvent } 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 { useAppDispatch } from "@/hooks";
+import { Card, CardHeader, CardTitle } from "@/components/ui/card";
+
+export function SelectTemplate() {
+ const dispatch = useAppDispatch();
+
+ return (
+
+
+
+
+
+ {/**/}
+
+
+
+ Choisissez un cadre
+
+
+
+
+ );
+}
diff --git a/src/components/layout/sidebar/sidebar.tsx b/src/components/layout/sidebar/sidebar.tsx
new file mode 100644
index 0000000..9bf912f
--- /dev/null
+++ b/src/components/layout/sidebar/sidebar.tsx
@@ -0,0 +1,15 @@
+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";
+
+export function Sidebar() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/components/layout/sidebar/text-input.tsx b/src/components/layout/sidebar/text-input.tsx
new file mode 100644
index 0000000..831b501
--- /dev/null
+++ b/src/components/layout/sidebar/text-input.tsx
@@ -0,0 +1,60 @@
+import React, { type ChangeEvent, useState } from "react";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
+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";
+
+export function TextInput() {
+ const dispatch = useAppDispatch();
+ const [inputText, setInputText] = useState("");
+ const [open, setOpen] = useState(false);
+ const handleTextAdd = () => {
+ dispatch(addText({ initialValue: inputText }));
+ setOpen(false);
+ };
+ const handleInputChange = (e: ChangeEvent) => {
+ setInputText(e.target.value);
+ };
+
+ return (
+
+
+
+
+
+
+
+ Ajouter votre text
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/text-toolbar.tsx b/src/components/text-toolbar.tsx
new file mode 100644
index 0000000..ab6a829
--- /dev/null
+++ b/src/components/text-toolbar.tsx
@@ -0,0 +1,61 @@
+import { FontFamilyPicker } from "./texte-editing-tools/text-family-picker";
+import { FontSizeSelector } from "./texte-editing-tools/text-size-selector";
+import type { ChangeEvent } from "react";
+import { FontStyle } from "./texte-editing-tools/text-style-selector";
+import { FontAlign } from "./texte-editing-tools/text-align-selector";
+import { Separator } from "./ui/separator";
+import { SpacingSettings } from "./texte-editing-tools/text-spacing-settings";
+import { type TransformableTextProps } from "@/components/transformable-text";
+import { Button } from "@/components/ui/button";
+import { useAppDispatch } from "@/hooks";
+import { deleteShape } from "@/store/app.slice";
+
+type Props = {
+ selectedItemId: string;
+ currentText: TransformableTextProps["textProps"];
+ onTextColorChange: (e: ChangeEvent) => void;
+};
+export const TextToolbar = ({
+ currentText,
+ selectedItemId,
+ onTextColorChange,
+}: Props) => {
+ const dispatch = useAppDispatch();
+ if (!currentText) return null;
+ const deleteTextHandler = (selectedItemId) => {
+ dispatch(deleteShape(selectedItemId));
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/components/text.tsx b/src/components/text.tsx
new file mode 100644
index 0000000..b67919d
--- /dev/null
+++ b/src/components/text.tsx
@@ -0,0 +1,71 @@
+import React, { useRef, useEffect } from "react";
+import { Text, Transformer } from "react-konva";
+
+export function ResizableText({
+ x,
+ y,
+ text,
+ isSelected,
+ width,
+ onResize,
+ onClick,
+ onDoubleClick,
+}) {
+ const textRef = useRef(null);
+ const transformerRef = useRef(null);
+
+ useEffect(() => {
+ if (isSelected && transformerRef.current !== null) {
+ transformerRef.current.nodes([textRef.current]);
+ transformerRef.current.getLayer().batchDraw();
+ }
+ }, [isSelected]);
+
+ function handleResize() {
+ if (textRef.current !== null) {
+ const textNode = textRef.current;
+ const newWidth = textNode.width() * textNode.scaleX();
+ const newHeight = textNode.height() * textNode.scaleY();
+ textNode.setAttrs({
+ width: newWidth,
+ scaleX: 1,
+ });
+ onResize(newWidth, newHeight);
+ }
+ }
+
+ const transformer = isSelected ? (
+ {
+ newBox.width = Math.max(30, newBox.width);
+ return newBox;
+ }}
+ />
+ ) : null;
+
+ return (
+ <>
+
+ {transformer}
+ >
+ );
+}
diff --git a/src/components/texte-editing-tools/text-edit-toolbar.tsx b/src/components/texte-editing-tools/text-edit-toolbar.tsx
new file mode 100644
index 0000000..fd34789
--- /dev/null
+++ b/src/components/texte-editing-tools/text-edit-toolbar.tsx
@@ -0,0 +1,5 @@
+import React from "react";
+
+export function TextEditToolbar() {
+ return <>Text Edit toolbar>;
+}
diff --git a/src/components/texte-editing-tools/text-style-selector.tsx b/src/components/texte-editing-tools/text-style-selector.tsx
index 999b429..4af6e12 100644
--- a/src/components/texte-editing-tools/text-style-selector.tsx
+++ b/src/components/texte-editing-tools/text-style-selector.tsx
@@ -31,7 +31,6 @@ export function FontStyle({ currentText, selectedItemId }: Props) {
const handleTextDecorationToggle =
(button: "underline" | "line-through") => () => {
if (!selectedItemId) return;
-
dispatch(
updateText({
id: selectedItemId,
@@ -47,8 +46,7 @@ export function FontStyle({ currentText, selectedItemId }: Props) {
onClick={handleFontStyleToggle("bold")}
value={currentText.fontStyle}
>
-
- Bold
+
-
- Italic
+
-
-
-
- Souligné
+
-
- Barré
+
>
diff --git a/src/components/toolbar.tsx b/src/components/toolbar.tsx
index a13fc98..436fbeb 100644
--- a/src/components/toolbar.tsx
+++ b/src/components/toolbar.tsx
@@ -1,31 +1,19 @@
+import React, { type ChangeEvent } from "react";
+import { ImageToolbar } from "@/components/image-toolbar";
+import { TextToolbar } from "@/components/text-toolbar";
import { useAppDispatch, useAppSelector } from "@/hooks";
import { updateText } from "@/store/app.slice";
-import { FontFamilyPicker } from "./texte-editing-tools/text-family-picker";
-import { FontSizeSelector } from "./texte-editing-tools/text-size-selector";
-import type { ChangeEvent } from "react";
-import { FontStyle } from "./texte-editing-tools/text-style-selector";
-import { FontAlign } from "./texte-editing-tools/text-align-selector";
-import { Separator } from "./ui/separator";
-import { SpacingSettings } from "./texte-editing-tools/text-spacing-settings";
-import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
-import {
- HoverCard,
- HoverCardContent,
- HoverCardTrigger,
-} from "@/components/ui/hover-card";
-import {
- AiOutlineAlignCenter,
- AiOutlineAlignLeft,
- AiOutlineAlignRight,
-} from "react-icons/ai";
export const Toolbar = () => {
const dispatch = useAppDispatch();
const selectedItemId = useAppSelector((state) => state.app.selectedItemId);
const texts = useAppSelector((state) => state.app.texts);
+ const images = useAppSelector((state) => state.app.images);
const currentText = texts.find((t) => t.id === selectedItemId);
- if (!currentText) return null;
+ const currentImage = images.find((img) => img.id === selectedItemId);
+
+ if (!selectedItemId) return null;
const handleTextColorChange = (e: ChangeEvent) => {
if (!selectedItemId) return;
@@ -38,32 +26,15 @@ export const Toolbar = () => {
};
return (
-
-
-
-
-
-
-
-
-
-
-
-
+
+ {currentImage && }
+ {currentText && (
+
+ )}
);
};
diff --git a/src/components/transformable-text.tsx b/src/components/transformable-text.tsx
index 07d84d7..43aa972 100644
--- a/src/components/transformable-text.tsx
+++ b/src/components/transformable-text.tsx
@@ -1,6 +1,7 @@
import type { TextConfig } from "konva/lib/shapes/Text";
import { useRef, type ElementRef, useEffect } from "react";
import { Text, Transformer } from "react-konva";
+import * as console from "console";
type TransformableTextConfig = Omit
& {
text?: TextConfig["text"];
diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx
new file mode 100644
index 0000000..afa13ec
--- /dev/null
+++ b/src/components/ui/card.tsx
@@ -0,0 +1,79 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+const Card = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+Card.displayName = "Card"
+
+const CardHeader = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardHeader.displayName = "CardHeader"
+
+const CardTitle = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardTitle.displayName = "CardTitle"
+
+const CardDescription = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardDescription.displayName = "CardDescription"
+
+const CardContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardContent.displayName = "CardContent"
+
+const CardFooter = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardFooter.displayName = "CardFooter"
+
+export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx
new file mode 100644
index 0000000..9f9a6dc
--- /dev/null
+++ b/src/components/ui/textarea.tsx
@@ -0,0 +1,24 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+export interface TextareaProps
+ extends React.TextareaHTMLAttributes {}
+
+const Textarea = React.forwardRef(
+ ({ className, ...props }, ref) => {
+ return (
+
+ )
+ }
+)
+Textarea.displayName = "Textarea"
+
+export { Textarea }
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 741a605..9e1e004 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -8,6 +8,7 @@ export default function Home() {
<>
Labbel Application
+
diff --git a/src/store/app.slice.ts b/src/store/app.slice.ts
index a75dcb4..0488bcf 100644
--- a/src/store/app.slice.ts
+++ b/src/store/app.slice.ts
@@ -36,7 +36,7 @@ export const appSlice = createSlice({
addText: (state, action: PayloadAction<{ initialValue: string }>) => {
const textId = v1();
-
+ console.log(state);
state.texts.push({
text: action.payload.initialValue,
id: textId,
@@ -68,8 +68,22 @@ export const appSlice = createSlice({
...action.payload,
};
},
+
+ deleteShape: (state, action: PayloadAction) => {
+ console.log(state.texts);
+ return {
+ ...state,
+ texts: state.texts.filter((shape) => shape.id !== action.payload),
+ };
+ },
},
});
-export const { addImage, addText, selectItem, deselectItem, updateText } =
- appSlice.actions;
+export const {
+ addImage,
+ addText,
+ selectItem,
+ deselectItem,
+ updateText,
+ deleteShape,
+} = appSlice.actions;
diff --git a/todo.md b/todo.md
index 86afbb5..fe46217 100644
--- a/todo.md
+++ b/todo.md
@@ -1,7 +1,3 @@
-Property 'PopoverTriggerProps' is missing in type '{ handleTextFontFamilyChange: (e: string | undefined) => void; }' but required in type 'Props'.ts(2741)
-font-family-picker.tsx(31, 3): 'PopoverTriggerProps' is declared here.
-(alias) function FontFamilyPicker({ handleTextFontFamilyChange }: Props): React.JSX.Element
-import FontFamilyPicker
-
-
-
+Change Text
+Delete Text
+TextToolbar - text ? text : image
\ No newline at end of file