convert website from hugo to gatsby (#1369)
This commit is contained in:
committed by
Shawn Erquhart
parent
d6c03707d8
commit
04c44518b2
70
website/src/components/docs-nav.js
Normal file
70
website/src/components/docs-nav.js
Normal 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;
|
39
website/src/components/docsearch.js
Normal file
39
website/src/components/docsearch.js
Normal 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;
|
30
website/src/components/edit-link.js
Normal file
30
website/src/components/edit-link.js
Normal 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;
|
71
website/src/components/event-widget.js
Normal file
71
website/src/components/event-widget.js
Normal 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;
|
36
website/src/components/footer.js
Normal file
36
website/src/components/footer.js
Normal 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;
|
94
website/src/components/header.js
Normal file
94
website/src/components/header.js
Normal 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;
|
11
website/src/components/markdownify.js
Normal file
11
website/src/components/markdownify.js
Normal 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;
|
32
website/src/components/mobile-nav.js
Normal file
32
website/src/components/mobile-nav.js
Normal 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;
|
41
website/src/components/video-embed.js
Normal file
41
website/src/components/video-embed.js
Normal 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">▶</div>}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default VideoEmbed;
|
90
website/src/components/widgets.js
Normal file
90
website/src/components/widgets.js
Normal 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;
|
Reference in New Issue
Block a user