vuetifyのv-calendarで祝日の色を変える

前回([vuetifyのv-calendarで土日の色を変える](https://qiita.com/a_nakaji/items/a8374bdf948595e66b06))の続きです。
# 前提
vue: 2.6.11
vuetify: 2.6.0

# 手順
### 祝日ライブラリーの選定
[JavaScriptで日本の祝日を扱うライブラリ5選](https://qiita.com/the_red/items/43c7f88ccbba001a1a95)を参考にし
今回は **japanese-holidays-js** 使うこととしました。

https://github.com/osamutake/japanese-holidays-js



#### 採用理由
 * 2020年のオリンピック特例も対応されており、メンテナンスがされていること
 * ライブラリの容量が軽いこと

### 祝日ライブラリーを設定する

``` index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <script src="https://cdn.rawgit.com/osamutake/japanese-holidays-js/v1.0.10/lib/japanese-holidays.min.js"></script>
    <title><%= htmlWebpackPlugin.options.title %></title>
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
+    <script>
+      const getJPHoliday = function(year) {
+        return JapaneseHolidays.getHolidaysOf(year)
+      }
+    </script>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app">
+      <App :get-jp-holiday-list="getJPHoliday"></App>
    </div>
    <!-- built files will be auto injected -->
  </body>
</html>

```
vueの中ではライブラリを設定することができないので、index.htmlで設定し、vueに渡します。
` JapaneseHolidays.getHolidaysOf ` でその年の祝日の月・日・祝日名を配列で取得します。
``` App.vue
<script>
export default {
  name: 'App',
+  props: ['getJPHolidayList'],
  methods:{
    changeColor: function () {
      setTimeout(() => {
        const dayList = Array.from(this.$refs.calendar.$vnode.elm.getElementsByClassName('v-calendar-weekly__week'))
        dayList.map(el => {
          el.children[0].classList.add('sunday')
          el.children[6].classList.add('saturday')
        })
      }, 200)
 +     // eslint-disable-next-line no-undef
 +     const holidaysOfYear = getJPHoliday(2022);
 +    console.log(holidaysOfYear)

    }
  },
};
</script>
```
`+`が前回土日カレンダーから変更した箇所です。

これで、`console.log(holidaysOfYear)`で2022年の祝日を配列で取得することを確認することができました。

![スクリーンショット 2022-07-07 17.43.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629280/5ae5336b-ecbd-764d-d03a-8b5f9981efa4.png)

:::note warn
  `const holidaysOfYear = getJPHoliday(2022);`がeslintでエラーが出てしまったので、例外的に下記のコメントを入れてエラーを回避してます。
  `// eslint-disable-next-line no-undef`
:::

### 祝日を抽出する
``` App.vue
methods:{
    changeColor: function () {
      setTimeout(() => {
        const dayList = Array.from(this.$refs.calendar.$vnode.elm.getElementsByClassName('v-calendar-weekly__week'))
        dayList.map(el => {
          el.children[0].classList.add('sunday')
          el.children[6].classList.add('saturday')
        })
      }, 200)
      // eslint-disable-next-line no-undef
      const holidaysOfYear = getJPHoliday(2022);
 　　　   console.log(holidaysOfYear)
 +    const holidayList = holidaysOfYear.filter(holiday => {
 +       if (holiday.month == 7) {
 +         return holiday
 +       }
 +     　　})
 +     　　console.log("holidayList:",holidayList)
```
今月(2022/7)の祝日を抽出します。

![スクリーンショット 2022-07-15 17.09.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629280/bea0036e-63e7-c9a9-4d95-227d5db6cdeb.png)

### 祝日に背景色を設定
```App.vue
    const holidayList = holidaysOfYear.filter(holiday => {
       if (holiday.month == 7) {
         return holiday
       }
     　　})
 +     setTimeout(() => {
 +       const dayList = Array.from(this.$refs.calendar.$vnode.elm.getElementsByClassName('v-calendar-weekly__day'))
 +           .filter(el => !el.classList.contains('v-outside'))
 +       for (let [i, dayEl] of dayList.entries()) {
 +         let dayNum = i + 1
 +         holidayList.forEach((holiday) => {
 +          if (holiday.date === dayNum) {
 +            dayEl.classList.add('holiday');
 +          }
 +           })
 +          }
 +         }, 200)
```
![スクリーンショット 2022-07-15 17.52.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629280/6fcb2d9c-e5dd-60c9-c2c8-426721fafdac.png)

`v-outside`は当月ではない日付の部分なので、こちらは排除します。


[entries](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/entries)でkey, valueをそれぞれ取得し、取得した祝日のdateとkeyが一緒であれば、classListに`holiday`を追加します。

![スクリーンショット 2022-07-19 10.56.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629280/ed25a74d-2dc8-edd4-304b-f8e08b43d46e.png)

完成 :clap: 

### 最終形のソースはこちらです。
``` index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <script src="https://cdn.rawgit.com/osamutake/japanese-holidays-js/v1.0.10/lib/japanese-holidays.min.js"></script>
    <title><%= htmlWebpackPlugin.options.title %></title>
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
    <script>
      const getJPHoliday = function(year) {
        return JapaneseHolidays.getHolidaysOf(year)
      }
    </script>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app">
      <App :get-jp-holiday-list="getJPHoliday"></App>
    </div>
    <!-- built files will be auto injected -->
  </body>
</html>

```


```App.vue
<template>
  <v-app>
    <div>
      <v-sheet height="94vh">
        <v-calendar ref="calendar" @change="changeColor"></v-calendar>
      </v-sheet>
    </div>
  </v-app>
</template>

<script>
export default {
  name: 'App',
  props: ['getJPHolidayList'],
  methods:{
    changeColor: function () {
      setTimeout(() => {
        const dayList = Array.from(this.$refs.calendar.$vnode.elm.getElementsByClassName('v-calendar-weekly__week'))
        dayList.map(el => {
          el.children[0].classList.add('sunday')
          el.children[6].classList.add('saturday')
        })
      }, 200)
      // eslint-disable-next-line no-undef
      const holidaysOfYear = getJPHoliday(2022);
      const holidayList = holidaysOfYear.filter(holiday => {
        if (holiday.month == 7) {
          return holiday
        }
      })
      setTimeout(() => {
        const dayList = Array.from(this.$refs.calendar.$vnode.elm.getElementsByClassName('v-calendar-weekly__day'))
            .filter(el => !el.classList.contains('v-outside'))
        for (let [i, dayEl] of dayList.entries()) {
          let dayNum = i + 1
          holidayList.forEach((holiday) => {
            if (holiday.date === dayNum) {
              dayEl.classList.add('holiday');
            }
          })
        }
      }, 200)
    }
  },
};
</script>
<style>
.saturday {
  background: rgba(0, 0, 255, .1);
}
.sunday {
  background: rgba(255, 0, 0, .1);
}
.holiday {
  background: rgba(255, 0, 0, .3);
}
</style>
```

# 感想

意外と探せばあるようでなかったので、今回は土日編と祝日編に分けて記事を書かせていただきました。

この記事では今月(2022年7月)の祝日に色を塗るだけでしたが、moment.jsなどを使って、カレンダーの月が変わったらその都度取得するようにしたり、祝日名も取得できるので、ラベルで祝日を出してもいいかもしれません。

moment.jsをかなり苦戦した部分もあるので、いつか記事にできたらなと思います。

お付き合いいただき、ありがとうございました。