Is there a way to update the children before the framer motion disassembles the items? - framer-motion

I would like to update the content and the border before the element dismounts.
https://codesandbox.io/s/agitated-cerf-siq8e?file=/src/App.js

You are never allowing the content to change as you are testing for true only:
{isOpen && (
The value will never be false within the component.
You can simplify the example by removing AnimatedPresense and introducing useCycle to cycle efficiently between animation variants.
import { motion, useCycle } from "framer-motion";
import * as React from "react";
const variants = {
open: {
height: "auto" ,
transition: { duration: 0.8, ease: "easeInOut" }
},
collapsed: {
height: 0 ,
transition: { duration: 0.8, ease: "easeInOut" }
}
}
export default function App() {
const [variant, toggleVariant] = useCycle('open', 'collapsed');
return (
<div className="App">
<motion.section
style={{ overflow: "hidden" }}
variants={variants}
animate={variant}
>
<div
style={{
border: `5px solid ${variant === 'open' ? "red" : "blue"}`,
height: 100
}}
>
{variant}
</div>
</motion.section>
<button onClick={toggleVariant}>
{variant === 'open' ? 'collapse me': 'expand me'}
</button>
</div>
);
}
Codesandbox

Related

Change the background color of the draggable allotment pane

I have made a draggable split panel by https://github.com/johnwalley/allotment.
I would like to make the background of the pane below green. But after dragging the split, the background color is not systematically updated in that area.
Does anyone know how to amend the code to achieve that?
https://codesandbox.io/s/reset-forked-rfifun?file=/src/App.js
import React from "react";
import { Allotment } from "allotment";
import "allotment/dist/style.css";
import styles from "./App.module.css";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
toExpand: true
};
this.myRef = React.createRef();
}
handleChange = (sizes) => {
if (sizes.length > 1) {
if (sizes[1] < 31) {
this.setState({ toExpand: true });
} else {
this.setState({ toExpand: false });
}
}
};
render() {
return (
<div>
<div className={styles.container}>
<Allotment vertical onChange={this.handleChange} ref={this.myRef}>
<Allotment.Pane>Main Area</Allotment.Pane>
<Allotment.Pane preferredSize="0%">
<div
style={{ backgroundColor: "green" }}
onClick={() => {
if (this.state.toExpand) {
this.myRef.current.resize([50, 50]);
} else {
this.myRef.current.resize([10000, 0]);
}
}}
>
Console
{this.state.toExpand ? "ArrowUp" : "ArrowDown"}
</div>
</Allotment.Pane>
</Allotment>
</div>
</div>
);
}
}
It seems that the content div can be set to take full height of the panel:
Forked demo with modification: codesandbox
<Allotment.Pane preferredSize="0%">
<div
// 👇 Set the content div to take full height
style={{ backgroundColor: "green", height: "100%" }}
onClick={() => {
if (this.state.toExpand) {
this.myRef.current.resize([50, 50]);
} else {
this.myRef.current.resize([10000, 0]);
}
}}
>
Console
{this.state.toExpand ? "ArrowUp" : "ArrowDown"}
</div>
</Allotment.Pane>;

crop image with react - customize react-easy-crop styling

