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

2
.gitignore vendored
View File

@ -8,5 +8,5 @@ yarn-error.log
.vscode/
manifest.yml
.imdone/
website/site/data/contributors.yml
website/data/contributors.json
/coverage/

2
.nvmrc
View File

@ -1 +1 @@
6
8

View File

@ -1,7 +1,4 @@
{
"presets": ["es2015"],
"plugins": [
"syntax-object-rest-spread",
"transform-object-rest-spread"
]
"presets": ["react", "es2015", "stage-1"],
"plugins": ["add-module-exports"]
}

5
website/.eslintrc Normal file
View File

@ -0,0 +1,5 @@
{
"globals": {
"graphql": true
}
}

3
website/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.cache
public
dist

View File

@ -1 +1 @@
node
8

View File

@ -4,7 +4,7 @@ This directory builds netlifycms.org. If you'd like to propose changes to the si
## Local development
The site is built with [Hugo](https://gohugo.io/), managed as an npm dependency via [hugo-bin](https://www.npmjs.com/package/hugo-bin).
The site is built with [GatsbyJS](https://gatsbyjs.org/).
To run the site locally, you'll need to have [Node](https://nodejs.org) and [Yarn](https://yarnpkg.com/en/) installed on your computer.
@ -15,5 +15,5 @@ yarn
yarn start
```
Then visit http://localhost:3000/ - BrowserSync will automatically reload CSS or
Then visit http://localhost:8000/ - Gatsby will automatically reload CSS or
refresh the page when stylesheets or content changes.

View File

@ -1,13 +1,9 @@
---
title: Add to Your Site
weight: 20
menu:
docs:
parent: start
group: start
---
# Add Netlify CMS to Your Site
Netlify CMS is adaptable to a wide variety of projects. It works with any content written in markdown, JSON, YAML, or TOML files, stored in a repo on [GitHub](https://github.com/), [GitLab](https://about.gitlab.com/), or [Bitbucket](https://bitbucket.org). You can also create your own custom backend.
This tutorial will guide you through the steps for adding Netlify CMS to a site that's built with a common [static site generator](https://www.staticgen.com/), like Jekyll, Hugo, Hexo, or Gatsby. Alternatively, you can [start from a template](https://www.netlifycms.org/docs/start-with-a-template) or dive right into to [configuration options](https://www.netlifycms.org/docs/configuration-options).
@ -126,7 +122,8 @@ public_folder: "/images/uploads" # The src attribute for uploaded media will beg
The configuration above adds a new setting, `public_folder`. While `media_folder` specifies where uploaded files will be saved in the repo, `public_folder` indicates where they can be found in the published site. This path is used in image `src` attributes and is relative to the file where it's called. For this reason, we usually start the path at the site root, using the opening `/`.
_If `public_folder` is not set, Netlify CMS will default to the same value as `media_folder`, adding an opening `/` if one is not included._
*If `public_folder` is not set, Netlify CMS will default to the same value as `media_folder`, adding an opening `/` if one is not included.*
### Collections

View File

@ -1,13 +1,9 @@
---
title: Architecture
weight: 90
menu:
docs:
parent: reference
position: 90
group: contributing
---
# Technical Architecture
Netlify CMS is a React application, using Redux for state management with immutable data structures (immutable.js).
The core abstractions for content editing are `collections`, `entries` and `widgets`.

View File

@ -1,11 +1,8 @@
---
title: Authentication & Backends
weight: 25
menu:
docs:
parent: start
group: start
---
# Authentication & Backends
Netlify CMS stores content in your GitHub, GitLab, or Bitbucket repository. In order for this to work, you need to authenticate with your Git host, and in most cases that requires a server. We have a few options for handling this.

View File

@ -1,11 +1,9 @@
---
title: Beta Features!
weight: 200
menu:
docs:
parent: reference
group: reference
---
# Beta Features!
We run new functionality in an open beta format from time to time. That means that this functionality is totally available for use, and we _think_ it might be ready for primetime, but it could break or change without notice.
**Use these features at your own risk.**

View File

@ -1,11 +1,8 @@
---
title: Collection Types
weight: 27
menu:
docs:
parent: start
group: start
---
# Collection Types
All editable content types are defined in the `collections` field of your `config.yml` file, and display in the left sidebar of the Content page of the editor UI.

View File

@ -1,13 +1,9 @@
---
title: Configuration Options
weight: 23
menu:
docs:
parent: reference
group: reference
---
# Configuration Options
All configuration options for Netlify CMS are specified in a `config.yml` file, in the folder where you access the editor UI (usually in the `/admin` folder).
Alternatively, you can specify a custom config file using a link tag:

View File

@ -1,13 +1,9 @@
---
title: Contributor Guide
weight: 100
menu:
docs:
parent: contributing
group: contributing
---
# Welcome, contributors!
We're hoping that Netlify CMS will do for the [JAMstack](https://www.jamstack.org) what WordPress did for dynamic sites back in the day. We know we can't do that without building a thriving community of contributors and users, and we'd love to have you join us.
While we work on building this page (and you can help!), here are some links with more information about getting involved:

View File

@ -1,11 +1,8 @@
---
title: Creating Custom Widgets
weight: 35
menu:
docs:
parent: guides
group: guides
---
# Custom Widgets
The NetlifyCMS exposes a `window.CMS` global object that you can use to register custom widgets, previews, and editor plugins. The same object is also the default export if you import Netify CMS as an npm module. The available widget extension methods are:

View File

@ -1,11 +1,8 @@
---
title: Creating Custom Previews
weight: 50
menu:
docs:
parent: guides
position: 50
group: guides
---
# Customizing the Preview Pane
The NetlifyCMS exposes a `window.CMS` global object that you can use to register custom widgets, previews and editor plugins. The available customization methods are:

View File

@ -1,13 +1,9 @@
---
title: Examples
weight: 110
menu:
docs:
parent: start
group: start
---
# Examples
Do you have a great, open source example? Submit a pull request to this page!
Example | Tools | Type | Source | More info |

View File

@ -1,13 +1,9 @@
---
title: Introduction
weight: 1
menu:
docs:
parent: start
group: start
---
# Introduction
Netlify CMS is an open source content management system for your Git workflow that enables you to provide editors with friendly UI and intuitive workflow. You can use it with any static site generator to create faster, more flexible web projects. Content is stored in your Git repository alongside your code for easier versioning, multi-channel publishing, and the option to handle content updates directly in Git.
At its core, Netlify CMS is an open-source React app that acts as a wrapper for the Git workflow, using the GitHub, GitLab, or Bitbucket API. This provides many advantages, including:

View File

@ -1,13 +1,9 @@
---
title: Start with a Template
weight: 10
menu:
docs:
parent: start
group: start
---
# Start with a Template
Netlify CMS can be [added to an existing site](https://www.netlifycms.org/docs/add-to-your-site), but the quickest way to get started is with a template. Our featured templates below deploy to Netlify, giving you a fully working CMS-enabled site with just a few clicks.
<div style="display: flex; justify-content: space-around; text-align: center; margin-bottom: 1.5em;">

View File

@ -1,13 +1,9 @@
---
title: Update the CMS Version
weight: 60
menu:
docs:
parent: start
group: start
---
# Update the CMS Version
The update procedure for your CMS depends upon the method you used to install Netlify CMS.
## Package Manager

View File

@ -1,10 +1,8 @@
---
label: "Boolean"
target: "boolean"
target: boolean
---
### Boolean
The boolean widget translates a toggle switch input to a true/false value.
- **Name:** `boolean`

View File

@ -1,10 +1,8 @@
---
label: "Date"
target: "date"
target: date
---
### Date
The date widget translates a date picker input to a date string. For saving date and time together, use the datetime widget.
- **Name:** `date`

View File

@ -1,10 +1,8 @@
---
label: "DateTime"
target: "datetime"
target: datetime
---
### DateTime
The datetime widget translates a datetime picker to a datetime string. For saving the date only, use the date widget.
- **Name:** `datetime`

View File

@ -1,10 +1,8 @@
---
label: "File"
target: "file"
target: file
---
### File
The file widget allows editors to upload a file or select an existing one from the media library. The path to the file will be saved to the field as a string.
- **Name:** `file`

View File

@ -1,10 +1,8 @@
---
label: "Hidden"
target: "hidden"
target: hidden
---
### Hidden
Hidden widgets do not display in the UI. In folder collections that allow users to create new items, you will often want to set a default for hidden fields, so they will be set without requiring an input.
- **Name:** `hidden`

View File

@ -1,10 +1,8 @@
---
label: "Image"
target: "image"
target: image
---
### Image
The image widget allows editors to upload an image or select an existing one from the media library. The path to the image file will be saved to the field as a string.
- **Name:** `image`

View File

@ -1,13 +1,9 @@
---
title: Widgets
weight: 30
menu:
docs:
parent: reference
group: reference
---
# Widgets
Widgets define the data type and interface for entry fields. Netlify CMS comes with several built-in widgets. Click the widget names in the sidebar to jump to specific widget details. Were always adding new widgets, and you can also [create your own](https://www.netlifycms.org/docs/custom-widgets)!
Widgets are specified as collection fields in the Netlify CMS `config.yml` file. Note that [YAML syntax](https://en.wikipedia.org/wiki/YAML#Basic_components) allows lists and objects to be written in block or inline style, and the code samples below include a mix of both.

View File

@ -1,10 +1,8 @@
---
label: "List"
target: "list"
target: list
---
### List
The list widget allows you to create a repeatable item in the UI which saves as a list of widget values. map a user-provided string with a comma delimiter into a list. You can choose any widget as a child of a list widget—even other lists.
- **Name:** `list`

View File

@ -1,10 +1,8 @@
---
label: "Markdown"
target: "markdown"
target: markdown
---
### Markdown
The markdown widget provides a full fledged text editor - which is based on [slate](https://github.com/ianstormtaylor/slate) - that allows users to format text with features such as headings and blockquotes. Users are also allowed to write in markdown by simply flipping a switch.
*Please note:* in case you want to use your markdown editor to fill a markdown's file content after the frontmatter, you'll have name the field as `body` so then the CMS can recognize it and save the file accordingly.

View File

@ -1,11 +1,8 @@
---
label: "Number"
target: "number"
target: number
---
### Number
The number widget uses an HTML number input, saving the value as a string, integer, or floating point number.
- **Name:** `number`

View File

@ -1,10 +1,8 @@
---
label: "Object"
target: "object"
target: object
---
### Object
The object widget allows you to group multiple widgets together, nested under a single field. You can choose any widget as a child of an object widget—even other objects.
- **Name:** `object`

View File

@ -1,10 +1,8 @@
---
label: "Relation"
target: "relation"
target: relation
---
### Relation
The relation widget allows you to reference items from another collection. It provides a search input with a list of entries from the collection you're referencing, and the list automatically updates with matched entries based on what you've typed.
- **Name:** `relation`

View File

@ -1,10 +1,8 @@
---
label: "Select"
target: "select"
target: select
---
### Select
The select widget allows you to pick a single string value from a dropdown menu.
- **Name:** `select`

View File

@ -1,10 +1,8 @@
---
label: "String"
target: "string"
target: string
---
### String
The string widget translates a basic text input to a string value. For larger textarea inputs, use the text widget.
- **Name:** `string`

View File

@ -1,10 +1,8 @@
---
label: "Text"
target: "text"
target: text
---
### Text
The text widget takes a multiline text field and saves it as a string. For shorter text inputs, use the string widget.
- **Name:** `text`

View File

@ -1,7 +1,5 @@
---
title: Community
layout: community
url: /community
headline: Be a part of building the CMS of the future.
subhead: Were serious about being community driven, so everyone is welcome to join the [community chat](https://gitter.im/netlify/NetlifyCMS), and to be a part of our bi-weekly planning sessions (details below).

View File

@ -1,4 +1,3 @@
---
styleoverrides: '/docs.css'
headline: Netlify builds, deploys, and hosts your front end.
bottomcta:
@ -10,4 +9,3 @@ bottomcta:
- type: secondary
btntext: Read our Tutorials
linksto: /tags/tutorial/
---

View File

@ -1,8 +1,6 @@
---
footer:
buttons:
- name: "Twitter"
url: "https://twitter.com/netlifycms"
- name: "GitHub"
url: "https://github.com/netlify/netlify-cms"
---

View File

@ -1,4 +1,3 @@
---
hero:
headline: "Open source content management for your Git&nbsp;workflow"
subhead: "Use Netlify CMS with any static site generator for a faster and more flexible web&nbsp;project"
@ -21,13 +20,13 @@ editors:
features:
- feature: "Editor-friendly user interface"
description: "The web-based app includes rich-text editing, real-time preview, and drag-and-drop media uploads."
imgpath: "/img/feature-editor.svg"
imgpath: "feature-editor.svg"
- feature: "Intuitive workflow for content teams"
description: "Writers and editors can easily manage content from draft to review to publish across any number of custom content types."
imgpath: "/img/feature-workflow.svg"
imgpath: "feature-workflow.svg"
- feature: "Instant access without GitHub&nbsp;account"
description: "With [Git Gateway](/docs/authentication-backends/#git-gateway-with-netlify-identity), you can add CMS access for any team member — even if they dont have a GitHub account. "
imgpath: "/img/feature-access.svg"
imgpath: "feature-access.svg"
community:
hook: "Supported by a growing&nbsp;community"
@ -40,4 +39,3 @@ community:
description: "Netlify CMS is built by a community of more than 100 contributors — and you can help. Join our [bi-weekly planning sessions](/community) or read the [contributing guide](/docs/contributor-guide) to join in."
contributors: "Made possible by awesome contributors"
---

View File

@ -0,0 +1,22 @@
notifications:
- loud: true
message: >-
Register to join us online for our next community dev meeting, every other
Wednesday at 9am-10am PT!
published: false
title: Netlify CMS Development Planning Sessions Promo
url: >-
https://www.eventbrite.com/e/netlify-cms-planning-session-bi-weekly-tickets-35794058994
- loud: true
message: >-
We have a community on Gitter - join now to ask questions and discuss the
project with other devs!
published: false
title: Gitter shoutout
url: 'https://gitter.im/netlify/netlifycms'
- loud: true
message: >-
Netlify CMS now supports GitLab!
published: true
title: GitLab announcement
url: '/blog/2018/06/netlify-cms-now-supports-gitlab-as-a-backend/'

View File

@ -45,59 +45,59 @@ updates:
- date: '2018-03-29'
description: 'More control over filenames, lots of bugfixes, and new beta features!'
version: 1.4.0
- date: 2018-03-06T00:00:00.000Z
- date: '2018-03-06'
description: Fixes styling issues
version: 1.3.5
- date: 2018-03-06T00:00:00.000Z
- date: '2018-03-06'
description: Fixes editorial workflow entry failure.
version: 1.3.4
- date: 2018-03-06T00:00:00.000Z
- date: '2018-03-06'
description: Fixes load failure
version: 1.3.3
- date: 2018-03-06T00:00:00.000Z
- date: '2018-03-06'
description: >-
Fixes date widget default format, collection load failure when entry
fails.
version: 1.3.2
- date: 2018-03-03T00:00:00.000Z
- date: '2018-03-03'
description: Fixes editorial workflow failure for unknown collections.
version: 1.3.1
- date: 2018-02-27T00:00:00.000Z
- date: '2018-02-27'
description: >-
Multi-part extensions, e.g. "en.md", a11y improvements in the editor, and
bugfixes.
version: 1.3.0
- date: 2018-02-21T00:00:00.000Z
- date: '2018-02-21'
description: Fixes ES5 transpiling.
version: 1.2.2
- date: 2018-02-21T00:00:00.000Z
- date: '2018-02-21'
description: >-
Allows label_singular config for collections and lists and distinct
frontmatter delimiters.
version: 1.2.1
- date: 2018-02-13T00:00:00.000Z
- date: '2018-02-13'
description: >-
Adds support for multiple frontmatter formats and custom delimiters, UI
improvements.
version: '1.2'
- date: 2018-01-25T00:00:00.000Z
- date: '2018-01-25'
description: You can now register external backends and we improved metadata handling.
version: '1.1'
- date: 2018-01-23T00:00:00.000Z
- date: '2018-01-23'
description: >-
Fixes various UI bugs and adds expand / collapse functionality to the
object widget.
version: 1.0.4
- date: 2018-12-19T00:00:00.000Z
- date: '2018-12-19'
description: Small bug fix release.
version: 1.0.3
- date: 2018-12-07T00:00:00.000Z
- date: '2018-12-07'
description: Small bug fix release.
version: 1.0.2
- date: 2018-12-07T00:00:00.000Z
- date: '2018-12-07'
description: Small bug fix release.
version: 1.0.1
- date: 2018-12-07T00:00:00.000Z
- date: '2018-12-07'
description: >-
The first major release of Netlify CMS with an all-new UI, revamped
documentation and much more.

10
website/gatsby-browser.js Normal file
View File

@ -0,0 +1,10 @@
import SmoothScroll from 'smooth-scroll';
// Make scroll behavior of internal links smooth
exports.onClientEntry = () => {
new SmoothScroll('a[href*="#"]', {
offset() {
return document.querySelector('#header').offsetHeight;
}
});
};

96
website/gatsby-config.js Normal file
View File

@ -0,0 +1,96 @@
const pkg = require('./package.json');
const neatgrid = require('postcss-neat');
const nestedcss = require('postcss-nested');
const colorfunctions = require('postcss-colour-functions');
const hdBackgrounds = require('postcss-at2x');
const cssextend = require('postcss-simple-extend');
const cssvars = require('postcss-simple-vars-async');
const styleVariables = require('./src/theme');
const postCssPlugins = [
neatgrid(),
nestedcss(),
colorfunctions(),
hdBackgrounds(),
cssextend(),
cssvars({ variables: styleVariables })
];
module.exports = {
siteMetadata: {
title: 'Netlify CMS | Open-Source Content Management System',
description: 'Open source content management for your Git workflow',
siteUrl: pkg.homepage,
menu: {
docs: [
{
name: 'start',
title: 'Quick Start'
},
{
name: 'guides',
title: 'Guides'
},
{
name: 'reference',
title: 'Reference'
},
{
name: 'contributing',
title: 'Contributing'
}
]
}
},
plugins: [
{
resolve: 'gatsby-source-filesystem',
options: {
path: `${__dirname}/content`,
name: 'content'
}
},
'gatsby-transformer-yaml',
'gatsby-transformer-json',
{
resolve: 'gatsby-source-filesystem',
options: {
path: `${__dirname}/data`,
name: 'data'
}
},
{
resolve: 'gatsby-transformer-remark',
options: {
// prettier-ignore
plugins: [
'gatsby-remark-autolink-headers',
'gatsby-remark-prismjs'
]
}
},
{
resolve: 'gatsby-plugin-postcss-sass',
options: {
postCssPlugins
}
},
'gatsby-plugin-react-helmet',
'gatsby-plugin-react-next',
'gatsby-plugin-catch-links',
{
resolve: `gatsby-plugin-manifest`,
options: {
name: 'NetlifyCMS',
short_name: 'NetlifyCMS',
start_url: '/',
background_color: '#ffffff',
theme_color: '#ffffff',
display: 'standalone',
icon: 'static/img/favicon/icon-512x512.png'
}
}
]
};

91
website/gatsby-node.js Normal file
View File

@ -0,0 +1,91 @@
const path = require('path');
const { createFilePath } = require('gatsby-source-filesystem');
exports.createPages = async ({ graphql, boundActionCreators }) => {
const { createPage } = boundActionCreators;
const docPage = path.resolve('./src/templates/doc-page.js');
const blogPost = path.resolve('./src/templates/blog-post.js');
// get all markdown with a frontmatter path field and title
const allMarkdown = await graphql(`
{
allMarkdownRemark(filter: { frontmatter: { title: { ne: null } } }) {
edges {
node {
fields {
slug
}
frontmatter {
title
}
}
}
}
}
`);
if (allMarkdown.errors) {
console.error(allMarkdown.errors);
throw Error(allMarkdown.errors);
}
allMarkdown.data.allMarkdownRemark.edges.forEach(({ node }) => {
const { slug } = node.fields;
let template = docPage;
if (slug.includes('blog/')) {
template = blogPost;
}
createPage({
path: slug,
component: template,
context: {
slug
}
});
});
};
const pad = n => (n >= 10 ? n : `0${n}`);
exports.onCreateNode = ({ node, boundActionCreators, getNode }) => {
const { createNodeField } = boundActionCreators;
if (node.internal.type === 'MarkdownRemark') {
const value = createFilePath({ node, getNode });
const { relativePath } = getNode(node.parent);
let slug = value;
if (relativePath.includes('blog/')) {
const date = new Date(node.frontmatter.date);
const year = date.getFullYear();
const month = date.getMonth() + 1;
const filename = path.basename(relativePath, '.md');
slug = `/blog/${year}/${pad(month)}/${filename}`;
createNodeField({
node,
name: 'date',
value: date.toJSON()
});
}
// used for doc posts
createNodeField({
node,
name: 'slug',
value: slug
});
// used to create GitHub edit link
createNodeField({
node,
name: 'path',
value: relativePath
});
}
};

View File

@ -1,128 +0,0 @@
import gulp from "gulp";
import cp from "child_process";
import hugoBin from "hugo-bin";
import gutil from "gulp-util";
import postcss from "gulp-postcss";
import transform from "gulp-transform";
import yaml from "yamljs";
import rename from "gulp-rename";
import cssImport from "postcss-import";
import neatgrid from "postcss-neat";
import nestedcss from "postcss-nested";
import colorfunctions from "postcss-colour-functions";
import hdBackgrounds from "postcss-at2x";
import cssvars from "postcss-simple-vars-async";
import cssextend from "postcss-simple-extend";
import styleVariables from "./config/variables";
import BrowserSync from "browser-sync";
import webpack from "webpack";
import webpackConfig from "./webpack.conf";
import url from "url";
// Always exit non-zero on unhandled promise rejection
process.on('unhandledRejection', err => { throw err });
const browserSync = BrowserSync.create();
const defaultArgs = ["-d", "../dist", "-s", "site", "-v"];
function buildSite(cb, options) {
const args = options ? defaultArgs.concat(options) : defaultArgs;
return cp.spawn(hugoBin, args, { stdio: "inherit" }).on("close", code => {
if (code === 0) {
browserSync.reload();
cb();
} else {
browserSync.notify("Hugo build failed :(");
cb("Hugo build failed");
}
});
}
gulp.task("hugo", ["copy"], cb => buildSite(cb));
gulp.task("hugo-preview", ["copy"], cb =>
buildSite(cb, ["--buildDrafts", "--buildFuture"])
);
gulp.task("build", ["css", "js", "images", "hugo"]);
gulp.task("build-preview", ["css", "js", "images", "hugo-preview"]);
gulp.task("css", () =>
gulp
.src("./src/css/**/*.css")
.pipe(
postcss([
cssImport({ from: "./src/css/main.css" }),
neatgrid(),
nestedcss(),
colorfunctions(),
hdBackgrounds(),
cssextend(),
cssvars({ variables: styleVariables })
])
)
.pipe(gulp.dest("./dist/css"))
.pipe(browserSync.stream())
);
gulp.task("js", cb => {
const myConfig = Object.assign({}, webpackConfig);
webpack(myConfig, (err, stats) => {
if (err) throw new gutil.PluginError("webpack", err);
gutil.log(
"[webpack]",
stats.toString({
colors: true,
progress: true
})
);
browserSync.reload();
cb();
});
});
gulp.task("images", () =>
gulp
.src("./src/img/**/*")
.pipe(gulp.dest("./dist/img"))
.pipe(browserSync.stream())
);
gulp.task("copy", () =>
gulp
.src("../.all-contributorsrc")
.pipe(
transform(
"utf8",
content =>
new Promise((resolve, reject) => {
const contributors = JSON.parse(content).contributors.map(c => {
const avatar = url.parse(c['avatar_url'], true);
avatar.search = undefined; // Override so that we can use `avatar.query`.
if (avatar.hostname.endsWith('githubusercontent.com')) {
// Use 32px avatars, instead of full-size.
avatar.query['s'] = '32';
}
const avatar_url = url.format(avatar);
return Object.assign({}, c, { avatar_url });
});
resolve(yaml.dump({ contributors }));
})
)
)
.pipe(rename("contributors.yml"))
.pipe(gulp.dest("./site/data"))
);
gulp.task("server", ["css", "js", "images", "hugo"], () => {
browserSync.init({
server: {
baseDir: "./dist"
},
notify: false
});
gulp.watch("./src/js/**/*.js", ["js"]);
gulp.watch("./src/css/**/*.css", ["css"]);
gulp.watch("./src/img/**/*", ["images"]);
gulp.watch("./site/**/*", ["hugo"]);
});

View File

@ -1,55 +1,46 @@
{
"name": "victor-hugo",
"name": "netlify-cms-site",
"version": "1.0.0",
"description": "Victor Hugo is a Hugo boilerplate for creating truly epic websites!",
"main": "index.js",
"description": "Netlify CMS documentation website built with Gatsby",
"scripts": {
"hugo": "gulp hugo",
"webpack": "gulp webpack",
"build": "gulp build",
"build-preview": "gulp build-preview",
"start": "gulp server",
"start": "gatsby develop",
"build": "gatsby build && mv public dist",
"copy:contribs": "cp ../.all-contributorsrc data/contributors.json",
"prepare": "npm run copy:contribs",
"reset": "rm -rf .cache",
"lint": "eslint src"
},
"author": "",
"homepage": "https://www.netlifycms.org/",
"license": "MIT",
"dependencies": {
"autoprefixer": "^6.3.7",
"babel-eslint": "^6.1.2",
"babel-loader": "^6.2.4",
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
"babel-plugin-transform-class-properties": "^6.10.2",
"babel-plugin-transform-object-assign": "^6.8.0",
"babel-plugin-transform-object-rest-spread": "^6.8.0",
"babel-preset-es2015": "^6.9.0",
"babel-register": "^6.11.6",
"browser-sync": "^2.13.0",
"css-loader": "^0.23.1",
"classnames": "^2.2.5",
"eslint": "^3.1.1",
"eslint-plugin-import": "^1.11.1",
"exports-loader": "^0.6.3",
"file-loader": "^0.9.0",
"gulp": "^3.9.1",
"gulp-babel": "^6.1.2",
"gulp-postcss": "^6.1.1",
"gulp-rename": "^1.2.2",
"gulp-transform": "^3.0.5",
"gulp-util": "^3.0.7",
"hugo-bin": "^0.23.0",
"imports-loader": "^0.6.5",
"gatsby": "^1.9.270",
"gatsby-plugin-catch-links": "^1.0.22",
"gatsby-plugin-manifest": "^1.0.21",
"gatsby-plugin-postcss-sass": "^1.0.20",
"gatsby-plugin-react-helmet": "^2.0.11",
"gatsby-plugin-react-next": "^1.0.11",
"gatsby-remark-autolink-headers": "^1.4.18",
"gatsby-remark-prismjs": "^2.0.2",
"gatsby-source-filesystem": "^1.5.35",
"gatsby-transformer-json": "^1.0.17",
"gatsby-transformer-remark": "^1.7.42",
"gatsby-transformer-yaml": "^1.5.16",
"postcss-at2x": "^2.0.0",
"postcss-colour-functions": "^1.5.1",
"postcss-cssnext": "^2.7.0",
"postcss-import": "^8.1.2",
"postcss-loader": "^0.9.1",
"postcss-neat": "^2.5.2",
"postcss-nested": "^1.0.0",
"postcss-simple-extend": "^1.0.0",
"postcss-simple-vars-async": "^1.2.1",
"url-loader": "^0.5.7",
"webpack": "^1.13.1",
"whatwg-fetch": "^1.0.0",
"yamljs": "^0.2.8"
"prismjs": "^1.14.0",
"react-helmet": "^5.2.0",
"react-markdown": "^3.3.2",
"react-sidecar": "^0.1.1",
"smooth-scroll": "^14.2.0"
},
"devDependencies": {}
}

View File

@ -1,22 +0,0 @@
baseurl: "https://www.netlifycms.org/"
languageCode: "en-us"
title: "Netlify CMS | Open-Source Content Management System"
disable404: true
pluralizeListTitles: false
metaDataFormat: "yaml"
permalinks:
blog: /blog/:year/:month/:title/
menu:
docs:
- name: start
title: Quick Start
weight: 100
- name: guides
title: Guides
weight: 200
- name: reference
title: Reference
weight: 300
- name: contributing
title: Contributing
weight: 400

View File

@ -1,22 +0,0 @@
notifications:
- loud: true
message: >-
Register to join us online for our next community dev meeting, every other
Wednesday at 9am-10am PT!
published: false
title: Netlify CMS Development Planning Sessions Promo
url: >-
https://www.eventbrite.com/e/netlify-cms-planning-session-bi-weekly-tickets-35794058994
- loud: true
message: >-
We have a community on Gitter - join now to ask questions and discuss the
project with other devs!
published: false
title: Gitter shoutout
url: 'https://gitter.im/netlify/netlifycms'
- loud: true
message: >-
Netlify CMS now supports GitLab!
published: true
title: GitLab announcement
url: '/blog/2018/06/netlify-cms-now-supports-gitlab-as-a-backend/'

View File

@ -1,46 +0,0 @@
{{ partial "header" . }}
{{ $activePage := .Params.position }}
<div class="docs page">
<div class="container">
<aside id="sidebar" class="sidebar">
<nav id="docs-nav" class="docs-nav" id="docs-nav">
{{ $currentPage := . }}
{{ range $index, $element := .Site.Menus.docs }}
{{ if .HasChildren }}
<div class="docs-nav-section">
<div class="docs-nav-section-title">{{ .Title }}</div>
<ul class="docs-nav-section-list">
{{ range .Children }}
<a href="{{ .URL }}" class="nav-link {{ if $currentPage.IsMenuCurrent "docs" . }}active{{ end }}">{{ .Title }}</a>
{{ end }}
</ul>
</div>
{{ end }}
{{ end }}
</nav>
<div class="mobile docs-nav">
<select class="btn-primary" id="mobile-docs-nav">
<option value="" selected="selected">Select A Topic</option>
{{ range $index, $element := .Site.Menus.docs }}
{{ if .HasChildren }}
<optgroup label="{{ .Title }}"/>
{{ range .Children }}
<option value="{{ .URL }}" class="nav-link {{ if $currentPage.IsMenuCurrent "docs" . }}active{{ end }}">{{ .Title }}</option>
{{ end }}
{{ end }}
{{ end }}
</select>
</div>
</aside>
<article class="docs-content" id="docs-content">
{{ partial "edit-link" . }}
{{ .Content }}
{{- if eq .Title "Widgets" -}} <!-- Check if is widgets page, if so, add the partial "widgets" for the widget cloud functionality -->
{{- partial "widgets" . -}}
{{- end -}}
</article>
</div>
</div>
{{ partial "footer" . }}

View File

@ -1,16 +0,0 @@
{{ partial "header" . }}
<div class="docs page">
<div class="container">
<article class="blog-content" id="blog-content">
<div class="blog-post-header">
<h1>{{ .Title }}</h1>
<p class="meta-info">by {{ .Params.author }} on {{ .Date.Format "January 2, 2006" }}</p>
</div>
{{ .Content }}
</article>
</div>
</div>
{{ partial "footer" . }}

View File

@ -1,88 +0,0 @@
{{ partial "header" . }}
<div class="landing page">
<section class="landing hero">
<div class="contained">
<div class="hero-copy">
<h1 class="headline">{{ .Site.Data.landing.hero.headline | markdownify }}</h1>
<span class="subhead">{{ .Site.Data.landing.hero.subhead | markdownify }}</span>
<span class="cta-header">{{ .Site.Data.landing.cta.button | markdownify }}</span>
</div>
<div class="hero-features">
{{ range $index, $id := .Site.Data.landing.hero.devfeatures }}
<div class="feature">
<h3>{{ .feature | markdownify }}</h3>
<p>{{ .description | markdownify }}</p>
</div>
{{ end }}
</div>
<a class="hero-graphic">
<img src="/img/screenshot-editor.jpg" class="responsive"/>
<div class="hero-videolink">&#x25b6;</div>
</a>
</div>
</section>
<section class="cta">
<div class="cta-primary">
<p><span class="hook">{{ .Site.Data.landing.cta.primaryhook | markdownify }}</span>&nbsp;{{ .Site.Data.landing.cta.primary | markdownify }}</p>{{ .Site.Data.landing.cta.button | markdownify }}
</div>
</section>
<section class="whatsnew">
<div class="contained">
<ol>
{{ range .Site.Data.updates }}
{{ range first 3 . }}
<a href="https://github.com/netlify/netlify-cms/releases/tag/{{ .version }}"><li><div class="update-metadata"><span class="update-version">{{ .version }}</span><span class="update-date">{{ dateFormat "January 2, 2006" .date }}</span></div><span class="update-description">{{ .description | markdownify }}</span></li></a>
{{ end }}
{{ end }}
</ol>
</div>
</section>
<section class="editors">
<div class="contained">
<h2>{{ .Site.Data.landing.editors.hook | markdownify }}</h2>
<p id="editor-intro">{{ .Site.Data.landing.editors.intro | markdownify }}</p>
<div class="editors-features">
{{ range $index, $id := .Site.Data.landing.editors.features }}
<div class="feature">
<img src="{{ .imgpath }}" >
<h3>{{ .feature | markdownify }}</h3>
<p>{{ .description | markdownify }}</p>
</div>
{{ end }}
</div>
</div>
</section>
<section class="communitysupport">
<div class="contained">
<h2>{{ .Site.Data.landing.community.hook | markdownify }}</h2>
<div class="community">
<div class="community-features">
{{ range $index, $id := .Site.Data.landing.community.features }}
<div class="feature">
<h3>{{ .feature | markdownify }}</h3>
<p>{{ .description | markdownify }}</p>
</div>
{{ end }}
</div>
</div>
<div class="contributors feature">
<h3>{{ .Site.Data.landing.community.contributors | markdownify }}</h3>
{{ range .Site.Data.contributors }}
<div class="contributor-list">
{{ range . }}
<a href="{{.profile}}" title="{{.name}}"><img src="{{.avatar_url}}" alt="{{.login}}" /></a>
{{ end }}
</div>
{{ end }}
</div>
</div>
</section>
{{ partial "footer" . }}

View File

@ -1,38 +0,0 @@
{{ partial "header" . }}
<div class="community page">
<section class="community hero">
<div class="contained">
<div class="hero-copy">
<h1 class="headline">{{ .Params.headline | markdownify }}</h1>
<h2 class="subhead">{{ .Params.subhead | markdownify }}</h2>
<h3 class="ctas"><ul><li>{{ .Params.primarycta | markdownify }}</li></ul></h3>
</div>
<div class="calendar-cta">
<h2>{{ .Params.upcomingevent.hook }}</h2>
<div class="calendar">
<div class="month"></div>
<div class="day"></div>
</div>
<h3><strong></strong> at <strong></strong></h3>
<div class="cal-cta">{{ .Params.primarycta | markdownify }}</div>
</div>
</div>
</section>
<section class="how-it-works clearfix">
<div class="contained">
<div class="half">
<h4 class="section-label">How it works</h4>
<p>{{ .Params.howitworks | markdownify }}</p>
<h4 class="section-label">How to join</h4>
<p>{{ .Params.howtojoin | markdownify }}</p>
</div>
</div>
</section>
</div>
{{ partial "footer" . }}

View File

@ -1,10 +0,0 @@
<a class="edit-this-page" href="https://github.com/netlify/netlify-cms/blob/master/website/site/content/{{ .File.Path }}">
<svg version="1.1" id="pencil" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="14px" height="14px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="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>

View File

@ -1,46 +0,0 @@
<footer>
<div class="contained">
<div class="social-buttons">
{{ range $index, $id := .Site.Data.global.footer.buttons }}
<a href="{{ .url }}">{{ .name }}</a>
{{ end }}
</div>
<div class="footer-info">
<p><a href="https://github.com/netlify/netlify-cms/blob/master/LICENSE" class="text-link">Distributed under MIT License</a> ·
<a href="https://github.com/netlify/netlify-cms/blob/master/CODE_OF_CONDUCT.md" class="text-link">Code of Conduct</a></p>
</div>
</div>
</footer>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
{{ if or (eq .Section "docs") (eq .Title "Docs") }}
<script src="/jquery.scrollTo.min.js"></script>
<script src="/jquery.localScroll.min.js"></script>
<script src="/prism.js"></script>
{{ end }}
{{ if eq .Title "Community" }}
<script src="/moment.min.js"></script>
{{ end }}
<script src="/app.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
<script type="text/javascript"> docsearch({
apiKey: '08d03dc80862e84c70c5a1e769b13019',
indexName: 'netlifycms',
inputSelector: '.algolia-search',
debug: false // Set debug to true if you want to inspect the dropdown
});
</script>
{{- if eq .Title "Widgets" -}} <!-- Check if is widgets page, if so, add the widget cloud js script -->
<script src="/widgets.js"></script>
{{- end -}}
<script async defer src="https://buttons.github.io/buttons.js"></script>
{{ if or (eq .Section "docs") (eq .Title "Docs") (eq .Title "Community") }}
<script>
((window.gitter = {}).chat = {}).options = {
room: 'netlify/NetlifyCMS'
};
</script>
<script src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer></script>
{{ end }}
</body>
</html>

View File

@ -1,63 +0,0 @@
<!doctype html>
<html>
<head>
<title>{{ .Site.Title }}</title>
<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="manifest" href="/img/favicon/manifest.json">
<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">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="/css/main.css"/>
<meta name="description" content=
{{ if .Params.meta_description }}
{{ .Params.meta_description }}
{{ else if .Params.description }}
{{ .Params.description }}
{{ else if or (eq .Section "blog") (eq .Title "Blog") }}
"Recent news and updates about Netlify CMS."
{{ else }}
"Netlify CMS provides open source content management for your Git workflow."
{{ end }}
>
{{ if or (eq .Section "docs") (eq .Title "Docs") }}
<link rel="stylesheet" href="/css/lib/prism.css"/>
{{ end }}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" />
{{ range .AlternativeOutputFormats -}}
{{ printf `<link rel="%s" type="%s+%s" href="%s" title="%s" />` .Rel .MediaType.Type .MediaType.Suffix .Permalink $.Site.Title | safeHTML }}
{{ end -}}
</head>
<body>
{{ range .Site.Data.notifications }}
{{ range . }}
{{ if .published }}
<a href="{{ .url }}" class="notification {{ if .loud }}notification-loud{{end}}">{{ .message }}</a>
{{ end }}
{{ end }}
{{ end }}
<header id="header" class="{{ .Section }}">
<div class="contained">
<div class="logo-container">
<a href="/" class="logo"><img src="/img/netlify-cms-logo.svg"/></a>
{{ if or (eq .Section "docs") (eq .Title "Docs") }}
<a class="utility-input">
<img src="/img/search.svg" />
<input type="search" placeholder="Search the docs" class="algolia-search"/>
</a>
{{ end }}
</div>
<div class="nav-container">
<a class="nav-link docs-link" href="/docs/intro/">Docs</a>
<a class="nav-link contributing-link" href="/docs/contributor-guide">Contributing</a>
<a class="nav-link" href="/community">Community</a>
<a class="nav-link" href="/blog/">Blog</a>
<a id="ghstars" class="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>
</div>
</div>
</header>

View File

@ -1,14 +0,0 @@
<section class="widgets">
<div class="widgets__cloud">
{{- range $index, $widget := .Resources -}}
<span class="widgets__item {{if eq $index 0}}widgets__item_active{{end}}" data-widget-target="{{.Params.target}}">{{.Params.label}}</span>
{{- end -}}
</div>
<div class="widgets__container">
{{- range $index, $widget := .Resources -}}
<div class="widget {{if eq $index 0}}widget_open{{end}}" id="{{.Params.target}}">
{{.Content}}
</div>
{{- end -}}
</div>
</section>

View File

@ -1,17 +0,0 @@
{{ partial "header" . }}
<div class="blog page">
<div class="container">
<h1>Netlify CMS Blog</h1>
{{ range (.Paginate .Data.Pages.ByDate.Reverse ).Pages }}
<article class="blog-list-item">
<h2><a href="{{ .Permalink }}" class="article">{{ .Title }}</a></h2>
<p class="meta-info">by {{ .Params.author }} on {{ .Date.Format "January 2, 2006" }}</p>
<p>{{ .Description }}</p>
</article>
{{ end }}
{{ template "_internal/pagination.html" . }}
</div>
</div>
{{ partial "footer" . }}

View File

@ -1,7 +0,0 @@
/**
* Copyright (c) 2007-2016 Ariel Flesler - aflesler<a>gmail<d>com | http://flesler.blogspot.com
* Licensed under MIT
* @author Ariel Flesler
* @version 1.4.0
*/
;(function(a){if(typeof define==='function'&&define.amd){define(['jquery'],a)}else{a(jQuery)}}(function($){var g=location.href.replace(/#.*/,'');var h=$.localScroll=function(a){$('body').localScroll(a)};h.defaults={duration:1000,axis:'y',event:'click',stop:true,target:window};$.fn.localScroll=function(a){a=$.extend({},h.defaults,a);if(a.hash&&location.hash){if(a.target)window.scrollTo(0,0);scroll(0,location,a)}return a.lazy?this.on(a.event,'a,area',function(e){if(filter.call(this)){scroll(e,this,a)}}):this.find('a,area').filter(filter).bind(a.event,function(e){scroll(e,this,a)}).end().end();function filter(){return!!this.href&&!!this.hash&&this.href.replace(this.hash,'')===g&&(!a.filter||$(this).is(a.filter))}};h.hash=function(){};function scroll(e,a,b){var c=a.hash.slice(1),elem=document.getElementById(c)||document.getElementsByName(c)[0];if(!elem)return;if(e)e.preventDefault();var d=$(b.target);if(b.lock&&d.is(':animated')||b.onBefore&&b.onBefore(e,elem,d)===false)return;if(b.stop){d.stop(true)}if(b.hash){var f=elem.id===c?'id':'name',$a=$('<a> </a>').attr(f,c).css({position:'absolute',top:$(window).scrollTop(),left:$(window).scrollLeft()});elem[f]='';$('body').prepend($a);location.hash=a.hash;$a.remove();elem[f]=c}d.scrollTo(elem,b).trigger('notify.serialScroll',[elem])}return h}));

View File

@ -1,7 +0,0 @@
/**
* Copyright (c) 2007-2015 Ariel Flesler - aflesler<a>gmail<d>com | http://flesler.blogspot.com
* Licensed under MIT
* @author Ariel Flesler
* @version 2.1.0
*/
;(function(l){'use strict';l(['jquery'],function($){var k=$.scrollTo=function(a,b,c){return $(window).scrollTo(a,b,c)};k.defaults={axis:'xy',duration:0,limit:true};function isWin(a){return!a.nodeName||$.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!==-1}$.fn.scrollTo=function(f,g,h){if(typeof g==='object'){h=g;g=0}if(typeof h==='function'){h={onAfter:h}}if(f==='max'){f=9e9}h=$.extend({},k.defaults,h);g=g||h.duration;var j=h.queue&&h.axis.length>1;if(j){g/=2}h.offset=both(h.offset);h.over=both(h.over);return this.each(function(){if(f===null)return;var d=isWin(this),elem=d?this.contentWindow||window:this,$elem=$(elem),targ=f,attr={},toff;switch(typeof targ){case'number':case'string':if(/^([+-]=?)?\d+(\.\d+)?(px|%)?$/.test(targ)){targ=both(targ);break}targ=d?$(targ):$(targ,elem);if(!targ.length)return;case'object':if(targ.is||targ.style){toff=(targ=$(targ)).offset()}}var e=$.isFunction(h.offset)&&h.offset(elem,targ)||h.offset;$.each(h.axis.split(''),function(i,a){var b=a==='x'?'Left':'Top',pos=b.toLowerCase(),key='scroll'+b,prev=$elem[key](),max=k.max(elem,a);if(toff){attr[key]=toff[pos]+(d?0:prev-$elem.offset()[pos]);if(h.margin){attr[key]-=parseInt(targ.css('margin'+b),10)||0;attr[key]-=parseInt(targ.css('border'+b+'Width'),10)||0}attr[key]+=e[pos]||0;if(h.over[pos]){attr[key]+=targ[a==='x'?'width':'height']()*h.over[pos]}}else{var c=targ[pos];attr[key]=c.slice&&c.slice(-1)==='%'?parseFloat(c)/100*max:c}if(h.limit&&/^\d+$/.test(attr[key])){attr[key]=attr[key]<=0?0:Math.min(attr[key],max)}if(!i&&h.axis.length>1){if(prev===attr[key]){attr={}}else if(j){animate(h.onAfterFirst);attr={}}}});animate(h.onAfter);function animate(a){var b=$.extend({},h,{queue:true,duration:g,complete:a&&function(){a.call(elem,targ,h)}});$elem.animate(attr,b)}})};k.max=function(a,b){var c=b==='x'?'Width':'Height',scroll='scroll'+c;if(!isWin(a))return a[scroll]-$(a)[c.toLowerCase()]();var d='client'+c,doc=a.ownerDocument||a.document,html=doc.documentElement,body=doc.body;return Math.max(html[scroll],body[scroll])-Math.min(html[d],body[d])};function both(a){return $.isFunction(a)||$.isPlainObject(a)?a:{top:a,left:a}}$.Tween.propHooks.scrollLeft=$.Tween.propHooks.scrollTop={get:function(t){return $(t.elem)[t.prop]()},set:function(t){var a=this.get(t);if(t.options.interrupt&&t._last&&t._last!==a){return $(t.elem).stop()}var b=Math.round(t.now);if(a!==b){$(t.elem)[t.prop](b);t._last=this.get(t)}}};return k})}(typeof define==='function'&&define.amd?define:function(a,b){'use strict';if(typeof module!=='undefined'&&module.exports){module.exports=b(require('jquery'))}else{b(jQuery)}}));

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,76 +0,0 @@
// Widgets section
function widgetsCloud() {
const widgetItems = document.getElementsByClassName("widgets__item"), // Widget word cloud
widgets = document.getElementsByClassName("widget"); // Widgets' bodies
let activeWidgetItem = document.getElementsByClassName("widgets__item_active")[0]; // Active button in the widgets cloud
if (document.getElementsByClassName("widgets")) {
if (loadWidgetFromHash()) { // Scroll to widget cloud if URL hash set to a widget.
setTimeout(() => {
document.getElementsByClassName("widgets")[0].scrollIntoView({
behavior: "smooth",
block: "nearest"
}); // Scrolls to the widgets section
}, 200);
}
window.addEventListener('hashchange', loadWidgetFromHash);
for (let i = 0; i < widgetItems.length; i++) {
widgetItems[i].addEventListener("click", e => { // Add click event for each widget button in the cloud
const targetWidget = document.getElementById(e.target.dataset.widgetTarget), // Defines which widget the user is trying to render
openedWidget = document.getElementsByClassName("widget_open")[0], // Defines the current open widget
targetWidgetItem = e.target; // Defines the current open widget
changeWidgets(openedWidget, targetWidget, targetWidgetItem); // Runs the function to change which widget is displayed
})
}
}
function loadWidgetFromHash() {
if (!window.location.hash) return; // Check if the given URL has a hash to make each widget shareable
const targetWidget = document.getElementById(window.location.hash.substr(1)),
openedWidget = document.getElementsByClassName("widget_open")[0];
let targetWidgetItem = "";
for (let i = 0; i < widgetItems.length; i++) {
if (widgetItems[i].dataset.widgetTarget == window.location.hash.substr(1)) {
targetWidgetItem = widgetItems[i]
}
};
if (!targetWidgetItem) return; // Make sure the hash pointed to a widget, not something else.
changeWidgets(openedWidget, targetWidget, targetWidgetItem, true); // Runs the function to change which widget is displayed
return true;
}
function changeWidgets(active, target, cloudItem, preventHistoryUpdate) {
target.classList.add("widget_opening"); // Starts the process of opening the next widget
active.classList.remove("widget_open"); // Removes the active state of the current widget
active.classList.add("widget_closing"); // But guarantees the current active widget a closing class for transition purposes
activeWidgetItem.classList.remove("widgets__item_active"); // Removes the active state of the current widget item in the cloud
activeWidgetItem = cloudItem; // Sets the new active widget item as the clicked one
activeWidgetItem.classList.add("widgets__item_active"); // And adds the active CSS class to it
if (!preventHistoryUpdate) {
if (history.pushState) {
history.pushState(null, null, '#' + cloudItem.dataset.widgetTarget);
}
else {
location.hash = '#' + cloudItem.dataset.widgetTarget;
}
}
setTimeout(() => {
target.classList.remove("widget_opening"); // Removes the opening class to finish transition
target.classList.add("widget_open"); // Defines the new target widget
active.classList.remove("widget_closing"); // Finally gets completely rid of the previously openened widget
}, 150) // When the transition is done, finish the process by attributing the final classes to each widget
}
}
widgetsCloud();

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 {
@ -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;
@ -225,6 +223,10 @@ header {
text-decoration: none;
line-height: $small;
}
img {
margin-right: 4px;
}
}
.logo-container {

View File

@ -7,18 +7,24 @@
}
.widgets__item {
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 .2s ease, background .2s ease;
transition: color 0.2s ease, background 0.2s ease;
display: inline-block;
font-weight: normal;
}
.widgets__item:hover, .widgets__item_active {
.widgets__item:hover,
.widgets__item_active {
background: $darkGreen;
color: white !important;
}
@ -27,11 +33,11 @@
margin: 1em 0;
background: $lightGrey;
border-radius: $borderRadius;
transition: height .15s ease;
transition: height 0.15s ease;
}
.widget {
padding: .5em 1em;
padding: 0.5em 1em;
display: none;
}
@ -41,7 +47,7 @@
.widget_closing {
display: block;
animation: widgetOpacity .15s ease forwards reverse;
animation: widgetOpacity 0.15s ease forwards reverse;
}
.widget_opening {
@ -50,15 +56,15 @@
top: 0;
left: 0;
opacity: 0;
animation: widgetOpacity .15s .05s ease forwards;
animation: widgetOpacity 0.15s 0.05s ease forwards;
}
@keyframes widgetOpacity {
from {
opacity: 0
opacity: 0;
}
to {
opacity: 1
opacity: 1;
}
}

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

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"
}

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;

View File

@ -6,17 +6,18 @@ backend:
publish_mode: editorial_workflow
media_folder: "website/site/static/img" # Folder where user uploaded files should go
media_folder: "website/static/img" # Folder where user uploaded files should go
public_folder: "img"
collections: # A list of collections the CMS should be able to edit
- name: "docs" # Used in routes, ie.: /admin/collections/:slug/edit
label: "Docs" # Used in the UI, ie.: "New Post"
folder: "website/site/content/docs" # The path to the folder where the documents are stored
folder: "website/content/docs" # The path to the folder where the documents are stored
create: true # Allow users to create new documents in this collection
fields: # The fields each document in this collection have
- {label: "Title", name: "title", widget: "string", tagname: "h1"}
- {label: "Position", name: "position", widget: "number"}
- {label: "Group", name: "group", widget: "string"}
- {label: "Weight", name: "weight", widget: "number"}
- {label: "Body", name: "body", widget: "markdown"}
- name: "blog"
label: "Blog"
@ -35,7 +36,7 @@ collections: # A list of collections the CMS should be able to edit
files:
- name: releases
label: Releases
file: website/site/data/updates.yml
file: website/data/updates.yml
fields:
- name: updates
label: Releases
@ -46,7 +47,7 @@ collections: # A list of collections the CMS should be able to edit
- {name: description, label: Description}
- name: notifications
label: Notifications
file: website/site/data/notifications.yml
file: website/data/notifications.yml
description: Site-top notifications - publish one at a time
fields:
- name: notifications

View File

Before

Width:  |  Height:  |  Size: 356 KiB

After

Width:  |  Height:  |  Size: 356 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -2,7 +2,7 @@
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<square150x150logo src="mstile-150x150.png"/>
<TileColor>#222222</TileColor>
</tile>
</msapplication>

View File

Before

Width:  |  Height:  |  Size: 560 B

After

Width:  |  Height:  |  Size: 560 B

Some files were not shown because too many files have changed in this diff Show More