Reactjs hover and display questions - javascript

I am developing a text based web game using react.
I have an ItemDisplay class which displays bunch of Item classes, each representing an item.
I used a lot of react-bootstrap to deal with modal and tooltip, but there are still some of the issues I haven't been able to solve.
Here's a screenshot of the ItemDisplay:
https://imgur.com/a/NUtFFsL
ItemDisplay code
ItemDisplay
class ItemDisplay extends Component {
constructor(props) {
super(props);
this.state = {
show: false
}
}
render() {
// items attributes are in object that looks like: {name: 'sword', desc: 'a stupid sword', atk: 8, dex: 1}, {name: 'shield', desc: 'a stupid shield', def: 1}];
let items = Object.keys(this.props.items).map(item => <Item key={this.props.items[item].name} show={this.state.show} attri={this.props.items[item]}></Item>);
return (
<div className='item-list'>
{items}
</div>
)
}
}
export default ItemDisplay;
and Item class
class Item extends Component {
constructor(props) {
super(props);
this.state = {
show: false
};
this.handleHoverOn = this.handleHoverOn.bind(this);
this.handleHoverOff = this.handleHoverOff.bind(this);
}
handleHoverOn() {
this.setState({show: true});
}
handleHoverOff() {
this.setState({show: false});
}
toTitle = (word) => {
return word.charAt(0).toUpperCase() + word.slice(1);
}
render() {
let attributes = Object.keys(this.props.attri).map((key) => <p>{this.toTitle(key)}: {this.props.attri[key]}</p>)
return (
<div className='aux'>
<OverlayTrigger key={this.props.attri.name} placement='top' className='item'
overlay={<Tooltip id={attributes.name}>{attributes}</Tooltip>}>
<p>{this.props.attri.name}</p>
</OverlayTrigger>
</div>
)
}
}
export default Item;
Here are my problems:
As you can see from the screenshot, I cannot wrap Sword of A Thousand Truths within that box. I don't know if that's because I have a div wrapping the OverlayTrigger
As of now, hovering over the name of the items (only the words) will display the attributes (atk ,def, etc.) in a Tooltip, but I want to be able to hover over the entire box instead of the words only to have the same effects. Is there a way to do it?
EDIT: Think I should add my css as well
ItemDisplay.css
.item-list {
height: 100%;
white-space: nowrap;
overflow-y: hidden;
overflow-x: scroll;
padding-left: 0;
}
.overlay {
height: 100%;
broder: 1px solid black;
}
Item.css
.item {
word-wrap: break-word;
}
.aux {
display: inline-block;
height: 100%;
width: 100px;
border: 1px solid;
border-radius: 5px;
margin: 2px 2px 2px 2px;
text-align: center;
top: 50%;
}
.equip {
border: 2px solid #267f0b;
font-weight: bold;
}

Related

ReactJS - pass object keys and values as props to div

