convert website from hugo to gatsby (#1369)

This commit is contained in:
Zeb Pykosz
2018-07-25 07:47:26 -04:00
committed by Shawn Erquhart
parent d6c03707d8
commit 04c44518b2
113 changed files with 7441 additions and 2771 deletions

View File

@ -0,0 +1,70 @@
import React, { Component } from 'react';
import Link from 'gatsby-link';
/**
* Maually get table of contents since tableOfContents from markdown
* nodes have code added.
*
* https://github.com/gatsbyjs/gatsby/issues/5436
*/
class TableOfContents extends Component {
state = {
headings: []
};
componentDidMount() {
const contentHeadings = document.querySelectorAll('.docs-content h2');
const headings = [];
contentHeadings.forEach(h => {
headings.push({
id: h.id,
text: h.innerText
});
});
this.setState({
headings
});
}
render() {
const { headings } = this.state;
return (
<ul className="nav-subsections">
{headings.map(h => (
<li key={h.id}>
<a href={`#${h.id}`} className="subnav-link">
{h.text}
</a>
</li>
))}
</ul>
);
}
}
const DocsNav = ({ items, location }) => (
<nav className="docs-nav" id="docs-nav">
{items.map(item => (
<div className="docs-nav-section" key={item.title}>
<div className="docs-nav-section-title">{item.title}</div>
<ul className="docs-nav-section-list">
{item.group.edges.map(({ node }) => (
<li className="docs-nav-item" key={node.fields.slug}>
<Link
to={node.fields.slug}
className="nav-link"
activeClassName="active">
{node.frontmatter.title}
</Link>
{location.pathname === node.fields.slug && <TableOfContents />}
</li>
))}
</ul>
</div>
))}
</nav>
);
export default DocsNav;

View File

@ -0,0 +1,39 @@
import React, { Component } from 'react';
import searchIcon from '../img/search.svg';
class DocSearch extends Component {
state = {
enabled: true
};
componentDidMount() {
if (window.docsearch) {
window.docsearch({
apiKey: '08d03dc80862e84c70c5a1e769b13019',
indexName: 'netlifycms',
inputSelector: '.algolia-search',
debug: false // Set debug to true if you want to inspect the dropdown
});
} else {
this.setState({ enabled: false });
}
}
render() {
if (!this.state.enabled) {
return null;
}
return (
<a className="utility-input">
<img src={searchIcon} />
<input
type="search"
placeholder="Search the docs"
className="algolia-search"
/>
</a>
);
}
}
export default DocSearch;

View File

@ -0,0 +1,30 @@
import React from 'react';
const EditLink = ({ path }) => (
<a
className="edit-this-page"
href={`https://github.com/netlify/netlify-cms/blob/master/website/content/${path}`}>
<svg
version="1.1"
id="pencil"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
width="14px"
height="14px"
viewBox="0 0 512 512"
enableBackground="new 0 0 512 512"
xmlSpace="preserve">
<path
d="M398.875,248.875L172.578,475.187l-22.625-22.625L376.25,226.265L398.875,248.875z M308.375,158.39L82.063,384.687
l45.266,45.25L353.625,203.64L308.375,158.39z M263.094,113.125L36.828,339.437l22.625,22.625L285.75,135.765L263.094,113.125z
M308.375,67.875L285.719,90.5L421.5,226.265l22.625-22.625L308.375,67.875z M376.25,0L331,45.25l135.75,135.766L512,135.781
L376.25,0z M32,453.5V480h26.5L32,453.5 M0,376.25L135.766,512H0V376.25L0,376.25z"
/>
</svg>{' '}
Edit this page
</a>
);
export default EditLink;

View File

@ -0,0 +1,71 @@
import React, { Component } from 'react';
import moment from 'moment';
class EventWidget extends Component {
state = {
loading: false,
eventDate: ''
};
componentDidMount() {
const eventbriteToken = 'C5PX65CJBVIXWWLNFKLO';
const eventbriteOrganiser = '14281996019';
const url = `https://www.eventbriteapi.com/v3/events/search/?token=${eventbriteToken}&organizer.id=${eventbriteOrganiser}&expand=venue%27`;
this.setState({
loading: true
});
fetch(url)
.then(res => res.json())
.then(data => {
const eventDate = data.events[0].start.utc;
this.setState({
loading: false,
eventDate
});
})
.catch(err => {
console.log(err);
// TODO: set state to show error message
this.setState({
loading: false
});
});
}
render() {
const { loading, eventDate } = this.state;
if (loading) {
return <span>Loading...</span>;
}
const eventDateMoment = moment(eventDate);
const offset = eventDateMoment.isDST() ? -7 : -8;
const month = eventDateMoment.format('MMMM');
const day = eventDateMoment.format('DD');
const datePrefix = eventDateMoment.format('dddd, MMMM Do');
const dateSuffix = eventDateMoment.utcOffset(offset).format('h a');
return (
<div>
<div className="calendar">
<div className="month">{month}</div>
<div className="day">{day}</div>
</div>
<h3>
<strong>
{datePrefix} at {dateSuffix} PT
</strong>
</h3>
</div>
);
}
}
export default EventWidget;

View File

@ -0,0 +1,36 @@
import React from 'react';
import '../css/imports/footer.css';
const Footer = ({ buttons }) => (
<footer>
<div className="contained">
<div className="social-buttons">
{buttons.map(btn => (
<a href={btn.url} key={btn.url}>
{btn.name}
</a>
))}
</div>
<div className="footer-info">
<p>
<a
href="https://github.com/netlify/netlify-cms/blob/master/LICENSE"
className="text-link"
>
Distributed under MIT License
</a>{' '}
·{' '}
<a
href="https://github.com/netlify/netlify-cms/blob/master/CODE_OF_CONDUCT.md"
className="text-link"
>
Code of Conduct
</a>
</p>
</div>
</div>
</footer>
);
export default Footer;

View File

@ -0,0 +1,94 @@
import React, { Component } from 'react';
import Link from 'gatsby-link';
import classnames from 'classnames';
import DocSearch from './docsearch';
import logo from '../img/netlify-cms-logo.svg';
import '../css/imports/header.css';
class Header extends Component {
state = {
scrolled: false
};
componentDidMount() {
// TODO: use raf to throttle events
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll = event => {
const currentWindowPos =
document.documentElement.scrollTop || document.body.scrollTop;
const scrolled = currentWindowPos > 0;
this.setState({
scrolled
});
};
render() {
const { location } = this.props;
const { scrolled } = this.state;
const isDocs = location.pathname.indexOf('docs') !== -1;
const isBlog = location.pathname.indexOf('blog') !== -1;
return (
<header
id="header"
className={classnames({
docs: isDocs,
blog: isBlog,
scrolled
})}
>
<div className="contained">
<div className="logo-container">
<Link to="/" className="logo">
<img src={logo} />
</Link>
{isDocs && <DocSearch />}
</div>
<div className="nav-container">
<Link className="nav-link docs-link" to="/docs/intro">
Docs
</Link>
<Link
className="nav-link contributing-link"
to="/docs/contributor-guide"
>
Contributing
</Link>
<Link className="nav-link" to="/community">
Community
</Link>
<Link className="nav-link" to="/blog">
Blog
</Link>
<span className="gh-button">
<a
id="ghstars"
className="github-button"
href="https://github.com/netlify/netlify-cms"
data-icon="octicon-star"
data-show-count="true"
aria-label="Star netlify/netlify-cms on GitHub"
>
Star
</a>
</span>
</div>
</div>
</header>
);
}
}
export default Header;

View File

@ -0,0 +1,11 @@
import React, { Fragment } from 'react';
import Markdown from 'react-markdown';
const Markdownify = ({ source }) => (
<Markdown
source={source}
renderers={{ root: Fragment, paragraph: Fragment }}
/>
);
export default Markdownify;

View File

@ -0,0 +1,32 @@
import React, { Component } from 'react';
class MobileNav extends Component {
handleChange = event => {
this.props.history.push(event.target.value);
};
render() {
const { items } = this.props;
return (
<div className="mobile docs-nav">
<select className="btn-primary" onChange={this.handleChange}>
<option>Select A Topic</option>
{items.map(item => (
<optgroup label={item.title} key={item.title}>
{item.group.edges.map(({ node }) => (
<option
value={node.fields.slug}
key={node.fields.slug}
className="nav-link">
{node.frontmatter.title}
</option>
))}
</optgroup>
))}
</select>
</div>
);
}
}
export default MobileNav;

View File

@ -0,0 +1,41 @@
import React, { Component } from 'react';
import screenshotEditor from '../img/screenshot-editor.jpg';
class VideoEmbed extends Component {
state = {
toggled: false
};
toggleVideo = event => {
this.setState({
toggled: true
});
};
render() {
const { toggled } = this.state;
const embedcode = (
<iframe
width={560}
height={315}
src="https://www.youtube-nocookie.com/embed/p6h-rYSVX90?rel=0&showinfo=0&autoplay=1"
frameBorder={0}
allow="autoplay; encrypted-media"
allowFullScreen
/>
);
const imgPlaceholder = (
<img src={screenshotEditor} className="responsive" />
);
return (
<a className="hero-graphic" onClick={this.toggleVideo}>
{toggled ? embedcode : imgPlaceholder}
{!toggled && <div className="hero-videolink">&#x25b6;</div>}
</a>
);
}
}
export default VideoEmbed;

View File

@ -0,0 +1,90 @@
import React, { Component } from 'react';
import classnames from 'classnames';
import '../css/imports/widgets.css';
class Widgets extends Component {
state = {
currentWidget: null
};
componentDidMount() {
const { widgets } = this.props;
const hash = window.location.hash
? window.location.hash.replace('#', '')
: '';
const widgetsContainHash = widgets.edges.some(
w => w.node.frontmatter.target === hash
);
if (widgetsContainHash) {
return this.setState({
currentWidget: hash
});
}
this.setState({
currentWidget: widgets.edges[0].node.frontmatter.target
});
}
handleWidgetChange = (event, target) => {
event.preventDefault();
this.setState(
{
currentWidget: target
},
() => {
history.pushState(null, null, `#${target}`);
}
);
};
render() {
const { widgets } = this.props;
const { currentWidget } = this.state;
return (
<div>
<section className="widgets">
<div className="widgets__cloud">
{widgets.edges.map(({ node }) => {
const { label, target } = node.frontmatter;
return (
<button
key={target}
className={classnames('widgets__item', {
widgets__item_active: currentWidget === target
})}
onClick={event => this.handleWidgetChange(event, target)}
>
{label}
</button>
);
})}
</div>
<div className="widgets__container">
{widgets.edges.map(({ node }) => {
const { label, target } = node.frontmatter;
return (
<div
key={label}
className={classnames('widget', {
widget_open: currentWidget === target
})}
>
<h3>{label}</h3>
<div dangerouslySetInnerHTML={{ __html: node.html }} />
</div>
);
})}
</div>
</section>
</div>
);
}
}
export default Widgets;