diff --git a/lib/commands/start.js b/lib/commands/start.js
index 5b7a96e831..3dcc529b73 100644
--- a/lib/commands/start.js
+++ b/lib/commands/start.js
@@ -50,7 +50,7 @@ module.exports = async function start(siteDir, cliOptions = {}) {
}
const port = await getPort(cliOptions.port);
- const hotPort = await getPort(port + 1);
+ const hotPort = await getPort(5555);
const {baseUrl} = props;
// create compiler from generated webpack config
diff --git a/lib/core/App.js b/lib/core/App.js
index 6cef21dbbe..dd1ca50162 100644
--- a/lib/core/App.js
+++ b/lib/core/App.js
@@ -3,5 +3,6 @@ import {renderRoutes} from 'react-router-config';
import routes from '@generated/routes'; // eslint-disable-line
import docsData from '@generated/docsData'; // eslint-disable-line
import pagesData from '@generated/pagesData'; // eslint-disable-line
+import config from '@site/siteConfig.js'; //eslint-disable-line
-export default () => renderRoutes(routes, {docsData, pagesData});
+export default () => renderRoutes(routes, {docsData, pagesData, config});
diff --git a/lib/theme/Docs.js b/lib/theme/Docs.js
deleted file mode 100644
index ac697dde59..0000000000
--- a/lib/theme/Docs.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import React from 'react';
-import Layout from '@theme/Layout';
-
-export default class Docs extends React.Component {
- render() {
- return {this.props.children};
- }
-}
diff --git a/lib/theme/Docs/index.js b/lib/theme/Docs/index.js
new file mode 100644
index 0000000000..5a6ff22aa4
--- /dev/null
+++ b/lib/theme/Docs/index.js
@@ -0,0 +1,38 @@
+/* eslint-disable */
+import React from 'react';
+import Helmet from 'react-helmet';
+import styles from './styles.css';
+import Layout from '@theme/Layout'; // eslint-disable-line
+
+export default class Docs extends React.Component {
+ render() {
+ const {location, docsData, config} = this.props;
+ const currentDoc = docsData.find(data => data.path === location.pathname);
+
+ const highlight = Object.assign(
+ {},
+ {
+ version: '9.12.0',
+ theme: 'default'
+ },
+ config.highlight
+ );
+
+ // Use user-provided themeUrl if it exists, else construct one from version and theme.
+ const highlightThemeURL = highlight.themeUrl
+ ? highlight.themeUrl
+ : `//cdnjs.cloudflare.com/ajax/libs/highlight.js/${
+ highlight.version
+ }/styles/${highlight.theme}.min.css`;
+
+ return (
+
+
+ {currentDoc.title || 'Document'}
+
+
+ {this.props.children}
+
+ );
+ }
+}
diff --git a/lib/theme/layout.css b/lib/theme/Docs/styles.css
similarity index 100%
rename from lib/theme/layout.css
rename to lib/theme/Docs/styles.css
diff --git a/lib/theme/Layout.js b/lib/theme/Layout.js
deleted file mode 100644
index 259b8c37dc..0000000000
--- a/lib/theme/Layout.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react';
-import style from './layout.css';
-
-export default class Layout extends React.Component {
- render() {
- const {children} = this.props;
- return
{children}
;
- }
-}
diff --git a/lib/theme/Layout/index.js b/lib/theme/Layout/index.js
new file mode 100644
index 0000000000..fc677cc836
--- /dev/null
+++ b/lib/theme/Layout/index.js
@@ -0,0 +1,27 @@
+import React from 'react';
+import {Link} from 'react-router-dom';
+import styles from './styles.css';
+
+/* eslint-disable react/prefer-stateless-function */
+export default class Layout extends React.Component {
+ render() {
+ console.log(this.props);
+ const {children, pagesData, docsData, location} = this.props;
+ const routeLinks = [...pagesData, ...docsData].map(
+ data =>
+ data.path !== location.pathname && (
+
+ {data.path}
+
+ )
+ );
+ return (
+
+ );
+ }
+}
diff --git a/lib/theme/Layout/styles.css b/lib/theme/Layout/styles.css
new file mode 100644
index 0000000000..fe65cd056e
--- /dev/null
+++ b/lib/theme/Layout/styles.css
@@ -0,0 +1,38 @@
+.footer {
+ color: #777;
+ padding: 10px 15px;
+ height: 20px;
+ text-align: center;
+ border-top: 1px solid #e6e6e6;
+}
+
+.routeLinks {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ position: absolute;
+ right: 0;
+ left: 0;
+}
+
+.routeLinks li {
+ display: inline;
+}
+
+.routeLinks li a {
+ color: inherit;
+ margin: 3px;
+ padding: 3px 7px;
+ text-decoration: none;
+ border: 1px solid transparent;
+ border-radius: 3px;
+}
+
+.routeLinks li a.selected,
+.routeLinks li a:hover {
+ border-color: rgba(175, 47, 47, 0.1);
+}
+
+.routeLinks li a.selected {
+ border-color: rgba(175, 47, 47, 0.2);
+}
\ No newline at end of file
diff --git a/lib/webpack/server.js b/lib/webpack/server.js
index 33b6fe0c29..bc236de34d 100644
--- a/lib/webpack/server.js
+++ b/lib/webpack/server.js
@@ -26,7 +26,7 @@ module.exports = function createServerConfig(props) {
paths
}
]);
-
+
// show compilation progress bar and build time
config
.plugin('niceLog')
diff --git a/website/components/Tictactoe/board.js b/website/components/Tictactoe/board.js
new file mode 100644
index 0000000000..a189cb5a45
--- /dev/null
+++ b/website/components/Tictactoe/board.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import Square from './square';
+import styles from './styles.css';
+
+export default class Board extends React.Component {
+ renderSquare(i) {
+ return (
+ this.props.onClick(i)}
+ />
+ );
+ }
+
+ render() {
+ return (
+
+
+ {this.renderSquare(0)}
+ {this.renderSquare(1)}
+ {this.renderSquare(2)}
+
+
+ {this.renderSquare(3)}
+ {this.renderSquare(4)}
+ {this.renderSquare(5)}
+
+
+ {this.renderSquare(6)}
+ {this.renderSquare(7)}
+ {this.renderSquare(8)}
+
+
+ );
+ }
+}
diff --git a/website/components/Tictactoe/index.js b/website/components/Tictactoe/index.js
new file mode 100644
index 0000000000..9602eaa1e7
--- /dev/null
+++ b/website/components/Tictactoe/index.js
@@ -0,0 +1,106 @@
+import React from 'react';
+import Helmet from 'react-helmet';
+import Layout from '@theme/Layout';
+import Board from './board';
+import styles from './styles.css';
+
+class Game extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ history: [
+ {
+ squares: Array(9).fill(null)
+ }
+ ],
+ stepNumber: 0,
+ xIsNext: true
+ };
+ }
+
+ calculateWinner(squares) {
+ const lines = [
+ [0, 1, 2],
+ [3, 4, 5],
+ [6, 7, 8],
+ [0, 3, 6],
+ [1, 4, 7],
+ [2, 5, 8],
+ [0, 4, 8],
+ [2, 4, 6]
+ ];
+ for (let i = 0; i < lines.length; i++) {
+ const [a, b, c] = lines[i];
+ if (
+ squares[a] &&
+ squares[a] === squares[b] &&
+ squares[a] === squares[c]
+ ) {
+ return squares[a];
+ }
+ }
+ return null;
+ }
+
+ handleClick(i) {
+ const history = this.state.history.slice(0, this.state.stepNumber + 1);
+ const current = history[history.length - 1];
+ const squares = current.squares.slice();
+ if (this.calculateWinner(squares) || squares[i]) {
+ return;
+ }
+ squares[i] = this.state.xIsNext ? 'X' : 'O';
+ this.setState({
+ history: history.concat([
+ {
+ squares: squares
+ }
+ ]),
+ stepNumber: history.length,
+ xIsNext: !this.state.xIsNext
+ });
+ }
+
+ jumpTo(step) {
+ this.setState({
+ stepNumber: step,
+ xIsNext: step % 2 === 0
+ });
+ }
+
+ render() {
+ const history = this.state.history;
+ const current = history[this.state.stepNumber];
+ const winner = this.calculateWinner(current.squares);
+
+ const moves = history.map((step, move) => {
+ const desc = move ? 'Go to move #' + move : 'Go to game start';
+ return (
+
+
+
+ );
+ });
+
+ let status;
+ if (winner) {
+ status = 'Winner: ' + winner;
+ } else {
+ status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
+ }
+
+ return (
+
+
+ this.handleClick(i)} />
+
+
+
+ );
+ }
+}
+
+export default Game;
diff --git a/website/components/Tictactoe/square.js b/website/components/Tictactoe/square.js
new file mode 100644
index 0000000000..1fed93a270
--- /dev/null
+++ b/website/components/Tictactoe/square.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import styles from './styles.css';
+
+export default props => {
+ return (
+
+ );
+};
diff --git a/website/pages/tictactoe.css b/website/components/Tictactoe/styles.css
similarity index 81%
rename from website/pages/tictactoe.css
rename to website/components/Tictactoe/styles.css
index 4a1a102c6e..5d5a305eba 100644
--- a/website/pages/tictactoe.css
+++ b/website/components/Tictactoe/styles.css
@@ -30,6 +30,9 @@
.game {
display: flex;
flex-direction: row;
+ margin-left: auto;
+ margin-right: auto;
+ justify-content: center;
}
.gameInfo {
diff --git a/website/components/Todo/TodoItem.js b/website/components/Todo/TodoItem.js
new file mode 100644
index 0000000000..a6a68879d5
--- /dev/null
+++ b/website/components/Todo/TodoItem.js
@@ -0,0 +1,94 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import classNames from 'classnames';
+
+const ESCAPE_KEY = 27;
+const ENTER_KEY = 13;
+
+export default class TodoItem extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ editText: props.todo.title
+ };
+ this.handleEdit = this.handleEdit.bind(this);
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.handleKeyDown = this.handleKeyDown.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ return (
+ nextProps.todo !== this.props.todo ||
+ nextProps.editing !== this.props.editing ||
+ nextState.editText !== this.state.editText
+ );
+ }
+
+ componentDidUpdate(prevProps) {
+ if (!prevProps.editing && this.props.editing) {
+ const node = ReactDOM.findDOMNode(this.refs.editField);
+ node.focus();
+ node.setSelectionRange(node.value.length, node.value.length);
+ }
+ }
+
+ handleSubmit() {
+ const val = this.state.editText.trim();
+ if (val) {
+ this.props.onSave(val);
+ this.setState({editText: val});
+ } else {
+ this.props.onDestroy();
+ }
+ }
+
+ handleEdit() {
+ this.props.onEdit();
+ this.setState({editText: this.props.todo.title});
+ }
+
+ handleKeyDown(event) {
+ if (event.which === ESCAPE_KEY) {
+ this.setState({editText: this.props.todo.title});
+ this.props.onCancel(event);
+ } else if (event.which === ENTER_KEY) {
+ this.handleSubmit(event);
+ }
+ }
+
+ handleChange(event) {
+ if (this.props.editing) {
+ this.setState({editText: event.target.value});
+ }
+ }
+
+ render() {
+ return (
+
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/website/components/Todo/TodoList.js b/website/components/Todo/TodoList.js
new file mode 100644
index 0000000000..070a815c16
--- /dev/null
+++ b/website/components/Todo/TodoList.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import TodoItem from './TodoItem';
+
+export default function TodoList(props) {
+ const todoItems = props.todos.map(todo => (
+ {
+ props.onToggle(todo);
+ }}
+ onDestroy={() => {
+ props.onDestroy(todo);
+ }}
+ onEdit={() => {
+ props.onEdit(todo);
+ }}
+ editing={props.editing(todo)}
+ onSave={text => {
+ props.onSave(todo, text);
+ }}
+ onCancel={() => {
+ props.onCancel();
+ }}
+ />
+ ));
+
+ return {todoItems}
;
+}
diff --git a/website/components/Todo/index.js b/website/components/Todo/index.js
new file mode 100644
index 0000000000..ec158d4a28
--- /dev/null
+++ b/website/components/Todo/index.js
@@ -0,0 +1,209 @@
+import React from 'react';
+import Helmet from 'react-helmet';
+import TodoList from './TodoList';
+
+const ENTER_KEY = 13;
+
+function uuid() {
+ function s4() {
+ return Math.floor((1 + Math.random()) * 0x10000)
+ .toString(16)
+ .substring(1);
+ }
+ return `${s4()}${s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`;
+}
+
+const todos = [
+ {
+ id: 'ed0bcc48-bbbe-5f06-c7c9-2ccb0456ceba',
+ title: 'Build this Todo App.',
+ completed: true
+ },
+ {
+ id: '42582304-3c6e-311e-7f88-7e3791caf88c',
+ title: '?????',
+ completed: true
+ },
+ {
+ id: '1cf63885-5f75-8deb-19dc-9b6765deae6c',
+ title: '1,000 stars on GitHub.',
+ completed: false
+ },
+ {
+ id: '63a871b2-0b6f-4427-9c35-304bc680a4b7',
+ title: 'Write a popular medium post.',
+ completed: false
+ },
+ {
+ id: '63a871b2-0b6f-4422-9c35-304bc680a4b7',
+ title: 'Earn money through open source work.',
+ completed: false
+ },
+ {
+ id: '036af7f9-1181-fb8f-258f-3f06034c020f',
+ title: 'Write a blog post.',
+ completed: false
+ }
+];
+
+class TodoApp extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ editing: null,
+ newTodo: '',
+ todos: todos
+ };
+ }
+
+ handleChange(event) {
+ this.setState({newTodo: event.target.value});
+ }
+
+ handleNewTodoKeyDown(event) {
+ if (event.keyCode !== ENTER_KEY) {
+ return;
+ }
+
+ event.preventDefault();
+
+ const val = this.state.newTodo.trim();
+
+ if (val) {
+ this.setState({
+ todos: this.state.todos.concat({
+ id: uuid(),
+ title: val,
+ completed: false
+ }),
+ newTodo: ''
+ });
+ }
+ }
+
+ toggleAll(event) {
+ const {checked} = event.target;
+ this.setState({
+ todos: this.state.todos.map(todo =>
+ Object.assign({}, todo, {completed: checked})
+ )
+ });
+ }
+
+ toggle(todoToToggle) {
+ this.setState({
+ todos: this.state.todos.map(todo => {
+ if (todo === todoToToggle) {
+ return Object.assign({}, todo, {
+ completed: !todo.completed
+ });
+ }
+ return todo;
+ })
+ });
+ }
+
+ destroy(passedTodo) {
+ this.setState({
+ todos: this.state.todos.filter(todo => todo !== passedTodo)
+ });
+ }
+
+ edit(todo) {
+ this.setState({editing: todo.id});
+ }
+
+ save(todoToSave, text) {
+ this.setState({
+ todos: this.state.todos.map(todo => {
+ if (todo === todoToSave) {
+ return Object.assign({}, todo, {
+ title: text
+ });
+ }
+ return todo;
+ }),
+ editing: null
+ });
+ }
+
+ cancel() {
+ this.setState({editing: null});
+ }
+
+ clearCompleted() {
+ this.setState({
+ todos: this.state.todos.filter(todo => !todo.completed)
+ });
+ }
+
+ render() {
+ let main;
+ const {todos} = this.state;
+
+ const activeTodoCount = todos.reduce(
+ (accum, todo) => (todo.completed ? accum : accum + 1),
+ 0
+ );
+
+ if (todos.length) {
+ main = (
+
+ );
+ }
+
+ return (
+
+
+ Todo App
+
+
+
+ {main}
+
+ );
+ }
+}
+
+export default TodoApp;
diff --git a/website/pages/index.js b/website/pages/index.js
index 5dd3022176..4b7db8f1e5 100644
--- a/website/pages/index.js
+++ b/website/pages/index.js
@@ -1,21 +1,17 @@
import React from 'react';
import Helmet from 'react-helmet';
-import {Link} from 'react-router-dom';
+import Layout from '@theme/Layout';
+import Todo from '@site/components/Todo';
export default class Home extends React.Component {
render() {
- const {pagesData, docsData} = this.props;
- const routeLinks = [...pagesData, ...docsData].map(data => (
-
- {data.path}
-
- ));
return (
-
+
+
+ Homepage
+
+
+
);
}
}
diff --git a/website/pages/tictactoe.js b/website/pages/tictactoe.js
index e9c93dd8a2..41072c4387 100644
--- a/website/pages/tictactoe.js
+++ b/website/pages/tictactoe.js
@@ -1,142 +1,17 @@
-import React from 'react';
-import Helmet from 'react-helmet';
-import style from './tictactoe.css';
-
-function Square(props) {
- return (
-
- );
-}
-
-function calculateWinner(squares) {
- const lines = [
- [0, 1, 2],
- [3, 4, 5],
- [6, 7, 8],
- [0, 3, 6],
- [1, 4, 7],
- [2, 5, 8],
- [0, 4, 8],
- [2, 4, 6]
- ];
- for (let i = 0; i < lines.length; i++) {
- const [a, b, c] = lines[i];
- if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
- return squares[a];
- }
- }
- return null;
-}
-
-class Board extends React.Component {
- renderSquare(i) {
- return (
- this.props.onClick(i)}
- />
- );
- }
-
- render() {
- return (
-
-
- {this.renderSquare(0)}
- {this.renderSquare(1)}
- {this.renderSquare(2)}
-
-
- {this.renderSquare(3)}
- {this.renderSquare(4)}
- {this.renderSquare(5)}
-
-
- {this.renderSquare(6)}
- {this.renderSquare(7)}
- {this.renderSquare(8)}
-
-
- );
- }
-}
-
-class Game extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- history: [
- {
- squares: Array(9).fill(null)
- }
- ],
- stepNumber: 0,
- xIsNext: true
- };
- }
-
- handleClick(i) {
- const history = this.state.history.slice(0, this.state.stepNumber + 1);
- const current = history[history.length - 1];
- const squares = current.squares.slice();
- if (calculateWinner(squares) || squares[i]) {
- return;
- }
- squares[i] = this.state.xIsNext ? 'X' : 'O';
- this.setState({
- history: history.concat([
- {
- squares: squares
- }
- ]),
- stepNumber: history.length,
- xIsNext: !this.state.xIsNext
- });
- }
-
- jumpTo(step) {
- this.setState({
- stepNumber: step,
- xIsNext: step % 2 === 0
- });
- }
-
- render() {
- const history = this.state.history;
- const current = history[this.state.stepNumber];
- const winner = calculateWinner(current.squares);
-
- const moves = history.map((step, move) => {
- const desc = move ? 'Go to move #' + move : 'Go to game start';
- return (
-
-
-
- );
- });
-
- let status;
- if (winner) {
- status = 'Winner: ' + winner;
- } else {
- status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
- }
-
- return (
-
-
-
- this.handleClick(i)} />
-
-
-
- );
- }
-}
-
-export default Game;
+import React from 'react';
+import Helmet from 'react-helmet';
+import Layout from '@theme/Layout';
+import Tictactoe from '@site/components/Tictactoe';
+
+export default class Home extends React.Component {
+ render() {
+ return (
+
+
+ Tic Tac Toe
+
+
+
+ );
+ }
+}