[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\n    \"@babel/preset-env\",\n    \"@babel/preset-react\",\n  ],\n  \"plugins\": [\n    \"@babel/plugin-proposal-class-properties\",\n    \"@babel/plugin-transform-async-to-generator\",\n    [\"babel-plugin-styled-components\", {\n      \"ssr\": true,\n      \"displayName\": true\n    }]\n  ]\n}"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"parser\": \"babel-eslint\",\n  \"parserOptions\": {\n    \"allowImportExportEverywhere\": true\n  },\n  \"extends\": [\n    \"airbnb\",\n    \"eslint:recommended\"\n  ],\n  \"plugins\": [\n    \"babel\"\n  ],\n  \"rules\": {\n    \"arrow-parens\": 0,\n    \"eol-last\": 0,\n    \"global-require\": 0,\n    \"arrow-body-style\": 0,\n    \"consistent-return\": 0,\n    \"no-unneeded-ternary\": 0,\n    \"max-len\": 0,\n    \"no-param-reassign\": 2,\n    \"new-cap\": 0,\n    \"no-console\": 0,\n    \"object-curly-spacing\": 0,\n    \"spaced-comment\": 0,\n    \"import/first\": 0,\n    \"import/no-extraneous-dependencies\": 0,\n    \"import/prefer-default-export\": 0,\n    \"import/no-mutable-exports\": 0,\n    \"import/no-named-as-default\": 0,\n    \"no-trailing-spaces\": 0,\n    \"no-underscore-dangle\": 0,\n    \"no-use-before-define\": 0,\n    \"no-duplicate-imports\": 0,\n    \"import/no-duplicates\": 1,\n    \"no-useless-escape\": 0,\n    \"no-unused-expressions\": [1 , {\"allowTernary\": true}],\n    \"react/jsx-filename-extension\": [1, { \"extensions\": [\".js\", \".jsx\"] }],\n    \"react/sort-comp\": 0,\n    \"react/prop-types\": 0,\n    \"react/destructuring-assignment\": 0\n  },\n  \"env\": {\n    \"jest\": true,\n    \"node\": true\n  },\n  \"globals\": {\n    \"jest\": true,\n    \"td\": true\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules\njspm_packages\n\n# Optional npm cache directory\n.npm\n\n# Optional REPL history\n.node_repl_history\n\n.eslintcache\n.idea\n"
  },
  {
    "path": ".npmignore",
    "content": "*\n!lib/*"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Yusinto Ngadiman\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n    <img src=\"logo.jpg\" width=\"390\" />\n</p>\n\n[![npm version](https://img.shields.io/npm/v/react-site-nav.svg?style=flat-square)](https://www.npmjs.com/package/react-site-nav) [![npm downloads](https://img.shields.io/npm/dm/react-site-nav.svg?style=flat-square)](https://www.npmjs.com/package/react-site-nav) [![npm](https://img.shields.io/npm/dt/react-site-nav.svg?style=flat-square)](https://www.npmjs.com/package/react-site-nav) [![npm](https://img.shields.io/npm/l/react-site-nav.svg?style=flat-square)](https://www.npmjs.com/package/react-site-nav)\n\n> **A beautiful site navigation bar to be proud of. Powered by styled components inspired by stripe.com** :tada:\n\n<b>Check out the live preview <a href=\"https://react-site-nav.now.sh\" target=\"_blank\">here</a> (powered by <a href=\"https://zeit.co/now\">now</a>).</b>\n\n![react-site-nav-clip](/animated.gif)\n\nYour search for the perfect site navigation bar ends here. Finally a world class navigation bar \nyou can use straight out of the box. Why use this package?\n* Beautiful animations\n* Automatic viewport detection and correction so flyouts never get rendered off screen\n* Completely customisable\n* Powered by css grid, css animations and styled components\n\nNo more compromises. Welcome to react-site-nav.\n\n## Installation\n\nyarn add react-site-nav\n\n## Quickstart\n\n```js\nimport React from 'react';\nimport {Switch, Link, Route} from 'react-router-dom';\nimport SiteNav, {ContentGroup} from 'react-site-nav'; // 1. Do this\nimport Home from './home';\nimport MyStory from './myStory';\n\nexport default () =>\n  (\n    <div>\n      {/* 2. Add SiteNav with ContentGroup as children */}\n      <SiteNav>\n        <ContentGroup title=\"About\" height=\"200\">\n          {/* 3. You can add anything in a ContentGroup */}\n          <ul>\n            {/* react router link! */}\n            <li><Link to=\"/my-story\">My Story</Link></li>\n            <li>Another list item</li>\n          </ul>\n        </ContentGroup>\n        <ContentGroup title=\"Contact\" height=\"200\">\n          Free text followed by some links.<br/>\n          <a href=\"mailto:yusinto@gmail.com\">Email</a><br/>\n          <a href=\"https://github.com/yusinto\">Github</a>\n        </ContentGroup>\n      </SiteNav>\n      <Switch>\n        <Route exact path=\"/\" component={Home}/>\n        <Route path=\"/my-story\" component={MyStory}/>\n      </Switch>\n    </div>\n  );\n\n```\n\nCheck the two [examples](https://github.com/yusinto/react-site-nav/tree/master/examples) for a fully working spa with\nreact router, server side rendering and hot reload.\n\n## Api\n### SiteNav\nThe main react component that represents the site nav. The root container is a css grid so\nmost of the props below maps directly to this grid and should be self-explanatory. Place\nContentGroup components as children of SiteNav to render the \"flyouts\".\n\n```jsx\n  <SiteNav\n    align=\"center\" /* center, left, right. This directly maps to justify-content of the root grid. */\n    columnWidth=\"150\"\n    rowHeight=\"45\"\n    background=\"#323232\"\n    color=\"#fff\"\n    fontSize=\"18\"\n    fontFamily=\"Helvetica, sans-serif\"\n    contentBackground=\"#fff\" /* Applies to all content groups */\n    contentColor=\"#323232\" /* Applies to all content groups */\n    contentTop=\"0\" /* Adjusts the distance between ContentGroups and root items */\n    breakpoint=\"768\" /* Show site nav at this breakpoint */\n    debug={false} /* Keep ContentGroups open to make debugging easier */\n  >\n    { /* These will render as flyouts */}\n    <ContentGroup>...</ContentGroup>\n    <ContentGroup>...</ContentGroup>\n  </SiteNav>\n```\n\n### ContentGroup\nEach SiteNav contains ContentGroup children components. Each ContentGroup will be rendered\nas a \"flyout\" on hover of the root items. It accepts the following props which are self-explanatory.\n\n```jsx\n  <ContentGroup \n    title=\"Products\" \n    width=\"420\"\n    height=\"270\"\n    rootUrl=\"https://some/link\" /* Optional. Render root item as a link */\n    background=\"white\" /* Optional. Overrides SiteNav contentBackground property */\n  >\n  {\n    /* You can render anything here! */\n  }\n  </ContentGroup>\n```\n\nTo render a root item as a link without a ContentGroup, you can do this:\n\n```jsx\n  <ContentGroup title=\"Open Source\" rootUrl=\"https://github.com/yusinto\" />\n```\n\nBy not specifying width and height, SiteNav assumes you just want to render the root item\nwithout a ContentGroup. Of course you can have a linked root item plus a ContentGroup. Just specify either\na width or a height so SiteNav knows you want to render the group. \n\n```jsx\n  <ContentGroup title=\"Open Source\" rootUrl=\"https://github.com/yusinto\" height=\"200\">\n    {\n      /* You can render anything here! */\n    }\n  </ContentGroup>\n```\n\nCheck the demo in my [blog](https://reactjunkie.com/)."
  },
  {
    "path": "examples/advanced/.babelrc",
    "content": "{\n  \"presets\": [\n    \"@babel/preset-env\",\n    \"@babel/preset-react\"\n  ],\n  \"plugins\": [\n    \"@babel/plugin-proposal-class-properties\",\n    \"@babel/plugin-transform-async-to-generator\",\n    [\"babel-plugin-styled-components\", {\n      \"ssr\": true,\n      \"displayName\": true\n    }]\n  ]\n}"
  },
  {
    "path": "examples/advanced/.eslintrc",
    "content": "{\n  \"parser\": \"babel-eslint\",\n  \"parserOptions\": {\n    \"allowImportExportEverywhere\": true\n  },\n  \"extends\": [\n    \"airbnb\",\n    \"eslint:recommended\",\n    \"plugin:react/recommended\"\n  ],\n  \"plugins\": [\n    \"babel\"\n  ],\n  \"rules\": {\n    \"arrow-parens\": 0,\n    \"eol-last\": 0,\n    \"global-require\": 0,\n    \"arrow-body-style\": 0,\n    \"consistent-return\": 0,\n    \"no-unneeded-ternary\": 0,\n    \"max-len\": 0,\n    \"no-param-reassign\": 2,\n    \"new-cap\": 0,\n    \"no-console\": 0,\n    \"object-curly-spacing\": 0,\n    \"spaced-comment\": 0,\n    \"import/no-extraneous-dependencies\": 0,\n    \"import/first\": 0,\n    \"import/prefer-default-export\": 0,\n    \"import/no-mutable-exports\": 0,\n    \"import/no-named-as-default\": 0,\n    \"react/jsx-filename-extension\": 0,\n    \"react/jsx-indent\": 0,\n    \"react/jsx-indent-props\": 0,\n    \"react/jsx-space-before-closing\": 0,\n    \"react/jsx-first-prop-new-line\": 0,\n    \"react/prefer-stateless-function\": 0,\n    \"react/jsx-closing-bracket-location\": 0,\n    \"react/require-extension\": 0,\n    \"react/sort-comp\": 0,\n    \"react/jsx-wrap-multilines\": 0,\n    \"react/jsx-no-bind\": 0,\n    \"react/jsx-users-react\": 0,\n    \"react/jsx-tag-spacing\": 0,\n    \"jsx-a11y/anchor-is-valid\": 0,\n    \"jsx-a11y/img-has-alt\": 0,\n    \"no-trailing-spaces\": 0,\n    \"no-underscore-dangle\": 0,\n    \"no-use-before-define\": 0,\n    \"no-duplicate-imports\": 0,\n    \"import/no-duplicates\": 1,\n    \"no-useless-escape\": 0,\n    \"no-unused-expressions\": [1 , {\"allowTernary\": true}]\n  },\n  \"env\": {\n    \"browser\": true,\n    \"jest\": true,\n    \"node\": true\n  },\n  \"globals\": {\n    \"React\": true,\n    \"fetch\": true,\n    \"jest\": true\n  }\n}\n"
  },
  {
    "path": "examples/advanced/.gitignore",
    "content": "node_modules\n.idea\nnpm-debug.log\ndist\n.eslintcache\nnow/build"
  },
  {
    "path": "examples/advanced/README.md",
    "content": "# react-site-nav advanced example\n\nLive demo [here](https://react-site-nav.now.sh).\n\nyarn && yarn start\n"
  },
  {
    "path": "examples/advanced/now/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <title>React Site Nav - Advanced</title>\n</head>\n<body>\n<div id=\"reactDiv\"/>\n<script type=\"application/javascript\" src=\"build/bundle.js\"></script>\n</body>\n</html>"
  },
  {
    "path": "examples/advanced/now/now.json",
    "content": "{\n  \"name\": \"react-site-nav\",\n  \"alias\": \"react-site-nav\",\n  \"public\": true\n}"
  },
  {
    "path": "examples/advanced/package.json",
    "content": "{\n  \"name\": \"react-site-menu-example\",\n  \"version\": \"1.1.0\",\n  \"description\": \"Demo of react-site-menu a kickass navigation menu inspired by stripe.com\",\n  \"main\": \"src/server/index.js\",\n  \"scripts\": {\n    \"start\": \"node src/server/index.js\",\n    \"lint\": \"eslint ./src\",\n    \"serve\": \"webpack-serve webpack.config.server\",\n    \"build\": \"rimraf now/build/* && webpack --config webpack.prod.config.js\",\n    \"now\": \"npm run build && cd ./now && now && now alias\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/yusinto/react-site-menu.git\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"site\",\n    \"navigation\",\n    \"menu\",\n    \"bar\",\n    \"animated\",\n    \"stripe\"\n  ],\n  \"author\": \"Yus Ng\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/yusinto/react-site-menu\"\n  },\n  \"homepage\": \"https://github.com/yusinto/react-site-menu\",\n  \"dependencies\": {\n    \"@babel/plugin-transform-async-to-generator\": \"^7.0.0-beta.54\",\n    \"@babel/polyfill\": \"^7.0.0-beta.54\",\n    \"babel-plugin-styled-components\": \"^1.5.1\",\n    \"express\": \"^4.16.3\",\n    \"lodash\": \"^4.17.11\",\n    \"lodash.kebabcase\": \"^4.1.1\",\n    \"memoize-one\": \"^4.0.2\",\n    \"prop-types\": \"^15.6.2\",\n    \"react\": \"^16.5.0\",\n    \"react-dom\": \"^16.5.0\",\n    \"react-router-dom\": \"^4.3.1\",\n    \"react-site-nav\": \"^0.2.1\",\n    \"styled-components\": \"^3.4.6\"\n  },\n  \"devDependencies\": {\n    \"@babel/cli\": \"^7.0.0-beta.54\",\n    \"@babel/core\": \"^7.0.0-beta.54\",\n    \"@babel/plugin-proposal-class-properties\": \"^7.0.0-beta.54\",\n    \"@babel/preset-env\": \"^7.0.0-beta.54\",\n    \"@babel/preset-react\": \"^7.0.0-beta.54\",\n    \"babel-eslint\": \"^8.2.6\",\n    \"babel-loader\": \"^8.0.0-beta\",\n    \"eslint\": \"^5.5.0\",\n    \"eslint-config-airbnb\": \"^17.1.0\",\n    \"eslint-plugin-import\": \"^2.14.0\",\n    \"eslint-plugin-jsx-a11y\": \"^6.1.1\",\n    \"eslint-plugin-react\": \"^7.11.1\",\n    \"file-loader\": \"^2.0.0\",\n    \"rimraf\": \"^2.6.2\",\n    \"universal-hot-reload\": \"^1.0.6\",\n    \"webpack\": \"^4.18.0\",\n    \"webpack-cli\": \"^3.1.0\",\n    \"webpack-node-externals\": \"^1.7.2\",\n    \"webpack-serve\": \"^2.0.2\"\n  }\n}\n"
  },
  {
    "path": "examples/advanced/src/client/index.js",
    "content": "import React from 'react';\nimport {hydrate} from 'react-dom';\nimport {BrowserRouter} from 'react-router-dom';\nimport App from '../universal/app/app';\n\nhydrate(\n  <BrowserRouter>\n    <App/>\n  </BrowserRouter>,\n  document.getElementById('reactDiv'),\n);"
  },
  {
    "path": "examples/advanced/src/server/index.js",
    "content": "const UniversalHotReload = require('universal-hot-reload').default;\nUniversalHotReload(require('../../webpack.config.server.js'), require('../../webpack.config.client.js'));"
  },
  {
    "path": "examples/advanced/src/server/server.js",
    "content": "import Express from 'express';\nimport React from 'react';\nimport {renderToString} from 'react-dom/server';\nimport {ServerStyleSheet, StyleSheetManager} from 'styled-components';\nimport {StaticRouter} from 'react-router-dom';\nimport App from '../universal/app/app';\n\nconst PORT = 3000;\nconst app = Express();\n\napp.use('/dist', Express.static('dist', {maxAge: '1d'}));\n\napp.use((req, res) => {\n  const sheet = new ServerStyleSheet();\n  const StyledApp =\n    <StyleSheetManager sheet={sheet.instance}>\n      <App/>\n    </StyleSheetManager>;\n  const styleTags = sheet.getStyleTags();\n\n  const html = `<!DOCTYPE html>\n                    <html>\n                      <head>\n                        <meta charset=\"utf-8\">\n                        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n                        <title>React Site Nav - Advanced</title>\n                        ${styleTags}\n                      </head>\n                      <body>\n                        <div id=\"reactDiv\">${renderToString(\n    <StaticRouter\n      location={req.url}\n      context={{}}>\n      <App/>\n    </StaticRouter>)}\n                        </div>\n                        <script type=\"application/javascript\" src=\"http://localhost:3002/dist/bundle.js\"></script>\n                      </body>\n                    </html>`;\n\n  res.end(html);\n});\n\nconst httpServer = app.listen(PORT, () => {\n  console.log(`Example app listening at ${PORT}...`);\n});\n\n// export httpServer object so universal-hot-reload can access it\nmodule.exports = httpServer;\n"
  },
  {
    "path": "examples/advanced/src/universal/app/app.js",
    "content": "import React, {Component} from 'react';\nimport {Switch, Link, Route, Redirect} from 'react-router-dom';\nimport styled, {injectGlobal} from 'styled-components';\nimport SiteNav, {ContentGroup} from 'react-site-nav';\nimport Home from '../home';\nimport Contact from '../contact';\nimport logo from '../../../assets/logo-transparent.png';\nimport ProductsContentGroup from './products';\nimport Developers from './developers';\nimport Company from './company';\nimport Pricing from './pricing';\n\ninjectGlobal`\n  // auto-generated from https://www.svgbackgrounds.com/#abstract-envelope\n  body {\n    background-color: #77aa77;\n    background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25' viewBox='0 0 2 1'%3E%3Cdefs%3E%3ClinearGradient id='a' gradientUnits='userSpaceOnUse' x1='0' x2='0' y1='0' y2='1' gradientTransform='rotate(0,0.5,0.5)'%3E%3Cstop offset='0' stop-color='%2377aa77'/%3E%3Cstop offset='1' stop-color='%234fd'/%3E%3C/linearGradient%3E%3ClinearGradient id='b' gradientUnits='userSpaceOnUse' x1='0' y1='0' x2='0' y2='1' gradientTransform='rotate(360,0.5,0.5)'%3E%3Cstop offset='0' stop-color='%23cf8' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23cf8' stop-opacity='1'/%3E%3C/linearGradient%3E%3ClinearGradient id='c' gradientUnits='userSpaceOnUse' x1='0' y1='0' x2='2' y2='2' gradientTransform='rotate(105,0.5,0.5)'%3E%3Cstop offset='0' stop-color='%23cf8' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23cf8' stop-opacity='1'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect x='0' y='0' fill='url(%23a)' width='2' height='1'/%3E%3Cg fill-opacity='0.85'%3E%3Cpolygon fill='url(%23b)' points='0 1 0 0 2 0'/%3E%3Cpolygon fill='url(%23c)' points='2 1 2 0 0 0'/%3E%3C/g%3E%3C/svg%3E\");\n    background-attachment: fixed;\n    background-size: cover;\n    background-position: center;\n  }\n  \n  a {\n    text-decoration: none;\n  }\n  \n  a:visited {\n    color: lightslategray;\n  }\n  \n  ul {\n    list-style-type: none;\n    margin: 0;\n    padding: 0;\n  }\n`;\nconst Header = styled.div`\n  display: grid;\n  grid-template-columns: 400px auto;\n  grid-template-rows: 80px;\n  background: transparent;\n`;\n\nexport default class App extends Component {\n  render() {\n    return (\n      <div>\n        <header>\n          <Header>\n            <Link to=\"/\">\n              <img alt=\"logo\" style={{height: '80px', marginLeft: '20px', marginTop: '0px'}} src={logo}/>\n            </Link>\n          </Header>\n          <SiteNav\n            background=\"transparent\"\n            fontSize=\"18\"\n            fontFamily=\"Helvetica, sans-serif\"\n          >\n            <ContentGroup title=\"Products\" width=\"420\" height=\"270\">\n              <ProductsContentGroup/>\n            </ContentGroup>\n            <ContentGroup title=\"Developers\" width=\"370\" height=\"300\">\n              <Developers/>\n            </ContentGroup>\n            <ContentGroup title=\"Company\" width=\"260\" height=\"220\">\n              <Company/>\n            </ContentGroup>\n            <ContentGroup title=\"Pricing\" width=\"420\" height=\"300\">\n              <Pricing/>\n            </ContentGroup>\n          </SiteNav>\n        </header>\n        <main>\n          <Switch>\n            <Route exact path=\"/\" component={Home}/>\n            <Route path=\"/home\">\n              <Redirect to=\"/\"/>\n            </Route>\n            <Route path=\"/contact\" component={Contact}/>\n          </Switch>\n        </main>\n      </div>\n    );\n  }\n}"
  },
  {
    "path": "examples/advanced/src/universal/app/company.js",
    "content": "import React from 'react';\nimport styled from 'styled-components';\nimport aboutMeIcon from '../../../assets/about-me.png';\nimport customersIcon from '../../../assets/customers.png';\nimport jobsIcon from '../../../assets/jobs.png';\nimport environmentIcon from '../../../assets/environment.png';\n\nconst List = styled.ul`\n  display: flex;\n  flex-direction: column;\n  margin-top: 10px;\n  margin-left: 30px;\n`;\nconst ListItem = styled.li`\n  display: flex;\n  flex-direction: row;\n  margin-top: 20px;\n  align-items: center;\n`;\nconst Heading = styled.div`\n  margin: 0;\n  color: #6772e5;\n  font-size: 16px;\n  line-height: 22px;\n  font-weight: 600;\n  letter-spacing: .025em;\n  margin-left: 10px;\n`;\nconst StyledLink = styled.a`\n  display: flex;\n  align-items: center;\n  &:hover {\n    opacity: 0.7;\n  }\n`;\n\nexport default () => {\n  return (\n    <List>\n      <ListItem>\n        <StyledLink href=\"http://reactjunkie.com\">\n          <img src={aboutMeIcon} width={24} height={24}/>\n          <Heading>ABOUT ME</Heading>\n        </StyledLink>\n      </ListItem>\n      <ListItem>\n        <img src={customersIcon} width={24} height={24}/>\n        <Heading>CUSTOMERS</Heading>\n      </ListItem>\n      <ListItem>\n        <img src={jobsIcon} width={24} height={24}/>\n        <Heading>JOBS</Heading>\n      </ListItem>\n      <ListItem>\n        <img src={environmentIcon} width={24} height={24}/>\n        <Heading>ENVIRONMENT</Heading>\n      </ListItem>\n    </List>\n  );\n};"
  },
  {
    "path": "examples/advanced/src/universal/app/developers.js",
    "content": "import React from 'react';\nimport styled from 'styled-components';\nimport documentIcon from '../../../assets/documentation.png';\n\nconst RootGrid = styled.div`\n  display: grid;\n  grid-template-columns: 60px auto;\n  grid-template-rows: [top-space] 30px [doco-row] 60px [row-space] 20px [list-row] 60px;\n  font-size: 15px;\n  color: lightslategray;\n`;\nconst DocoLogo = styled.div`\n  grid-row: doco-row / span 1;\n  grid-column: 1 / span 1;\n  display: flex;\n  justify-content: flex-end;\n  padding-right: 10px;\n`;\nconst HeadingText = styled.div`\n  grid-row: doco-row / span 1;\n  grid-column: 2 / span 1;\n  display: flex;\n  flex-direction: column;\n  justify-content: flex-start;\n  font-size: 15px;\n`;\nconst DocumentationHeading = styled.div`\n  margin: 0;\n  padding-bottom: 10px;\n  color: #6772e5;\n  font-size: 16px;\n  line-height: 22px;\n  font-weight: 600;\n  letter-spacing: .025em;\n`;\nconst ListGroupGrid = styled.div`\n  grid-row: list-row / span 1;\n  grid-column: 2 / span 1;\n  display: grid;\n  grid-template-columns: [get-started] auto [popular-topics] auto;\n  grid-template-rows: 120px;\n`;\nconst GetStartedGridItem = styled.div`\n  grid-row: 1 / span 1;\n  grid-column: get-started / span 1;\n  display: flex;\n  flex-direction: column;\n  justify-content: flex-start;\n`;\nconst PopularGridItem = styled.div`\n  grid-row: 1 / span 1;\n  grid-column: popular-topics / span 1;\n  display: flex;\n  flex-direction: column;\n  justify-content: flex-start;\n`;\nconst ColumnHeading = styled.div`\n  color: #8898aa; \n  font-size: 14px;\n`;\nconst List = styled.ul`\n  color: #6772e5;\n  margin-top: 5px;\n  margin-bottom: 5px;\n  font-size: 14px\n`;\nconst ListItem = styled.li`\n  margin-top: 8px;\n  margin-bottom: 8px;\n`;\n\nexport default () => {\n  return (\n    <RootGrid>\n      <DocoLogo>\n        <img src={documentIcon} width={24} height={24}/>\n      </DocoLogo>\n      <HeadingText>\n        <DocumentationHeading>DOCUMENTATION</DocumentationHeading>\n        <span>Start integrating products and tools.</span>\n      </HeadingText>\n      <ListGroupGrid>\n        <GetStartedGridItem>\n          <ColumnHeading>GET STARTED</ColumnHeading>\n          <List>\n            <ListItem>Elements</ListItem>\n            <ListItem>Checkout</ListItem>\n            <ListItem>Mobile apps</ListItem>\n            <ListItem>Libraries</ListItem>\n          </List>\n        </GetStartedGridItem>\n        <PopularGridItem>\n          <ColumnHeading>POPULAR TOPICS</ColumnHeading>\n          <List>\n            <ListItem>Apple Pay</ListItem>\n            <ListItem>Testing</ListItem>\n            <ListItem>Launch checklist</ListItem>\n            <ListItem>Plug-ins</ListItem>\n          </List>\n        </PopularGridItem>\n      </ListGroupGrid>\n    </RootGrid>\n  );\n};"
  },
  {
    "path": "examples/advanced/src/universal/app/pricing.js",
    "content": "import React from 'react';\nimport styled from 'styled-components';\nimport smiley from '../../../assets/smiley.png';\n\nconst List = styled.ul`\n  display: flex;\n  flex-direction: column;\n  margin-top: 10px;\n  margin-left: 30px;\n  color: lightslategray;\n`;\nconst ListItem = styled.li`\n  display: flex;\n  flex-direction: row;\n  margin-top: 20px;\n  align-items: center;\n  a:hover {\n    opacity: 0.7;\n  }\n`;\nconst Heading = styled.div`\n  margin: 0;\n  color: #6772e5;\n  font-size: 16px;\n  line-height: 22px;\n  font-weight: 600;\n  letter-spacing: .025em;\n`;\nconst HeadingText = styled.div`\n  display: flex;\n  flex-direction: column;\n  margin-left: 10px;\n`;\nconst Text = styled.div`\n  font-size: 13px;\n`;\nconst StyledLink = styled.a`\n  display: flex;\n  align-items: center;\n  &:hover {\n    opacity: 0.7;\n  }\n`;\n\nexport default () => {\n  return (\n    <List>\n      <ListItem>\n        <StyledLink href=\"https://github.com/yusinto/react-site-nav\">\n          <img src={smiley} width={24} height={24}/>\n          <HeadingText>\n            <Heading>STAR IT!</Heading>\n            <Text>github.com/yusinto/react-site-nav</Text>\n          </HeadingText>\n        </StyledLink>\n      </ListItem>\n    </List>\n  );\n};"
  },
  {
    "path": "examples/advanced/src/universal/app/products.js",
    "content": "import React from 'react';\nimport styled from 'styled-components';\nimport {Link} from 'react-router-dom';\nimport paymentIcon from '../../../assets/payment.png';\nimport billingIcon from '../../../assets/billing.png';\nimport connectIcon from '../../../assets/connect.png';\n\nconst ListContainer = styled.div`\n  display: flex;\n  height: 100%;\n  width: 100%;\n  justify-content: center;\n`;\nconst List = styled.ul`\n  color: lightslategray;\n  display: flex;\n  flex-direction: column;\n  justify-content: space-evenly;\n`;\nconst ListItemContent = styled.div`\n  display: flex;\n  flex-direction: row;\n  &:hover {\n    opacity: 0.7;\n  }\n`;\nconst LisItemHeadingText = styled.div`\n  display: flex;\n  flex-direction: column;\n  justify-content: space-between;\n  font-size: 15px;\n  margin-left: 10px;\n`;\nconst ListItemHeading = styled.div`\n  margin: 0;\n  color: #6772e5;\n  font-size: 16px;\n  line-height: 22px;\n  font-weight: 600;\n  letter-spacing: .025em;\n`;\n\nexport default () => {\n  return (\n    <ListContainer>\n      <List>\n        <li>\n          <Link to=\"/contact\">\n            <ListItemContent>\n              <img src={paymentIcon} width={48} height={48}/>\n              <LisItemHeadingText>\n                <ListItemHeading>PAYMENTS</ListItemHeading>\n                <div>A complete payments platform engineered.</div>\n              </LisItemHeadingText>\n            </ListItemContent>\n          </Link>\n        </li>\n        <li>\n          <ListItemContent>\n            <img src={billingIcon} width={48} height={48}/>\n            <LisItemHeadingText>\n              <ListItemHeading>BILLING</ListItemHeading>\n              <div>Build and scale your recurring business model.</div>\n            </LisItemHeadingText>\n          </ListItemContent>\n        </li>\n        <li>\n          <ListItemContent>\n            <img src={connectIcon} width={48} height={48}/>\n            <LisItemHeadingText>\n              <ListItemHeading>CONNECT</ListItemHeading>\n              <div>Everything platforms need to get sellers paid.</div>\n            </LisItemHeadingText>\n          </ListItemContent>\n        </li>\n      </List>\n    </ListContainer>\n  );\n};"
  },
  {
    "path": "examples/advanced/src/universal/contact.js",
    "content": "import React, {Component, Timeout} from 'react';\nimport styled from 'styled-components';\n\nconst RootDiv = styled.div`\n  margin-top: 30px; \n  margin-left: 30px; \n  color: #fff;\n`;\nconst Heading = styled.h1`\n  font-weight: 400;\n  color: #fff;\n`;\nexport default props =>\n  <RootDiv>\n    <Heading>Thanks for checking react-site-nav!</Heading>\n    <p>\n      Check out my blog at <a href=\"http://reactjunkie.com\" target=\"_blank\"\n                              rel=\"noopener noreferrer\">reactjunkie.com</a>\n    </p>\n    <div>You can reach me via:</div>\n    <ul>\n      <li>\n        <a href=\"mailto:yusinto@gmail.com\">Email</a>\n      </li>\n    </ul>\n  </RootDiv>;\n"
  },
  {
    "path": "examples/advanced/src/universal/home.js",
    "content": "import React, {Component} from 'react';\nimport styled from 'styled-components';\n\nconst RootDiv = styled.div`\n  margin-top: 30px; \n  margin-left: 30px; \n  color: #fff;\n`;\nconst Heading = styled.h1`\n  font-weight: 400;\n  color: #fff;\n`;\n\nexport default class Home extends Component {\n  render() {\n    return (\n      <RootDiv>\n        <Heading>React Site Nav</Heading>\n        The new standard in site navigation.\n      </RootDiv>\n    );\n  }\n}"
  },
  {
    "path": "examples/advanced/webpack.config.client.js",
    "content": "const path = require('path');\n\nconst WebpackServeUrl = 'http://localhost:3002';\n\nmodule.exports = {\n  mode: 'development',\n  devtool: 'source-map',\n  entry: ['@babel/polyfill', './src/client/index'],\n  output: {\n    path: path.resolve('dist'),\n    publicPath: `${WebpackServeUrl}/dist/`, // MUST BE FULL PATH!\n    filename: 'bundle.js',\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.jsx?$/,\n        include: path.resolve('src'),\n        exclude: /node_modules/,\n        loader: 'babel-loader',\n        options: {\n          cacheDirectory: true,\n        },\n      },\n      {\n        test: /\\.(png|jpg|gif)$/,\n        use: [\n          {\n            loader: 'file-loader',\n            options: {\n              publicPath: 'dist/',\n            }\n          }\n        ]\n      }\n    ],\n  },\n};"
  },
  {
    "path": "examples/advanced/webpack.config.server.js",
    "content": "const path = require('path');\nconst nodeExternals = require('webpack-node-externals');\n\nmodule.exports = {\n  mode: 'development',\n  devtool: 'source-map',\n  entry: ['@babel/polyfill', './src/server/server.js'], // set this to your server entry point. This should be where you start your express server with .listen()\n  target: 'node', // tell webpack this bundle will be used in nodejs environment.\n  externals: [nodeExternals()], // Omit node_modules code from the bundle. You don't want and don't need them in the bundle.\n  output: {\n    path: path.resolve('dist'),\n    filename: 'serverBundle.js',\n    libraryTarget: 'commonjs2', // IMPORTANT! Add module.exports to the beginning of the bundle, so universal-hot-reload can access your app.\n  },\n  // The rest of the config is pretty standard and can contain other webpack stuff you need.\n  module: {\n    rules: [\n      {\n        test: /\\.jsx?$/,\n        include: path.resolve('src'),\n        exclude: /node_modules/,\n        loader: 'babel-loader',\n        options: {\n          cacheDirectory: true,\n        },\n      },\n      {\n        test: /\\.(png|jpg|gif)$/,\n        use: [\n          {\n            loader: 'file-loader',\n            options: {\n              publicPath: 'dist/',\n            }\n          }\n        ]\n      }\n    ],\n  },\n};"
  },
  {
    "path": "examples/advanced/webpack.prod.config.js",
    "content": "const path = require('path');\n\nmodule.exports = {\n  mode: 'production',\n  devtool: 'source-map',\n  entry: ['@babel/polyfill', './src/client/index'],\n  output: {\n    path: path.resolve('now/build'),\n    filename: 'bundle.js',\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.jsx?$/,\n        include: path.resolve('src'),\n        exclude: /node_modules/,\n        loader: 'babel-loader',\n        options: {\n          cacheDirectory: true,\n        },\n      },\n      {\n        test: /\\.(png|jpg|gif)$/,\n        use: [\n          {\n            loader: 'file-loader',\n            options: {\n              publicPath: 'build/',\n            }\n          }\n        ]\n      }\n    ],\n  },\n};"
  },
  {
    "path": "examples/basic/.babelrc",
    "content": "{\n  \"presets\": [\n    \"@babel/preset-env\",\n    \"@babel/preset-react\"\n  ],\n  \"plugins\": [\n    \"@babel/plugin-proposal-class-properties\",\n    \"@babel/plugin-transform-async-to-generator\",\n    [\"babel-plugin-styled-components\", {\n      \"ssr\": true,\n      \"displayName\": true\n    }]\n  ]\n}"
  },
  {
    "path": "examples/basic/.eslintrc",
    "content": "{\n  \"parser\": \"babel-eslint\",\n  \"parserOptions\": {\n    \"allowImportExportEverywhere\": true\n  },\n  \"extends\": [\n    \"airbnb\",\n    \"eslint:recommended\",\n    \"plugin:react/recommended\"\n  ],\n  \"plugins\": [\n    \"babel\"\n  ],\n  \"rules\": {\n    \"arrow-parens\": 0,\n    \"eol-last\": 0,\n    \"global-require\": 0,\n    \"arrow-body-style\": 0,\n    \"consistent-return\": 0,\n    \"no-unneeded-ternary\": 0,\n    \"max-len\": 0,\n    \"no-param-reassign\": 2,\n    \"new-cap\": 0,\n    \"no-console\": 0,\n    \"object-curly-spacing\": 0,\n    \"spaced-comment\": 0,\n    \"import/no-extraneous-dependencies\": 0,\n    \"import/first\": 0,\n    \"import/prefer-default-export\": 0,\n    \"import/no-mutable-exports\": 0,\n    \"import/no-named-as-default\": 0,\n    \"react/jsx-filename-extension\": 0,\n    \"react/jsx-indent\": 0,\n    \"react/jsx-indent-props\": 0,\n    \"react/jsx-space-before-closing\": 0,\n    \"react/jsx-first-prop-new-line\": 0,\n    \"react/prefer-stateless-function\": 0,\n    \"react/jsx-closing-bracket-location\": 0,\n    \"react/require-extension\": 0,\n    \"react/sort-comp\": 0,\n    \"react/jsx-wrap-multilines\": 0,\n    \"react/jsx-no-bind\": 0,\n    \"react/jsx-users-react\": 0,\n    \"react/jsx-tag-spacing\": 0,\n    \"jsx-a11y/anchor-is-valid\": 0,\n    \"jsx-a11y/img-has-alt\": 0,\n    \"no-trailing-spaces\": 0,\n    \"no-underscore-dangle\": 0,\n    \"no-use-before-define\": 0,\n    \"no-duplicate-imports\": 0,\n    \"import/no-duplicates\": 1,\n    \"no-useless-escape\": 0,\n    \"no-unused-expressions\": [1 , {\"allowTernary\": true}]\n  },\n  \"env\": {\n    \"browser\": true,\n    \"jest\": true,\n    \"node\": true\n  },\n  \"globals\": {\n    \"React\": true,\n    \"fetch\": true,\n    \"jest\": true\n  }\n}\n"
  },
  {
    "path": "examples/basic/.gitignore",
    "content": "node_modules\n.idea\nnpm-debug.log\ndist\n.eslintcache"
  },
  {
    "path": "examples/basic/README.md",
    "content": "# react-site-nav basic example\n\nA simple spa demonstrating react-site-nav.\n\nyarn && yarn start\n"
  },
  {
    "path": "examples/basic/package.json",
    "content": "{\n  \"name\": \"react-site-menu-example\",\n  \"version\": \"1.1.0\",\n  \"description\": \"Demo of react-site-menu a kickass navigation menu inspired by stripe.com\",\n  \"main\": \"src/server/index.js\",\n  \"scripts\": {\n    \"start\": \"node src/server/index.js\",\n    \"lint\": \"eslint ./src\",\n    \"serve\": \"webpack-serve webpack.config.server\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/yusinto/react-site-menu.git\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"site\",\n    \"navigation\",\n    \"menu\",\n    \"bar\",\n    \"animated\",\n    \"stripe\"\n  ],\n  \"author\": \"Yus Ng\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/yusinto/react-site-menu\"\n  },\n  \"homepage\": \"https://github.com/yusinto/react-site-menu\",\n  \"dependencies\": {\n    \"@babel/plugin-transform-async-to-generator\": \"^7.0.0-beta.54\",\n    \"@babel/polyfill\": \"^7.0.0-beta.54\",\n    \"babel-plugin-styled-components\": \"^1.5.1\",\n    \"css-loader\": \"^1.0.0\",\n    \"express\": \"^4.16.3\",\n    \"lodash\": \"^4.17.11\",\n    \"lodash.kebabcase\": \"^4.1.1\",\n    \"memoize-one\": \"^4.0.2\",\n    \"prop-types\": \"^15.6.2\",\n    \"react\": \"^16.5.0\",\n    \"react-dom\": \"^16.5.0\",\n    \"react-router-dom\": \"^4.3.1\",\n    \"react-site-nav\": \"^0.2.6\",\n    \"style-loader\": \"^0.23.0\",\n    \"styled-components\": \"^3.4.6\"\n  },\n  \"devDependencies\": {\n    \"@babel/cli\": \"^7.0.0-beta.54\",\n    \"@babel/core\": \"^7.0.0-beta.54\",\n    \"@babel/plugin-proposal-class-properties\": \"^7.0.0-beta.54\",\n    \"@babel/preset-env\": \"^7.0.0-beta.54\",\n    \"@babel/preset-react\": \"^7.0.0-beta.54\",\n    \"babel-eslint\": \"^8.2.6\",\n    \"babel-loader\": \"^8.0.0-beta\",\n    \"eslint\": \"^5.5.0\",\n    \"eslint-config-airbnb\": \"^17.1.0\",\n    \"eslint-plugin-import\": \"^2.14.0\",\n    \"eslint-plugin-jsx-a11y\": \"^6.1.1\",\n    \"eslint-plugin-react\": \"^7.11.1\",\n    \"file-loader\": \"^2.0.0\",\n    \"universal-hot-reload\": \"^1.0.6\",\n    \"webpack\": \"^4.18.0\",\n    \"webpack-cli\": \"^3.1.0\",\n    \"webpack-node-externals\": \"^1.7.2\",\n    \"webpack-serve\": \"^2.0.2\"\n  }\n}\n"
  },
  {
    "path": "examples/basic/src/client/index.js",
    "content": "import React from 'react';\nimport {hydrate} from 'react-dom';\nimport {BrowserRouter} from 'react-router-dom';\nimport App from '../universal/app';\n\nhydrate(\n  <BrowserRouter>\n    <App/>\n  </BrowserRouter>,\n  document.getElementById('reactDiv'),\n);"
  },
  {
    "path": "examples/basic/src/server/index.js",
    "content": "const UniversalHotReload = require('universal-hot-reload').default;\nUniversalHotReload(require('../../webpack.config.server.js'), require('../../webpack.config.client.js'));"
  },
  {
    "path": "examples/basic/src/server/server.js",
    "content": "import Express from 'express';\nimport React from 'react';\nimport {renderToString} from 'react-dom/server';\nimport {ServerStyleSheet, StyleSheetManager} from 'styled-components';\nimport {StaticRouter} from 'react-router-dom';\nimport App from '../universal/app';\n\nconst PORT = 3000;\nconst app = Express();\n\napp.use('/dist', Express.static('dist', {maxAge: '1d'}));\n\napp.use((req, res) => {\n  const html = `<!DOCTYPE html>\n                    <html>\n                      <head>\n                        <meta charset=\"utf-8\">\n                        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n                        <title>React Site Nav - Basic</title>\n                      </head>\n                      <body style=\"margin:0;background: #F6F9FC;\">\n                        <div id=\"reactDiv\">${renderToString(\n    <StaticRouter\n      location={req.url}\n      context={{}}>\n      <App/>\n    </StaticRouter>)}\n                        </div>\n                        <script type=\"application/javascript\" src=\"http://localhost:3002/dist/bundle.js\"></script>\n                      </body>\n                    </html>`;\n\n  res.end(html);\n});\n\nconst httpServer = app.listen(PORT, () => {\n  console.log(`Example app listening at ${PORT}...`);\n});\n\n// export httpServer object so universal-hot-reload can access it\nmodule.exports = httpServer;\n"
  },
  {
    "path": "examples/basic/src/universal/app.css",
    "content": "h1 {\n\n}\n\nul {\n    list-style-type: none;\n}\n\nli {\n    margin-top: 10px\n}\n\na {\n    text-decoration: none;\n    color: #24b47e;\n}\n\na:hover {\n    opacity: 0.5;\n}\n\na:visited {\n    color: #6b7c93;\n}\n\n.header {\n    display: grid;\n    grid-template-columns: 100px auto 100px;\n    grid-template-rows: 80px;\n    background: #fff;\n}\n\n.logo {\n    height: 70px;\n    margin-left: 20px;\n    margin-top: 5px\n}"
  },
  {
    "path": "examples/basic/src/universal/app.js",
    "content": "import React, {Component} from 'react';\nimport {Switch, Link, Route, Redirect} from 'react-router-dom';\nimport Home from './home';\nimport SpaLink1 from './spaLink1';\nimport SpaLink2 from './spaLink2';\nimport SiteNav, {ContentGroup} from 'react-site-nav';\nimport './app.css';\nimport logo from '../../assets/logo.jpg';\n\nexport default () =>\n  (\n    <div>\n      <header>\n        <div style={{width: '100%', background: 'white'}}>\n          <Link to=\"/\">\n            <img style={{height: '70px', marginLeft: '20px', marginTop: '5px'}} src={logo}/>\n          </Link>\n        </div>\n        <SiteNav>\n          <ContentGroup title=\"About\" width=\"300\" height=\"100\">\n            <ul>\n              <li><Link to=\"/spa-link-1\">Spa Link</Link></li>\n              <li><Link to=\"/spa-link-2\">Another Spa Link</Link></li>\n            </ul>\n          </ContentGroup>\n          <ContentGroup title=\"Contact\" width=\"200\" height=\"150\">\n            <ul style={{listStyleType: 'none'}}>\n              <li>\n                <a href=\"mailto:yusinto@gmail.com\">Email</a>\n              </li>\n              <li>\n                <a href=\"https://github.com/yusinto\">Github</a>\n              </li>\n              <li>\n                <a href=\"https://twitter.com/yusinto\">Twitter</a>\n              </li>\n              <li>\n                <a href=\"https://linkedin.com/in/yusinto\">Tinkedin</a>\n              </li>\n            </ul>\n          </ContentGroup>\n        </SiteNav>\n      </header>\n      <main>\n        <Switch>\n          <Route exact path=\"/\" component={Home}/>\n          <Route path=\"/home\">\n            <Redirect to=\"/\"/>\n          </Route>\n          <Route path=\"/spa-link-1\" component={SpaLink1}/>\n          <Route path=\"/spa-link-2\" component={SpaLink2}/>\n        </Switch>\n      </main>\n    </div>\n  );\n"
  },
  {
    "path": "examples/basic/src/universal/home.js",
    "content": "import React, {Component} from 'react';\n\nexport default class Home extends Component {\n  render() {\n    return (\n      <div style={{marginTop: '30px', marginLeft: '30px'}}>\n        <h1>react-site-nav</h1>\n        <p>Welcome to react-site-nav! This is a basic demo of its usage.</p>\n        <div>Prs and comments welcome!</div>\n      </div>\n    );\n  }\n}"
  },
  {
    "path": "examples/basic/src/universal/spaLink1.js",
    "content": "import React, {Component, Timeout} from 'react';\n\nexport default props =>\n  <div style={{marginTop: '30px', marginLeft: '30px'}}>\n    <h1>This is spa link #1</h1>\n    <p>Thanks for checking react-site-nav!</p>\n    <p>\n      Check out my blog at <a href=\"http://reactjunkie.com\" target=\"_blank\"\n                              rel=\"noopener noreferrer\">reactjunkie.com</a>\n    </p>\n    <div>You can reach me via:</div>\n    <ul>\n      <li>\n        <a href=\"mailto:yusinto@gmail.com\">email</a>\n      </li>\n      <li>\n        <a href=\"https://github.com/yusinto\">github</a>\n      </li>\n      <li>\n        <a href=\"https://twitter.com/yusinto\">twitter</a>\n      </li>\n      <li>\n        <a href=\"https://linkedin.com/in/yusinto\">linkedin</a>\n      </li>\n    </ul>\n  </div>;\n"
  },
  {
    "path": "examples/basic/src/universal/spaLink2.js",
    "content": "import React, {Component, Timeout} from 'react';\n\nexport default props =>\n  <div style={{marginTop: '30px', marginLeft: '30px'}}>\n    <h1>This is spa link #2</h1>\n    <p>\n      Check out my blog at <a href=\"http://reactjunkie.com\" target=\"_blank\"\n                              rel=\"noopener noreferrer\">reactjunkie.com</a>\n    </p>\n  </div>;\n"
  },
  {
    "path": "examples/basic/webpack.config.client.js",
    "content": "const path = require('path');\n\nconst WebpackServeUrl = 'http://localhost:3002';\n\nmodule.exports = {\n  mode: 'development',\n  devtool: 'source-map',\n  entry: ['@babel/polyfill', './src/client/index'],\n  output: {\n    path: path.resolve('dist'),\n    publicPath: `${WebpackServeUrl}/dist/`, // MUST BE FULL PATH!\n    filename: 'bundle.js',\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.jsx?$/,\n        include: path.resolve('src'),\n        exclude: /node_modules/,\n        loader: 'babel-loader',\n        options: {\n          cacheDirectory: true,\n        },\n      },\n      {\n        test: /\\.(png|jpg|gif)$/,\n        use: [\n          {\n            loader: 'file-loader',\n            options: {\n              publicPath: 'dist/',\n            }\n          }\n        ]\n      },\n      {\n        test: /\\.css$/,\n        use: [\n          {loader: \"style-loader\"},\n          {\n            loader: \"css-loader\",\n            options: {\n              importLoaders: 1,\n              modules: true,\n              localIdentName: '[folder]--[name]--[local]--[hash:base64:2]',\n            },\n          }\n        ]\n      },\n    ],\n  },\n};"
  },
  {
    "path": "examples/basic/webpack.config.server.js",
    "content": "const path = require('path');\nconst nodeExternals = require('webpack-node-externals');\n\nmodule.exports = {\n  mode: 'development',\n  devtool: 'source-map',\n  entry: ['@babel/polyfill', './src/server/server.js'], // set this to your server entry point. This should be where you start your express server with .listen()\n  target: 'node', // tell webpack this bundle will be used in nodejs environment.\n  externals: [nodeExternals()], // Omit node_modules code from the bundle. You don't want and don't need them in the bundle.\n  output: {\n    path: path.resolve('dist'),\n    filename: 'serverBundle.js',\n    libraryTarget: 'commonjs2', // IMPORTANT! Add module.exports to the beginning of the bundle, so universal-hot-reload can access your app.\n  },\n  // The rest of the config is pretty standard and can contain other webpack stuff you need.\n  module: {\n    rules: [\n      {\n        test: /\\.jsx?$/,\n        include: path.resolve('src'),\n        exclude: /node_modules/,\n        loader: 'babel-loader',\n        options: {\n          cacheDirectory: true,\n        },\n      },\n      {\n        test: /\\.(png|jpg|gif)$/,\n        use: [\n          {\n            loader: 'file-loader',\n            options: {\n              publicPath: 'dist/',\n            }\n          }\n        ]\n      },\n      {\n        test: /\\.css$/,\n        loader: \"css-loader\",\n        options: {\n          importLoaders: 1,\n          modules: true,\n          localIdentName: '[folder]--[name]--[local]--[hash:base64:2]',\n        },\n      },\n    ],\n  },\n};"
  },
  {
    "path": "examples/cra-with-nav/.gitignore",
    "content": "# See https://help.github.com/ignore-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n\n# testing\n/coverage\n\n# production\n/build\n\n# misc\n.DS_Store\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n"
  },
  {
    "path": "examples/cra-with-nav/README.md",
    "content": "## Create react app with react-site-nav\n\nLive demo [here](https://build-licattzisr.now.sh/).\n"
  },
  {
    "path": "examples/cra-with-nav/package.json",
    "content": "{\n  \"name\": \"cra-with-nav\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"dependencies\": {\n    \"react\": \"^16.5.0\",\n    \"react-dom\": \"^16.5.0\",\n    \"react-site-nav\": \"^0.2.6\"\n  },\n  \"devDependencies\": {\n    \"react-scripts\": \"1.1.5\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts start\",\n    \"build\": \"react-scripts build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\",\n    \"now\": \"npm run build && cd ./build && now --public\"\n  }\n}\n"
  },
  {
    "path": "examples/cra-with-nav/public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n    <meta name=\"theme-color\" content=\"#000000\">\n    <!--\n      manifest.json provides metadata used when your web app is added to the\n      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/\n    -->\n    <link rel=\"manifest\" href=\"%PUBLIC_URL%/manifest.json\">\n    <link rel=\"shortcut icon\" href=\"%PUBLIC_URL%/favicon.ico\">\n    <!--\n      Notice the use of %PUBLIC_URL% in the tags above.\n      It will be replaced with the URL of the `public` folder during the build.\n      Only files inside the `public` folder can be referenced from the HTML.\n\n      Unlike \"/favicon.ico\" or \"favicon.ico\", \"%PUBLIC_URL%/favicon.ico\" will\n      work correctly both with client-side routing and a non-root public URL.\n      Learn how to configure a non-root public URL by running `npm run build`.\n    -->\n    <title>React App</title>\n  </head>\n  <body>\n    <noscript>\n      You need to enable JavaScript to run this app.\n    </noscript>\n    <div id=\"root\"></div>\n    <!--\n      This HTML file is a template.\n      If you open it directly in the browser, you will see an empty page.\n\n      You can add webfonts, meta tags, or analytics to this file.\n      The build step will place the bundled scripts into the <body> tag.\n\n      To begin the development, run `npm start` or `yarn start`.\n      To create a production bundle, use `npm run build` or `yarn build`.\n    -->\n  </body>\n</html>\n"
  },
  {
    "path": "examples/cra-with-nav/public/manifest.json",
    "content": "{\n  \"short_name\": \"React App\",\n  \"name\": \"Create React App Sample\",\n  \"icons\": [\n    {\n      \"src\": \"favicon.ico\",\n      \"sizes\": \"64x64 32x32 24x24 16x16\",\n      \"type\": \"image/x-icon\"\n    }\n  ],\n  \"start_url\": \"./index.html\",\n  \"display\": \"standalone\",\n  \"theme_color\": \"#000000\",\n  \"background_color\": \"#ffffff\"\n}\n"
  },
  {
    "path": "examples/cra-with-nav/src/App.css",
    "content": "ul {\n    list-style-type: none;\n    margin: 0;\n    padding: 0;\n}\n\nli {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    height: 45px;\n    font-size: 15px;\n}\n\nli:hover {\n    opacity: 0.7;\n}\n\nli > a {\n    flex: 0 0 110px;\n    text-align: left;\n    margin-left: 10px;\n    text-decoration: none;\n    color: dodgerblue;\n}\n\n.App {\n    text-align: center;\n}\n\n.App-logo {\n    /* this causes an issue in firefox which breaks the flyout animation */\n    /*animation: App-logo-spin infinite 20s linear;*/\n    height: 80px;\n}\n\n.App-header {\n    background-color: #222;\n    height: 150px;\n    padding: 20px;\n    color: white;\n}\n\n.App-title {\n    font-size: 1.5em;\n}\n\n.App-intro {\n    font-size: large;\n}\n\n@keyframes App-logo-spin {\n    from {\n        transform: rotate(0deg);\n    }\n    to {\n        transform: rotate(360deg);\n    }\n}\n\n"
  },
  {
    "path": "examples/cra-with-nav/src/App.js",
    "content": "import React from 'react';\nimport logo from './logo.svg';\nimport reactMenuImage from './react-logo.png';\nimport aboutMeImage from './about-me.png';\nimport docsImage from './docs.png';\nimport communityImage from './community.png';\nimport reactSiteNavImage from './react-site-nav-logo.png';\nimport tutorialImage from './tutorial.png';\nimport './App.css';\nimport SiteNav, {ContentGroup} from 'react-site-nav';\n\nexport default () => (\n  <div className=\"App\">\n    <header className=\"App-header\">\n      <img src={logo} className=\"App-logo\" alt=\"logo\"/>\n      <h1 className=\"App-title\">Welcome to React</h1>\n    </header>\n    <SiteNav debug={false}>\n      <ContentGroup title=\"Github\" width=\"220\" height=\"100\">\n        <ul>\n          <li>\n            <img src={reactMenuImage} width=\"30\" height=\"30\" alt=\"react\"/>\n            <a href=\"https://github.com/facebook/react\">React</a>\n          </li>\n          <li>\n            <img src={reactSiteNavImage} height=\"30\" alt=\"react site nav\"/>\n            <a href=\"https://github.com/yusinto/react-site-nav\">React Site Nav</a>\n          </li>\n        </ul>\n      </ContentGroup>\n      <ContentGroup title=\"Developers\" width=\"180\" height=\"200\">\n        <ul>\n          <li>\n            <img src={docsImage} height=\"30\" alt=\"docs\"/>\n            <a href=\"https://reactjs.org/docs/getting-started.html\">Docs</a>\n          </li>\n          <li>\n            <img src={tutorialImage} height=\"30\" alt=\"tutorial\"/>\n            <a href=\"https://reactjs.org/tutorial/tutorial.html\">Tutorial</a>\n          </li>\n          <li>\n            <img src={communityImage} height=\"30\" alt=\"community\"/>\n            <a href=\"https://reactjs.org/community/support.html\">Community</a>\n          </li>\n          <li>\n            <img src={aboutMeImage} height=\"30\" alt=\"blog\"/>\n            <a href=\"http://www.reactjunkie.com/\">Blog</a>\n          </li>\n        </ul>\n      </ContentGroup>\n    </SiteNav>\n    <p className=\"App-intro\">\n      To get started, edit <code>src/App.js</code> and save to reload.\n    </p>\n  </div>\n);"
  },
  {
    "path": "examples/cra-with-nav/src/App.test.js",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport App from './App';\n\nit('renders without crashing', () => {\n  const div = document.createElement('div');\n  ReactDOM.render(<App />, div);\n  ReactDOM.unmountComponentAtNode(div);\n});\n"
  },
  {
    "path": "examples/cra-with-nav/src/index.css",
    "content": "body {\n  margin: 0;\n  padding: 0;\n  font-family: sans-serif;\n}\n"
  },
  {
    "path": "examples/cra-with-nav/src/index.js",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport registerServiceWorker from './registerServiceWorker';\n\nReactDOM.render(<App />, document.getElementById('root'));\nregisterServiceWorker();\n"
  },
  {
    "path": "examples/cra-with-nav/src/registerServiceWorker.js",
    "content": "// In production, we register a service worker to serve assets from local cache.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on the \"N+1\" visit to a page, since previously\n// cached resources are updated in the background.\n\n// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.\n// This link also includes instructions on opting out of this behavior.\n\nconst isLocalhost = Boolean(\n  window.location.hostname === 'localhost' ||\n    // [::1] is the IPv6 localhost address.\n    window.location.hostname === '[::1]' ||\n    // 127.0.0.1/8 is considered localhost for IPv4.\n    window.location.hostname.match(\n      /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n    )\n);\n\nexport default function register() {\n  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n    // The URL constructor is available in all browsers that support SW.\n    const publicUrl = new URL(process.env.PUBLIC_URL, window.location);\n    if (publicUrl.origin !== window.location.origin) {\n      // Our service worker won't work if PUBLIC_URL is on a different origin\n      // from what our page is served on. This might happen if a CDN is used to\n      // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374\n      return;\n    }\n\n    window.addEventListener('load', () => {\n      const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n      if (isLocalhost) {\n        // This is running on localhost. Lets check if a service worker still exists or not.\n        checkValidServiceWorker(swUrl);\n\n        // Add some additional logging to localhost, pointing developers to the\n        // service worker/PWA documentation.\n        navigator.serviceWorker.ready.then(() => {\n          console.log(\n            'This web app is being served cache-first by a service ' +\n              'worker. To learn more, visit https://goo.gl/SC7cgQ'\n          );\n        });\n      } else {\n        // Is not local host. Just register service worker\n        registerValidSW(swUrl);\n      }\n    });\n  }\n}\n\nfunction registerValidSW(swUrl) {\n  navigator.serviceWorker\n    .register(swUrl)\n    .then(registration => {\n      registration.onupdatefound = () => {\n        const installingWorker = registration.installing;\n        installingWorker.onstatechange = () => {\n          if (installingWorker.state === 'installed') {\n            if (navigator.serviceWorker.controller) {\n              // At this point, the old content will have been purged and\n              // the fresh content will have been added to the cache.\n              // It's the perfect time to display a \"New content is\n              // available; please refresh.\" message in your web app.\n              console.log('New content is available; please refresh.');\n            } else {\n              // At this point, everything has been precached.\n              // It's the perfect time to display a\n              // \"Content is cached for offline use.\" message.\n              console.log('Content is cached for offline use.');\n            }\n          }\n        };\n      };\n    })\n    .catch(error => {\n      console.error('Error during service worker registration:', error);\n    });\n}\n\nfunction checkValidServiceWorker(swUrl) {\n  // Check if the service worker can be found. If it can't reload the page.\n  fetch(swUrl)\n    .then(response => {\n      // Ensure service worker exists, and that we really are getting a JS file.\n      if (\n        response.status === 404 ||\n        response.headers.get('content-type').indexOf('javascript') === -1\n      ) {\n        // No service worker found. Probably a different app. Reload the page.\n        navigator.serviceWorker.ready.then(registration => {\n          registration.unregister().then(() => {\n            window.location.reload();\n          });\n        });\n      } else {\n        // Service worker found. Proceed as normal.\n        registerValidSW(swUrl);\n      }\n    })\n    .catch(() => {\n      console.log(\n        'No internet connection found. App is running in offline mode.'\n      );\n    });\n}\n\nexport function unregister() {\n  if ('serviceWorker' in navigator) {\n    navigator.serviceWorker.ready.then(registration => {\n      registration.unregister();\n    });\n  }\n}\n"
  },
  {
    "path": "jest.config.js",
    "content": "module.exports = {\n  setupFiles: ['./test/setup.js'],\n};"
  },
  {
    "path": "lib/index.js",
    "content": "\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = exports.ContentGroup = void 0;\n\nvar _react = _interopRequireWildcard(require(\"react\"));\n\nvar _styledComponents = _interopRequireWildcard(require(\"styled-components\"));\n\nvar _memoizeOne = _interopRequireDefault(require(\"memoize-one\"));\n\nvar _lodash = _interopRequireDefault(require(\"lodash.kebabcase\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }\n\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nvar defaultRootAlign = 'center';\nvar defaultColor = '#fff';\nvar defaultColumnWidth = 150;\nvar defaultRowHeight = 45;\nvar defaultBackground = '#323232';\nvar defaultBreakpoint = 768;\nvar defaultContentBackground = '#fff';\nvar defaultContentColor = '#323232';\nvar defaultContentWidth = 320;\nvar defaultContentHeight = 200;\nvar defaultContentTop = 0;\nvar arrowHeight = 8;\nvar perspective = 850;\nvar fadeOutSeconds = 0.34;\nvar fadeInSeconds = 0.25;\nvar moveSeconds = 0.25;\nvar moveArrowSeconds = 0.28;\nvar fadeOutContentSeconds = 0.29;\nvar fadeInContentSeconds = 0.1;\nvar OffScreenPadding = 10;\n\nvar setFromProps = function setFromProps(camelCaseKey) {\n  return (0, _styledComponents.css)([\"\", \"\"], function (props) {\n    return props[camelCaseKey] ? \"\".concat((0, _lodash.default)(camelCaseKey), \": \").concat(props[camelCaseKey]) : null;\n  });\n};\n\nvar GridContainer = _styledComponents.default.div.withConfig({\n  displayName: \"src__GridContainer\",\n  componentId: \"sc-178mgjs-0\"\n})([\"@media(max-width:\", \"px){position:absolute;visibility:hidden;}@media(min-width:\", \"px){display:grid;\", \";justify-items:stretch;grid-template-columns:repeat(\", \",\", \"px);grid-template-rows:\", \"px;position:relative;\", \";\", \";\", \";\", \"px;}\"], function (_ref) {\n  var breakpoint = _ref.breakpoint;\n  return breakpoint - 1;\n}, function (_ref2) {\n  var breakpoint = _ref2.breakpoint;\n  return breakpoint;\n}, setFromProps('justifyContent'), function (_ref3) {\n  var columns = _ref3.columns;\n  return columns;\n}, function (_ref4) {\n  var columnWidth = _ref4.columnWidth;\n  return columnWidth;\n}, function (_ref5) {\n  var rowHeight = _ref5.rowHeight;\n  return rowHeight;\n}, setFromProps('background'), setFromProps('color'), setFromProps('fontFamily'), setFromProps('fontSize'));\n\nvar GridItemLink = _styledComponents.default.a.withConfig({\n  displayName: \"src__GridItemLink\",\n  componentId: \"sc-178mgjs-1\"\n})([\"grid-column:\", \" / span 1;display:flex;justify-content:center;align-items:center;&:hover{opacity:0.5;}\", \";&:visited{\", \";}\"], function (_ref6) {\n  var index = _ref6.index;\n  return index + 1;\n}, setFromProps('color'), setFromProps('color'));\n\nvar GridItem = _styledComponents.default.div.withConfig({\n  displayName: \"src__GridItem\",\n  componentId: \"sc-178mgjs-2\"\n})([\"grid-column:\", \" / span 1;display:flex;justify-content:center;align-items:center;&:hover{opacity:0.5;cursor:default;}\"], function (_ref7) {\n  var index = _ref7.index;\n  return index + 1;\n});\n\nvar ContentRow = _styledComponents.default.div.withConfig({\n  displayName: \"src__ContentRow\",\n  componentId: \"sc-178mgjs-3\"\n})([\"grid-column:1 / span \", \";grid-row:2 / span 1;position:relative;height:0;\"], function (_ref8) {\n  var columns = _ref8.columns;\n  return columns;\n});\n\nvar Move = function Move(fromData, toData) {\n  return (0, _styledComponents.keyframes)([\"from{left:\", \"px;width:\", \"px;height:\", \"px;}to{left:\", \"px;width:\", \"px;height:\", \"px;}\"], fromData.left, fromData.width, fromData.height, toData.left, toData.width, toData.height);\n};\n\nvar FadeIn = (0, _styledComponents.keyframes)([\"from{opacity:0;transform:perspective(\", \"px) rotateX(-60deg);transform-origin:top center;}to{opacity:1;transform:perspective(\", \"px) rotateX(0deg);transform-origin:top center;}\"], perspective, perspective);\nvar FadeOut = (0, _styledComponents.keyframes)([\"from{opacity:1;transform:perspective(\", \"px) rotateX(0deg);transform-origin:top center;}to{opacity:0;transform:perspective(\", \"px) rotateX(-60deg);transform-origin:top center;visibility:hidden;}\"], perspective, perspective);\n\nvar MovingDiv = _styledComponents.default.div.withConfig({\n  displayName: \"src__MovingDiv\",\n  componentId: \"sc-178mgjs-4\"\n})([\"opacity:1;\", \";\", \";position:absolute;top:\", \"px;left:\", \"px;width:\", \"px;height:\", \"px;display:\", \";border-radius:4px;box-shadow:0 8px 28px 1px rgba(138,126,138,0.67);animation:\", \" \", \" forwards ease;\"], setFromProps('color'), setFromProps('background'), function (_ref9) {\n  var top = _ref9.top;\n  return top;\n}, function (_ref10) {\n  var fromData = _ref10.fromData;\n  return fromData ? fromData.left : 0;\n}, function (_ref11) {\n  var fromData = _ref11.fromData;\n  return fromData ? fromData.width : 0;\n}, function (_ref12) {\n  var fromData = _ref12.fromData;\n  return fromData ? fromData.height : 0;\n}, function (_ref13) {\n  var display = _ref13.display;\n  return display;\n}, function (_ref14) {\n  var fadeOut = _ref14.fadeOut,\n      display = _ref14.display,\n      fromData = _ref14.fromData,\n      toData = _ref14.toData;\n  if (fadeOut) return FadeOut;\n\n  if (display === 'block') {\n    if (fromData.left === toData.left) return FadeIn;\n    if (fromData) return Move(fromData, toData);\n  }\n\n  return ''; // display: none; don't animate\n}, function (_ref15) {\n  var fadeOut = _ref15.fadeOut,\n      display = _ref15.display,\n      fromData = _ref15.fromData,\n      toData = _ref15.toData;\n  if (fadeOut) return \"\".concat(fadeOutSeconds, \"s\");\n\n  if (display === 'block') {\n    if (fromData.left === toData.left) return \"\".concat(fadeInSeconds, \"s\"); // fade in\n\n    if (fromData) return \"\".concat(moveSeconds, \"s\"); // move\n  }\n\n  return '0s'; // display: none; don't animate\n});\n\nvar FadeInArrow = (0, _styledComponents.keyframes)([\"from{opacity:0;}to{opacity:1;}\"]);\nvar FadeOutArrow = (0, _styledComponents.keyframes)([\"from{opacity:1;}to{opacity:0;}\"]);\n\nvar calculateArrowMarginLeft = function calculateArrowMarginLeft(data, leftOffset, rightOffset) {\n  return (0, _styledComponents.css)([\"margin-left:\", \"px;\"], data ? data.left + data.width / 2 - leftOffset + rightOffset - arrowHeight - (leftOffset > 0 || rightOffset > 0 ? OffScreenPadding : 0) : 0);\n};\n\nvar MoveArrow = function MoveArrow(fromData, toData, leftOffset, rightOffset) {\n  return (0, _styledComponents.keyframes)([\"from{\", \"}to{\", \"}\"], calculateArrowMarginLeft(fromData, leftOffset, rightOffset), calculateArrowMarginLeft(toData, leftOffset, rightOffset));\n};\n\nvar Arrow = _styledComponents.default.div.withConfig({\n  displayName: \"src__Arrow\",\n  componentId: \"sc-178mgjs-5\"\n})([\"top:-\", \"px;z-index:1;position:absolute;\", \" display:\", \";width:0;height:0;border-left:\", \"px solid transparent;border-right:\", \"px solid transparent;border-bottom:\", \"px solid \", \";animation:\", \" \", \" forwards ease;\"], function (_ref16) {\n  var top = _ref16.top;\n  return arrowHeight - top;\n}, function (_ref17) {\n  var toData = _ref17.toData,\n      leftOffset = _ref17.leftOffset,\n      rightOffset = _ref17.rightOffset;\n  return calculateArrowMarginLeft(toData, leftOffset, rightOffset);\n}, function (_ref18) {\n  var display = _ref18.display,\n      toData = _ref18.toData;\n\n  if (toData && toData.width === 0 && toData.height === 0) {\n    return 'none';\n  }\n\n  return display;\n}, arrowHeight, arrowHeight, arrowHeight, function (_ref19) {\n  var background = _ref19.background;\n  return background;\n}, function (_ref20) {\n  var fadeOut = _ref20.fadeOut,\n      display = _ref20.display,\n      fromData = _ref20.fromData,\n      toData = _ref20.toData,\n      leftOffset = _ref20.leftOffset,\n      rightOffset = _ref20.rightOffset;\n  if (fadeOut) return FadeOutArrow;\n\n  if (display === 'block') {\n    if (fromData.left === toData.left) return FadeInArrow;\n    if (fromData) return MoveArrow(fromData, toData, leftOffset, rightOffset);\n  }\n\n  return ''; // display: none; don't animate\n}, function (_ref21) {\n  var fadeOut = _ref21.fadeOut,\n      display = _ref21.display,\n      fromData = _ref21.fromData,\n      toData = _ref21.toData;\n  if (fadeOut) return \"\".concat(fadeOutSeconds, \"s\");\n\n  if (display === 'block') {\n    if (fromData.left === toData.left) return \"\".concat(fadeInSeconds, \"s\"); // fade in\n\n    if (fromData) return \"\".concat(moveArrowSeconds, \"s\"); // move\n  }\n\n  return '0s'; // display: none; don't animate\n});\n\nvar FadeInContent = (0, _styledComponents.keyframes)([\"from{opacity:0;}to{opacity:1;}\"]);\nvar FadeOutContent = (0, _styledComponents.keyframes)([\"from{opacity:1;}to{opacity:0;visibility:hidden;}\"]);\n\nvar ContentGroupContainer = _styledComponents.default.div.withConfig({\n  displayName: \"src__ContentGroupContainer\",\n  componentId: \"sc-178mgjs-6\"\n})([\"position:absolute;margin-top:0;margin-bottom:0;width:100%;height:100%;opacity:\", \";z-index:\", \";pointer-events:\", \";animation:\", \"  \", \"s forwards;\"], function (_ref22) {\n  var show = _ref22.show;\n  return show ? 1 : 0;\n}, function (_ref23) {\n  var show = _ref23.show;\n  return show ? 1 : 0;\n}, function (_ref24) {\n  var show = _ref24.show;\n  return show ? 'auto' : 'none';\n}, function (_ref25) {\n  var show = _ref25.show,\n      fadeOut = _ref25.fadeOut;\n  if (show) return FadeInContent;\n  if (fadeOut) return FadeOutContent;\n  return ''; // cold start and everything else just show without animation \n}, function (_ref26) {\n  var show = _ref26.show;\n  return show ? \"\".concat(fadeInContentSeconds) : \"\".concat(fadeOutContentSeconds);\n});\n\nvar ContentGroup = function ContentGroup(_ref27) {\n  var title = _ref27.title,\n      width = _ref27.width,\n      height = _ref27.height,\n      background = _ref27.background;\n  return _react.default.createElement(_react.default.Fragment, null, title, width, \"x\", height, background);\n};\n\nexports.ContentGroup = ContentGroup;\n\nvar SiteNav =\n/*#__PURE__*/\nfunction (_Component) {\n  _inherits(SiteNav, _Component);\n\n  function SiteNav() {\n    var _getPrototypeOf2;\n\n    var _this;\n\n    _classCallCheck(this, SiteNav);\n\n    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n      args[_key] = arguments[_key];\n    }\n\n    _this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(SiteNav)).call.apply(_getPrototypeOf2, [this].concat(args)));\n\n    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"state\", {\n      display: 'none',\n      fadeOut: false,\n      fromData: null,\n      toData: null,\n      leftOffset: 0,\n      rightOffset: 0\n    });\n\n    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"memoizeMenuData\", (0, _memoizeOne.default)(function (columnWidth, children) {\n      return _react.default.Children.map(children, function (child, i) {\n        // if width and height are not specified, that means we don't want to render the content group i.e. we only\n        // want to render root item\n        var _child$props = child.props,\n            width = _child$props.width,\n            height = _child$props.height;\n        var sanitisedWidth, sanitisedHeight;\n\n        if (!width && !height) {\n          sanitisedWidth = 0;\n          sanitisedHeight = 0;\n        } else {\n          // if width or height is not specified, add defaults\n          sanitisedWidth = width || defaultContentWidth;\n          sanitisedHeight = height || defaultContentHeight;\n        }\n\n        return _objectSpread({}, child.props, {\n          // order is important here! spread child.props after height, followed by width.\n          height: sanitisedHeight,\n          width: sanitisedWidth,\n          index: i,\n          left: (i + 1) * columnWidth - columnWidth / 2 - sanitisedWidth / 2\n        });\n      });\n    }));\n\n    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"memoizeGridItems\", (0, _memoizeOne.default)(function (children, color) {\n      return _react.default.Children.map(children, function (child, i) {\n        var _child$props2 = child.props,\n            title = _child$props2.title,\n            rootUrl = _child$props2.rootUrl;\n\n        if (rootUrl) {\n          return _react.default.createElement(GridItemLink, {\n            href: rootUrl,\n            key: \"menu-title-\".concat(i),\n            index: i,\n            onMouseEnter: function onMouseEnter(e) {\n              return _this.onMouseEnter(e.target, i);\n            },\n            color: color\n          }, title);\n        }\n\n        return _react.default.createElement(GridItem, {\n          key: \"menu-title-\".concat(i),\n          index: i,\n          onMouseEnter: function onMouseEnter(e) {\n            return _this.onMouseEnter(e.target, i);\n          },\n          color: color\n        }, title);\n      });\n    }));\n\n    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"memoizeContent\", (0, _memoizeOne.default)(function (children, fromData, toData) {\n      return _react.default.Children.map(children, function (child, i) {\n        return _react.default.createElement(ContentGroupContainer, {\n          key: \"content-group-\".concat(i),\n          show: toData && toData.index === i,\n          fadeOut: fromData && fromData.index === i\n        }, child.props.children);\n      });\n    }));\n\n    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"memoizeColumns\", (0, _memoizeOne.default)(function (children) {\n      return _react.default.Children.count(children);\n    }));\n\n    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"memoizeAlign\", (0, _memoizeOne.default)(function (align) {\n      switch (align) {\n        case 'left':\n          return 'start';\n\n        case 'right':\n          return 'end';\n\n        default:\n          return 'center';\n      }\n    }));\n\n    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"close\", function () {\n      if (_this.props.debug) return;\n\n      _this.setState(function (prevState) {\n        return {\n          fadeOut: true,\n          fromData: prevState.toData\n        };\n      });\n    });\n\n    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"onMouseEnter\", function (target, menuDataIndex) {\n      _this.setState(function (prevState) {\n        var fadeOut = false;\n        var display = 'block';\n\n        var toDataOriginal = _this.memoizeMenuData(_this.props.columnWidth, _this.props.children)[menuDataIndex];\n\n        var toData = _objectSpread({}, toDataOriginal);\n\n        var leftOffset = 0;\n        var rightOffset = 0;\n\n        if (target) {\n          // off screen detection\n          // target is rootGridItem\n          var _target$getBoundingCl = target.getBoundingClientRect(),\n              left = _target$getBoundingCl.left,\n              width = _target$getBoundingCl.width;\n\n          var siteNavWidth = target.parentNode.clientWidth;\n          leftOffset = toData.width / 2 - (left + width / 2);\n          rightOffset = toData.width / 2 - (siteNavWidth - (left + width / 2));\n\n          if (leftOffset > 0) {\n            // if off screen, toData.left needs to be moved to be on-screen!\n            toData.left += leftOffset + OffScreenPadding;\n          } else {\n            leftOffset = 0;\n          }\n\n          if (rightOffset > 0) {\n            toData.left -= rightOffset - OffScreenPadding;\n          } else {\n            rightOffset = 0;\n          }\n\n          var fromData;\n\n          if (prevState.fadeOut || !prevState.toData) {\n            // on cold start, pop up right from the current item\n            fromData = toData;\n          } else {\n            // on warm start, start animation from the previous item\n            fromData = prevState.toData;\n          }\n\n          return {\n            display: display,\n            fadeOut: fadeOut,\n            fromData: fromData,\n            toData: toData,\n            leftOffset: leftOffset,\n            rightOffset: rightOffset\n          };\n        }\n      });\n    });\n\n    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"onMouseLeave\", function () {\n      return _this.close();\n    });\n\n    _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), \"onClickMovingDiv\", function () {\n      return _this.close();\n    });\n\n    return _this;\n  }\n\n  _createClass(SiteNav, [{\n    key: \"render\",\n    value: function render() {\n      var _this$props = this.props,\n          columnWidth = _this$props.columnWidth,\n          rowHeight = _this$props.rowHeight,\n          background = _this$props.background,\n          contentBackground = _this$props.contentBackground,\n          contentColor = _this$props.contentColor,\n          contentTop = _this$props.contentTop,\n          children = _this$props.children,\n          align = _this$props.align,\n          fontSize = _this$props.fontSize,\n          fontFamily = _this$props.fontFamily,\n          color = _this$props.color,\n          breakpoint = _this$props.breakpoint;\n      var _this$state = this.state,\n          fromData = _this$state.fromData,\n          toData = _this$state.toData,\n          display = _this$state.display,\n          fadeOut = _this$state.fadeOut,\n          leftOffset = _this$state.leftOffset,\n          rightOffset = _this$state.rightOffset;\n      var columns = this.memoizeColumns(children);\n      var rootGridItems = this.memoizeGridItems(children, color);\n      var content = this.memoizeContent(children, fromData, toData);\n      var justifyContent = this.memoizeAlign(align);\n      var contentBackgroundSanitised = toData && toData.background || contentBackground;\n      return _react.default.createElement(\"nav\", null, _react.default.createElement(GridContainer, {\n        background: background,\n        columnWidth: columnWidth,\n        rowHeight: rowHeight,\n        justifyContent: justifyContent,\n        fontSize: fontSize,\n        fontFamily: fontFamily,\n        color: color,\n        breakpoint: breakpoint\n        /* Below are not configurable */\n        ,\n        onMouseLeave: this.onMouseLeave,\n        columns: columns\n      }, rootGridItems, _react.default.createElement(ContentRow, {\n        columns: columns\n      }, _react.default.createElement(Arrow, {\n        display: display,\n        fadeOut: fadeOut,\n        fromData: fromData,\n        toData: toData,\n        top: contentTop,\n        onClick: this.onClickMovingDiv,\n        background: contentBackgroundSanitised,\n        leftOffset: leftOffset,\n        rightOffset: rightOffset\n      }), _react.default.createElement(MovingDiv, {\n        display: display,\n        fadeOut: fadeOut,\n        fromData: fromData,\n        toData: toData,\n        color: contentColor,\n        top: contentTop,\n        onClick: this.onClickMovingDiv,\n        background: contentBackgroundSanitised\n      }, content))));\n    }\n  }]);\n\n  return SiteNav;\n}(_react.Component);\n\nexports.default = SiteNav;\n\n_defineProperty(SiteNav, \"defaultProps\", {\n  align: defaultRootAlign,\n  columnWidth: defaultColumnWidth,\n  rowHeight: defaultRowHeight,\n  background: defaultBackground,\n  contentBackground: defaultContentBackground,\n  contentColor: defaultContentColor,\n  contentTop: defaultContentTop,\n  breakpoint: defaultBreakpoint,\n  color: defaultColor,\n  debug: false\n});"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-site-nav\",\n  \"version\": \"0.2.9\",\n  \"description\": \"A kick ass site menu powered by styled components inspired by Stripe.\",\n  \"main\": \"lib/index.js\",\n  \"scripts\": {\n    \"test\": \"jest\",\n    \"build\": \"rimraf lib/* && babel src -d lib --ignore *.test.js\",\n    \"lint\": \"eslint --cache --format 'node_modules/eslint-friendly-formatter' ./src\",\n    \"prep-publish\": \"npm run build && npm version patch -m 'Upgrade to %s' && npm publish && git push\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/yusinto/react-site-nav.git\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"site\",\n    \"navigation\",\n    \"menu\",\n    \"bar\",\n    \"animated\",\n    \"stripe\"\n  ],\n  \"author\": \"Yusinto Ngadiman\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/yusinto/react-site-nav/issues\"\n  },\n  \"homepage\": \"https://github.com/yusinto/react-site-nav#readme\",\n  \"devDependencies\": {\n    \"@babel/cli\": \"^7.0.0\",\n    \"@babel/core\": \"^7.0.1\",\n    \"@babel/plugin-proposal-class-properties\": \"^7.0.0\",\n    \"@babel/preset-env\": \"^7.0.0\",\n    \"@babel/preset-react\": \"^7.0.0\",\n    \"babel-eslint\": \"^9.0.0\",\n    \"babel-plugin-styled-components\": \"^1.7.1\",\n    \"babel-preset-minify\": \"^0.4.3\",\n    \"eslint\": \"^5.5.0\",\n    \"eslint-config-airbnb\": \"^17.1.0\",\n    \"eslint-friendly-formatter\": \"^4.0.1\",\n    \"eslint-plugin-babel\": \"^5.2.0\",\n    \"eslint-plugin-import\": \"^2.14.0\",\n    \"eslint-plugin-jsx-a11y\": \"^6.1.1\",\n    \"eslint-plugin-react\": \"^7.11.1\",\n    \"rimraf\": \"^2.6.2\"\n  },\n  \"dependencies\": {\n    \"lodash.kebabcase\": \"^4.1.1\",\n    \"memoize-one\": \"^4.0.2\",\n    \"react\": \"^16.5.0\",\n    \"styled-components\": \"^3.4.6\"\n  }\n}\n"
  },
  {
    "path": "src/index.js",
    "content": "import React, {Component} from 'react';\nimport styled, {keyframes, css} from 'styled-components';\nimport memoize from 'memoize-one';\nimport kebabCase from 'lodash.kebabcase';\n\nconst defaultRootAlign = 'center';\nconst defaultColor = '#fff';\nconst defaultColumnWidth = 150;\nconst defaultRowHeight = 45;\nconst defaultBackground = '#323232';\nconst defaultBreakpoint = 768;\nconst defaultContentBackground = '#fff';\nconst defaultContentColor = '#323232';\nconst defaultContentWidth = 320;\nconst defaultContentHeight = 200;\nconst defaultContentTop = 0;\n\nconst arrowHeight = 8;\nconst perspective = 850;\n\nconst fadeOutSeconds = 0.34;\nconst fadeInSeconds = 0.25;\nconst moveSeconds = 0.25;\nconst moveArrowSeconds = 0.28;\nconst fadeOutContentSeconds = 0.29;\nconst fadeInContentSeconds = 0.1;\nconst OffScreenPadding = 10;\n\nconst setFromProps = camelCaseKey => css`\n  ${props => props[camelCaseKey] ? `${kebabCase(camelCaseKey)}: ${props[camelCaseKey]}` : null}`;\n\nconst GridContainer = styled.div`\n  // use visibility hidden instead of display none because menu flashes when breakpoint changes for some reason!\n  @media(max-width: ${({breakpoint}) => (breakpoint - 1)}px) {\n    position: absolute;\n    visibility: hidden;\n  }\n  \n  @media(min-width: ${({breakpoint}) => breakpoint}px) {\n    display: grid;\n    ${setFromProps('justifyContent')};\n    justify-items: stretch;\n    grid-template-columns: repeat(${({columns}) => columns}, ${({columnWidth}) => columnWidth}px);\n    grid-template-rows: ${({rowHeight}) => rowHeight}px;\n    position: relative;\n    ${setFromProps('background')};\n    ${setFromProps('color')};  \n    ${setFromProps('fontFamily')};\n    ${setFromProps('fontSize')}px;  \n  }\n`;\nconst GridItemLink = styled.a`\n  grid-column: ${({index}) => index + 1} / span 1;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  &:hover {\n    opacity: 0.5;\n  }\n  ${setFromProps('color')};\n  \n  &:visited {\n    ${setFromProps('color')};\n  }\n`;\nconst GridItem = styled.div`\n  grid-column: ${({index}) => index + 1} / span 1;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  &:hover {\n    opacity: 0.5;\n    cursor: default;\n  }\n`;\nconst ContentRow = styled.div`\n  grid-column: 1 / span ${({columns}) => columns};\n  grid-row: 2 / span 1;\n  position: relative;\n  height: 0;\n`;\nconst Move = (fromData, toData) => keyframes`\n  from {\n    left: ${fromData.left}px;\n    width: ${fromData.width}px;\n    height: ${fromData.height}px;\n  }\n  \n  to {\n    left: ${toData.left}px;\n    width: ${toData.width}px;\n    height: ${toData.height}px;\n  }\n`;\nconst FadeIn = keyframes`\n  from {\n    opacity: 0;\n    transform: perspective(${perspective}px) rotateX(-60deg);\n    transform-origin: top center;\n  }\n  \n  to {\n    opacity: 1;\n    transform: perspective(${perspective}px) rotateX(0deg);\n    transform-origin: top center;\n  }\n`;\nconst FadeOut = keyframes`\n  from {\n    opacity: 1;\n    transform: perspective(${perspective}px) rotateX(0deg);\n    transform-origin: top center;\n  }\n  \n  to {\n    opacity: 0;\n    transform: perspective(${perspective}px) rotateX(-60deg);\n    transform-origin: top center;\n    visibility: hidden;\n  }\n`;\nconst MovingDiv = styled.div`\n  opacity: 1;\n  ${setFromProps('color')};\n  ${setFromProps('background')};\n  position: absolute;\n  top: ${({top}) => top}px;\n  left: ${({fromData}) => fromData ? fromData.left : 0}px;\n  width: ${({fromData}) => fromData ? fromData.width : 0}px;\n  height: ${({fromData}) => fromData ? fromData.height : 0}px;\n  display: ${({display}) => display};\n  border-radius: 4px;\n  box-shadow: 0 8px 28px 1px rgba(138,126,138,0.67); // Ripped from: https://www.cssmatic.com/box-shadow\n  animation: ${({fadeOut, display, fromData, toData}) => {\n  if (fadeOut) return FadeOut;\n  if (display === 'block') {\n    if (fromData.left === toData.left) return FadeIn;\n    if (fromData) return Move(fromData, toData);\n  }\n  return ''; // display: none; don't animate\n}}\n  \n  // fade out and in slower than moving sideways\n  ${({fadeOut, display, fromData, toData}) => {\n  if (fadeOut) return `${fadeOutSeconds}s`;\n  if (display === 'block') {\n    if (fromData.left === toData.left) return `${fadeInSeconds}s`; // fade in\n    if (fromData) return `${moveSeconds}s`; // move\n  }\n  return '0s'; // display: none; don't animate\n}}\n  \n  forwards ease;\n`;\nconst FadeInArrow = keyframes`\n  from {\n    opacity: 0;\n  }\n  \n  to {\n    opacity: 1;\n  }\n`;\nconst FadeOutArrow = keyframes`\n  from {\n    opacity: 1;\n  }\n  \n  to {\n    opacity: 0;\n  }\n`;\nconst calculateArrowMarginLeft = (data, leftOffset, rightOffset) => css`\n  margin-left: ${\n  data ? data.left + (data.width / 2) - leftOffset + rightOffset - arrowHeight\n    - (leftOffset > 0 || rightOffset > 0 ? OffScreenPadding : 0)\n    : 0\n  }px;\n`;\nconst MoveArrow = (fromData, toData, leftOffset, rightOffset) => keyframes`\n  from {\n    ${calculateArrowMarginLeft(fromData, leftOffset, rightOffset)}\n  }\n  \n  to {\n    ${calculateArrowMarginLeft(toData, leftOffset, rightOffset)}\n  }\n`;\nconst Arrow = styled.div`\n  top: -${({top}) => (arrowHeight - top)}px;\n  z-index: 1;\n  position: absolute;\n  ${({toData, leftOffset, rightOffset}) => calculateArrowMarginLeft(toData, leftOffset, rightOffset)}\n  display: ${({display, toData}) => {\n  if (toData && toData.width === 0 && toData.height === 0) {\n    return 'none';\n  }\n  return display;\n}};\n  width: 0; \n  height: 0;\n  border-left: ${arrowHeight}px solid transparent;\n  border-right: ${arrowHeight}px solid transparent;\n  border-bottom: ${arrowHeight}px solid ${({background}) => background};\n  animation: ${({fadeOut, display, fromData, toData, leftOffset, rightOffset}) => {\n  if (fadeOut) return FadeOutArrow;\n  if (display === 'block') {\n    if (fromData.left === toData.left) return FadeInArrow;\n    if (fromData) return MoveArrow(fromData, toData, leftOffset, rightOffset);\n  }\n  return ''; // display: none; don't animate\n}}\n  \n  // fade out and in slower than moving sideways\n  ${({fadeOut, display, fromData, toData}) => {\n  if (fadeOut) return `${fadeOutSeconds}s`;\n  if (display === 'block') {\n    if (fromData.left === toData.left) return `${fadeInSeconds}s`; // fade in\n    if (fromData) return `${moveArrowSeconds}s`; // move\n  }\n  return '0s'; // display: none; don't animate\n}}\n  \n  forwards ease;\n`;\nconst FadeInContent = keyframes`\n  from {\n    opacity: 0;\n  }\n  \n  to {\n    opacity: 1;\n  }\n`;\nconst FadeOutContent = keyframes`\n  from {\n    opacity: 1;\n  }\n  \n  to {\n    opacity: 0;\n    visibility: hidden;\n  }\n`;\nconst ContentGroupContainer = styled.div`\n  position: absolute;\n  margin-top: 0;\n  margin-bottom: 0;\n  width: 100%;\n  height: 100%;\n  opacity: ${({show}) => show ? 1 : 0};\n  z-index: ${({show}) => show ? 1 : 0};\n  pointer-events: ${({show}) => show ? 'auto' : 'none'}; // disregard mouse event if content group is inactive\n  animation: ${({show, fadeOut}) => {\n  if (show) return FadeInContent;\n  if (fadeOut) return FadeOutContent;\n  return ''; // cold start and everything else just show without animation \n}} \n  ${({show}) => show ? `${fadeInContentSeconds}` : `${fadeOutContentSeconds}`}s\n  forwards;\n`;\nexport const ContentGroup = ({title, width, height, background}) => {\n  return (\n    <>\n      {title}\n      {width}x{height}\n      {background}\n    </>\n  );\n};\n\nexport default class SiteNav extends Component {\n  state = {display: 'none', fadeOut: false, fromData: null, toData: null, leftOffset: 0, rightOffset: 0};\n\n  static defaultProps = {\n    align: defaultRootAlign,\n    columnWidth: defaultColumnWidth,\n    rowHeight: defaultRowHeight,\n    background: defaultBackground,\n    contentBackground: defaultContentBackground,\n    contentColor: defaultContentColor,\n    contentTop: defaultContentTop,\n    breakpoint: defaultBreakpoint,\n    color: defaultColor,\n    debug: false,\n  };\n\n  /**\n   * Injects index and left properties into MenuData\n   */\n  memoizeMenuData = memoize((columnWidth, children) => React.Children.map(children, (child, i) => {\n    // if width and height are not specified, that means we don't want to render the content group i.e. we only\n    // want to render root item\n    const {width, height} = child.props;\n    let sanitisedWidth, sanitisedHeight;\n\n    if (!width && !height) {\n      sanitisedWidth = 0;\n      sanitisedHeight = 0;\n    } else {\n      // if width or height is not specified, add defaults\n      sanitisedWidth = width || defaultContentWidth;\n      sanitisedHeight = height || defaultContentHeight;\n    }\n\n    return {\n      ...child.props, // order is important here! spread child.props after height, followed by width.\n      height: sanitisedHeight,\n      width: sanitisedWidth,\n      index: i,\n      left: (((i + 1) * columnWidth) - (columnWidth / 2)) - (sanitisedWidth / 2),\n    };\n  }));\n  memoizeGridItems = memoize((children, color) => React.Children.map(children, (child, i) => {\n      const {title, rootUrl} = child.props;\n\n      if (rootUrl) {\n        return (\n          <GridItemLink\n            href={rootUrl}\n            key={`menu-title-${i}`}\n            index={i}\n            onMouseEnter={(e) => this.onMouseEnter(e.target, i)}\n            color={color}\n          >\n            {title}\n          </GridItemLink>\n        );\n      }\n\n      return (\n        <GridItem\n          key={`menu-title-${i}`}\n          index={i}\n          onMouseEnter={(e) => this.onMouseEnter(e.target, i)}\n          color={color}\n        >\n          {title}\n        </GridItem>\n      );\n    }\n  ));\n  memoizeContent = memoize((children, fromData, toData) => React.Children.map(children, (child, i) => (\n    <ContentGroupContainer\n      key={`content-group-${i}`}\n      show={toData && toData.index === i}\n      fadeOut={fromData && fromData.index === i}\n    >\n      {child.props.children}\n    </ContentGroupContainer>\n  )));\n  memoizeColumns = memoize(children => React.Children.count(children));\n  memoizeAlign = memoize(align => {\n    switch (align) {\n      case 'left':\n        return 'start';\n      case 'right':\n        return 'end';\n      default:\n        return 'center';\n    }\n  });\n\n  close = () => {\n    if (this.props.debug) return;\n    this.setState((prevState) => ({fadeOut: true, fromData: prevState.toData}));\n  };\n  onMouseEnter = (target, menuDataIndex) => {\n    this.setState((prevState) => {\n      const fadeOut = false;\n      const display = 'block';\n      const toDataOriginal = this.memoizeMenuData(this.props.columnWidth, this.props.children)[menuDataIndex];\n      const toData = {...toDataOriginal};\n      let leftOffset = 0;\n      let rightOffset = 0;\n\n      if (target) { // off screen detection\n        // target is rootGridItem\n        const {left, width} = target.getBoundingClientRect();\n        const siteNavWidth = target.parentNode.clientWidth;\n        leftOffset = (toData.width / 2) - (left + (width / 2));\n        rightOffset = (toData.width / 2) - (siteNavWidth - (left + (width / 2)));\n\n        if (leftOffset > 0) {\n          // if off screen, toData.left needs to be moved to be on-screen!\n          toData.left += leftOffset + OffScreenPadding;\n        } else {\n          leftOffset = 0;\n        }\n\n        if (rightOffset > 0) {\n          toData.left -= rightOffset - OffScreenPadding;\n        } else {\n          rightOffset = 0;\n        }\n\n        let fromData;\n        if (prevState.fadeOut || !prevState.toData) {\n          // on cold start, pop up right from the current item\n          fromData = toData;\n        } else {\n          // on warm start, start animation from the previous item\n          fromData = prevState.toData;\n        }\n\n        return {\n          display,\n          fadeOut,\n          fromData,\n          toData,\n          leftOffset,\n          rightOffset,\n        };\n      }\n    });\n  };\n  onMouseLeave = () => this.close();\n  onClickMovingDiv = () => this.close();\n\n  render() {\n    const {\n      columnWidth, rowHeight, background, contentBackground, contentColor, contentTop,\n      children, align, fontSize, fontFamily, color, breakpoint\n    } = this.props;\n    const {fromData, toData, display, fadeOut, leftOffset, rightOffset} = this.state;\n    const columns = this.memoizeColumns(children);\n    const rootGridItems = this.memoizeGridItems(children, color);\n    const content = this.memoizeContent(children, fromData, toData);\n    const justifyContent = this.memoizeAlign(align);\n    const contentBackgroundSanitised = (toData && toData.background) || contentBackground;\n\n    return (\n      <nav>\n        <GridContainer\n          background={background}\n          columnWidth={columnWidth}\n          rowHeight={rowHeight}\n          justifyContent={justifyContent}\n          fontSize={fontSize}\n          fontFamily={fontFamily}\n          color={color}\n          breakpoint={breakpoint}\n\n          /* Below are not configurable */\n          onMouseLeave={this.onMouseLeave}\n          columns={columns}\n        >\n          {rootGridItems}\n          <ContentRow columns={columns}>\n            <Arrow\n              display={display}\n              fadeOut={fadeOut}\n              fromData={fromData}\n              toData={toData}\n              top={contentTop}\n              onClick={this.onClickMovingDiv}\n              background={contentBackgroundSanitised}\n              leftOffset={leftOffset}\n              rightOffset={rightOffset}\n            />\n            <MovingDiv\n              display={display}\n              fadeOut={fadeOut}\n              fromData={fromData}\n              toData={toData}\n              color={contentColor}\n              top={contentTop}\n              onClick={this.onClickMovingDiv}\n              background={contentBackgroundSanitised}\n            >\n              {content}\n            </MovingDiv>\n          </ContentRow>\n        </GridContainer>\n      </nav>\n    );\n  }\n}"
  },
  {
    "path": "test/setup.js",
    "content": "// TODO:\n// import td from 'testdouble';\n//\n// global.td = td;\n"
  }
]