【Power Apps】Outlook風のカレンダーでカッコよく日時指定したい

# はじめに
こんにちは、reireです。

皆さん、Outlookのカレンダー機能ってご存じですか？
もしくは他のアプリでも良いです、↓こんなのです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3461887/9bd01f9d-c7ba-507b-cdc0-9b47df6d1bb9.png)

いつ、何時に何の予定が入っているのか、
その予定は何時間で予定されているのかなど、イベントに関する情報が一目で俯瞰できて分かりやすいですよね。

逆に予定を入れるときも直感的で分かりやすいです。

今回は上記のようなOutlookカレンダー『っぽい』スケジュール表をPower Appsで作成してみたので記事にしていきたいと思います。

# 動作イメージ

善は急げ、以下動作イメージになります。
![Videotogif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3461887/e3913a5e-ddd1-3c77-70e3-a33f4c51f341.gif)
(※画像ファイルサイズの問題で倍速再生をさせています。)

# 機能要件

今回は以下の機能を実装していきます。

- Outlook風のカレンダー画面
- クリックで日付が取得できる
- 分数の間隔を任意に選べる

:::note info
一部色の操作などで[私の過去の記事](https://qiita.com/reire/items/8b3a35818aadecba024a)を参考にした操作が出てきますが、本記事では説明は省きますのでそちらの記事も是非ご覧いただければと思います。
:::

https://qiita.com/reire/items/8b3a35818aadecba024a

# さっそく実装

## 1. さくっとアプリの画面を作成

今回は機能に関するものだけ作成していきます。
構成は↓の画像の通り。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3461887/ae32c29a-d445-fd20-4026-5a37646b618b.png)


主だったものから順に解説していきます。


## 2. まずは日付操作系から

カレンダー表示を行うにも、まずは基準となる日付の操作ができなければ意味がありません。

以下ボタン類の処理から記載していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3461887/c419aa8b-33b1-11c9-d432-3aef74254883.png)

```Button_Reset.OnSelect
Set(
    First_DayofWeek,
    DateAdd(
        Today(),
        -Weekday(Today())+1,
        TimeUnit.Days
    )
)
```
これで今週の日曜日の日付をゲットできます。
こちらの関数は画面のOnVisibleにも記載してあげてください。

```Icon_Tomorrow.OnSelect
Set(
    First_DayofWeek,
    DateAdd(
        First_DayofWeek,
        1,
        TimeUnit.Days
    )
)
```

基準となるFirst_DayofWeekに加算することで日めくりを実現します。
Icon_YesterdayやIcon_Nextweek、Icon_Lastweekは加算する数字を変えるだけなので割愛。

```Dropdown_Interval.Items
[
    {
        Text:"30分",
        Value:2
    },
    {
        Text:"15分",
        Value:4
    },
    {
        Text:"10分",
        Value:6
    }
]
```
こちらはカレンダーを何分間隔で表示するかを選択するドロップダウンになります。
Value値はつまり30分間隔なら60(分)÷30(分)=2分割すればいいよねってことです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3461887/b5854286-8f1f-3c47-bfb6-b08dedadaca2.png)


## 3. カレンダー

肝心のカレンダー表示。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3461887/84fb3d99-8c6e-e651-172c-d27fd9799299.png)

まず今回はカレンダー表示と、見出しとして時間を表示する2つの垂直ギャラリーがあります。

時間表示の方はシンプルで、

```Gallery_TimeView.Items
Sequence(24,0)
```

:::note info
Sequence関数は第2引数に数値を指定してあげると、Valueに振られる数値の開始値を指定できます。
:::


```Gallery_TimeView>Label_Time.Text
ThisItem.Value & ":00"
```
これでテキストには00:00から23:00が表示されます。

そしてカレンダー部分ですが、こちらはギャラリーがネストされています。
まずは親ギャラリー

```Gallery_EventView.Items
Sequence(168,0)
```
```Gallery_EventView.WrapCount
7
```

24(時間)×7(日)=168レコードのテーブルをギャラリーに渡し、7列目で折り返し表示します。
これでとりあえず24時間×7日間の枠は作成できました。

でも、せっかくなら分単位で指定したいですよね。
そこでさらにギャラリーをネストしていくわけです。

まぁ例えば30分単位なら(60÷30)×24×7=336レコードでも良いのですが、
ギャラリーをネストすることで1時間の枠と30分の枠でボーダーの描画を分けること等が可能になるので、こちらの方がUI的に見やすくなると思います。

ということで子ギャラリーにテーブルを渡していきます。

```Gallery_Hour.Items
ForAll(
    Sequence(
        Dropdown_Interval.Selected.Value,
        0
    ),
    DateAdd(
        DateAdd(
            DateAdd(
                First_DayofWeek,
                Mod(ThisItem.Value,7),
                TimeUnit.Days
            ),
            Int(ThisItem.Value/7),
            TimeUnit.Hours
        ),
        Value * (60/Dropdown_Interval.Selected.Value),
        TimeUnit.Minutes
    )
)
```

