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;

View File

@ -145,6 +145,12 @@ pre {
line-height: 1 !important;
}
/* fixes overflowing code example in widgets */
.widget pre {
box-sizing: border-box;
width: 100%;
}
.code,
code {
font-family: 'Roboto Mono', monospace !important;

View File

@ -202,7 +202,6 @@
h2:not(:first-child) {
margin-top: $medium;
}
p {
line-height: 1.7;
}
@ -226,18 +225,13 @@
border-radius: $borderRadius;
}
code {
:not(pre) > code {
color: inherit;
background: $lightestGrey;
border-radius: 2px;
padding: 2px 6px;
white-space: nowrap;
}
pre > code {
background: initial;
padding: initial;
white-space: inherit;
}
}
.blog-content h1 {

View File

@ -15,7 +15,7 @@
em {
font-style: normal;
color: #8B8B8B;
color: #8b8b8b;
padding: 0 8px;
}
@ -71,7 +71,7 @@ header {
position: absolute;
padding: $medium 0;
text-align: center;
transition: background .2s ease, padding .2s ease, box-shadow .2s ease;
transition: background 0.2s ease, padding 0.2s ease, box-shadow 0.2s ease;
width: 100%;
z-index: 100;
@ -102,14 +102,12 @@ header {
}
}*/
/*.utility-input {
@media screen and (max-width: 767px) {
display: block;
height: $small;
margin: $tiny 0 0 0;
}
@media screen and (min-width: 487px) and (max-width: 767px) {
margin-top: 32px;
}
@ -131,7 +129,8 @@ header {
}
}
.nav-link + iframe {
.gh-button {
display: inline-block;
margin-left: $small;
vertical-align: middle;
@media screen and (max-width: $mobile) {
@ -184,7 +183,6 @@ header {
margin-top: -35px;
}
}
}
.utility-input {
@ -194,7 +192,7 @@ header {
top: -4px;
width: auto;
border-radius: 4px;
background-color: rgba(255,255,255,0.1);
background-color: rgba(255, 255, 255, 0.1);
color: white;
margin: 0 0 0 $tiny;
padding-top: -5px;
@ -204,7 +202,7 @@ header {
font-weight: 600;
font-size: 16px;
line-height: 24px;
transition: all .2s ease-in-out;
transition: all 0.2s ease-in-out;
@media screen and (min-width: 1024px) {
display: inline;
@ -218,13 +216,17 @@ header {
&::-webkit-input-placeholder,
&:-moz-placeholder,
&::-moz-placeholder,
&:-ms-input-placeholder {
&:-ms-input-placeholder {
font-size: $tiny;
font-weight: $semibold;
text-align: left;
text-decoration: none;
line-height: $small;
}
img {
margin-right: 4px;
}
}
.logo-container {

View File

@ -1,65 +1,71 @@
.widgets {
margin: 2rem 0;
margin: 2rem 0;
}
.widgets__cloud {
margin: $micro calc(-$micro / 2);
margin: $micro calc(-$micro / 2);
}
.widgets__item {
color: $darkGrey;
border: 2px solid $darkGreen;
border-radius: $borderRadius;
padding: calc($micro /2) $micro;
margin: calc($micro / 2);
cursor: pointer;
transition: color .2s ease, background .2s ease;
display: inline-block;
font-weight: normal;
font-family: inherit;
font-size: inherit;
line-height: inherit;
background: transparent;
border: 0;
color: $darkGrey;
border: 2px solid $darkGreen;
border-radius: $borderRadius;
padding: calc($micro / 2) $micro;
margin: calc($micro / 2);
cursor: pointer;
transition: color 0.2s ease, background 0.2s ease;
display: inline-block;
font-weight: normal;
}
.widgets__item:hover, .widgets__item_active {
background: $darkGreen;
color: white !important;
.widgets__item:hover,
.widgets__item_active {
background: $darkGreen;
color: white !important;
}
.widgets__container {
margin: 1em 0;
background: $lightGrey;
border-radius: $borderRadius;
transition: height .15s ease;
margin: 1em 0;
background: $lightGrey;
border-radius: $borderRadius;
transition: height 0.15s ease;
}
.widget {
padding: .5em 1em;
display: none;
padding: 0.5em 1em;
display: none;
}
.widget_open {
display: block;
display: block;
}
.widget_closing {
display: block;
animation: widgetOpacity .15s ease forwards reverse;
display: block;
animation: widgetOpacity 0.15s ease forwards reverse;
}
.widget_opening {
display: block;
position: absolute;
top: 0;
left: 0;
opacity: 0;
animation: widgetOpacity .15s .05s ease forwards;
display: block;
position: absolute;
top: 0;
left: 0;
opacity: 0;
animation: widgetOpacity 0.15s 0.05s ease forwards;
}
@keyframes widgetOpacity {
from {
opacity: 0
}
to {
opacity: 1
}
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.widget pre {
@ -68,5 +74,5 @@
}
.widget h3 {
margin-top: 0 !important;
}
margin-top: 0 !important;
}

View File

@ -1,13 +0,0 @@
@import "imports/base.css";
@import "imports/utilities.css";
@import "imports/header.css";
@import "imports/hero.css";
@import "imports/cta.css";
@import "imports/whatsnew.css";
@import "imports/editors.css";
@import "imports/community.css";
@import "imports/collab.css";
@import "imports/docs.css";
@import "imports/widgets.css";
@import "imports/footer.css";
@import "imports/gitter.css";

86
website/src/html.js Normal file
View File

@ -0,0 +1,86 @@
import React from 'react';
import Gitter from 'react-sidecar';
let stylesStr;
if (process.env.NODE_ENV === `production`) {
try {
stylesStr = require('!raw-loader!../public/styles.css');
} catch (e) {
console.log(e);
}
}
const JS_NPM_URLS = [
'https://buttons.github.io/buttons.js',
'//unpkg.com/docsearch.js@2.4.1/dist/cdn/docsearch.min.js'
];
module.exports = class HTML extends React.Component {
render() {
let css;
if (process.env.NODE_ENV === 'production') {
css = (
<style
id="gatsby-inlined-css"
dangerouslySetInnerHTML={{ __html: stylesStr }}
/>
);
}
const js = JS_NPM_URLS.map(src => <script key={src} src={src} />);
return (
<html {...this.props.htmlAttributes}>
<head>
<meta charSet="utf-8" />
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/img/favicon/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
href="/img/favicon/favicon-32x32.png"
sizes="32x32"
/>
<link
rel="icon"
type="image/png"
href="/img/favicon/favicon-16x16.png"
sizes="16x16"
/>
<link
rel="mask-icon"
href="/img/favicon/safari-pinned-tab.svg"
color="#96cf05"
/>
<meta name="apple-mobile-web-app-title" content="NetlifyCMS" />
<meta name="application-name" content="NetlifyCMS" />
{this.props.headComponents}
{css}
<link
rel="stylesheet"
href="https://unpkg.com/docsearch.js@2.5.2/dist/cdn/docsearch.min.css"
/>
</head>
<body {...this.props.bodyAttributes}>
{this.props.preBodyComponents}
<div
key={'body'}
id="___gatsby"
dangerouslySetInnerHTML={{ __html: this.props.body }}
/>
{this.props.postBodyComponents}
<Gitter room="netlify/NetlifyCMS" />
{js}
</body>
</html>
);
}
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#222222</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,18 +0,0 @@
{
"name": "NetlifyCMS",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -1,98 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="16.000000pt" height="16.000000pt" viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,16.000000) scale(0.002286,-0.002286)"
fill="#000000" stroke="none">
<path d="M2750 6290 c-393 -390 -712 -711 -710 -714 3 -2 196 -85 430 -184
234 -99 447 -189 473 -201 49 -21 49 -21 76 -2 74 53 141 66 299 58 33 -2 33
-2 318 438 156 242 288 447 294 455 15 22 145 223 153 237 4 6 -113 131 -298
315 -168 166 -309 304 -313 305 -4 1 -329 -317 -722 -707z"/>
<path d="M4193 6162 c-23 -37 -92 -143 -152 -237 -60 -93 -200 -311 -311 -483
-111 -172 -204 -321 -207 -331 -3 -11 2 -30 11 -42 8 -13 22 -51 30 -83 9 -32
17 -60 19 -62 3 -4 76 -35 227 -99 69 -29 275 -116 459 -194 183 -78 339 -140
345 -137 70 35 110 64 111 81 1 11 5 36 8 55 4 19 9 53 12 75 3 22 8 51 10 65
3 14 7 39 9 55 3 17 14 86 25 155 12 69 23 143 27 165 3 22 7 51 10 65 5 32
33 204 40 250 3 19 10 55 14 80 6 31 5 50 -3 60 -20 24 -627 625 -635 627 -4
1 -26 -28 -49 -65z"/>
<path d="M1495 5040 c-209 -209 -379 -383 -378 -388 2 -7 418 -619 440 -647 4
-5 20 -28 35 -50 15 -22 103 -152 197 -289 l170 -248 109 -2 108 -1 88 135
c48 74 109 169 136 210 26 41 167 259 313 485 l264 409 -33 49 c-19 27 -41 73
-50 103 -15 52 -21 144 -11 161 11 17 -10 31 -108 73 -55 23 -279 118 -497
211 -218 93 -398 169 -400 169 -2 0 -174 -171 -383 -380z"/>
<path d="M5066 5373 c-7 -37 -66 -399 -72 -440 -2 -18 -11 -72 -19 -120 -9
-49 -18 -104 -21 -123 -3 -19 -7 -46 -9 -60 -17 -105 -20 -93 38 -137 l53 -39
43 18 c24 10 57 25 75 33 17 7 119 51 226 95 107 45 209 88 225 95 17 8 52 23
79 35 l50 22 -327 324 c-180 178 -329 324 -331 324 -2 0 -7 -12 -10 -27z"/>
<path d="M3491 4679 c-67 -81 -156 -122 -270 -123 l-63 -1 -234 -360 c-129
-198 -238 -367 -242 -375 -4 -8 -41 -67 -82 -130 -202 -308 -234 -359 -227
-367 5 -4 191 70 342 138 33 14 333 142 1165 496 58 25 119 51 135 58 17 7
127 55 245 105 118 50 218 94 221 98 4 4 9 26 12 49 l5 41 -77 33 c-42 18
-101 43 -131 57 -30 13 -93 40 -140 59 -47 19 -206 86 -353 149 -148 63 -269
114 -270 114 -1 0 -17 -19 -36 -41z"/>
<path d="M5755 4534 c-71 -31 -215 -92 -320 -136 -264 -112 -285 -122 -282
-131 1 -5 93 -47 202 -93 110 -46 209 -88 220 -94 11 -5 81 -34 155 -66 74
-31 149 -62 165 -69 17 -8 75 -32 130 -55 55 -23 114 -47 130 -55 204 -91 822
-345 834 -343 11 3 -59 78 -209 227 -124 122 -371 368 -551 547 -179 178 -329
324 -335 324 -5 0 -67 -26 -139 -56z"/>
<path d="M720 4279 c-454 -445 -720 -713 -712 -720 4 -3 99 -25 212 -48 243
-50 814 -169 850 -177 35 -8 61 -13 95 -20 17 -2 46 -9 65 -14 19 -5 50 -12
68 -15 18 -3 65 -12 105 -21 39 -8 81 -17 92 -19 11 -3 52 -11 90 -19 116 -25
109 -26 153 33 l41 54 -43 61 c-23 34 -130 187 -236 341 -428 620 -447 648
-459 662 -6 7 -16 24 -23 38 -6 14 -14 25 -17 25 -4 0 -15 14 -24 30 -10 17
-21 30 -25 30 -4 0 -109 -99 -232 -221z"/>
<path d="M5030 4045 c-31 -33 -94 -70 -147 -85 -49 -15 -48 -13 -68 -155 -4
-22 -8 -47 -10 -55 -2 -8 -7 -35 -10 -60 -3 -25 -10 -67 -15 -95 -5 -27 -11
-66 -14 -85 -3 -19 -12 -75 -20 -125 -21 -124 -65 -392 -72 -439 -3 -21 -7
-46 -9 -55 -19 -102 -19 -98 24 -147 26 -29 47 -68 58 -103 9 -31 21 -61 27
-67 6 -6 58 -21 116 -32 58 -11 193 -39 300 -62 107 -22 209 -43 225 -46 17
-3 46 -9 65 -14 29 -7 102 -23 215 -46 11 -2 31 -6 45 -9 14 -3 43 -8 65 -11
l40 -5 490 487 490 487 -45 19 c-25 11 -209 89 -410 174 -201 85 -374 158
-385 163 -11 5 -41 18 -67 30 -26 12 -50 21 -53 21 -2 0 -28 11 -57 24 -77 35
-744 316 -751 316 -2 0 -14 -11 -27 -25z"/>
<path d="M4465 3985 c-128 -56 -140 -61 -325 -140 -85 -36 -168 -72 -185 -79
-16 -8 -75 -33 -130 -56 -154 -65 -1196 -509 -1230 -524 -16 -8 -54 -24 -82
-36 -48 -20 -53 -25 -53 -54 0 -17 4 -36 8 -43 6 -10 143 -43 272 -67 14 -2
81 -16 150 -31 69 -14 136 -29 150 -31 60 -12 221 -45 290 -59 243 -51 408
-85 430 -89 14 -3 34 -7 45 -9 11 -3 103 -22 204 -43 l184 -37 19 24 c56 73
132 123 200 131 35 5 38 9 44 50 3 18 14 87 25 153 23 141 26 157 34 210 3 23
8 52 10 65 3 14 10 54 15 90 18 111 51 314 55 340 3 14 12 73 21 133 16 102
16 108 -1 127 -24 27 -35 25 -150 -25z"/>
<path d="M138 3305 c-8 -9 652 -660 665 -657 7 2 172 71 367 154 195 83 374
158 398 167 23 9 42 22 42 28 0 7 -3 13 -7 14 -13 1 -39 6 -266 53 -120 25
-228 48 -240 50 -66 13 -186 38 -217 46 -19 5 -48 11 -65 14 -50 9 -86 16
-180 36 -49 11 -101 22 -115 24 -14 3 -101 21 -195 40 -186 39 -179 38 -187
31z"/>
<path d="M2406 2816 c-17 -27 -25 -13 106 -201 30 -44 111 -161 180 -260 68
-99 140 -202 159 -230 19 -27 86 -124 149 -215 63 -91 274 -397 470 -680 196
-283 387 -560 425 -615 38 -55 70 -102 72 -105 2 -2 28 20 58 50 46 46 56 62
60 100 3 25 7 52 10 60 2 8 7 35 10 60 3 25 12 81 20 125 7 44 16 100 20 125
3 25 10 68 15 95 5 28 12 68 15 90 3 23 8 52 10 65 3 14 8 43 11 65 2 22 18
117 34 210 15 94 31 186 34 205 22 145 77 478 80 493 3 12 -7 23 -34 37 -47
24 -103 89 -127 146 -21 51 -13 48 -218 88 -157 32 -251 51 -350 72 -42 9
-121 26 -145 30 -14 2 -56 11 -95 20 -65 14 -136 29 -195 40 -14 3 -59 12
-100 20 -41 9 -138 29 -215 44 -77 16 -205 43 -285 59 -80 17 -148 31 -151 31
-4 0 -14 -11 -23 -24z"/>
<path d="M1420 2684 c-162 -69 -330 -140 -372 -157 -43 -18 -78 -37 -78 -42 0
-16 360 -366 370 -360 4 3 35 49 69 102 34 54 113 177 176 273 169 259 168
256 149 286 -9 13 -17 24 -18 24 0 0 -134 -57 -296 -126z"/>
<path d="M2185 2642 c-43 -16 -193 -21 -237 -8 -25 7 -29 3 -85 -83 -32 -51
-89 -140 -128 -200 -38 -59 -86 -132 -104 -162 -19 -30 -59 -90 -88 -135 -29
-45 -53 -86 -53 -91 0 -10 424 -435 940 -943 113 -111 388 -383 611 -606 224
-222 412 -405 418 -406 6 -2 89 75 185 171 l175 174 -22 32 c-12 18 -50 73
-83 122 -34 48 -116 167 -182 263 -66 96 -135 195 -152 220 -38 54 -241 348
-351 510 -44 63 -93 135 -110 160 -18 25 -176 254 -352 510 -175 256 -324 470
-331 476 -8 8 -22 6 -51 -4z"/>
<path d="M4683 2343 c-9 -13 -40 -38 -68 -54 l-52 -29 -22 -133 c-11 -72 -23
-143 -25 -157 -6 -32 -37 -228 -41 -255 -2 -11 -26 -157 -53 -325 -28 -168
-53 -323 -56 -345 -4 -22 -11 -68 -17 -103 -12 -67 -8 -83 14 -61 419 410
1287 1276 1287 1284 0 6 -73 25 -162 43 -90 19 -172 35 -183 38 -11 2 -54 11
-95 19 -41 8 -86 18 -100 21 -14 3 -36 7 -50 10 -14 3 -88 18 -165 34 -210 43
-193 42 -212 13z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -1,135 +0,0 @@
// Sticky Nav Functionality in Vanilla JS
var header = $("#header");
if ($('.landing.page, .community.page').length) {
window.onscroll = function() {
var currentWindowPos = document.documentElement.scrollTop || document.body.scrollTop;
if (currentWindowPos > 0) {
header.addClass('scrolled');
} else {
header.removeClass('scrolled');
}
};
}
// Auto Indexing Docs Nav
var indexDocsSubsections = function() {
if ($('.docs.page').length) {
if ($('#docs-content h2').length > 1) {
$('<ul class="nav-subsections" id="nav-subsections"></ul>').insertAfter($('#docs-nav .nav-link.active'));
$('#docs-content h2').each(function() {
var sectionTitle = $(this).html(),
anchor = $(this).attr("id");
$('#nav-subsections').append('<li><a class="subnav-link for-' + anchor + '" href="#' + anchor + '">' + sectionTitle + '</a></li>');
});
$.localScroll({
offset: -120,
duration:200,
hash:true
});
}
$(window).on('scroll', function(){
window.requestAnimationFrame(scrollHandler);
function scrollHandler() {
var scrollTop = $(window).scrollTop(),
windowHeight = $(window).height(),
first = false,
allSubnavLinks = $("#docs-nav .subnav-link");
$("#docs-content h2").each( function() {
var offset = $(this).offset(),
thisLink = '.for-' + $(this).attr("id");
if (scrollTop <= offset.top && ($(this).height() + offset.top) < (scrollTop + windowHeight) && first == false) {
allSubnavLinks.removeClass('active');
$(thisLink).addClass('active');
first=true;
} else {
first=false;
}
});
}
});
}
}
indexDocsSubsections();
var docsMobileMenu = function() {
if ($('.docs.page').length) {
$("#mobile-docs-nav").change(function() {
window.location = $(this).find("option:selected").val();
});
}
}
docsMobileMenu();
// search
$('#search-button').click(function() {
console.log("clicked")
$('.search-icon').toggleClass('active');
$('.algolia-search').toggleClass('closed');
});
// eventbrite info
var eventInfoLoad = function() {
if ($('.community.page').length) {
var eventRequest = new XMLHttpRequest;
var eventbriteToken = 'C5PX65CJBVIXWWLNFKLO';
var eventbriteOrganiser = '14281996019';
eventRequest.open('GET', 'https://www.eventbriteapi.com/v3/events/search/?token=' + eventbriteToken + '&organizer.id=' + eventbriteOrganiser + '&expand=venue%27', true);
eventRequest.onload = function() {
if (eventRequest.status >= 200 && eventRequest.status < 400) {
// Success!
var data = JSON.parse(eventRequest.responseText);
var upcomingDate = data.events[0].start.utc;
updateDate(upcomingDate);
} else {
var upcomingDate = "0000-00-00T00:00:00Z";
updateDate(upcomingDate);
}
};
eventRequest.onerror = function() {
alert('The event info could not be loaded at this time, please try again later.');
};
function updateDate(eventDate) {
const eventDateMoment = moment(eventDate);
const offset = eventDateMoment.isDST() ? -7 : -8;
$('.month').append(eventDateMoment.format('MMMM'));
$('.day').append(eventDateMoment.format('DD'));
$('.calendar-cta h3 strong:first-child()').append(eventDateMoment.format('dddd, MMMM Do'));
$('.calendar-cta h3 strong:last-child()').append(`${eventDateMoment.utcOffset(offset).format('h a')} PT`);
}
eventRequest.send();
}
}
eventInfoLoad();
// Load inline YouTube video
var embedcode = '<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/p6h-rYSVX90?rel=0&amp;showinfo=0&amp;autoplay=1" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>';
$('.hero-graphic').click(function() {
$('.hero-graphic img').replaceWith(embedcode);
$('.hero-videolink').remove();
});

View File

@ -0,0 +1,66 @@
import React, { Fragment } from 'react';
import Helmet from 'react-helmet';
import classnames from 'classnames';
import Header from '../components/header';
import Footer from '../components/footer';
import '../css/imports/base.css';
import '../css/imports/utilities.css';
import '../css/imports/gitter.css';
const Layout = ({ data, location, children }) => {
const { title, description } = data.site.siteMetadata;
const notifs = data.notifs.notifications.filter(notif => notif.published);
return (
<Fragment>
<Helmet defaultTitle={title} titleTemplate={`%s | ${title}`}>
<meta name="description" content={description} />
</Helmet>
{notifs.map((node, i) => (
<a
key={i}
href={node.url}
className={classnames('notification', {
'notification-loud': node.loud
})}
>
{node.message}
</a>
))}
<Header location={location} notifications={notifs} />
{children()}
<Footer buttons={data.dataYaml.footer.buttons} />
</Fragment>
);
};
export const pageQuery = graphql`
query layoutQuery {
site {
siteMetadata {
title
description
}
}
dataYaml(id: { regex: "/global/" }) {
footer {
buttons {
url
name
}
}
}
notifs: dataYaml(id: { regex: "/notifications/" }) {
notifications {
published
loud
message
url
}
}
}
`;
export default Layout;

58
website/src/pages/blog.js Normal file
View File

@ -0,0 +1,58 @@
import React from 'react';
import Helmet from 'react-helmet';
import Link from 'gatsby-link';
const Blog = ({ data }) => (
<div className="blog page">
<Helmet>
<title>Blog</title>
<meta
name="description"
content="Recent news and updates about Netlify CMS."
/>
</Helmet>
<div className="container">
<h1>Netlify CMS Blog</h1>
{data.allMarkdownRemark.edges.map(({ node }) => (
<article className="blog-list-item" key={node.id}>
<h2>
<Link to={node.fields.slug} className="article">
{node.frontmatter.title}
</Link>
</h2>
<p className="meta-info">
by {node.frontmatter.author} on {node.frontmatter.date}
</p>
<p>{node.frontmatter.description}</p>
</article>
))}
{/* TODO: pagination */}
</div>
</div>
);
export default Blog;
export const pageQuery = graphql`
query blogList {
allMarkdownRemark(
filter: { fields: { slug: { regex: "/blog/" } } }
sort: { order: DESC, fields: [fields___date] }
) {
edges {
node {
id
frontmatter {
title
description
author
date(formatString: "MMMM D, YYYY")
}
fields {
slug
}
}
}
}
}
`;

