<div id="root"></div>
#root {
  color: #555555;
}

.container {
  background: #eeeeff;
  width: 350px;
  height: 1000px;
  margin: 0 auto;
  overflow: hidden;
  position: relative;
}

.box {
  border: 2px solid #bbbbbb;
  border-radius: 5px;
  background: white;
  margin: 20px 20px;
  padding: 0px 20px;
}

.modal {
  opacity: 0;
  visibility: hidden;
  transition: opacity .3s, visibility 0s ease 1s;

  background: rgba(0, 0, 0, 0.5);
  position: fixed;
  top: 0;
  right: 0;
  left: 0;
  padding-top: 100px;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
}

.modal.active {
  transition-delay: 0s;

  opacity: 1;
  visibility: visible;
}

.board-row:after {
  clear: both;
  content: "";
  display: table;
}

.square {
  display: inline-block;
  border: 2px solid red;
  float: left;
  font-size: 55px;
  font-weight: bold;
  width: 80px;
  height: 80px;
/*   lien-height: 30px; */
  text-align: center;
  margin-right: -2px;
  margin-top: -2px;
  padding: 0;
  background: white;
}

footer {
  padding: 10px;
  background: white;
  position: fixed;
  bottom: 0;
  margin-bottom: 15vh;
}

#nav-drawer {
  position: relative;
}

.nav-unshown {
  display: none;
}

#nav-open {
  display: inline-block;
  width: 30px;
  height: 22px;
  vertical-align: middle;
}

#nav-open span,
#nav-open span:before,
#nav-open span:after {
  position: absolute;
  height: 3px; /*線の太さ*/
  width: 25px; /*長さ*/
  border-radius: 3px;
  background: #555;
  display: block;
  content: "";
  cursor: pointer;
/*   z-index: 10000; */
}
#nav-open span:before {
  bottom: -8px;
}
#nav-open span:after {
  bottom: -16px;
}

#nav-close {
  display: none; /*はじめは隠しておく*/
  position: fixed;
  z-index: 99;
  top: 0; /*全体に広がるように*/
  left: 0;
  width: 100%;
  height: 100%;
  background: black;
  opacity: 0;
  transition: 0.3s ease-in-out;
}

#nav-content {
  overflow: auto;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9999; /*最前面に*/
  width: 90%; /*右側に隙間を作る(閉じるカバーを表示)*/
  max-width: 330px; /*最大幅(調整してください)*/
  height: 100%;
  background: #fff; /*背景色*/
  transition: 0.3s ease-in-out; /*滑らかに表示*/
  -webkit-transform: translateX(-105%);
  transform: translateX(-105%); /*左に隠しておく*/
  
  display: flex;
  justify-content: center;
}

#nav-content .board {
  position: absolute;
  bottom: 70px;
}

#nav-input:checked ~ #nav-close {
  display: block; /*カバーを表示*/
  opacity: 0.5;
}

#nav-input:checked ~ #nav-content {
  -webkit-transform: translateX(0%);
  transform: translateX(0%); /*中身を表示(右へスライド)*/
  box-shadow: 6px 0 25px rgba(0, 0, 0, 0.15);
}

.ranking .rows {
  width: 270px;
  background: #eeeeee;
}
.ranking .row {
  display: flex;
  width: 100%;
  height: 53px;
  padding: 2%;
}
.rank {
  display: flex;
  width: 20%;
  justify-content: center;
  align-items: center;
  font-weight: bold;
  font-size: 24px;
  background: #ffffff;
}
.info {
  width: 75%;
  background: #ddddee;
  padding: 1%;
}
.sub-row {
  display: flex;
  width: 100%;
}
.ranker-name {
/*   width: 25%; */
  width: 100%;
  background: #ffffff;
}
.ranker-from {
  max-width: 70%;
}
.hide-over {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.scores {
  text-align: right;
  background: #ffffdd;
  padding: 1px;
}
.ranker-bingo-number {
  width: 30%;
  font-size: 14px;
/*   background: #eeeeff; */
  padding: 1px;
  padding-left: 10px;
}
.ranker-score {
  width: 50%;
  font-weight: bold;
/*   background: #ffdddd; */
}
function Square(props) {
  return <div className="square">{props.value}</div>;
}
class Board extends React.Component {
  renderSquare(i) {
    return <Square value={this.props.squares[i]} />;
  }

  render() {
    return (
      <div className="board">
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
        </div>
        <div className="board-row">
          {this.renderSquare(3)}
          {this.renderSquare(4)}
          {this.renderSquare(5)}
        </div>
        <div className="board-row">
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
        </div>
      </div>
    );
  }
}
class Game extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      squares: this.makeBingoCard(),
      balls: Array(9).fill(0).map((_,i) => i + 1),
      numberOfBingo: 0,
      modalIsActive: false
    };
  }
  
  closeModal() {
    this.setState({modalIsActive: false});
  }
  
  makeBingoCard() {
    return this.makeRow();
  }
  makeRow() {
    let b = this.makeColumn(1);
    let o = this.makeColumn(4);
    let n = this.makeColumn(7);
    let list = new Array(9);
    for (let i = 0; i < 3; i++) {
      list[i * 3 + 0] = b[i];
      list[i * 3 + 1] = o[i];
      list[i * 3 + 2] = n[i];
    }
    list[4] = '@';
    console.log(list);
    return list;
  }
  
  makeColumn(base) {
    const array = Array(3).fill(0).map((_,i) => i + base);
    const list = [];
    for (let i = 1; i <= 3; i++) {
      let a = Math.floor(Math.random() * array.length);
      list.push(array[a]);
      // console.log(array[a]);
      array.splice(a, 1);
    }
    return list;
  }
  
  galapon() {
    const squares = this.state.squares.slice();
    
    const a = Math.floor(Math.random() * this.state.balls.length);
    const ball = this.state.balls[a];
    this.state.balls.splice(a, 1);
    console.log(ball);
    console.log(this.state.balls);
    const foundIndex = this.state.squares.findIndex(number => number === ball);
    console.log(foundIndex);
    squares[foundIndex] = '@';
    this.setState({
      squares: squares, 
      numberOfBingo: this.checkBingo(squares),
      modalIsActive: true
    });
  }
  checkBingo(squares) {
    let counter = 0;
    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[b] === '@' && squares[c] === '@') {
        counter++;
      }
    }
    return counter;
  }

  render() {
    let className = 'modal';
    if (this.state.modalIsActive) {
      className += ' active';
    }
    
    return (
      <div className="container">
        <Survey />
        <TesterName />
        <TesterSex />
        
        <div className={className} onClick={() => this.closeModal()}>
          <Board 
            squares={this.state.squares}
          />
        </div>
        <button onClick={() => this.galapon()}>ガラポンを回す</button>
        <div>ビンゴ数:{this.state.numberOfBingo}</div>
        
        <footer>
          <div id="nav-drawer">
            <input id="nav-input" type="checkbox" className="nav-unshown" />
            <label id="nav-open" htmlFor="nav-input"><span></span></label>
            <label className="nav-unshown" id="nav-close" htmlFor="nav-input"></label>
            <div id="nav-content">
              <Ranking />
              <Board 
                squares={this.state.squares}
              />
            </div>
          </div>
        </footer>
      </div>
    );
  }
}