これで日時を振っていきます。
詳しい説明は…、ちょっと大変なので気が向くか必要としている人がいるなら追記していこうと思います。

```Gallery_EventView.OnSelect
Set(Slc_date,Gallery_Hour.Selected.Value);

If(Click_status,
    Set(StartDay,Slc_date);
    Set(EndDay,
        DateAdd(
            Slc_date,
            60/Dropdown_Interval.Selected.Value,
            TimeUnit.Minutes
        )
    );,
    If(
        StartDay < Slc_date,
            Set(EndDay,
                DateAdd(
                    Slc_date,
                    60/Dropdown_Interval.Selected.Value
                    ,TimeUnit.Minutes
                )
            );,
            Set(EndDay,
                DateAdd(
                    StartDay,
                    60/Dropdown_Interval.Selected.Value,
                    TimeUnit.Minutes
                )
            );
            Set(StartDay,Slc_date);
    )
)
```
ギャラリーを選択した際に値の日時を変数StartDayとEndDayに格納します。
日時のテキスト表示や計算、SharePointへの登録などの際にはこちらの変数を呼び出せば選択した日時を扱うことができます。

```Gallery_Hour.TemplateFill
If(
    ThisItem.Value >= StartDay And
    ThisItem.Value < EndDay ,
    First(Main_theme).select,
    RGBA(255,255,255,0)
)
```
選択した範囲のカレンダーが色づきます。
どこ選択してるか分からなくなっちゃうので大事です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3461887/55551cfe-4d88-2831-2693-51b01ce6fed9.png)



以上で基本的な部分は完了です。

## 4. ちょっと見た目を整えます

動作に関する部分は書き終わりましたが、見た目も大事ですからね。
工夫している部分だけ書いていきます。

```Gallery_EventView.Height、Gallery_TimeView.Height
H_Container.Height*4
```
こちらはEventViewたちの高さを指定しているわけですが、
親の水平コンテナーより大きくすることでスクロールが可能になります。
また、倍率は水平コンテナー内に何時間表示させたいかによって変わります。

例えば水平コンテナー内に12時間表示させたい場合は
24(時間)÷12(時間)=2
をかければよいということになります。

こちらもDropdown_Intervalと同じようなドロップダウンを配置してあげれば、ユーザー側が任意に指定できるようになりますね。

```Gallery_EventView.TemplateSize、Gallery_TimeView.TemplateSize
Self.Height/24
```
```Gallery_Hour.TemplateSize
Self.Height/Dropdown_Interval.Selected.Value
```
この辺は指定したインターバル等に合わせてギャラリー内のサイズを調整する式です。

あとカレンダー上部に曜日と日付を表示してあげなきゃですね。

```Gallery_WeekDay.Items
Sequence(7,0)
```
まずは一週間分のレコードを用意。

```Label_Week.Text
Left(
    Text(
        DateAdd(
            First_DayofWeek,
            ThisItem.Value,
            TimeUnit.Days
        ),
        "ddd"
    ),
    1
)
```

こちらは週表示になります。
基準日であるFirst_DayofWeekに日数を加算し、Text関数で曜日を取得。
更に今回は頭一文字だけでよいかなと思いLeft関数でトリミングしています。

```Label_Day.Text
If(
    ThisItem.Value = 0 Or 
    Day(DateAdd(First_DayofWeek,ThisItem.Value,TimeUnit.Days)) = 1,
    Text(
        DateAdd(
            First_DayofWeek,
            ThisItem.Value,
            TimeUnit.Days
        ),
        "m/d"
    ),
    Text(
        DateAdd(
            First_DayofWeek,
            ThisItem.Value,
            TimeUnit.Days
        ),
        "d"
    )
)
```
こちらは日付の表示になります。
基本的な理屈は週表示の方と同じなのですが、
カレンダーで一番左の日付と毎月1日だけ月も追加で表示するようにしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3461887/a9a76d9e-2ed9-3915-0c72-b59ef4da916f.png)
今何月を操作しているのか分かりやすいように、ちょっとした優しさってやつです。

## 5. 動作確認

![Videotogif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3461887/e3913a5e-ddd1-3c77-70e3-a33f4c51f341.gif)

ちょっと長くなってしまいましたがおつかれさまです！
これで画像のような処理が実現できると思います。

あとは選択した日時をSharePointに登録してもよし、Flowに渡してもよし、
Fillに条件式を指定して、他のデータソースから取得した日時をもとにイベントがあることを表示してもよし！
好きに扱ってもらえればと思います！

# おわり
以上、Power AppsでOutlookっぽいカレンダーを実現してみました。

本当はドラッグドロップでの範囲指定や既に登録した予定の移動なども実現してみたかったのですが、
Power Appsにはそういったオブジェクトはないので、今回のところはこの辺で勘弁しておいてあげようと思います。ﾏｯﾀｸｼｶﾀﾅｲﾈ

まぁそれでもなかなか分かりやすくて使いやすい日時指定機能を作れたのではないかなと思いますので、ぜひ皆さんも参考にしていただければと思います。

それでは。