In my Class component Field.jsx render(), I'm expanding my <Position> component using <Flipper>, (an abstracted flip animation), like so:
import { Flipper, Flipped } from 'react-flip-toolkit'
import { Position } from "./Position";
import "./css/Position.css";
class Field extends Component {
constructor(props) {
super(props);
this.state = {
fullScreen: false,
};
}
toggleFullScreen() {
this.setState({ fullScreen: !this.state.fullScreen });
}
...
render() {
const { players } = this.props;
const { fullScreen } = this.state;
if(players){
return (
<div className="back">
<div className="field-wrapper" >
<Output output={this.props.strategy} />
<Flipper flipKey={fullScreen}>
<Flipped flipId="player">
<div className="field-row">
{this.getPlayersByPosition(players, 5).map((player,i) => (
<Position
key={i}
className={fullScreen ? "full-screen-player" : "player"}
getPositionData={this.getPositionData}
toggleFullScreen={this.toggleFullScreen.bind(this)}
>{player.name}</Position>
))}
</div>
</Flipped>
</Flipper>
</div>
</div>
);
}else{
return null}
}
When I render it, I get clickable items from the mapped function getPlayersByPosition(), like so:
And if I click on each item, it expands to a div with player name:
Which is passed as props.children at component <div>
Position.jsx
import React from "react";
import "./css/Position.css";
export const Position = props => (
<div
className={props.className}
onClick={() => {
props.getPositionData(props.children);
props.toggleFullScreen();
console.log(props.getPositionData(props.children))
}}
>
{props.children}
</div>
);
getPositionData(), however, returns an object with many items on its turn, as seen by console above:
{matches: 7, mean: 6.15, price: 9.46, value: 0.67, G: 3, …}
QUESTION:
How do I pass and print theses other props keys and values on the expanded purple div as text?, so as to end with:
Patrick de Paula
matches: 7
mean: 6.15
price:9.46
....
NOTE:
Position.css
.position-wrapper {
height: 4em;
display: flex;
justify-content: center;
align-items: center;
font-weight: lighter;
font-size: 1.4em;
color: #888888;
flex: 1;
/*outline: 1px solid #888888;*/
}
.player {
height: 4em;
width: 4em;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
font-weight: lighter;
font-size: 1.4em;
/*background-color: #66CD00;*/
color: #ffffff;
}
.full-screen-player {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: pointer;
background-image: linear-gradient(
45deg,
rgb(121, 113, 234),
rgb(97, 71, 182)
);
}
Looks like the props are all set & ready to be print as seen on your console. You can access them via props.getPositionData(props.children).property_name_here or destructure them
export const Position = props => {
const { matches, mean, price } = props.getPositionData(props.children);
return (
<div
className={props.className}
onClick={() => {
props.getPositionData(props.children);
props.toggleFullScreen();
console.log(props.getPositionData(props.children))
}}
>
<p>Name: {props.children}</p>
<p>Matches: {matches}</p>
<p>Mean: {mean}</p>
<p>Price: {price}</p>
</div>
)
}
Regarding the issue on the fullScreen prop (see comments section):
Is there a way to print them ONLY after toggleFullScreen()
Since you already have a state on the Field component which holds your fullScreen value, on your Field component, you need to pass the fullScreen prop as well to the Position component. e.g., fullScreen={this.state.fullScreen}. Back on Position component, have some condition statements when you are rendering.
Example:
<>
{props.fullScreen &&
<p>Name: {props.children}</p>
}
</>

React custom dropdown with event listener

I created a Dropdown that when I click outside of it the dropdown disappears. I used a click event listener to determine if I clicked outside the dropdown.
After a few clicks, the page slows down and crashes. Perhaps the state is being rendered in a loop or too many events are being fired at once?
How do I fix this?
Also, is there a more React way to determine if I clicked outside an element? (Instead of using a document.body event listener)
Here is the codepen:
const items = [
{
value: 'User1'
},
{
value: 'User2'
},
{
value: 'User3'
},
{
value: 'User4'
},
{
value: 'User5'
}
];
class Dropdown extends React.Component {
state = {
isActive: false,
}
render() {
const { isActive } = this.state;
document.addEventListener('click', (evt) => {
if (evt.target.closest('#dropdownContent')) {
//console.warn('clicked inside target do nothing');
return;
}
if (evt.target.closest('#dropdownHeader')) {
//console.warn('clicked the header toggle');
this.setState({isActive: !isActive});
}
//console.warn('clicked outside target');
if (isActive) {
this.setState({isActive: false});
}
});
return (
<div id="container">
<div id="dropdownHeader">select option</div>
{isActive && (
<div id="dropdownContent">
{items.map((item) => (
<div id="item" key={item.value}>
{item.value}
</div>
))}
</div>
)}
</div>
);
};
}
ReactDOM.render(
<Dropdown items={items} />,
document.getElementById('root')
);
#container {
position: relative;
height: 250px;
border: 1px solid black;
}
#dropdownHeader {
width: 100%;
max-width: 12em;
padding: 0.2em 0 0.2em 0.2em;
margin: 1em;
cursor: pointer;
box-shadow: 0 1px 4px 3px rgba(34, 36, 38, 0.15);
}
#dropdownContent {
display: flex;
flex-direction: column;
position: absolute;
top: 3em;
width: 100%;
max-width: 12em;
margin-left: 1em;
box-shadow: 0 1px 4px 0 rgba(34, 36, 38, 0.15);
padding: 0.2em;
}
#item {
font-size: 12px;
font-weight: 500;
padding: 0.75em 1em 0.75em 2em;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root">
<!-- This element's contents will be replaced with your component. -->
</div>
There's a pretty simple explanation for what you're experiencing. :)
The way I was able to figure it out was the number of warnings that were showing up in the terminal every time I clicked somewhere was getting higher and higher, especially when the state changed.
The answer though is that since you were adding the event listener code in the render function, every time the code re-rendered it would add more and more event listeners slowing down your code.
Basically the solution is that you should move the adding of event listeners to componentDidMount so it's only run once.
Updated working javascript:
const items = [
{
value: 'User1'
},
{
value: 'User2'
},
{
value: 'User3'
},
{
value: 'User4'
},
{
value: 'User5'
}
];
class Dropdown extends React.Component {
state = {
isActive: false,
}
// added component did mount here
componentDidMount(){
const { isActive } = this.state;
document.addEventListener('click', (evt) => {
if (evt.target.closest('#dropdownContent')) {
console.warn('clicked inside target do nothing');
return;
}
if (evt.target.closest('#dropdownHeader')) {
console.warn('clicked the header toggle');
this.setState({isActive: !isActive});
}
console.warn('clicked outside target');
if (isActive) {
this.setState({isActive: false});
}
});
}
render() {
const { isActive } = this.state;
//removed event listener here
return (
<div id="container">
<div id="dropdownHeader">select option</div>
{isActive && (
<div id="dropdownContent">
{items.map((item) => (
<div id="item" key={item.value}>
{item.value}
</div>
))}
</div>
)}
</div>
);
};
}
ReactDOM.render(
<Dropdown items={items} />,
document.getElementById('root')
);