View File

@ -0,0 +1,87 @@
import React, { Component } from 'react';
import Helmet from 'react-helmet';
import Markdown from 'react-markdown';
import EventWidget from '../components/event-widget';
import Markdownify from '../components/markdownify';
import '../css/imports/collab.css';
const CommunityPage = ({ data }) => {
const {
title,
headline,
subhead,
primarycta,
upcomingevent,
howitworks,
howtojoin
} = data.markdownRemark.frontmatter;
return (
<div className="community page">
<Helmet title={title} />
<section className="community hero">
<div className="contained">
<div className="hero-copy">
<h1 className="headline">
<Markdownify source={headline} />
</h1>
<h2 className="subhead">
<Markdownify source={subhead} />
</h2>
<h3 className="ctas">
<ul>
<li>
<Markdownify source={primarycta} />
</li>
</ul>
</h3>
</div>
<div className="calendar-cta">
<h2>{upcomingevent.hook}</h2>
<EventWidget />
<div className="cal-cta">
<Markdownify source={primarycta} />
</div>
</div>
</div>
</section>
<section className="how-it-works clearfix">
<div className="contained">
<div className="half">
<h4 className="section-label">How it works</h4>
<p>
<Markdown source={howitworks} />
</p>
<h4 className="section-label">How to join</h4>
<p>
<Markdown source={howtojoin} />
</p>
</div>
</div>
</section>
</div>
);
};
export const pageQuery = graphql`
query communityPage {
markdownRemark(id: { regex: "/community/" }) {
frontmatter {
headline
subhead
primarycta
upcomingevent {
hook
}
howitworks
howtojoin
}
}
}
`;
export default CommunityPage;

