responsive div height on conditional rendering? - javascript

I am working on a react project, where I want to conditionally render a div above an existing div that currently covers the screen with an image. I would like the existing div to reduce in height, by shrinking the size of the image, when the second div conditionally renders. The second div can have a varied height. ( e.g. list of items).
This is simplified version of what I have in my App so far:
function App() {
const [show, setShow] = useState(false);
return (
<div
style={{
border: "solid",
width: "100%",
maxHeight: "100vh"
}}
>
{show && (
<div>
<div style={{ height: "300px", backgroundColor: "red" }}></div>
<button
onClick={() => {
setShow(false);
}}
>
Close
</button>
</div>
)}
<div
style={{
display: "flex",
justifyContent: "center"
}}
>
<img
src="https://i.imgur.com/LiAUjYw.png"
alt="test"
style={{
maxWidth: "100%",
height: "auto"
}}
/>
</div>
<button onClick={() => setShow(true)}>SHow</button>
</div>
);
}
It initially looks like this: https://imgur.com/a/R0e74Xk
And on clicking the 'Show' button, it looks like this: https://imgur.com/a/Iy6osio
As you can see the bottom div gets shifted down, and goes over the container div.
I was wondering how I can make the div responsive enough to change its height when the other element is rendered.
I am fairly new to styling so any help on this would be greatly appreciated.
Thank you.

Here's one simple idea. See how the style is given to the divs like so:
<div
style={{
display: "flex",
justifyContent: "center"
}}
>
Note how the style has this weird {{ }} notation. That's because style accepts an object, and the object here is:
{
display: "flex",
justifyContent: "center"
}
You could take that object and save it as a variable somewhere, for example:
const styleWhenShow = {
display: "flex",
justifyContent: "center",
height: "100px"
}
const styleWhenNoShow = {
display: "flex",
justifyContent: "center",
height: "500px"
}
Since you already have your show state, now it's just a matter of replacing the style prop with:
style={show ? styleWhenShow : styleWhenNoShow}
In case you don't want to repeat some common styles between the show and noshow styles, you can also do something like:
const commonStyle = {
display: "flex",
justifyContent: "center"
}
const styleWhenShow = {
height: "100px"
}
const styleWhenNoShow = {
height: "500px"
}
And set the style prop like:
style={show ? { ...commonStyle, ...styleWhenShow } : { ...commonStyle, ...styleWhenNoShow }}

Related

Left space between two texts (Dynamic)