How to create vertical tab using reactjs and css

Am trying to bulid vertical tab that will function exactly like in the demo link below
sample links from w3schools
here is the screenshot of what am trying to achieve as per the demo sample above
To this effect tried solution found here at but it does not give me what I want as per demo sample above
Stackoverflow link
Now I have decided to go my own way in trying it out.
I have succeeded in displaying the content from an array via reactjs. when user click on each country, the content
of that country gets displayed.
My Problem:
My issue is that I cannot get it to display the content in a vertical tab div as can be seen in the screenshot
Here is the coding so far
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
class Country extends React.Component {
state = { open: false };
toggleOpen = id => {
alert(id);
this.setState(prevState => ({
open: !prevState.open
}));
};
render() {
return (
<React.Fragment>
<div key={this.props.data.id}>
<button onClick={() => this.toggleOpen(this.props.data.id)}>
{this.props.data.name}
</button>
</div>
<div>
{this.state.open && (
<div>
<div>
<b>id: </b>
{this.props.data.id}
</div>
<div>
<b>Info: </b>
{this.props.data.info}
</div>
<div>
<b>Country name:</b> {this.props.data.name}
</div>
content for <b> {this.props.data.name}</b> will appear here..
</div>
)}
</div>
</React.Fragment>
);
}
}
class VerticalTab extends React.Component {
constructor() {
super();
this.state = {
data: [
{ id: "1", name: "London", info: "London is the capital city of England." },
{ id: "2", name: "Paris", info: "Paris is the capital of France." },
{ id: "3", name: "Tokyo", info: "Tokyo is the capital of Japan." }
]
};
}
render() {
return (
<div>
<div>
{this.state.data.map(country => (
<Country key={country.id} data={country} />
))}
</div>
</div>
);
}
}
Is this what you are looking for?
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
currentTab: -1,
data: [
{ id: "1", name: "London" ,info: "London is the capital city of England."},
{ id: "2", name: "Paris" ,info: "Paris is the capital of France." },
{ id: "3", name: "Tokyo" ,info: "Tokyo is the capital of Japan."}
]
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(currentTab) {
this.setState({ currentTab });
}
render() {
return (
<div>
<h2>Vertical Tabs</h2>
<p>Click on the buttons inside the tabbed menu:</p>
<div className="tab">
{this.state.data.map((button, i) => (
<button key={button.name} className="tablinks" onClick={() => this.handleClick(i)}>{button.name}</button>
)
)
}
</div>
<div className="tabcontent">
{this.state.currentTab !== -1 &&
<React.Fragment>
<h3>{this.state.data[this.state.currentTab].name}</h3>
<p>{this.state.data[this.state.currentTab].info}</p>
</React.Fragment>
}
</div>
</div>
)
}
}
ReactDOM.render( < App / > ,
document.getElementById('root')
);
* {box-sizing: border-box}
body {font-family: "Lato", sans-serif;}
/* Style the tab */
.tab {
float: left;
border: 1px solid #ccc;
background-color: #f1f1f1;
width: 30%;
height: 300px;
}
/* Style the buttons inside the tab */
.tab button {
display: block;
background-color: inherit;
color: black;
padding: 22px 16px;
width: 100%;
border: none;
outline: none;
text-align: left;
cursor: pointer;
transition: 0.3s;
font-size: 17px;
}
/* Change background color of buttons on hover */
.tab button:hover {
background-color: #ddd;
}
/* Create an active/current "tab button" class */
.tab button.active {
background-color: #ccc;
}
/* Style the tab content */
.tabcontent {
float: left;
padding: 0px 12px;
border: 1px solid #ccc;
width: 70%;
border-left: none;
height: 300px;
}
<script src="https://unpkg.com/react#16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js" crossorigin></script>
<div id="root" />
React Tabs with Hooks
Here is a link to react tabs. Maybe this will help you.
enter code here

