How to map elements into two table columns, two elements per row? - javascript

I'm trying to get the end result to look like, (following this example):
<style>
* {
box-sizing: border-box;
}
.column {
float: left;
width: 50%;
padding: 10px;
height: 300px;
}
.row:after {
content: "";
display: table;
clear: both;
}
</style>
<div class="row">
<div class="column">
<Component />
</div>
<div class="column">
<Component />
</div>
</div>
The problem is that I can't quite figure out how to do this in React. The original map function currently looks like:
<div className="main">
{
arrayOfComponents.map((({ name }), index) => (
<Component
key={`${index}-${name}`}
label={name}
/>
))
}
</div>
I've tried doing something like,
<div className="main">
{
arrayOfComponents.map((({ name }), index) => (
<div className={index % 2 === 0 ? 'row' : ''}>
<div className="column">
<Component
key={`${index}-${name}`}
label={name}
/>
</div>
</div>
))
}
</div>
But that didn't seem to work, and if it did, it seems messy. How can I go about this?
arrayOfComponents looks something like
const arrayOfComponents = [
{ name: 'abc', key: 'value1'},
{ name: 'def', key: 'value2' },
. . .
]

You just need to map over the array and return the Component wrapped in a div with className of column like this :
<div className="main">
<div className="row">
{
arrayOfComponents.map(({name},index) => (
<div className="column">
<Component
key={`${index}-${name}`}
label={name}
/>
</div>
))
}
</div>
</div>
* {
box-sizing: border-box;
}
/* Create two equal columns that floats next to each other */
.column {
float: left;
width: 50%;
padding: 10px;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
.bg{
background:orange;
border: 2px solid red;
margin: 0 auto 100px;
}
<div class="row bg">
<div class="column">
<h2>Column 1</h2>
<p>Some text..</p>
</div>
<div class="column">
<h2>Column 2</h2>
<p>Some text..</p>
</div>
</div>
<div class="bg">
<div class="column">
<h2>Column 1</h2>
<p>Some text..</p>
</div>
<div class="column">
<h2>Column 2</h2>
<p>Some text..</p>
</div>
</div>
Hope this helps !

If you specifically need additional row elements, You can use conditional rendering like this. This would create a new row after 2 columns.
<div className="main">
{
arrayOfComponents.map(({ name }, index) => index%2!=0?null:(
<div className="row">
<div className="column">
<Component
key={`${index}-${name}`}
label={name}
/>
</div>
{arrayOfComponents[index+1] && (
<div className="column">
<Component
key={`${index+1}-${arrayOfComponents[index+1].name}`}
label={arrayOfComponents[index+1].name}
/>
</div>
)}
</div>
))
}
</div>

You can use reduce for splitting your data:
const rows = arrayOfComponents.reduce((acc, rec, index) => {
if (index % 2 === 0) {
return [...acc, [rec]]
}
acc[acc.length - 1] = [...acc[acc.length - 1], rec]
return acc
}, [])
const Component = (props) => {
return <div>
<h2>{props.item.name}</h2>
<p>{props.item.key}</p>
</div>
}
const TodoApp = () => {
return rows.map((row, index)=>{
return <div key={index}>{
row.map((col) => <div className="column" key={col.name}><Component item={col}/></div>)
}</div>
})
}
See full example in playground: https://jsfiddle.net/denisstukalov/ut8x9Lkq/21/#&togetherjs=Q7jLIOHHOP

Related

Apply flex property to React mapped items

I have an array of items that I'm mapping over and would like to style them as flex wrap items so they end up in a row. However, I've tried multiple ways to go about this but the items remain listed in one column.
This is the result I'm going for: https://www.nike.com/w/mens-training-gym-shoes-58jtoznik1zy7ok
<div>
<h3>All Shoes ({allShoes.length})</h3>
{allShoes.map(item => {
return (
<section key={item.id} style={{display: 'flex'}}>
<div>
<img src={item.image} alt={item.name}/>
<p>{item.name}</p>
<p>{item.subTitle}</p>
<p>{item.price}</p>
</div>
</section>)
})}
</div>
You are creating several section elements, each containing just one div.
To get the divs displayed in a row, create one section and put multiple divs inside it. Something like this:
<div>
<h3>All Shoes</h3>
<section style="display: flex">
{allShoes.map(item => {
return (
<div key={item.id}>
<img src={item.image} alt={item.name}/>
<p>{item.name}</p>
<p>{item.subTitle}</p>
<p>{item.price}</p>
</div>)
})}
</section>
</div>
Here's what that looks like as rendered HTML:
section {
background-color: #a0a0a0;
}
section:before {
content: "section";
padding: 1rem;
color: #606060;
}
section div {
margin: 4px;
border: 1px solid gray;
background-color: white;
}
section div p {
padding-left: 0.5rem;
margin: 0;
}
<div>
<h3>All Shoes</h3>
<!-- a single section with flex -->
<section style="display: flex;">
<!-- containing multiple divs -->
<div>
<img src="https://via.placeholder.com/140" />
<p>item.name</p>
<p>item.subTitle</p>
<p>item.price</p>
</div>
<div>
<img src="https://via.placeholder.com/140" />
<p>item.name</p>
<p>item.subTitle</p>
<p>item.price</p>
</div>
<div>
<img src="https://via.placeholder.com/140" />
<p>item.name</p>
<p>item.subTitle</p>
<p>item.price</p>
</div>
</section>
</div>

React - Show/hide when mouse hovers using hooks

I'm trying to create a show/hide div for when my mouse enters and leaves the container. I was following this example here (https://upmostly.com/tutorials/react-onhover-event-handling-with-examples) but I've run into a problem when I hover my mouse over the 'delete-container' div, it disappears. This problem occurs because it takes the action onMouseLeave. Is there a way I can fix this or possibly make my solution better? Here is a demo of what is happening (https://i.gyazo.com/528d909625b6b3828325c4e62894d1c3.mp4).
import React, { useState } from 'react';
import axios from 'axios';
export default function JobInfo({ jobs_info }) {
const [isShown, setIsShown] = useState(false);
const changeBackground = (x) => {
x.target.style.backgroundImage =
'linear-gradient(to right, #4639a7, #78019c)';
};
const changeBackground2 = (x) => {
x.target.style.background = 'rgb(37, 45, 73)';
};
return (
<div className='row'>
<div
className='container'
onMouseEnter={() => setIsShown(true)}
onMouseLeave={() => setIsShown(false)}
>
<div className='row'>
<div className='job-title'>{jobs_info.title}</div>
</div>
<div className='row wrapper'>
<div className='category-title'>Category</div>
<div className='location-title'>Location</div>
<div className='type-title'>Type of Job</div>
<div className='creator-title'>Job Creator</div>
</div>
<div className='row wrapper'>
<div className='category'>{jobs_info.job_team.title}</div>
<div className='location'>
{jobs_info.job_location.title}
</div>
<div className='type'>{jobs_info.job_work_type.title}</div>
<div className='creator'>{jobs_info.user.name}</div>
</div>
</div>
<div
className='counter-container'
id='counter-container'
onMouseEnter={changeBackground}
onMouseLeave={changeBackground2}
>
Candidates <br />
{jobs_info.candidates_count}
</div>
{isShown && (
<div className='delete-container center'>
<ion-icon id='trash' name='trash'></ion-icon>
</div>
)}
</div>
);
}
The issue happens because delete-container is outside of the container hierarchy and hence mouseleave event is triggered on the container. Render the delete-container as a child of container and style it accordingly
return (
<div className='row'>
<div
className='container'
onMouseEnter={() => setIsShown(true)}
onMouseLeave={() => setIsShown(false)}
>
<div className='row'>
<div className='job-title'>{jobs_info.title}</div>
</div>
<div className='row wrapper'>
<div className='category-title'>Category</div>
<div className='location-title'>Location</div>
<div className='type-title'>Type of Job</div>
<div className='creator-title'>Job Creator</div>
</div>
<div className='row wrapper'>
<div className='category'>{jobs_info.job_team.title}</div>
<div className='location'>
{jobs_info.job_location.title}
</div>
<div className='type'>{jobs_info.job_work_type.title}</div>
<div className='creator'>{jobs_info.user.name}</div>
</div>
{isShown && (
<div className='delete-container center'>
<ion-icon id='trash' name='trash'></ion-icon>
</div>
)}
</div>
<div
className='counter-container'
id='counter-container'
onMouseEnter={changeBackground}
onMouseLeave={changeBackground2}
>
Candidates <br />
{jobs_info.candidates_count}
</div>
</div>
);
However you could simply use CSS hover event to show or hide the delete-container without the need for state after renderingdelete-containeras a child ofcontainer`
.scss
.container {
.delete-container {
display: none;
// other styles
}
&:hover {
.delete-container {
display: block;
}
}
// other styles
}

How to make image scale to the size of div [ReactJS TS]

I am currently working on a admin panel that must display a list of videos / text links on cards.
The environment that I'm working on does not use bootstrap, so I could not simply use their cards. Which meant that I had to recreate the card components.
I have made it so the cards display normally as if it is a regular card as follow:
The code for this is:
<div className="">
<Link className="styledLink" to={`adminhelpcard/${this.state.id}`}>
<div className="card">
<h5 className="card-header">{this.state.title}</h5>
<div>
<img
className="Sprite"
onLoad={() => this.setState({ imageLoading: false })}
onError={() => this.setState({ tooManyRequests: true })}
src={this.state.thumbnail}
style={
this.state.tooManyRequests
? { display: "none" }
: this.state.imageLoading
? { display: "null" }
: { display: "null" }
}
/>
</div>
<div>
{this.state.tooManyRequests ? (
<h6 className="mx-auto">
<span className="badge badge-danger mt-2"></span>
</h6>
) : null}
<div className="card-body mx-auto">
<h6 className="card-title">
{this.state.title
.toLowerCase()
.split(" ")
.map((letter) => letter.charAt(0).toUpperCase() + letter.substring(1))
.join(" ")}
</h6>
</div>
</div>
</div>
</Link>
</div>
I am trying to re-create the horizontal card (https://getbootstrap.com/docs/4.3/components/card/#horizontal)
for specifically the links containing videos. But it comes out like so
The code for the recreated attempt is as follows:
<div className="horizontalCard">
<div className="innerCard">
<div className="leftImage">
<img
className="Sprite"
onLoad={() => this.setState({ imageLoading: false })}
onError={() => this.setState({ tooManyRequests: true })}
src={this.state.thumbnail}
style={
this.state.tooManyRequests
? { display: "none" }
: this.state.imageLoading
? { display: "null" }
: { display: "null" }
}
/>
</div>
<div className="rightText">
<div className="card-body">
<h5 className="card-title">{this.state.title}</h5>
<p className="card-text">...</p>
<p className="card-text">...</p>
</div>
</div>
</div>
</div>
So my main question, is how can I go about recreating this / adjusting the image size so that it stays within the dimensions of the leftImage div.
With height: auto you can fit an image with respect to the aspect ration.
img.Sprite {
display: block; /*optional*/
width: 100%; /*optional*/
max-width: 100%;
height: auto;
border: 0;
}
The parent of the img must have a width (in %, px, em, rem, or whatever)

React.js - Close and add another row div after X items

I'm learning React.js.
I have this code:
const articles = Object
.keys(this.state.articles)
.map(key => <ArticleThumb key={key} details={this.state.articles[key]} />)
;
return (
<div className="container">
<div className="row">
{ articles }
</div>
</div>
)
Assuming I have 6 items, here is the code after rendering:
<div className="container">
<div className="row">
<div id="item1"></div>
<div id="item2"></div>
<div id="item3"></div>
<div id="item4"></div>
<div id="item5"></div>
<div id="item6"></div>
</div>
</div>
But I want to have only 3 items per line like this:
<div className="container">
<div className="row">
<div id="item1"></div>
<div id="item2"></div>
<div id="item3"></div>
</div>
<div className="row">
<div id="item4"></div>
<div id="item5"></div>
<div id="item6"></div>
</div>
</div>
What is the best way to do this?
The easy answer is to use something like lodash's chunk function https://lodash.com/docs/4.17.4#chunk
However, if you are not using npm and or do not want to add additional dependencies you are going to have to use a good old fashioned javascript for loop to perform the grouping you need.
With the markup you have you could do it via css:
.container {
display: flex;
flex-wrap: wrap;
}
.row {
width: 33%;
box-sizing: border-box;
}
I made a ArticleGrid component:
class ArticleGrid extends Component {
render() {
let articlesRows = Object
.keys(this.props.rows)
.map(key => <ArticleRow key={key} articles={this.props.rows[key]} />)
;
return (
<div id="articles" className="container">
{articlesRows}
</div>
);
}
}
export default ArticleGrid;
A ArticleRow component:
class ArticleRow extends Component {
render() {
let articles = Object
.keys(this.props.articles)
.map(key => <ArticleThumb key={key} details={this.props.articles[key]} />)
;
return (
<div className="row">
{articles}
</div>
);
}
}
export default ArticleRow;
I used lodash's chunk and values function (ty Deadron) and it's done:
let articles = _.values(this.state.articles);
let articlesRows = _.chunk(articles, 3);
return (<ArticleGrid rows={articlesRows} />);

Display div when clicking on another div

I've added three pictures. Look at them please.
What is the best solution to create something like this? I would create after each row a big container and this container is collapsed. After clicking on one of the 3 overlying containers I would fill the container with the text and show it. But what happens when the display can't show 3 divs in a row, because I will use flex boxes? Is there a better solution with much less jquery?
Maybe something like this is a good place to start:
https://jsfiddle.net/547ec3bx/
HTML
<div class="wrapper">
<div class="row">
<div class="element">
</div>
<div class="element">
</div>
<div class="element">
</div>
</div>
<div class="row">
<div class="element">
</div>
<div class="element">
</div>
<div class="element">
</div>
</div>
<div class="row">
<div class="element">
</div>
<div class="element">
</div>
<div class="element">
</div>
</div>
</div>
Javascript
document.querySelectorAll('.row').forEach((element, index) => {
element.style.order = index * 2;
});
document.querySelectorAll('.element').forEach(element => {
element.addEventListener('click', event => {
var newRow = document.createElement('div');
newRow.classList.add('row');
newRow.style.order = +event.currentTarget.parentNode.style.order + 1;
var newElement = document.createElement('div');
newElement.classList.add('element');
newRow.appendChild(newElement);
event.currentTarget.parentNode.parentNode.appendChild(newRow);
});
});
CSS
.element {
min-width: 100px;
height: 50px;
flex: 1;
border: 1px solid black;
flex-wrap: nowrap;
}
.row {
width: 350px;
display: flex;
flex-direction: row;
}
.wrapper {
display: flex;
flex-direction: column;
}
you can use javascript to solve this problem , like this ....
html code:
<div id="a">
<h3>wellcome</h3>
</div>
<div id="b">
<h3>hello...</h3>
</div>
javascript code in external file
function goB()
{
document.getElementById("a").style.display="none";
document.getElementById("b").style.display="block";
}
function
{
document.getElementById("b").style.display="none";
document.getElementById("a").style.display="block";
}
Add a div and set its display to Hidden.
Get onclick event for a particular div and set the display property to block
window.addEventListener("load", function(){
document.getElementById("click").onclick = function(){
document.getElementById("div-1").style.display = "block";
};
});
.row
{
width:100%;
}
.one-third
{
width:33.33333%;
float:left;
padding:30px 0;
}
.full-width
{
display:none;
width:100%;
text-align:center;
}
<div class=wrap>
<div class="row">
<div id="click" class="one-third">
Column 1-Click Me
</div>
<div class="one-third">
Column 2
</div>
<div class="one-third">
Column 3
</div>
</div>
<div class="full-width" id="div-1">Full width Div</div>
<div class="row">
<div class="one-third">
Column 1
</div>
<div class="one-third">
Column 2
</div>
<div class="one-third">
Column 3
</div>
</div>

Categories