diff --git a/src/app/_components/_sharedcomponents/Cards/CardTypes.ts b/src/app/_components/_sharedcomponents/Cards/CardTypes.ts index 6bd57d83..f790dc7b 100644 --- a/src/app/_components/_sharedcomponents/Cards/CardTypes.ts +++ b/src/app/_components/_sharedcomponents/Cards/CardTypes.ts @@ -33,6 +33,7 @@ export interface ICardData { zone?: string; epicActionSpent?: boolean; onStartingSide?: boolean; + controlled: boolean; } export interface IServerCardData { @@ -108,4 +109,4 @@ interface ICardPlayer { name: string; label: string; uuid: string; -} \ No newline at end of file +} diff --git a/src/app/_components/_sharedcomponents/Cards/GameCard.tsx b/src/app/_components/_sharedcomponents/Cards/GameCard.tsx index 510244de..35a30555 100644 --- a/src/app/_components/_sharedcomponents/Cards/GameCard.tsx +++ b/src/app/_components/_sharedcomponents/Cards/GameCard.tsx @@ -2,6 +2,8 @@ import React from 'react'; import { Typography, Box, + Popover, + PopoverOrigin, } from '@mui/material'; import Grid from '@mui/material/Grid2'; import { IGameCardProps, ICardData, CardStyle } from './CardTypes'; @@ -20,6 +22,51 @@ const GameCard: React.FC = ({ }) => { const { sendGameMessage, connectedPlayer, getConnectedPlayerPrompt, distributionPromptData } = useGame(); + const cardInPlayersHand = card.controller?.id === connectedPlayer && card.zone === 'hand'; + const cardInOpponentsHand = card.controller?.id !== connectedPlayer && card.zone === 'hand'; + + const [anchorElement, setAnchorElement] = React.useState(null); + const hoverTimeout = React.useRef(undefined); + const open = Boolean(anchorElement); + + const handlePreviewOpen = (event: React.MouseEvent) => { + const target = event.currentTarget; + if (cardInOpponentsHand) { + return; + } + hoverTimeout.current = window.setTimeout(() => { + setAnchorElement(target); + }, 500); + }; + + const handlePreviewClose = () => { + clearTimeout(hoverTimeout.current); + setAnchorElement(null); + }; + + const popoverConfig = (): { anchorOrigin: PopoverOrigin, transformOrigin: PopoverOrigin } => { + if (cardInPlayersHand) { + return { + anchorOrigin:{ + vertical: -5, + horizontal: 'center', + }, + transformOrigin: { + vertical: 'bottom', + horizontal: 'center', + } }; + } + + return { + anchorOrigin:{ + vertical: 'center', + horizontal: -5, + }, + transformOrigin: { + vertical: 'center', + horizontal: 'right', + } }; + } const showValueAdjuster = getConnectedPlayerPrompt()?.promptType === 'distributeAmongTargets' && card.selectable; if (showValueAdjuster) { @@ -251,12 +298,27 @@ const GameCard: React.FC = ({ backgroundColor:'black', mb:'0px', position:'relative' - } + }, + cardPreview: { + borderRadius: '.38em', + backgroundImage: `url(${s3CardImageURL(card)})`, + backgroundSize: 'cover', + backgroundRepeat: 'no-repeat', + aspectRatio: '1 / 1.4', + width: '16rem', + }, } return ( - + { !!distributionAmount && ( @@ -302,6 +364,19 @@ const GameCard: React.FC = ({ )} + + + + {otherUpgradeCards.map((subcard) => ( = ({ ); }; -export default GameCard; \ No newline at end of file +export default GameCard; diff --git a/src/app/_components/_sharedcomponents/Cards/LeaderBaseCard.tsx b/src/app/_components/_sharedcomponents/Cards/LeaderBaseCard.tsx index dbad6241..b302d0c0 100644 --- a/src/app/_components/_sharedcomponents/Cards/LeaderBaseCard.tsx +++ b/src/app/_components/_sharedcomponents/Cards/LeaderBaseCard.tsx @@ -1,7 +1,8 @@ import React from 'react'; import { Typography, - Box + Box, + Popover } from '@mui/material'; import { ILeaderBaseCardProps, LeaderBaseCardStyle } from './CardTypes'; import { useGame } from '@/app/_contexts/Game.context'; @@ -17,10 +18,28 @@ const LeaderBaseCard: React.FC = ({ disabled = false, }) => { const { sendGameMessage, connectedPlayer, getConnectedPlayerPrompt, distributionPromptData } = useGame(); - + + const [anchorElement, setAnchorElement] = React.useState(null); + const hoverTimeout = React.useRef(undefined); + const open = Boolean(anchorElement); + if (!card) { return null } + + const handlePreviewOpen = (event: React.MouseEvent) => { + const target = event.currentTarget; + if (isDeployed) return; + hoverTimeout.current = window.setTimeout(() => { + setAnchorElement(target); + }, 500); + }; + + const handlePreviewClose = () => { + clearTimeout(hoverTimeout.current); + setAnchorElement(null); + }; + const isDeployed = card.hasOwnProperty('zone') && card.zone !== 'base'; const borderColor = getBorderColor(card, connectedPlayer, getConnectedPlayerPrompt()?.promptType); const distributionAmount = distributionPromptData?.valueDistribution.find((item) => item.uuid === card.uuid)?.amount || 0; @@ -122,7 +141,23 @@ const LeaderBaseCard: React.FC = ({ color: 'white', fontWeight: '600', fontSize: '1em', - } + }, + cardPreview: { + borderRadius: '.38em', + backgroundImage: `url(${s3CardImageURL(card)})`, + backgroundSize: 'cover', + backgroundRepeat: 'no-repeat', + aspectRatio: '1.4 / 1', + width: '21rem', + }, + cardPreviewDeployed: { + borderRadius: '.38em', + backgroundImage: `url(${s3CardImageURL(card)})`, + backgroundSize: 'cover', + backgroundRepeat: 'no-repeat', + aspectRatio: '1 / 1.4', + width: '16rem', + }, } return ( @@ -133,6 +168,10 @@ const LeaderBaseCard: React.FC = ({ sendGameMessage(['cardClicked', card.uuid]); } }} + aria-owns={open ? 'mouse-over-popover' : undefined} + aria-haspopup="true" + onMouseEnter={handlePreviewOpen} + onMouseLeave={handlePreviewClose} > @@ -152,6 +191,26 @@ const LeaderBaseCard: React.FC = ({ )} + + + + {cardStyle === LeaderBaseCardStyle.Leader && title && ( <> @@ -165,4 +224,4 @@ const LeaderBaseCard: React.FC = ({ ); }; -export default LeaderBaseCard; \ No newline at end of file +export default LeaderBaseCard;