Date Picker (Materialize CSS)のレイアウトをカスタマイズする

# TL; DR
[Materialize CSS](https://materializecss.com) の [Date Picker](https://materializecss.com/pickers.html#date-picker) において、日付が表示される部分のレイアウトを動的（dynamic）に変更できるようになる。
### 動機
カレンダーの表示をオプションで日本語化ができるのだが、レイアウトが酷すぎる。
![after.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69840/2e973e43-4a1e-adbe-1c14-cb377421bcca.png)
これを日めくりカレンダー風に変更してみた。
![after.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69840/e3d9c798-cdf0-ffe7-868a-dfdbac23e079.png)

# datepicker.js
`materialize.js`(`materialize.min.js`)にコンポーネントやらなんやらの設定が詰まっている。が、これとは別に`datepicker.js`を読み込むとこちらの内容が優先されるようである（ドキュメントが見つけられなかったので「ようである」という表現）。

そこで、このファイルに変更を加えてゆく。

### Datepicker._template
ピッカーのテンプレートは次のようになっていて、これをベースに JavaScript でレンダリングしている。しかし、class`.datepicker-date-display`のコンテナが年(`.year-text`)と曜日・月・日(`.date-text`)の二つしかないので使いづらい。そこで、コンテナのタグを`span`ではなく`div`にした上で年、月、日、曜日の四つに変更する。


```html
<div class= "modal datepicker-modal">
  <div class="modal-content datepicker-container">
    <div class="datepicker-date-display">
      <span class="year-text"></span>
      <span class="date-text"></span>
    </div>
    <div class="datepicker-calendar-container">
      <div class="datepicker-calendar"></div>
      <div class="datepicker-footer">
        <button class="btn-flat datepicker-clear waves-effect" style="visibility: hidden;" type="button"></button>
        <div class="confirmation-btns">
          <button class="btn-flat datepicker-cancel waves-effect" type="button"></button>
          <button class="btn-flat datepicker-done waves-effect" type="button"></button>
        </div>
      </div>
    </div>
  </div>
</div>
```

### _renderDateDisplay()
レンダリングはこのメソッドでなされるが、``this.dateTextEl.innerHTML = `${day}, ${month} ${date}`;``の部分が使いづらくしている諸悪の根源なので、ここを変更する。

上述の方針で変更したのが次のとおり。
<details><summary>diff の読み方</summary>
`*** 324,330 ***` ：古い方の324行目から330行目は次の7行でこうなってますよ。
`--- 324,332 ---` ：新しい方の324行目から332行目は次の9行でこうなってますよ。
`!`：古いのと新しいのと対応する行で違いがあります。
`+`：新しい方で追加されました。
</details>


```diff&nbsp;-c&nbsp;datepicker.jp
***************
*** 324,330 ****
        let month = i18n.monthsShort[displayDate.getMonth()];
        let date = displayDate.getDate();
        this.yearTextEl.innerHTML = displayDate.getFullYear();
!       this.dateTextEl.innerHTML = `${day}, ${month} ${date}`;
      }
  
      /**
--- 324,332 ----
        let month = i18n.monthsShort[displayDate.getMonth()];
        let date = displayDate.getDate();
        this.yearTextEl.innerHTML = displayDate.getFullYear();
!       this.dateTextEl.innerHTML = displayDate.getDate();
!       this.dayTextEl.innerHTML = i18n.weekdaysShort[displayDate.getDay()];
!       this.monthTextEl.innerHTML = i18n.monthsShort[displayDate.getMonth()];
      }
  
      /**
***************
*** 751,756 ****
--- 753,760 ----
  
        this.yearTextEl = this.modalEl.querySelector('.year-text');
        this.dateTextEl = this.modalEl.querySelector('.date-text');
+       this.dayTextEl = this.modalEl.querySelector('.day-text');
+       this.monthTextEl = this.modalEl.querySelector('.month-text');
        if (this.options.showClearBtn) {
          this.clearBtn = this.modalEl.querySelector('.datepicker-clear');
        }
***************
*** 950,957 ****
      '<div class= "modal datepicker-modal">',
      '<div class="modal-content datepicker-container">',
      '<div class="datepicker-date-display">',
!     '<span class="year-text"></span>',
!     '<span class="date-text"></span>',
      '</div>',
      '<div class="datepicker-calendar-container">',
      '<div class="datepicker-calendar"></div>',
--- 954,963 ----
      '<div class= "modal datepicker-modal">',
      '<div class="modal-content datepicker-container">',
      '<div class="datepicker-date-display">',
!     '<div class="year-text"></div>',
!     '<div class="day-text"></div>',
!     '<div class="month-text"></div>',
!     '<div class="date-text"></div>',
      '</div>',
      '<div class="datepicker-calendar-container">',
      '<div class="datepicker-calendar"></div>',
```

# カスタマイズ
##### `sample.html`では以下の3箇所がいつもと違うところ
* ①. カスタマイズした`datepicker.js`を読み込む
* ②. レイアウト用のCSS`sample.css`を読み込む
* ③. ピッカーの変更用`sample.js`を読み込む

```html:sample.html
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <!-- Compiled and minified CSS -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
    <!-- Compiled and minified JavaScript -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
    <!-- カスタマイズされたdatepicker.jsとレイアウト用のCSSを読み込む -->
    <script type="text/javascript" src="datepicker.js"></script> <!-- ① -->
    <link rel="stylesheet" href="sample.css"> <!-- ② -->
  </head>
  <body>
    <input type="text" class="datepicker">
    <script type="text/javascript" src="sample.js"></script> <!-- ③ -->
  </body>
</html>
```

##### `sample.css`で見た目の変更（解説省略）
```css:sample.css
.datepicker-date-display { text-align:center; }
.year-text { font-size:2rem !important; }
.day-text, .month-text { font-size:3rem; }
.date-text { font-size:8rem !important; padding:2rem; }
```

##### `sample.js`がカスタマイズの本丸
 * ①. ドキュメント（チュートリアル）そのまま
 * ②. `i18n`オプションを変更することで日本語化できる
 * ③. `onDraw`オプションはモーダルウィンドウが表示された後に呼ばれるので、ここでレイアウトを動的に変更する
 * ④. `DOM`の要素を取得して一旦除去する
 * ⑤. 目的に従ってレイアウトを再構成する。
ここでは単純に順番を入れ替えているだけだが、デフォルトでは日にちの`日`が表示されないので、`date.appendChild(document.createTextNode('日'));`などとして付け加えることもできる

```javascript:sample.js
M.AutoInit(); //①
let options = {
  onDraw:function(e){ //③
  let panel = document.querySelector('.datepicker-date-display'); //④
  let year = e.yearTextEl.parentNode.removeChild(e.yearTextEl); //④
  let day = e.dayTextEl.parentNode.removeChild(e.dayTextEl); //④
  let month = e.monthTextEl.parentNode.removeChild(e.monthTextEl); //④
  let date = e.dateTextEl.parentNode.removeChild(e.dateTextEl); //④
  panel.appendChild(year); //⑤
  panel.appendChild(month); //⑤
  panel.appendChild(date); //⑤
  panel.appendChild(day); //⑤
},
  i18n:{ //②
    months:['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'],
    monthsShort:['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'],
    weekdays:['日曜日','月曜日','火曜日','水曜日','木曜日','金曜日','土曜日'],
    weekdaysShort:['日曜日','月曜日','火曜日','水曜日','木曜日','金曜日','土曜日'],
    weekdaysAbbrev:['日','月','火','水','木','金','土']
  }
};
document.addEventListener('DOMContentLoaded', function() { //①
  var elems = document.querySelectorAll('.datepicker'); //①
  var instances = M.Datepicker.init(elems, options); //①
}); //①
```

# 補足
 -  Materialize CSSのバージョンは1.0.0、レイアウトを目視で確認したのはPC版のChrome、Safari、Firefoxのみ
 - 上述では日本語ページ、英語ページがある場合など動的に変更する必要がある場合を想定してカスタマイズしているが、固定したレイアウトであれば`datepicker.js`で変更してしまってもよい。
 - `datepicker.js`は [GitHub](https://github.com/Dogfalo/materialize) の [`jsディレクトリ`](https://github.com/Dogfalo/materialize/blob/v1-dev/js/datepicker.js) にある
 -  `jsディレクトリ`には他にも `buttons.js`やら`timepicker.js`やら個別の設定ファイルがあるので、同じ方針でカスタマイズできるかもしれない。
 - 上述のカスタマイズでは以下の3行が不要なのだが、ポイントとなる変更箇所に注力するため残してある
  
   ```
   323   let day = i18n.weekdaysShort[displayDate.getDay()];
   324   let month = i18n.monthsShort[displayDate.getMonth()];
   325   let date = displayDate.getDate();
   ```