React-Day-Pickerのスタイルを変える方法


日付選択機能が拡張しやすそうというで理由で[blueprint](https://blueprintjs.com/)というUI Toolkitを使っているのだが、実際に使ってみて見た目を変えたいなと思った所、結構癖があったのでメモしていく。

## blueprintのDatePickerはReact-Day-Pickerのラッパー的なもの

blueprintのDatePickerは[React-Day-Picker](http://react-day-picker.js.org/)をベースにしていて、プロパティ経由で簡単にReact-Day-Pickerのプロパティを指定することができる。

ので、見た目を変えるときはReact-Day-Pickerの機能を使う必要が出てくる。

[React-Day-Picker Styling](http://react-day-picker.js.org/docs/styling/)

```jsx
export function Test() {
  const customProps = { ... } //React-Day-Pickerの設定
  return <DatePicker dayPickerProps={customProps} />
}
```

スタイルを変えるときに取ることが出来る方法は以下のものがある。

ここで注意するのが、日にちに対するスタイルの指定とUI全体のスタイルの指定の二種類ある
(classNameも用意されている)

カレンダーの日にちのスタイルを変更する

- modifiers
- modifiersStyles

DayPicker全体のスタイルを変更する

- classNames

# 日にちのスタイルを変更する

React-Day-Pickerではある日にちをいじりたいときはmodifiersプロパティを使って指定する形をとっている。

その際グループ化したい日にちのパターンとその名前という形で設定していく。

設定したグループの日にちには`DayPicker-Day--<グループ名>`という名前のCSSクラスの名前が与えられるので
それに対してスタイルを設定すると好きな見た目に変更することが出来る。

もちろん自動生成されたCSSクラス名をCSSファイルに書かないといけない訳ではなく、`modifiersStyles`にグループ名をキーをとしたinline-Styleを設定すればいい。

[Matching days with modifiers](http://react-day-picker.js.org/docs/matching-days/)にあるサンプルコードをちょっと変えたもの。

```jsx
import React from 'react';
import DayPicker from 'react-day-picker';
import 'react-day-picker/lib/style.css';

const birthdayStyle = `.DayPicker-Day--highlighted {
  background-color: orange;
  color: white;
}`;

const modifiers = {
  highlighted: new Date(2018, 8, 19),
};
const modifiersStyles = {
  highlighted: {
    color: '#FF0000'
  }
}
export default function MyBirthday() {
  return (
    <div>
      <style>{birthdayStyle}</style>
      <DayPicker
        modifiers={modifiers}
        modifiersStyles={modifiersStyles}
        month={new Date(2018, 8)} />
    </div>
  );
}
```

# 日にちの指定の仕方

グループを作る際の日にちの指定には以下の種類があるので適当に使い分ける。

グループは複数指定でき、ある日にちが二つ以上のグループにあるときはその全てが適応される。

- from/toキー この二つに与えたDateオブジェクトの範囲内の日にちをグループ化する
- beforeキー 与えた日にちより前のものをグループ化する
- afterキー 与えた日にちより後のものをグループ化する
- daysOfWeek 指定した曜日でグループ化する。曜日は0から6の範囲を取り、それぞれ日曜から土曜に対応している。配列で渡すと複数の曜日を指定できる。
- function(day: Date):boolean グループ化する日にちのときはtrueを返すDateオブジェクトを受け取る関数

```jsx
const from_to = [
  from: new Date(2018, 8, 19),
  to: new Date(2018, 9, 19),
]
const before_after = {
  after: new Date(2018, 9, 1),
  before: new Date(2018, 10, 1)
}
const daysOfWeek = {
  daysOfWeek: [0] // 日曜日
}
const func = (day) => { return day.getDate() % 2 === 0}

export default function Grouping() {
  return (
    <div>
      <style>{birthdayStyle}</style>
      <DayPicker
        modifiers={[from_to, before_after, daysOfWeek]} />
    </div>
  );
}
```

# 用意されているグループ

ちなみに<b>today</b>と<b>outside</b>が予め用意されており、それぞれ今日の日にちと現在の月以外の日にちが対象になる。

# イベントとの組み合わせ

グループはDayPickerのイベントが起きたときに日にちを識別する目的にも使うことが出来る。

グループ情報を参照することが出来るイベントにはmodifiersがオブジェクトとして引数に渡されていて、
そのオブジェクトには所属しているグループの名前をboolean型として持っている。

```jsx
//ドキュメントから拝借
import 'react-day-picker/lib/style.css';

export default class EventHandlers extends React.Component {
  constructor(props) {
    super(props);
    this.handleDayClick = this.handleDayClick.bind(this);
    this.handleDayMouseEnter = this.handleDayMouseEnter.bind(this);
  }

  handleDayMouseEnter(day, { firstOfMonth }) {
    if (firstOfMonth) {
      // Do something when the first day of month has been mouse-entered
    }
  }

  handleDayClick(day, { sunday, disabled }) {
    if (sunday) {
      window.alert('Sunday has been clicked');
    }
    if (disabled) {
      window.alert('This day is disabled');
    }
  }

  render() {
    return (
      <DayPicker
        disabledDays={new Date()}
        modifiers={{
          sunday: day => day.getDay() === 0,
          firstOfMonth: day => day.getDate() === 1,
        }}
        onDayClick={this.handleDayClick}
        onDayMouseEnter={this.handleDayMouseEnter}
      />
    );
  }
}
```

と、かなり汎用性があるものになっている。

## DayPicker全体のスタイルを変更するときはclassNames用のテンプレートを使おう

日にちについてはかなり柔軟なカスタマイズが出来るようになっているが、
それ以外の例えばヘッダー部分とかの背景色を変更したいときはclassNamesプロパティを使う。

# 

classNamesにはDayPickerで使われる要素全てにCSSを指定する形になっている。

各要素には使用するCSSクラスを指定するのだが、要素の数は結構あるので公式から用意されているテンプレートを書き換えるのがいいと思う。

[テンプレートはこちら react-day-picker/src/classNames.js](https://github.com/gpbl/react-day-picker/blob/master/src/classNames.js)

テンプレートのインターフェイスは`import {ClassNames} from 'react-day-picker'`からインポート出来る。

```jsx
function makeClassNames(config) {
  const DEFAULT = {
    container: 'DayPicker',
    wrapper: 'DayPicker-wrapper',
    interactionDisabled: 'DayPicker--interactionDisabled',
    months: 'DayPicker-Months',
    month: 'DayPicker-Month',

    navBar: 'DayPicker-NavBar',
    navButtonPrev: 'DayPicker-NavButton DayPicker-NavButton--prev',
    navButtonNext: 'DayPicker-NavButton DayPicker-NavButton--next',
    navButtonInteractionDisabled: 'DayPicker-NavButton--interactionDisabled',

    caption: 'DayPicker-Caption',
    weekdays: 'DayPicker-Weekdays',
    weekdaysRow: 'DayPicker-WeekdaysRow',
    weekday: 'DayPicker-Weekday',
    body: 'DayPicker-Body',
    week: 'DayPicker-Week',
    weekNumber: 'DayPicker-WeekNumber',
    day: 'DayPicker-Day',
    footer: 'DayPicker-Footer',
    todayButton: 'DayPicker-TodayButton',

    // default modifiers
    today: 'today',
    selected: 'selected',
    disabled: 'disabled',
    outside: 'outside',
  };
  return Object.assign({}, DEFAULT, config)
}

export default function CustomClassNames() {
  //
  const classNames = makeClassNames({
    container: 'changeBackground'
  })
  return (
    <DayPicker 
      classNames={ styles } 
      modifiers={{
        [styles.birthday]: new Date(2018, 8, 19)
      }}
    />
  );
}
```

ちなみにデフォルトで設定されているクラスを完全に上書きしてしまうとレイアウトが崩れたので、次のように追記する形にしたほうがいいと思う。

指定したクラス名はそのまま設定されるようなので、`classnames`パッケージとか利用したら便利。

```js
const custom = {
  caption: classnames('DayPicker-Caption', 'appendStyles')
}
```

## もちろんCSSModuleも使える

ので、好きな方法でスタイルをカスタマイズすることが出来る。

かなり拡張性が高いパッケージになっていて、日時選択にはこれが一番融通がきくのではないかなと。

おすすめのパッケージです。