188
website/src/pages/index.js Normal file
View File

@ -0,0 +1,188 @@
import React, { Component, Fragment } from 'react';
import moment from 'moment';
import Markdownify from '../components/markdownify';
import VideoEmbed from '../components/video-embed';
import '../css/imports/hero.css';
import '../css/imports/cta.css';
import '../css/imports/whatsnew.css';
import '../css/imports/editors.css';
import '../css/imports/community.css';
const Features = ({ items }) => (
<Fragment>
{items.map(item => (
<div className="feature" key={item.feature}>
{item.imgpath && <img src={require(`../img/${item.imgpath}`)} />}
<h3>
<Markdownify source={item.feature} />
</h3>
<p>
<Markdownify source={item.description} />
</p>
</div>
))}
</Fragment>
);
const HomePage = ({ data }) => {
const { landing, updates, contribs } = data;
return (
<div className="landing page">
<section className="landing hero">
<div className="contained">
<div className="hero-copy">
<h1 className="headline">
<Markdownify source={landing.hero.headline} />
</h1>
<span className="subhead">
<Markdownify source={landing.hero.subhead} />
</span>
<span className="cta-header">
<Markdownify source={landing.cta.button} />
</span>
</div>
<div className="hero-features">
<Features items={landing.hero.devfeatures} />
</div>
<VideoEmbed />
</div>
</section>
<section className="cta">
<div className="cta-primary">
<p>
<span className="hook">
<Markdownify source={landing.cta.primaryhook} />
</span>{' '}
<Markdownify source={landing.cta.primary} />
</p>
<Markdownify source={landing.cta.button} />
</div>
</section>
<section className="whatsnew">
<div className="contained">
<ol>
{updates.updates.slice(0, 3).map(node => (
<a
href={`https://github.com/netlify/netlify-cms/releases/tag/${
node.version
}`}
key={node.version}
>
<li>
<div className="update-metadata">
<span className="update-version">{node.version}</span>
<span className="update-date">
{moment(node.date).format('MMMM D, YYYY')}
</span>
</div>
<span className="update-description">
<Markdownify source={node.description} />
</span>
</li>
</a>
))}
</ol>
</div>
</section>
<section className="editors">
<div className="contained">
<h2>
<Markdownify source={landing.editors.hook} />
</h2>
<p id="editor-intro">
<Markdownify source={landing.editors.intro} />
</p>
<div className="editors-features">
<Features items={landing.editors.features} />
</div>
</div>
</section>
<section className="communitysupport">
<div className="contained">
<h2>
<Markdownify source={landing.community.hook} />
</h2>
<div className="community">
<div className="community-features">
<Features items={landing.community.features} />
</div>
</div>
<div className="contributors feature">
<h3>{landing.community.contributors}</h3>
<div className="contributor-list">
{contribs.contributors.map(user => (
<a href={user.profile} title={user.name} key={user.login}>
<img
src={user.avatar_url.replace('v=4', 's=32')}
alt={user.login}
/>
</a>
))}
</div>
</div>
</div>
</section>
</div>
);
};
export const pageQuery = graphql`
query homeQuery {
updates: dataYaml(id: { regex: "/updates/" }) {
updates {
date
description
version
}
}
landing: dataYaml(id: { regex: "/landing/" }) {
hero {
headline
subhead
devfeatures {
feature
description
}
}
cta {
primary
primaryhook
button
}
editors {
hook
intro
features {
feature
imgpath
description
}
}
community {
hook
features {
feature
description
}
contributors
}
}
contribs: dataJson(id: { regex: "/contributors/" }) {
contributors {
name
profile
avatar_url
login
}
}
}
`;
export default HomePage;

