diff --git a/client/src/assets/icons/messageIcon.svg b/client/src/assets/icons/messageIcon.svg new file mode 100644 index 00000000..bce658ce --- /dev/null +++ b/client/src/assets/icons/messageIcon.svg @@ -0,0 +1,2 @@ + + diff --git a/client/src/pixi/World.tsx b/client/src/pixi/World.tsx index 296d5a74..ce721fa9 100644 --- a/client/src/pixi/World.tsx +++ b/client/src/pixi/World.tsx @@ -3,6 +3,7 @@ import Rectangle from "./components/Rectangle"; import "@pixi/events"; import { DisplayPosition, Position } from "./Position"; import React from "react"; +import Message from "./components/Message"; interface Props { userPosition: Position; @@ -55,6 +56,14 @@ const World: React.FC = ({ userPosition, userDisplayPosition }) => { width={100} height={100} /> + ); }; diff --git a/client/src/pixi/components/Message.tsx b/client/src/pixi/components/Message.tsx new file mode 100644 index 00000000..a628f3ad --- /dev/null +++ b/client/src/pixi/components/Message.tsx @@ -0,0 +1,113 @@ +import { Container, Graphics, Sprite, Text } from "@pixi/react"; +import messageIcon from "/src/assets/icons/messageIcon.svg"; +import React, { useRef, useState, useEffect, useCallback } from "react"; +import { Graphics as PIXIGraphics, TextStyle } from "pixi.js"; +import PIXI from "pixi.js"; +import { DisplayPosition } from "../Position"; +import { themeColors } from "../theme"; + +const messageIconSize = 30; + +interface MessageBubbleProps { + width: number; + height: number; +} + +const MessageBubble: React.FC = (props) => { + const draw = useCallback( + (g: PIXIGraphics) => { + g.clear(); + g.lineStyle(2, 0x000000); + g.beginFill(themeColors.backgroundPrimary); + g.drawRoundedRect(0, 0, props.width, props.height, 10); + g.endFill(); + }, + [props], + ); + return ; +}; + +interface Props { + messageText: string; + displayPosition: DisplayPosition; + user: { + name: string; + iconUrl: string; + }; +} + +const userNameTextStyle = new TextStyle({ fontSize: 14, fill: "black" }); +const messageTextStyle = new TextStyle({ + fontSize: 16, + fill: "black", + wordWrap: true, + wordWrapWidth: 180, + breakWords: true, +}); + +const Message: React.FC = ({ messageText, displayPosition, user }) => { + const [showMessage, setShowMessage] = useState(false); + const textRef = useRef(null); + const [bubbleSize, setBubbleSize] = useState({ width: 200, height: 100 }); + + const handleMouseOver = useCallback(() => setShowMessage(true), []); + const handleMouseOut = useCallback(() => setShowMessage(false), []); + + useEffect(() => { + if (!showMessage) return; + if (textRef.current) { + const { width, height } = textRef.current; + setBubbleSize({ + width: width + 20, + height: height + 20, + }); + } + }, [showMessage]); + + const iconImageSrc = showMessage ? user.iconUrl : messageIcon; + + return ( + + + {showMessage && ( + <> + + + + + + + )} + + ); +}; + +export default Message; diff --git a/client/src/pixi/theme.ts b/client/src/pixi/theme.ts new file mode 100644 index 00000000..7652acc1 --- /dev/null +++ b/client/src/pixi/theme.ts @@ -0,0 +1,31 @@ +// @ts-expect-error tailwind config +import tailwindConfig from "../../tailwind.config"; + +const { colors } = tailwindConfig.theme.extend; + +type ThemeColors = Record< + | "backgroundPrimary" + | "backgroundSecondary" + | "backgroundTertiary" + | "textPrimary" + | "textSecondary" + | "textTertiary" + | "accentPrimary" + | "accentSecondary" + | "accentTertiary" + | "accentHover", + string +>; + +export const themeColors: ThemeColors = { + backgroundPrimary: colors["background-primary"], + backgroundSecondary: colors["background-secondary"], + backgroundTertiary: colors["background-tertiary"], + textPrimary: colors["text-primary"], + textSecondary: colors["text-secondary"], + textTertiary: colors["text-tertiary"], + accentPrimary: colors["accent-primary"], + accentSecondary: colors["accent-secondary"], + accentTertiary: colors["accent-tertiary"], + accentHover: colors["accent-hover"], +};