Update the CSS transition for breadcrumbs

The actual transition length might need adjusting, but this is fairly close to what was requested.
This commit is contained in:
Travis Ralston 2020-06-09 15:06:54 -06:00
parent b5f9c4ba8a
commit 1467191a5d
5 changed files with 89 additions and 28 deletions

View file

@ -23,6 +23,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
import defaultDispatcher from "../../../dispatcher/dispatcher";
import Analytics from "../../../Analytics";
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
import { CSSTransition, TransitionGroup } from "react-transition-group";
/*******************************************************************
* CAUTION *
@ -36,6 +37,14 @@ interface IProps {
}
interface IState {
// Both of these control the animation for the breadcrumbs. For details on the
// actual animation, see the CSS.
//
// doAnimation is to lie to the CSSTransition component (see onBreadcrumbsUpdate
// for info). skipFirst is used to try and reduce jerky animation - also see the
// breadcrumb update function for info on that.
doAnimation: boolean;
skipFirst: boolean;
}
export default class RoomBreadcrumbs2 extends React.PureComponent<IProps, IState> {
@ -44,6 +53,11 @@ export default class RoomBreadcrumbs2 extends React.PureComponent<IProps, IState
constructor(props: IProps) {
super(props);
this.state = {
doAnimation: true, // technically we want animation on mount, but it won't be perfect
skipFirst: false, // render the thing, as boring as it is
};
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
}
@ -54,7 +68,16 @@ export default class RoomBreadcrumbs2 extends React.PureComponent<IProps, IState
private onBreadcrumbsUpdate = () => {
if (!this.isMounted) return;
this.forceUpdate(); // we have no state, so this is the best we can do
// We need to trick the CSSTransition component into updating, which means we need to
// tell it to not animate, then to animate a moment later. This causes two updates
// which means two renders. The skipFirst change is so that our don't-animate state
// doesn't show the breadcrumb we're about to reveal as it causes a visual jump/jerk.
// The second update, on the next available tick, causes the "enter" animation to start
// again and this time we want to show the newest breadcrumb because it'll be hidden
// off screen for the animation.
this.setState({doAnimation: false, skipFirst: true});
setTimeout(() => this.setState({doAnimation: true, skipFirst: false}), 0);
};
private viewRoom = (room: Room, index: number) => {
@ -77,14 +100,26 @@ export default class RoomBreadcrumbs2 extends React.PureComponent<IProps, IState
)
});
if (tiles.length === 0) {
tiles.push(
<div className="mx_RoomBreadcrumbs2_placeholder">
{_t("No recently visited rooms")}
if (tiles.length > 0) {
// NOTE: The CSSTransition timeout MUST match the timeout in our CSS!
return (
<CSSTransition
appear={true} in={this.state.doAnimation} timeout={300}
classNames='mx_RoomBreadcrumbs2'
>
<div className='mx_RoomBreadcrumbs2'>
{tiles.slice(this.state.skipFirst ? 1 : 0)}
</div>
</CSSTransition>
);
} else {
return (
<div className='mx_RoomBreadcrumbs2'>
<div className="mx_RoomBreadcrumbs2_placeholder">
{_t("No recently visited rooms")}
</div>
</div>
);
}
return <div className='mx_RoomBreadcrumbs2'>{tiles}</div>;
}
}