How to render different element based on props?

I am in a project where we use very different headings.
I am trying to unify them into one component
I am trying to decouple semantics (h1, h2, ...) from looks
Here is what the currently looks like (work in progress)
import * as React from 'react';
import './Heading.css';
import { MarkupContent } from 'app-types';
import HeadingElement from './HeadingElement';
interface HeadingProps {
type: 'fullwidth' | 'emphasis';
children: MarkupContent;
element: 'h1' | 'h2';
}
function Heading(props: HeadingProps) {
switch (props.type) {
case 'fullwidth':
return (
<div className="big-heading-container">
<div className="big-heading-section">
<HeadingElement element={props.element} classes="big-heading-text">
{props.children}
</HeadingElement>
</div>
</div>
);
case 'emphasis':
return (
<h2 className="heading--emphasized">
{props.children}
</h2>
);
default:
return (
<></>
);
}
}
export default Heading;
import * as React from 'react';
import { MarkupContent } from 'app-types';
interface HeadingElementProps {
element: 'h1' | 'h2';
classes: string;
children: MarkupContent;
}
function HeadingElement(props: HeadingElementProps) {
switch (props.element) {
case 'h1':
return (
<h1 className={props.classes}>{props.children}</h1>
);
case 'h2':
return (
<h2 className={props.classes}>{props.children}</h2>
);
default:
return (
<></>
);
}
}
export default HeadingElement;
#import "../../parameters.scss";
.big-heading {
&-container {
padding: 90px 25px 0 25px;
background-image: url("../../images/heading-background.png");
border-bottom: 1px solid $green;
}
&-section {
max-width: $max-width;
margin: 0 auto 0 auto;
display: flex;
}
&-text {
font-size: 1.5rem;
text-transform: uppercase;
border-bottom: 4px solid $green;
padding: 0 0 15px 0;
display: inline;
}
}
.heading--emphasized {
font-size: 1.7rem;
line-height: 2.0rem;
font-weight: bold;
text-transform: uppercase;
display: inline;
border-top: solid 4px #94d500;
padding-top: 10px;
padding-right: 30px;
}
I am particularly interested in the switch statement where I return an or element with passed on props.children.
Is this a good approach or is there a better way to switch which element is rendered based on a prop?
Looks fine to me. The same approach is also used for changing states to render something different.
If props.element can only be 'h1' or 'h2' (two possible values) I'd rather use ternary conditional statements instead of a switch statement.
Is something like this looks better?
function HeadingElement(props: HeadingElementProps) {
return props.element === 'h1' ? <h1 className={props.classes}>{props.children}</h1> : <h2 className={props.classes}>{props.children}</h2>
}