I'm trying to make a very simple react component that would crop images with react-easy-crop. Apparently it is possible to customize the style of react-easy-crop module with style prop that takes 3 objects: containerStyle, mediaStyle and cropAreaStyle.
This is the default layout:
I want to expand cropArea to full width of its container and to fit media in it by height (so that we don't see the part of the original image outside of cropArea) but can't figure out how to do it. The cropAreaStyle object doesn't seem to affect width or height since it is calculated and injected in the module file (even after setting disableAutomaticStylesInjection to true).
import React from 'react'
import ReactDOM from 'react-dom'
import Cropper from 'react-easy-crop'
import './styles.css'
class App extends React.Component {
state = {
imageSrc:
'https://img.huffingtonpost.com/asset/5ab4d4ac2000007d06eb2c56.jpeg?cache=sih0jwle4e&ops=1910_1000',
crop: { x: 0, y: 0 },
zoom: 1,
aspect: 1 / 1,
style: { containerStyle: { position: "absolute", top: "0", width: "calc(100% - 2px)", height: window.innerWidth, overflow: "hidden", border: "1px solid black" },
mediaStyle: { height: "100%", display: "block" },
cropAreaStyle: {position: "absolute", top: "0", border: "1px solid black", width: "100%", height: "100%" }}
}
onCropChange = (crop) => {
this.setState({ crop })
}
onCropComplete = (croppedArea, croppedAreaPixels) => {
console.log(croppedArea, croppedAreaPixels)
}
onZoomChange = (zoom) => {
this.setState({ zoom })
}
render() {
return (
<div className="App">
<div className="crop-container">
<Cropper
image={this.state.imageSrc}
crop={this.state.crop}
zoom={this.state.zoom}
aspect={this.state.aspect}
onCropChange={this.onCropChange}
onCropComplete={this.onCropComplete}
onZoomChange={this.onZoomChange}
style={this.state.style}
disableAutomaticStylesInjection={true}
/>
</div>
</div>
)
}
}
const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)
This is what I'm trying to achieve:
The black square is cropArea that I can't resize...
I want cropArea to remain square.
Is there an easy way to do this, without changing the module file?
The solution with another module is acceptable also
Thanks in advance
I tried to use the object cropAreaStyle but it's not working, instead use the prop cropSize and don't pass the prop aspect.
In order to get the height of the media pass the prop onMediaLoaded:
onMediaLoad = (mediaSize) => {
this.setState({
cropHeight: mediaSize.height,
});
};
App.js
import React from 'react';
import ReactDOM from 'react-dom';
import Cropper from 'react-easy-crop';
import './style.css';
class App extends React.Component {
state = {
imageSrc:
'https://img.huffingtonpost.com/asset/5ab4d4ac2000007d06eb2c56.jpeg?cache=sih0jwle4e&ops=1910_1000',
crop: { x: 0, y: 0 },
zoom: 1,
cropHeight: 0,
};
onCropChange = (crop) => {
this.setState({ crop });
};
onCropComplete = (croppedArea, croppedAreaPixels) => {
console.log(croppedArea, croppedAreaPixels);
};
onZoomChange = (zoom) => {
this.setState({ zoom });
};
onMediaLoad = (mediaSize) => {
this.setState({
cropHeight: mediaSize.height,
});
};
render() {
const cropSize = {
height: `${this.state.cropHeight}px`,
width: '100%',
};
return (
<div className="App">
<div className="crop-container">
<Cropper
image={this.state.imageSrc}
crop={this.state.crop}
zoom={this.state.zoom}
onCropChange={this.onCropChange}
onCropComplete={this.onCropComplete}
onZoomChange={this.onZoomChange}
onMediaLoaded={this.onMediaLoad}
cropSize={cropSize}
/>
</div>
</div>
);
}
}
export default App;
Demo: https://stackblitz.com/edit/react-4zmgud
It seems that what you need is the objectFit property set to vertical-cover.
See this demo: https://codesandbox.io/s/crazy-liskov-04u7m0

React- Conditionally applying css in div but it does not work

Have looked at other examples and trying to do the same thing but not sure why my code is not working. I have code which loops through some keys and renders a div. I want to conditionally apply some styles based on whether the key is even or odd. Example:
<div className={parseInt(key) % 2 === 0 ? 'label1' : 'label2' }>
<span style={{ marginLeft: "10px" }}>{key}:00</span>
</div>
The styles are accessible in the same file and look something like:
# Material UI
const useStyles = makeStyles((theme) => ({
label1: {
width: "50px",
height: "16px",
top: "458px",
background: "yellow",
fontSize: "12px",
},
label2: {
width: "50px",
height: "16px",
top: "458px",
background: "red",
fontSize: "12px",
},
}));
What am I doing wrong? Currently no style is getting applied to the div
You need to use the classes from the material ui useStyles hook.
const classes = useStyles()
....
<div className={parseInt(key) % 2 === 0 ? classes.label1 : classes.label2 }>
<span style={{ marginLeft: "10px" }}>{key}:00</span>
</div>
Check the useStyles hook api: https://material-ui.com/styles/basics/
If you have a class component and you can use hooks then you can do it with the withStyles higher order component, like this example:
import { withStyles } from "#material-ui/core/styles"
const styles = theme => ({
label1: {
backgroundColor: "red",
},
label2: {
backgroundColor: "red",
},
})
class ClassComponent extends Component {
state = {
searchNodes: "",
}
render() {
const { classes } = this.props
return (
<div className={parseInt(key) % 2 === 0 ? classes.label1 : classes.label2}>
<span style={{ marginLeft: "10px" }}>{key}:00</span>
</div>
)
}
}
export default withStyles(styles, { withTheme: true })(ClassComponent)

Change scrollbar by hover. (React /css) - Material-UI

