diff --git a/frontend/src/assets/text/general.ts b/frontend/src/assets/text/general.ts index c56da070..3d2d8fb6 100644 --- a/frontend/src/assets/text/general.ts +++ b/frontend/src/assets/text/general.ts @@ -19,6 +19,7 @@ export const general = { inProgress: "in progress", // Normal text rollingDice: "rolling dice", + plusOne: "+1", chat: "chat", history: "history", defaultTime: "0:00", diff --git a/frontend/src/atoms/animations.ts b/frontend/src/atoms/animations.ts index ba7eed6a..6ee10052 100644 --- a/frontend/src/atoms/animations.ts +++ b/frontend/src/atoms/animations.ts @@ -1,5 +1,5 @@ import { keyframes } from "@emotion/react"; -import { color, opacity, spacing, zIndex } from "../design"; +import { color, opacity, powerUp, spacing, zIndex } from "../design"; // TODO: delete // Hands @@ -48,6 +48,17 @@ export const fadeIn = keyframes` 100% { opacity: ${opacity.visible}; } `; +export const grow = keyframes` + from { + opacity: ${opacity.hidden}; + transform: scale(0); + } + to { + opacity: ${opacity.visible}; + transform: scale(1); + } +`; + export const fadeInPop = keyframes` 0% { opacity: ${opacity.hidden}; @@ -77,6 +88,48 @@ export const slideUp = keyframes` } `; +export const fadeTransformUp = keyframes` + 0% { + -webkit-transform: translateY(0); + transform: translateY(0); + opacity: ${opacity.background}; + } + 10%{ + opacity: ${opacity.visible}; + } + 100% { + -webkit-transform: translateY(-${powerUp.xl}); + transform: translateY(-${powerUp.xl}); + opacity: ${opacity.hidden}; + } +`; + +export const flash = keyframes` + 0% { + background: ${color.lightGrey}; + } + 50%{ + background: ${color.cloudWhite}; + } + 100% { + background: ${color.lightGrey}; + } + +`; + +export const scaleTransformation = keyframes` + 0% { + opacity: ${opacity.hidden}; + } + 50% { + transform: scale(0); + } + 100% { + opacity: 1; + transform: scale(1); + } +`; + export const slideRight = keyframes` 0% { diff --git a/frontend/src/atoms/layout.ts b/frontend/src/atoms/layout.ts index 8ef77f09..d35ffb62 100644 --- a/frontend/src/atoms/layout.ts +++ b/frontend/src/atoms/layout.ts @@ -1,7 +1,9 @@ +import { css } from "@emotion/react"; import styled from "@emotion/styled"; import { color, layoutHeight, layoutWidth } from "../design"; import { getSidebarHeight, switchStyle } from "../util"; +import { flash } from "./animations"; interface Props { active?: boolean; @@ -53,8 +55,20 @@ export const PlayerBox = styled(LayoutBase)` border-right: 1px solid ${color.mediumGrey}; `; -export const HUDPlayerBox = styled(HUDBlock)` +interface HUDProps { + showPowerUpAnimation?: boolean; +} + +export const HUDPlayerBox = styled(HUDBlock)` width: ${layoutWidth.sm}; + ${({ showPowerUpAnimation }) => + showPowerUpAnimation && + css` + animation: ${flash}; + animation-duration: 0.6s; + animation-delay: 0s; + animation-fill-mode: forwards; + `}; `; export const PlayerMenuBox = styled(LayoutBase)` diff --git a/frontend/src/design/index.ts b/frontend/src/design/index.ts index de2be04f..420d2ce3 100644 --- a/frontend/src/design/index.ts +++ b/frontend/src/design/index.ts @@ -16,3 +16,4 @@ export * from "./mobile-layout"; export * from "./die"; export * from "./patterns"; export * from "./avatar"; +export * from "./power-up"; diff --git a/frontend/src/design/power-up.ts b/frontend/src/design/power-up.ts new file mode 100644 index 00000000..0e27eb08 --- /dev/null +++ b/frontend/src/design/power-up.ts @@ -0,0 +1,10 @@ +export const POWER_UPS_IN_VIEW = 5; + +export const powerUp = { + xxs: "clamp(10px, 1.04vw + 0px, 40px)", + xs: "0.5vh", + sm: "1.57vw", + md: "2vw", + lg: "3.63vh", + xl: "4.5vh", +}; diff --git a/frontend/src/hooks/index.ts b/frontend/src/hooks/index.ts index f8dc89e5..4f21e244 100644 --- a/frontend/src/hooks/index.ts +++ b/frontend/src/hooks/index.ts @@ -4,3 +4,5 @@ export * from "./use-observer"; export * from "./use-sound"; export * from "./use-analytics"; export * from "./use-mobile"; +export * from "./use-mount"; +export * from "./use-update-value"; diff --git a/frontend/src/hooks/use-mount.ts b/frontend/src/hooks/use-mount.ts new file mode 100644 index 00000000..d59e07fa --- /dev/null +++ b/frontend/src/hooks/use-mount.ts @@ -0,0 +1,16 @@ +import { useEffect, useRef } from "react"; + +/** + * This hook is used to check if the component/page has already been mounted. + * It is used when you only want an effect to happen after the component is already mounted. + */ + +export const useIsMounted = () => { + const isMountRef = useRef(true); + + useEffect(() => { + isMountRef.current = false; + }, []); + + return isMountRef.current; +}; diff --git a/frontend/src/hooks/use-update-value.ts b/frontend/src/hooks/use-update-value.ts new file mode 100644 index 00000000..5c81d619 --- /dev/null +++ b/frontend/src/hooks/use-update-value.ts @@ -0,0 +1,18 @@ +import { useEffect, useState } from "react"; +import { NOTIFICATION_VISIBILITY_TIME } from "../constants"; + +/** + * This hook is used to in conjunction with a timer to return a true value after the timer amount has passed. + * Its initial value is false and will return true once the timer has run out. + */ + +export const useUpdateValue = () => { + const [show, setShow] = useState(false); + useEffect(() => { + const timeout = setTimeout(() => { + setShow(true); + }, NOTIFICATION_VISIBILITY_TIME); + + return () => clearTimeout(timeout); + }, [show]); +}; diff --git a/frontend/src/molecules/hud-player/hud-player.tsx b/frontend/src/molecules/hud-player/hud-player.tsx index cc5691a4..2ecf68b8 100644 --- a/frontend/src/molecules/hud-player/hud-player.tsx +++ b/frontend/src/molecules/hud-player/hud-player.tsx @@ -10,20 +10,22 @@ import { HudPlayerWrapper } from "./styles"; interface Props { player: PlayerPublic; isHovered?: boolean; + showPowerUpAnimation?: boolean; } /** * This component is for the player avatar. * @param {PlayerPublic} player - The hud player. * @param {boolean} isHovered - A boolean to define if the hud is being hovered. + * @param {boolean} showPowerUpAnimation - A boolean used to begin the power up animation. The animation occurs when a new power up is added to your hand. */ -export const HudPlayer: FC = ({ player, isHovered = false }) => { +export const HudPlayer: FC = ({ player, isHovered = false, showPowerUpAnimation = false }) => { const avatarName = avatars[player.avatarId].name; const avatar = handProportion(avatarName); return ( - + void; + showPowerUpAnimation?: boolean; } /** This component shows a list of power ups in the hud. It uses the small power up cards @@ -20,31 +20,41 @@ interface Props { * @param {boolean} isPowerUpDisabled - Is true when the powerUp can't be used. A lock icon appears on the power-ups * @param {string} width - is the width of the power up list * @param {Function} onClick - Opens up a modal when the power up list is clicked + * @param {boolean} showPowerUpAnimation - A boolean used to begin the power up animation. The animation occurs when a new power up is added to your hand. */ -export const PowerUpRow: FC = ({ powerUpIds, width, isPowerUpDisabled, onClick }) => { +export const PowerUpRow: FC = ({ powerUpIds, isPowerUpDisabled, showPowerUpAnimation = false, onClick }) => { if (!powerUpIds || !powerUpIds.length) return <>; - const powerUpsShown = getPowerUpsShown(width); return ( - - } iconColor={color.transparent} strokeColor={color.black} />} - heading={text.param.xAmount(powerUpIds.length)} - headingFontSize={fontSizes.body} - headingLineHeight={lineHeights.body} - headingFontWeight={fontWeights.light} - iconPosition="row-reverse" - transformText="lowercase" - /> - - {powerUpIds.slice(0, powerUpsShown).map((powerUpId, i) => { + + + } iconColor={color.transparent} strokeColor={color.black} />} + heading={text.param.xAmount(powerUpIds.length)} + headingFontSize={fontSizes.body} + headingLineHeight={lineHeights.body} + headingFontWeight={fontWeights.light} + iconPosition="row-reverse" + transformText="lowercase" + /> + + {text.general.plusOne} + + + + {powerUpIds.map((powerUpId, i) => { const powerUp = getPowerUp(powerUpId); return ( - + ); })} - ); diff --git a/frontend/src/molecules/power-up-row/styles.ts b/frontend/src/molecules/power-up-row/styles.ts index 427dcdac..fba7e6e2 100644 --- a/frontend/src/molecules/power-up-row/styles.ts +++ b/frontend/src/molecules/power-up-row/styles.ts @@ -1,39 +1,104 @@ +import { css } from "@emotion/react"; import styled from "@emotion/styled"; -import { BaseRow, fadeInPop, slideUp } from "../../atoms"; -import { PowerUpWrapper } from "../../components/power-up/styles"; -import { opacity } from "../../design"; +import { BaseGrid, BaseIconWrapper, BodyText, Card, grow, fadeTransformUp, slideUp } from "../../atoms"; -export const PowerUpRowWrapper = styled(BaseRow)` +import { breakpoints, containerHeight, containerWidth, iconSize, maxHeight, opacity, powerUp, POWER_UPS_IN_VIEW } from "../../design"; +import { DisabledSmallPowerUpWrapper } from "../power-up/styles"; + +interface PowerUpCardProps { + amount: number; +} + +interface PowerUpRowProps { + showPowerUpAnimation: boolean; +} + +export const PowerUpRowWrapper = styled(BaseGrid)` + flex-wrap: wrap; + grid-template-columns: 1fr 1fr 1fr 1fr 1fr; + ${DisabledSmallPowerUpWrapper} { + ${BaseIconWrapper} { + ${({ amount }) => + amount > POWER_UPS_IN_VIEW && + ` + width: ${powerUp.xxs}; + height: ${powerUp.xxs}; + > svg { + width: ${powerUp.xxs}; + height: ${powerUp.xxs}; + } + `} + } + } :hover { animation: ${slideUp}; - animation-duration: 0.7s; + animation-duration: 1s; animation-delay: 0s; animation-fill-mode: forwards; } - ${PowerUpWrapper} { - -webkit-animation-name: ${fadeInPop}; - -webkit-animation-timing-function: cubic-bezier(0.4, -0.38, 0.6, 1.91); - -webkit-animation-duration: 0.3s; - -webkit-animation-fill-mode: forwards; - } - ${PowerUpWrapper}:nth-of-type(1) { - animation-delay: 0.7s; + ${Card} { + animation-name: ${grow}; + animation-timing-function: ease-in; + animation-duration: 0.9s; + animation-fill-mode: forwards; opacity: ${opacity.hidden}; + width: ${({ amount }): string => (amount > POWER_UPS_IN_VIEW ? powerUp.md : containerWidth.xs)}; + height: ${({ amount }): string => (amount > POWER_UPS_IN_VIEW ? powerUp.lg : containerHeight.sm)}; + min-height: ${({ amount }): string => (amount > POWER_UPS_IN_VIEW ? powerUp.lg : maxHeight.sm)}; + + @media screen and (min-device-width: ${breakpoints.xl}) and (max-device-width: ${breakpoints.xxl}) { + width: ${({ amount }): string => (amount > POWER_UPS_IN_VIEW ? powerUp.sm : containerWidth.xs)}; + min-height: ${({ amount }): string => (amount > POWER_UPS_IN_VIEW ? powerUp.lg : maxHeight.sm)}; + } + + @media (min-width: ${breakpoints.xxl}) { + min-height: ${({ amount }): string => (amount > POWER_UPS_IN_VIEW ? powerUp.lg : maxHeight.lg)}; + } } - ${PowerUpWrapper}:nth-of-type(2) { + ${Card}:nth-of-type(1) { animation-delay: 0.9s; - opacity: ${opacity.hidden}; } - ${PowerUpWrapper}:nth-of-type(3) { - animation-delay: 1.1s; - opacity: ${opacity.hidden}; + ${Card}:nth-of-type(2) { + animation-delay: 1s; } - ${PowerUpWrapper}:nth-of-type(4) { + ${Card}:nth-of-type(3) { + animation-delay: 1.2s; + } + ${Card}:nth-of-type(4) { animation-delay: 1.3s; - opacity: ${opacity.hidden}; } - ${PowerUpWrapper}:nth-of-type(5) { + ${Card}:nth-of-type(5) { + animation-delay: 1.4s; + } + ${Card}:nth-of-type(6) { animation-delay: 1.5s; - opacity: ${opacity.hidden}; + } + ${Card}:nth-of-type(7) { + animation-delay: 1.6s; + } + ${Card}:nth-of-type(8) { + animation-delay: 1.7s; + } + ${Card}:nth-of-type(9) { + animation-delay: 1.8s; } `; + +export const AddPowerUp = styled(BodyText)` + padding-left: ${iconSize.xxs}; + position: absolute; + top: -${powerUp.xs}; + z-index: 2; + opacity: ${opacity.hidden}; + ${({ showPowerUpAnimation }) => + showPowerUpAnimation && + css` + animation-name: ${fadeTransformUp}; + animation-timing-function: ease-in; + animation-duration: 1s; + animation-fill-mode: forwards; + animation-delay: 0.8s; + `}; +`; + +export const PowerUpRowContainer = styled.div``; diff --git a/frontend/src/molecules/power-up/power-up-small.tsx b/frontend/src/molecules/power-up/power-up-small.tsx index 48971e97..64381079 100644 --- a/frontend/src/molecules/power-up/power-up-small.tsx +++ b/frontend/src/molecules/power-up/power-up-small.tsx @@ -11,6 +11,7 @@ interface PowerUpSmallProps { padding?: string; isPowerUpDisabled?: boolean; plusAmount?: number; + pointer?: boolean; } /** @@ -20,6 +21,7 @@ interface PowerUpSmallProps { * @param {number} plusAmount - The amount of powerUps that are not visible, isEmpty prop has to be true * @param {string} padding - If the powerUp is used to display the hidden amount of powerUps. This parameter represents that value. * @param {string} isPowerUpDisabled - If the powerUp is used to display the hidden amount of powerUps. This parameter represents that value. + * @param {boolean} pointer - A boolean to indicate when true if the cursor is a pointer or if it is the default cursor. */ export const PowerUpSmall: FC = ({ @@ -29,6 +31,7 @@ export const PowerUpSmall: FC = ({ plusAmount = 0, padding, isPowerUpDisabled, + pointer, }) => { return ( @@ -37,7 +40,7 @@ export const PowerUpSmall: FC = ({ {isPowerUpDisabled && ( - } radius={radius.xxs} width={iconSize.sm} height={iconSize.sm} /> + } radius={radius.xxs} width={iconSize.sm} height={iconSize.sm} pointer={pointer} /> )} diff --git a/frontend/src/pages/test/styles.ts b/frontend/src/pages/test/styles.ts index 318afd13..59de5b29 100644 --- a/frontend/src/pages/test/styles.ts +++ b/frontend/src/pages/test/styles.ts @@ -1,7 +1,8 @@ // TODO: delete file +import { css } from "@emotion/react"; import styled from "@emotion/styled"; -import { Card, BaseColumn, BaseRow, HUDBlock, PlayerInformationBlock, FluidImage } from "../../atoms"; +import { Card, BaseColumn, BaseRow, HUDBlock, PlayerInformationBlock, FluidImage, flash } from "../../atoms"; import { spacing, color } from "../../design"; export const PowerUpCard = styled(Card)` @@ -58,6 +59,8 @@ export const MainWrapper = styled.div` top: 0; width: 62.5vw; height: 89vh; + background: ${color.lightGrey}; + border-bottom: 1px solid ${color.mediumGrey}; `; export const HubInfoBlock = styled(PlayerInformationBlock)` @@ -71,13 +74,25 @@ export const MatchHeadingColumn = styled(BaseColumn)` margin-top: ${spacing.xxl}; `; -export const HudContainer = styled(HubInfoBlock)` +interface HudProps { + showPowerUpAnimation?: boolean; +} + +export const HudContainer = styled(HubInfoBlock)` display: flex; width: 62.5vw; align-items: center; justify-content: center; padding-left: ${spacing.md}; padding-right: ${spacing.md}; + ${({ showPowerUpAnimation }) => + showPowerUpAnimation && + css` + animation: ${flash}; + animation-duration: 0.6s; + animation-delay: 0s; + animation-fill-mode: forwards; + `}; `; export const HudChild = styled(BaseRow)` diff --git a/frontend/src/pages/test/test.tsx b/frontend/src/pages/test/test.tsx index ee583b0f..9fe1f3e3 100644 --- a/frontend/src/pages/test/test.tsx +++ b/frontend/src/pages/test/test.tsx @@ -1,8 +1,9 @@ // TODO: delete this file when done testing -import { FC, useCallback, useState } from "react"; +import { FC, useCallback, useState, useEffect } from "react"; import { CallBoloney, + CallBoloneyWinner, CallExact, CookieIconSVG, DiscordLogo, @@ -87,21 +88,37 @@ import { TooltipFrame, CookieBanner, Contact, + HudPlayer, } from "../../molecules"; -import { getPowerUp, parseMessages, range } from "../../util"; +import { getPowerUp, getPowerUpData, parseMessages, range } from "../../util"; import { Die } from "../../molecules/die"; import { TemporaryDice } from "../../molecules/die/temporary-die"; import { DiceRow } from "../../molecules/die/dice-row"; import { Bid, Die as DieType, PowerUpId } from "../../types"; import { LastBidPlayer } from "../../molecules/die/last-bid"; -import { AlignColumn, BackgroundRow, PlayerMenuOne, HalfColumn } from "./styles"; +import { + AlignColumn, + BackgroundRow, + BottomHud, + HalfColumn, + Layout, + MatchHeadingColumn, + HudContainer, + HudChild, + MainWrapper, + PlayerMenuOne, +} from "./styles"; import { PowerUpCard, PowerUpSmall } from "../../molecules/power-up"; import { ActiveDropdown } from "../../molecules/top-navigation"; -import { GeneralNavigationBar, MatchNavigationBar, MatchOptionsBar, PlayerMenu } from "../../organisms"; - -import { fakeHistory } from "../../assets/fake-data/fake-history"; -import { useViewport } from "../../hooks"; +import { GeneralNavigationBar, MainContainer, MatchNavigationBar, MatchOptionsBar, PlayerMenu } from "../../organisms"; +import { MatchSideBar } from "../../molecules/match-sidebar"; +import { PowerUpPile } from "../../molecules/power-up-pile"; +import { fakePowerUps } from "../../assets/fake-data/fake-power-ups"; +import { MatchImage } from "../../molecules/match-image"; +import { PowerUpRow } from "../../molecules/power-up-row"; +import { useIsMounted, useViewport } from "../../hooks"; import { useStore } from "../../store"; +import { fakeHistory } from "../../assets/fake-data/fake-history"; const onClick = () => { console.log("Button works"); @@ -185,6 +202,14 @@ export const Test: FC = () => { const isOverlayVisible = useStore((state) => state.isOverlayVisible); const showModal = useStore((state) => state.showModal); const setModalComponentChildren = useStore((state) => state.setModalComponentChildren); + const isMount = useIsMounted(); + const [addPowerUp, setAddPowerUp] = useState([fakePowerUps[0], fakePowerUps[1]]); + const [updatedPowerUp, setUpdatedPowerUp] = useState(false); + + const addPowerUpFunct = () => { + setAddPowerUp([...addPowerUp, fakePowerUps[2]]); + }; + const channelId = "123"; const availablePowerUps: Set = new Set(); @@ -233,6 +258,17 @@ export const Test: FC = () => { }, 10000); }, []); + useEffect(() => { + if (!isMount) { + setUpdatedPowerUp(true); + setTimeout(() => { + setUpdatedPowerUp(false); + }, 1500); + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [addPowerUp]); + if (!powerUp1 || !powerUp2 || !powerUp3 || !powerUp4 || !powerUp5 || !powerUp6 || !powerUp7 || !powerUp8 || !powerUp9) return <>; const handleDropdownClick = (dropdown: ActiveDropdown) => { if (activeDropdown === dropdown) { @@ -270,7 +306,16 @@ export const Test: FC = () => { if (loadSpinner) return ; return ( - <> +
+
+
+ console.log("")} showPowerUpAnimation={updatedPowerUp} /> +
+
+
+ console.log("")} /> +
+
{/* */} @@ -291,33 +336,28 @@ export const Test: FC = () => {


-
-
-
setLoadSpinner(true)} />
- {/* + - - - - - + + + {/* */} { setModalComponentChildren("power-up-list"); showModal("power-up-list"); }} - width={width} + showPowerUpAnimation={updatedPowerUp} /> - + {/* setIsChatOpen(!isChatOpen)} @@ -331,7 +371,7 @@ export const Test: FC = () => { messageInput={messageInput} handleSendEvent={handleSendEvent} /> - + */} @@ -347,8 +387,10 @@ export const Test: FC = () => { - */} + +
+ addPowerUpFunct()} />


@@ -1302,6 +1344,6 @@ export const Test: FC = () => {
intro text
- +
); };