├── .dockerignore
├── .editorconfig
├── .eslintrc.json
├── .gitignore
├── .yarnrc
├── Dockerfile
├── README.md
├── components
├── background-slider.js
├── button.js
├── container.js
├── css-config.js
├── docs
│ ├── docs.mdx
│ ├── documentation.js
│ ├── head.js
│ ├── heading.js
│ ├── sidebar.js
│ └── text
│ │ ├── code.js
│ │ ├── headings.js
│ │ ├── link.js
│ │ └── quotes.js
├── fade.js
├── footer.js
├── header.js
├── hoc
│ └── pure.js
├── home
│ ├── browser.js
│ ├── campaign.js
│ ├── company-slider.js
│ ├── demo.js
│ ├── demos
│ │ ├── code.js
│ │ ├── frame.js
│ │ ├── more.js
│ │ ├── stateful-counter.js
│ │ ├── stateful-input.js
│ │ └── window-size.js
│ ├── editor.js
│ ├── faq.js
│ ├── features.js
│ ├── intro.js
│ ├── learn.js
│ └── tab-button.js
├── icons
│ ├── arrow-next.js
│ ├── arrow-previous.js
│ ├── arrow-right-long.js
│ ├── arrow-right.js
│ ├── arrow-up.js
│ ├── companies
│ │ ├── auth0.js
│ │ ├── binance.js
│ │ ├── coinbase.js
│ │ ├── docker.js
│ │ ├── eaze.js
│ │ ├── github.js
│ │ ├── hulu.js
│ │ ├── invision.js
│ │ ├── jet.js
│ │ ├── magic-leap.js
│ │ ├── mozilla-vr.js
│ │ ├── netflix.js
│ │ ├── opencollective.js
│ │ ├── pling.js
│ │ ├── scale.js
│ │ ├── starbucks.js
│ │ ├── tencent.js
│ │ ├── ticketmaster.js
│ │ └── trulia.js
│ ├── external-link.js
│ ├── github.js
│ ├── heart.js
│ ├── next-logo.js
│ ├── notification-indicator.js
│ ├── permalink.js
│ ├── rehooks-logo.js
│ ├── spectrum.js
│ ├── toggle.js
│ ├── zeit-logo-outline.js
│ ├── zeit-logo.js
│ └── zeit-white-full-logo.js
├── image.js
├── intersection-observer
│ ├── index.js
│ ├── intersection-observer.js
│ ├── manager.js
│ └── utils.js
├── logo.js
├── media-query.js
├── navbar.js
├── notification.js
├── page.js
├── popover.js
├── screen.js
├── section-header.js
├── social-meta.js
├── table.js
├── tabs.js
├── text
│ ├── blockquote.js
│ ├── caption.js
│ ├── code.js
│ ├── heading.js
│ ├── hr.js
│ ├── link.js
│ ├── list.js
│ ├── paragraph.js
│ └── terminal.js
└── word-slider.js
├── lib
├── analytics.js
├── polyfill.js
├── rehype-readme.js
└── router-events.js
├── next.config.js
├── now.json
├── package.json
├── pages
├── _document.js
├── docs
│ └── index.js
└── index.js
├── site-manifest.js
├── static
├── favicon
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── apple-touch-icon.png
│ ├── browserconfig.xml
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon.ico
│ ├── mstile-150x150.png
│ ├── safari-pinned-tab.svg
│ └── site.webmanifest
└── images
│ └── og
│ └── home.png
└── yarn.lock
/.dockerignore:
--------------------------------------------------------------------------------
1 | *
2 | !src
3 | !pages
4 | !yarn.lock
5 | !package.json
6 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [{package.json,*.yml}]
12 | indent_style = space
13 | indent_size = 2
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": "airbnb",
4 | "rules": {
5 | "react/react-in-jsx-scope": 0,
6 | "react/prop-types": 0,
7 | "react/jsx-filename-extension": 0,
8 | "jsx-a11y/anchor-is-valid": 0
9 | }
10 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .next
2 | node_modules
3 | .DS_Store
4 | .vscode
5 | out
6 | yarn-error.log
7 | bundles
8 |
--------------------------------------------------------------------------------
/.yarnrc:
--------------------------------------------------------------------------------
1 | save-prefix ""
2 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mhart/alpine-node:10
2 | # We store all our files in /usr/src to perform the build
3 | WORKDIR /usr/src
4 | # We first add only the files required for installing deps
5 | # If package.json or yarn.lock don't change, no need to re-install later
6 | COPY package.json yarn.lock ./
7 | # We install our deps
8 | RUN yarn
9 | # We copy all source files
10 | COPY . .
11 | # We run the build and expose as /public
12 | RUN yarn build && yarn export -o /public
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rehooks website
2 |
3 | The official website for [Rehooks](https://rehooks.com)
4 |
5 | ## Contribution
6 |
7 | If you need to make any changes to the website:
8 |
9 | 1. Clone this repo
10 | 2. Run `yarn` to install dependencies
11 | 3. Run `yarn dev` to start the server on `http://localhost:3000`
12 |
13 | ## Credits
14 |
15 | Big thank you to the [Next.js team](https://github.com/zeit/next-site) for the starting point for this website!
16 |
--------------------------------------------------------------------------------
/components/background-slider.js:
--------------------------------------------------------------------------------
1 | export default ({ duration, children }) => (
2 |
3 |
26 |
27 |
{children}
28 |
{children}
29 |
30 |
31 | );
32 |
--------------------------------------------------------------------------------
/components/button.js:
--------------------------------------------------------------------------------
1 | import Link from 'next/link'
2 | import classNames from 'classnames'
3 |
4 | import withPure from './hoc/pure'
5 |
6 | export default withPure(
7 | ({ children, invert, href, as, className, prefetch, ...props }) => {
8 | const a = (
9 |
14 | {children}
15 |
51 |
52 | )
53 |
54 | if (href) {
55 | return (
56 |
57 | {a}
58 |
59 | )
60 | }
61 | return a
62 | }
63 | )
64 |
--------------------------------------------------------------------------------
/components/container.js:
--------------------------------------------------------------------------------
1 | export default ({
2 | center,
3 | vCenter,
4 | dark,
5 | gray,
6 | wide,
7 | small,
8 | padding,
9 | overflow,
10 | minHeight,
11 | dotBackground,
12 | children,
13 | mobileStyle,
14 | ...props
15 | }) => (
16 |
17 |
59 | {children}
60 |
61 | )
62 |
--------------------------------------------------------------------------------
/components/css-config.js:
--------------------------------------------------------------------------------
1 | export const FONT_FAMILY_SANS =
2 | '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif'
3 |
4 | export const FONT_FAMILY_MONO =
5 | 'Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif'
6 |
7 | export const COLOR_ERROR = '#FF001F'
8 | export const COLOR_SUCCESS = '#067DF7'
9 |
10 | export const COLOR_CODE_LIGHT = '#000000'
11 |
--------------------------------------------------------------------------------
/components/docs/docs.mdx:
--------------------------------------------------------------------------------
1 | ## Getting Started
2 |
3 | ### Setup
4 |
5 | First, you need to install **React v16.8 or higher**:
6 |
7 | ```bash
8 | npm install --save react@^16.8.0 react-dom@^16.8.0
9 | ```
10 |
11 | Or with yarn:
12 |
13 | ```bash
14 | yarn add react@^16.8.0 react-dom@^16.8.0
15 | ```
16 |
17 | Now you can start writing `useState` and `useEffect` in your code. Here's a simple counter example:
18 |
19 | ```jsx
20 | import { useState } from 'react'
21 |
22 | function Example() {
23 | const [count, setCount] = useState(0)
24 |
25 | return (
26 |
27 |
You clicked {count} times
28 |
setCount(count + 1)}>
29 | Click me
30 |
31 |
32 | )
33 | }
34 | ```
35 |
36 | Alternatively, this is class-based equivalent:
37 |
38 | ```jsx
39 | class Example extends React.Component {
40 | constructor(props) {
41 | super(props)
42 | this.state = {
43 | count: 0
44 | }
45 | }
46 |
47 | onClick = () => {
48 | this.setState({
49 | count: this.state.count + 1
50 | })
51 | }
52 |
53 | render() {
54 | return (
55 |
56 |
You clicked {this.state.count} times
57 |
58 | Click me
59 |
60 |
61 | )
62 | }
63 | }
64 | ```
65 |
66 | ### Controlled Inputs
67 |
68 | First install `@rehooks/input-value`:
69 |
70 | ```
71 | yarn add @rehooks/input-value
72 | ```
73 |
74 | Then apply `useInputValue` in your component:
75 |
76 | ```jsx
77 | import useInputValue from '@rehooks/input-value';
78 |
79 | function MyComponent() {
80 | let name = useInputValue('Jamie');
81 | // name = { value: 'Jamie', onChange: [Function] }
82 | return ;
83 | }
84 | ```
85 |
86 | ## FAQ
87 |
88 | ### What are React Hooks?
89 |
90 | React Hooks allow you to use React concepts like state, context and methods without writing a class.
91 |
92 | ### What are Rehooks?
93 |
94 | Rehooks are packages that make it easy to use Hooks in your existing application.
95 |
96 | ## Contributing
97 |
98 | Please check out the [rehooks/ideas repo](https://github.com/rehooks/ideas) or if you already know a Hook you want to build, check out the [rehooks/template repo](https://github.com/rehooks/template).
99 |
100 | ## Edit this page
101 |
102 | You can find the repo for this website at [rehooks/website](http://github.com/rehooks/website).
--------------------------------------------------------------------------------
/components/docs/documentation.js:
--------------------------------------------------------------------------------
1 | import { Component } from 'react'
2 | import Router from 'next/router'
3 | import { format, parse } from 'url'
4 | import Head from './head'
5 | import Sidebar from './sidebar'
6 | import { H1, H2, H3, H4, H5 } from './text/headings'
7 | import { Blockquote } from './text/quotes'
8 | import { InlineCode, Code } from './text/code'
9 | import { GenericLink } from './text/link'
10 | import Heading from './heading'
11 |
12 | import { MediaQueryConsumer } from '../media-query'
13 |
14 | if (typeof window !== 'undefined') {
15 | require('intersection-observer')
16 | }
17 |
18 | function changeHash(hash) {
19 | const { pathname, query } = Router
20 |
21 | const parsedUrl = parse(location.href)
22 | parsedUrl.hash = hash
23 |
24 | Router.router.changeState(
25 | 'replaceState',
26 | format({ pathname, query }),
27 | format(parsedUrl)
28 | )
29 | }
30 |
31 | export default class Documentation extends Component {
32 | constructor(props) {
33 | super(props)
34 | this.state = {
35 | currentSelection: null
36 | }
37 | this.contentNode = null
38 | this.observer = null
39 | this.preventScrollObserverUpdate = false
40 |
41 | this.updateSelected = this.updateSelected.bind(this)
42 | this.onHashChange = this.onHashChange.bind(this)
43 | }
44 |
45 | componentDidMount() {
46 | window.addEventListener('hashchange', this.onHashChange)
47 |
48 | const nodes = [...this.contentNode.querySelectorAll('h3 [id], h4 [id]')]
49 | const intersectingTargets = new Set()
50 |
51 | this.observer = new IntersectionObserver(entries => {
52 | for (const { isIntersecting, target } of entries) {
53 | if (isIntersecting) {
54 | intersectingTargets.add(target)
55 | } else {
56 | intersectingTargets.delete(target)
57 | }
58 | }
59 |
60 | if (this.preventScrollObserverUpdate) {
61 | this.preventScrollObserverUpdate = false
62 | return
63 | }
64 | if (!intersectingTargets.size) return
65 |
66 | let minIndex = Infinity
67 | let id = ''
68 |
69 | for (let target of intersectingTargets.values()) {
70 | let index = nodes.indexOf(target)
71 | if (index < minIndex) {
72 | minIndex = index
73 | id = target.id
74 | }
75 | }
76 |
77 | const hash = '#' + (id || '')
78 | this.updateSelected(hash)
79 | })
80 |
81 | for (const node of nodes) {
82 | this.observer.observe(node)
83 | }
84 |
85 | const { hash } = window.location
86 | this.setState({ currentSelection: hash })
87 | }
88 |
89 | componentWillUnmount() {
90 | window.removeEventListener('hashchange', this.onHashChange)
91 |
92 | this.observer.disconnect()
93 | this.observer = null
94 | }
95 |
96 | updateSelected = hash => {
97 | if (this.state.currentSelection !== hash) {
98 | this.setState({
99 | currentSelection: hash
100 | })
101 | }
102 | }
103 |
104 | onHashChange() {
105 | this.preventScrollObserverUpdate = true
106 | this.updateSelected(window.location.hash)
107 | }
108 |
109 | render() {
110 | const { headings } = this.props
111 |
112 | return (
113 |
114 | {({ isMobile, isTablet }) => {
115 | return (
116 | <>
117 |
123 |
124 |
125 |
131 |
132 |
133 |
(this.contentNode = ref)}
136 | >
137 | {this.props.children}
138 |
139 |
140 |
141 |
173 |
174 | >
175 | )
176 | }}
177 |
178 | )
179 | }
180 | }
181 |
182 | const DocH2 = ({ children, id }) => (
183 |
184 |
185 | {children}
186 |
187 |
192 |
193 | )
194 |
195 | const DocH3 = ({ children, id }) => (
196 |
197 |
198 | {children}
199 |
200 |
205 |
206 | )
207 |
208 | const DocH4 = ({ children, id }) => (
209 |
210 |
211 | {children}
212 |
213 |
214 | )
215 |
216 | const Details = ({ children }) => {
217 | return (
218 |
219 | {children}
220 |
226 |
227 | )
228 | }
229 |
230 | const Summary = ({ children }) => {
231 | return (
232 |
233 | {children}
234 |
245 |
246 | )
247 | }
248 |
249 | export const components = {
250 | h1: H1,
251 | h2: DocH2,
252 | h3: DocH3,
253 | h4: DocH4,
254 | h5: H5,
255 | blockquote: Blockquote,
256 | code: Code,
257 | inlineCode: InlineCode,
258 | a: GenericLink,
259 | details: Details,
260 | summary: Summary
261 | }
262 |
--------------------------------------------------------------------------------
/components/docs/head.js:
--------------------------------------------------------------------------------
1 | import Head from 'next/head'
2 |
3 | export default ({ children, ...props }) => (
4 |
5 | {props.title}
6 |
10 |
11 | {props.description ? (
12 |
13 | ) : null}
14 | {props.ogDescription ? (
15 |
16 | ) : (
17 |
18 | )}
19 | {props.video
20 | ? [
21 | ,
22 | ,
23 |
24 | ]
25 | : null}
26 |
27 | {children}
28 |
29 | )
30 |
--------------------------------------------------------------------------------
/components/docs/heading.js:
--------------------------------------------------------------------------------
1 | import GithubSlugger from 'github-slugger';
2 |
3 | const PermalinkIcon = () => (
4 |
5 |
6 | permalink
7 |
8 |
15 |
23 |
24 |
25 |
26 | );
27 |
28 | class Heading extends React.Component {
29 | render() {
30 | const { component, className, children, ...rest } = this.props;
31 | return React.cloneElement(
32 | component,
33 | {
34 | className: [className, component.props.className || ''].join(' '),
35 | ...rest
36 | },
37 | children
38 | );
39 | }
40 | }
41 |
42 | export default props => {
43 | const { offsetTop } = props;
44 | const component = props.children;
45 | const children = component.props.children || '';
46 |
47 | let id = props.id;
48 | let text = children;
49 |
50 | const slugger = new GithubSlugger();
51 |
52 | if (null == id) {
53 | // If there are sub components, convert them to text
54 | if (Array.isArray(children)) {
55 | text = children
56 | .map(child => {
57 | return typeof child === 'object' ? child.props.children : child;
58 | })
59 | .join('');
60 | }
61 |
62 | id = slugger.slug(text);
63 | }
64 |
65 | const targetStyle =
66 | null != offsetTop
67 | ? { marginTop: -offsetTop + 'px', paddingTop: offsetTop + 'px' }
68 | : null;
69 | return (
70 |
75 |
76 | {children}
77 |
78 |
79 |
80 |
149 |
150 | );
151 | };
152 |
--------------------------------------------------------------------------------
/components/docs/text/code.js:
--------------------------------------------------------------------------------
1 | export const Code = ({ children, syntax }) => (
2 |
3 | {children}
4 |
21 |
22 | );
23 |
24 | export const InlineCode = ({ children, wrap = false }) => (
25 |
26 | {children}
27 |
57 |
58 | );
59 |
--------------------------------------------------------------------------------
/components/docs/text/headings.js:
--------------------------------------------------------------------------------
1 | const H1 = ({ children, ...props }) => (
2 |
3 | {children}
4 |
5 |
10 |
11 | );
12 |
13 | const H2 = ({ children, ...props }) => (
14 |
15 | {children}
16 |
17 |
21 |
22 | );
23 |
24 | const H3 = ({ children, ...props }) => (
25 |
26 | {children}
27 |
28 |
32 |
33 | );
34 |
35 | const H4 = ({ children, ...props }) => (
36 |
37 | {children}
38 |
39 |
43 |
44 | );
45 |
46 | const H5 = ({ children, ...props }) => (
47 |
48 | {children}
49 |
50 |
55 |
56 | );
57 |
58 | export { H1, H2, H3, H4, H5 };
59 |
--------------------------------------------------------------------------------
/components/docs/text/link.js:
--------------------------------------------------------------------------------
1 | import NativeLink from 'next/link';
2 |
3 | export const GenericLink = props => {
4 | if (props.href.startsWith('/') && !props.href.startsWith('/docs')) {
5 | return ;
6 | }
7 |
8 | if (props.href.includes('@') || props.href.startsWith('#')) {
9 | return ;
10 | }
11 |
12 | return ;
13 | };
14 |
15 | export const InternalLink = ({ href, as, children, error = false }) => (
16 |
17 |
18 | {children}
19 |
20 |
30 |
31 |
32 | );
33 |
34 | export const AnchorLink = ({ href, onClick, children }) => (
35 |
36 | {children}
37 |
38 |
53 |
54 | );
55 |
56 | export const ExternalLink = ({ href, children }) => (
57 |
58 | {children}
59 |
60 |
68 |
69 | );
70 |
--------------------------------------------------------------------------------
/components/docs/text/quotes.js:
--------------------------------------------------------------------------------
1 | export const Blockquote = ({ children, ...props }) => (
2 |
3 | { children }
4 |
5 |
20 |
21 | )
22 |
23 |
--------------------------------------------------------------------------------
/components/fade.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import { Transition, animated, config as cfg } from 'react-spring';
3 |
4 | const wrap = (child, styles) => {
5 | styles = { willChange: Object.keys(styles).join(','), ...styles };
6 | if (!child || !animated[child.type]) {
7 | // Wrap components into animated divs
8 | return {child} ;
9 | }
10 | // Otherwise inject styles into existing component-props
11 | const Component = animated[child.type];
12 | const props = {
13 | ...child.props,
14 | style: {
15 | ...child.props.style,
16 | ...styles
17 | }
18 | };
19 | return ;
20 | };
21 |
22 | export default class extends PureComponent {
23 | render() {
24 | const {
25 | children,
26 | show = true,
27 | config = cfg.fast,
28 | from = { opacity: 0 },
29 | enter = { opacity: 1 },
30 | leave = { opacity: 0 },
31 | ...rest
32 | } = this.props;
33 | const result = styles => wrap(children, styles);
34 | return (
35 |
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/components/footer.js:
--------------------------------------------------------------------------------
1 | import Link from 'next/link'
2 |
3 | import Container from './container'
4 | import withPure from './hoc/pure'
5 |
6 | import { links } from '../site-manifest'
7 |
8 | export default withPure(() => (
9 |
10 |
11 |
12 |
67 |
68 |
69 |
70 |
71 | Docs
72 |
73 |
74 |
75 |
76 | FAQ
77 |
78 |
79 |
80 |
81 | Setup
82 |
83 |
84 |
85 |
86 | Examples
87 |
88 |
89 |
90 |
91 | Contributing
92 |
93 |
94 |
95 |
119 |
120 |
121 |
122 |
123 | ))
124 |
--------------------------------------------------------------------------------
/components/header.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react'
2 | import classNames from 'classnames'
3 |
4 | export default class extends PureComponent {
5 | state = {
6 | scrolled: false,
7 | fixed: false,
8 | active: false
9 | }
10 |
11 | onScroll = () => {
12 | const scroll = window.scrollY || document.body.scrollTop
13 | const scrolled = scroll > (this.props.distance || 0)
14 | const fixed = scroll >= (this.props.distance || 0)
15 | const active = scroll >= (this.props.active || 0)
16 |
17 | if (
18 | scrolled !== this.state.scrolled ||
19 | fixed !== this.state.fixed ||
20 | active !== this.state.active
21 | ) {
22 | this.setState({ scrolled, fixed, active })
23 | }
24 | }
25 |
26 | componentDidMount() {
27 | window.addEventListener('scroll', this.onScroll)
28 | this.onScroll()
29 | }
30 |
31 | componentWillUnmount() {
32 | window.removeEventListener('scroll', this.onScroll)
33 | }
34 |
35 | render() {
36 | const { scrolled, fixed, active } = this.state
37 | const {
38 | height,
39 | offset,
40 | shadow,
41 | zIndex,
42 | distance,
43 | background,
44 | defaultActive,
45 | children
46 | } = this.props
47 |
48 | return (
49 |
50 |
57 | {children}
58 |
59 |
96 |
97 | )
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/components/hoc/pure.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react'
2 |
3 | export default function (Comp) {
4 | return class extends PureComponent {
5 | render () {
6 | return
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/components/home/browser.js:
--------------------------------------------------------------------------------
1 | import { ellipsis } from 'polished'
2 |
3 | import Tabs from '../tabs'
4 |
5 | export default ({ data, children }) => (
6 |
7 |
64 |
{
65 | (onSelect, selectedTab, selectedIndex) => <>
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | {selectedTab}
74 |
75 |
76 |
77 | {(() => {
78 | let SelectedTab = data.browserMapping[selectedTab]
79 | return
80 | })()}
81 |
82 | >
83 | }
84 |
85 | )
--------------------------------------------------------------------------------
/components/home/campaign.js:
--------------------------------------------------------------------------------
1 | import WordSlider from '../word-slider'
2 |
3 | export default () => (
4 |
5 |
6 | Everything
7 | Device Orientation
8 | Online Status
9 | Component Resizing
10 | Document Visibility
11 | Input Values
12 | Network Status
13 | Window Resizing
14 |
15 |
24 |
25 | )
26 |
--------------------------------------------------------------------------------
/components/home/company-slider.js:
--------------------------------------------------------------------------------
1 | import BackgroundSlider from '../background-slider'
2 |
3 | import OpenCollective from '../icons/companies/opencollective'
4 | import Eaze from '../icons/companies/eaze'
5 | import MagicLeap from '../icons/companies/magic-leap'
6 | import Trulia from '../icons/companies/trulia'
7 | import MozillaVR from '../icons/companies/mozilla-vr'
8 | import Netflix from '../icons/companies/netflix'
9 | import GitHub from '../icons/companies/github'
10 | import Scale from '../icons/companies/scale'
11 | import Auth0 from '../icons/companies/auth0'
12 | import Ticketmaster from '../icons/companies/ticketmaster'
13 |
14 | import Tencent from '../icons/companies/tencent'
15 | import Jet from '../icons/companies/jet'
16 | import Coinbase from '../icons/companies/coinbase'
17 | import Docker from '../icons/companies/docker'
18 | import Invision from '../icons/companies/invision'
19 | import Binance from '../icons/companies/binance'
20 | import Hulu from '../icons/companies/hulu'
21 | import Pling from '../icons/companies/pling'
22 | import Starbucks from '../icons/companies/starbucks'
23 |
24 | export default () => (
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
78 |
79 | )
80 |
--------------------------------------------------------------------------------
/components/home/demo.js:
--------------------------------------------------------------------------------
1 | import Container from '../container'
2 | import Tabs from '../tabs'
3 | import Editor from './editor'
4 | import Browser from './browser'
5 | import { MediaQueryConsumer } from '../media-query'
6 |
7 | import TabButton from './tab-button'
8 |
9 | const DEMO_DATA = {
10 | 'Stateful Counter': require('./demos/stateful-counter').default,
11 | 'Stateful Input': require('./demos/stateful-input').default,
12 | 'Window Resizer': require('./demos/window-size').default,
13 | 'More...': require('./demos/more').default
14 | }
15 |
16 | export default () => {
17 | return (
18 |
19 | {({ isMobile, isTablet }) => (
20 |
21 |
22 |
23 | {(onSelect, selectedId, selectedIndex) => (
24 |
25 |
61 |
62 | {Object.keys(DEMO_DATA).map(id => (
63 | onSelect(id)}
70 | >
71 | {id}
72 |
73 | ))}
74 | {/*
75 |
81 | More...
82 |
83 | */}
84 |
85 |
86 | {isTablet &&
87 | (() => {
88 | let data = DEMO_DATA[selectedId]
89 | if (!data.type.length) {
90 | return data.getBody({ isTablet, isMobile }) || null
91 | }
92 |
93 | return (
94 |
95 |
96 | {(onSelect, _selectedId, selectedIndex) => {
97 | let content = null
98 | let data = DEMO_DATA[selectedId]
99 | if (_selectedId === data.tabs[0]) {
100 | content =
101 | data.type[0] === 'editor' ? (
102 |
103 | ) : (
104 |
105 | )
106 | } else {
107 | content =
108 | data.type[1] === 'editor' ? (
109 |
110 | ) : (
111 |
112 | )
113 | }
114 | return (
115 |
116 | {content}
117 |
118 | onSelect(data.tabs[0])}
124 | >
125 | {data.tabs[0]}
126 |
127 | onSelect(data.tabs[1])}
133 | >
134 | {data.tabs[1]}
135 |
136 |
137 | )
138 | }}
139 |
140 |
141 | )
142 | })()}
143 | {!isTablet &&
144 | (() => {
145 | let data = DEMO_DATA[selectedId]
146 | if (!data.type.length) {
147 | return data.getBody({}) || null
148 | }
149 |
150 | let content1 =
151 | data.type[0] === 'editor' ? (
152 |
153 | ) : (
154 |
155 | )
156 | let content2 =
157 | data.type[1] === 'editor' ? (
158 |
159 | ) : (
160 |
161 | )
162 |
163 | return (
164 | <>
165 |
{content1}
166 |
{content2}
167 | >
168 | )
169 | })()}
170 |
171 |
172 |
{DEMO_DATA[selectedId].note}
173 |
174 |
175 | )}
176 |
177 |
178 |
179 | )}
180 |
181 | )
182 | }
183 |
--------------------------------------------------------------------------------
/components/home/demos/code.js:
--------------------------------------------------------------------------------
1 | import Highlight from 'react-highlight/lib/optimized'
2 |
3 | export default ({ lang, children }) => (
4 |
5 |
28 | {lang === 'none' ? (
29 |
30 | {children}
31 |
32 | ) : (
33 |
37 | {children}
38 |
39 | )}
40 |
41 | )
42 |
--------------------------------------------------------------------------------
/components/home/demos/frame.js:
--------------------------------------------------------------------------------
1 | import Frame from 'react-frame-component'
2 |
3 | const makeLink = onSelect => ({ tab, children }) => (
4 | onSelect(tab)}>
5 | {children}
6 |
7 | )
8 |
9 | export default Comp => ({ onSelect }) => (
10 |
11 |
12 |
13 | )
14 |
--------------------------------------------------------------------------------
/components/home/demos/more.js:
--------------------------------------------------------------------------------
1 | import Link from 'next/link'
2 | import withPure from '../../hoc/pure'
3 |
4 | import ArrowRightLong from '../../icons/arrow-right-long'
5 |
6 | const EXAMPLES = [
7 | {
8 | name: 'window-size',
9 | description: 'Subscribe to window size',
10 | href: 'https://github.com/rehooks/window-size'
11 | },
12 | {
13 | name: 'input-value',
14 | description: 'Create input values and onChange',
15 | href: 'https://github.com/rehooks/input-value'
16 | },
17 | {
18 | name: 'component-size',
19 | description: 'Determine the size of a component',
20 | href: 'https://github.com/rehooks/component-size'
21 | },
22 | {
23 | name: 'device-orientation',
24 | description: 'Device Orientation API',
25 | href: 'https://github.com/rehooks/device-orientation'
26 | },
27 | {
28 | name: 'document-visibility',
29 | description: 'Monitor document visibility',
30 | href: 'https://github.com/rehooks/document-visibility'
31 | },
32 | {
33 | name: 'online-status',
34 | description: 'Subscribe to online/offline status',
35 | href: 'https://github.com/rehooks/online-status'
36 | },
37 | {
38 | name: 'window-scroll-position',
39 | description: 'Get X and Y scroll position',
40 | href: 'https://github.com/rehooks/window-scroll-position'
41 | }
42 | ]
43 |
44 | const ExampleCard = withPure(({ name, href, description }) => (
45 |
46 |
47 |
48 | {name}
49 | {description}
50 |
51 | Open this package{' '}
52 |
53 |
54 |
55 |
56 |
99 |
100 |
101 |
102 | ))
103 |
104 | export default {
105 | type: [],
106 | tabs: [],
107 | getBody: ({ isTablet, isMobile }) => (
108 |
109 | {(isMobile
110 | ? EXAMPLES.slice(0, 5)
111 | : isTablet
112 | ? EXAMPLES.slice(0, 8)
113 | : EXAMPLES
114 | ).map((example, index) => (
115 |
116 |
117 |
118 | ))}
119 |
134 |
158 |
159 | )
160 | }
161 |
--------------------------------------------------------------------------------
/components/home/demos/stateful-counter.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 | import withFrame from './frame'
3 | import Code from './code'
4 |
5 | const IndexFile = () => (
6 | {`import { useState } from 'react'
7 |
8 | function Example() {
9 | const [count, setCount] = useState(0)
10 |
11 | return (
12 |
13 |
You clicked {count} times
14 |
setCount(count + 1)}>
15 | Click me
16 |
17 |
18 | )
19 | }
20 | `}
21 | )
22 |
23 | const IndexPage = withFrame(() => {
24 | const [count, setCount] = useState(0)
25 |
26 | return (
27 |
28 |
You clicked {count} times
29 |
setCount(count + 1)}>Click me
30 |
31 | )
32 | })
33 |
34 | export default {
35 | type: ['editor', 'browser'],
36 | tabs: ['Code', 'Website'],
37 | editor1: {
38 | editorTabs: ['index.js'],
39 | editorMapping: {
40 | 'index.js': IndexFile
41 | }
42 | },
43 | browser2: {
44 | browserTabs: ['http://localhost:3000'],
45 | browserMapping: {
46 | 'http://localhost:3000': IndexPage
47 | }
48 | },
49 | note: (
50 | <>
51 |
52 | A Hook is a special function that lets you “hook into” React features.
53 | For example, useState
is a Hook that lets you add React
54 | state to function components.
55 |
56 | >
57 | )
58 | }
59 |
--------------------------------------------------------------------------------
/components/home/demos/stateful-input.js:
--------------------------------------------------------------------------------
1 | import useInputValue from '@rehooks/input-value'
2 | import withFrame from './frame'
3 | import Code from './code'
4 |
5 | const IndexFile = () => (
6 | {`import useInputValue from '@rehooks/input-value'
7 |
8 | function Example() {
9 | let name = useInputValue('Jamie')
10 | // name = { value: 'Jamie', onChange: [Function] }
11 |
12 | return (
13 |
14 |
15 |
Hi {name.value}!
16 |
17 | )
18 | }`}
19 | )
20 |
21 | const IndexPage = withFrame(() => {
22 | let name = useInputValue('Jamie')
23 |
24 | return (
25 | <>
26 |
27 | Hi {name.value}!
28 | >
29 | )
30 | })
31 |
32 | export default {
33 | type: ['editor', 'browser'],
34 | tabs: ['Code', 'Website'],
35 | editor1: {
36 | editorTabs: ['index.js'],
37 | editorMapping: {
38 | 'index.js': IndexFile
39 | }
40 | },
41 | browser2: {
42 | browserTabs: ['http://localhost:3000'],
43 | browserMapping: {
44 | 'http://localhost:3000': IndexPage
45 | }
46 | },
47 | note: (
48 | <>
49 |
50 | You can return an object from your Hook to simplify connecting your
51 | components to state without introducing classes. That's what Stateful
52 | Hooks are for!
53 |
54 | >
55 | )
56 | }
57 |
--------------------------------------------------------------------------------
/components/home/demos/window-size.js:
--------------------------------------------------------------------------------
1 | import useWindowSize from '@rehooks/window-size'
2 | import withFrame from './frame'
3 | import Code from './code'
4 |
5 | const IndexFile = () => (
6 | {`import useWindowSize from '@rehooks/window-size'
7 |
8 | function Example() {
9 | let windowSize = useWindowSize()
10 | // { innerWidth, innerHeight,
11 | // outerWidth, outerHeight }
12 |
13 | return (
14 |
15 | {JSON.stringify(windowSize, null, 2)}
16 |
17 | )
18 | }
19 | `}
20 | )
21 |
22 | const IndexPage = withFrame(() => {
23 | let windowSize = useWindowSize()
24 |
25 | return {JSON.stringify(windowSize, null, 2)}
26 | })
27 |
28 | export default {
29 | type: ['editor', 'browser'],
30 | tabs: ['Code', 'Website'],
31 | editor1: {
32 | editorTabs: ['index.js'],
33 | editorMapping: {
34 | 'index.js': IndexFile
35 | }
36 | },
37 | browser2: {
38 | browserTabs: ['http://localhost:3000'],
39 | browserMapping: {
40 | 'http://localhost:3000': IndexPage
41 | }
42 | },
43 | note: (
44 | <>
45 |
46 | Effect Hooks let you tell React that your component needs to do
47 | something after render. By default, they run on every render but that
48 | can be customized.
49 |
50 | >
51 | )
52 | }
53 |
--------------------------------------------------------------------------------
/components/home/editor.js:
--------------------------------------------------------------------------------
1 | import classNames from 'classnames'
2 |
3 | import Tabs from '../tabs'
4 |
5 | export default ({ data, children }) => {
6 | return
7 |
69 |
{
70 | (onSelect, selectedFile, selectedIndex) => <>
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | {data.editorTabs.map(file =>
79 | onSelect(file)}
83 | >
84 | {file}
85 | )}
86 |
87 |
88 |
89 | {(() => {
90 | let SelectedFile = data.editorMapping[selectedFile]
91 | return
92 | })()}
93 |
94 | >
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/components/home/faq.js:
--------------------------------------------------------------------------------
1 | import Container from '../container'
2 | import Button from '../button'
3 | import SectionHeader from '../section-header'
4 |
5 | export default () =>
6 |
7 | TODO
8 | ( or let’s just put the examples here {`https://zeit.co/blog/next2#more-examples`} ? )
9 |
10 |
--------------------------------------------------------------------------------
/components/home/features.js:
--------------------------------------------------------------------------------
1 | import Container from '../container'
2 | import Button from '../button'
3 | import SectionHeader from '../section-header'
4 |
5 | import { links } from '../../site-manifest'
6 |
7 | export default () => (
8 |
9 |
10 |
11 |
12 |
13 |
Easy-to-use
14 |
15 | Setting up React Hooks is as simple as upgrading to{' '}
16 | React v16.8 and using the new methods like{' '}
17 | useState
.
18 |
19 |
Try it out →
20 |
21 |
22 |
Backwards-compatible
23 |
24 | Hooks enable you to use existing React features like state, context
25 | and lifecycle. Without requiring classes.
26 |
27 |
28 | React Docs →
29 |
30 |
31 |
32 |
Growing Community
33 |
34 | While still relatively new, we believe Hooks will fundamentally
35 | change how most developers write React.
36 |
37 |
Join us on Spectrum →
38 |
39 |
40 |
58 |
59 |
60 | )
61 |
--------------------------------------------------------------------------------
/components/home/intro.js:
--------------------------------------------------------------------------------
1 | import classNames from 'classnames'
2 | import Link from 'next/link'
3 |
4 | import Logo from '../logo'
5 | import Container from '../container'
6 | import Button from '../button'
7 | import Popover from '../popover'
8 | import Campaign from './campaign'
9 |
10 | import { links } from '../../site-manifest'
11 |
12 | export default () => {
13 | return (
14 |
27 |
28 |
29 |
108 |
109 |
110 |
111 |
112 |
React Hooks for
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | Check out on GitHub
121 |
122 |
123 |
124 |
125 | View Docs
126 |
127 |
128 |
129 |
130 |
131 |
132 | )
133 | }
134 |
--------------------------------------------------------------------------------
/components/home/learn.js:
--------------------------------------------------------------------------------
1 | import Container from '../container'
2 | import Button from '../button'
3 | import SectionHeader from '../section-header'
4 |
5 | export default () => (
6 |
7 |
8 |
9 |
10 | Check out awesome-rehooks to find tutorials and
11 | packages to use.
12 |
13 |
14 |
46 |
47 |
48 |
49 | Get Started
50 |
51 |
52 |
53 | )
54 |
--------------------------------------------------------------------------------
/components/home/tab-button.js:
--------------------------------------------------------------------------------
1 | import classNames from 'classnames'
2 |
3 | export default ({
4 | isMobile,
5 | href,
6 | light,
7 | invert,
8 | selected,
9 | onClick,
10 | children
11 | }) => (
12 |
20 |
49 | {children}
50 |
51 | )
52 |
--------------------------------------------------------------------------------
/components/icons/arrow-next.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/components/icons/arrow-previous.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/components/icons/arrow-right-long.js:
--------------------------------------------------------------------------------
1 | export default ({ color, size }) =>
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/components/icons/arrow-right.js:
--------------------------------------------------------------------------------
1 | export default () =>
2 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/components/icons/arrow-up.js:
--------------------------------------------------------------------------------
1 | export default ({color}) =>
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/components/icons/companies/auth0.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
15 |
19 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/components/icons/companies/binance.js:
--------------------------------------------------------------------------------
1 | export default () =>
2 |
--------------------------------------------------------------------------------
/components/icons/companies/coinbase.js:
--------------------------------------------------------------------------------
1 | export default () =>
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/components/icons/companies/docker.js:
--------------------------------------------------------------------------------
1 | export default () => {
2 | // make element id unique
3 | let id = 'docker-logo-' + (~~(Math.random() * 1000))
4 |
5 | return
6 |
7 |
8 |
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/components/icons/companies/eaze.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
13 |
14 |
--------------------------------------------------------------------------------
/components/icons/companies/github.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/components/icons/companies/hulu.js:
--------------------------------------------------------------------------------
1 | export default () =>
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/components/icons/companies/invision.js:
--------------------------------------------------------------------------------
1 | export default () =>
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/components/icons/companies/jet.js:
--------------------------------------------------------------------------------
1 | export default () =>
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/components/icons/companies/magic-leap.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
15 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/components/icons/companies/mozilla-vr.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
15 |
20 |
25 |
26 |
--------------------------------------------------------------------------------
/components/icons/companies/netflix.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
13 |
--------------------------------------------------------------------------------
/components/icons/companies/opencollective.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
9 |
13 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/components/icons/companies/pling.js:
--------------------------------------------------------------------------------
1 | export default () => (
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | );
13 |
--------------------------------------------------------------------------------
/components/icons/companies/scale.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
13 |
14 |
--------------------------------------------------------------------------------
/components/icons/companies/tencent.js:
--------------------------------------------------------------------------------
1 | export default () =>
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/components/icons/companies/ticketmaster.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
13 |
--------------------------------------------------------------------------------
/components/icons/companies/trulia.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
15 |
22 |
26 |
30 |
34 |
35 |
--------------------------------------------------------------------------------
/components/icons/external-link.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
9 |
10 |
11 |
18 |
19 |
20 |
21 |
30 |
35 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/components/icons/github.js:
--------------------------------------------------------------------------------
1 | export default () =>
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/components/icons/heart.js:
--------------------------------------------------------------------------------
1 | export default ({color}) =>
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/components/icons/next-logo.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
13 |
--------------------------------------------------------------------------------
/components/icons/notification-indicator.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
18 |
--------------------------------------------------------------------------------
/components/icons/permalink.js:
--------------------------------------------------------------------------------
1 | export default () =>
2 |
7 |
8 |
--------------------------------------------------------------------------------
/components/icons/rehooks-logo.js:
--------------------------------------------------------------------------------
1 | export default () => (
2 |
3 |
4 |
8 |
13 |
14 |
15 | )
16 |
--------------------------------------------------------------------------------
/components/icons/spectrum.js:
--------------------------------------------------------------------------------
1 | export default () =>
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/components/icons/toggle.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/components/icons/zeit-logo-outline.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
9 |
--------------------------------------------------------------------------------
/components/icons/zeit-logo.js:
--------------------------------------------------------------------------------
1 | export default () =>
8 |
9 |
15 |
16 |
17 |
18 |
19 |
25 |
--------------------------------------------------------------------------------
/components/icons/zeit-white-full-logo.js:
--------------------------------------------------------------------------------
1 | export default () =>
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/components/image.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import IObserver from './intersection-observer';
4 |
5 | // This component might look a little complex
6 | // because one could argue that keeping the aspect ratio
7 | // of an image can be solved with `height: auto`,
8 | // but it's actually not that easy if you want to prevent
9 | // element flickering
10 |
11 | // Because if you want to do that, you need to set the aspect
12 | // ratio of the image's container BEFORE the image loads
13 |
14 | class Image extends Component {
15 | static defaultProps = {
16 | lazy: true
17 | };
18 |
19 | static propTypes = {
20 | width: PropTypes.number.isRequired,
21 | height: PropTypes.number.isRequired,
22 | lazy: PropTypes.bool
23 | };
24 |
25 | state = {
26 | src: !this.props.lazy ? this.props.videoSrc || this.props.src : undefined
27 | };
28 |
29 | handleIntersect = entry => {
30 | if (entry.isIntersecting) {
31 | this.setState({ src: this.props.videoSrc || this.props.src });
32 | }
33 | };
34 |
35 | render() {
36 | const {
37 | caption,
38 | width,
39 | height,
40 | margin = 40,
41 | video = false,
42 | videoSrc,
43 | captionSpacing = null,
44 | renderImage,
45 | oversize = true,
46 | lazy,
47 | ...rest
48 | } = this.props;
49 |
50 | const aspectRatio = `${String((height / width) * 100)}%`;
51 | const classes = width > 650 && oversize ? 'oversize' : '';
52 |
53 | return (
54 |
60 |
61 |
62 |
63 | {this.state.src ? (
64 | videoSrc || video ? (
65 |
66 | ) : renderImage ? (
67 | renderImage(rest)
68 | ) : (
69 |
70 | )
71 | ) : null}
72 |
73 |
74 | {caption && (
75 |
76 | {caption}
77 |
78 | )}
79 |
80 |
81 |
119 |
120 |
121 | );
122 | }
123 | }
124 |
125 | export const Video = props => ;
126 |
127 | export default Image;
128 |
--------------------------------------------------------------------------------
/components/intersection-observer/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './intersection-observer';
2 |
--------------------------------------------------------------------------------
/components/intersection-observer/intersection-observer.js:
--------------------------------------------------------------------------------
1 | // Packages
2 | import { Component, cloneElement, Children } from 'react';
3 | import { findDOMNode } from 'react-dom';
4 | import PropTypes from 'prop-types';
5 |
6 | // Private Observer Manager functions and manager
7 | import { getObserver, observeTarget, unobserveTarget } from './manager';
8 | import { hasEqualOptions, isDOMNode } from './utils';
9 |
10 | export default class Observer extends Component {
11 | static propTypes = {
12 | disabled: PropTypes.bool,
13 | once: PropTypes.bool,
14 | onIntersect: PropTypes.func.isRequired,
15 | render: PropTypes.func,
16 | root: PropTypes.element,
17 | rootMargin: PropTypes.string,
18 | threshold: PropTypes.oneOfType([
19 | PropTypes.arrayOf(PropTypes.number),
20 | PropTypes.number
21 | ]),
22 | value: PropTypes.string
23 | };
24 |
25 | static defaultProps = {
26 | disabled: false,
27 | once: false
28 | };
29 |
30 | shouldReobserve = false;
31 |
32 | componentDidMount() {
33 | this.observer = getObserver(getOptions(this.props));
34 | this.observe();
35 | }
36 |
37 | UNSAFE_componentWillReceiveProps(nextProps) {
38 | const nextOptions = getOptions(nextProps);
39 | if (!hasEqualOptions(this.observer, nextOptions)) {
40 | this.unobserve();
41 | this.observer = getObserver(nextOptions);
42 | this.shouldReobserve = true;
43 | }
44 |
45 | if (this.props.disabled && !nextProps.disabled) {
46 | this.shouldReobserve = true;
47 | }
48 |
49 | if (!this.props.disabled && nextProps.disabled) {
50 | this.unobserve();
51 | }
52 | }
53 |
54 | componentDidUpdate() {
55 | if (this.shouldReobserve) {
56 | this.observe();
57 | this.shouldReobserve = false;
58 | }
59 | }
60 |
61 | componentWillUnmount() {
62 | this.unobserve();
63 | }
64 |
65 | handleTarget = node => {
66 | // eslint-disable-next-line
67 | const element = isDOMNode(node) ? node : findDOMNode(node);
68 | if (this.target && this.target !== element) {
69 | this.unobserve();
70 | this.shouldReobserve = true;
71 | }
72 | this.target = element;
73 | };
74 |
75 | observe() {
76 | if (!this.props.disabled) {
77 | observeTarget(this.observer, this.target, this.handleIntersect);
78 | }
79 | }
80 |
81 | unobserve() {
82 | unobserveTarget(this.observer, this.target);
83 | }
84 |
85 | handleIntersect = entry => {
86 | this.props.onIntersect(entry, this.props.value);
87 | if (this.props.once && entry.isIntersecting) {
88 | this.unobserve();
89 | }
90 | };
91 |
92 | render() {
93 | return this.props.render
94 | ? this.props.render({ innerRef: this.handleTarget })
95 | : cloneElement(Children.only(this.props.children), {
96 | ref: this.handleTarget
97 | });
98 | }
99 | }
100 |
101 | const getOptions = props => ({
102 | root: props.root,
103 | rootMargin: props.rootMargin,
104 | threshold: props.threshold
105 | });
106 |
--------------------------------------------------------------------------------
/components/intersection-observer/manager.js:
--------------------------------------------------------------------------------
1 | import { hasEqualOptions, parseOptions } from './utils';
2 |
3 | if (typeof window !== 'undefined') {
4 | require('intersection-observer');
5 | }
6 |
7 | const manager = (function makeManager() {
8 | const observers = new Map();
9 |
10 | function getObserver(options) {
11 | return (
12 | findObserver(options) ||
13 | new IntersectionObserver(intersectionCallback, options)
14 | );
15 | }
16 |
17 | function findObserver(options = {}) {
18 | const parsedOptions = parseOptions(options);
19 | for (const observer of observers.keys()) {
20 | if (hasEqualOptions(observer, parsedOptions)) {
21 | return observer;
22 | }
23 | }
24 | return null;
25 | }
26 |
27 | function getObserverTargets(observer) {
28 | return !observers.has(observer)
29 | ? observers.set(observer, new Map()).get(observer)
30 | : observers.get(observer);
31 | }
32 |
33 | function observeTarget(observer, target, handler) {
34 | const targets = getObserverTargets(observer);
35 | targets.set(target, handler);
36 | observer.observe(target);
37 | }
38 |
39 | function unobserveTarget(observer, target) {
40 | const handlers = getObserverTargets(observer);
41 | handlers.delete(target);
42 | observer.unobserve(target);
43 | }
44 |
45 | function intersectionCallback(entries, observer) {
46 | for (let entry of entries) {
47 | const handlers = getObserverTargets(observer);
48 | const handler = handlers.get(entry.target);
49 | if (handler) {
50 | handler(entry);
51 | }
52 | }
53 | }
54 |
55 | return {
56 | getObserver,
57 | observeTarget,
58 | unobserveTarget
59 | };
60 | })();
61 |
62 | export default manager;
63 | export const { getObserver } = manager;
64 | export const { observeTarget } = manager;
65 | export const { unobserveTarget } = manager;
66 |
--------------------------------------------------------------------------------
/components/intersection-observer/utils.js:
--------------------------------------------------------------------------------
1 | export function isDOMNode(node) {
2 | return (
3 | node && Object.prototype.hasOwnProperty.call(node, 'getBoundingClientRect')
4 | );
5 | }
6 |
7 | export function parseOptions(options = {}) {
8 | return {
9 | root: options.root || null,
10 | rootMargin: parseRootMargin(options.rootMargin),
11 | threshold: parseThreshold(options.threshold)
12 | };
13 | }
14 |
15 | export function hasEqualOptions(observer, options) {
16 | return (
17 | equalPair(options.root, observer.root) &&
18 | equalPair(options.rootMargin, observer.rootMargin) &&
19 | equalPair(options.threshold, observer.thresholds)
20 | );
21 | }
22 |
23 | function parseRootMargin(rootMargin) {
24 | const margins = (rootMargin || '0px').trim().split(/\s+/);
25 | margins.forEach(validateRootMargin);
26 | margins[1] = margins[1] || margins[0];
27 | margins[2] = margins[2] || margins[0];
28 | margins[3] = margins[3] || margins[1];
29 | return margins.join(' ');
30 | }
31 |
32 | function validateRootMargin(margin) {
33 | if (!/^-?\d*\.?\d+(px|%)$/.test(margin)) {
34 | throw new Error('rootMargin must be specified as a CSS margin property');
35 | }
36 | }
37 |
38 | function parseThreshold(threshold) {
39 | return !Array.isArray(threshold)
40 | ? [typeof threshold !== 'undefined' ? threshold : 0]
41 | : threshold;
42 | }
43 |
44 | function equalPair(optionA, optionB) {
45 | if (Array.isArray(optionA) && Array.isArray(optionB)) {
46 | if (optionA.length === optionB.length) {
47 | return optionA.every((element, idx) =>
48 | equalPair(optionA[idx], optionB[idx])
49 | );
50 | }
51 | }
52 | return optionA === optionB;
53 | }
54 |
--------------------------------------------------------------------------------
/components/logo.js:
--------------------------------------------------------------------------------
1 | import withPure from './hoc/pure'
2 |
3 | export default withPure(({ size }) => (
4 |
5 |
6 |
10 |
15 |
16 |
17 | ))
18 |
--------------------------------------------------------------------------------
/components/media-query.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 |
3 | const {
4 | Provider: MediaQueryProvider,
5 | Consumer: MediaQueryConsumer
6 | } = React.createContext({
7 | isMobile: false,
8 | isTablet: false
9 | });
10 |
11 | const withMediaQuery = Comp =>
12 | class extends PureComponent {
13 | state = {
14 | isMobile: false,
15 | isTablet: false
16 | };
17 |
18 | onResize = () => {
19 | const isMobile = window.innerWidth < 640;
20 | const isTablet = window.innerWidth < 960;
21 | if (isMobile !== this.state.isMobile) {
22 | this.setState({ isMobile });
23 | }
24 | if (isTablet !== this.state.isTablet) {
25 | this.setState({ isTablet });
26 | }
27 | };
28 |
29 | componentDidMount() {
30 | window.addEventListener('resize', this.onResize);
31 | this.onResize();
32 | }
33 |
34 | componentWillUnmount() {
35 | window.removeEventListener('resize', this.onResize);
36 | }
37 |
38 | render() {
39 | const { isMobile, isTablet } = this.state;
40 |
41 | return (
42 |
43 |
44 |
45 | );
46 | }
47 | };
48 |
49 | export { MediaQueryProvider, MediaQueryConsumer, withMediaQuery };
50 |
--------------------------------------------------------------------------------
/components/notification.js:
--------------------------------------------------------------------------------
1 | import Link from 'next/link'
2 | import { ellipsis } from 'polished'
3 |
4 | import { MediaQueryConsumer } from './media-query'
5 | import Container from './container'
6 | import withPure from './hoc/pure'
7 |
8 | export default withPure(({ href, title, titleMobile, children }) => (
9 |
10 | {({ isMobile }) => (
11 |
37 | )}
38 |
39 | ))
40 |
--------------------------------------------------------------------------------
/components/popover.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import classNames from 'classnames';
3 |
4 | export default class Popover extends PureComponent {
5 | state = {
6 | show: false,
7 | top: true,
8 | left: false
9 | };
10 |
11 | onMouseEnter = () => {
12 | let top = Infinity;
13 | let right = Infinity;
14 | let left = 0;
15 |
16 | if (this.containerEl) {
17 | const bounding = this.containerEl.getBoundingClientRect();
18 | top = bounding.top;
19 | right = window.innerWidth - bounding.right;
20 | left = bounding.left;
21 | }
22 |
23 | this.setState({
24 | show: true,
25 | left: right < 100,
26 | right: left < 100,
27 | bottom: top < (this.props.top || 110)
28 | });
29 | };
30 |
31 | onMouseLeave = () => {
32 | this.setState({ show: false });
33 | };
34 |
35 | handleClickOutside = ev => {
36 | if (
37 | this.state.show &&
38 | this.containerEl &&
39 | (this.containerEl === ev.target || this.containerEl.contains(ev.target))
40 | ) {
41 | this.onMouseLeave();
42 | }
43 | };
44 |
45 | componentDidMount() {
46 | window.addEventListener('mousedown', this.handleClickOutside);
47 | window.addEventListener('touchstart', this.handleClickOutside);
48 | }
49 |
50 | componentWillUnmount() {
51 | window.removeEventListener('mousedown', this.handleClickOutside);
52 | window.removeEventListener('touchstart', this.handleClickOutside);
53 | }
54 |
55 | render() {
56 | const {
57 | bottom: _bottom,
58 | left: _left,
59 | right: _right,
60 | content,
61 | children
62 | } = this.props;
63 | const { show, left, right, bottom } = this.state;
64 |
65 | return (
66 | (this.containerEl = el)}
69 | onTouchStart={this.onMouseEnter}
70 | onMouseEnter={this.onMouseEnter}
71 | onMouseLeave={this.onMouseLeave}
72 | >
73 |
142 | {children}
143 |
151 | {content}
152 |
153 |
154 | );
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/components/screen.js:
--------------------------------------------------------------------------------
1 | import withPure from './hoc/pure';
2 |
3 | export default withPure(({ id, offset, children }) => (
4 |
10 |
18 | {children}
19 |
20 | ));
21 |
--------------------------------------------------------------------------------
/components/section-header.js:
--------------------------------------------------------------------------------
1 | import withPure from './hoc/pure';
2 |
3 | export default withPure(({ anchor, id, title, description }) => (
4 |
5 |
21 | {anchor && }
22 |
23 | {title}
24 |
25 | {description && {description} }
26 |
27 | ));
28 |
--------------------------------------------------------------------------------
/components/social-meta.js:
--------------------------------------------------------------------------------
1 | import Head from 'next/head'
2 |
3 | export default ({ title, description, image, url }) => (
4 |
5 | {title && }
6 | {url && }
7 | {description && }
8 | {description && }
9 | {image && }
10 | {image && }
11 |
12 | )
13 |
--------------------------------------------------------------------------------
/components/table.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | // Packages
3 | import React from 'react';
4 |
5 | const Table = ({ children }) => (
6 |
7 |
8 | {children}
9 |
10 |
19 |
20 |
21 | );
22 |
23 | class Row extends React.Component {
24 | getChildContext() {
25 | return {
26 | header: this.props.header || false
27 | };
28 | }
29 |
30 | render() {
31 | return (
32 |
33 | {this.props.children}
34 |
35 |
45 |
46 | );
47 | }
48 | }
49 |
50 | Row.childContextTypes = {
51 | header: PropTypes.bool
52 | };
53 |
54 | const Column = ({ children }, context) =>
55 | React.createElement(
56 | context.header ? 'th' : 'td',
57 | {
58 | style: {
59 | verticalAlign: 'top'
60 | }
61 | },
62 | children
63 | );
64 |
65 | Column.contextTypes = {
66 | header: PropTypes.bool
67 | };
68 |
69 | const Cell = Column;
70 |
71 | export { Table, Row, Column, Cell };
72 |
--------------------------------------------------------------------------------
/components/tabs.js:
--------------------------------------------------------------------------------
1 | import { PureComponent } from 'react';
2 | import Router from 'next/router';
3 | import GithubSlugger from 'github-slugger';
4 |
5 | export default class Tabs extends PureComponent {
6 | constructor(props) {
7 | super();
8 |
9 | this.state = {
10 | selected: props.data[0]
11 | };
12 | }
13 | componentDidMount() {
14 | const slugger = new GithubSlugger();
15 |
16 | if (this.props.anchor) {
17 | let index = this.props.data
18 | .map(tab => slugger.slug(tab))
19 | .indexOf(window.location.hash.slice(1));
20 | if (index !== -1) {
21 | this.setState({
22 | selected: this.props.data[index]
23 | });
24 | }
25 | }
26 | }
27 |
28 | onSelect = id => {
29 | if (this.props.data.indexOf(id) === -1) {
30 | return;
31 | }
32 | if (this.state.selected === id) {
33 | return;
34 | }
35 | if (this.props.anchor) {
36 | // wait 300ms for re-render
37 | // for the performance reason
38 | setTimeout(() => {
39 | const slugger = new GithubSlugger();
40 |
41 | Router.replace(
42 | window.location.pathname,
43 | window.location.pathname + '#' + slugger.slug(id),
44 | { shallow: true }
45 | );
46 | }, 300);
47 | }
48 | this.setState({
49 | selected: id
50 | });
51 | };
52 |
53 | componentDidUpdate(prevProps) {
54 | if (this.props.data !== prevProps.data) {
55 | this.setState({
56 | selected: this.props.data[0]
57 | });
58 | }
59 | }
60 |
61 | render() {
62 | const { data, anchor, children } = this.props;
63 | if (!data.length) {
64 | return null;
65 | }
66 |
67 | let { selected } = this.state;
68 | const index = data.indexOf(selected);
69 | if (index === -1) {
70 | selected = data[0];
71 | }
72 |
73 | if (typeof children !== 'function') {
74 | return null;
75 | }
76 | return children(this.onSelect, selected, index);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/components/text/blockquote.js:
--------------------------------------------------------------------------------
1 | const Blockquote = ({ children }) => (
2 |
3 | {children}
4 |
5 |
70 |
71 | );
72 |
73 | export default Blockquote;
74 |
--------------------------------------------------------------------------------
/components/text/caption.js:
--------------------------------------------------------------------------------
1 | import { FONT_FAMILY_MONO } from '../css-config';
2 |
3 | const Caption = ({ children }) => (
4 |
5 | {children}
6 |
16 |
17 | );
18 |
19 | const Code = ({ children }) => (
20 |
21 | {children}
22 |
38 |
39 | );
40 |
41 | Caption.Code = Code;
42 |
43 | export default Caption;
44 |
--------------------------------------------------------------------------------
/components/text/code.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import { FONT_FAMILY_MONO, COLOR_CODE_LIGHT } from '../css-config';
3 |
4 | export const Code = ({ children, syntax }, { darkBg } = {}) => (
5 |
6 | {children}
7 |
34 |
35 | );
36 |
37 | Code.contextTypes = {
38 | darkBg: PropTypes.bool
39 | };
40 |
41 | export const InlineCode = ({ children, noWrap }, { disabled, darkBg } = {}) => (
42 |
49 | {children}
50 |
80 |
81 | );
82 |
83 | InlineCode.contextTypes = {
84 | darkBg: PropTypes.bool,
85 | disabled: PropTypes.bool
86 | };
87 |
--------------------------------------------------------------------------------
/components/text/heading.js:
--------------------------------------------------------------------------------
1 | import { bool } from 'prop-types';
2 |
3 | export const H1 = ({ className, children }, { darkBg }) => (
4 |
5 | {children}
6 |
22 |
23 | );
24 |
25 | H1.contextTypes = {
26 | darkBg: bool
27 | };
28 |
29 | const B = ({ children }) => (
30 |
31 | {children}
32 |
39 |
40 | );
41 |
42 | H1.B = B;
43 |
44 | export const H2 = ({ children }, { darkBg }) => (
45 |
46 | {children}
47 |
62 |
63 | );
64 |
65 | H2.contextTypes = {
66 | darkBg: bool
67 | };
68 |
69 | export const H3 = ({ children }, { darkBg }) => (
70 |
71 | {children}
72 |
87 |
88 | );
89 |
90 | H3.contextTypes = {
91 | darkBg: bool
92 | };
93 |
94 | export const H4 = ({ children, isCommand }, { darkBg }) => (
95 |
96 | {children}
97 |
120 |
121 | );
122 |
123 | H4.contextTypes = {
124 | darkBg: bool
125 | };
126 |
127 | export const H5 = ({ children }, { darkBg }) => (
128 |
129 | {children}
130 |
145 |
146 | );
147 |
148 | H5.contextTypes = {
149 | darkBg: bool
150 | };
151 |
--------------------------------------------------------------------------------
/components/text/hr.js:
--------------------------------------------------------------------------------
1 | const HR = () => (
2 |
9 | );
10 |
11 | export default HR;
12 |
--------------------------------------------------------------------------------
/components/text/link.js:
--------------------------------------------------------------------------------
1 | // Packages
2 | import NativeLink from 'next/link';
3 | import PropTypes from 'prop-types';
4 |
5 | export const GenericLink = props => {
6 | if (
7 | props.href.startsWith('/') &&
8 | !props.href.startsWith('/docs') &&
9 | !props.href.startsWith('/api')
10 | ) {
11 | return ;
12 | }
13 |
14 | if (props.href.includes('@') || props.href.startsWith('#')) {
15 | return ;
16 | }
17 |
18 | return ;
19 | };
20 |
21 | export const InternalLink = (
22 | { href, as, children, error = false, underlineOnHover = true },
23 | { disabled, darkBg = false, inError = false } = {}
24 | ) => (
25 |
26 |
34 | {children}
35 |
36 |
69 |
70 |
71 | );
72 |
73 | InternalLink.contextTypes = {
74 | darkBg: PropTypes.bool,
75 | disabled: PropTypes.bool,
76 | inError: PropTypes.bool
77 | };
78 |
79 | export const AnchorLink = ({
80 | href,
81 | onClick,
82 | children,
83 | underlineOnHover = true
84 | }) => (
85 |
86 | {children}
87 |
88 |
106 |
107 | );
108 |
109 | export const ExternalLink = ({ href, children }, { disabled, darkBg } = {}) => (
110 |
119 | {children}
120 |
121 |
143 |
144 | );
145 |
146 | ExternalLink.contextTypes = {
147 | darkBg: PropTypes.bool,
148 | disabled: PropTypes.bool
149 | };
150 |
--------------------------------------------------------------------------------
/components/text/list.js:
--------------------------------------------------------------------------------
1 | export const UL = ({ children }) => (
2 |
21 | );
22 |
23 | export const OL = ({ children }) => (
24 |
25 | {children}
26 |
34 |
35 | );
36 |
37 | export const LI = ({ children }) => (
38 |
39 | {children}
40 |
49 |
50 | );
51 |
--------------------------------------------------------------------------------
/components/text/paragraph.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 |
3 | export const P = ({ className = '', children, large }, { darkBg } = {}) => (
4 |
5 | {children}
6 |
17 |
18 | );
19 |
20 | P.contextTypes = {
21 | darkBg: PropTypes.bool
22 | };
23 |
24 | export const PDIV = ({ children }) => (
25 |
26 | {children}
27 |
34 |
35 | );
36 |
37 | const B = ({ children }) => (
38 |
39 | {children}
40 |
45 |
46 | );
47 |
48 | export const HR = () => (
49 |
50 |
57 |
58 | );
59 |
60 | export const Quote = ({ children }, { darkBg } = {}) => (
61 |
62 | {children}
63 |
79 |
80 | );
81 |
82 | Quote.contextTypes = {
83 | darkBg: PropTypes.bool
84 | };
85 |
86 | P.B = B;
87 |
--------------------------------------------------------------------------------
/components/text/terminal.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cn from 'classnames';
3 | import PropTypes from 'prop-types';
4 | import { GenericLink } from './link';
5 | import { FONT_FAMILY_MONO, COLOR_CODE_LIGHT } from '../css-config';
6 |
7 | export const TerminalInput = (
8 | { children, prefix },
9 | { disabled, darkBg } = {}
10 | ) => (
11 |
12 | {children}
13 |
41 |
42 | );
43 |
44 | TerminalInput.contextTypes = {
45 | darkBg: PropTypes.bool,
46 | disabled: PropTypes.bool
47 | };
48 |
49 | export class TerminalOutput extends React.Component {
50 | static childContextTypes = {
51 | darkBg: PropTypes.bool
52 | };
53 |
54 | getChildContext() {
55 | return { darkBg: true };
56 | }
57 |
58 | render() {
59 | const { children, className, showPrompt } = this.props;
60 | return (
61 |
62 | {children}
63 |
89 |
90 | );
91 | }
92 | }
93 |
94 | export const TerminalLink = props => (
95 |
96 |
97 |
102 |
103 | );
104 |
--------------------------------------------------------------------------------
/components/word-slider.js:
--------------------------------------------------------------------------------
1 | /* global window */
2 | import React, { PureComponent } from 'react';
3 | import { Transition, animated } from 'react-spring';
4 |
5 | export default class extends PureComponent {
6 | constructor(props) {
7 | super(props);
8 | this.children = React.Children.toArray(props.children);
9 | this.state = {
10 | count: React.Children.count(props.children),
11 | currentIndex: 0
12 | };
13 | }
14 |
15 | componentDidMount() {
16 | this.startAnimation();
17 | }
18 |
19 | componentWillUnmount() {
20 | clearInterval(this.animation);
21 | }
22 |
23 | startAnimation() {
24 | this.animation = setInterval(() => {
25 | if (window.document.visibilityState === 'hidden') {
26 | // tab invisible; pause for one round to avoid flickering
27 | this.pauseAnimation = true;
28 | return;
29 | }
30 | if (!this.pauseAnimation) {
31 | this.setState({
32 | currentIndex: (this.state.currentIndex + 1) % this.state.count
33 | });
34 | } else {
35 | this.pauseAnimation = false;
36 | }
37 | }, this.props.duration || 1500);
38 | }
39 |
40 | render() {
41 | const currentIndex = this.state.currentIndex;
42 | return (
43 |
44 |
52 | {({ opacity, y }) => (
53 | `translate3d(0, ${y}%, 0)`),
57 | opacity
58 | }}
59 | >
60 | {this.props.children[currentIndex]}
61 |
62 | )}
63 |
64 |
77 |
78 | );
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/lib/analytics.js:
--------------------------------------------------------------------------------
1 | export const GA_TRACKING_ID = 'UA-117491914-2';
2 |
3 | export const trackPageview = url => {
4 | window.gtag('config', GA_TRACKING_ID, {
5 | page_location: url
6 | });
7 | };
8 |
9 | export const trackEvent = ({ action, category, label, value }) => {
10 | window.gtag('event', action, {
11 | event_category: category,
12 | event_label: label,
13 | value: value
14 | });
15 | };
16 |
--------------------------------------------------------------------------------
/lib/polyfill.js:
--------------------------------------------------------------------------------
1 | if (!Object.entries) {
2 | Object.entries = function(obj) {
3 | var ownProps = Object.keys(obj),
4 | i = ownProps.length,
5 | resArray = new Array(i);
6 | while (i--) {
7 | resArray[i] = [ownProps[i], obj[ownProps[i]]];
8 | }
9 | return resArray;
10 | };
11 | }
12 |
13 | if (!String.prototype.startsWith) {
14 | String.prototype.startsWith = function(searchString, position) {
15 | position = position || 0;
16 | return this.indexOf(searchString, position) === position;
17 | };
18 | }
19 |
--------------------------------------------------------------------------------
/lib/rehype-readme.js:
--------------------------------------------------------------------------------
1 | const url = require('url')
2 | const visit = require('unist-util-visit')
3 | const GitHubSlugger = require('github-slugger')
4 |
5 | /**
6 | * 1. relative urls => github urls
7 | * 2. relative images => github images
8 | */
9 | const ABSOLUTE_URL = /^https?:\/\/|^\/\//i
10 | const resolveURL = (base, href) => url.resolve(base, href.replace(/^\//, ''))
11 |
12 | const toAbsoluteURL = ({ repo, img }) => node => {
13 | // match relative url
14 | if (node.properties) {
15 | let href = node.tagName === 'a' ? node.properties.href : node.properties.src
16 | if (href && href[0] !== '#' && !ABSOLUTE_URL.test(href)) {
17 | if (node.tagName === 'a') {
18 | node.properties.href = resolveURL(repo, href)
19 | } else {
20 | node.properties.src = resolveURL(img, href)
21 | }
22 | }
23 | }
24 | }
25 |
26 | /**
27 | * tokenizer to match `` or ``
28 | */
29 | const C_TAB = '\t'
30 | const C_SPACE = ' '
31 | const C_NEWLINE = '\n'
32 |
33 | function generateTokenizer(name, repo) {
34 | return function(eat, value) {
35 | let index = 0
36 | let length = value.length
37 | let next
38 | let line
39 |
40 | let sequence = [
41 | new RegExp('^<' + name + '(?=(s|>|$))'),
42 | new RegExp('' + name + '>'),
43 | name.length + 2
44 | ]
45 |
46 | // eat initial spacing
47 | while (index < length) {
48 | let character = value.charAt(index)
49 | if (character !== C_TAB && character !== C_SPACE) {
50 | break
51 | }
52 | index++
53 | }
54 |
55 | next = value.indexOf(C_NEWLINE, index + 1)
56 | next = next === -1 ? length : next
57 | line = value.slice(index, next)
58 |
59 | if (!sequence[0].test(line)) {
60 | return
61 | }
62 |
63 | index = index + sequence[2]
64 | let now = eat.now()
65 | now.column += index
66 | now.offset += index
67 |
68 | let valueStart = index
69 |
70 | // match content
71 | while (index < length) {
72 | next = value.indexOf(C_NEWLINE, index + 1)
73 | next = next === -1 ? length : next
74 | line = value.slice(index + 1, next)
75 |
76 | if (sequence[1].test(line)) {
77 | if (line) {
78 | index = next
79 | }
80 | break
81 | }
82 |
83 | index = next
84 | }
85 |
86 | let subvalue = value.slice(0, index)
87 | let content = value.slice(valueStart, index - valueStart - 1)
88 |
89 | // trim content
90 | content = content
91 | .split('\n')
92 | .map(str => str.trim())
93 | .join('\n')
94 |
95 | // match links inside html (` `)
96 | content = content.replace(/]*)(>[^<]+<\/a>)/g, function(
97 | str,
98 | attr,
99 | rest
100 | ) {
101 | attr = attr.replace(/href=(['"])([^'"]*)\1/, function(str, quote, href) {
102 | if (href && href[0] !== '#' && !ABSOLUTE_URL.test(href)) {
103 | return 'href=' + quote + resolveURL(repo, href) + quote
104 | }
105 | return 'href=' + quote + href + quote
106 | })
107 |
108 | // add target='_blank'
109 | if (!attr.match(/target=['"]_blank\1/)) {
110 | attr += ' target="_blank"'
111 | }
112 |
113 | return ' {
142 | let headings = [
143 | {
144 | level: 0,
145 | title: 'Table of Contents'
146 | }
147 | ]
148 | let levelHeads = [headings]
149 | let tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].slice(0, level)
150 |
151 | visit(
152 | ast,
153 | node => tags.indexOf(node.tagName) !== -1,
154 | node => {
155 | let level = tags.indexOf(node.tagName) + 1
156 | let data = {
157 | level,
158 | title: node.children.map(node => node.value).join('')
159 | }
160 |
161 | do {
162 | if (!levelHeads.length) {
163 | break
164 | }
165 |
166 | let currentLevelList = levelHeads[levelHeads.length - 1]
167 | if (!currentLevelList.length || currentLevelList[0].level === level) {
168 | // empty
169 | currentLevelList.push(data)
170 | break
171 | } else if (currentLevelList[0].level < level) {
172 | // add as child
173 | let newLevelList = [data]
174 | levelHeads.push(newLevelList)
175 | currentLevelList.push(newLevelList)
176 | break
177 | } else {
178 | // pop
179 | while (currentLevelList.length && currentLevelList[0].level > level) {
180 | levelHeads.pop()
181 | currentLevelList = levelHeads[levelHeads.length - 1]
182 | }
183 | }
184 | } while (true)
185 | }
186 | )
187 |
188 | return headings.slice(1)
189 | }
190 |
191 | /**
192 | * The plugin
193 | */
194 | const readme = function(options) {
195 | if (!options.repo) {
196 | throw new Error(
197 | 'Please set a GitHub repo name in `options.repo`! e.g. `rehooks`'
198 | )
199 | }
200 | options.branch = options.branch || 'master'
201 | options.level = options.level || 6
202 |
203 | // base URL
204 | let repo =
205 | 'https://github.com/' + options.repo + '/blob/' + options.branch + '/'
206 | let img =
207 | 'https://raw.githubusercontent.com/' +
208 | options.repo +
209 | '/' +
210 | options.branch +
211 | '/'
212 |
213 | const Parser = this.Parser
214 |
215 | const tokenizers = Parser.prototype.blockTokenizers
216 | tokenizers.details = generateTokenizer('details', repo)
217 | tokenizers.summary = generateTokenizer('summary', repo)
218 |
219 | const methods = Parser.prototype.blockMethods
220 | methods.splice(methods.indexOf('html'), 0, 'summary')
221 | methods.splice(methods.indexOf('html'), 0, 'details')
222 |
223 | return function(ast) {
224 | visit(
225 | ast,
226 | node => node.tagName === 'a' || node.tagName === 'img',
227 | toAbsoluteURL({ repo, img })
228 | )
229 |
230 | let tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].slice(0, options.level)
231 | const slugger = new GitHubSlugger()
232 |
233 | visit(ast, 'element', node => {
234 | if (tags.indexOf(node.tagName) !== -1) {
235 | node.properties.id = slugger.slug(
236 | node.children.map(node => node.value).join('')
237 | )
238 | }
239 | })
240 |
241 | let headings = getHeadings(ast, options.level)
242 | ast.children.push({
243 | type: 'export',
244 | value: 'export const headings = ' + JSON.stringify(headings)
245 | })
246 | }
247 | }
248 |
249 | module.exports = readme
250 |
--------------------------------------------------------------------------------
/lib/router-events.js:
--------------------------------------------------------------------------------
1 | import mitt from 'mitt';
2 | import Router from 'next/router';
3 |
4 | const emitter = mitt();
5 | export default emitter;
6 |
7 | Router.onRouteChangeStart = (...args) => {
8 | emitter.emit('routeChangeStart', ...args);
9 | };
10 |
11 | Router.onRouteChangeComplete = (...args) => {
12 | emitter.emit('routeChangeComplete', ...args);
13 | };
14 |
15 | Router.onRouteChangeError = (...args) => {
16 | emitter.emit('routeChangeError', ...args);
17 | };
18 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const rehypePrism = require('@mapbox/rehype-prism')
4 | const rehypeReadme = require('./lib/rehype-readme')
5 | const nextMDX = require('@zeit/next-mdx')
6 |
7 | // only enable rehypeReadme for this file
8 | // because the github relative path replacement
9 | // might break things in other markdowns
10 | const withGitHubMDX = nextMDX({
11 | extension: path.join(__dirname, 'components', 'docs', 'docs.mdx'),
12 | options: {
13 | hastPlugins: [
14 | rehypePrism,
15 | [
16 | rehypeReadme,
17 | {
18 | repo: 'rehooks/template',
19 | branch: 'master',
20 | level: 4
21 | }
22 | ]
23 | ]
24 | }
25 | })
26 |
27 | const withMDX = nextMDX({
28 | extension: /\/(pages)\/(.+)\.mdx?$/,
29 | options: {
30 | hastPlugins: [rehypePrism]
31 | }
32 | })
33 |
34 | const webpack = require('webpack')
35 |
36 | var config = {
37 | pageExtensions: ['jsx', 'js', 'mdx'],
38 | webpack: (config, { dev, isServer }) => {
39 | config.plugins = config.plugins || []
40 | config.plugins.push(
41 | new webpack.ContextReplacementPlugin(
42 | /highlight\.js[\/\\]lib[\/\\]languages$/,
43 | new RegExp(`^./(${['javascript', 'json', 'xml'].join('|')})$`)
44 | )
45 | )
46 |
47 | if (isServer && !dev) {
48 | const originalEntry = config.entry
49 | config.entry = async () => {
50 | const entries = { ...(await originalEntry()) }
51 |
52 | return entries
53 | }
54 | }
55 |
56 | return config
57 | }
58 | }
59 |
60 | if (process.env.BUNDLE_ANALYZE) {
61 | const withBundleAnalyzer = require('@zeit/next-bundle-analyzer')
62 | config = withBundleAnalyzer({
63 | analyzeServer: ['server', 'both'].includes(process.env.BUNDLE_ANALYZE),
64 | analyzeBrowser: ['browser', 'both'].includes(process.env.BUNDLE_ANALYZE),
65 | bundleAnalyzerConfig: {
66 | server: {
67 | analyzerMode: 'static',
68 | reportFilename: '../../bundles/server.html'
69 | },
70 | browser: {
71 | analyzerMode: 'static',
72 | reportFilename: '../bundles/client.html'
73 | }
74 | },
75 | ...config
76 | })
77 | }
78 |
79 | module.exports = withGitHubMDX(withMDX(config))
80 |
--------------------------------------------------------------------------------
/now.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rehooks-website",
3 | "alias": ["rehooks.com", "www.rehooks.com", "rehooks.now.sh"],
4 | "type": "static",
5 | "static": {
6 | "directoryListing": false,
7 | "headers": [
8 | {
9 | "source": "/_next/static/chunks/*",
10 | "headers": [
11 | {
12 | "key": "Cache-Control",
13 | "value": "public, max-age=31536000, immutable"
14 | }
15 | ]
16 | },
17 | {
18 | "source": "/_next/static/runtime/*",
19 | "headers": [
20 | {
21 | "key": "Cache-Control",
22 | "value": "public, max-age=31536000, immutable"
23 | }
24 | ]
25 | },
26 | {
27 | "source": "/_next/static/commons/**",
28 | "headers": [
29 | {
30 | "key": "Cache-Control",
31 | "value": "public, max-age=31536000, immutable"
32 | }
33 | ]
34 | },
35 | {
36 | "source": "/static/favicon/**",
37 | "headers": [
38 | {
39 | "key": "Cache-Control",
40 | "value": "public, max-age=31536000, immutable"
41 | }
42 | ]
43 | },
44 | {
45 | "source": "/static/images/**",
46 | "headers": [
47 | {
48 | "key": "Cache-Control",
49 | "value": "public, max-age=31536000, immutable"
50 | }
51 | ]
52 | },
53 | {
54 | "source": "/_next/static/*/pages/**/*.js",
55 | "headers": [
56 | {
57 | "key": "Cache-Control",
58 | "value": "public, max-age=31536000, immutable"
59 | }
60 | ]
61 | }
62 | ]
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rehooks-website",
3 | "description": "Rehooks website",
4 | "version": "1.0.0",
5 | "main": "index.js",
6 | "repository": "rehooks/website",
7 | "author": "",
8 | "homepage": "https://rehooks.com",
9 | "license": "MIT",
10 | "scripts": {
11 | "dev": "next",
12 | "build": "next build",
13 | "export": "next export",
14 | "start": "next start",
15 | "lint": "eslint",
16 | "prettier": "prettier **/*.js --write --single-quote",
17 | "lint-staged": "lint-staged"
18 | },
19 | "pre-commit": "lint-staged",
20 | "lint-staged": {
21 | "*.js": [
22 | "prettier --write --single-quote",
23 | "git add"
24 | ]
25 | },
26 | "dependencies": {
27 | "@mapbox/rehype-prism": "^0.3.0",
28 | "@mdx-js/mdx": "^0.15.0",
29 | "@mdx-js/tag": "^0.15.0",
30 | "@rehooks/document-visibility": "1.0.1",
31 | "@rehooks/input-value": "1.0.0",
32 | "@rehooks/window-size": "1.0.2",
33 | "@zeit/next-bundle-analyzer": "^0.1.1",
34 | "@zeit/next-mdx": "^1.1.0",
35 | "babel-plugin-module-resolver": "^3.1.1",
36 | "classnames": "^2.2.6",
37 | "date-fns": "^1.29.0",
38 | "github-slugger": "1.2.0",
39 | "highlight.js": "^9.12.0",
40 | "intersection-observer": "^0.5.0",
41 | "mitt": "^1.1.3",
42 | "next": "7.0.2",
43 | "polished": "^2.0.3",
44 | "prop-types": "^15.6.2",
45 | "react": "^16.7.0-alpha.0",
46 | "react-dom": "^16.7.0-alpha.0",
47 | "react-frame-component": "^4.0.1",
48 | "react-highlight": "^0.12.0",
49 | "react-spring": "^5.6.14",
50 | "react-virtualized": "^9.20.1",
51 | "scroll-into-view-if-needed": "^2.2.16",
52 | "unist-util-visit": "1.4.0"
53 | },
54 | "devDependencies": {
55 | "babel-eslint": "^9.0.0",
56 | "eslint": "^5.6.0",
57 | "eslint-config-airbnb": "^17.1.0",
58 | "eslint-plugin-import": "^2.14.0",
59 | "eslint-plugin-jsx-a11y": "^6.1.1",
60 | "eslint-plugin-react": "^7.11.1",
61 | "lint-staged": "^7.2.2",
62 | "pre-commit": "^1.2.2",
63 | "prettier": "^1.14.2"
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/pages/_document.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/no-danger */
2 | import Document, { Head, Main, NextScript } from 'next/document'
3 |
4 | import { GA_TRACKING_ID } from '../lib/analytics'
5 |
6 | export default class NextSite extends Document {
7 | render() {
8 | return (
9 |
10 |
11 |
15 |
20 |
26 |
32 |
33 |
38 |
39 |
40 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | )
53 | }
54 | }
55 |
56 | function GA() {
57 | return (
58 | <>
59 |
63 |
73 | >
74 | )
75 | }
76 |
--------------------------------------------------------------------------------
/pages/docs/index.js:
--------------------------------------------------------------------------------
1 | import Page from '../../components/page'
2 | import Header from '../../components/header'
3 | import Navbar from '../../components/navbar'
4 | import Container from '../../components/container'
5 | import { MediaQueryConsumer } from '../../components/media-query'
6 | import withPure from '../../components/hoc/pure'
7 |
8 | import Markdown, { headings } from '../../components/docs/docs.mdx'
9 | import Documentation, { components } from '../../components/docs/documentation'
10 |
11 | const Content = withPure(() => )
12 |
13 | export default () => (
14 |
15 |
16 | {({ isMobile }) => (
17 |
24 | )}
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | )
33 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import Page from '../components/page'
2 | import Header from '../components/header'
3 | import Footer from '../components/footer'
4 | import Navbar from '../components/navbar'
5 | import Notification from '../components/notification'
6 | import { MediaQueryConsumer } from '../components/media-query'
7 |
8 | import Intro from '../components/home/intro'
9 | import Demo from '../components/home/demo'
10 | import Features from '../components/home/features'
11 | import SocialMeta from '../components/social-meta'
12 |
13 | export default () => (
14 |
15 |
21 |
22 | {({ isMobile }) => (
23 |
30 |
35 | React Hooks was just announced at ReactConf! Go to React docs to
36 | learn more →
37 |
38 |
39 |
40 | )}
41 |
42 |
43 |
44 |
45 | {/* */}
46 |
47 |
48 | )
49 |
--------------------------------------------------------------------------------
/site-manifest.js:
--------------------------------------------------------------------------------
1 | export const links = {
2 | logos: 'https://github.com/rehooks/logo#readme',
3 | spectrum: 'https://spectrum.chat/rehooks'
4 | }
5 |
--------------------------------------------------------------------------------
/static/favicon/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rehooks/website/00ccc2682dfb422f5cfd0e24e2c078e1abda64e0/static/favicon/android-chrome-192x192.png
--------------------------------------------------------------------------------
/static/favicon/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rehooks/website/00ccc2682dfb422f5cfd0e24e2c078e1abda64e0/static/favicon/android-chrome-512x512.png
--------------------------------------------------------------------------------
/static/favicon/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rehooks/website/00ccc2682dfb422f5cfd0e24e2c078e1abda64e0/static/favicon/apple-touch-icon.png
--------------------------------------------------------------------------------
/static/favicon/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #000000
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/static/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rehooks/website/00ccc2682dfb422f5cfd0e24e2c078e1abda64e0/static/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/static/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rehooks/website/00ccc2682dfb422f5cfd0e24e2c078e1abda64e0/static/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/static/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rehooks/website/00ccc2682dfb422f5cfd0e24e2c078e1abda64e0/static/favicon/favicon.ico
--------------------------------------------------------------------------------
/static/favicon/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rehooks/website/00ccc2682dfb422f5cfd0e24e2c078e1abda64e0/static/favicon/mstile-150x150.png
--------------------------------------------------------------------------------
/static/favicon/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/static/favicon/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Rehooks",
3 | "short_name": "Rehooks",
4 | "icons": [
5 | {
6 | "src": "/static/favicons/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/static/favicons/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#000000",
17 | "background_color": "#000000",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/static/images/og/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rehooks/website/00ccc2682dfb422f5cfd0e24e2c078e1abda64e0/static/images/og/home.png
--------------------------------------------------------------------------------