I have a scrollbar in multiple lists in the React app.
My global CSS within Material-UI is:
MuiCssBaseline: {
...theme.overrides?.MuiCssBaseline,
'#global': {
'#font-face': fontFace,
'*::-webkit-scrollbar': {
width: '1.3%',
maxWidth: '5px'
},
'*::-webkit-scrollbar-thumb': {
backgroundColor: 'white'
},
'*:focus': {
outline: 'none'
}
}
}
};
I am trying to add global state for hover.
When I hover on the box(!) not on the scroll itself, the scrollbar should be changed to gray.
Let's say I have box like:
<Box className={listStyle.root}>
<AutoSizer>
{({ height, width }) => (
<List dense ......
return (
<ListItem
/>
);
})}
</List>
)}
</AutoSizer>
I tried something like the following and it's not working:
.listStyle:{
'&:hover':{
'*::-webkit-scrollbar-thumb': {
backgroundColor: 'gray'
},
}
}
How can I achieve that?
The primary issue I see is that when nesting a rule (e.g. ::-webkit-scrollbar-thumb pseudo-element within :hover pseudo-class), you need to use & to refer to the parent rule instead of using *. So your listStyle should look like:
.listStyle:{
'&:hover':{
'&::-webkit-scrollbar-thumb': {
backgroundColor: 'gray'
},
}
}
It would also be equivalent to do the following:
.listStyle:{
'&:hover::-webkit-scrollbar-thumb': {
backgroundColor: 'gray'
}
}
If you want to apply this globally, you can do something like the following (I've changed the colors just to make the effects more easily distinguishable from browser defaults):
const theme = createMuiTheme({
overrides: {
MuiCssBaseline: {
"#global": {
"*::-webkit-scrollbar": {
width: "1.3%",
maxWidth: "5px"
},
"*::-webkit-scrollbar-thumb": {
backgroundColor: "purple"
},
"*:hover": {
"&::-webkit-scrollbar-thumb": {
backgroundColor: "green"
}
}
/* Equivalent alternative:
"*:hover::-webkit-scrollbar-thumb": {
backgroundColor: "green"
}
*/
}
}
}
});
Here is a full example showing both the global approach and overriding that using a class name at a lower level:
import React from "react";
import CssBaseline from "#material-ui/core/CssBaseline";
import {
ThemeProvider,
createMuiTheme,
makeStyles
} from "#material-ui/core/styles";
const theme = createMuiTheme({
overrides: {
MuiCssBaseline: {
"#global": {
"*::-webkit-scrollbar": {
width: "1.3%",
maxWidth: "5px"
},
"*::-webkit-scrollbar-thumb": {
backgroundColor: "purple"
},
"*:hover": {
"&::-webkit-scrollbar-thumb": {
backgroundColor: "green"
}
}
/* Equivalent alternative:
"*:hover::-webkit-scrollbar-thumb": {
backgroundColor: "green"
}
*/
}
}
}
});
const useStyles = makeStyles({
divStyle: {
"&:hover::-webkit-scrollbar-thumb": {
backgroundColor: "red"
}
}
});
export default function App() {
const classes = useStyles();
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<div style={{ height: "200px", overflowY: "scroll" }}>
<h1>
Div with enough content to scroll. Globally controlled scrollbar hover
color
</h1>
<h2>1</h2>
<h2>2</h2>
<h2>3</h2>
<h2>4</h2>
<h2>5</h2>
<h2>6</h2>
</div>
<div
style={{ height: "200px", overflowY: "scroll" }}
className={classes.divStyle}
>
<h1>
Div with enough content to scroll. Scrollbar hover color controlled by
classes.divStyle.
</h1>
<h2>1</h2>
<h2>2</h2>
<h2>3</h2>
<h2>4</h2>
<h2>5</h2>
<h2>6</h2>
</div>
</ThemeProvider>
);
}

How can I change background colors to the react element upon onclick event

I've 3 react components which are basically 3 divs. Now I select one component out of them by clicking.now I want to change the selected component's background color to black. If I select another component then previously selected component should back to normal. How can I do that?
Here's a simple solution:
.App {
display: flex;
}
.box {
width: 150px;
height: 150px;
margin: 20px;
padding: 20px;
}
import React from "react";
import "./styles.css";
export default function App() {
const [bg, changeBGColor] = React.useState(1);
return (
<div className="App">
<div
className="box"
onClick={() => changeBGColor(1)}
style={{
backgroundColor: bg === 1 ? "black" : "red"
}}
/>
<div
className="box"
onClick={() => changeBGColor(2)}
style={{
backgroundColor: bg === 2 ? "black" : "yellow"
}}
/>
<div
className="box"
onClick={() => changeBGColor(3)}
style={{
backgroundColor: bg === 3 ? "black" : "green"
}}
/>
</div>
);
}
Paste this in a Codesandbox to see the output.
Here, I'm giving using a React hook which takes in a unique id. If it's set, then that component will be black.
So 1st div is black by default. If I click on 2nd div, I change the value of bg to 2 & in the style property I check if 2 is selected then set black color. Similarly, I do the same thing for 3rd div.
assuming those components have their own unique properties (or you should add them) you can add modify their className like className={this.state.selectedComponent==="uniqueID" ? "activeClassName" : ""} while having a state property (selectedComponent) which is set to that unique property upon onClick
import React, { Component } from 'react'
export default class YourClass extends Component {
constructor(props) {
super(props)
this.state = {
color: 'black'
}
this.changeColor = this.changeColor.bind(this)
this.changeColor2 = this.changeColor2.bind(this)
}
changeColor() {
this.setState({
color: 'red'
})
}
changeColor2() {
this.setState({
color: 'green'
})
}
render() {
return (
<div style={{ backgroundColor: this.state.color }}>
<button onClick={this.changeColor} />
<button onClick={this.changeColor2} />
</div>
)
}
}
This is a very rudimentary way to do it, by default the div is black, when you click the first button you can change it to red and the second button to green
You can use this simple logic for several things

Resources