React JS Rendering parent does not render children again

I am having some issues with React JS rendering children when rendering the parent.
Here I am trying to implement the "Game of Life" (a project from Freecodecamp class).
I am stuck in this situation. I click on a dead cell and it becomes alive (blue). Then, suppose I want to clear the grid, that is, make all cells dead, but it doesn't work. It seems that even re-rendering the parent will not re-render the children.
Any idea?
var board = [];
var width = 80;
var length = 50;
var cells = width * length;
var states = ["alive", "dead"];
class BoardGrid extends React.Component {
constructor(props) {
super(props);
//this.initBoardArray = this.initBoardArray.bind(this);
}
render() {
//this.initBoardArray();
let boardDOM = this.props.board.map(function(cell) {
return <BoardGridCell status={cell.status} id={cell.id} />;
});
return (
<div id="game-grid">
{boardDOM}
</div>
);
}
}
class BoardGridCell extends React.Component {
render() {
return (
<div
id={this.props.id}
className={`cell ${this.props.status}`}
data-status={this.props.status}
/>
);
}
}
function initBoard() {
for (let cellIndex = 0; cellIndex < cells; cellIndex++) {
let cell = { id: cellIndex, status: "dead" };
board[cellIndex] = cell;
}
}
function drawBoard() {
ReactDOM.render(
<BoardGrid board={board} />,
document.getElementById("game-grid-wrapper")
);
}
function clearBoard() {
for (let cellIndex = 0; cellIndex < cells; cellIndex++) {
let cell = { id: cellIndex, status: "dead" };
board[cellIndex] = cell;
}
}
$("#game-grid-wrapper").on("click", ".cell", function() {
let currentState = $(this).attr("data-status");
let currentStateIndex = states.indexOf(currentState);
let newState = states[(currentStateIndex + 1) % 2];
$(this).attr("class", `cell ${newState}`);
$(this).attr("data-status", newState);
});
$("#stop").on("click", function() {
alert("clearing");
clearBoard();
drawBoard();
});
initBoard();
drawBoard();
html,
body {
height: 100%;
text-align: center;
font-family: 'Open Sans', sans-serif;
}
h1,
h2 {
font-family: 'Press Start 2P', cursive;
}
.button {
width: 100px;
border: 1px solid #555;
padding: 5px;
margin: 5px;
cursor: pointer;
border-radius: 4px;
}
.button:hover {
opacity: 0.9;
}
#main {
margin: 10px;
}
#game-grid {
background-color: #000;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
width: 800px;
height: 500px;
overflow: hidden;
}
#game-grid .cell {
border: 1px solid #767676;
width: 10px;
height: 10px;
color: #fff;
font-size: 9px;
box-sizing: border-box;
}
.alive {
background-color: #2185d0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">
<div id="game-actions">
<div id="start" class="button"><i class="fa fa-play"></i> Start</div>
<div id="pause" class="button"><i class="fa fa-pause"></i> Pause</div>
<div id="stop" class="button"><i class="fa fa-stop"></i> Stop</div>
</div>
<div id='game-grid-wrapper'></div>
</div>
You should not use jQuery together with React if you don't have to. Both manipulate the DOM but based on different information which can make them interfere in an unexpected way.
For your board state you should use the state of your BoardGrid component. Initialize your state in the constructor and add an onClick() callback to each cell when rendering it.
When the cell is clicked call the callback function given by the board component and pass it's id with it. Use that id to update the board state using setState() in your BoardGrid component.
I can add some example code later, if you struggle with anything.

Categories

Resources