I have a list of squared items displayed in a grid. When I click on one of the items, I want to display a section below it that takes the full width of the parent container.
Images for clarity:
Collapsed grid
With expanded section (the content will change depending on the item selected, but the size will be the same)
Simplified code:
const MyItems = () => {
const Item = (props) => {
return (
// squared item
)
}
return (
<div className="container">
{itemList.map((item, index) => (
<Item key={index} {...item} />
))}
</div>
)
}
Styling of div container:
flex: 1;
display: grid;
grid-template-columns: repeat(auto-fill, 150px);
grid-template-rows: repeat(auto-fill, auto);
grid-gap: 1rem;
justify-content: space-evenly;
What I tried:
I tried adding another container below the item, like so
<div className="container">
{itemList.map((item, index) => (
<div key={index}>
<Item {...item} />
<div className="expanded-section">
// content
</div>
</div>
))}
</div>
and added width: 100% and height: 300px to the expanded container but of course the width is limited by the grid item and won't take the width of the parent, so this is the result.
How can I achieve my goal using just CSS, if possible?
Related
I am trying to do a horizontal slider with buttons. So i have 6 cards and 6 dot buttons. As i click for example in the second button with id of 1 it should slide to the second image. But fore some reason the scroll event is not working.
This is my css:
#media only screen and (max-width: 810px){
.fake-magicwall ul{
overflow: hidden;
overflow-x: auto;
flex-wrap: nowrap;
}
.dot{
width: 15px;
height: 15px;
border-radius: 50%;
margin: 10px;
}
}
And this is my component:
import React from 'react'
import './Work.css'
import Cta from '../atoms/Cta'
import Headphone from '../assets/images/headphones3.png'
import Calendar from '../assets/images/calendar.png'
import DevConnector from '../assets/images/dev.png'
import Bulls from '../assets/images/bulls2.png'
import Expenses from '../assets/images/reactExpenses.png'
import SixFlags from '../assets/images/sixflags.png'
import useDots from '../hooks/useDots';
const images = [Calendar, Headphone, SixFlags, DevConnector, Bulls, Expenses];
const Work = () => {
const [activeIndex, setActiveIndex] = useDots(images.length);
function handleDotClick(index) {
console.log("Clicked dot with index:", index);
setActiveIndex(index);
const targetCard = document.querySelector(`[data-id="${index}"]`);
console.log("Target card:", targetCard);
if (targetCard) {
const magicWall = document.getElementById('home-magicwall');
console.log("Magic wall:", magicWall);
console.log("Target card offsetLeft:", targetCard.offsetLeft);
console.log("Magic wall offsetLeft:", magicWall.offsetLeft);
magicWall.scroll({
left: targetCard.offsetLeft - magicWall.offsetLeft,
behavior: 'smooth'
});
}
}
return (
<div className="main-container">
<section id="section-work">
<div id="header">
<h2>My Work</h2>
</div>
<div className="text-zone-2">
<div>
<p>
A small gallery of recent projects chosen by me. I've done them all together with amazing people from
companies around the globe. It's only a drop in the ocean compared to the entire list. Interested to see
some more? Visit my work page.
<br />
</p>
</div>
<div className="btn-container">
<Cta className="btn" link="https://github.com/mateoghidini1998" content="See More" />
</div>
</div>
<div className="fake-big-2">Work</div>
</section>
<div className="dots">
{images.map((image, index) => (
<button
key={index}
className={`dot ${index === activeIndex ? 'active' : ''}`}
onClick={() => handleDotClick(index)}
></button>
))}
</div>
<div id="home-magicwall" className="fake-magicwall">
<ul>
{images.map((image, index) => (
<li key={index} className={`magic-wall_item ${index === activeIndex ? 'active' : ''}`}>
<img src={image} alt="image" />
<div className="hover-content"></div>
<a href="/" className="colorbox" data-id={index}></a>
</li>
))}
</ul>
</div>
</div>
);
};
export default Work;
This is my custom hook:
import { useState } from 'react';
function useDots(images) {
const [activeIndex, setActiveIndex] = useState(0);
return [activeIndex, setActiveIndex];
}
export default useDots;
I did some console log and the index of the dot i am clicking is correct.
The data-id on the <a> tags is correct.
I also get Target card offsetLeft: 133
and Magic wall offsetLeft: 0 Every time i click in a button
Any reason why it is not scrolling?
You have applied the overflow-x: auto to the ul element inside of the home-magicwall div and not the home-magicwall div itself so that technically isn't scrollable. Try the ul instead:
if (targetCard) {
const magicWall = document.querySelector('.home-magicwall ul');
console.log("Magic wall:", magicWall);
console.log("Target card offsetLeft:", targetCard.offsetLeft);
console.log("Magic wall offsetLeft:", magicWall.offsetLeft);
magicWall.scroll({
left: targetCard.offsetLeft - magicWall.offsetLeft,
behavior: 'smooth'
});
}
I am trying to have my React components that are created based on user input stacked like this:
|Component|
|Component|
|Component|
|Component|
|Component|
I want them to to be in the middle of the page, and stacked on on top of the other. However, they are currently ending up on the left side stacked like that.
This is my CSS code:
.ingredients-input-bar {
display: flex;
background-color: rgb(71, 79, 105);
justify-content: center;
width: 400px;
}
This is the code for the input bar:
return (
<div className='ingredients-input-bar'>
<form>
<input
type='number'
id='quantity'
value={quantity}
onChange={getVariables}
/>
</form>
<h3>New Quantity: {fractionalize( ( (props.newServings / props.originalServings) * quantity) )}</h3>
</div>
And this code builds all of the input bars:
export const IngredientsInput = (props) => {
let inputBars = [];
for (let i = 0; i < props.numIngredients; i++) {
inputBars.push(
<IngredientsInputBar
newServings={props.newServings}
originalServings={props.originalServings} />);
}
return inputBars;
First of all you shouldn't render elements like that. You should use .map function in react. Here you can read more about it.
This should work.
const numIngredients = new Array(5).fill("");
This line just creates and array of empty strings depending on a number. In your code it will be props.numIngredients
const numIngredients = new Array(5).fill("");
return (
<div
style={{
display: "flex",
margin: "auto",
flexDirection: "column",
width: "400px"
}}
>
{numIngredients.map(() => (
<div style={{ display: "flex" }}>One div</div>
))}
</div>
);
I created working example for you with flex - https://codesandbox.io/s/react-functional-component-forked-k1owj?file=/src/index.js
I have an array like this:
const arr = [1,2,3,4,5,6,7,8,9,10]
And I want to place them as 2 per one line in two dimensional divs. Let me illustrate what im trying to say:
This is main React element:
render() {
return <div className="base">{...}</div>
}
Expected Result:
render() {
return <div className="base">
<div className="container">
<div className="left">
<span>1</span>
<span>2</span>
</div>
<div className="right">
<span>3</div>
<span>4</div>
</div>
</div>
<div className="container">
<div className="left">
<span>5</span>
<span>6</span>
</div>
<div className="right">
<span>7</div>
<span>8</div>
</div>
</div>
<div className="container">
<div className="left">
<span>9</span>
{/* no 10th element because 9 is the last. */}
</div>
{/* no right div because no more elements. */}
</div>
</div>
}
If this was a typical odd even situation, I would do i % 2 here. However, how am i going to close div tag on odd or even? But it is a bit more complicated than that. Is there any way to achieve this in React? I'm using TypeScript by the way and latest ReactJS.
In order to render more advanced loops in react, you need to get used to chunking your arrays. you can simply chunk the array twice using the following function, then you can map this array recursively to render your desired output:
function chunk_array(arr,chunkSize=2) {
const result=[];
for(let i=0;i<arr.length;i+=chunkSize) {
result.push(arr.slice(i,i+chunkSize));
}
return result;
}
**NOTE: I made a JS fiddle so you can visualize how this array changes to become the shape we need to render your desired html output ** JS FIDDLE
now you can utilize this in render to accomplish what you want.
import React from 'react';
function chunk_array(arr,chunkSize=2) {
const result=[];
for(let i=0;i<arr.length;i+=chunkSize) {
result.push(arr.slice(i,i+chunkSize));
}
return result;
}
const arr = [1,2,3,4,5,6,7,8,9,10];
const chunked = chunk_array(chunk_array(arr));// we chunk array twice to get desired structure.
class Example extends Component {
render() {
return (
<div className="container">
{chunked.map((container,i)=>(
<div className="base" key={i}>
{container.map((row,i2)=>(
<div key={i2} className={(i2===0?"left":"right")}>
{row.map((item,i3)=><span key={i3}>{item}</span>)}
</div>))}
</div>))}
</div>
)
}
}
This can be done in some ways but here is a sample: https://codesandbox.io/s/distracted-joliot-lkdxq
import React from "react";
import "./styles.css";
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const Result = () => {
return (arr.map(item => {
return (
<div key={item} className="box">
{item}
</div>
);
}));
};
export default function App() {
return (
<div className="App">
<Result />
</div>
);
}
Here is the css:
.App {
display: grid;
grid-template-columns: 50px 50px;
grid-gap: 10px;
}
.box {
display: flex;
border: 1px black solid;
width: 50px;
height: 50px;
align-items: center;
justify-content: center;
}
I think this is the answer you are looking for:
https://codesandbox.io/s/goofy-snowflake-tdzjn?file=/src/App.js
import React from "react";
import "./styles.css";
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const Result = () => {
return arr.map((item, index) => {
return (
index % 2 === 0 && (
<div key={item} className="box">
<span>{arr[index]}</span>
<span>{arr[index + 1]}</span>
</div>
)
);
});
};
export default function App() {
return (
<div className="App">
<Result />
</div>
);
}
Here is the css:
.App {
display: grid;
grid-template-columns: 50px 50px;
grid-gap: 10px;
}
.box {
display: flex;
border: 1px black solid;
width: 50px;
height: 50px;
align-items: center;
justify-content: center;
}
.box span {
margin: 0 5px;
}
You could use bootstrap to do this. Put the everything inside a div className="row" and then have each of the items have className="col-6". Bootstrap cuts every row into 12 columns, so giving each element 6 of those columns will cause them to be 2 on a line as you want. You'll need to npm install bootstrap and include the appropriate script tags in your application. Take a look here, https://getbootstrap.com/docs/3.3/getting-started/. Let me know if this helps!
Whats my requirement: i have some images in my external folder and i need to import to component and display it and also have to use Virtual Scroll here i have to display 1 row in div and in that 1 row have to show 5-6 images
What i did : i consumed images using context from external folder and showed images in 1 rows in div and 5-6 images but i am facing issue unable to set it to Virtual scrolling
as i checked react-virtualized & react-window plugin but i am not sure how my data is used in that format
After using the react-tiny-virtual-list images are getting stacked
below is my code
class App extends React.Component{
state={
Response:[],
}
importAll(r) {
return r.keys().map(r);
}
componentWillMount() {
let data = this.importAll(require.context('./imageFolder/', false, /\.(png|jpe?g|svg)$/));
this.setState({ Response:data})
}
render(){
return(
<div className="container" id="imagecontainer">
<div className="viewport">
{this.state.Response.map((image, index) => <img key={index} src={image} alt="info"></img> )} }
</div>
</div>
)
}
.container {
padding: 0% 6%;
height: 400px;
}
.viewport {
height: -webkit-fill-available;
width: 100%;
border: 1px solid black;
overflow: scroll;
}
img {
height: 250px;
width: 150px;
padding: 35px;
}
After implementing React-tiny-list
<div id="container">
<div id="viewport">
<VirtualList
height='400px'
width='100%'
itemCount={this.state.items.length}
itemSize={20} // Also supports variable heights (array or function getter)
style={{padding:'20px'}}
renderItem={({index, style}) =>
<div key={index} style={style}>
<img key={index} src={this.state.items[index]} alt="info"></img>
</div>
}
/>
</div>
</div>
you can also use the https://github.com/bvaughn/react-virtualized plugin in this if you want to display this as table you can choose list or you can choose grid also .For you requirement i recommend using Masonry from 'react-virtualized';
below is the sample for displaying
import React from 'react';
import {
CellMeasurer,
CellMeasurerCache,
createMasonryCellPositioner,
Masonry
} from 'react-virtualized';
import 'react-virtualized/styles.css';
var images = [];
const columnWidth = 250;
const defaultHeight = 260;
const defaultWidth = columnWidth;
const cache = new CellMeasurerCache({
defaultHeight,
defaultWidth,
fixedWidth: true
})
// Our masonry layout will use 3 columns with a 10px gutter between
const cellPositioner = createMasonryCellPositioner({
cellMeasurerCache: cache,
columnCount: 4,
columnWidth,
spacer: 27
})
function cellRenderer ({ index, key, parent, style }) {
const datum = images[index]
const height = columnWidth || defaultHeight ;
return (
<CellMeasurer
cache={cache}
index={index}
key={key}
parent={parent}
>
<div style={style}>
<img
src={datum}
style={{
height: height,
width: columnWidth,
display: "block"
}}
alt="info"
/>
</div>
</CellMeasurer>
)
}
class Grid extends React.Component{
importAll(r) {
return r.keys().map(r);
}
componentWillMount() {
images = this.importAll(require.context('../imageFolder/', false, /\.(png|jpe?g|svg)$/));
}
render(){
return(
<div id="container">
<div id="viewport">
<Masonry
cellCount={images.length}
cellMeasurerCache={cache}
cellPositioner={cellPositioner}
cellRenderer={cellRenderer}
height={400}
width={1320}
/>
</div>
</div>
)
}
}
export default Grid;
I hope this will resolve your issue
If you're having trouble implementing the virtual scroll, note that the order of the imports is important when doing this, so pay heed to this - it could be contributing to your issue. (An aside: There is an npm plugin for implementing a virtual list.)
An overview of the import order for virtual scroll is:
import * as React from 'react';
import Paper from '#material-ui/core/Paper';
import {
Grid,
VirtualTable,
TableHeaderRow,
} [from material ui];
import {
your-components
} from 'your-path';
(above is non-specific, just a rough guide to the order)
You could also use a ScrollView if you are unable to implement a "Virtual scroll".
The following style will give you a horizontal scroll (as opposed to the default vertical), to enable you to display your images in a horizontally-scrollable row
<ScrollView horizontal={true}>
Hope this helps
I'm trying to create a header and sidebar where the header sits just left of the sidebar and not overlayed like the image below.
I've created the Sidebar and Header components separately in different files and am importing both to render into an App.js file like below:
App.js:
render() {
return (
<div className='fp-wrapper'>
<AppSidebar />
<div className='fp-panel-main'>
<AppHeader />
</div>
</div>
);
}
The issue is that the two components are being rendered ontop of each other (see screenshot). Is there any way to build these components separately and have them fit together?
I have found that one way to solve this is to build the components directly to the sidebar (see Sidebar.Pusher & text Stuff) which is rendered correctly sitting right of the sidebar whereas the header is being overlapped.
Sidebar:
render() {
const { activeItem } = this.state
return (
<Sidebar.Pusher>
<Sidebar
as={Menu}
animation='push'
direction='left'
icon='labeled'
inverted
visible='true'
vertical
width='thin'
>
<Menu.Item as='a'>
<Icon name='home' />
Lexis
</Menu.Item>
<Menu.Item as='a'>
Page 1
</Menu.Item>
<Menu.Item as='a'>
Page 2
</Menu.Item>
</Sidebar>
<Sidebar.Pusher>
Stuff
</Sidebar.Pusher>
One way to do it would be wrapping the <AppSidebar> in a <div className="sidebar"> and use display: flex to display them side by side, like this.
render(){
return (
<div className='fp-wrapper'>
<div className="sidebar">
<AppSidebar />
</div>
<div className='fp-panel-main'>
<AppHeader />
</div>
</div>
);
}
and add CSS to that
.fp-wrapper {
width: 100%;
display: flex;
flex-wrap: nowrap; //This will keep the layout at all screen sizes.
}
.sidebar {
width: 30%;
}
.fp-panel-main {
width: 70%;
}
Note: You can also add a className or id to <SideBar.Pusher> and you wouldn't need to wrap the <AppSideBar /> with the <div className="sidebar">.