Advertisement
Guest User

Untitled

a guest
Aug 21st, 2019
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.17 KB | None | 0 0
  1. import React, { Component } from "react";
  2. import { CSSTransition, TransitionGroup } from "react-transition-group";
  3. import AnchorLink from "react-anchor-link-smooth-scroll";
  4. import { navLinks, navHeight } from "./config";
  5. import Menu from "./Menu";
  6. import styled from "styled-components";
  7. import theme from "../styles/theme";
  8. import media from "../styles/media";
  9. import mixins from "../styles/mixins";
  10. const { colors, fontSizes, fonts } = theme;
  11.  
  12. const throttle = (func, wait = 100) => {
  13. let timer = null;
  14. return function(...args) {
  15. if (timer === null) {
  16. timer = setTimeout(() => {
  17. func.apply(this, args);
  18. timer = null;
  19. }, wait);
  20. }
  21. };
  22. };
  23.  
  24. const NavContainer = styled.header`
  25. ${mixins.flexBetween};
  26. position: fixed;
  27. top: 0;
  28. padding: 0px 50px;
  29. background-color: ${colors.navy};
  30. transition: ${theme.transition};
  31. z-index: 11;
  32. filter: none !important;
  33. pointer-events: auto !important;
  34. user-select: auto !important;
  35. width: 100%;
  36. height: ${props =>
  37. props.scrollDirection === "none" ? theme.navHeight : theme.navScrollHeight};
  38. box-shadow: ${props =>
  39. props.scrollDirection === "up"
  40. ? `0 10px 30px -10px ${colors.shadowNavy}`
  41. : "none"};
  42. transform: translateY(
  43. ${props =>
  44. props.scrollDirection === "down" ? `-${theme.navScrollHeight}` : "0px"}
  45. );
  46. ${media.desktop`padding: 0 40px;`};
  47. ${media.tablet`padding: 0 25px;`};
  48. `;
  49. const Navbar = styled.nav`
  50. ${mixins.flexBetween};
  51. position: relative;
  52. width: 100%;
  53. color: ${colors.lightestSlate};
  54. font-family: ${fonts.SFMono};
  55. counter-reset: item 0;
  56. z-index: 12;
  57. `;
  58. const Logo = styled.div`
  59. ${mixins.flexCenter};
  60. `;
  61. const LogoLink = styled.a`
  62. display: block;
  63. color: ${colors.green};
  64. width: 42px;
  65. height: 42px;
  66. &:hover,
  67. &:focus {
  68. svg {
  69. fill: ${colors.transGreen};
  70. }
  71. }
  72. svg {
  73. fill: none;
  74. transition: ${theme.transition};
  75. user-select: none;
  76. }
  77. `;
  78. const Hamburger = styled.div`
  79. ${mixins.flexCenter};
  80. overflow: visible;
  81. margin: 0 -12px 0 0;
  82. padding: 15px;
  83. cursor: pointer;
  84. transition-timing-function: linear;
  85. transition-duration: 0.15s;
  86. transition-property: opacity, filter;
  87. text-transform: none;
  88. color: inherit;
  89. border: 0;
  90. background-color: transparent;
  91. display: none;
  92. ${media.tablet`display: flex;`};
  93. `;
  94. const HamburgerBox = styled.div`
  95. position: relative;
  96. display: inline-block;
  97. width: ${theme.hamburgerWidth}px;
  98. height: 24px;
  99. `;
  100. const HamburgerInner = styled.div`
  101. background-color: ${colors.green};
  102. position: absolute;
  103. width: ${theme.hamburgerWidth}px;
  104. height: 2px;
  105. border-radius: ${theme.borderRadius};
  106. top: 50%;
  107. left: 0;
  108. right: 0;
  109. transition-duration: 0.22s;
  110. transition-property: transform;
  111. transition-delay: ${props => (props.menuOpen ? `0.12s` : `0s`)};
  112. transform: rotate(${props => (props.menuOpen ? `225deg` : `0deg`)});
  113. transition-timing-function: cubic-bezier(
  114. ${props =>
  115. props.menuOpen ? `0.215, 0.61, 0.355, 1` : `0.55, 0.055, 0.675, 0.19`}
  116. );
  117. &:before,
  118. &:after {
  119. content: "";
  120. display: block;
  121. background-color: ${colors.green};
  122. position: absolute;
  123. left: auto;
  124. right: 0;
  125. width: ${theme.hamburgerWidth}px;
  126. height: 2px;
  127. transition-timing-function: ease;
  128. transition-duration: 0.15s;
  129. transition-property: transform;
  130. border-radius: 4px;
  131. }
  132. &:before {
  133. width: ${props => (props.menuOpen ? `100%` : `120%`)};
  134. top: ${props => (props.menuOpen ? `0` : `-10px`)};
  135. opacity: ${props => (props.menuOpen ? 0 : 1)};
  136. transition: ${props =>
  137. props.menuOpen ? theme.hamBeforeActive : theme.hamBefore};
  138. }
  139. &:after {
  140. width: ${props => (props.menuOpen ? `100%` : `80%`)};
  141. bottom: ${props => (props.menuOpen ? `0` : `-10px`)};
  142. transform: rotate(${props => (props.menuOpen ? `-90deg` : `0`)});
  143. transition: ${props =>
  144. props.menuOpen ? theme.hamAfterActive : theme.hamAfter};
  145. }
  146. `;
  147. const NavLinks = styled.div`
  148. display: flex;
  149. align-items: center;
  150. ${media.tablet`display: none;`};
  151. `;
  152. const NavList = styled.ol`
  153. div {
  154. ${mixins.flexBetween};
  155. }
  156. `;
  157. const NavListItem = styled.li`
  158. margin: 0 10px;
  159. position: relative;
  160. font-size: ${fontSizes.smallish};
  161. counter-increment: item 1;
  162. &:before {
  163. content: "0" counter(item) ".";
  164. text-align: right;
  165. color: ${colors.green};
  166. font-size: ${fontSizes.xsmall};
  167. }
  168. `;
  169. const NavLink = styled(AnchorLink)`
  170. padding: 12px 10px;
  171. `;
  172. const ResumeLink = styled.a`
  173. ${mixins.smallButton};
  174. margin-left: 10px;
  175. font-size: ${fontSizes.smallish};
  176. `;
  177.  
  178. const DELTA = 5;
  179.  
  180. class Nav extends Component {
  181. state = {
  182. isMounted: false,
  183. menuOpen: false,
  184. scrollDirection: "none",
  185. lastScrollTop: 0
  186. };
  187.  
  188. componentDidMount() {
  189. setTimeout(() => this.setState({ isMounted: true }), 100);
  190.  
  191. window.addEventListener("scroll", () => throttle(this.handleScroll()));
  192. window.addEventListener("resize", () => throttle(this.handleResize()));
  193. window.addEventListener("keydown", e => this.handleKeydown(e));
  194. }
  195.  
  196. componentWillUnmount() {
  197. this.setState({ isMounted: false });
  198.  
  199. window.removeEventListener("scroll", () => this.handleScroll());
  200. window.removeEventListener("resize", () => this.handleResize());
  201. window.removeEventListener("keydown", e => this.handleKeydown(e));
  202. }
  203.  
  204. toggleMenu = () => this.setState({ menuOpen: !this.state.menuOpen });
  205.  
  206. handleScroll = () => {
  207. const { isMounted, menuOpen, scrollDirection, lastScrollTop } = this.state;
  208. const fromTop = window.scrollY;
  209.  
  210. // Make sure they scroll more than DELTA
  211. if (!isMounted || Math.abs(lastScrollTop - fromTop) <= DELTA || menuOpen) {
  212. return;
  213. }
  214.  
  215. if (fromTop < DELTA) {
  216. this.setState({ scrollDirection: "none" });
  217. } else if (fromTop > lastScrollTop && fromTop > navHeight) {
  218. if (scrollDirection !== "down") {
  219. this.setState({ scrollDirection: "down" });
  220. }
  221. } else if (fromTop + window.innerHeight < document.body.scrollHeight) {
  222. if (scrollDirection !== "up") {
  223. this.setState({ scrollDirection: "up" });
  224. }
  225. }
  226.  
  227. this.setState({ lastScrollTop: fromTop });
  228. };
  229.  
  230. handleResize = () => {
  231. if (window.innerWidth > 768 && this.state.menuOpen) {
  232. this.toggleMenu();
  233. }
  234. };
  235.  
  236. handleKeydown = e => {
  237. if (!this.state.menuOpen) {
  238. return;
  239. }
  240.  
  241. if (e.which === 27 || e.key === "Escape") {
  242. this.toggleMenu();
  243. }
  244. };
  245.  
  246. render() {
  247. const { isMounted, menuOpen, scrollDirection } = this.state;
  248.  
  249. return (
  250. <NavContainer scrollDirection={scrollDirection}>
  251. <Navbar>
  252. <TransitionGroup>
  253. {isMounted && (
  254. <CSSTransition classNames="fade" timeout={3000}>
  255. <Hamburger onClick={this.toggleMenu}>
  256. <HamburgerBox>
  257. <HamburgerInner menuOpen={menuOpen} />
  258. </HamburgerBox>
  259. </Hamburger>
  260. </CSSTransition>
  261. )}
  262. </TransitionGroup>
  263.  
  264. <NavLinks>
  265. <NavList>
  266. <TransitionGroup>
  267. {isMounted &&
  268. navLinks &&
  269. navLinks.map(({ url, name }, i) => (
  270. <CSSTransition key={i} classNames="fadedown" timeout={3000}>
  271. <NavListItem
  272. key={i}
  273. style={{ transitionDelay: `${i * 100}ms` }}
  274. >
  275. <NavLink href={url}>{name}</NavLink>
  276. </NavListItem>
  277. </CSSTransition>
  278. ))}
  279. </TransitionGroup>
  280. </NavList>
  281.  
  282. <TransitionGroup>
  283. {isMounted && (
  284. <CSSTransition classNames="fadedown" timeout={3000}>
  285. <div style={{ transitionDelay: `600ms` }}>
  286. <ResumeLink
  287. href="/resume.pdf"
  288. target="_blank"
  289. rel="nofollow noopener noreferrer"
  290. >
  291. Resume
  292. </ResumeLink>
  293. </div>
  294. </CSSTransition>
  295. )}
  296. </TransitionGroup>
  297. </NavLinks>
  298. </Navbar>
  299.  
  300. <Menu menuOpen={menuOpen} toggleMenu={this.toggleMenu} />
  301. </NavContainer>
  302. );
  303. }
  304. }
  305.  
  306. export default Nav;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement