- );
- }
-}
-```
+`embed:animation/react-transition-group.js`
> Note:
>
@@ -78,25 +37,7 @@ In this component, when a new item is added to `ReactCSSTransitionGroup` it will
You can use these classes to trigger a CSS animation or transition. For example, try adding this CSS and adding a new list item:
-```css
-.example-enter {
- opacity: 0.01;
-}
-
-.example-enter.example-enter-active {
- opacity: 1;
- transition: opacity 500ms ease-in;
-}
-
-.example-leave {
- opacity: 1;
-}
-
-.example-leave.example-leave-active {
- opacity: 0.01;
- transition: opacity 300ms ease-in;
-}
-```
+`embed:animation/animation.css`
You'll notice that animation durations need to be specified in both the CSS and the render method; this tells React when to remove the animation classes from the element and -- if it's leaving -- when to remove the element from the DOM.
@@ -104,20 +45,7 @@ You'll notice that animation durations need to be specified in both the CSS and
`ReactCSSTransitionGroup` provides the optional prop `transitionAppear`, to add an extra transition phase at the initial mount of the component. There is generally no transition phase at the initial mount as the default value of `transitionAppear` is `false`. The following is an example which passes the prop `transitionAppear` with the value `true`.
-```javascript{5-6}
-render() {
- return (
-
-
Fading at Initial Mount
-
- );
-}
-```
+`embed:animation/initial-mounting.js`
During the initial mount `ReactCSSTransitionGroup` will get the `example-appear` CSS class and the `example-appear-active` CSS class added in the next tick.
@@ -144,30 +72,7 @@ At the initial mount, all children of the `ReactCSSTransitionGroup` will `appear
It is also possible to use custom class names for each of the steps in your transitions. Instead of passing a string into transitionName you can pass an object containing either the `enter` and `leave` class names, or an object containing the `enter`, `enter-active`, `leave-active`, and `leave` class names. If only the enter and leave classes are provided, the enter-active and leave-active classes will be determined by appending '-active' to the end of the class name. Here are two examples using custom classes:
-```javascript
-// ...
-
- {item}
-
-
-
- {item2}
-
-// ...
-```
+`embed:animation/custom-classes.js`
### Animation Group Must Be Mounted To Work
@@ -175,45 +80,13 @@ In order for it to apply transitions to its children, the `ReactCSSTransitionGro
The example below would **not** work, because the `ReactCSSTransitionGroup` is being mounted along with the new item, instead of the new item being mounted within it. Compare this to the [Getting Started](#getting-started) section above to see the difference.
-```javascript{4,6,13}
-render() {
- const items = this.state.items.map((item, i) => (
-
this.handleRemove(i)}>
-
- {item}
-
-
- ));
-
- return (
-
-
- {items}
-
- );
-}
-```
+`embed:animation/invalid-unmounted-example.js`
### Animating One or Zero Items
In the example above, we rendered a list of items into `ReactCSSTransitionGroup`. However, the children of `ReactCSSTransitionGroup` can also be one or zero items. This makes it possible to animate a single element entering or leaving. Similarly, you can animate a new element replacing the current element. For example, we can implement a simple image carousel like this:
-```javascript{10}
-import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
-
-function ImageCarousel(props) {
- return (
-
-
-
-
-
- );
-}
-```
+`embed:animation/image-carousel.js`
### Disabling Animations
diff --git a/content/docs/hello-world.md b/content/docs/hello-world.md
index be30b5b9b0f..fabdcf2fb33 100644
--- a/content/docs/hello-world.md
+++ b/content/docs/hello-world.md
@@ -16,12 +16,7 @@ The easiest way to get started with React is to use [this Hello World example co
The smallest React example looks like this:
-```js
-ReactDOM.render(
-
Hello, world!
,
- document.getElementById('root')
-);
-```
+```embed:hello-world.js```
It renders a header saying "Hello, world!" on the page.
diff --git a/examples/animation/animation.css b/examples/animation/animation.css
new file mode 100644
index 00000000000..002137d5667
--- /dev/null
+++ b/examples/animation/animation.css
@@ -0,0 +1,17 @@
+.example-enter {
+ opacity: 0.01;
+}
+
+.example-enter.example-enter-active {
+ opacity: 1;
+ transition: opacity 500ms ease-in;
+}
+
+.example-leave {
+ opacity: 1;
+}
+
+.example-leave.example-leave-active {
+ opacity: 0.01;
+ transition: opacity 300ms ease-in;
+}
\ No newline at end of file
diff --git a/examples/animation/custom-classes.js b/examples/animation/custom-classes.js
new file mode 100644
index 00000000000..698e4de685b
--- /dev/null
+++ b/examples/animation/custom-classes.js
@@ -0,0 +1,21 @@
+
+ );
+ }
+}
diff --git a/gatsby-config.js b/gatsby-config.js
index 6c65bdf2958..148375593e5 100644
--- a/gatsby-config.js
+++ b/gatsby-config.js
@@ -56,10 +56,18 @@ module.exports = {
},
},
'gatsby-remark-autolink-headers',
+ {
+ resolve: 'gatsby-remark-embed-snippet',
+ options: {
+ classPrefix: 'gatsby-code-',
+ directory: `${__dirname}/examples/`,
+ },
+ },
{
resolve: 'gatsby-remark-code-repls',
options: {
defaultText: 'Try it on CodePen',
+ dependencies: ['react', 'react-dom'],
directory: `${__dirname}/examples/`,
externals: [
`//unpkg.com/react/umd/react.development.js`,
diff --git a/plugins/gatsby-remark-embed-snippet/README.md b/plugins/gatsby-remark-embed-snippet/README.md
new file mode 100644
index 00000000000..fb5c82ff597
--- /dev/null
+++ b/plugins/gatsby-remark-embed-snippet/README.md
@@ -0,0 +1,120 @@
+# gatsby-remark-embed-snippet
+
+Embeds the contents of specified files within Prism-formatted HTML blocks.
+
+## Overview
+
+### Embedding Files
+
+For example, given the following project directory structure:
+```
+./examples/
+├── sample-javascript-file.js
+├── sample-html-file.html
+```
+
+The following markdown syntax could be used to embed the contents of these files:
+```md
+# Sample JavaScript
+`embed:sample-javascript-file.js`
+
+# Sample HTML
+`embed:sample-html-file.html`
+```
+
+The resulting HTML for the above markdown would look something like this:
+```html
+
Sample JavaScript
+
+
+
+
+
+
+
+
+
Sample HTML
+
+
+
+
+
+
+
+```
+
+### Highlighting Lines
+
+You can also specify specific lines for Prism to highlight using `highlight-line` and `highlight-next-line` comments.
+
+#### JavaScript example
+```js
+import React from 'react';
+import ReactDOM from 'react-dom';
+
+// highlight-next-line
+ReactDOM.render(
+
+
+
+```
+
+#### YAML example
+```yaml
+foo: 1 # highlight-line
+bar: 2
+# highlight-next-line
+baz: 3
+```
+
+## Installation
+
+`npm install --save gatsby-remark-embed-snippet`
+
+## Usage
+
+```javascript
+// In your gatsby-config.js
+{
+ resolve: 'gatsby-remark-embed-snippet',
+ options: {
+ // Class prefix for
tags containing syntax highlighting;
+ // defaults to 'language-' (eg
).
+ // If your site loads Prism into the browser at runtime,
+ // (eg for use with libraries like react-live),
+ // you may use this to prevent Prism from re-processing syntax.
+ // This is an uncommon use-case though;
+ // If you're unsure, it's best to use the default value.
+ classPrefix: 'language-',
+
+ // Example code links are relative to this dir.
+ // eg examples/path/to/file.js
+ directory: `${__dirname}/examples/`,
+ },
+},
+```
\ No newline at end of file
diff --git a/plugins/gatsby-remark-embed-snippet/index.js b/plugins/gatsby-remark-embed-snippet/index.js
new file mode 100644
index 00000000000..d31b0492da6
--- /dev/null
+++ b/plugins/gatsby-remark-embed-snippet/index.js
@@ -0,0 +1,141 @@
+'use strict';
+
+const fs = require(`fs`);
+const normalizePath = require(`normalize-path`);
+const rangeParser = require(`parse-numeric-range`);
+const visit = require(`unist-util-visit`);
+
+// HACK: It would be nice to find a better way to share this utility code.
+const highlightCode = require(`gatsby-remark-prismjs/highlight-code`);
+
+// Language defaults to extension.toLowerCase();
+// This map tracks languages that don't match their extension.
+const FILE_EXTENSION_TO_LANGUAGE_MAP = {
+ js: `jsx`,
+ md: `markup`,
+ sh: `bash`,
+};
+
+const HIGHLIGHT_LINE_REGEX = /\s+(\{\/\*|\/\*|\/\/|)*/;
+
+const getLanguage = file => {
+ if (!file.includes(`.`)) {
+ return `none`;
+ }
+
+ const extension = file.split(`.`).pop();
+
+ return FILE_EXTENSION_TO_LANGUAGE_MAP.hasOwnProperty(extension)
+ ? FILE_EXTENSION_TO_LANGUAGE_MAP[extension]
+ : extension.toLowerCase();
+};
+
+module.exports = (
+ {markdownAST},
+ {classPrefix = `language-`, directory} = {},
+) => {
+ if (!directory) {
+ throw Error(`Required option "directory" not specified`);
+ } else if (!fs.existsSync(directory)) {
+ throw Error(`Invalid directory specified "${directory}"`);
+ } else if (!directory.endsWith(`/`)) {
+ directory += `/`;
+ }
+
+ visit(markdownAST, `inlineCode`, node => {
+ const {value} = node;
+
+ if (value.startsWith(`embed:`)) {
+ const file = value.substr(6);
+ const path = normalizePath(`${directory}${file}`);
+
+ if (!fs.existsSync(path)) {
+ throw Error(`Invalid snippet specified; no such file "${path}"`);
+ }
+
+ // Parse file contents and extract highlight markers:
+ // highlight-line, highlight-next-line, highlight-range
+ // We support JS, JSX, HTML, CSS, and YAML style comments.
+ // Turn them into an Array format as expected by highlightCode().
+ // The order if these operations is important!
+ // Filtering next-line comments impacts line-numbers for same-line comments.
+ const highlightLines = [];
+ const code = fs
+ .readFileSync(path, `utf8`)
+ .split(`\n`)
+ .filter((line, index) => {
+ if (line.includes(`highlight-next-line`)) {
+ // Although we're highlighting the next line,
+ // We can use the current index since we also filter this lines.
+ // (Highlight line numbers are 1-based).
+ highlightLines.push(index + 1);
+
+ // Strip lines that contain highlight-next-line comments.
+ return false;
+ } else if (line.includes(`highlight-range`)) {
+ const match = line.match(/highlight-range{([^}]+)}/);
+ if (!match) {
+ console.warn(`Invalid match specified: "${line.trim()}"`);
+ return false;
+ }
+ const range = match[1];
+
+ // Highlight line numbers are 1-based but so are offsets.
+ // Remember that the current line (index) will be removed.
+ rangeParser.parse(range).forEach(offset => {
+ highlightLines.push(index + offset);
+ });
+
+ // Strip lines that contain highlight-range comments.
+ return false;
+ }
+
+ return true;
+ })
+ .map((line, index) => {
+ if (line.includes(`highlight-line`)) {
+ // Mark this line for highlighting.
+ // (Highlight line numbers are 1-based).
+ highlightLines.push(index + 1);
+
+ // Strip the highlight comment itself.
+ return line.replace(HIGHLIGHT_LINE_REGEX, ``);
+ }
+
+ return line;
+ })
+ .join(`\n`)
+ .trim();
+
+ // PrismJS's theme styles are targeting pre[class*="language-"]
+ // to apply its styles. We do the same here so that users
+ // can apply a PrismJS theme and get the expected, ready-to-use
+ // outcome without any additional CSS.
+ //
+ // @see https://github.com/PrismJS/prism/blob/1d5047df37aacc900f8270b1c6215028f6988eb1/themes/prism.css#L49-L54
+ const language = getLanguage(file);
+
+ // Allow users to specify a custom class prefix to avoid breaking
+ // line highlights if Prism is required by any other code.
+ // This supports custom user styling without causing Prism to
+ // re-process our already-highlighted markup.
+ // @see https://github.com/gatsbyjs/gatsby/issues/1486
+ const className = language
+ .split(` `)
+ .map(token => `${classPrefix}${token}`)
+ .join(` `);
+
+ // Replace the node with the markup we need to make 100% width highlighted code lines work
+ node.type = `html`;
+ node.value = `