Reputation: 3076
I have a react component representing a button which changes the content and color depending on some props
. If a form component includes this button and gets submitted the button changes it's content and color. This works well.
Now I want an animation (CSS transition) for the changing with when the content of the button changes. Setting an explicit width
for different states is NOT an option because the application is multilingual. When using max-width
the animation only works when the button extends but not when it decreases.
Here is a snippet with a button component which changes the state every second. Like in the real component this one uses the bootstrap btn
classes:
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {status: 0};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
tick() {
let status = this.state.status;
status++;
if(status >= 2) {
status = 0;
}
this.setState({
status: status
});
}
render() {
const btnClass = this.state.status ? 'primary' : 'secondary';
return (
<button className={`btn btn-${btnClass}`} type='button'>
{this.state.status ? this.props.large : this.props.short}
</button>
);
}
}
// Render it
ReactDOM.render(
<Button large='This is a long button you can click on!' short='short'/>,
document.getElementById('react')
);
.btn {
-webkit-transition: width 1s; /* Safari */
transition: max-width 1s;
width: auto;
}
.btn-primary {
max-width: 300px;
}
.btn-secondary {
max-width: 50px;
}
<div id="react"></div>
<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>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
width
(when the button gets smaller again)?Please consider that an answer doesn't have to be just CSS related. I thought about that the react component calculates it's width and then sets it inside the componentWillMount
function but that didn't work either...
Upvotes: 4
Views: 8524
Reputation: 3039
Try setting width: 100%
to .btn
class. There is nothing wrong with animating max-width
property. The issue arises when the text is changing from a small text to a larger one and the browser will have to recalculate what auto
means as a value for the width
property. It seems that 100%
does not work the same way.
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {status: 0};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
tick() {
let status = this.state.status;
status++;
if(status >= 2) {
status = 0;
}
this.setState({
status: status
});
}
render() {
const btnClass = this.state.status ? 'primary' : 'secondary';
return (
<button className={`btn btn-${btnClass}`} type='button'>
{this.state.status ? this.props.large : this.props.short}
</button>
);
}
}
// Render it
ReactDOM.render(
<Button large='This is a long button you can click on!' short='short'/>,
document.getElementById('react')
);
.btn {
-webkit-transition: width 1s; /* Safari */
transition: max-width 1s;
width: 100%;
}
.btn-primary {
max-width: 300px;
}
.btn-secondary {
max-width: 50px;
}
<div id="react"></div>
<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>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
Upvotes: 3
Reputation: 3103
I have a solution. Add min-width to your css classes and transition as below.
Animate on the way up. Animate on the way down.
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {status: 0};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
tick() {
let status = this.state.status;
status++;
if(status >= 2) {
status = 0;
}
this.setState({
status: status
});
}
render() {
const btnClass = this.state.status ? 'primary' : 'secondary';
return (
<button className={`btn btn-${btnClass}`} type='button'>
{this.state.status ? this.props.large : this.props.short}
</button>
);
}
}
// Render it
ReactDOM.render(
<Button large='This is a long button you can click on!' short='short'/>,
document.getElementById('react')
);
.btn {
-webkit-transition: width 1s; /* Safari */
transition: max-width 1s, min-width 1s;
width: auto;
}
.btn-primary {
max-width: 330px;
min-width: 230px;
}
.btn-secondary {
max-width: 60px;
min-width: 40px;
text-align: left;
}
<div id="react"></div>
<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>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
Upvotes: 1