class Survey extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2,
      playerName: '田中',
      playerSex: 'male',
      food: 'apple',
    };
    this.handleInputChange = this.handleInputChange.bind(this);
  }
  
  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;
    
    this.setState({
      [name]: value,
    });
  }
  
  render() {
    const title = 'あなたについてのアンケート';
    // const questions = [
    //   {type: ''}
    // ];
    
    return (
      <form>
        <div className="title box">
          <h1>{title}</h1>
        </div>
        <div className="player-name box">
          <label>
            名前:
            <input
              name="playerName"
              type="text"
              value={this.state.playerName}
              onChange={this.handleInputChange} />
          </label>
        </div>
        <div className="player-sex box">
          <label>
            <input
              name="playerSex"
              type="radio"
              value="male"
              checked={this.state.playerSex === 'male'}
              onChange={this.handleInputChange}
              className="player-sex-input"/>
            男性
          </label>
          <label>
            <input
              name="playerSex"
              type="radio"
              value="female"
              checked={this.state.playerSex === 'female'}
              onChange={this.handleInputChange}
              className="player-sex-input"/>
            女性
          </label>
        </div>
        <div className="player-food box">
          <label>
            好きな果物:
            <select name="food" value={this.state.food} onChange={this.handleInputChange}>
              <option value="banana">ばなな</option>
              <option value="apple">りんご</option>
              <option value="orange">みかん</option>
            </select>
          </label>
        </div>
        <label>
          is going:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          Number of guests:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}

class Ranking extends React.Component {
  render() {
    const rankers = [
      {rank: 1, name: '二宮', from: '愛媛大学', bingoNumber: 8, score: 488},
      {rank: 2, name: '櫻井', from: '長崎美容専門学校', bingoNumber: 7, score: 302},
      {rank: 2, name: '相葉', from: '九州大学', bingoNumber: 7, score: 198},
      {rank: 3, name: 'MJ', from: '大衆食堂しゃーき', bingoNumber: 6, score: 127},
      {rank: 4, name: '大野', from: '早稲田大学', bingoNumber: 5, score: 92},
    ];
    
    return (
      <div className="ranking">
        <div className="title">
          <h2>ランキング</h2>
          <div className="rows">
            {rankers.map((ranker, index) => {
              return(
                <div className="row" key={index}>
                  <div className="rank">{ranker.rank}</div>
                  <div className="info">
                    <div className="ranker sub-row">
                      <div className="ranker-name hide-over">{ranker.name}({ranker.from})</div>
                      {/*<div className="ranker-from hide-over">({ranker.from})</div>*/}
                    </div>
                    <div className="scores sub-row">
                      <div className="ranker-bingo-number">{ranker.bingoNumber} BINGO</div>
                      <div className="ranker-score">{ranker.score}</div>
                    </div>
                  </div>
                </div>
              )
            })}
          </div>
        </div>
      </div>
    );
  }
}

class TesterName extends React.Component {
  render() {
    return (
      <div className="tester-name box">
        <h2>田中</h2>
      </div>
    );
  }
}
class TesterSex extends React.Component {
  render() {
    return (
      <div className="tester-sex box">
        <h2>男</h2>
      </div>
    );
  }
}

// ReactDOM.render(<h1>hey</h1>, document.getElementById('root'));
ReactDOM.render(<Game />, document.getElementById("root"));
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://unpkg.com/react@16/umd/react.development.js
  2. https://unpkg.com/react-dom@16/umd/react-dom.development.js