React Base SPA (study)

Single Page Application React, Bootstrap4 Bootstrap4のデフォルトの色使いが強すぎて使い方が難しい。

by Masaki Moriwaki

HTML

<link rel="stylesheet" href="https://cdn.rawgit.com/twbs/bootstrap/v4-dev/dist/css/bootstrap.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.2/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.2/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.33/browser.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-router/1.0.0-rc3/ReactRouter.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/5.0.1/markdown-it.js"></script>
<script src="https://facebook.github.io/react/js/jsfiddle-integration-babel.js"></script>

<div id="application"></div>

CSS

html {
    overflow: hidden;
}
body {
    position: fixed;
    width: 100%;
    height: 100%;
}
.application {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    height: 100vh;
}
.viewport {
    flex-grow: 1;
    display: flex;
    flex-direction: row;
    overflow: hidden;
    max-height: 100vh;
}
.sidenav {
    flex: 0 0 10rem;
    display: flex;
    flex-direction: column;
    border-right: #ccc 1px solid;
    padding: 1rem 1rem 1rem 0;
}
main {
    display: flex;
    flex-grow: 1;
}

.editor {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
}
.editor textarea {
    resize: none;
}
.editor header {
    flex: 0 1 auto;
    border-bottom: #ccc 1px solid;
    padding: 0.5rem;
}
.editor header input {
    border: none;
}
.editor header h2 {
    margin: 0;
}
.editor section {
    flex: 1 1 auto;
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: row;
}
.editor section .markdown {
    flex: 1 1 50%;
    border: none;
    overflow: auto;
    padding: 0.5rem;
}
.editor section .preview {
    flex: 1 1 50%;
    border-left: #ccc 1px solid;
    overflow: auto;
    padding: 0.5rem;
}
.editor footer {
    flex: 0 1 auto;
    display: flex;
    flex-direction: row;
    border-top: #ccc 1px solid;
}
.editor footer .markdown {
    flex: 1 1 50%;
    border: none;
    overflow: auto;
    padding: 0.5rem;
}
.editor footer .preview {
    flex: 1 1 50%;
    border-left: #ccc 1px solid;
    overflow: auto;
    padding: 0.5rem;
}

.list {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
}
.list header {
    border-bottom: #ccc 1px solid;
    padding: 0.5rem;
}
.list header h2 {
    margin: 0;
}
.list section {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: row;
}
.list section nav {
    flex: 1 1 25%;
    border-right: #ccc 1px solid;
    overflow: auto;
    padding: 0;
}
.list section nav ul li {
    border-bottom: #ccc 1px solid;
    padding:...

JavaScript 1.7

const Router = window.ReactRouter.Router;
const Route = window.ReactRouter.Route;
const Link = window.ReactRouter.Link;
const md = window.markdownit();

class Message extends React.Component {
  constructor(props) {
    super(props);
    this.state = {hidden: false};
  }
  close() {
    this.setState({hidden: true});
  }
  render() {
    const level = {
      success: 'bg-success',
      info: 'bg-info',
      warning: 'bg-warning',
      danger: 'bg-danger'
    };
    return (
      <div ref="message" className={level[this.props.level]} hidden={this.state.hidden}>
        {this.props.text}
        <button onClick={this.close.bind(this)} type="button" className="close">
          <span>&times;</span>
        </button>
      </div>
    )
  }
};

class Notifications extends React.Component {
  render() {
    return (
      <aside>
        {this.props.messages.map((message, i) => {
          return <Message key={i} {...message} />;
        })}
      </aside>
    );
  }
};

class Navigation extends React.Component {
  render() {
    return (
      <nav className="sidenav">
        <ul className="nav nav-pills nav-stacked">
          <li className="nav-item"><Link to="/editor" className="nav-link" activeClassName="active">新規投稿</Link></li>
          <li className="nav-item"><Link to="/list" className="nav-link" activeClassName="active">投稿一覧</Link></li>
        </ul>
      </nav>
    );
  }
};

class Editor extends React.Component {
  updatePreview() {
    const html = md.render(this.refs.markdown.value);
    this.refs.preview.innerHTML = html;
  }
  save() {
    const post = {
      title: this.refs.title.value,
      markdown: this.refs.markdown.value
    };
    datastore.push(post);
  }
  render() {
    return (
      <div className="editor">
        <header>
          <h2>
            <button onClick={this.save.bind(this)} type="button" className="btn btn-primary pull-right">
              保存
            </button>
            <input type="text" placeholder="タイトル"...