diff --git a/src/views/Test.js b/src/views/Test.js
deleted file mode 100644
index 24c7e318..00000000
--- a/src/views/Test.js
+++ /dev/null
@@ -1,82 +0,0 @@
-import * as React from 'react';
-import styled from 'styled-components';
-import Sticky from 'react-sticky-el';
-
-import DeviceHeader from 'components/DeviceHeader';
-
-const App = styled.div`
- border: 2px dashed lime;
- width: 100vh;
- padding: 150px;
- height: 1800px;
-`;
-
-const Wrapper = styled(Sticky)`
- max-width: 400px;
- padding: 20px;
- height: 100vh;
- display: flex;
- border: 2px dashed blue;
- flex-direction: column;
-`;
-
-const Content = styled.div`
- flex: 1;
- overflow: auto;
- height: 100%;
- border: 2px solid gold;
-`;
-
-const Header = styled(Sticky)`
- flex-shrink: 0;
- border: 2px solid red;
- background: white;
-`;
-
-const Footer = styled(Sticky)`
- border: 2px solid tan;
-`;
-
-const Item = styled.div`
- width: 100%;
- height: 35px;
- border: 1px solid navy;
- padding: 5px;
- margin: 10px 0;
-`;
-
-const T = () => (
-
-
-
-
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
- - aaa
-
-
-
-
-
-);
-
-export default T;
diff --git a/src/views/Wallet/components/LeftNavigation/components/StickyContainer/index.js b/src/views/Wallet/components/LeftNavigation/components/StickyContainer/index.js
new file mode 100644
index 00000000..dd20c6f2
--- /dev/null
+++ b/src/views/Wallet/components/LeftNavigation/components/StickyContainer/index.js
@@ -0,0 +1,182 @@
+/* @flow */
+
+
+// https://github.com/KyleAMathews/react-headroom/blob/master/src/shouldUpdate.js
+
+import * as React from 'react';
+import raf from 'raf';
+import { getViewportHeight, getScrollY } from 'utils/windowUtils';
+import styled from 'styled-components';
+import colors from 'config/colors';
+
+type Props = {
+ location: string,
+ deviceSelection: boolean,
+ children?: React.Node,
+}
+
+const AsideWrapper = styled.aside`
+ position: relative;
+ top: 0;
+ width: 320px;
+ overflow: hidden;
+ background: ${colors.MAIN};
+ border-right: 1px solid ${colors.DIVIDER};
+
+ .fixed {
+ position: fixed;
+ border-right: 1px solid ${colors.DIVIDER};
+ }
+
+ .fixed-bottom {
+ padding-bottom: 60px;
+ .sticky-bottom {
+ position: fixed;
+ bottom: 0;
+ background: ${colors.MAIN};
+ border-right: 1px solid ${colors.DIVIDER};
+ }
+ }
+`;
+
+const StickyContainerWrapper = styled.div`
+ position: relative;
+ top: 0;
+ width: 320px;
+ overflow: hidden;
+`;
+
+export default class StickyContainer extends React.PureComponent {
+ // Class variables.
+ currentScrollY: number = 0;
+
+ lastKnownScrollY: number = 0;
+
+ topOffset: number = 0;
+
+ firstRender: boolean = false;
+
+ framePending: boolean = false;
+
+ stickToBottom: boolean = false;
+
+ top: number = 0;
+
+ aside: ?HTMLElement;
+
+ wrapper: ?HTMLElement;
+
+ subscribers = [];
+
+ // handleResize = (event: Event) => {
+
+ // }
+
+ shouldUpdate = () => {
+ const { wrapper, aside }: {wrapper: ?HTMLElement, aside: ?HTMLElement} = this;
+ if (!wrapper || !aside) return;
+ const bottom: ?HTMLElement = wrapper.querySelector('.sticky-bottom');
+ if (!bottom) return;
+
+ const viewportHeight: number = getViewportHeight();
+ const bottomBounds = bottom.getBoundingClientRect();
+ const asideBounds = aside.getBoundingClientRect();
+ const wrapperBounds = wrapper.getBoundingClientRect();
+ const scrollDirection = this.currentScrollY >= this.lastKnownScrollY ? 'down' : 'up';
+ const distanceScrolled = Math.abs(this.currentScrollY - this.lastKnownScrollY);
+
+ console.warn(wrapper, bottom);
+
+ if (asideBounds.top < 0) {
+ wrapper.classList.add('fixed');
+ let maxTop: number = 1;
+ if (wrapperBounds.height > viewportHeight) {
+ const bottomOutOfBounds: boolean = (bottomBounds.bottom <= viewportHeight && scrollDirection === 'down');
+ const topOutOfBounds: boolean = (wrapperBounds.top > 0 && scrollDirection === 'up');
+ if (!bottomOutOfBounds && !topOutOfBounds) {
+ this.topOffset += scrollDirection === 'down' ? -distanceScrolled : distanceScrolled;
+ }
+ maxTop = viewportHeight - wrapperBounds.height;
+ }
+
+ if (this.topOffset > 0) this.topOffset = 0;
+ if (maxTop < 0 && this.topOffset < maxTop) this.topOffset = maxTop;
+ wrapper.style.top = `${this.topOffset}px`;
+ } else {
+ wrapper.classList.remove('fixed');
+ wrapper.style.top = '0px';
+ this.topOffset = 0;
+ }
+
+ if (wrapperBounds.height > viewportHeight) {
+ wrapper.classList.remove('fixed-bottom');
+ } else if (wrapper.classList.contains('fixed-bottom')) {
+ if (bottomBounds.top < wrapperBounds.bottom - bottomBounds.height) {
+ wrapper.classList.remove('fixed-bottom');
+ }
+ } else if (bottomBounds.bottom < viewportHeight) {
+ wrapper.classList.add('fixed-bottom');
+ }
+
+ aside.style.minHeight = `${wrapperBounds.height}px`;
+ }
+
+ update = () => {
+ this.currentScrollY = getScrollY();
+ this.shouldUpdate();
+ this.framePending = false;
+ this.lastKnownScrollY = this.currentScrollY;
+ }
+
+ componentDidMount() {
+ window.addEventListener('scroll', this.handleScroll);
+ window.addEventListener('resize', this.handleScroll);
+ raf(this.update);
+ }
+
+ componentDidUpdate(prevProps: Props) {
+ if (this.props.location !== prevProps.location && this.aside) {
+ const asideBounds = this.aside.getBoundingClientRect();
+ if (asideBounds.top < 0) {
+ window.scrollTo(0, getScrollY() + asideBounds.top);
+ this.topOffset = 0;
+ }
+ raf(this.update);
+ } else if (this.props.deviceSelection !== prevProps.deviceSelection) {
+ raf(this.update);
+ } else if (!this.firstRender) {
+ raf(this.update);
+ this.firstRender = true;
+ }
+ }
+
+ componentWillUnmount() {
+ window.removeEventListener('scroll', this.handleScroll);
+ window.removeEventListener('resize', this.handleScroll);
+ }
+
+ handleScroll = (/* event: ?Event */) => {
+ if (!this.framePending) {
+ this.framePending = true;
+ raf(this.update);
+ }
+ }
+
+ render() {
+ return (
+ { this.aside = node; }}
+ onScroll={this.handleScroll}
+ onTouchStart={this.handleScroll}
+ onTouchMove={this.handleScroll}
+ onTouchEnd={this.handleScroll}
+ >
+ { this.wrapper = node; }}
+ >
+ {this.props.children}
+
+
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/views/Wallet/components/LeftNavigation/index.js b/src/views/Wallet/components/LeftNavigation/index.js
index 47703bf4..dc57e112 100644
--- a/src/views/Wallet/components/LeftNavigation/index.js
+++ b/src/views/Wallet/components/LeftNavigation/index.js
@@ -10,14 +10,7 @@ import DeviceHeader from 'components/DeviceHeader';
import AccountMenu from './components/AccountMenu';
import CoinMenu from './components/CoinMenu';
import DeviceMenu from './components/DeviceMenu';
-
-const Wrapper = styled.div`
- width: 320px;
- overflow: scroll;
- border: 2px solid red;
- background: ${colors.MAIN};
- border-right: 1px solid ${colors.DIVIDER};
-`;
+import StickyContainer from './components/StickyContainer';
const Header = styled(DeviceHeader)``;
@@ -157,27 +150,24 @@ class LeftNavigation extends Component {
render() {
return (
-
-
- this.handleOpen()}
- device={this.props.wallet.selectedDevice}
- transport={this.props.connect.transport}
- devices={this.props.devices}
- isOpen={this.props.deviceDropdownOpened}
- {...this.props}
- />
-
+ this.handleOpen()}
+ device={this.props.wallet.selectedDevice}
+ transport={this.props.connect.transport}
+ devices={this.props.devices}
+ isOpen={this.props.deviceDropdownOpened}
+ {...this.props}
+ />
{this.state.shouldRenderDeviceSelection && }
{this.shouldRenderAccounts() && this.getMenuTransition()}
{this.shouldRenderCoins() && this.getMenuTransition()}
-
-
+
);
}
}