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:
parent
b5f9c4ba8a
commit
1467191a5d
5 changed files with 89 additions and 28 deletions
|
@ -94,6 +94,7 @@
|
||||||
"react-dom": "^16.9.0",
|
"react-dom": "^16.9.0",
|
||||||
"react-focus-lock": "^2.2.1",
|
"react-focus-lock": "^2.2.1",
|
||||||
"react-resizable": "^1.10.1",
|
"react-resizable": "^1.10.1",
|
||||||
|
"react-transition-group": "^4.4.1",
|
||||||
"resize-observer-polyfill": "^1.5.0",
|
"resize-observer-polyfill": "^1.5.0",
|
||||||
"sanitize-html": "^1.18.4",
|
"sanitize-html": "^1.18.4",
|
||||||
"text-encoding-utf-8": "^1.0.1",
|
"text-encoding-utf-8": "^1.0.1",
|
||||||
|
@ -126,6 +127,7 @@
|
||||||
"@types/qrcode": "^1.3.4",
|
"@types/qrcode": "^1.3.4",
|
||||||
"@types/react": "^16.9",
|
"@types/react": "^16.9",
|
||||||
"@types/react-dom": "^16.9.8",
|
"@types/react-dom": "^16.9.8",
|
||||||
|
"@types/react-transition-group": "^4.4.0",
|
||||||
"@types/zxcvbn": "^4.4.0",
|
"@types/zxcvbn": "^4.4.0",
|
||||||
"babel-eslint": "^10.0.3",
|
"babel-eslint": "^10.0.3",
|
||||||
"babel-jest": "^24.9.0",
|
"babel-jest": "^24.9.0",
|
||||||
|
|
|
@ -14,34 +14,32 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@keyframes breadcrumb-popin {
|
|
||||||
0% {
|
|
||||||
// Ideally we'd use `width` instead of `opacity`, but we only
|
|
||||||
// have 16 nanoseconds to render the frame, and width is expensive.
|
|
||||||
opacity: 0;
|
|
||||||
transform: scale(0);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomBreadcrumbs2 {
|
.mx_RoomBreadcrumbs2 {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
// Create a flexbox for the crumbs
|
// Create a flexbox for the crumbs
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.mx_RoomBreadcrumbs2_crumb {
|
.mx_RoomBreadcrumbs2_crumb {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
|
|
||||||
// React loves to add elements, so only target the one we want to animate
|
|
||||||
&:first-child {
|
|
||||||
animation: breadcrumb-popin 0.3s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These classes come from the CSSTransition component. There's many more classes we
|
||||||
|
// could care about, but this is all we worried about for now. The animation works by
|
||||||
|
// first triggering the enter state with the newest breadcrumb off screen (-40px) then
|
||||||
|
// sliding it into view.
|
||||||
|
&.mx_RoomBreadcrumbs2-enter {
|
||||||
|
margin-left: -40px; // 32px for the avatar, 8px for the margin
|
||||||
|
}
|
||||||
|
&.mx_RoomBreadcrumbs2-enter-active {
|
||||||
|
margin-left: 0;
|
||||||
|
|
||||||
|
// Timing function is as-requested by design.
|
||||||
|
// NOTE: The transition time MUST match the value passed to CSSTransition!
|
||||||
|
transition: margin-left 300ms cubic-bezier(0.66, 0.02, 0.36, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomBreadcrumbs2_placeholder {
|
.mx_RoomBreadcrumbs2_placeholder {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
||||||
import Analytics from "../../../Analytics";
|
import Analytics from "../../../Analytics";
|
||||||
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
||||||
|
import { CSSTransition, TransitionGroup } from "react-transition-group";
|
||||||
|
|
||||||
/*******************************************************************
|
/*******************************************************************
|
||||||
* CAUTION *
|
* CAUTION *
|
||||||
|
@ -36,6 +37,14 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
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> {
|
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) {
|
constructor(props: IProps) {
|
||||||
super(props);
|
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);
|
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +68,16 @@ export default class RoomBreadcrumbs2 extends React.PureComponent<IProps, IState
|
||||||
|
|
||||||
private onBreadcrumbsUpdate = () => {
|
private onBreadcrumbsUpdate = () => {
|
||||||
if (!this.isMounted) return;
|
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) => {
|
private viewRoom = (room: Room, index: number) => {
|
||||||
|
@ -77,14 +100,26 @@ export default class RoomBreadcrumbs2 extends React.PureComponent<IProps, IState
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (tiles.length === 0) {
|
if (tiles.length > 0) {
|
||||||
tiles.push(
|
// 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">
|
<div className="mx_RoomBreadcrumbs2_placeholder">
|
||||||
{_t("No recently visited rooms")}
|
{_t("No recently visited rooms")}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className='mx_RoomBreadcrumbs2'>{tiles}</div>;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
"types": [
|
"types": [
|
||||||
"node",
|
"node",
|
||||||
"react",
|
"react",
|
||||||
"flux"
|
"flux",
|
||||||
|
"react-transition-group"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
|
|
29
yarn.lock
29
yarn.lock
|
@ -968,7 +968,7 @@
|
||||||
core-js-pure "^3.0.0"
|
core-js-pure "^3.0.0"
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4":
|
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
|
||||||
version "7.10.2"
|
version "7.10.2"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839"
|
||||||
integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==
|
integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==
|
||||||
|
@ -1352,6 +1352,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react-transition-group@^4.4.0":
|
||||||
|
version "4.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.0.tgz#882839db465df1320e4753e6e9f70ca7e9b4d46d"
|
||||||
|
integrity sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react@*", "@types/react@^16.9":
|
"@types/react@*", "@types/react@^16.9":
|
||||||
version "16.9.35"
|
version "16.9.35"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.35.tgz#a0830d172e8aadd9bd41709ba2281a3124bbd368"
|
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.35.tgz#a0830d172e8aadd9bd41709ba2281a3124bbd368"
|
||||||
|
@ -2835,7 +2842,7 @@ cssstyle@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
cssom "0.3.x"
|
cssom "0.3.x"
|
||||||
|
|
||||||
csstype@^2.2.0:
|
csstype@^2.2.0, csstype@^2.6.7:
|
||||||
version "2.6.10"
|
version "2.6.10"
|
||||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.10.tgz#e63af50e66d7c266edb6b32909cfd0aabe03928b"
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.10.tgz#e63af50e66d7c266edb6b32909cfd0aabe03928b"
|
||||||
integrity sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==
|
integrity sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==
|
||||||
|
@ -3054,6 +3061,14 @@ doctrine@^3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
esutils "^2.0.2"
|
esutils "^2.0.2"
|
||||||
|
|
||||||
|
dom-helpers@^5.0.1:
|
||||||
|
version "5.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.1.4.tgz#4609680ab5c79a45f2531441f1949b79d6587f4b"
|
||||||
|
integrity sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.8.7"
|
||||||
|
csstype "^2.6.7"
|
||||||
|
|
||||||
dom-serializer@0, dom-serializer@^0.2.1:
|
dom-serializer@0, dom-serializer@^0.2.1:
|
||||||
version "0.2.2"
|
version "0.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
|
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
|
||||||
|
@ -7136,6 +7151,16 @@ react-test-renderer@^16.0.0-0, react-test-renderer@^16.9.0:
|
||||||
react-is "^16.8.6"
|
react-is "^16.8.6"
|
||||||
scheduler "^0.19.1"
|
scheduler "^0.19.1"
|
||||||
|
|
||||||
|
react-transition-group@^4.4.1:
|
||||||
|
version "4.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9"
|
||||||
|
integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.5.5"
|
||||||
|
dom-helpers "^5.0.1"
|
||||||
|
loose-envify "^1.4.0"
|
||||||
|
prop-types "^15.6.2"
|
||||||
|
|
||||||
react@^16.9.0:
|
react@^16.9.0:
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
|
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
|
||||||
|
|
Loading…
Reference in a new issue