From 0607a1b999b8fca890e47fe10d81c57017828cbf Mon Sep 17 00:00:00 2001 From: HugoHMZ Date: Wed, 8 Jan 2025 01:49:11 +0900 Subject: [PATCH 1/3] [+/*] dropdown popup & header styling --- .../_subcomponents/PlayerTray/DeckDiscard.tsx | 51 +++++------ .../_sharedcomponents/Popup/Popup.styles.ts | 16 ++-- .../_sharedcomponents/Popup/Popup.tsx | 36 ++++---- .../_sharedcomponents/Popup/Popup.types.ts | 16 ++-- .../Popup/PopupVariant/DefaultPopup.tsx | 4 +- .../Popup/PopupVariant/DropdownPopup.tsx | 87 +++++++++++++++++++ .../Popup/PopupVariant/PilePopup.tsx | 6 +- src/app/_contexts/Game.context.tsx | 12 ++- src/app/_contexts/Popup.context.tsx | 28 +++--- 9 files changed, 173 insertions(+), 83 deletions(-) create mode 100644 src/app/_components/_sharedcomponents/Popup/PopupVariant/DropdownPopup.tsx diff --git a/src/app/_components/Gameboard/_subcomponents/PlayerTray/DeckDiscard.tsx b/src/app/_components/Gameboard/_subcomponents/PlayerTray/DeckDiscard.tsx index deb68ab3..50cdbca6 100644 --- a/src/app/_components/Gameboard/_subcomponents/PlayerTray/DeckDiscard.tsx +++ b/src/app/_components/Gameboard/_subcomponents/PlayerTray/DeckDiscard.tsx @@ -9,7 +9,7 @@ const DeckDiscard: React.FC = ( trayPlayer ) => { const { gameState } = useGame(); - const { openPopup } = usePopup(); + const { togglePopup } = usePopup(); // ------------------------STYLES------------------------// const containerStyle = { @@ -20,12 +20,21 @@ const DeckDiscard: React.FC = ( alignItems: 'center', }; - const discardCardStyle = { + const cardPileStyle = { backgroundColor: '#282828E6', width: '7vh', height: '9.5vh', }; + const selectableStyle = { + '&:hover': { + backgroundColor: '#282828', + cursor: 'pointer', + scale: '1.1', + transition: 'all ease-in-out 0.15s', + }, + } + const cardContentStyle = { height: '100%', display: 'flex', @@ -41,34 +50,20 @@ const DeckDiscard: React.FC = ( color: 'white', }; - const pileStyle = { - backgroundColor: 'transparent', - padding: '0', - borderRadius: '16px', - }; - return ( - - + + togglePopup('pile', { + uuid: `${trayPlayer.trayPlayer}-discard`, + title: `${trayPlayer.trayPlayer}'s discard`, + cards: + gameState?.players[trayPlayer.trayPlayer]?.cardPiles['discard'], + })}> + + Discard + + + Deck diff --git a/src/app/_components/_sharedcomponents/Popup/Popup.styles.ts b/src/app/_components/_sharedcomponents/Popup/Popup.styles.ts index 8d7a790e..7d70f803 100644 --- a/src/app/_components/_sharedcomponents/Popup/Popup.styles.ts +++ b/src/app/_components/_sharedcomponents/Popup/Popup.styles.ts @@ -31,7 +31,6 @@ export const contentStyle = (index: number) => ({ background: 'linear-gradient(#0F1F27, #030C13) padding-box, linear-gradient(to top, #30434B, #50717D) border-box', zIndex: 11 + index, - marginTop: index * 30, }); export const containerStyle = { @@ -53,10 +52,11 @@ export const containerStyle = { export const headerStyle = (isMinimized: boolean) => ({ display: 'flex', width: '100%', - justifyContent: 'space-between', alignItems: 'center', + justifyContent: 'center', marginTop: '-10px', marginBottom: isMinimized ? '-20px' : '0', + position: 'relative', }); export const footerStyle = { @@ -68,20 +68,20 @@ export const footerStyle = { width: '100%', }; -export const minimalButtonStyle = { +export const minimizeButtonStyle = { marginTop: '-15px', color: 'white', - display: 'flex', + position: 'absolute', + right: 0, + }; export const titleStyle = { color: 'white', fontSize: '1.25rem', fontWeight: 'bold', - flex: 1, - display: 'flex', - alignItems: 'center', - justifyContent: 'center', + textAlign: 'center', + width: '80%' }; export const textStyle = { diff --git a/src/app/_components/_sharedcomponents/Popup/Popup.tsx b/src/app/_components/_sharedcomponents/Popup/Popup.tsx index ce97b46d..b31cb3d3 100644 --- a/src/app/_components/_sharedcomponents/Popup/Popup.tsx +++ b/src/app/_components/_sharedcomponents/Popup/Popup.tsx @@ -2,12 +2,13 @@ import { PopupData, PopupType, usePopup } from '@/app/_contexts/Popup.context'; import { Box, Button, SxProps, Theme } from '@mui/material'; import React from 'react'; -import { DefaultPopup, PilePopup, SelectCardsPopup } from './Popup.types'; +import { DefaultPopup, DropdownPopup, PilePopup, SelectCardsPopup } from './Popup.types'; import { DefaultPopupModal } from './PopupVariant/DefaultPopup'; import { PilePopupModal } from './PopupVariant/PilePopup'; import { SelectCardsPopupModal } from './PopupVariant/SelectCardsPopup'; import { contentStyle } from './Popup.styles'; import { useGame } from '@/app/_contexts/Game.context'; +import { DropdownPopupModal } from './PopupVariant/DropdownPopup'; const overlayStyle = { position: 'absolute', @@ -19,27 +20,27 @@ const overlayStyle = { zIndex: 10, }; -const focusHandlerStyle = (type: PopupType, data: PopupData, index: number, playerName:string): SxProps => ({ +const focusHandlerStyle = (type: PopupType, data: PopupData, index: number, playerName:string, containCards?:boolean): SxProps => ({ zIndex: 11 + index, - padding: 0, - minWidth: 'auto', - '&:hover': { - backgroundColor: 'transparent', - }, pointerEvents: 'auto', - ...getPopupPosition(type, data, index, playerName) + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + ...getPopupPosition(type, data, index, playerName, containCards) }); -export const getPopupPosition = (type: PopupType, data: PopupData, index: number, playerName:string) => { +export const getPopupPosition = (type: PopupType, data: PopupData, index: number, playerName:string, containCards?:boolean) => { const basePosition = { position: 'absolute', - left: '50%', - transform: `translate(-50%, 0) translate(0px, ${index * 10}px)`, + left:'50%', + marginTop: '150px', + transform: `translate(0px, ${index * 50}px)`, }; const pilePosition = { position: 'absolute', - left: '325px', + left: containCards ? '-23%' : '-30.5%', + width: '100%', } if (type === 'pile') { @@ -64,6 +65,7 @@ export const getPopupPosition = (type: PopupType, data: PopupData, index: number const PopupShell: React.FC = () => { const { popups, focusPopup } = usePopup(); const { connectedPlayer }= useGame(); + const isPilePopup = (popup: PopupData): popup is PilePopup => popup.type === 'pile'; if (popups.length === 0) return null; // No popup to display @@ -75,6 +77,8 @@ const PopupShell: React.FC = () => { return ; case 'select': return ; + case 'dropdown': + return ; default: return null; } @@ -82,15 +86,13 @@ const PopupShell: React.FC = () => { const renderPopup= (popup: PopupData, index:number) => { return ( - + ) } diff --git a/src/app/_components/_sharedcomponents/Popup/Popup.types.ts b/src/app/_components/_sharedcomponents/Popup/Popup.types.ts index 738869dd..11302884 100644 --- a/src/app/_components/_sharedcomponents/Popup/Popup.types.ts +++ b/src/app/_components/_sharedcomponents/Popup/Popup.types.ts @@ -25,17 +25,17 @@ export type SelectCardsPopup = { onConfirm: (cards: ICardData[]) => void; }; -// Necessary ? -export type SelectFromPilePopup = { - type: 'pile-select'; - uuid: string; - pile: ICardData[]; - onConfirm: (cards: ICardData[]) => void; -}; - export type PilePopup = { type: 'pile'; uuid: string; title: string; cards: ICardData[]; +}; + +export type DropdownPopup = { + type: 'dropdown'; + uuid: string; + title: string; + description?: string; + options: string[]; }; \ No newline at end of file diff --git a/src/app/_components/_sharedcomponents/Popup/PopupVariant/DefaultPopup.tsx b/src/app/_components/_sharedcomponents/Popup/PopupVariant/DefaultPopup.tsx index 5d56d45d..49ea61f8 100644 --- a/src/app/_components/_sharedcomponents/Popup/PopupVariant/DefaultPopup.tsx +++ b/src/app/_components/_sharedcomponents/Popup/PopupVariant/DefaultPopup.tsx @@ -8,7 +8,7 @@ import { containerStyle, footerStyle, headerStyle, - minimalButtonStyle, + minimizeButtonStyle, textStyle, titleStyle, } from '../Popup.styles'; @@ -59,7 +59,7 @@ export const DefaultPopupModal = ({ data }: ButtonProps) => { {data.title} diff --git a/src/app/_components/_sharedcomponents/Popup/PopupVariant/DropdownPopup.tsx b/src/app/_components/_sharedcomponents/Popup/PopupVariant/DropdownPopup.tsx new file mode 100644 index 00000000..bd02efa0 --- /dev/null +++ b/src/app/_components/_sharedcomponents/Popup/PopupVariant/DropdownPopup.tsx @@ -0,0 +1,87 @@ +import { useGame } from '@/app/_contexts/Game.context'; +import { usePopup } from '@/app/_contexts/Popup.context'; +import { Box, Button, IconButton, MenuItem, Select, SelectChangeEvent, Typography } from '@mui/material'; +import { MouseEvent, useState } from 'react'; +import { BiMinus, BiPlus } from 'react-icons/bi'; +import { + buttonStyle, + containerStyle, + footerStyle, + headerStyle, + minimizeButtonStyle, + textStyle, + titleStyle, +} from '../Popup.styles'; +import { DropdownPopup } from '../Popup.types'; + +interface ButtonProps { + data: DropdownPopup; +} + +export const DropdownPopupModal = ({ data }: ButtonProps) => { + const { sendGameMessage } = useGame(); + const { closePopup } = usePopup(); + const [isMinimized, setIsMinimized] = useState(false); + const [selectedOption, setSelectedOption] = useState(''); + + const handleChange = (event: SelectChangeEvent) => { + setSelectedOption(event.target.value as string); + }; + + const handleDone = () => { + sendGameMessage(['menuButton', selectedOption, data.uuid]); + closePopup(data.uuid); + }; + + const renderPopupContent = () => { + if (isMinimized) return null; + return ( + <> + {data.description && ( + {data.description} + )} + + + + + + + ); + }; + + const handleMinimize = (e: MouseEvent) => { + e.stopPropagation(); + setIsMinimized(!isMinimized); + }; + + return ( + + + {data.title} + + {isMinimized ? : } + + + {renderPopupContent()} + + ); +}; \ No newline at end of file diff --git a/src/app/_components/_sharedcomponents/Popup/PopupVariant/PilePopup.tsx b/src/app/_components/_sharedcomponents/Popup/PopupVariant/PilePopup.tsx index 14fd6793..9121b5c3 100644 --- a/src/app/_components/_sharedcomponents/Popup/PopupVariant/PilePopup.tsx +++ b/src/app/_components/_sharedcomponents/Popup/PopupVariant/PilePopup.tsx @@ -8,7 +8,7 @@ import { containerStyle, footerStyle, headerStyle, - minimalButtonStyle, + minimizeButtonStyle, titleStyle, } from '../Popup.styles'; import { PilePopup } from '../Popup.types'; @@ -56,6 +56,8 @@ export const PilePopupModal = ({ data }: ButtonProps) => { ))} + {data.cards.length === 0 && No cards to display} + + )} + ) + } - // sort and not filter cards by selectable true first - const sortCards = (cards: ICardData[]) => - cards.sort((a, b) => Number(b.selectable) - Number(a.selectable)); return ( - {data.title} - - {sortCards(data.cards).map((card, index) => ( - - - - - ))} - - - + {isMinimized ? : } + + {renderPopupContent()} ); }; \ No newline at end of file diff --git a/src/app/_contexts/Game.context.tsx b/src/app/_contexts/Game.context.tsx index 927ff6a6..c44b9d7e 100644 --- a/src/app/_contexts/Game.context.tsx +++ b/src/app/_contexts/Game.context.tsx @@ -55,14 +55,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, }); } @@ -151,4 +165,4 @@ export const useGame = () => { throw new Error('useGame must be used within a GameProvider'); } return context; -}; \ No newline at end of file +}; \ No newline at end of file From 2dfa87dbd2a1a4056a2e5088d65e758c2c28063d Mon Sep 17 00:00:00 2001 From: HugoHMZ Date: Thu, 16 Jan 2025 23:38:36 +0900 Subject: [PATCH 3/3] [*] gameMessage: undo previous change in gameMessage to match the server expectation --- .../_sharedcomponents/Popup/PopupVariant/SelectCardsPopup.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/_components/_sharedcomponents/Popup/PopupVariant/SelectCardsPopup.tsx b/src/app/_components/_sharedcomponents/Popup/PopupVariant/SelectCardsPopup.tsx index 59f69d9d..ab32be8d 100644 --- a/src/app/_components/_sharedcomponents/Popup/PopupVariant/SelectCardsPopup.tsx +++ b/src/app/_components/_sharedcomponents/Popup/PopupVariant/SelectCardsPopup.tsx @@ -34,7 +34,7 @@ const cardSelectorStyle = { export const SelectCardsPopupModal = ({ data }: ButtonProps) => { const { closePopup } = usePopup(); - const { sendGameMessage, connectedPlayer } = useGame(); + const { sendGameMessage } = useGame(); const [isMinimized, setIsMinimized] = useState(false); const renderPopupContent = () => { @@ -75,7 +75,7 @@ export const SelectCardsPopupModal = ({ data }: ButtonProps) => { sx={perCardButtonStyle} variant="contained" onClick={() => { - sendGameMessage([connectedPlayer, button.arg, cardUuid, data.uuid]); closePopup(data.uuid) + sendGameMessage([button.command, button.arg, cardUuid, data.uuid]); closePopup(data.uuid) }} > {button.text}