409 lines
12 KiB

/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* @emails react-core
*/
import ButtonLink from 'components/ButtonLink';
import Container from 'components/Container';
import Flex from 'components/Flex';
import CodeExample from 'components/CodeExample';
import PropTypes from 'prop-types';
import React, {Component} from 'react';
import {graphql} from 'gatsby';
import TitleAndMetaTags from 'components/TitleAndMetaTags';
import Layout from 'components/Layout';
import {colors, media, sharedStyles} from 'theme';
import loadScript from 'utils/loadScript';
import createOgUrl from 'utils/createOgUrl';
import {babelURL} from 'site-constants';
import ReactDOM from 'react-dom';
import logoWhiteSvg from 'icons/logo-white.svg';
class Home extends Component {
state = {
babelLoaded: false,
};
componentDidMount() {
loadScript(babelURL).then(
() => {
this.setState({
babelLoaded: true,
});
},
error => {
console.error('Babel failed to load.');
},
);
}
render() {
const {babelLoaded} = this.state;
const {data, location} = this.props;
const {codeExamples, examples, marketing} = data;
const code = codeExamples.edges.reduce((lookup, {node}) => {
lookup[node.mdAbsolutePath] = node;
return lookup;
}, {});
return (
<Layout location={location}>
<TitleAndMetaTags
title="React &ndash; A JavaScript library for building user interfaces"
ogUrl={createOgUrl('index.html')}
/>
<div css={{width: '100%'}}>
<header
css={{
backgroundColor: colors.dark,
color: colors.white,
}}>
<div
css={{
paddingTop: 45,
paddingBottom: 20,
[media.greaterThan('small')]: {
paddingTop: 60,
paddingBottom: 70,
},
[media.greaterThan('xlarge')]: {
paddingTop: 95,
paddingBottom: 85,
maxWidth: 1500, // Positioning of background logo
marginLeft: 'auto',
marginRight: 'auto',
position: 'relative',
'::before': {
content: ' ',
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
backgroundImage: `url(${logoWhiteSvg})`,
backgroundRepeat: 'no-repeat',
backgroundPosition: '100% 100px',
backgroundSize: '50% auto',
opacity: 0.05,
},
},
}}>
<div
css={{
// Content should be above absolutely-positioned hero image
position: 'relative',
}}>
<Container>
<h1
css={{
color: colors.brand,
textAlign: 'center',
margin: 0,
fontSize: 45,
letterSpacing: '0.01em',
[media.size('xsmall')]: {
fontSize: 30,
},
[media.greaterThan('xlarge')]: {
fontSize: 60,
},
}}>
React
</h1>
<p
css={{
paddingTop: 15,
textAlign: 'center',
fontSize: 24,
letterSpacing: '0.01em',
fontWeight: 200,
[media.size('xsmall')]: {
fontSize: 16,
maxWidth: '12em',
marginLeft: 'auto',
marginRight: 'auto',
},
[media.greaterThan('xlarge')]: {
paddingTop: 20,
fontSize: 30,
},
}}>
A JavaScript library for building user interfaces
</p>
<Flex
valign="center"
css={{
paddingTop: 40,
[media.greaterThan('xlarge')]: {
paddingTop: 65,
},
}}>
<CtaItem>
<ButtonLink
to="/docs/getting-started.html"
type="primary">
Get Started
</ButtonLink>
</CtaItem>
<CtaItem>
<ButtonLink to="/tutorial/tutorial.html" type="secondary">
Take the Tutorial
</ButtonLink>
</CtaItem>
</Flex>
</Container>
</div>
</div>
</header>
<Container>
<div css={sharedStyles.markdown}>
<section
css={[
sectionStyles,
{
[media.lessThan('medium')]: {
marginTop: 0,
marginBottom: 0,
overflowX: 'auto',
paddingTop: 30,
WebkitOverflowScrolling: 'touch',
position: 'relative',
maskImage:
'linear-gradient(to right, transparent, white 10px, white 90%, transparent)',
},
},
]}>
<div
css={{
display: 'flex',
flexDirection: 'row',
[media.lessThan('medium')]: {
display: 'block',
whiteSpace: 'nowrap',
},
}}>
{marketing.edges.map(({node: column}, index) => (
<div
key={index}
css={{
display: 'flex',
flexDirection: 'column',
flex: '0 1 33%',
marginLeft: 40,
'&:first-of-type': {
marginLeft: 0,
[media.lessThan('medium')]: {
marginLeft: 10,
},
},
[media.lessThan('medium')]: {
display: 'inline-block',
verticalAlign: 'top',
marginLeft: 0,
whiteSpace: 'normal',
width: '75%',
marginRight: 20,
paddingBottom: 40,
'&:first-of-type': {
marginTop: 0,
},
},
}}>
<h3
css={[
headingStyles,
{
'&&': {
// Make specificity higher than the site-wide h3 styles.
color: colors.subtle,
paddingTop: 0,
fontWeight: 300,
fontSize: 20,
[media.greaterThan('xlarge')]: {
fontSize: 24,
fontWeight: 200,
},
},
},
]}>
{column.frontmatter.title}
</h3>
<div dangerouslySetInnerHTML={{__html: column.html}} />
</div>
))}
</div>
</section>
<hr
css={{
height: 1,
marginBottom: -1,
border: 'none',
borderBottom: `1 solid ${colors.divider}`,
}}
/>
<section css={sectionStyles}>
<div id="examples">
{examples.edges.map(({node}, index) => {
const snippet = code[node.fileAbsolutePath];
return (
<CodeExample
key={index}
id={snippet.id}
code={snippet.code}
loaded={babelLoaded}>
<h3 css={headingStyles}>{node.frontmatter.title}</h3>
<div dangerouslySetInnerHTML={{__html: node.html}} />
</CodeExample>
);
})}
</div>
</section>
</div>
</Container>
<section
css={{
background: colors.dark,
color: colors.white,
paddingTop: 45,
paddingBottom: 45,
}}>
<Container>
<Flex valign="center">
<CtaItem>
<ButtonLink to="/docs/getting-started.html" type="primary">
Get Started
</ButtonLink>
</CtaItem>
<CtaItem>
<ButtonLink to="/tutorial/tutorial.html" type="secondary">
Take the Tutorial
</ButtonLink>
</CtaItem>
</Flex>
</Container>
</section>
</div>
</Layout>
);
}
}
Home.propTypes = {
data: PropTypes.shape({
examples: PropTypes.object.isRequired,
marketing: PropTypes.object.isRequired,
}).isRequired,
};
function renderExamplePlaceholder(containerId) {
ReactDOM.render(
<h4>Loading code example...</h4>,
document.getElementById(containerId),
);
}
const CtaItem = ({children, primary = false}) => (
<div
css={{
width: '50%',
[media.between('small', 'large')]: {
paddingLeft: 20,
},
[media.greaterThan('xlarge')]: {
paddingLeft: 40,
},
'&:first-child': {
textAlign: 'right',
paddingRight: 15,
},
'&:nth-child(2)': {
[media.greaterThan('small')]: {
paddingLeft: 15,
},
},
}}>
{children}
</div>
);
export const pageQuery = graphql`
query IndexMarkdown {
codeExamples: allExampleCode {
edges {
node {
id
code
mdAbsolutePath
}
}
}
examples: allMarkdownRemark(
filter: {fileAbsolutePath: {regex: "//home/examples//"}}
sort: {fields: [frontmatter___order], order: ASC}
) {
edges {
node {
fileAbsolutePath
fields {
slug
}
frontmatter {
title
}
html
}
}
}
marketing: allMarkdownRemark(
filter: {fileAbsolutePath: {regex: "//home/marketing//"}}
sort: {fields: [frontmatter___order], order: ASC}
) {
edges {
node {
frontmatter {
title
}
html
}
}
}
}
`;
export default Home;
const sectionStyles = {
marginTop: 20,
marginBottom: 15,
[media.greaterThan('medium')]: {
marginTop: 60,
marginBottom: 65,
},
};
const headingStyles = {
'&&': {
marginBottom: 20,
},
};