View File

@ -0,0 +1,46 @@
import React from 'react';
import Helmet from 'react-helmet';
const BlogPost = ({ data }) => {
const { html, frontmatter } = data.markdownRemark;
const { author, title, date, description, meta_description } = frontmatter;
const desc = meta_description || description;
return (
<div className="docs page">
<Helmet>
<title>{title}</title>
{desc && <meta name="description" content={desc} />}
</Helmet>
<div className="container">
<article className="blog-content" id="blog-content">
<div className="blog-post-header">
<h1>{title}</h1>
<p className="meta-info">
by {author} on {date}
</p>
</div>
<div dangerouslySetInnerHTML={{ __html: html }} />
</article>
</div>
</div>
);
};
export default BlogPost;
export const pageQuery = graphql`
query blogPost($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
frontmatter {
title
description
# meta_description
date(formatString: "MMMM D, YYYY")
author
}
html
}
}
`;

View File

@ -0,0 +1,108 @@
import React from 'react';
import Helmet from 'react-helmet';
import { matchPath } from 'react-router-dom';
import EditLink from '../components/edit-link';
import Widgets from '../components/widgets';
import DocsNav from '../components/docs-nav';
import MobileNav from '../components/mobile-nav';
import '../css/lib/prism.css';
import '../css/imports/docs.css';
const toMenu = (menu, nav) =>
menu.map(group => ({
title: group.title,
group: nav.group.find(g => g.fieldValue === group.name)
}));
const DocPage = ({ data, location, history }) => {
const { nav, page, widgets, menu } = data;
const docsNav = toMenu(menu.siteMetadata.menu.docs, nav);
const showWidgets = matchPath(location.pathname, { path: '/docs/widgets' });
return (
<div className="docs detail page">
<Helmet title={page.frontmatter.title} />
<div className="container">
<aside id="sidebar" className="sidebar">
<DocsNav items={docsNav} location={location} />
<MobileNav items={docsNav} history={history} />
</aside>
<article className="docs-content" id="docs-content">
<EditLink path={page.fields.path} />
<h1>{page.frontmatter.title}</h1>
<div dangerouslySetInnerHTML={{ __html: page.html }} />
{showWidgets && <Widgets widgets={widgets} />}
</article>
</div>
</div>
);
};
export const pageQuery = graphql`
query docPage($slug: String!) {
page: markdownRemark(fields: { slug: { eq: $slug } }) {
fields {
path
}
frontmatter {
title
}
html
}
nav: allMarkdownRemark(
sort: { fields: [frontmatter___weight], order: ASC }
filter: {
frontmatter: { title: { ne: null }, group: { ne: null } }
fields: { slug: { regex: "/docs/" } }
}
) {
group(field: frontmatter___group) {
fieldValue
edges {
node {
fields {
slug
}
frontmatter {
title
group
}
tableOfContents
}
}
}
}
menu: site {
siteMetadata {
menu {
docs {
name
title
}
}
}
}
widgets: allMarkdownRemark(
sort: { fields: [frontmatter___label], order: ASC }
filter: {
frontmatter: { label: { ne: null } }
fields: { slug: { regex: "/widgets/" } }
}
) {
edges {
node {
frontmatter {
label
target
}
html
}
}
}
}
`;
export default DocPage;

51
website/src/theme.js Normal file
View File

@ -0,0 +1,51 @@
// if you change these you must restart the server
module.exports = {
// colors
lightestGrey: '#E6E6E6',
lighterGrey: '#F7F8F8',
lightGrey: '#F6F6F6',
lightishGrey: '#51555D',
grey: '#313D3E',
darkGrey: '#2F3132',
darkerGrey: '#1E1F21',
blueGrey: '#BCC2CE',
lightGreen: '#97bf2f',
green: '#C9FA4B',
darkGreen: '#7CA511',
darkerGreen: '#628013',
shadeBlue: '#EFF0F4',
blue: '#3A69C7',
// typography
thin: 100,
light: 300,
regular: 400,
semibold: 500,
bold: 700,
black: 900,
// fonts
roboto: "'Roboto', -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif",
// padding
micro: '8px',
tiny: '16px',
small: '24px',
medium: '40px',
large: '64px',
xl: '104px',
xxl: '152px',
// border radius
borderRadius: '4px',
largeBorderRadius: '8px',
// responsive breakpoints
mobile: '480px',
tablet: '768px',
desktop: '960px',
display: '1200px',
xlarge: '1280px'
}