I’m using React js and @szhsin/react-menu to create a way for the user to create their own formation and edit the formation. If the user right clicks on an object, a menu pops up where they can select from several options. My current issue is that no matter what I try to do, the MenuItem and MenuButton objects will always be overlapped by other PlayerCardMini objects generated by my ‘Formations’ component. I have tried changing the z-index for everything to no avail. Here is an example of the problem:
[Example of problem] (https://i.sstatic.net/jyeCwpxF.png)
Here is my code for the Menu and PlayerCard object.
//PlayerCardMini.js
const PlayerCardMenu = () => {
return (
<Menu
menuButton={<MenuButton
className="menu-button"
style={{ marginLeft: '-50px',
left: `${menuState.x}px`,
top: `${menuState.y}px`, display: 'flex', float: 'left'}}
>Edit</MenuButton>}
onClose={handleCloseMenu}>
<MenuItem style={{position: 'relative', zIndex: 100}}
onClick={() => {
if (isQueue) {
console.log('Player being removed from queue at index: ', index);
removePlayer(setFunction, index);
} else {
removePlayer(position);
}
handleCloseMenu(); // Optionally, you might want to close the menu here as well
}}
>
Remove Player
</MenuItem>
{!isQueue && <MenuItem onClick={() => { handleSwitch() }} disabled={!canSwitch}>Switch Player</MenuItem>}
<MenuItem onClick={toggleStatsPopup}>Show Stats Menu</MenuItem>
<MenuItem onClick={handleCloseMenu}>Exit</MenuItem>
</Menu>
);
}
return (
<div onDoubleClick={() => handleDoubleClick(player)}
className="player-card-mini"
onContextMenu={handleContextMenu}
ref={combineRefs}
style={{
opacity: isDragging ? 0.5 : 1,
cursor: 'grab',
}}>
<div className="left-panel">
<div className="player-info">
<h2 style={{ fontSize: '20px' }}>{player.overall}</h2>
<h3 style={{ fontSize: getPosition().length > 2 ? '12px' : '16px' }}>
<span dangerouslySetInnerHTML={{ __html: getPosition() }} />
</h3>
<img src={player.clubLogo} alt="Club Logo" className="club-logo" />
<img src={player.flag} alt="Country Flag" className="country-flag" />
</div>
</div>
<div className="right-panel">
<img src={imageUrl} onError={handleImageError} alt={player.name} className="player-photo" />
<p style={{ marginBottom: '10px', fontSize: '14px' }}>{getLastName(player.name)}</p>
</div>
<div id="player-card-menu-portal"></div>
{menuState.isOpen && <PlayerCardMenu />}
{statsPopupOpen && <PlayerStatsPopup
player={player}
saveAllChanges={saveAllChanges}
onClose={toggleStatsPopup}
getPlayerStats={getPlayerStats} />}
</div>
);
PlayerCardMini.css
.player-card-mini {
position: relative;
top: 0;
left: 0;
display: flex;
justify-content: space-between;
align-items: center;
width: 150px;
/* Adjust width as needed */
height: 200px;
/* Adjust height as needed */
background-image: url('empty_card.png');
/* Set the background image */
background-size: cover;
z-index: 2;
margin-left: -7px;
}
.player-card-mini .left-panel {
display: flex;
flex-direction: column;
align-items: center;
margin-left: 7px;
}
.player-card-mini .right-panel {
text-align: center;
font-family: "Lucida Sans", sans-serif;
margin-bottom: 70px;
}
.player-card-mini .player-info {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
margin-left: 15px;
}
.player-card-mini .player-photo {
width: 75px;
/* Adjust width as needed */
height: 75px;
/* Adjust height as needed */
border-radius: 50%;
margin-bottom: 5px;
margin-left: 10px;
}
.player-card-mini .club-logo {
width: 25px;
/* Adjust width as needed */
height: 25px;
/* Adjust height as needed */
margin-bottom: 5px;
}
.player-card-mini .country-flag {
width: 15px;
/* Adjust width as needed */
height: auto;
margin-bottom: 5px;
}
.player-card-mini .szh-menu {
position: absolute;
z-index: 1000;
}
.menu-button {
border-radius: 10px;
color: white;
background-color: #007BFF;
padding: 10px 20px;
border: none;
cursor: pointer;
transition: background-color 0.3 ease;
z-index: 5;
}
.menu-button:hover {
background-color: #0056b3; /* Darker shade on hover */
}
How my Positions are generated within Formation.js (Each includes a PlayerCard object):
const generatePositions = (positionType, numPlayers) => {
return Array.from({ length: numPlayers }, (_, index) => {
const { originalName, modifiedName } = generatePositionName(positionType, index, numPlayers);
const positionKey = `${positionType}-${index}`
return <Position
key={positionKey}
position={originalName}
modifiedPosition={modifiedName}
player={positions[positionKey]}
highlightedPosition={highlightedPosition}
setHighlightedPosition={setHighlightedPosition}
removePlayer={() => removePlayer(positionKey)}
saveAllChanges={saveAllChanges}
switchPlayer={switchPlayer}
getPlayerStats={getPlayerStats}
/>;
});
};
const positionGroups = [
{ type: 'Striker', positions: generatePositions('Striker', formation.strikers) },
{ type: 'aMidfielder', positions: generatePositions('aMidfielder', formation.aMidfielders) },
{ type: 'Midfielder', positions: generatePositions('Midfielder', formation.midfielders) },
{ type: 'dMidfielder', positions: generatePositions('dMidfielder', formation.dMidfielders) },
{ type: 'Defender', positions: generatePositions('Defender', formation.defenders) },
{ type: 'Goalkeeper', positions: generatePositions('Goalkeeper', 1) } // Assuming generatePositions can handle the goalkeeper as a special case
];
useEffect(() => {
const allPositionNames = positionGroups.flatMap(group =>
group.positions.map(child => child.props.modifiedPosition)
);
setPositionNames(allPositionNames);
// Only call these functions if players are already initialized
findBestPlayers(allPositionNames, queue);
}, [formation]);
return (
<div className="soccer-field">
<div className="positions-container">
{positionGroups.map(group => (
<div className="positions-section" key={group.type}>
{group.positions}
</div>
))}
</div>
</div>
);
Position component:
return (
<div ref={drop} className=”position” onDoubleClick={() => handleDoubleClick()}>
<div className={empty-card ${isHighlighted ? 'highlight' : ''}
}>
{positionName}
{player && (
)}
);
//Formations.css
.soccer-field {
margin-top: 5px;
background-image: url('./resources/soccerfield.png');
background-size: 100%;
background-position: center bottom;
height: 1000px;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
z-index: -1;
}
.positions-container {
display: flex;
flex-direction: column;
z-index: 1;
}
.positions-section {
display: flex;
flex-direction: row;
justify-content: center;
flex-wrap: wrap;
}
.position {
position: relative;
z-index: 0;
}
.empty-card.highlight {
border-color: rgb(25, 0, 255);
}
.empty-card {
display: flex;
justify-content: space-between;
align-items: center;
width: 150px;
/* Adjust width as needed */
height: 200px;
/* Adjust height as needed */
background-image: url('empty_card.png');
background-size: cover;
background-position: center;
position: relative;
border: 3px solid rgba(255, 255, 255, 0.5);
z-index: 0;
transform: scale(0.9);
}
.empty-card .position-label {
position: absolute;
color: white;
font-weight: bold;
font-size: 16px;
text-align: center;
z-index: 1;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
MasterGX is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.