const Btn = () => {
const options = ['test1', 'test2', 'test3'];
return (
<div style={{ position: 'absolute', left: '8px', widht: 'auto', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', backgroundColor: '#006C84' }}>
{options.map(opt => (
<span style={{ paddingRight: '10px' }}>{opt}</span>)
)}
</div>
)
}
Above is my code and after the end of the text, there is some extra space are left. How to remove that space.
So you are giving paddingRight: 10px to the span, so at the end of the last child it's showing some space left.
There are two ways you can achive this
JS way
Css way
JS way
{options.map((opt,index) => (
<span style={{ paddingRight: options.length - 1 === index ? '10px' : "0px" }}>{opt}</span>)
)}
Css way
you need to change the inline style to explicit style for this, I would say this is the recommended way of giving css over inline style or may be you can create one style object for that.
<div className="parent">
{options.map(opt => (
<span style={{ paddingRight: '10px' }}>{opt}</span>)
)}
</div>
.parent{//parent css goes here}
.parent span:not(::last-of-type){padding-right: 10px}
const Btn = () => {
const options = ['test1', 'test2', 'test3'];
return (
<div style={{ position: 'absolute', left: '8px', widht: 'auto', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', backgroundColor: '#006C84', display: 'flex', gap: '10px' }}>
{options.map(opt => (
<span>{opt}</span>)
)}
</div>
)
}
use display: flex and gap inside the parent div style
https://codesandbox.io/s/keen-ganguly-kl4ys7?file=/src/App.js

How can I animate a flex container with a conditional react-component inside?

I have a table with a custom right side control, where the user can set the columns visibility as well as reorder them. Upon clicking a button, the control expands and the user can now make changes. The basic functionality is there. I am now trying to make the transition a smoother and that is where I am failing.
Here is the minimal markup that is rendered:
<div
style={{
display: "flex",
justifyContent: "flex-start"
}}
>
<div style={{ flex: "auto" }}>
<Table columns={columns} dataSource={data} pagination={"false"} />
</div>
<div
style={{
flex: hasExpandedControls ? "2 1 1" : "0 0 1",
transition: "all 500ms ease-in-out"
}}
>
<div
style={{
display: "flex"
}}
>
<div>
{hasExpandedControls ? (
<Tree
checkable
draggable
treeData={columnsTree}
checkedKeys={visibleColumns.map((col) => col.key)}
onCheck={handleCheck}
onDrop={handleDrop}
style={{ marginRight: "0.5em" }}
/>
) : null}
</div>
<div>
<div style={{ padding: "50% 0", height: 0, width: "40px" }}>
<Button
onClick={() => setHasExpandedControls((prev) => !prev)}
style={{
marginLeft: ".5em",
display: "block",
transformOrigin: "top left",
// transform: 'rotate(-90deg) translate(-100%)',
transform: "rotate(90deg) translate(0, -100%)",
marginTop: "-50%"
}}
>
Spalten
</Button>
</div>
</div>
</div>
</div>
Clicking the button triggers a state change, which causes the Tree to be rendered and in this instance the div 'snaps' to its full width. I am failing to accomplish my goal using css transitions, so I was wondering if there was an easier way to accomplish my goal.
I've tried setting the transition on various wrapping elements and all elements, but the 'snap' into existence remains.
Codepen to reproduce: https://codesandbox.io/s/basic-usage-antd-4-17-3-forked-yq2u7?file=/index.jshttps://codesandbox.io/s/gu4hs

Check if the css class is changed when a Tab is clicked

I'm using Tab from Semantic UI for the top of a table. It contains more tabs and I noticed in Developer tools that the selected one contains beside its normal class elements a new element active.
This is the code:
const panes = [
{
menuItem: (
<Menu.Item
className="tab-title"
key="Reference"
style={{
display: 'block',
background:'url(https://svgshare.com/i/Nnu.svg) left center no-repeat',
backgroundSize: 'cover',
minWidth: 292,
borderColor: 'transparent',
}}>
<p>Reference</p>
</Menu.Item>
),
render: () => <Tab.Pane>{referenceTab}</Tab.Pane>,
},
{
menuItem: (
<Menu.Item
className="tab-title"
key="List"
style={{
display: 'block',
background:'url(https://svgshare.com/i/Nnu.svg) left center no-repeat',
backgroundSize: 'cover',
minWidth: 300,
borderColor: 'transparent',
}}>
<p>List</p>
</Menu.Item>
),
render: () => <Tab.Pane>{listTab}</Tab.Pane>
},
];
return (
<Layout>
<TabContainer panes={panes} />
</Layout>
);
And in inspect mode, the selected tab has this class: active item tab-title while the non-selected one has item tab-title.
Is there a way to use this in code? For example I want to change the background url with another one if class contains active.
You can use Element.classList.contains("active").
MDN for classList. MDN for contains
Edit:
Alternatively, you could add css which would look like
.active.tab-title {
background-color:red;
}
Try something like this for setting a css property dynamically.
style={{
background-color: ${active ? 'red' : 'blue'},
}}>

Passing values from database to flip cards in JS React

I am trying to pass my API value to the Flippy card to dynamically render image and the word displayed on flip.
I am struggling with something I thought would be easy: I have an API where I call back an image link and a word to populate the flip cards. I want to dynamically render my flip cards to the page using that data, but right now my image link and the word are hard coded and I don't know to pass my API call values to the component. API works and correctly returns links and words; hard coded flip card also works with no issues and functions as intended. I believe I have to implement props and pass it somehow to the const DefaultCardContents- thanks so much for taking a stab at it.
I tried implemeneting props but I am unsure how to pass them to the constant
THIS IS MY FLASHCARD COMPONENT
import React, { Component } from 'react';
import Flippy, { FrontSide, BackSide } from './../lib';
import Button from 'react-bootstrap/Button';
import './style.css';
const FlippyStyle = {
width: '200px',
height: '200px',
textAlign: 'center',
color: '#FFF',
fontFamily: 'sans-serif',
fontSize: '30px',
justifyContent: 'center'
}
const DefaultCardContents = ({ children }) => (
<React.Fragment>
<FrontSide
style={{
backgroundColor: 'white',
display: 'flex',
alignItems: 'center',
flexDirection: 'column'
}}
>
<img
src="https://parent.guide/wp-content/uploads/2014/08/Banana-baby-food-recipes.jpg"
style={{ maxWidth: '100%', maxHeight: '100%' }}
/>
<span
style={{
fontSize: '12px',
position: 'absolute',
bottom: '10px',
width: '100%'
}}>
{children}<br />
Hover over to show key word
</span>
</FrontSide>
<BackSide
style={{
backgroundColor: '#EB6864',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column'
}}>
<h1>Banana</h1>
<span
style={{
fontSize: '12px',
position: 'absolute',
bottom: '10px',
width: '100%'
}}>
{children}<br />
<Button variant="success">Success</Button>
<Button variant="outline-warning">Warning</Button>
</span>
</BackSide>
</React.Fragment>);
const FlippyOnHover = ({ flipDirection = 'vertical' }) => (
<Flippy
flipOnHover={true}
flipDirection={flipDirection}
style={FlippyStyle}
>
<DefaultCardContents>
</DefaultCardContents>
</Flippy>
);
class Flashcard extends Component {
constructor(props) {
super(props);
this.state = {
isFlipped: false
}
}
render() {
return (
<div className="App">
<div style={{ display: 'flex', flex: '1 0 200px', justifyContent: 'space-around', 'flex-wrap': 'wrap' }}>
<FlippyOnHover flipDirection="horizontal" />
</div>
</div>
);
}
}
export default Flashcard;
THIS IS MY API CALL AND PAGE RENDER
import React, { Component } from "react";
import Flashcard from "../components/Flashcard";
import API from "../utils/API";
class Flashcards extends Component {
state = {
flashcards: [],
flashcardName: "",
flashcardImage: "",
flipped: false
};
componentDidMount() {
this.loadFlashcards();
};
loadFlashcards = () => {
API.getFlashcards()
.then(res => {
// console.log(res.data);
this.setState({ flashcards: res.data, flashcardName: "", flashcardImage: "" })
// console.log("flashhhhhhhhhh" + JSON.stringify(this.state.flashcards));
})
.catch(err => console.log(err));
};
flipped = () => {
console.log(this.state)
if (this.state.flipped === false) {
this.setState({ flipped: true })
}
}
render() {
return (
<div>
{this.state.flashcards.length ? (
<div>
{this.state.flashcards.map(flashcards => (
<div key={flashcards._id} >
<Flashcard/>
</div>
))}
</div>
) : (
<h3>No Results to Display</h3>
)}
</div>
)
}
}
export default Flashcards;
Expected result is a dynamically generated array of cards base don API
You need to modify your Flashcard component to accept props for the title and the URL for the image, assuming that is in the data returned from the API.
In your map, pass the values to the Flashcard, something like:
{this.state.flashcards.map(flashcard => (
<div key={flashcard._id}>
<Flashcard
title={flashcard.title}
imageUrl={flashcard.image}
/>
</div>
))}
Edit:
Looks like your are using the react-flippy library, so you don't need to manage what state the flash card is in.
Crudly, your FlashCard component could look something like:
import React, { Component } from "react";
import Flippy, { FrontSide, BackSide } from "./../lib";
import Button from "react-bootstrap/Button";
import "./style.css";
const FlippyStyle = {
width: "200px",
height: "200px",
textAlign: "center",
color: "#FFF",
fontFamily: "sans-serif",
fontSize: "30px",
justifyContent: "center"
};
const CardContents = ({ title, imageUrl }) => (
<React.Fragment>
<FrontSide
style={{
backgroundColor: "white",
display: "flex",
alignItems: "center",
flexDirection: "column"
}}
>
<img
src={imageUrl}
style={{ maxWidth: "100%", maxHeight: "100%" }}
/>
<span
style={{
fontSize: "12px",
position: "absolute",
bottom: "10px",
width: "100%"
}}
>
<br />
Hover over to show key word
</span>
</FrontSide>
<BackSide
style={{
backgroundColor: "#EB6864",
display: "flex",
alignItems: "center",
justifyContent: "center",
flexDirection: "column"
}}
>
<h1>{title}</h1>
<span
style={{
fontSize: "12px",
position: "absolute",
bottom: "10px",
width: "100%"
}}
>
<br />
<Button variant="success">Success</Button>
<Button variant="outline-warning">Warning</Button>
</span>
</BackSide>
</React.Fragment>
);
class Flashcard extends Component {
render() {
return (
<div>
<div
style={{
display: "flex",
flex: "1 0 200px",
justifyContent: "space-around",
"flex-wrap": "wrap"
}}
>
<Flippy flipOnHover={true} flipDirection='horizontal' style={FlippyStyle}>
<CardContents imageUrl={this.props.imageUrl} title={this.props.title}/>
</Flippy>
</div>
</div>
);
}
}
export default Flashcard;

Dynamic positioning of a React JSX button

I am working with React and I need put this button like that:
but I have a big problem because I am forbidden to use margin or other form of positional where I have values with px, or rem, basicaly static positional. Now, I have this code
<div style={{ float: 'right' }}>
<ActionButton style={{
display : 'flex',
flex : '1',
flexDirection: 'row',
alignItems : 'center',
justifyContent : 'center',
height: '40px',
width: '151px',
}} name={<div style = {{
display : 'flex',
flex : '2',
backgroundColor : 'red',
float : 'right'
}}>FILTRAR</div>}
icon={<div style = {{
display : 'flex',
flex : '2',
backgroundColor : 'blue',
width: '24px',
height: '24px',
float : 'left'}}><FilterIcon/></div>}>
</ActionButton>
</div>
And now my button is like that
Parent's styles should already align children (name and icon) by the center, not by the top. And yes, no positioning styles are required, unless there are hardcoded heigh/margin values in children itself.
Desired behavior can be broken because of the implementation of ActionButton.
Anyway, my suggestions to tackle this issue:
use flex-direction: 'row-reverse' instead of floats;
remove display: 'flex' from child elements.
Here is a simplified and working example for web https://codesandbox.io/s/62nynm1wzk, hope it can help you to get started.
Update: here is an example using button instead of div as container, since it is closer to original markup: https://codesandbox.io/s/y7q1vlwkwj
You can use margin in % (ie: 50%) and then use transforms, in percent (ie: transform: translateX(-50%), to adjust position. If you can provide a static stylable html I can produce an example for you.
Try to remove the flex and display style property in name and icon props like below and check it. Because flex:2 will change the width of child element with equal spacing.
<div style={{ float: 'right' }}>
<ActionButton style={{
display : 'flex',
flex : '1',
flexDirection: 'row',
alignItems : 'center',
justifyContent : 'center',
height: '40px',
width: '151px',
}} name={<div style = {{
backgroundColor : 'red',
float : 'right'
}}>FILTRAR</div>}
icon={<div style = {{
backgroundColor : 'blue',
width: '24px',
height: '24px',
float : 'left'}}><FilterIcon/></div>}>
</ActionButton>
</div>

Categories