Repository: yusinto/react-site-nav
Branch: master
Commit: d93cc53f02f7
Files: 59
Total size: 87.4 KB
Directory structure:
gitextract_u844912z/
├── .babelrc
├── .eslintrc
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── examples/
│ ├── advanced/
│ │ ├── .babelrc
│ │ ├── .eslintrc
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── now/
│ │ │ ├── index.html
│ │ │ └── now.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── client/
│ │ │ │ └── index.js
│ │ │ ├── server/
│ │ │ │ ├── index.js
│ │ │ │ └── server.js
│ │ │ └── universal/
│ │ │ ├── app/
│ │ │ │ ├── app.js
│ │ │ │ ├── company.js
│ │ │ │ ├── developers.js
│ │ │ │ ├── pricing.js
│ │ │ │ └── products.js
│ │ │ ├── contact.js
│ │ │ └── home.js
│ │ ├── webpack.config.client.js
│ │ ├── webpack.config.server.js
│ │ └── webpack.prod.config.js
│ ├── basic/
│ │ ├── .babelrc
│ │ ├── .eslintrc
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── client/
│ │ │ │ └── index.js
│ │ │ ├── server/
│ │ │ │ ├── index.js
│ │ │ │ └── server.js
│ │ │ └── universal/
│ │ │ ├── app.css
│ │ │ ├── app.js
│ │ │ ├── home.js
│ │ │ ├── spaLink1.js
│ │ │ └── spaLink2.js
│ │ ├── webpack.config.client.js
│ │ └── webpack.config.server.js
│ └── cra-with-nav/
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── public/
│ │ ├── index.html
│ │ └── manifest.json
│ └── src/
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ └── registerServiceWorker.js
├── jest.config.js
├── lib/
│ └── index.js
├── package.json
├── sketch/
│ ├── logo-sketch.sketch
│ └── react-site-nav-logo.sketch
├── src/
│ └── index.js
└── test/
└── setup.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .babelrc
================================================
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-async-to-generator",
["babel-plugin-styled-components", {
"ssr": true,
"displayName": true
}]
]
}
================================================
FILE: .eslintrc
================================================
{
"parser": "babel-eslint",
"parserOptions": {
"allowImportExportEverywhere": true
},
"extends": [
"airbnb",
"eslint:recommended"
],
"plugins": [
"babel"
],
"rules": {
"arrow-parens": 0,
"eol-last": 0,
"global-require": 0,
"arrow-body-style": 0,
"consistent-return": 0,
"no-unneeded-ternary": 0,
"max-len": 0,
"no-param-reassign": 2,
"new-cap": 0,
"no-console": 0,
"object-curly-spacing": 0,
"spaced-comment": 0,
"import/first": 0,
"import/no-extraneous-dependencies": 0,
"import/prefer-default-export": 0,
"import/no-mutable-exports": 0,
"import/no-named-as-default": 0,
"no-trailing-spaces": 0,
"no-underscore-dangle": 0,
"no-use-before-define": 0,
"no-duplicate-imports": 0,
"import/no-duplicates": 1,
"no-useless-escape": 0,
"no-unused-expressions": [1 , {"allowTernary": true}],
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
"react/sort-comp": 0,
"react/prop-types": 0,
"react/destructuring-assignment": 0
},
"env": {
"jest": true,
"node": true
},
"globals": {
"jest": true,
"td": true
}
}
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
.eslintcache
.idea
================================================
FILE: .npmignore
================================================
*
!lib/*
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 Yusinto Ngadiman
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<p align="center">
<img src="logo.jpg" width="390" />
</p>
[](https://www.npmjs.com/package/react-site-nav) [](https://www.npmjs.com/package/react-site-nav) [](https://www.npmjs.com/package/react-site-nav) [](https://www.npmjs.com/package/react-site-nav)
> **A beautiful site navigation bar to be proud of. Powered by styled components inspired by stripe.com** :tada:
<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>

Your search for the perfect site navigation bar ends here. Finally a world class navigation bar
you can use straight out of the box. Why use this package?
* Beautiful animations
* Automatic viewport detection and correction so flyouts never get rendered off screen
* Completely customisable
* Powered by css grid, css animations and styled components
No more compromises. Welcome to react-site-nav.
## Installation
yarn add react-site-nav
## Quickstart
```js
import React from 'react';
import {Switch, Link, Route} from 'react-router-dom';
import SiteNav, {ContentGroup} from 'react-site-nav'; // 1. Do this
import Home from './home';
import MyStory from './myStory';
export default () =>
(
<div>
{/* 2. Add SiteNav with ContentGroup as children */}
<SiteNav>
<ContentGroup title="About" height="200">
{/* 3. You can add anything in a ContentGroup */}
<ul>
{/* react router link! */}
<li><Link to="/my-story">My Story</Link></li>
<li>Another list item</li>
</ul>
</ContentGroup>
<ContentGroup title="Contact" height="200">
Free text followed by some links.<br/>
<a href="mailto:yusinto@gmail.com">Email</a><br/>
<a href="https://github.com/yusinto">Github</a>
</ContentGroup>
</SiteNav>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/my-story" component={MyStory}/>
</Switch>
</div>
);
```
Check the two [examples](https://github.com/yusinto/react-site-nav/tree/master/examples) for a fully working spa with
react router, server side rendering and hot reload.
## Api
### SiteNav
The main react component that represents the site nav. The root container is a css grid so
most of the props below maps directly to this grid and should be self-explanatory. Place
ContentGroup components as children of SiteNav to render the "flyouts".
```jsx
<SiteNav
align="center" /* center, left, right. This directly maps to justify-content of the root grid. */
columnWidth="150"
rowHeight="45"
background="#323232"
color="#fff"
fontSize="18"
fontFamily="Helvetica, sans-serif"
contentBackground="#fff" /* Applies to all content groups */
contentColor="#323232" /* Applies to all content groups */
contentTop="0" /* Adjusts the distance between ContentGroups and root items */
breakpoint="768" /* Show site nav at this breakpoint */
debug={false} /* Keep ContentGroups open to make debugging easier */
>
{ /* These will render as flyouts */}
<ContentGroup>...</ContentGroup>
<ContentGroup>...</ContentGroup>
</SiteNav>
```
### ContentGroup
Each SiteNav contains ContentGroup children components. Each ContentGroup will be rendered
as a "flyout" on hover of the root items. It accepts the following props which are self-explanatory.
```jsx
<ContentGroup
title="Products"
width="420"
height="270"
rootUrl="https://some/link" /* Optional. Render root item as a link */
background="white" /* Optional. Overrides SiteNav contentBackground property */
>
{
/* You can render anything here! */
}
</ContentGroup>
```
To render a root item as a link without a ContentGroup, you can do this:
```jsx
<ContentGroup title="Open Source" rootUrl="https://github.com/yusinto" />
```
By not specifying width and height, SiteNav assumes you just want to render the root item
without a ContentGroup. Of course you can have a linked root item plus a ContentGroup. Just specify either
a width or a height so SiteNav knows you want to render the group.
```jsx
<ContentGroup title="Open Source" rootUrl="https://github.com/yusinto" height="200">
{
/* You can render anything here! */
}
</ContentGroup>
```
Check the demo in my [blog](https://reactjunkie.com/).
================================================
FILE: examples/advanced/.babelrc
================================================
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-async-to-generator",
["babel-plugin-styled-components", {
"ssr": true,
"displayName": true
}]
]
}
================================================
FILE: examples/advanced/.eslintrc
================================================
{
"parser": "babel-eslint",
"parserOptions": {
"allowImportExportEverywhere": true
},
"extends": [
"airbnb",
"eslint:recommended",
"plugin:react/recommended"
],
"plugins": [
"babel"
],
"rules": {
"arrow-parens": 0,
"eol-last": 0,
"global-require": 0,
"arrow-body-style": 0,
"consistent-return": 0,
"no-unneeded-ternary": 0,
"max-len": 0,
"no-param-reassign": 2,
"new-cap": 0,
"no-console": 0,
"object-curly-spacing": 0,
"spaced-comment": 0,
"import/no-extraneous-dependencies": 0,
"import/first": 0,
"import/prefer-default-export": 0,
"import/no-mutable-exports": 0,
"import/no-named-as-default": 0,
"react/jsx-filename-extension": 0,
"react/jsx-indent": 0,
"react/jsx-indent-props": 0,
"react/jsx-space-before-closing": 0,
"react/jsx-first-prop-new-line": 0,
"react/prefer-stateless-function": 0,
"react/jsx-closing-bracket-location": 0,
"react/require-extension": 0,
"react/sort-comp": 0,
"react/jsx-wrap-multilines": 0,
"react/jsx-no-bind": 0,
"react/jsx-users-react": 0,
"react/jsx-tag-spacing": 0,
"jsx-a11y/anchor-is-valid": 0,
"jsx-a11y/img-has-alt": 0,
"no-trailing-spaces": 0,
"no-underscore-dangle": 0,
"no-use-before-define": 0,
"no-duplicate-imports": 0,
"import/no-duplicates": 1,
"no-useless-escape": 0,
"no-unused-expressions": [1 , {"allowTernary": true}]
},
"env": {
"browser": true,
"jest": true,
"node": true
},
"globals": {
"React": true,
"fetch": true,
"jest": true
}
}
================================================
FILE: examples/advanced/.gitignore
================================================
node_modules
.idea
npm-debug.log
dist
.eslintcache
now/build
================================================
FILE: examples/advanced/README.md
================================================
# react-site-nav advanced example
Live demo [here](https://react-site-nav.now.sh).
yarn && yarn start
================================================
FILE: examples/advanced/now/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>React Site Nav - Advanced</title>
</head>
<body>
<div id="reactDiv"/>
<script type="application/javascript" src="build/bundle.js"></script>
</body>
</html>
================================================
FILE: examples/advanced/now/now.json
================================================
{
"name": "react-site-nav",
"alias": "react-site-nav",
"public": true
}
================================================
FILE: examples/advanced/package.json
================================================
{
"name": "react-site-menu-example",
"version": "1.1.0",
"description": "Demo of react-site-menu a kickass navigation menu inspired by stripe.com",
"main": "src/server/index.js",
"scripts": {
"start": "node src/server/index.js",
"lint": "eslint ./src",
"serve": "webpack-serve webpack.config.server",
"build": "rimraf now/build/* && webpack --config webpack.prod.config.js",
"now": "npm run build && cd ./now && now && now alias"
},
"repository": {
"type": "git",
"url": "https://github.com/yusinto/react-site-menu.git"
},
"keywords": [
"react",
"site",
"navigation",
"menu",
"bar",
"animated",
"stripe"
],
"author": "Yus Ng",
"license": "MIT",
"bugs": {
"url": "https://github.com/yusinto/react-site-menu"
},
"homepage": "https://github.com/yusinto/react-site-menu",
"dependencies": {
"@babel/plugin-transform-async-to-generator": "^7.0.0-beta.54",
"@babel/polyfill": "^7.0.0-beta.54",
"babel-plugin-styled-components": "^1.5.1",
"express": "^4.16.3",
"lodash": "^4.17.11",
"lodash.kebabcase": "^4.1.1",
"memoize-one": "^4.0.2",
"prop-types": "^15.6.2",
"react": "^16.5.0",
"react-dom": "^16.5.0",
"react-router-dom": "^4.3.1",
"react-site-nav": "^0.2.1",
"styled-components": "^3.4.6"
},
"devDependencies": {
"@babel/cli": "^7.0.0-beta.54",
"@babel/core": "^7.0.0-beta.54",
"@babel/plugin-proposal-class-properties": "^7.0.0-beta.54",
"@babel/preset-env": "^7.0.0-beta.54",
"@babel/preset-react": "^7.0.0-beta.54",
"babel-eslint": "^8.2.6",
"babel-loader": "^8.0.0-beta",
"eslint": "^5.5.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "^6.1.1",
"eslint-plugin-react": "^7.11.1",
"file-loader": "^2.0.0",
"rimraf": "^2.6.2",
"universal-hot-reload": "^1.0.6",
"webpack": "^4.18.0",
"webpack-cli": "^3.1.0",
"webpack-node-externals": "^1.7.2",
"webpack-serve": "^2.0.2"
}
}
================================================
FILE: examples/advanced/src/client/index.js
================================================
import React from 'react';
import {hydrate} from 'react-dom';
import {BrowserRouter} from 'react-router-dom';
import App from '../universal/app/app';
hydrate(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.getElementById('reactDiv'),
);
================================================
FILE: examples/advanced/src/server/index.js
================================================
const UniversalHotReload = require('universal-hot-reload').default;
UniversalHotReload(require('../../webpack.config.server.js'), require('../../webpack.config.client.js'));
================================================
FILE: examples/advanced/src/server/server.js
================================================
import Express from 'express';
import React from 'react';
import {renderToString} from 'react-dom/server';
import {ServerStyleSheet, StyleSheetManager} from 'styled-components';
import {StaticRouter} from 'react-router-dom';
import App from '../universal/app/app';
const PORT = 3000;
const app = Express();
app.use('/dist', Express.static('dist', {maxAge: '1d'}));
app.use((req, res) => {
const sheet = new ServerStyleSheet();
const StyledApp =
<StyleSheetManager sheet={sheet.instance}>
<App/>
</StyleSheetManager>;
const styleTags = sheet.getStyleTags();
const html = `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>React Site Nav - Advanced</title>
${styleTags}
</head>
<body>
<div id="reactDiv">${renderToString(
<StaticRouter
location={req.url}
context={{}}>
<App/>
</StaticRouter>)}
</div>
<script type="application/javascript" src="http://localhost:3002/dist/bundle.js"></script>
</body>
</html>`;
res.end(html);
});
const httpServer = app.listen(PORT, () => {
console.log(`Example app listening at ${PORT}...`);
});
// export httpServer object so universal-hot-reload can access it
module.exports = httpServer;
================================================
FILE: examples/advanced/src/universal/app/app.js
================================================
import React, {Component} from 'react';
import {Switch, Link, Route, Redirect} from 'react-router-dom';
import styled, {injectGlobal} from 'styled-components';
import SiteNav, {ContentGroup} from 'react-site-nav';
import Home from '../home';
import Contact from '../contact';
import logo from '../../../assets/logo-transparent.png';
import ProductsContentGroup from './products';
import Developers from './developers';
import Company from './company';
import Pricing from './pricing';
injectGlobal`
// auto-generated from https://www.svgbackgrounds.com/#abstract-envelope
body {
background-color: #77aa77;
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");
background-attachment: fixed;
background-size: cover;
background-position: center;
}
a {
text-decoration: none;
}
a:visited {
color: lightslategray;
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
}
`;
const Header = styled.div`
display: grid;
grid-template-columns: 400px auto;
grid-template-rows: 80px;
background: transparent;
`;
export default class App extends Component {
render() {
return (
<div>
<header>
<Header>
<Link to="/">
<img alt="logo" style={{height: '80px', marginLeft: '20px', marginTop: '0px'}} src={logo}/>
</Link>
</Header>
<SiteNav
background="transparent"
fontSize="18"
fontFamily="Helvetica, sans-serif"
>
<ContentGroup title="Products" width="420" height="270">
<ProductsContentGroup/>
</ContentGroup>
<ContentGroup title="Developers" width="370" height="300">
<Developers/>
</ContentGroup>
<ContentGroup title="Company" width="260" height="220">
<Company/>
</ContentGroup>
<ContentGroup title="Pricing" width="420" height="300">
<Pricing/>
</ContentGroup>
</SiteNav>
</header>
<main>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/home">
<Redirect to="/"/>
</Route>
<Route path="/contact" component={Contact}/>
</Switch>
</main>
</div>
);
}
}
================================================
FILE: examples/advanced/src/universal/app/company.js
================================================
import React from 'react';
import styled from 'styled-components';
import aboutMeIcon from '../../../assets/about-me.png';
import customersIcon from '../../../assets/customers.png';
import jobsIcon from '../../../assets/jobs.png';
import environmentIcon from '../../../assets/environment.png';
const List = styled.ul`
display: flex;
flex-direction: column;
margin-top: 10px;
margin-left: 30px;
`;
const ListItem = styled.li`
display: flex;
flex-direction: row;
margin-top: 20px;
align-items: center;
`;
const Heading = styled.div`
margin: 0;
color: #6772e5;
font-size: 16px;
line-height: 22px;
font-weight: 600;
letter-spacing: .025em;
margin-left: 10px;
`;
const StyledLink = styled.a`
display: flex;
align-items: center;
&:hover {
opacity: 0.7;
}
`;
export default () => {
return (
<List>
<ListItem>
<StyledLink href="http://reactjunkie.com">
<img src={aboutMeIcon} width={24} height={24}/>
<Heading>ABOUT ME</Heading>
</StyledLink>
</ListItem>
<ListItem>
<img src={customersIcon} width={24} height={24}/>
<Heading>CUSTOMERS</Heading>
</ListItem>
<ListItem>
<img src={jobsIcon} width={24} height={24}/>
<Heading>JOBS</Heading>
</ListItem>
<ListItem>
<img src={environmentIcon} width={24} height={24}/>
<Heading>ENVIRONMENT</Heading>
</ListItem>
</List>
);
};
================================================
FILE: examples/advanced/src/universal/app/developers.js
================================================
import React from 'react';
import styled from 'styled-components';
import documentIcon from '../../../assets/documentation.png';
const RootGrid = styled.div`
display: grid;
grid-template-columns: 60px auto;
grid-template-rows: [top-space] 30px [doco-row] 60px [row-space] 20px [list-row] 60px;
font-size: 15px;
color: lightslategray;
`;
const DocoLogo = styled.div`
grid-row: doco-row / span 1;
grid-column: 1 / span 1;
display: flex;
justify-content: flex-end;
padding-right: 10px;
`;
const HeadingText = styled.div`
grid-row: doco-row / span 1;
grid-column: 2 / span 1;
display: flex;
flex-direction: column;
justify-content: flex-start;
font-size: 15px;
`;
const DocumentationHeading = styled.div`
margin: 0;
padding-bottom: 10px;
color: #6772e5;
font-size: 16px;
line-height: 22px;
font-weight: 600;
letter-spacing: .025em;
`;
const ListGroupGrid = styled.div`
grid-row: list-row / span 1;
grid-column: 2 / span 1;
display: grid;
grid-template-columns: [get-started] auto [popular-topics] auto;
grid-template-rows: 120px;
`;
const GetStartedGridItem = styled.div`
grid-row: 1 / span 1;
grid-column: get-started / span 1;
display: flex;
flex-direction: column;
justify-content: flex-start;
`;
const PopularGridItem = styled.div`
grid-row: 1 / span 1;
grid-column: popular-topics / span 1;
display: flex;
flex-direction: column;
justify-content: flex-start;
`;
const ColumnHeading = styled.div`
color: #8898aa;
font-size: 14px;
`;
const List = styled.ul`
color: #6772e5;
margin-top: 5px;
margin-bottom: 5px;
font-size: 14px
`;
const ListItem = styled.li`
margin-top: 8px;
margin-bottom: 8px;
`;
export default () => {
return (
<RootGrid>
<DocoLogo>
<img src={documentIcon} width={24} height={24}/>
</DocoLogo>
<HeadingText>
<DocumentationHeading>DOCUMENTATION</DocumentationHeading>
<span>Start integrating products and tools.</span>
</HeadingText>
<ListGroupGrid>
<GetStartedGridItem>
<ColumnHeading>GET STARTED</ColumnHeading>
<List>
<ListItem>Elements</ListItem>
<ListItem>Checkout</ListItem>
<ListItem>Mobile apps</ListItem>
<ListItem>Libraries</ListItem>
</List>
</GetStartedGridItem>
<PopularGridItem>
<ColumnHeading>POPULAR TOPICS</ColumnHeading>
<List>
<ListItem>Apple Pay</ListItem>
<ListItem>Testing</ListItem>
<ListItem>Launch checklist</ListItem>
<ListItem>Plug-ins</ListItem>
</List>
</PopularGridItem>
</ListGroupGrid>
</RootGrid>
);
};
================================================
FILE: examples/advanced/src/universal/app/pricing.js
================================================
import React from 'react';
import styled from 'styled-components';
import smiley from '../../../assets/smiley.png';
const List = styled.ul`
display: flex;
flex-direction: column;
margin-top: 10px;
margin-left: 30px;
color: lightslategray;
`;
const ListItem = styled.li`
display: flex;
flex-direction: row;
margin-top: 20px;
align-items: center;
a:hover {
opacity: 0.7;
}
`;
const Heading = styled.div`
margin: 0;
color: #6772e5;
font-size: 16px;
line-height: 22px;
font-weight: 600;
letter-spacing: .025em;
`;
const HeadingText = styled.div`
display: flex;
flex-direction: column;
margin-left: 10px;
`;
const Text = styled.div`
font-size: 13px;
`;
const StyledLink = styled.a`
display: flex;
align-items: center;
&:hover {
opacity: 0.7;
}
`;
export default () => {
return (
<List>
<ListItem>
<StyledLink href="https://github.com/yusinto/react-site-nav">
<img src={smiley} width={24} height={24}/>
<HeadingText>
<Heading>STAR IT!</Heading>
<Text>github.com/yusinto/react-site-nav</Text>
</HeadingText>
</StyledLink>
</ListItem>
</List>
);
};
================================================
FILE: examples/advanced/src/universal/app/products.js
================================================
import React from 'react';
import styled from 'styled-components';
import {Link} from 'react-router-dom';
import paymentIcon from '../../../assets/payment.png';
import billingIcon from '../../../assets/billing.png';
import connectIcon from '../../../assets/connect.png';
const ListContainer = styled.div`
display: flex;
height: 100%;
width: 100%;
justify-content: center;
`;
const List = styled.ul`
color: lightslategray;
display: flex;
flex-direction: column;
justify-content: space-evenly;
`;
const ListItemContent = styled.div`
display: flex;
flex-direction: row;
&:hover {
opacity: 0.7;
}
`;
const LisItemHeadingText = styled.div`
display: flex;
flex-direction: column;
justify-content: space-between;
font-size: 15px;
margin-left: 10px;
`;
const ListItemHeading = styled.div`
margin: 0;
color: #6772e5;
font-size: 16px;
line-height: 22px;
font-weight: 600;
letter-spacing: .025em;
`;
export default () => {
return (
<ListContainer>
<List>
<li>
<Link to="/contact">
<ListItemContent>
<img src={paymentIcon} width={48} height={48}/>
<LisItemHeadingText>
<ListItemHeading>PAYMENTS</ListItemHeading>
<div>A complete payments platform engineered.</div>
</LisItemHeadingText>
</ListItemContent>
</Link>
</li>
<li>
<ListItemContent>
<img src={billingIcon} width={48} height={48}/>
<LisItemHeadingText>
<ListItemHeading>BILLING</ListItemHeading>
<div>Build and scale your recurring business model.</div>
</LisItemHeadingText>
</ListItemContent>
</li>
<li>
<ListItemContent>
<img src={connectIcon} width={48} height={48}/>
<LisItemHeadingText>
<ListItemHeading>CONNECT</ListItemHeading>
<div>Everything platforms need to get sellers paid.</div>
</LisItemHeadingText>
</ListItemContent>
</li>
</List>
</ListContainer>
);
};
================================================
FILE: examples/advanced/src/universal/contact.js
================================================
import React, {Component, Timeout} from 'react';
import styled from 'styled-components';
const RootDiv = styled.div`
margin-top: 30px;
margin-left: 30px;
color: #fff;
`;
const Heading = styled.h1`
font-weight: 400;
color: #fff;
`;
export default props =>
<RootDiv>
<Heading>Thanks for checking react-site-nav!</Heading>
<p>
Check out my blog at <a href="http://reactjunkie.com" target="_blank"
rel="noopener noreferrer">reactjunkie.com</a>
</p>
<div>You can reach me via:</div>
<ul>
<li>
<a href="mailto:yusinto@gmail.com">Email</a>
</li>
</ul>
</RootDiv>;
================================================
FILE: examples/advanced/src/universal/home.js
================================================
import React, {Component} from 'react';
import styled from 'styled-components';
const RootDiv = styled.div`
margin-top: 30px;
margin-left: 30px;
color: #fff;
`;
const Heading = styled.h1`
font-weight: 400;
color: #fff;
`;
export default class Home extends Component {
render() {
return (
<RootDiv>
<Heading>React Site Nav</Heading>
The new standard in site navigation.
</RootDiv>
);
}
}
================================================
FILE: examples/advanced/webpack.config.client.js
================================================
const path = require('path');
const WebpackServeUrl = 'http://localhost:3002';
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: ['@babel/polyfill', './src/client/index'],
output: {
path: path.resolve('dist'),
publicPath: `${WebpackServeUrl}/dist/`, // MUST BE FULL PATH!
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.jsx?$/,
include: path.resolve('src'),
exclude: /node_modules/,
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
publicPath: 'dist/',
}
}
]
}
],
},
};
================================================
FILE: examples/advanced/webpack.config.server.js
================================================
const path = require('path');
const nodeExternals = require('webpack-node-externals');
module.exports = {
mode: 'development',
devtool: 'source-map',
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()
target: 'node', // tell webpack this bundle will be used in nodejs environment.
externals: [nodeExternals()], // Omit node_modules code from the bundle. You don't want and don't need them in the bundle.
output: {
path: path.resolve('dist'),
filename: 'serverBundle.js',
libraryTarget: 'commonjs2', // IMPORTANT! Add module.exports to the beginning of the bundle, so universal-hot-reload can access your app.
},
// The rest of the config is pretty standard and can contain other webpack stuff you need.
module: {
rules: [
{
test: /\.jsx?$/,
include: path.resolve('src'),
exclude: /node_modules/,
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
publicPath: 'dist/',
}
}
]
}
],
},
};
================================================
FILE: examples/advanced/webpack.prod.config.js
================================================
const path = require('path');
module.exports = {
mode: 'production',
devtool: 'source-map',
entry: ['@babel/polyfill', './src/client/index'],
output: {
path: path.resolve('now/build'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.jsx?$/,
include: path.resolve('src'),
exclude: /node_modules/,
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
publicPath: 'build/',
}
}
]
}
],
},
};
================================================
FILE: examples/basic/.babelrc
================================================
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-async-to-generator",
["babel-plugin-styled-components", {
"ssr": true,
"displayName": true
}]
]
}
================================================
FILE: examples/basic/.eslintrc
================================================
{
"parser": "babel-eslint",
"parserOptions": {
"allowImportExportEverywhere": true
},
"extends": [
"airbnb",
"eslint:recommended",
"plugin:react/recommended"
],
"plugins": [
"babel"
],
"rules": {
"arrow-parens": 0,
"eol-last": 0,
"global-require": 0,
"arrow-body-style": 0,
"consistent-return": 0,
"no-unneeded-ternary": 0,
"max-len": 0,
"no-param-reassign": 2,
"new-cap": 0,
"no-console": 0,
"object-curly-spacing": 0,
"spaced-comment": 0,
"import/no-extraneous-dependencies": 0,
"import/first": 0,
"import/prefer-default-export": 0,
"import/no-mutable-exports": 0,
"import/no-named-as-default": 0,
"react/jsx-filename-extension": 0,
"react/jsx-indent": 0,
"react/jsx-indent-props": 0,
"react/jsx-space-before-closing": 0,
"react/jsx-first-prop-new-line": 0,
"react/prefer-stateless-function": 0,
"react/jsx-closing-bracket-location": 0,
"react/require-extension": 0,
"react/sort-comp": 0,
"react/jsx-wrap-multilines": 0,
"react/jsx-no-bind": 0,
"react/jsx-users-react": 0,
"react/jsx-tag-spacing": 0,
"jsx-a11y/anchor-is-valid": 0,
"jsx-a11y/img-has-alt": 0,
"no-trailing-spaces": 0,
"no-underscore-dangle": 0,
"no-use-before-define": 0,
"no-duplicate-imports": 0,
"import/no-duplicates": 1,
"no-useless-escape": 0,
"no-unused-expressions": [1 , {"allowTernary": true}]
},
"env": {
"browser": true,
"jest": true,
"node": true
},
"globals": {
"React": true,
"fetch": true,
"jest": true
}
}
================================================
FILE: examples/basic/.gitignore
================================================
node_modules
.idea
npm-debug.log
dist
.eslintcache
================================================
FILE: examples/basic/README.md
================================================
# react-site-nav basic example
A simple spa demonstrating react-site-nav.
yarn && yarn start
================================================
FILE: examples/basic/package.json
================================================
{
"name": "react-site-menu-example",
"version": "1.1.0",
"description": "Demo of react-site-menu a kickass navigation menu inspired by stripe.com",
"main": "src/server/index.js",
"scripts": {
"start": "node src/server/index.js",
"lint": "eslint ./src",
"serve": "webpack-serve webpack.config.server"
},
"repository": {
"type": "git",
"url": "https://github.com/yusinto/react-site-menu.git"
},
"keywords": [
"react",
"site",
"navigation",
"menu",
"bar",
"animated",
"stripe"
],
"author": "Yus Ng",
"license": "MIT",
"bugs": {
"url": "https://github.com/yusinto/react-site-menu"
},
"homepage": "https://github.com/yusinto/react-site-menu",
"dependencies": {
"@babel/plugin-transform-async-to-generator": "^7.0.0-beta.54",
"@babel/polyfill": "^7.0.0-beta.54",
"babel-plugin-styled-components": "^1.5.1",
"css-loader": "^1.0.0",
"express": "^4.16.3",
"lodash": "^4.17.11",
"lodash.kebabcase": "^4.1.1",
"memoize-one": "^4.0.2",
"prop-types": "^15.6.2",
"react": "^16.5.0",
"react-dom": "^16.5.0",
"react-router-dom": "^4.3.1",
"react-site-nav": "^0.2.6",
"style-loader": "^0.23.0",
"styled-components": "^3.4.6"
},
"devDependencies": {
"@babel/cli": "^7.0.0-beta.54",
"@babel/core": "^7.0.0-beta.54",
"@babel/plugin-proposal-class-properties": "^7.0.0-beta.54",
"@babel/preset-env": "^7.0.0-beta.54",
"@babel/preset-react": "^7.0.0-beta.54",
"babel-eslint": "^8.2.6",
"babel-loader": "^8.0.0-beta",
"eslint": "^5.5.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "^6.1.1",
"eslint-plugin-react": "^7.11.1",
"file-loader": "^2.0.0",
"universal-hot-reload": "^1.0.6",
"webpack": "^4.18.0",
"webpack-cli": "^3.1.0",
"webpack-node-externals": "^1.7.2",
"webpack-serve": "^2.0.2"
}
}
================================================
FILE: examples/basic/src/client/index.js
================================================
import React from 'react';
import {hydrate} from 'react-dom';
import {BrowserRouter} from 'react-router-dom';
import App from '../universal/app';
hydrate(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.getElementById('reactDiv'),
);
================================================
FILE: examples/basic/src/server/index.js
================================================
const UniversalHotReload = require('universal-hot-reload').default;
UniversalHotReload(require('../../webpack.config.server.js'), require('../../webpack.config.client.js'));
================================================
FILE: examples/basic/src/server/server.js
================================================
import Express from 'express';
import React from 'react';
import {renderToString} from 'react-dom/server';
import {ServerStyleSheet, StyleSheetManager} from 'styled-components';
import {StaticRouter} from 'react-router-dom';
import App from '../universal/app';
const PORT = 3000;
const app = Express();
app.use('/dist', Express.static('dist', {maxAge: '1d'}));
app.use((req, res) => {
const html = `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>React Site Nav - Basic</title>
</head>
<body style="margin:0;background: #F6F9FC;">
<div id="reactDiv">${renderToString(
<StaticRouter
location={req.url}
context={{}}>
<App/>
</StaticRouter>)}
</div>
<script type="application/javascript" src="http://localhost:3002/dist/bundle.js"></script>
</body>
</html>`;
res.end(html);
});
const httpServer = app.listen(PORT, () => {
console.log(`Example app listening at ${PORT}...`);
});
// export httpServer object so universal-hot-reload can access it
module.exports = httpServer;
================================================
FILE: examples/basic/src/universal/app.css
================================================
h1 {
}
ul {
list-style-type: none;
}
li {
margin-top: 10px
}
a {
text-decoration: none;
color: #24b47e;
}
a:hover {
opacity: 0.5;
}
a:visited {
color: #6b7c93;
}
.header {
display: grid;
grid-template-columns: 100px auto 100px;
grid-template-rows: 80px;
background: #fff;
}
.logo {
height: 70px;
margin-left: 20px;
margin-top: 5px
}
================================================
FILE: examples/basic/src/universal/app.js
================================================
import React, {Component} from 'react';
import {Switch, Link, Route, Redirect} from 'react-router-dom';
import Home from './home';
import SpaLink1 from './spaLink1';
import SpaLink2 from './spaLink2';
import SiteNav, {ContentGroup} from 'react-site-nav';
import './app.css';
import logo from '../../assets/logo.jpg';
export default () =>
(
<div>
<header>
<div style={{width: '100%', background: 'white'}}>
<Link to="/">
<img style={{height: '70px', marginLeft: '20px', marginTop: '5px'}} src={logo}/>
</Link>
</div>
<SiteNav>
<ContentGroup title="About" width="300" height="100">
<ul>
<li><Link to="/spa-link-1">Spa Link</Link></li>
<li><Link to="/spa-link-2">Another Spa Link</Link></li>
</ul>
</ContentGroup>
<ContentGroup title="Contact" width="200" height="150">
<ul style={{listStyleType: 'none'}}>
<li>
<a href="mailto:yusinto@gmail.com">Email</a>
</li>
<li>
<a href="https://github.com/yusinto">Github</a>
</li>
<li>
<a href="https://twitter.com/yusinto">Twitter</a>
</li>
<li>
<a href="https://linkedin.com/in/yusinto">Tinkedin</a>
</li>
</ul>
</ContentGroup>
</SiteNav>
</header>
<main>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/home">
<Redirect to="/"/>
</Route>
<Route path="/spa-link-1" component={SpaLink1}/>
<Route path="/spa-link-2" component={SpaLink2}/>
</Switch>
</main>
</div>
);
================================================
FILE: examples/basic/src/universal/home.js
================================================
import React, {Component} from 'react';
export default class Home extends Component {
render() {
return (
<div style={{marginTop: '30px', marginLeft: '30px'}}>
<h1>react-site-nav</h1>
<p>Welcome to react-site-nav! This is a basic demo of its usage.</p>
<div>Prs and comments welcome!</div>
</div>
);
}
}
================================================
FILE: examples/basic/src/universal/spaLink1.js
================================================
import React, {Component, Timeout} from 'react';
export default props =>
<div style={{marginTop: '30px', marginLeft: '30px'}}>
<h1>This is spa link #1</h1>
<p>Thanks for checking react-site-nav!</p>
<p>
Check out my blog at <a href="http://reactjunkie.com" target="_blank"
rel="noopener noreferrer">reactjunkie.com</a>
</p>
<div>You can reach me via:</div>
<ul>
<li>
<a href="mailto:yusinto@gmail.com">email</a>
</li>
<li>
<a href="https://github.com/yusinto">github</a>
</li>
<li>
<a href="https://twitter.com/yusinto">twitter</a>
</li>
<li>
<a href="https://linkedin.com/in/yusinto">linkedin</a>
</li>
</ul>
</div>;
================================================
FILE: examples/basic/src/universal/spaLink2.js
================================================
import React, {Component, Timeout} from 'react';
export default props =>
<div style={{marginTop: '30px', marginLeft: '30px'}}>
<h1>This is spa link #2</h1>
<p>
Check out my blog at <a href="http://reactjunkie.com" target="_blank"
rel="noopener noreferrer">reactjunkie.com</a>
</p>
</div>;
================================================
FILE: examples/basic/webpack.config.client.js
================================================
const path = require('path');
const WebpackServeUrl = 'http://localhost:3002';
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: ['@babel/polyfill', './src/client/index'],
output: {
path: path.resolve('dist'),
publicPath: `${WebpackServeUrl}/dist/`, // MUST BE FULL PATH!
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.jsx?$/,
include: path.resolve('src'),
exclude: /node_modules/,
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
publicPath: 'dist/',
}
}
]
},
{
test: /\.css$/,
use: [
{loader: "style-loader"},
{
loader: "css-loader",
options: {
importLoaders: 1,
modules: true,
localIdentName: '[folder]--[name]--[local]--[hash:base64:2]',
},
}
]
},
],
},
};
================================================
FILE: examples/basic/webpack.config.server.js
================================================
const path = require('path');
const nodeExternals = require('webpack-node-externals');
module.exports = {
mode: 'development',
devtool: 'source-map',
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()
target: 'node', // tell webpack this bundle will be used in nodejs environment.
externals: [nodeExternals()], // Omit node_modules code from the bundle. You don't want and don't need them in the bundle.
output: {
path: path.resolve('dist'),
filename: 'serverBundle.js',
libraryTarget: 'commonjs2', // IMPORTANT! Add module.exports to the beginning of the bundle, so universal-hot-reload can access your app.
},
// The rest of the config is pretty standard and can contain other webpack stuff you need.
module: {
rules: [
{
test: /\.jsx?$/,
include: path.resolve('src'),
exclude: /node_modules/,
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
publicPath: 'dist/',
}
}
]
},
{
test: /\.css$/,
loader: "css-loader",
options: {
importLoaders: 1,
modules: true,
localIdentName: '[folder]--[name]--[local]--[hash:base64:2]',
},
},
],
},
};
================================================
FILE: examples/cra-with-nav/.gitignore
================================================
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
================================================
FILE: examples/cra-with-nav/README.md
================================================
## Create react app with react-site-nav
Live demo [here](https://build-licattzisr.now.sh/).
================================================
FILE: examples/cra-with-nav/package.json
================================================
{
"name": "cra-with-nav",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.5.0",
"react-dom": "^16.5.0",
"react-site-nav": "^0.2.6"
},
"devDependencies": {
"react-scripts": "1.1.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"now": "npm run build && cd ./build && now --public"
}
}
================================================
FILE: examples/cra-with-nav/public/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
================================================
FILE: examples/cra-with-nav/public/manifest.json
================================================
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
================================================
FILE: examples/cra-with-nav/src/App.css
================================================
ul {
list-style-type: none;
margin: 0;
padding: 0;
}
li {
display: flex;
justify-content: center;
align-items: center;
height: 45px;
font-size: 15px;
}
li:hover {
opacity: 0.7;
}
li > a {
flex: 0 0 110px;
text-align: left;
margin-left: 10px;
text-decoration: none;
color: dodgerblue;
}
.App {
text-align: center;
}
.App-logo {
/* this causes an issue in firefox which breaks the flyout animation */
/*animation: App-logo-spin infinite 20s linear;*/
height: 80px;
}
.App-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}
.App-title {
font-size: 1.5em;
}
.App-intro {
font-size: large;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
================================================
FILE: examples/cra-with-nav/src/App.js
================================================
import React from 'react';
import logo from './logo.svg';
import reactMenuImage from './react-logo.png';
import aboutMeImage from './about-me.png';
import docsImage from './docs.png';
import communityImage from './community.png';
import reactSiteNavImage from './react-site-nav-logo.png';
import tutorialImage from './tutorial.png';
import './App.css';
import SiteNav, {ContentGroup} from 'react-site-nav';
export default () => (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo"/>
<h1 className="App-title">Welcome to React</h1>
</header>
<SiteNav debug={false}>
<ContentGroup title="Github" width="220" height="100">
<ul>
<li>
<img src={reactMenuImage} width="30" height="30" alt="react"/>
<a href="https://github.com/facebook/react">React</a>
</li>
<li>
<img src={reactSiteNavImage} height="30" alt="react site nav"/>
<a href="https://github.com/yusinto/react-site-nav">React Site Nav</a>
</li>
</ul>
</ContentGroup>
<ContentGroup title="Developers" width="180" height="200">
<ul>
<li>
<img src={docsImage} height="30" alt="docs"/>
<a href="https://reactjs.org/docs/getting-started.html">Docs</a>
</li>
<li>
<img src={tutorialImage} height="30" alt="tutorial"/>
<a href="https://reactjs.org/tutorial/tutorial.html">Tutorial</a>
</li>
<li>
<img src={communityImage} height="30" alt="community"/>
<a href="https://reactjs.org/community/support.html">Community</a>
</li>
<li>
<img src={aboutMeImage} height="30" alt="blog"/>
<a href="http://www.reactjunkie.com/">Blog</a>
</li>
</ul>
</ContentGroup>
</SiteNav>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
================================================
FILE: examples/cra-with-nav/src/App.test.js
================================================
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});
================================================
FILE: examples/cra-with-nav/src/index.css
================================================
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
================================================
FILE: examples/cra-with-nav/src/index.js
================================================
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
================================================
FILE: examples/cra-with-nav/src/registerServiceWorker.js
================================================
// In production, we register a service worker to serve assets from local cache.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the "N+1" visit to a page, since previously
// cached resources are updated in the background.
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
// This link also includes instructions on opting out of this behavior.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://goo.gl/SC7cgQ'
);
});
} else {
// Is not local host. Just register service worker
registerValidSW(swUrl);
}
});
}
}
function registerValidSW(swUrl) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and
// the fresh content will have been added to the cache.
// It's the perfect time to display a "New content is
// available; please refresh." message in your web app.
console.log('New content is available; please refresh.');
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
if (
response.status === 404 ||
response.headers.get('content-type').indexOf('javascript') === -1
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}
================================================
FILE: jest.config.js
================================================
module.exports = {
setupFiles: ['./test/setup.js'],
};
================================================
FILE: lib/index.js
================================================
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.ContentGroup = void 0;
var _react = _interopRequireWildcard(require("react"));
var _styledComponents = _interopRequireWildcard(require("styled-components"));
var _memoizeOne = _interopRequireDefault(require("memoize-one"));
var _lodash = _interopRequireDefault(require("lodash.kebabcase"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _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; } }
function _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); }
function _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; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _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); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _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); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _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; }
var defaultRootAlign = 'center';
var defaultColor = '#fff';
var defaultColumnWidth = 150;
var defaultRowHeight = 45;
var defaultBackground = '#323232';
var defaultBreakpoint = 768;
var defaultContentBackground = '#fff';
var defaultContentColor = '#323232';
var defaultContentWidth = 320;
var defaultContentHeight = 200;
var defaultContentTop = 0;
var arrowHeight = 8;
var perspective = 850;
var fadeOutSeconds = 0.34;
var fadeInSeconds = 0.25;
var moveSeconds = 0.25;
var moveArrowSeconds = 0.28;
var fadeOutContentSeconds = 0.29;
var fadeInContentSeconds = 0.1;
var OffScreenPadding = 10;
var setFromProps = function setFromProps(camelCaseKey) {
return (0, _styledComponents.css)(["", ""], function (props) {
return props[camelCaseKey] ? "".concat((0, _lodash.default)(camelCaseKey), ": ").concat(props[camelCaseKey]) : null;
});
};
var GridContainer = _styledComponents.default.div.withConfig({
displayName: "src__GridContainer",
componentId: "sc-178mgjs-0"
})(["@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) {
var breakpoint = _ref.breakpoint;
return breakpoint - 1;
}, function (_ref2) {
var breakpoint = _ref2.breakpoint;
return breakpoint;
}, setFromProps('justifyContent'), function (_ref3) {
var columns = _ref3.columns;
return columns;
}, function (_ref4) {
var columnWidth = _ref4.columnWidth;
return columnWidth;
}, function (_ref5) {
var rowHeight = _ref5.rowHeight;
return rowHeight;
}, setFromProps('background'), setFromProps('color'), setFromProps('fontFamily'), setFromProps('fontSize'));
var GridItemLink = _styledComponents.default.a.withConfig({
displayName: "src__GridItemLink",
componentId: "sc-178mgjs-1"
})(["grid-column:", " / span 1;display:flex;justify-content:center;align-items:center;&:hover{opacity:0.5;}", ";&:visited{", ";}"], function (_ref6) {
var index = _ref6.index;
return index + 1;
}, setFromProps('color'), setFromProps('color'));
var GridItem = _styledComponents.default.div.withConfig({
displayName: "src__GridItem",
componentId: "sc-178mgjs-2"
})(["grid-column:", " / span 1;display:flex;justify-content:center;align-items:center;&:hover{opacity:0.5;cursor:default;}"], function (_ref7) {
var index = _ref7.index;
return index + 1;
});
var ContentRow = _styledComponents.default.div.withConfig({
displayName: "src__ContentRow",
componentId: "sc-178mgjs-3"
})(["grid-column:1 / span ", ";grid-row:2 / span 1;position:relative;height:0;"], function (_ref8) {
var columns = _ref8.columns;
return columns;
});
var Move = function Move(fromData, toData) {
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);
};
var 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);
var 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);
var MovingDiv = _styledComponents.default.div.withConfig({
displayName: "src__MovingDiv",
componentId: "sc-178mgjs-4"
})(["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) {
var top = _ref9.top;
return top;
}, function (_ref10) {
var fromData = _ref10.fromData;
return fromData ? fromData.left : 0;
}, function (_ref11) {
var fromData = _ref11.fromData;
return fromData ? fromData.width : 0;
}, function (_ref12) {
var fromData = _ref12.fromData;
return fromData ? fromData.height : 0;
}, function (_ref13) {
var display = _ref13.display;
return display;
}, function (_ref14) {
var fadeOut = _ref14.fadeOut,
display = _ref14.display,
fromData = _ref14.fromData,
toData = _ref14.toData;
if (fadeOut) return FadeOut;
if (display === 'block') {
if (fromData.left === toData.left) return FadeIn;
if (fromData) return Move(fromData, toData);
}
return ''; // display: none; don't animate
}, function (_ref15) {
var fadeOut = _ref15.fadeOut,
display = _ref15.display,
fromData = _ref15.fromData,
toData = _ref15.toData;
if (fadeOut) return "".concat(fadeOutSeconds, "s");
if (display === 'block') {
if (fromData.left === toData.left) return "".concat(fadeInSeconds, "s"); // fade in
if (fromData) return "".concat(moveSeconds, "s"); // move
}
return '0s'; // display: none; don't animate
});
var FadeInArrow = (0, _styledComponents.keyframes)(["from{opacity:0;}to{opacity:1;}"]);
var FadeOutArrow = (0, _styledComponents.keyframes)(["from{opacity:1;}to{opacity:0;}"]);
var calculateArrowMarginLeft = function calculateArrowMarginLeft(data, leftOffset, rightOffset) {
return (0, _styledComponents.css)(["margin-left:", "px;"], data ? data.left + data.width / 2 - leftOffset + rightOffset - arrowHeight - (leftOffset > 0 || rightOffset > 0 ? OffScreenPadding : 0) : 0);
};
var MoveArrow = function MoveArrow(fromData, toData, leftOffset, rightOffset) {
return (0, _styledComponents.keyframes)(["from{", "}to{", "}"], calculateArrowMarginLeft(fromData, leftOffset, rightOffset), calculateArrowMarginLeft(toData, leftOffset, rightOffset));
};
var Arrow = _styledComponents.default.div.withConfig({
displayName: "src__Arrow",
componentId: "sc-178mgjs-5"
})(["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) {
var top = _ref16.top;
return arrowHeight - top;
}, function (_ref17) {
var toData = _ref17.toData,
leftOffset = _ref17.leftOffset,
rightOffset = _ref17.rightOffset;
return calculateArrowMarginLeft(toData, leftOffset, rightOffset);
}, function (_ref18) {
var display = _ref18.display,
toData = _ref18.toData;
if (toData && toData.width === 0 && toData.height === 0) {
return 'none';
}
return display;
}, arrowHeight, arrowHeight, arrowHeight, function (_ref19) {
var background = _ref19.background;
return background;
}, function (_ref20) {
var fadeOut = _ref20.fadeOut,
display = _ref20.display,
fromData = _ref20.fromData,
toData = _ref20.toData,
leftOffset = _ref20.leftOffset,
rightOffset = _ref20.rightOffset;
if (fadeOut) return FadeOutArrow;
if (display === 'block') {
if (fromData.left === toData.left) return FadeInArrow;
if (fromData) return MoveArrow(fromData, toData, leftOffset, rightOffset);
}
return ''; // display: none; don't animate
}, function (_ref21) {
var fadeOut = _ref21.fadeOut,
display = _ref21.display,
fromData = _ref21.fromData,
toData = _ref21.toData;
if (fadeOut) return "".concat(fadeOutSeconds, "s");
if (display === 'block') {
if (fromData.left === toData.left) return "".concat(fadeInSeconds, "s"); // fade in
if (fromData) return "".concat(moveArrowSeconds, "s"); // move
}
return '0s'; // display: none; don't animate
});
var FadeInContent = (0, _styledComponents.keyframes)(["from{opacity:0;}to{opacity:1;}"]);
var FadeOutContent = (0, _styledComponents.keyframes)(["from{opacity:1;}to{opacity:0;visibility:hidden;}"]);
var ContentGroupContainer = _styledComponents.default.div.withConfig({
displayName: "src__ContentGroupContainer",
componentId: "sc-178mgjs-6"
})(["position:absolute;margin-top:0;margin-bottom:0;width:100%;height:100%;opacity:", ";z-index:", ";pointer-events:", ";animation:", " ", "s forwards;"], function (_ref22) {
var show = _ref22.show;
return show ? 1 : 0;
}, function (_ref23) {
var show = _ref23.show;
return show ? 1 : 0;
}, function (_ref24) {
var show = _ref24.show;
return show ? 'auto' : 'none';
}, function (_ref25) {
var show = _ref25.show,
fadeOut = _ref25.fadeOut;
if (show) return FadeInContent;
if (fadeOut) return FadeOutContent;
return ''; // cold start and everything else just show without animation
}, function (_ref26) {
var show = _ref26.show;
return show ? "".concat(fadeInContentSeconds) : "".concat(fadeOutContentSeconds);
});
var ContentGroup = function ContentGroup(_ref27) {
var title = _ref27.title,
width = _ref27.width,
height = _ref27.height,
background = _ref27.background;
return _react.default.createElement(_react.default.Fragment, null, title, width, "x", height, background);
};
exports.ContentGroup = ContentGroup;
var SiteNav =
/*#__PURE__*/
function (_Component) {
_inherits(SiteNav, _Component);
function SiteNav() {
var _getPrototypeOf2;
var _this;
_classCallCheck(this, SiteNav);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(SiteNav)).call.apply(_getPrototypeOf2, [this].concat(args)));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "state", {
display: 'none',
fadeOut: false,
fromData: null,
toData: null,
leftOffset: 0,
rightOffset: 0
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "memoizeMenuData", (0, _memoizeOne.default)(function (columnWidth, children) {
return _react.default.Children.map(children, function (child, i) {
// if width and height are not specified, that means we don't want to render the content group i.e. we only
// want to render root item
var _child$props = child.props,
width = _child$props.width,
height = _child$props.height;
var sanitisedWidth, sanitisedHeight;
if (!width && !height) {
sanitisedWidth = 0;
sanitisedHeight = 0;
} else {
// if width or height is not specified, add defaults
sanitisedWidth = width || defaultContentWidth;
sanitisedHeight = height || defaultContentHeight;
}
return _objectSpread({}, child.props, {
// order is important here! spread child.props after height, followed by width.
height: sanitisedHeight,
width: sanitisedWidth,
index: i,
left: (i + 1) * columnWidth - columnWidth / 2 - sanitisedWidth / 2
});
});
}));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "memoizeGridItems", (0, _memoizeOne.default)(function (children, color) {
return _react.default.Children.map(children, function (child, i) {
var _child$props2 = child.props,
title = _child$props2.title,
rootUrl = _child$props2.rootUrl;
if (rootUrl) {
return _react.default.createElement(GridItemLink, {
href: rootUrl,
key: "menu-title-".concat(i),
index: i,
onMouseEnter: function onMouseEnter(e) {
return _this.onMouseEnter(e.target, i);
},
color: color
}, title);
}
return _react.default.createElement(GridItem, {
key: "menu-title-".concat(i),
index: i,
onMouseEnter: function onMouseEnter(e) {
return _this.onMouseEnter(e.target, i);
},
color: color
}, title);
});
}));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "memoizeContent", (0, _memoizeOne.default)(function (children, fromData, toData) {
return _react.default.Children.map(children, function (child, i) {
return _react.default.createElement(ContentGroupContainer, {
key: "content-group-".concat(i),
show: toData && toData.index === i,
fadeOut: fromData && fromData.index === i
}, child.props.children);
});
}));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "memoizeColumns", (0, _memoizeOne.default)(function (children) {
return _react.default.Children.count(children);
}));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "memoizeAlign", (0, _memoizeOne.default)(function (align) {
switch (align) {
case 'left':
return 'start';
case 'right':
return 'end';
default:
return 'center';
}
}));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "close", function () {
if (_this.props.debug) return;
_this.setState(function (prevState) {
return {
fadeOut: true,
fromData: prevState.toData
};
});
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onMouseEnter", function (target, menuDataIndex) {
_this.setState(function (prevState) {
var fadeOut = false;
var display = 'block';
var toDataOriginal = _this.memoizeMenuData(_this.props.columnWidth, _this.props.children)[menuDataIndex];
var toData = _objectSpread({}, toDataOriginal);
var leftOffset = 0;
var rightOffset = 0;
if (target) {
// off screen detection
// target is rootGridItem
var _target$getBoundingCl = target.getBoundingClientRect(),
left = _target$getBoundingCl.left,
width = _target$getBoundingCl.width;
var siteNavWidth = target.parentNode.clientWidth;
leftOffset = toData.width / 2 - (left + width / 2);
rightOffset = toData.width / 2 - (siteNavWidth - (left + width / 2));
if (leftOffset > 0) {
// if off screen, toData.left needs to be moved to be on-screen!
toData.left += leftOffset + OffScreenPadding;
} else {
leftOffset = 0;
}
if (rightOffset > 0) {
toData.left -= rightOffset - OffScreenPadding;
} else {
rightOffset = 0;
}
var fromData;
if (prevState.fadeOut || !prevState.toData) {
// on cold start, pop up right from the current item
fromData = toData;
} else {
// on warm start, start animation from the previous item
fromData = prevState.toData;
}
return {
display: display,
fadeOut: fadeOut,
fromData: fromData,
toData: toData,
leftOffset: leftOffset,
rightOffset: rightOffset
};
}
});
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onMouseLeave", function () {
return _this.close();
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onClickMovingDiv", function () {
return _this.close();
});
return _this;
}
_createClass(SiteNav, [{
key: "render",
value: function render() {
var _this$props = this.props,
columnWidth = _this$props.columnWidth,
rowHeight = _this$props.rowHeight,
background = _this$props.background,
contentBackground = _this$props.contentBackground,
contentColor = _this$props.contentColor,
contentTop = _this$props.contentTop,
children = _this$props.children,
align = _this$props.align,
fontSize = _this$props.fontSize,
fontFamily = _this$props.fontFamily,
color = _this$props.color,
breakpoint = _this$props.breakpoint;
var _this$state = this.state,
fromData = _this$state.fromData,
toData = _this$state.toData,
display = _this$state.display,
fadeOut = _this$state.fadeOut,
leftOffset = _this$state.leftOffset,
rightOffset = _this$state.rightOffset;
var columns = this.memoizeColumns(children);
var rootGridItems = this.memoizeGridItems(children, color);
var content = this.memoizeContent(children, fromData, toData);
var justifyContent = this.memoizeAlign(align);
var contentBackgroundSanitised = toData && toData.background || contentBackground;
return _react.default.createElement("nav", null, _react.default.createElement(GridContainer, {
background: background,
columnWidth: columnWidth,
rowHeight: rowHeight,
justifyContent: justifyContent,
fontSize: fontSize,
fontFamily: fontFamily,
color: color,
breakpoint: breakpoint
/* Below are not configurable */
,
onMouseLeave: this.onMouseLeave,
columns: columns
}, rootGridItems, _react.default.createElement(ContentRow, {
columns: columns
}, _react.default.createElement(Arrow, {
display: display,
fadeOut: fadeOut,
fromData: fromData,
toData: toData,
top: contentTop,
onClick: this.onClickMovingDiv,
background: contentBackgroundSanitised,
leftOffset: leftOffset,
rightOffset: rightOffset
}), _react.default.createElement(MovingDiv, {
display: display,
fadeOut: fadeOut,
fromData: fromData,
toData: toData,
color: contentColor,
top: contentTop,
onClick: this.onClickMovingDiv,
background: contentBackgroundSanitised
}, content))));
}
}]);
return SiteNav;
}(_react.Component);
exports.default = SiteNav;
_defineProperty(SiteNav, "defaultProps", {
align: defaultRootAlign,
columnWidth: defaultColumnWidth,
rowHeight: defaultRowHeight,
background: defaultBackground,
contentBackground: defaultContentBackground,
contentColor: defaultContentColor,
contentTop: defaultContentTop,
breakpoint: defaultBreakpoint,
color: defaultColor,
debug: false
});
================================================
FILE: package.json
================================================
{
"name": "react-site-nav",
"version": "0.2.9",
"description": "A kick ass site menu powered by styled components inspired by Stripe.",
"main": "lib/index.js",
"scripts": {
"test": "jest",
"build": "rimraf lib/* && babel src -d lib --ignore *.test.js",
"lint": "eslint --cache --format 'node_modules/eslint-friendly-formatter' ./src",
"prep-publish": "npm run build && npm version patch -m 'Upgrade to %s' && npm publish && git push"
},
"repository": {
"type": "git",
"url": "git+https://github.com/yusinto/react-site-nav.git"
},
"keywords": [
"react",
"site",
"navigation",
"menu",
"bar",
"animated",
"stripe"
],
"author": "Yusinto Ngadiman",
"license": "MIT",
"bugs": {
"url": "https://github.com/yusinto/react-site-nav/issues"
},
"homepage": "https://github.com/yusinto/react-site-nav#readme",
"devDependencies": {
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.1",
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"babel-eslint": "^9.0.0",
"babel-plugin-styled-components": "^1.7.1",
"babel-preset-minify": "^0.4.3",
"eslint": "^5.5.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-friendly-formatter": "^4.0.1",
"eslint-plugin-babel": "^5.2.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "^6.1.1",
"eslint-plugin-react": "^7.11.1",
"rimraf": "^2.6.2"
},
"dependencies": {
"lodash.kebabcase": "^4.1.1",
"memoize-one": "^4.0.2",
"react": "^16.5.0",
"styled-components": "^3.4.6"
}
}
================================================
FILE: src/index.js
================================================
import React, {Component} from 'react';
import styled, {keyframes, css} from 'styled-components';
import memoize from 'memoize-one';
import kebabCase from 'lodash.kebabcase';
const defaultRootAlign = 'center';
const defaultColor = '#fff';
const defaultColumnWidth = 150;
const defaultRowHeight = 45;
const defaultBackground = '#323232';
const defaultBreakpoint = 768;
const defaultContentBackground = '#fff';
const defaultContentColor = '#323232';
const defaultContentWidth = 320;
const defaultContentHeight = 200;
const defaultContentTop = 0;
const arrowHeight = 8;
const perspective = 850;
const fadeOutSeconds = 0.34;
const fadeInSeconds = 0.25;
const moveSeconds = 0.25;
const moveArrowSeconds = 0.28;
const fadeOutContentSeconds = 0.29;
const fadeInContentSeconds = 0.1;
const OffScreenPadding = 10;
const setFromProps = camelCaseKey => css`
${props => props[camelCaseKey] ? `${kebabCase(camelCaseKey)}: ${props[camelCaseKey]}` : null}`;
const GridContainer = styled.div`
// use visibility hidden instead of display none because menu flashes when breakpoint changes for some reason!
@media(max-width: ${({breakpoint}) => (breakpoint - 1)}px) {
position: absolute;
visibility: hidden;
}
@media(min-width: ${({breakpoint}) => breakpoint}px) {
display: grid;
${setFromProps('justifyContent')};
justify-items: stretch;
grid-template-columns: repeat(${({columns}) => columns}, ${({columnWidth}) => columnWidth}px);
grid-template-rows: ${({rowHeight}) => rowHeight}px;
position: relative;
${setFromProps('background')};
${setFromProps('color')};
${setFromProps('fontFamily')};
${setFromProps('fontSize')}px;
}
`;
const GridItemLink = styled.a`
grid-column: ${({index}) => index + 1} / span 1;
display: flex;
justify-content: center;
align-items: center;
&:hover {
opacity: 0.5;
}
${setFromProps('color')};
&:visited {
${setFromProps('color')};
}
`;
const GridItem = styled.div`
grid-column: ${({index}) => index + 1} / span 1;
display: flex;
justify-content: center;
align-items: center;
&:hover {
opacity: 0.5;
cursor: default;
}
`;
const ContentRow = styled.div`
grid-column: 1 / span ${({columns}) => columns};
grid-row: 2 / span 1;
position: relative;
height: 0;
`;
const Move = (fromData, toData) => keyframes`
from {
left: ${fromData.left}px;
width: ${fromData.width}px;
height: ${fromData.height}px;
}
to {
left: ${toData.left}px;
width: ${toData.width}px;
height: ${toData.height}px;
}
`;
const FadeIn = keyframes`
from {
opacity: 0;
transform: perspective(${perspective}px) rotateX(-60deg);
transform-origin: top center;
}
to {
opacity: 1;
transform: perspective(${perspective}px) rotateX(0deg);
transform-origin: top center;
}
`;
const FadeOut = keyframes`
from {
opacity: 1;
transform: perspective(${perspective}px) rotateX(0deg);
transform-origin: top center;
}
to {
opacity: 0;
transform: perspective(${perspective}px) rotateX(-60deg);
transform-origin: top center;
visibility: hidden;
}
`;
const MovingDiv = styled.div`
opacity: 1;
${setFromProps('color')};
${setFromProps('background')};
position: absolute;
top: ${({top}) => top}px;
left: ${({fromData}) => fromData ? fromData.left : 0}px;
width: ${({fromData}) => fromData ? fromData.width : 0}px;
height: ${({fromData}) => fromData ? fromData.height : 0}px;
display: ${({display}) => display};
border-radius: 4px;
box-shadow: 0 8px 28px 1px rgba(138,126,138,0.67); // Ripped from: https://www.cssmatic.com/box-shadow
animation: ${({fadeOut, display, fromData, toData}) => {
if (fadeOut) return FadeOut;
if (display === 'block') {
if (fromData.left === toData.left) return FadeIn;
if (fromData) return Move(fromData, toData);
}
return ''; // display: none; don't animate
}}
// fade out and in slower than moving sideways
${({fadeOut, display, fromData, toData}) => {
if (fadeOut) return `${fadeOutSeconds}s`;
if (display === 'block') {
if (fromData.left === toData.left) return `${fadeInSeconds}s`; // fade in
if (fromData) return `${moveSeconds}s`; // move
}
return '0s'; // display: none; don't animate
}}
forwards ease;
`;
const FadeInArrow = keyframes`
from {
opacity: 0;
}
to {
opacity: 1;
}
`;
const FadeOutArrow = keyframes`
from {
opacity: 1;
}
to {
opacity: 0;
}
`;
const calculateArrowMarginLeft = (data, leftOffset, rightOffset) => css`
margin-left: ${
data ? data.left + (data.width / 2) - leftOffset + rightOffset - arrowHeight
- (leftOffset > 0 || rightOffset > 0 ? OffScreenPadding : 0)
: 0
}px;
`;
const MoveArrow = (fromData, toData, leftOffset, rightOffset) => keyframes`
from {
${calculateArrowMarginLeft(fromData, leftOffset, rightOffset)}
}
to {
${calculateArrowMarginLeft(toData, leftOffset, rightOffset)}
}
`;
const Arrow = styled.div`
top: -${({top}) => (arrowHeight - top)}px;
z-index: 1;
position: absolute;
${({toData, leftOffset, rightOffset}) => calculateArrowMarginLeft(toData, leftOffset, rightOffset)}
display: ${({display, toData}) => {
if (toData && toData.width === 0 && toData.height === 0) {
return 'none';
}
return display;
}};
width: 0;
height: 0;
border-left: ${arrowHeight}px solid transparent;
border-right: ${arrowHeight}px solid transparent;
border-bottom: ${arrowHeight}px solid ${({background}) => background};
animation: ${({fadeOut, display, fromData, toData, leftOffset, rightOffset}) => {
if (fadeOut) return FadeOutArrow;
if (display === 'block') {
if (fromData.left === toData.left) return FadeInArrow;
if (fromData) return MoveArrow(fromData, toData, leftOffset, rightOffset);
}
return ''; // display: none; don't animate
}}
// fade out and in slower than moving sideways
${({fadeOut, display, fromData, toData}) => {
if (fadeOut) return `${fadeOutSeconds}s`;
if (display === 'block') {
if (fromData.left === toData.left) return `${fadeInSeconds}s`; // fade in
if (fromData) return `${moveArrowSeconds}s`; // move
}
return '0s'; // display: none; don't animate
}}
forwards ease;
`;
const FadeInContent = keyframes`
from {
opacity: 0;
}
to {
opacity: 1;
}
`;
const FadeOutContent = keyframes`
from {
opacity: 1;
}
to {
opacity: 0;
visibility: hidden;
}
`;
const ContentGroupContainer = styled.div`
position: absolute;
margin-top: 0;
margin-bottom: 0;
width: 100%;
height: 100%;
opacity: ${({show}) => show ? 1 : 0};
z-index: ${({show}) => show ? 1 : 0};
pointer-events: ${({show}) => show ? 'auto' : 'none'}; // disregard mouse event if content group is inactive
animation: ${({show, fadeOut}) => {
if (show) return FadeInContent;
if (fadeOut) return FadeOutContent;
return ''; // cold start and everything else just show without animation
}}
${({show}) => show ? `${fadeInContentSeconds}` : `${fadeOutContentSeconds}`}s
forwards;
`;
export const ContentGroup = ({title, width, height, background}) => {
return (
<>
{title}
{width}x{height}
{background}
</>
);
};
export default class SiteNav extends Component {
state = {display: 'none', fadeOut: false, fromData: null, toData: null, leftOffset: 0, rightOffset: 0};
static defaultProps = {
align: defaultRootAlign,
columnWidth: defaultColumnWidth,
rowHeight: defaultRowHeight,
background: defaultBackground,
contentBackground: defaultContentBackground,
contentColor: defaultContentColor,
contentTop: defaultContentTop,
breakpoint: defaultBreakpoint,
color: defaultColor,
debug: false,
};
/**
* Injects index and left properties into MenuData
*/
memoizeMenuData = memoize((columnWidth, children) => React.Children.map(children, (child, i) => {
// if width and height are not specified, that means we don't want to render the content group i.e. we only
// want to render root item
const {width, height} = child.props;
let sanitisedWidth, sanitisedHeight;
if (!width && !height) {
sanitisedWidth = 0;
sanitisedHeight = 0;
} else {
// if width or height is not specified, add defaults
sanitisedWidth = width || defaultContentWidth;
sanitisedHeight = height || defaultContentHeight;
}
return {
...child.props, // order is important here! spread child.props after height, followed by width.
height: sanitisedHeight,
width: sanitisedWidth,
index: i,
left: (((i + 1) * columnWidth) - (columnWidth / 2)) - (sanitisedWidth / 2),
};
}));
memoizeGridItems = memoize((children, color) => React.Children.map(children, (child, i) => {
const {title, rootUrl} = child.props;
if (rootUrl) {
return (
<GridItemLink
href={rootUrl}
key={`menu-title-${i}`}
index={i}
onMouseEnter={(e) => this.onMouseEnter(e.target, i)}
color={color}
>
{title}
</GridItemLink>
);
}
return (
<GridItem
key={`menu-title-${i}`}
index={i}
onMouseEnter={(e) => this.onMouseEnter(e.target, i)}
color={color}
>
{title}
</GridItem>
);
}
));
memoizeContent = memoize((children, fromData, toData) => React.Children.map(children, (child, i) => (
<ContentGroupContainer
key={`content-group-${i}`}
show={toData && toData.index === i}
fadeOut={fromData && fromData.index === i}
>
{child.props.children}
</ContentGroupContainer>
)));
memoizeColumns = memoize(children => React.Children.count(children));
memoizeAlign = memoize(align => {
switch (align) {
case 'left':
return 'start';
case 'right':
return 'end';
default:
return 'center';
}
});
close = () => {
if (this.props.debug) return;
this.setState((prevState) => ({fadeOut: true, fromData: prevState.toData}));
};
onMouseEnter = (target, menuDataIndex) => {
this.setState((prevState) => {
const fadeOut = false;
const display = 'block';
const toDataOriginal = this.memoizeMenuData(this.props.columnWidth, this.props.children)[menuDataIndex];
const toData = {...toDataOriginal};
let leftOffset = 0;
let rightOffset = 0;
if (target) { // off screen detection
// target is rootGridItem
const {left, width} = target.getBoundingClientRect();
const siteNavWidth = target.parentNode.clientWidth;
leftOffset = (toData.width / 2) - (left + (width / 2));
rightOffset = (toData.width / 2) - (siteNavWidth - (left + (width / 2)));
if (leftOffset > 0) {
// if off screen, toData.left needs to be moved to be on-screen!
toData.left += leftOffset + OffScreenPadding;
} else {
leftOffset = 0;
}
if (rightOffset > 0) {
toData.left -= rightOffset - OffScreenPadding;
} else {
rightOffset = 0;
}
let fromData;
if (prevState.fadeOut || !prevState.toData) {
// on cold start, pop up right from the current item
fromData = toData;
} else {
// on warm start, start animation from the previous item
fromData = prevState.toData;
}
return {
display,
fadeOut,
fromData,
toData,
leftOffset,
rightOffset,
};
}
});
};
onMouseLeave = () => this.close();
onClickMovingDiv = () => this.close();
render() {
const {
columnWidth, rowHeight, background, contentBackground, contentColor, contentTop,
children, align, fontSize, fontFamily, color, breakpoint
} = this.props;
const {fromData, toData, display, fadeOut, leftOffset, rightOffset} = this.state;
const columns = this.memoizeColumns(children);
const rootGridItems = this.memoizeGridItems(children, color);
const content = this.memoizeContent(children, fromData, toData);
const justifyContent = this.memoizeAlign(align);
const contentBackgroundSanitised = (toData && toData.background) || contentBackground;
return (
<nav>
<GridContainer
background={background}
columnWidth={columnWidth}
rowHeight={rowHeight}
justifyContent={justifyContent}
fontSize={fontSize}
fontFamily={fontFamily}
color={color}
breakpoint={breakpoint}
/* Below are not configurable */
onMouseLeave={this.onMouseLeave}
columns={columns}
>
{rootGridItems}
<ContentRow columns={columns}>
<Arrow
display={display}
fadeOut={fadeOut}
fromData={fromData}
toData={toData}
top={contentTop}
onClick={this.onClickMovingDiv}
background={contentBackgroundSanitised}
leftOffset={leftOffset}
rightOffset={rightOffset}
/>
<MovingDiv
display={display}
fadeOut={fadeOut}
fromData={fromData}
toData={toData}
color={contentColor}
top={contentTop}
onClick={this.onClickMovingDiv}
background={contentBackgroundSanitised}
>
{content}
</MovingDiv>
</ContentRow>
</GridContainer>
</nav>
);
}
}
================================================
FILE: test/setup.js
================================================
// TODO:
// import td from 'testdouble';
//
// global.td = td;
gitextract_u844912z/
├── .babelrc
├── .eslintrc
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── examples/
│ ├── advanced/
│ │ ├── .babelrc
│ │ ├── .eslintrc
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── now/
│ │ │ ├── index.html
│ │ │ └── now.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── client/
│ │ │ │ └── index.js
│ │ │ ├── server/
│ │ │ │ ├── index.js
│ │ │ │ └── server.js
│ │ │ └── universal/
│ │ │ ├── app/
│ │ │ │ ├── app.js
│ │ │ │ ├── company.js
│ │ │ │ ├── developers.js
│ │ │ │ ├── pricing.js
│ │ │ │ └── products.js
│ │ │ ├── contact.js
│ │ │ └── home.js
│ │ ├── webpack.config.client.js
│ │ ├── webpack.config.server.js
│ │ └── webpack.prod.config.js
│ ├── basic/
│ │ ├── .babelrc
│ │ ├── .eslintrc
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── client/
│ │ │ │ └── index.js
│ │ │ ├── server/
│ │ │ │ ├── index.js
│ │ │ │ └── server.js
│ │ │ └── universal/
│ │ │ ├── app.css
│ │ │ ├── app.js
│ │ │ ├── home.js
│ │ │ ├── spaLink1.js
│ │ │ └── spaLink2.js
│ │ ├── webpack.config.client.js
│ │ └── webpack.config.server.js
│ └── cra-with-nav/
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── public/
│ │ ├── index.html
│ │ └── manifest.json
│ └── src/
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ └── registerServiceWorker.js
├── jest.config.js
├── lib/
│ └── index.js
├── package.json
├── sketch/
│ ├── logo-sketch.sketch
│ └── react-site-nav-logo.sketch
├── src/
│ └── index.js
└── test/
└── setup.js
SYMBOL INDEX (28 symbols across 8 files)
FILE: examples/advanced/src/server/server.js
constant PORT (line 8) | const PORT = 3000;
FILE: examples/advanced/src/universal/app/app.js
class App (line 44) | class App extends Component {
method render (line 45) | render() {
FILE: examples/advanced/src/universal/home.js
class Home (line 14) | class Home extends Component {
method render (line 15) | render() {
FILE: examples/basic/src/server/server.js
constant PORT (line 8) | const PORT = 3000;
FILE: examples/basic/src/universal/home.js
class Home (line 3) | class Home extends Component {
method render (line 4) | render() {
FILE: examples/cra-with-nav/src/registerServiceWorker.js
function register (line 21) | function register() {
function registerValidSW (line 55) | function registerValidSW(swUrl) {
function checkValidServiceWorker (line 84) | function checkValidServiceWorker(swUrl) {
function unregister (line 111) | function unregister() {
FILE: lib/index.js
function _interopRequireDefault (line 16) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
function _interopRequireWildcard (line 18) | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { ret...
function _typeof (line 20) | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbo...
function _objectSpread (line 22) | function _objectSpread(target) { for (var i = 1; i < arguments.length; i...
function _classCallCheck (line 24) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
function _defineProperties (line 26) | function _defineProperties(target, props) { for (var i = 0; i < props.le...
function _createClass (line 28) | function _createClass(Constructor, protoProps, staticProps) { if (protoP...
function _possibleConstructorReturn (line 30) | function _possibleConstructorReturn(self, call) { if (call && (_typeof(c...
function _getPrototypeOf (line 32) | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? ...
function _inherits (line 34) | function _inherits(subClass, superClass) { if (typeof superClass !== "fu...
function _setPrototypeOf (line 36) | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf...
function _assertThisInitialized (line 38) | function _assertThisInitialized(self) { if (self === void 0) { throw new...
function _defineProperty (line 40) | function _defineProperty(obj, key, value) { if (key in obj) { Object.def...
function SiteNav (line 273) | function SiteNav() {
FILE: src/index.js
class SiteNav (line 272) | class SiteNav extends Component {
method render (line 420) | render() {
Condensed preview — 59 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (98K chars).
[
{
"path": ".babelrc",
"chars": 284,
"preview": "{\n \"presets\": [\n \"@babel/preset-env\",\n \"@babel/preset-react\",\n ],\n \"plugins\": [\n \"@babel/plugin-proposal-cla"
},
{
"path": ".eslintrc",
"chars": 1191,
"preview": "{\n \"parser\": \"babel-eslint\",\n \"parserOptions\": {\n \"allowImportExportEverywhere\": true\n },\n \"extends\": [\n \"airb"
},
{
"path": ".gitignore",
"chars": 598,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscov"
},
{
"path": ".npmignore",
"chars": 8,
"preview": "*\n!lib/*"
},
{
"path": "LICENSE",
"chars": 1073,
"preview": "MIT License\n\nCopyright (c) 2018 Yusinto Ngadiman\n\nPermission is hereby granted, free of charge, to any person obtaining "
},
{
"path": "README.md",
"chars": 4741,
"preview": "<p align=\"center\">\n <img src=\"logo.jpg\" width=\"390\" />\n</p>\n\n[.\n\nyarn && yarn start\n"
},
{
"path": "examples/advanced/now/index.html",
"chars": 296,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-"
},
{
"path": "examples/advanced/now/now.json",
"chars": 77,
"preview": "{\n \"name\": \"react-site-nav\",\n \"alias\": \"react-site-nav\",\n \"public\": true\n}"
},
{
"path": "examples/advanced/package.json",
"chars": 2052,
"preview": "{\n \"name\": \"react-site-menu-example\",\n \"version\": \"1.1.0\",\n \"description\": \"Demo of react-site-menu a kickass navigat"
},
{
"path": "examples/advanced/src/client/index.js",
"chars": 250,
"preview": "import React from 'react';\nimport {hydrate} from 'react-dom';\nimport {BrowserRouter} from 'react-router-dom';\nimport App"
},
{
"path": "examples/advanced/src/server/index.js",
"chars": 173,
"preview": "const UniversalHotReload = require('universal-hot-reload').default;\nUniversalHotReload(require('../../webpack.config.ser"
},
{
"path": "examples/advanced/src/server/server.js",
"chars": 1556,
"preview": "import Express from 'express';\nimport React from 'react';\nimport {renderToString} from 'react-dom/server';\nimport {Serve"
},
{
"path": "examples/advanced/src/universal/app/app.js",
"chars": 3420,
"preview": "import React, {Component} from 'react';\nimport {Switch, Link, Route, Redirect} from 'react-router-dom';\nimport styled, {"
},
{
"path": "examples/advanced/src/universal/app/company.js",
"chars": 1448,
"preview": "import React from 'react';\nimport styled from 'styled-components';\nimport aboutMeIcon from '../../../assets/about-me.png"
},
{
"path": "examples/advanced/src/universal/app/developers.js",
"chars": 2707,
"preview": "import React from 'react';\nimport styled from 'styled-components';\nimport documentIcon from '../../../assets/documentati"
},
{
"path": "examples/advanced/src/universal/app/pricing.js",
"chars": 1194,
"preview": "import React from 'react';\nimport styled from 'styled-components';\nimport smiley from '../../../assets/smiley.png';\n\ncon"
},
{
"path": "examples/advanced/src/universal/app/products.js",
"chars": 2128,
"preview": "import React from 'react';\nimport styled from 'styled-components';\nimport {Link} from 'react-router-dom';\nimport payment"
},
{
"path": "examples/advanced/src/universal/contact.js",
"chars": 654,
"preview": "import React, {Component, Timeout} from 'react';\nimport styled from 'styled-components';\n\nconst RootDiv = styled.div`\n "
},
{
"path": "examples/advanced/src/universal/home.js",
"chars": 440,
"preview": "import React, {Component} from 'react';\nimport styled from 'styled-components';\n\nconst RootDiv = styled.div`\n margin-to"
},
{
"path": "examples/advanced/webpack.config.client.js",
"chars": 794,
"preview": "const path = require('path');\n\nconst WebpackServeUrl = 'http://localhost:3002';\n\nmodule.exports = {\n mode: 'development"
},
{
"path": "examples/advanced/webpack.config.server.js",
"chars": 1293,
"preview": "const path = require('path');\nconst nodeExternals = require('webpack-node-externals');\n\nmodule.exports = {\n mode: 'deve"
},
{
"path": "examples/advanced/webpack.prod.config.js",
"chars": 683,
"preview": "const path = require('path');\n\nmodule.exports = {\n mode: 'production',\n devtool: 'source-map',\n entry: ['@babel/polyf"
},
{
"path": "examples/basic/.babelrc",
"chars": 283,
"preview": "{\n \"presets\": [\n \"@babel/preset-env\",\n \"@babel/preset-react\"\n ],\n \"plugins\": [\n \"@babel/plugin-proposal-clas"
},
{
"path": "examples/basic/.eslintrc",
"chars": 1617,
"preview": "{\n \"parser\": \"babel-eslint\",\n \"parserOptions\": {\n \"allowImportExportEverywhere\": true\n },\n \"extends\": [\n \"airb"
},
{
"path": "examples/basic/.gitignore",
"chars": 50,
"preview": "node_modules\n.idea\nnpm-debug.log\ndist\n.eslintcache"
},
{
"path": "examples/basic/README.md",
"chars": 95,
"preview": "# react-site-nav basic example\n\nA simple spa demonstrating react-site-nav.\n\nyarn && yarn start\n"
},
{
"path": "examples/basic/package.json",
"chars": 1949,
"preview": "{\n \"name\": \"react-site-menu-example\",\n \"version\": \"1.1.0\",\n \"description\": \"Demo of react-site-menu a kickass navigat"
},
{
"path": "examples/basic/src/client/index.js",
"chars": 246,
"preview": "import React from 'react';\nimport {hydrate} from 'react-dom';\nimport {BrowserRouter} from 'react-router-dom';\nimport App"
},
{
"path": "examples/basic/src/server/index.js",
"chars": 173,
"preview": "const UniversalHotReload = require('universal-hot-reload').default;\nUniversalHotReload(require('../../webpack.config.ser"
},
{
"path": "examples/basic/src/server/server.js",
"chars": 1361,
"preview": "import Express from 'express';\nimport React from 'react';\nimport {renderToString} from 'react-dom/server';\nimport {Serve"
},
{
"path": "examples/basic/src/universal/app.css",
"chars": 392,
"preview": "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: #24b4"
},
{
"path": "examples/basic/src/universal/app.js",
"chars": 1792,
"preview": "import React, {Component} from 'react';\nimport {Switch, Link, Route, Redirect} from 'react-router-dom';\nimport Home from"
},
{
"path": "examples/basic/src/universal/home.js",
"chars": 352,
"preview": "import React, {Component} from 'react';\n\nexport default class Home extends Component {\n render() {\n return (\n <"
},
{
"path": "examples/basic/src/universal/spaLink1.js",
"chars": 767,
"preview": "import React, {Component, Timeout} from 'react';\n\nexport default props =>\n <div style={{marginTop: '30px', marginLeft: "
},
{
"path": "examples/basic/src/universal/spaLink2.js",
"chars": 342,
"preview": "import React, {Component, Timeout} from 'react';\n\nexport default props =>\n <div style={{marginTop: '30px', marginLeft: "
},
{
"path": "examples/basic/webpack.config.client.js",
"chars": 1130,
"preview": "const path = require('path');\n\nconst WebpackServeUrl = 'http://localhost:3002';\n\nmodule.exports = {\n mode: 'development"
},
{
"path": "examples/basic/webpack.config.server.js",
"chars": 1520,
"preview": "const path = require('path');\nconst nodeExternals = require('webpack-node-externals');\n\nmodule.exports = {\n mode: 'deve"
},
{
"path": "examples/cra-with-nav/.gitignore",
"chars": 285,
"preview": "# See https://help.github.com/ignore-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n\n# testing\n/cov"
},
{
"path": "examples/cra-with-nav/README.md",
"chars": 93,
"preview": "## Create react app with react-site-nav\n\nLive demo [here](https://build-licattzisr.now.sh/).\n"
},
{
"path": "examples/cra-with-nav/package.json",
"chars": 466,
"preview": "{\n \"name\": \"cra-with-nav\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"dependencies\": {\n \"react\": \"^16.5.0\",\n \"re"
},
{
"path": "examples/cra-with-nav/public/index.html",
"chars": 1590,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-wid"
},
{
"path": "examples/cra-with-nav/public/manifest.json",
"chars": 317,
"preview": "{\n \"short_name\": \"React App\",\n \"name\": \"Create React App Sample\",\n \"icons\": [\n {\n \"src\": \"favicon.ico\",\n "
},
{
"path": "examples/cra-with-nav/src/App.css",
"chars": 850,
"preview": "ul {\n list-style-type: none;\n margin: 0;\n padding: 0;\n}\n\nli {\n display: flex;\n justify-content: center;\n "
},
{
"path": "examples/cra-with-nav/src/App.js",
"chars": 2035,
"preview": "import React from 'react';\nimport logo from './logo.svg';\nimport reactMenuImage from './react-logo.png';\nimport aboutMeI"
},
{
"path": "examples/cra-with-nav/src/App.test.js",
"chars": 248,
"preview": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport App from './App';\n\nit('renders without crashing', ()"
},
{
"path": "examples/cra-with-nav/src/index.css",
"chars": 63,
"preview": "body {\n margin: 0;\n padding: 0;\n font-family: sans-serif;\n}\n"
},
{
"path": "examples/cra-with-nav/src/index.js",
"chars": 254,
"preview": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport regis"
},
{
"path": "examples/cra-with-nav/src/registerServiceWorker.js",
"chars": 4384,
"preview": "// In production, we register a service worker to serve assets from local cache.\n\n// This lets the app load faster on su"
},
{
"path": "jest.config.js",
"chars": 56,
"preview": "module.exports = {\n setupFiles: ['./test/setup.js'],\n};"
},
{
"path": "lib/index.js",
"chars": 22300,
"preview": "\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = exports.ContentGroup ="
},
{
"path": "package.json",
"chars": 1642,
"preview": "{\n \"name\": \"react-site-nav\",\n \"version\": \"0.2.9\",\n \"description\": \"A kick ass site menu powered by styled components "
},
{
"path": "src/index.js",
"chars": 13688,
"preview": "import React, {Component} from 'react';\nimport styled, {keyframes, css} from 'styled-components';\nimport memoize from 'm"
},
{
"path": "test/setup.js",
"chars": 63,
"preview": "// TODO:\n// import td from 'testdouble';\n//\n// global.td = td;\n"
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the yusinto/react-site-nav GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 59 files (87.4 KB), approximately 25.4k tokens, and a symbol index with 28 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.