Skip to content

Commit

Permalink
Add expanding animation to the sidebar
Browse files Browse the repository at this point in the history
  • Loading branch information
nygrenh committed Jul 31, 2018
1 parent 3f34080 commit e6e62e2
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 23 deletions.
25 changes: 25 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"react-calendar-timeline": "^0.17.3",
"react-dom": "^16.4.1",
"react-helmet": "^5.2.0",
"react-motion": "^0.5.2",
"react-scrollspy": "^3.3.5",
"styled-components": "^3.3.3",
"typeface-open-sans-condensed": "0.0.54"
Expand Down
72 changes: 49 additions & 23 deletions src/components/TreeView/TreeViewItem.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import React from 'react'
import styled from 'styled-components'

import { Motion, spring } from 'react-motion'

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faCaretRight } from '@fortawesome/free-solid-svg-icons'
import { trackElementHeight } from '../../util/trackHeight'

const ChildrenList = styled.ul`
max-height: var(--max-height);
height: calc(var(--open-ratio) * var(--calculated-height) * 1px);
overflow: hidden;
`

Expand All @@ -28,7 +31,7 @@ const ItemTitleWrapper = styled.div`
&.active-section {
background-color: #ffdfdf;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
border-bottom-left-radius: 5px;
a {
color: black;
}
Expand All @@ -38,6 +41,8 @@ border-bottom-left-radius: 5px;
const StyledIcon = styled(FontAwesomeIcon)`
vertical-align: middle;
margin-right: 0.5rem;
margin-left: 0.5rem;
transform: rotate(calc(var(--open-ratio) * 90deg));
`

export default class TreeViewItem extends React.Component {
Expand All @@ -46,39 +51,60 @@ export default class TreeViewItem extends React.Component {
this.state = {
childrenVisible: props.item.childrenVisibleByDefault || false,
}
this.childrenListRef = React.createRef()
}

onClick = () => {
this.setState(prev => ({
childrenVisible: !prev.childrenVisible,
}))
}

componentDidMount() {
if (this.props.item.children) {
console.log(this.childrenListRef.current)
trackElementHeight(this.childrenListRef.current)
}
}
render() {
return (
<React.Fragment>
<ItemTitleWrapper
className={`nav-item-${this.props.item.name
.toLowerCase()
.replace(/ /g, '-')}`}
<Motion
style={{ openRatio: spring(this.state.childrenVisible ? 1 : 0) }}
>
{this.props.item.children && (
<StyledIcon icon={faCaretRight} size="1x" />
{({ openRatio }) => (
<React.Fragment>
<ItemTitleWrapper
className={`nav-item-${this.props.item.name
.toLowerCase()
.replace(/ /g, '-')}`}
>
{this.props.item.children && (
<StyledIcon
style={{ '--open-ratio': `${openRatio}` }}
icon={faCaretRight}
size="1x"
/>
)}
<a href={this.props.item.href}>
<ListItem onClick={this.onClick}>
{this.props.item.name}
</ListItem>
</a>
</ItemTitleWrapper>
{this.props.item.children && (
<ChildrenList
innerRef={this.childrenListRef}
style={{ '--open-ratio': `${openRatio}` }}
>
{this.props.item.children.map(i => (
<TreeViewItem key={i.name} item={i} />
))}
</ChildrenList>
)}
</React.Fragment>
)}
<a href={this.props.item.href}>
<ListItem onClick={this.onClick}>{this.props.item.name}</ListItem>
</a>
</ItemTitleWrapper>
{this.props.item.children && (
<ChildrenList
style={{
'--max-height': this.state.childrenVisible ? '9999px' : '0',
}}
>
{this.props.item.children.map(i => (
<TreeViewItem key={i.name} item={i} />
))}
</ChildrenList>
)}
</Motion>
</React.Fragment>
)
}
Expand Down
51 changes: 51 additions & 0 deletions src/util/trackHeight.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Used for animations
import { injectGlobal } from "styled-components";

injectGlobal`
.render-element-off-screen-for-measurement {
position: absolute !important;
top: -100000px !important;
height: auto !important;
}
`;

const saveHeight = (element, height) => {
element.style.setProperty("--calculated-height", height);
};

const calculateElementHeightOffScreen = element => {
return new Promise(resolve => {
element.classList.add("render-element-off-screen-for-measurement");
setTimeout(() => {
const height = element.getBoundingClientRect().height;
element.classList.remove("render-element-off-screen-for-measurement");
resolve(height);
}, 100);
});
};

const calculateElementHeight = async element => {
let { height } = element.getBoundingClientRect();
if (height === 0) {
height = await calculateElementHeightOffScreen(element);
}
saveHeight(element, height);
};

export const trackElementHeight = element => {
if (element === null) {
return;
}
element.classList.add("track-element-height-changes-for-animations");
calculateElementHeight(element);
};

export default () => {
window.addEventListener("resize", () => {
document
.querySelectorAll(".track-element-height-changes-for-animations")
.forEach(e => {
calculateElementHeight(e);
});
});
};

0 comments on commit e6e62e2

Please sign in to comment.