Skip to content

Commit

Permalink
Balkhrog/fine tuning popup (#62)
Browse files Browse the repository at this point in the history
Adding displayCards

---------

Co-authored-by: HugoHMZ <[email protected]>
Co-authored-by: Dan Bastin <[email protected]>
  • Loading branch information
3 people authored Jan 19, 2025
1 parent d0c9a77 commit c426582
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 67 deletions.
17 changes: 17 additions & 0 deletions src/app/_components/_sharedcomponents/Popup/Popup.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@ export const buttonStyle = {
},
};

export const perCardButtonStyle = {
borderRadius: '15px',
backgroundColor: '#1E2D32',
padding: '.5rem 1rem',
fontSize: '.75rem',

border: '2px solid transparent',
background:
'linear-gradient(#1E2D32, #1E2D32) padding-box, linear-gradient(to top, #038FC3, #595A5B) border-box',
'&:hover': {
background: 'hsl(195, 25%, 16%)',
},
'&:disabled': {
color: '#666666',
},
};

export const concedeButtonStyle = {
padding: '1rem 1.5rem',
borderRadius: '15px',
Expand Down
13 changes: 10 additions & 3 deletions src/app/_components/_sharedcomponents/Popup/Popup.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ export type PopupButton = {
arg: string;
};

export type PerCardButton = {
arg: string;
command: string;
text: string;
};

export type DefaultPopup = {
type: 'default';
uuid: string;
title: string;
promptType?: string;
description?: string;
buttons: PopupButton[];
};
Expand All @@ -20,9 +25,11 @@ export type SelectCardsPopup = {
type: 'select';
uuid: string;
title: string;
maxNumber?: number;
description?:string;


cards: ICardData[];
onConfirm: (cards: ICardData[]) => void;
perCardButtons: PerCardButton[];
};

export type PilePopup = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import { Box, Button, Typography } from '@mui/material';
import { useState } from 'react';
import { ICardData } from '../../Cards/CardTypes';
import GameCard from '../../Cards/GameCard';

import { Box, Button, IconButton, Typography } from '@mui/material';
import { MouseEvent, useState } from 'react';
import {
buttonStyle,
cardButtonStyle,
containerStyle,
footerStyle,
selectedCardBorderStyle,
selectedIndicatorStyle,
headerStyle,
minimizeButtonStyle,
perCardButtonStyle,
textStyle,
titleStyle,
} from '../Popup.styles';
import { SelectCardsPopup } from '../Popup.types';
import { PerCardButton, SelectCardsPopup } from '../Popup.types';
import { usePopup } from '@/app/_contexts/Popup.context';
import { useGame } from '@/app/_contexts/Game.context';
import { BiMinus, BiPlus } from 'react-icons/bi';
import GameCard from '../../Cards/GameCard';

interface ButtonProps {
data: SelectCardsPopup;
}

const cardListContainerStyle = {
display: 'flex',
Expand All @@ -27,67 +33,72 @@ const cardSelectorStyle = {
gap: '.5rem',
};

interface ButtonProps {
data: SelectCardsPopup;
}

export const SelectCardsPopupModal = ({ data }: ButtonProps) => {
const [selectedCards, setSelectedCards] = useState<
Record<number, ICardData[]>
>([]);
const { closePopup } = usePopup();
const { sendGameMessage } = useGame();
const [isMinimized, setIsMinimized] = useState(false);

const renderPopupContent = () => {
if (isMinimized) return null;
return (
<>
{data.description && (
<Typography sx={textStyle}>{data.description}</Typography>
)}
<Box sx={cardListContainerStyle}>
{data.cards.map((card) => {
return (
<Box key={card.uuid} sx={{ alignItems: 'center', display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<Box key={card.uuid} sx={cardSelectorStyle}>

<GameCard key={card.uuid} card={{ ...card, selectable: false }} />
</Box>
{renderButtons(card.uuid, data.perCardButtons)}
</Box>
)
})}
</Box>
</>
);
};

const handleCardClick = (index: number, card: ICardData) => {
if (!selectedCards[index]) {
setSelectedCards((prev) => ({
...prev,
[index]: [card],
}));
} else {
setSelectedCards((prev) => ({
...prev,
[index]: [...prev[index], card],
}));
}
const handleMinimize = (e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
setIsMinimized(!isMinimized);
};

const isSelectedCard = (index: number) => selectedCards[index] !== undefined;
const isButtonDisabled = () =>
Object.keys(selectedCards).length === 0 ||
(data.maxNumber !== undefined && data.maxNumber > Object.keys(selectedCards).length);
const renderButtons = (cardUuid: string, buttons: PerCardButton[]) => {
return (
<Box sx={{ gap: '1rem', flexDirection: 'column', display: 'flex' }}>
{buttons.map((button, index) =>
<Button
key={`${button.arg}:${index}`}
sx={perCardButtonStyle}
variant="contained"
onClick={() => {
sendGameMessage([button.command, button.arg, cardUuid, data.uuid]); closePopup(data.uuid)
}}
>
{button.text}
</Button>
)}</Box>
)
}

// sort and not filter cards by selectable true first
const sortCards = (cards: ICardData[]) =>
cards.sort((a, b) => Number(b.selectable) - Number(a.selectable));

return (
<Box sx={containerStyle}>
<Typography sx={titleStyle}>{data.title}</Typography>
<Box sx={cardListContainerStyle}>
{sortCards(data.cards).map((card, index) => (
<Box key={`card-${index}-${card.uuid}`} sx={cardSelectorStyle}>
<Button
sx={cardButtonStyle}
onClick={() => handleCardClick(index, card)}
variant="text"
>
<Box sx={selectedCardBorderStyle(isSelectedCard(index))}>
<GameCard card={card} />
</Box>
</Button>
<Box sx={selectedIndicatorStyle(isSelectedCard(index))} />
</Box>
))}
</Box>
<Box sx={footerStyle}>
<Button
disabled={isButtonDisabled()}
onClick={() => data.onConfirm(Object.values(selectedCards).flat())}
sx={buttonStyle}
variant="contained"
<Box sx={headerStyle(isMinimized)}>
<Typography sx={titleStyle}>{data.title}</Typography>
<IconButton
sx={minimizeButtonStyle}
aria-label="minimize"
onClick={handleMinimize}
>
Confirm
</Button>
{isMinimized ? <BiPlus /> : <BiMinus />}
</IconButton>
</Box>
{renderPopupContent()}
</Box>
);
};
20 changes: 17 additions & 3 deletions src/app/_contexts/Game.context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,28 @@ export const GameProvider = ({ children }: { children: ReactNode }) => {
if (!user || user.id == null) return; // TODO currently this doesn't support private lobbies where players aren't logged in.
if (gameState.players?.[user.id].promptState) {
const promptState = gameState.players?.[user.id].promptState;
const { buttons, menuTitle,promptTitle, promptUuid, selectCard, promptType, dropdownListOptions } =
const { buttons, menuTitle,promptTitle, promptUuid, selectCard, promptType, dropdownListOptions, perCardButtons, displayCards } =
promptState;
if (promptType === 'actionWindow') return;
else if (promptType === 'displayCards') {
const cards = displayCards.map((card: any) => {
return {
...card,
uuid: card.cardUuid,
};
});
return openPopup('select', {
uuid: promptUuid,
title: promptTitle,
description: menuTitle,
cards: cards,
perCardButtons: perCardButtons,
});
}
else if (buttons.length > 0 && menuTitle && promptUuid && !selectCard) {
return openPopup('default', {
uuid: promptUuid,
title: menuTitle,
promptType: promptType,
buttons,
});
}
Expand Down Expand Up @@ -167,4 +181,4 @@ export const useGame = () => {
throw new Error('useGame must be used within a GameProvider');
}
return context;
};
};

0 comments on commit c426582

Please sign in to comment.