diff --git a/Readme.md b/Readme.md
index f944b384f..d0c061dbe 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1 +1,15 @@
:)
+
+---
+
+## Notes
+
+H2's support these:
+
+ {: .-two-column}
+ {: .-three-column}
+ {: .-left-reference}
+
+Tables support these:
+
+ {: .-shortcuts}
diff --git a/_includes/2017/head.html b/_includes/2017/head.html
index 837771124..421133a20 100644
--- a/_includes/2017/head.html
+++ b/_includes/2017/head.html
@@ -9,6 +9,7 @@
+
diff --git a/_sass/2017/base/base.scss b/_sass/2017/base/base.scss
new file mode 100644
index 000000000..192375cc8
--- /dev/null
+++ b/_sass/2017/base/base.scss
@@ -0,0 +1,21 @@
+/*
+ * Base
+ */
+
+html, body {
+ background: #fcfcfc;
+ font-family: $body-font;
+ font-size: 14px;
+ line-height: 1.6;
+ color: $text-color;
+}
+
+body {
+ padding: 16px;
+ max-width: $column * 3 + 32px;
+ margin: 0 auto;
+}
+
+pre, code {
+ font-family: $monospace-font;
+}
diff --git a/_sass/2017/components/h2-section.scss b/_sass/2017/components/h2-section.scss
new file mode 100644
index 000000000..46d5f5fbb
--- /dev/null
+++ b/_sass/2017/components/h2-section.scss
@@ -0,0 +1,14 @@
+/*
+ * h2 section
+ */
+
+/* Hide the first h2 heading */
+.h2-section {
+ & > h2 {
+ margin-top: 64px;
+ }
+
+ &:first-child > h2 {
+ display: none;
+ }
+}
diff --git a/_sass/2017/components/h3-section-list.scss b/_sass/2017/components/h3-section-list.scss
new file mode 100644
index 000000000..f9452b523
--- /dev/null
+++ b/_sass/2017/components/h3-section-list.scss
@@ -0,0 +1,116 @@
+/*
+ * H3 section list:
+ * The body that is isotoped.
+ */
+
+.h3-section-list {
+ & {
+ margin: 0 auto;
+ }
+
+ // Clearfix
+ &::after {
+ content: '';
+ display: table;
+ clear: both;
+ zoom: 1;
+ }
+
+ // Each section
+ & > .h3-section {
+ float: left;
+ padding: $gutter / 2;
+ width: 100%;
+ }
+
+ @media (min-width: 768px) {
+ & > .h3-section {
+ width: 50%;
+ }
+
+ & > .h3-section.-wide {
+ width: 100%;
+ }
+
+ & > .h3-section.-halfwide {
+ width: 100%;
+ }
+ }
+
+ @media (min-width: 960px) {
+ & > .h3-section {
+ width: 33.33%;
+ }
+
+ & > .h3-section.-wide {
+ width: 66.67%;
+ }
+
+ & > .h3-section.-halfwide {
+ width: 50%;
+ }
+ }
+}
+
+/*
+ * Two column (default)
+ */
+
+.h3-section-list,
+.h3-section-list.-two-column {
+ @media (min-width: 768px) {
+ & > .h3-section {
+ width: 50%;
+ }
+ }
+}
+
+/*
+ * One column
+ */
+
+.h3-section-list.-one-column {
+ & > .h3-section {
+ width: 100%;
+ }
+}
+
+/*
+ * Three column
+ */
+
+.h3-section-list.-three-column {
+ @media (min-width: 768px) {
+ & > .h3-section {
+ width: 50%;
+ }
+ }
+
+ @media (min-width: 960px) {
+ & > .h3-section {
+ width: 33.33%;
+ }
+ }
+}
+
+/*
+ * Three column, left reference
+ */
+
+.h3-section-list.-left-reference {
+ @media (min-width: 768px) {
+ & > .h3-section {
+ width: 50%;
+ }
+ }
+
+ @media (min-width: 960px) {
+ & > .h3-section {
+ width: 66.67%;
+ }
+
+ & > .h3-section:first-child {
+ width: 33.33%;
+ }
+ }
+}
diff --git a/_sass/2017/components/h3-section.scss b/_sass/2017/components/h3-section.scss
new file mode 100644
index 000000000..1ec9711dd
--- /dev/null
+++ b/_sass/2017/components/h3-section.scss
@@ -0,0 +1,80 @@
+/*
+ * H3 section
+ */
+
+.h3-section > .body {
+ & > pre {
+ margin: 0;
+ padding: 16px;
+ font-size: 12px;
+ overflow: auto;
+ background: white;
+ }
+
+ & {
+ background: white;
+ box-shadow:
+ 0 4px 5px rgba(80, 100, 150, 0.05),
+ 0 2px 3px rgba(80, 100, 150, 0.1);
+ }
+
+ & > ul {
+ margin: 0;
+ padding: 0;
+ list-style-type: none;
+ }
+
+ & > ul > li {
+ padding: 8px 16px;
+ padding-left: 32px;
+ position: relative;
+ }
+
+ & > ul > li::before {
+ content: '';
+ position: absolute;
+ display: inline-block;
+ width: 8px;
+ height: 8px;
+ background: #666;
+ border-radius: 50%;
+ left: 16px;
+ top: 16px;
+ }
+
+ & > ul > li + li {
+ border-top: solid 1px $line-color;
+ }
+
+ /* Paragraphs */
+ & > p {
+ padding: 16px;
+ margin: 0;
+ }
+
+ /* Description paragraphs */
+ & > pre ~ p,
+ & > ul ~ p,
+ & > iframe ~ p,
+ & > table ~ p {
+ background: $gray-bg;
+ color: $gray-text;
+
+ /* Links */
+ & a,
+ & a:visited {
+ color: $text-color;
+ text-decoration: none;
+ border-bottom: solid 1px $line-color;
+ }
+
+ & a:hover {
+ color: $baseB-400;
+ }
+ }
+
+ /* Line divisions */
+ & > *:not(:first-child) {
+ border-top: solid 1px $line-color;
+ }
+}
diff --git a/_sass/2017/markdown/headings.scss b/_sass/2017/markdown/headings.scss
new file mode 100644
index 000000000..e778e629f
--- /dev/null
+++ b/_sass/2017/markdown/headings.scss
@@ -0,0 +1,57 @@
+/*
+ * MarkdownBody context
+ */
+
+.main-heading,
+.MarkdownBody h1,
+.MarkdownBody h2 {
+ font-weight: 300;
+ font-family: $body-font;
+ margin: $gutter / 2;
+ padding: 0;
+ margin-bottom: 16px;
+ padding-bottom: 16px;
+ border-bottom: solid 1px $line-color;
+}
+
+.main-heading,
+.MarkdownBody h1 {
+ font-size: 3.2em;
+}
+
+.MarkdownBody h2 {
+ font-size: 2.4em;
+}
+
+.MarkdownBody h3 {
+ margin: 0;
+ padding: 0;
+ margin-bottom: 16px;
+ font-family: $body-font;
+ font-size: 1.66em;
+ font-weight: 300;
+ color: $baseA-400;
+}
+
+.MarkdownBody {
+ a,
+ a:visited {
+ color: $baseB-400;
+ text-decoration: none;
+ }
+
+ a:hover {
+ text-decoration: underline;
+ }
+
+ em {
+ font-style: normal;
+ color: $gray-text;
+ }
+
+ iframe {
+ border: 0;
+ margin: 0;
+ width: 100%;
+ }
+}
diff --git a/_sass/2017/markdown/table.scss b/_sass/2017/markdown/table.scss
new file mode 100644
index 000000000..557b9c052
--- /dev/null
+++ b/_sass/2017/markdown/table.scss
@@ -0,0 +1,64 @@
+/*
+ * Table
+ */
+
+.MarkdownBody table {
+ & {
+ width: 100%;
+ }
+
+ & tr + tr {
+ border-top: solid 1px $line-color;
+ }
+
+ /* Horizontal lines */
+ & tbody + tbody {
+ border-top: solid 1px $dark-line-color;
+ }
+
+ & td,
+ & th {
+ padding: 8px 16px;
+ }
+
+ & tr td:last-child {
+ text-align: right;
+ }
+
+ & td:first-child > code {
+ font-size: 0.86em;
+ color: #35a;
+ }
+
+ & a,
+ & a:visited {
+ color: #35a;
+ text-decoration: none;
+ }
+
+ & td:first-child > code ~ em {
+ font-style: normal;
+ font-size: 0.86em;
+ color: $gray-text;
+ }
+
+ & thead {
+ display: none;
+ }
+}
+
+.MarkdownBody table.-shortcuts {
+ code {
+ font-size: 1em;
+ padding: 3px 8px;
+ background: #fafafa;
+ box-shadow:
+ 0 2px 0 rgba(0, 0, 0, 0.2),
+ inset 0 0 0 1px rgba(0, 0, 0, 0.05);
+ border-radius: 3px;
+ margin-right: 2px;
+ letter-spacing: 0.2em;
+ font-family: roboto;
+ color: $text-color;
+ }
+}
diff --git a/_sass/2017/style.scss b/_sass/2017/style.scss
new file mode 100644
index 000000000..6f4f16648
--- /dev/null
+++ b/_sass/2017/style.scss
@@ -0,0 +1,22 @@
+$gutter: 32px;
+$column: 400px;
+
+$body-font: roboto, sans-serif;
+$monospace-font: menlo, monospace;
+
+$gray-bg: #fcfcfc;
+$gray-text: #678;
+$text-color: #333;
+$baseA-400: #53a;
+$baseB-400: #35a;
+$line-color: #f5f5f5;
+$dark-line-color: #ccc;
+
+@import url('https://unpkg.com/sanitize.css@5.0.0/sanitize.css');
+
+@import './base/base';
+@import './markdown/headings';
+@import './markdown/table';
+@import './components/h2-section';
+@import './components/h3-section-list';
+@import './components/h3-section';
diff --git a/assets/2017/style.css b/assets/2017/style.css
deleted file mode 100644
index bd73e6e8f..000000000
--- a/assets/2017/style.css
+++ /dev/null
@@ -1,248 +0,0 @@
-@import url('https://unpkg.com/sanitize.css@5.0.0/sanitize.css');
-
-:root {
- --gutter: 32px;
- --column: 400px;
-
- --body-font: roboto, sans-serif;
- --monospace-font: menlo, monospace;
-
- --gray-bg: #fcfcfc;
- --gray-text: #678;
- --text-color: #333;
- --baseA-400: #53a;
- --line-color: #f5f5f5;
- --dark-line-color: #ccc;
-}
-
-/*
- * Base
- */
-
-html, body {
- background: #fcfcfc;
- font-family: var(--body-font);
- font-size: 14px;
- line-height: 1.6;
- color: var(--text-color);
-}
-
-body {
- padding: 16px;
- max-width: calc(var(--column) * 3 + 32px);
- margin: 0 auto;
-}
-
-pre, code {
- font-family: var(--monospace-font);
-}
-
-/*
- * MarkdownBody context
- */
-
-.main-heading,
-.MarkdownBody h1,
-.MarkdownBody h2 {
- font-weight: 300;
- font-family: var(--body-font);
- margin: calc(var(--gutter) / 2);
- padding: 0;
- margin-bottom: 16px;
- padding-bottom: 16px;
- border-bottom: solid 1px var(--line-color);
-}
-
-.main-heading,
-.MarkdownBody h1 {
- font-size: 3.2em;
-}
-
-.MarkdownBody h2 {
- font-size: 2.4em;
-}
-
-.MarkdownBody h3 {
- margin: 0;
- padding: 0;
- margin-bottom: 16px;
- font-family: var(--body-font);
- font-size: 1.66em;
- font-weight: 300;
- color: var(--baseA-400);
-}
-
-/*
- * h2 section
- */
-
-/* Hide the first h2 heading */
-.h2-section > h2 {
- margin-top: 64px;
-}
-
-.h2-section:first-child > h2 {
- display: none;
-}
-
-/*
- * H3 section list:
- * The body that is isotoped.
- */
-
-.h3-section-list {
- margin: 0 auto;
-}
-
-.h3-section-list::after {
- content: '';
- display: table;
- clear: both;
- zoom: 1;
-}
-.h3-section-list > .h3-section {
- float: left;
- padding: calc(var(--gutter) / 2);
- width: 100%;
-}
-
-@media (min-width: 768px) {
- .h3-section-list > .h3-section {
- width: 50%;
- }
-
- .h3-section-list > .h3-section.-wide {
- width: 100%;
- }
-
- .h3-section-list > .h3-section.-halfwide {
- width: 100%;
- }
-}
-
-@media (min-width: 960px) {
- .h3-section-list > .h3-section {
- width: 33.33%;
- }
-
- .h3-section-list > .h3-section.-wide {
- width: 66.67%;
- }
-
- .h3-section-list > .h3-section.-halfwide {
- width: 50%;
- }
-}
-
-/*
- * H3 section
- */
-
-.h3-section > .body > pre {
- margin: 0;
- padding: 16px;
- font-size: 12px;
- overflow: auto;
- background: white;
-}
-
-.h3-section > .body {
- background: white;
- box-shadow: 0 4px 5px rgba(80, 100, 150, 0.05), 0 2px 3px rgba(80, 100, 150, 0.1);
-}
-
-.h3-section > .body > ul {
- margin: 0;
- padding: 0;
- list-style-type: none;
-}
-
-.h3-section > .body > ul > li {
- padding: 8px 16px;
- padding-left: 32px;
- position: relative;
-}
-
-.h3-section > .body > ul > li::before {
- content: '';
- position: absolute;
- display: inline-block;
- width: 8px;
- height: 8px;
- background: #666;
- border-radius: 50%;
- left: 16px;
- top: 16px;
-}
-
-.h3-section > .body > ul > li + li {
- border-top: solid 1px var(--line-color);
-}
-
-/* Description paragraphs */
-.h3-section > .body > pre ~ p,
-.h3-section > .body > ul ~ p,
-.h3-section > .body > table ~ p {
- padding: 16px;
- margin: 0;
- background: var(--gray-bg);
- color: var(--gray-text);
-}
-
-.h3-section > .body > p > a,
-.h3-section > .body > p > a:visited {
- color: var(--text-color);
- text-decoration: none;
- border-bottom: solid 1px var(--line-color);
-}
-
-.h3-section > .body > *:not(:first-child) {
- border-top: solid 1px var(--line-color);
-}
-
-/*
- * Table
- */
-
-table {
- width: 100%;
-}
-
-table tr + tr {
- border-top: solid 1px var(--line-color);
-}
-
-/* Horizontal lines */
-table tbody + tbody {
- border-top: solid 1px var(--dark-line-color);
-}
-
-table td,
-table th {
- padding: 8px 16px;
-}
-
-table tr td:last-child {
- text-align: right;
-}
-
-table code {
- font-size: 0.86em;
- color: #35a;
-}
-
-table a,
-table a:visited {
- color: #35a;
- text-decoration: none;
-}
-
-table code ~ em {
- font-style: normal;
- font-size: 0.86em;
- color: var(--gray-text);
-}
-
-table thead {
- display: none;
-}
diff --git a/assets/2017/style.scss b/assets/2017/style.scss
new file mode 100644
index 000000000..45cd10867
--- /dev/null
+++ b/assets/2017/style.scss
@@ -0,0 +1,3 @@
+---
+---
+@import '2017/style.scss';
diff --git a/react.md b/react.md
index f5e8443d2..d75cb3c70 100644
--- a/react.md
+++ b/react.md
@@ -6,14 +6,41 @@ layout: 2017/sheet
{%raw%}
-Getting started
----------------
+Example
+-------
+{: .-left-reference}
-{:.-three-column}
+### Basic example
-
+```jsx
+class Hello extends React.Component {
+ render () {
+ return
+ Hello {this.props.name}
+
+ }
+}
+```
-### Getting started
+```jsx
+const el = document.body
+ReactDOM.render(, el)
+```
+
+Use the [React.js jsfiddle](http://jsfiddle.net/reactjs/69z2wepo/) to start hacking. (or the unofficial [jsbin](http://jsbin.com/yafixat/edit?js,output)).
+
+### Try it
+
+
+
+[Open in jsbin](http://jsbin.com/yafixat/edit?js,output)
+{: target="_blank"}
+
+Components
+----------
+{: .-three-column}
+
+### Class components
```jsx
class MyComponent extends React.Component {
@@ -30,8 +57,6 @@ const el = document.body
ReactDOM.render(, el)
```
-Use the [React.js jsfiddle](http://jsfiddle.net/reactjs/69z2wepo/) to start hacking. (or the unofficial [jsbin](http://jsbin.com/yafixat/edit?js,output)).
-
### Functional components
```jsx
@@ -66,34 +91,19 @@ Nest components to separate concerns. See: [multiple components](http://facebook
```jsx
this.forceUpdate()
-this.isMounted()
+```
+```jsx
this.setState({ ... })
this.replaceState({ ... })
+```
+```jsx
this.state
this.props
```
-These are methods available for `Component` instances. See [Component API](http://facebook.github.io/react/docs/component-api.html).
-
-
-### Component specs
-
-| Method | What |
-| ---- | ---- |
-| [`render()`](http://facebook.github.io/react/docs/component-specs.html#render) | |
-| ---- | ---- |
-| [`getInitialState()`](http://facebook.github.io/react/docs/component-specs.html#getinitialstate) | |
-| [`getDefaultProps()`](http://facebook.github.io/react/docs/component-specs.html#getdefaultprops) | |
-| ---- | ---- |
-| [`mixins: [ ... ]`](http://facebook.github.io/react/docs/component-specs.html#mixins) | Mixins ... [more](#mixins) |
-| [`propTypes: { ... }`](http://facebook.github.io/react/docs/component-specs.html#proptypes) | Validation ... [more](#property-validation) |
-| [`statics: { ... }`](http://facebook.github.io/react/docs/component-specs.html#statics) | Static methods |
-| [`displayName: "..."`](http://facebook.github.io/react/docs/component-specs.html#displayname) | Automatically filled by JSX |
-{:.greycode.no-head}
-
-Methods and properties you can override. See [component specs](http://facebook.github.io/react/docs/component-specs.html).
+These methods and properies are available for `Component` instances. See [Component API](http://facebook.github.io/react/docs/component-api.html).
### Properties
@@ -116,7 +126,6 @@ Use [props](https://facebook.github.io/react/docs/tutorial.html#using-props) (`t
```jsx
this.setState({ username: 'rstacruz' })
-this.replaceState({ ... })
```
```jsx
@@ -126,18 +135,19 @@ render () {
}
```
-Use [states](https://facebook.github.io/react/docs/tutorial.html#reactive-state) (`this.state`) to manage dynamic data.
+Use states (`this.state`) to manage dynamic data.
+See [States](https://facebook.github.io/react/docs/tutorial.html#reactive-state).
### Setting default props
```jsx
-class Hello extends React.Component {
- constructor (props) {
- super({ shown: true, ...props })
- }
+Hello.defaultProps = {
+ color: 'blue'
}
```
+See [defaultProps](https://facebook.github.io/react/docs/react-component.html#defaultprops).
+
### Setting default state
```jsx
@@ -151,32 +161,32 @@ class Hello extends React.Component {
Lifecycle
---------
-
-{:.-two-column}
+{: .-two-column}
### Mounting
-| `componentWillMount()` | Before rendering (no DOM yet) |
-| `componentDidMount()` | After rendering |
-| `componentWillUnmount()` | Invoked before DOM removal |
+| `constructor` _(props)_ | Before rendering [#](https://facebook.github.io/react/docs/react-component.html#constructor) |
+| `componentWillMount()` | _Don't use this_ [#](https://facebook.github.io/react/docs/react-component.html#componentwillmount) |
+| `render()` | Render [#](https://facebook.github.io/react/docs/react-component.html#render) |
+| `componentDidMount()` | After rendering (DOM available) [#](https://facebook.github.io/react/docs/react-component.html#componentdidmount) |
+| `componentWillUnmount()` | Before DOM removal [#](https://facebook.github.io/react/docs/react-component.html#componentwillunmount) |
-Before initial rendering occurs. Add your DOM stuff on didMount (events, timers, etc). See [reference](http://facebook.github.io/react/docs/component-specs.html#mounting-componentwillmount).
-
-Clear your DOM stuff in componentWillMount (probably done on didMount). See [reference](http://facebook.github.io/react/docs/component-specs.html#unmounting-componentwillunmount).
+Set initial the state on `constructor()`.
+Add DOM event handlers, timers (etc) on `componentDidMount()`, then remove them on `componentWillUnmount()`.
### Updating
-| `componentWillReceiveProps`*(newProps={})* | Use `setState()` here |
-| `shouldComponentUpdate`*(newProps={}, newState={})* | Skips `render()` if returns false |
-| `componentWillUpdate`*(newProps={}, newState={})* | Can't use `setState()` here |
-| `componentDidUpdate`*(prevProps={}, prevState={})* | Operate on the DOM here |
+| `componentWillReceiveProps` *(newProps)* | Use `setState()` here |
+| `shouldComponentUpdate` *(newProps, newState)* | Skips `render()` if returns false |
+| `componentWillUpdate` *(newProps, newState)* | Can't use `setState()` here |
+| `render()` | Render |
+| `componentDidUpdate` *(prevProps, prevState)* | Operate on the DOM here |
Called when parents change properties and `.setState()`. These are not called for initial renders. See [reference](http://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops).
DOM nodes
---------
-
-{:.-two-column}
+{: .-two-column}
### References
@@ -195,7 +205,7 @@ this.input.focus()
this.input.value()
```
-Allows access to DOM nodes. See [References](http://facebook.github.io/react/docs/more-about-refs.html).
+Allows access to DOM nodes. See [Refs and the DOM](https://facebook.github.io/react/docs/refs-and-the-dom.html).
### DOM Events
@@ -204,7 +214,6 @@ Allows access to DOM nodes. See [References](http://facebook.github.io/react/doc
value={this.state.value}
onChange={this.handleChange} />
```
-{:.light}
```jsx
handleChange: function(event) {
@@ -216,6 +225,7 @@ Add attributes like `onChange`. See [events](https://facebook.github.io/react/do
Property validation
-------------------
+{: .-three-column}
### React.PropTypes
@@ -258,60 +268,51 @@ MyComponent.propTypes = {
}
```
-See [propTypes](http://facebook.github.io/react/docs/reusable-components.html#prop-validation).
-
### Required types
```jsx
-MyComponent.propTypes = {
- requiredFunc: React.PropTypes.func.isRequired,
- requiredAny: React.PropTypes.any.isRequired
+MyCo.propTypes = {
+ name: React.PropTypes.string.isRequired
}
```
-Add `.isRequired`.
-
-### React elements
+### Elements
```jsx
-MyComponent.propTypes = {
+MyCo.propTypes = {
// React element
- element: React.PropTypes.element,
+ element: React.PropTypes.element,
// num, string, element, or an array of those
- node: React.PropTypes.node
+ node: React.PropTypes.node
}
```
-Use `.element`, `.node`.
-
-### Enumerables
+### Enumerables (oneOf)
+```jsx
+MyCo.propTypes = {
+ direction: React.PropTypes.oneOf([
+ 'left', 'right'
+ ])
+}
```
-propTypes: {
- enum: React.PropTypes.oneOf(['M','F']), // enum
- union: React.PropTypes.oneOfType([ // any
- React.PropTypes.string,
- React.PropTypes.number ]),
-```
-
-Use `.oneOf`, `.oneOfType`.
### Arrays and objects
```jsx
-propTypes: {
+MyCo.propTypes = {
array: React.PropTypes.array,
arrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
object: React.PropTypes.object,
objectOf: React.PropTypes.objectOf(React.PropTypes.number),
-
message: React.PropTypes.instanceOf(Message),
object2: React.PropTypes.shape({
color: React.PropTypes.string,
size: React.PropTypes.number
- }),
+ })
+}
```
Use `.array[Of]`, `.object[Of]`, `.instanceOf`, `.shape`.
@@ -319,20 +320,18 @@ Use `.array[Of]`, `.object[Of]`, `.instanceOf`, `.shape`.
### Custom validation
```jsx
-propTypes: {
- customProp: function(props, propName, componentName) {
- if (!/matchme/.test(props[propName])) {
- return new Error('Validation failed!');
+MyCo.propTypes = {
+ customProp: (props, key, componentName) => {
+ if (!/matchme/.test(props[key])) {
+ return new Error('Validation failed!')
}
}
}
```
-Supply your own function.
-
## Other features
-### Propagating properties
+### Transfering props
```html
@@ -347,32 +346,34 @@ class VideoPlayer extends React.Component {
```
Propagates `src="..."` down to the sub-component.
-
See [Transferring props](http://facebook.github.io/react/docs/transferring-props.html).
### Top-level API
```jsx
React.createClass({ ... })
-
React.isValidElement(c)
+```
-ReactDOM.findDOMNode(c) // 0.14+
-ReactDOM.render(, domnode, [callback]) // 0.14+
-ReactDOM.unmountComponentAtNode(domnode) // 0.14+
+```jsx
+ReactDOM.render(, domnode, [callback])
+ReactDOM.unmountComponentAtNode(domnode)
+```
-ReactDOMServer.renderToString() // 0.14+
-ReactDOMServer.renderToStaticMarkup() // 0.14+
+```jsx
+ReactDOMServer.renderToString()
+ReactDOMServer.renderToStaticMarkup()
```
JSX patterns
------------
+{: .-two-column}
### Style shorthand
```jsx
-var style = { backgroundImage: 'url(x.jpg)', height: 10 };
-return ;
+var style = { height: 10 }
+return
```
See [inline styles](https://facebook.github.io/react/tips/inline-styles.html).
@@ -389,23 +390,37 @@ See [dangerously set innerHTML](https://facebook.github.io/react/tips/dangerousl
### Lists
```jsx
-var TodoList = React.createClass({
- render: function() {
- function item(itemText) {
- return {itemText};
- };
- return {this.props.items.map(item)}
;
+class TodoList extends React.Component {
+ render () {
+ const { items } = this.props
+
+ return
+ {items.map(item =>
+ )}
+
}
-});
+}
```
-See also
+Always supply a `key` property.
+
+### Conditionals
+
+```jsx
+
+{showPopup
+ ?
+ : null}
+
+```
+
+Also see
--------
+{: .-one-column}
-{:.-two-column}
+This reference was made for React v15.
-### Also see
-
-* [Animations](http://facebook.github.io/react/docs/animation.html)
+* [React website](http://facebook.github.io/react) _(facebook.github.io)_
+* [Animations](http://facebook.github.io/react/docs/animation.html) _(facebook.github.io)_
{%endraw%}
diff --git a/sh.md b/sh.md
index 011c71e30..ad1d58514 100644
--- a/sh.md
+++ b/sh.md
@@ -1,261 +1,572 @@
---
title: Bash scripting
category: CLI
+layout: 2017/sheet
---
+Getting started
+---------------
+{: .-three-column}
+
+### Example
+
+```bash
+#!/usr/bin/env bash
+
+NAME="John"
+echo "Hello $NAME!"
+```
+
+### Variables
+
+```bash
+NAME="John"
+echo $NAME
+echo "$NAME"
+echo "${NAME}!"
+```
+
+### String quotes
+
+```bash
+NAME="John"
+echo "Hi $NAME" #=> Hi John
+echo 'Hi $NAME' #=> Hi $NAME
+```
+
+### Shell execution
+
+```bash
+echo "I'm in $(pwd)"
+echo "I'm in `pwd`"
+# Same
+```
+
+See [Command substitution](http://wiki.bash-hackers.org/syntax/expansion/cmdsubst)
+
+### Conditional execution
+
+```bash
+git commit && git push
+git commit || echo "Commit failed"
+```
+
+### Functions
+{: id='functions-example'}
+
+```bash
+get_name() {
+ echo "John"
+}
+
+echo "You are $(get_name)"
+```
+
+See: [Functions](#functions)
+
+### Conditionals
+{: id='conditionals-example'}
+
+```bash
+if [ -z "$string" ]; then
+ echo "String is empty"
+elsif [ -n "$string" ]; then
+ echo "String is not empty"
+fi
+```
+
+See: [Conditionals](#conditionals)
+
+### Strict mode
+
+```bash
+set -euo pipefail
+IFS=$'\n\t'
+```
+
+See: [Unofficial bash strict mode](http://redsymbol.net/articles/unofficial-bash-strict-mode/)
+
+### Brace expansion
+
+```bash
+echo {A,B}.js
+```
+
+| `{A,B}` | Same as `A B` |
+| `{A,B}.js` | Same as `A.js B.js` |
+| `{1..5}` | Same as `1 2 3 4 5` |
+
+See: [Brace expansion](http://wiki.bash-hackers.org/syntax/expansion/brace)
+
+
+Parameter expansions
+--------------------
+{: .-three-column}
+
+### Basics
+
+```bash
+name="John"
+echo ${name}
+echo ${name/J/j} #=> "john" (substitution)
+echo ${name:0:2} #=> "jo" (slicing)
+echo ${food:-Cake} #=> $food or "Cake"
+```
+
+```bash
+length=2
+echo ${name:0:length} #=> "jo"
+```
+
+See: [Parameter expansion](http://wiki.bash-hackers.org/syntax/pe)
+
### Pattern substitution
- STR=/path/to/foo.c
+```bash
+STR="/path/to/foo.cpp"
+```
- echo ${STR%.c} #=> "/path/to/foo"
- echo ${STR%.c}.o #=> "/path/to/foo.o"
- echo ${STR##*.} #=> "c" (extension)
+| `${STR%.cpp}` | `/path/to/foo` |
+| `${STR%.cpp}.o` | `/path/to/foo.o` |
+| `${STR##*.}` | `cpp` _(extension)_ |
+| `${STR##*/}` | `foo.cpp` _(basepath)_ |
- BASE=${STR##*/} #=> "foo.c" (basepath)
- DIR=${SRC%$BASE} #=> "/path/to"
+### Directory name
-### Substitutions by regex
+```bash
+SRC="/path/to/foo.cpp"
+BASE=${STR##*/} #=> "foo.cpp" (basepath)
+DIR=${SRC%$BASE} #=> "/path/to" (dirpath)
+```
- echo ${STR/hi/hello} # Replace first match
- echo ${STR//hi/hello} # Replace all matches
- echo ${STR/#hi/hello} # ^hi
- echo ${STR/%hi/hello} # hi$
+### Regexp-like substitution
- echo "${STR:0:3}" # .substr(0, 3) -- position, length
- echo "${STR:-3:3}" # Negative position = from the right
+```bash
+STR="Hey world"
+```
- echo ${#line} # Length of $line
+| `${STR/Hey/Hello}` | Replace first match |
+| `${STR//Hey/Hello}` | Replace all |
+| `${STR/#Hey/Hello}` | Like `^Hey` |
+| `${STR/%world/mundo}` | Like `world$` |
- [ -z "$CC" ] && CC=gcc # CC ||= "gcc" assignment
- ${CC:=gcc} # $CC || "gcc"
- ${CC:-gcc} # same as above
+### Substrings
-### Reading input
+| `${STR:0:3}` | Substring: `"Hey"` _(position, length)_ |
+| `${STR:-3:3}` | Substring from the right |
- echo -n "Proceed? [y/n]: "
- read ans
- echo $ans
+```bash
+STR="Hello world"
+echo ${STR:6:5} # "world"
+echo ${STR:-5:5} # "world"
+```
- read -n 1 ans # Just one character
+### Length
-Loops
------
+| `${#STR}` | Length of `$STR` |
-### Basic for loop
+### Default values
- for i in /etc/rc.*; do
- echo $i
- done
-
-### Ranges
-
- for i in {1..5}; do
- echo "Welcome $i"
- done
-
-### Reading lines
-
- cat file.txt | while read line; do
- echo $line
- done
-
-Functions
----------
-
-### Defining functions
-
- myfunc() { ... }
- fuction myfunc { ... }
- fuction myfunc() { ... }
-
-### Returning strings
-
- myfunc() {
- local myresult='some value'
- echo $myresult
- }
-
- result=$(myfunc)
-
-### Errors
-
- myfunc() { return 1; }
-
-### Arguments
-
- $# # Number of arguments
- $* # All args
- $1 # First argument
-
-Ifs - files
------------
-
- # File conditions
- if [ -a FILE ]; then # -e exists -d directory -f file
- fi # -r readable -w writeable -x executable
- # -h symlink -s size > 0
-
- # File comparisons
- if [ FILE1 -nt FILE2 ] # -nt 1 more recent than 2
- # -ot 2 more recent than 1
- # -ef same files
-
-Ifs
----
-
- # String
- if [ -z STRING ] # empty?
- if [ -n STRING ] # not empty?
-
- # Numeric
- if [ $? -eq 0 ] # -eq -ne -lt -le -gt -ge
- # $? is exit status by the way
-
- # Etc
- if [ -o noclobber ] # if OPTIONNAME is enabled
- if [ ! EXPR ] # not
- if [ ONE -a TWO ] # and
- if [ ONE -o TWO ] # or
-
- # Regex
- if [[ "A" =~ "." ]]
-
-### Numeric comparisons
-
- if (( $a < $b ))
+| `${CC:-gcc}` | `$CC || "gcc"` |
### Unset variables
-Assume `$FOO` is not set. Doing *this* will result in *that*:
+Assuming `$FOO` is not set:
- ${FOO:-word} # Returns word
- ${FOO:+word} # Returns empty, or word if set
- ${FOO:=word} # Sets parameter to word, returns word
- ${FOO:?message} # Echoes message and exits
+| `${FOO:-word}` | Returns `word` |
+| `${FOO:+word}` | Returns empty (or `word` if set) |
+| `${FOO:=word}` | Sets `$FOO` to `word`, returns `word` |
+| `${FOO:?message}` | Echoes message and exits |
- ${FOO=word} # : is optional in all of the above
+The `:` is optional (eg, `${FOO=word}` works)
+
+Loops
+-----
+{: .-three-column}
+
+### Basic for loop
+
+```bash
+for i in /etc/rc.*; do
+ echo $i
+done
+```
+
+### Ranges
+
+```bash
+for i in {1..5}; do
+ echo "Welcome $i"
+done
+```
+
+### Reading lines
+
+```bash
+cat file.txt | while read line; do
+ echo $line
+done
+```
+
+### Forever
+
+```bash
+while true; do
+ ···
+done
+```
+
+Functions
+---------
+{: .-three-column}
+
+### Defining functions
+
+```bash
+myfunc() {
+ echo "hello $1"
+}
+```
+
+```bash
+# Same as above (alternate syntax)
+function myfunc() {
+ echo "hello $1"
+}
+```
+
+```bash
+myfunc "John"
+```
+
+### Returning values
+
+```bash
+myfunc() {
+ local myresult='some value'
+ echo $myresult
+}
+```
+
+```bash
+result=$(myfunc)
+```
+
+### Raising errors
+
+```bash
+myfunc() {
+ return 1
+}
+```
+
+```bash
+if myfunc; then
+ echo "success"
+else
+ echo "failure"
+fi
+```
+
+### Arguments
+
+| Expression | Description |
+| --- | --- |
+| `$#` | Number of arguments |
+| `$*` | All arguments |
+| `$@` | All arguments, starting from first |
+| `$1` | First argument |
+
+See [Special parameters](http://wiki.bash-hackers.org/syntax/shellvars#special_parameters_and_shell_variables).
+
+Conditionals
+------------
+{: .-three-column}
+
+### Conditions
+
+| Condition | Description |
+| --- | --- |
+| `[ -z STRING ]` | Empty string |
+| `[ -n STRING ]` | Not empty string |
+| --- | --- |
+| `[ NUM -eq NUM ]` | Equal |
+| `[ NUM -ne NUM ]` | Not equal |
+| `[ NUM -lt NUM ]` | Less than |
+| `[ NUM -le NUM ]` | Less than or equal |
+| `[ NUM -gt NUM ]` | Greater than |
+| `[ NUM -ge NUM ]` | Greater than or equal |
+| --- | --- |
+| `[[ STRING =~ STRING ]]` | Regexp |
+| --- | --- |
+| `(( NUM < NUM ))` | Numeric conditions |
+
+| Condition | Description |
+| --- | --- |
+| `[ -o noclobber ]` | If OPTIONNAME is enabled |
+| --- | --- |
+| `[ ! EXPR ]` | Not |
+| `[ X ] && [ Y ]` | And |
+| `[ X ] || [ Y ]` | Or |
+
+### File conditions
+
+| Condition | Description |
+| --- | --- |
+| `[ -e FILE ]` | Exists |
+| `[ -r FILE ]` | Readable |
+| `[ -h FILE ]` | Symlink |
+| `[ -d FILE ]` | Directory |
+| `[ -w FILE ]` | Writable |
+| `[ -s FILE ]` | Size is > 0 bytes |
+| `[ -f FILE ]` | File |
+| `[ -x FILE ]` | Executable |
+| --- | --- |
+| `[ FILE1 -nt FILE2 ]` | 1 is more recent than 2 |
+| `[ FILE1 -ot FILE2 ]` | 2 is more recent than 1 |
+| `[ FILE1 -ef FILE2 ]` | Same files |
+
+### Example
+
+```bash
+# String
+if [ -z "$string" ]; then
+ echo "String is empty"
+elsif [ -n "$string" ]; then
+ echo "String is not empty"
+fi
+```
+
+```bash
+# Combinations
+if [ X ] && [ Y ]; then
+ ...
+fi
+```
+
+```bash
+# Regex
+if [[ "A" =~ "." ]]
+```
+
+```bash
+if (( $a < $b ))
+```
+
+```bash
+if [ -e "file.txt" ]; then
+ echo "file exists"
+fi
+```
Numeric calculations
--------------------
- $((RANDOM%=200)) # Random number 0..200
- $((a + 200)) # $ is optional
+```bash
+$((RANDOM%=200)) # Random number 0..200
+$((a + 200)) # $ is optional
+```
Arrays
------
- # Declaring using declare -a
- declare -a Fruits=('Apple' 'Banana' 'Orange')
+### Defining arrays
- Fruits[0]="Apple"
- Fruits[1]="Banana"
- Fruits[2]="Orange"
+```bash
+Fruits=('Apple' 'Banana' 'Orange')
+```
- echo ${Fruits[0]} # Element #0
- echo ${Fruits[@]} # All elements, space-separated
- echo ${#Fruits[@]} # Number of elements
- echo ${#Fruits} # String length of the 1st element
- echo ${#Fruits[3]} # String length of the Nth element
- echo ${Fruits[@]:3:2} # Range (from position 3, length 2)
+```bash
+Fruits[0]="Apple"
+Fruits[1]="Banana"
+Fruits[2]="Orange"
+```
+
+### Working with arrays
+
+```bash
+echo ${Fruits[0]} # Element #0
+echo ${Fruits[@]} # All elements, space-separated
+echo ${#Fruits[@]} # Number of elements
+echo ${#Fruits} # String length of the 1st element
+echo ${#Fruits[3]} # String length of the Nth element
+echo ${Fruits[@]:3:2} # Range (from position 3, length 2)
+```
### Operations
- Fruits=("${Fruits[@]}" "Watermelon") # Push
- Fruits=( ${Fruits[@]/Ap*/} ) # Remove by regex match
- unset Fruits[2] # Remove one item
- Fruits=("${Fruits[@]}") # Duplicate
- Fruits=("${Fruits[@]}" "${Veggies[@]}") # Concatenate
- lines=(`cat "logfile"`) # Read from file
+```bash
+Fruits=("${Fruits[@]}" "Watermelon") # Push
+Fruits=( ${Fruits[@]/Ap*/} ) # Remove by regex match
+unset Fruits[2] # Remove one item
+Fruits=("${Fruits[@]}") # Duplicate
+Fruits=("${Fruits[@]}" "${Veggies[@]}") # Concatenate
+lines=(`cat "logfile"`) # Read from file
+```
### Iteration
- for i in "${arrayName[@]}"; do
- echo $i
- done
+```bash
+for i in "${arrayName[@]}"; do
+ echo $i
+done
+```
-Misc crap
----------
-
- command -V cd #=> "cd is a function/alias/whatever"
+Options
+-------
### Options
- set -o noclobber # Avoid overlay files (echo "hi" > foo)
- set -o errexit # Used to exit upon error, avoiding cascading errors
- set -o pipefail # Unveils hidden failures
- set -o nounset # Exposes unset variables
+```bash
+set -o noclobber # Avoid overlay files (echo "hi" > foo)
+set -o errexit # Used to exit upon error, avoiding cascading errors
+set -o pipefail # Unveils hidden failures
+set -o nounset # Exposes unset variables
+```
### Glob options
- set -o nullglob # Non-matching globs are removed ('*.foo' => '')
- set -o failglob # Non-matching globs throw errors
- set -o nocaseglob # Case insensitive globs
- set -o globdots # Wildcards match dotfiles ("*.sh" => ".foo.sh")
- set -o globstar # Allow ** for recursive matches ('lib/**/*.rb' => 'lib/a/b/c.rb')
+```bash
+set -o nullglob # Non-matching globs are removed ('*.foo' => '')
+set -o failglob # Non-matching globs throw errors
+set -o nocaseglob # Case insensitive globs
+set -o globdots # Wildcards match dotfiles ("*.sh" => ".foo.sh")
+set -o globstar # Allow ** for recursive matches ('lib/**/*.rb' => 'lib/a/b/c.rb')
+```
-set GLOBIGNORE as a colon-separated list of patterns to be removed from glob
+Set `GLOBIGNORE` as a colon-separated list of patterns to be removed from glob
matches.
+Miscellaneous
+-------------
+
+### Subshells
+
+```bash
+(cd somedir; echo "I'm now in $PWD")
+pwd # still in first directory
+```
+
+### Redirection
+
+```bash
+python hello.py > output.txt # stdout to (file)
+python hello.py >> output.txt # stdout to (file), append
+python hello.py 2> error.log # stderr to (file)
+python hello.py 2>&1 # stdout to stderr
+python hello.py 2>/dev/null # stderr to (null)
+```
+
+```bash
+python hello.py < foo.txt
+```
+
+### Inspecting commands
+
+```bash
+command -V cd
+#=> "cd is a function/alias/whatever"
+```
+
### Trap errors
- trap 'echo Error at about $LINENO' ERR
+```bash
+trap 'echo Error at about $LINENO' ERR
+```
or
- traperr() {
- echo "ERROR: ${BASH_SOURCE[1]} at about ${BASH_LINENO[0]}"
- }
+```bash
+traperr() {
+ echo "ERROR: ${BASH_SOURCE[1]} at about ${BASH_LINENO[0]}"
+}
- set -o errtrace
- trap traperr ERR
+set -o errtrace
+trap traperr ERR
+```
### Case/switch
- case $1 in
- start | up)
- vagrant up
- ;;
+```bash
+case "$1" in
+ start | up)
+ vagrant up
+ ;;
- *)
- echo "Usage: $0 {start|stop|ssh}"
- ;;
- esac
+ *)
+ echo "Usage: $0 {start|stop|ssh}"
+ ;;
+esac
+```
### Source relative
- source "${0%/*}/../share/foo.sh"
+```bash
+source "${0%/*}/../share/foo.sh"
+```
### printf
- printf "Hello %s, I'm %s" Sven Olga
+```bash
+printf "Hello %s, I'm %s" Sven Olga
+#=> "Hello Sven, I'm Olga
+```
### Directory of script
- DIR="${0%/*}"
+```bash
+DIR="${0%/*}"
+```
### Getting options
- while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do case $1 in
- -V | --version )
- echo $version
- exit
- ;;
- -s | --string )
- shift; string=$1
- ;;
- -f | --flag )
- flag=1
- ;;
- esac; shift; done
- if [[ "$1" == '--' ]]; then shift; fi
+```bash
+while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do case $1 in
+ -V | --version )
+ echo $version
+ exit
+ ;;
+ -s | --string )
+ shift; string=$1
+ ;;
+ -f | --flag )
+ flag=1
+ ;;
+esac; shift; done
+if [[ "$1" == '--' ]]; then shift; fi
+```
### Heredoc
- cat <