FullCalendar React+TypeScript Example Projectを関数コンポーネントで書いてみる 02

今回のお題は、カレンダーのカスタマイズと週や日単位で表示するビューの追加です。でき上がりの動きやコードは、CodeSandboxに公開したつぎのサンプル001でお試しいただけます。

#### サンプル001■
[![2107002_004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128949/b136dd0a-e6ad-5139-7f89-689a3359f3e0.png)
\>> CodeSandboxへ](https://codesandbox.io/s/react-typescript-fullcalendar-example-calendar-02-hngmj?file=/src/App.tsx)


# イベントの要素をカスタマイズする
まず、カレンダーに予定として示される、イベントの要素をカスタマイズしてみましょう。そこで、`FullCalendar`コンポーネントに加えるのが[`eventContent`](https://fullcalendar.io/docs/event-render-hooks)です(「[Content Injection](https://fullcalendar.io/docs/content-injection)」参照)。定めたコールバック(`renderEventContent`)から、差し込むJSX要素を返してください。受け取る引数(`eventContent`)は、イベントの情報を収めたオブジェクトです。プロパティの`timeText`は時刻を示す文字列、`event`からイベントオブジェクトが得られます。

```react:src/App.tsx
import FullCalendar, {

  EventContentArg,
} from "@fullcalendar/react";

const renderEventContent = (eventContent: EventContentArg) => (
  <>
    <b>{eventContent.timeText}</b>
    <i>{eventContent.event.title}</i>
  </>
);
function App() {

  return (
    <div className="demo-app">
      <div className="demo-app-main">
        <FullCalendar

          eventContent={renderEventContent}

        />
      </div>
    </div>
  );
}
```

これで、カレンダーの予定に、カスタマイズした要素が差し込まれます。

#### 図001■カスタマイズしたイベントの表示
デフォルト
![2107002_001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128949/bb48191e-c398-757e-09d2-1d0b3b064805.png)
カスタマイズ後
![2107002_002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128949/08588ce0-4226-4cb9-4c0d-835078394326.png)



# ヘッダーのタイトルやボタンをカスタマイズする
つぎは、ヘッダー部にあるタイトルやボタンのカスタマイズです。そのボタンの中に、ビューの切り替えを加えます。そのために、[TimeGridビュー](https://fullcalendar.io/docs/timegrid-view)のプラグインをインストールしてください。このあと、モジュールに`import`したうえで、`FullCalendar`コンポーネントの`plugins`の配列に加えます。

```terminal
npm install --save @fullcalendar/timegrid
```

ヘッダーに置く項目の位置を、左(`start`)/中央(`center`)/右(`end`)に割り振るのが、`FullCalendar `に加える[`headerToolbar`](https://fullcalendar.io/docs/headerToolbar)です。つぎのように配置することにします。

* 左(`start`): 月の前後移動と今月表示ボタン
* 中央(`center`): 月タイトル
* 右(`end`): 月/週/日のビュー切り替えボタン

```react:src/App.tsx
import timeGridPlugin from "@fullcalendar/timegrid";

function App() {

  return (
    <div className="demo-app">
      <div className="demo-app-main">
        <FullCalendar
          // plugins={[dayGridPlugin, interactionPlugin]}
          plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
          headerToolbar={{
            start: "prev,next today",
            center: "title",
            end: "dayGridMonth,timeGridWeek,timeGridDay",
          }}

        />
      </div>
    </div>
  );
}
```

ヘッダーの項目の位置が変わるとともに、新たに月/週/日のビューの切り替えボタンが加わりました。

#### 図002■カスタマイズされたヘッダー
![2107002_003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128949/3622cdd8-489e-f8d2-6d5b-0cb93ede0cff.png)


# カレンダーにオプションのプロパティを加える
`FullCalendar`コンポーネントには、さまざまなオプションのプロパティが備わっています。今回、つぎの表001の4つのプロパティを加えてみました。これらの値はすべて真偽値で、デフォルト値は`false`です。

#### 表001■`FullCalendar`コンポーネントのオプションプロパティ
|プロパティ |説明 |
|:--|:--|
|[`selectMirror`](https://fullcalendar.io/docs/selectMirror)|TimeGridビューで、イベント作成中にプレースホルダーを表示するかどうかの真偽値。デフォルト値は`false`。|
|[`dayMaxEvents`](https://fullcalendar.io/docs/dayMaxEvents)|DayGridビューで、日付の高さを固定するかどうかの真偽値。固定すると、表示しきれないイベントは件数が示される。デフォルト値は`false`。|
|[`navLinks`](https://fullcalendar.io/docs/navLinks)|日付にリンクを加えるかどうかの真偽値で、リンク先は日付のビュー。デフォルト値は`false`。|
|[`businessHours`](https://fullcalendar.io/docs/businessHours)|平日の営業時間とそれ以外の表示を区別するかどうかの真偽値。デフォルト値は`false`。|

```react:src/App.tsx
function App() {

  return (
    <div className="demo-app">
      <div className="demo-app-main">
        <FullCalendar

          selectMirror={true}
          dayMaxEvents={true}
          navLinks={true}
          businessHours={true}
          handleWindowResize={true}

        />
      </div>
    </div>
  );
}
```

ルートモジュール`src/App.tsx`の記述は、つぎのコード001にまとめたとおりです。冒頭にリンクしたサンプル001のCodeSandboxで動きやコードをお確かめください。

#### コード001■ルートモジュール
```react:src/App.tsx
import { useCallback, useState } from "react";
import FullCalendar, {
  DateSelectArg,
  EventApi,
  EventClickArg,
  EventContentArg,
} from "@fullcalendar/react";
import dayGridPlugin from "@fullcalendar/daygrid";
import allLocales from "@fullcalendar/core/locales-all";
import interactionPlugin from "@fullcalendar/interaction";
import timeGridPlugin from "@fullcalendar/timegrid";
import { INITIAL_EVENTS, createEventId } from "./event-utils";

function App() {
  const [currentEvents, setCurrentEvents] = useState<EventApi[]>([]);
  const handleEvents = useCallback(
    (events: EventApi[]) => setCurrentEvents(events),
    []
  );
  const handleDateSelect = useCallback((selectInfo: DateSelectArg) => {
    let title = prompt("イベントのタイトルを入力してください")?.trim();
    let calendarApi = selectInfo.view.calendar;
    calendarApi.unselect();
    if (title) {
      calendarApi.addEvent({
        id: createEventId(),
        title,
        start: selectInfo.startStr,
        end: selectInfo.endStr,
        allDay: selectInfo.allDay,
      });
    }
  }, []);
  const handleEventClick = useCallback((clickInfo: EventClickArg) => {
    if (
      window.confirm(`このイベント「${clickInfo.event.title}」を削除しますか`)
    ) {
      clickInfo.event.remove();
    }
  }, []);
  const renderEventContent = (eventContent: EventContentArg) => (
    <>
      <b>{eventContent.timeText}</b>
      <i>{eventContent.event.title}</i>
    </>
  );
  return (
    <div className="demo-app">
      <div className="demo-app-main">
        <FullCalendar
          plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
          headerToolbar={{
            start: "prev,next today",
            center: "title",
            end: "dayGridMonth,timeGridWeek,timeGridDay",
          }}
          initialView="dayGridMonth"
          eventContent={renderEventContent}
          selectable={true}
          editable={true}
          selectMirror={true}
          dayMaxEvents={true}
          navLinks={true}
          businessHours={true}
          initialEvents={INITIAL_EVENTS}
          locales={allLocales}
          locale="ja"
          eventsSet={handleEvents}
          select={handleDateSelect}
          eventClick={handleEventClick}
        />
      </div>
    </div>
  );
}

export default App;
```

### シリーズ
「[FullCalendar React+TypeScript Example Projectを関数コンポーネントで書いてみる 01](https://qiita.com/FumioNonaka/items/936ca66c78361a02bbd4)」
「[FullCalendar React+TypeScript Example Projectを関数コンポーネントで書いてみる 03](https://qiita.com/FumioNonaka/items/22173f5a7999c09d123f)」