MudBlazor で作る "input type=file"

# MudBlazor にはファイル選択のコンポーネントはないっぽい

C# でシングルページ Web アプリケーション (いわゆる "SPA") を実装できる技術、Blazor に関する話題。

前回の投稿で、Blazor 用の Material Design な UI コンポーネントライブラリとして、["MudBlazor" を紹介](https://qiita.com/jsakamoto/private/da7f978e1281fabb6093#mudblazor)しました。 

MudBlazor を使えば、例えばユーザーのアバター画像ファイルを設定してもらう、といったようなページも、下図のようにそれなりにかっこよく作ることが容易になります。
![movie-000.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/79167dbb-90c9-c9e2-fb3f-db1e8ce244c8.gif)
しかし、上図でわかりますが、**ファイル選択のコンポーネントは MudBlazor に用意がない**ようで、そのため、**Blazor 標準の `<InputFile/>` コンポーネント (DOM 上は素の `<input type="file"/>` としてレンダリングされる) をそのまま配置するだけ**になっており、ちょっと**イケてない**です。

そこでもうちょっと頑張って自分で実装を加えることで、下図のようにしてみたいと思います。
![movie-001.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/56863b39-9fcd-2213-2ba5-ac015cdcb8af.gif)
※下記 URL で実際に動作するライブデモを公開しています。
- https://sample-by-jsakamoto.github.io/MudBlazorInputFile/

# 基本コンセプトは簡単 ― `<label/>` 要素を使う

基本コンセプトは簡単です。

`<InputFile/>` コンポーネントは非表示にしておき、代わりに、その `<InputFile/>` コンポーネントにリンクする `<label/>` 要素を配置・表示しておく、というものです。
このためには、`<InputFile/>` コンポーネントには `id` 属性を適切に設定しておき、続く `<label/>` 要素の `for` 属性にて、`<InputFile/>` コンポーネントに設定した id 値を指定します。

```html
<InputFile id="avatar-picture-inputfile" Style="display:none;" .../>
<Label for="avatar-picture-inputfile">
  ファイル選択
</Label>
```

そうすることで、その `<label/>` 要素をクリックすると、表示上は見えなくなっている `<input type="file"/>` 要素をクリックしたのと同じ挙動になります。

こうすることで、`<input type="file"/>` 要素の見た目をいろいろいじるのは難しいところ、代わりに設けた `<label/>` 要素であれば、その見た目をいい感じにイジれるようになります。

# 見た目を Material Design な感じにする

ということで早速、"ファイル選択" label 要素を、MudBlazor 提供のボタンコントロールと同じ見た目にしてみます。

MudBlazor の設計・実装のおかげで、これは簡単です。

`<label/>` 要素に対し、**MudBlazor 由来の CSS クラス名を設定するだけ**です。

今回のケースでは、配色は `Primary`、外観は `Filled`、サイズは中のボタンと同じにしたかったため、以下の CSS クラス名を `<label/>` 要素に適用しました。

- mud-button-root 
- mud-button 
- mud-button-filled 
- mud-button-filled-primary 
- mud-button-filled-size-medium 
- mud-ripple

```html
...
<Label ... class="mud-button-root mud-button mud-button-filled mud-button-filled-primary mud-button-filled-size-medium mud-ripple">
  ファイル選択
</Label>
```

こうして CSS クラス名を指定するだけで、`<label/>` 要素の見た目が、まるっきり、MudBlazor のボタンコントロールと同じにできました。
クリックすると、ちゃんとリップル効果も再現されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/8aa50d70-60f8-0384-a1b0-d4c7182efa5a.png)

# 入力フィールドっぽくしてみる

大抵の要件では、以上で完了と思います。

が、今回はもう少し踏み込んで、このファイル選択用のユーザーインターフェースを、MudBlazor 提供のテキスト入力コンポーネントっぽく拡張してみます。

具体的には以下の表示要素を付け加えます。

- 入力項目の名称を表示するキャプション
- 選択されたファイル名を表示する入力フィールド ("入力" はできないけど)
- ヘルプテキスト

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/89811f33-d8b6-06d2-0bee-d81cbf10f251.png)

## 入力項目の名称を表示するキャプション

MudBlazor の入力系コンポーネントに表示される入力項目名のキャプションと、同じ見た目のテキストを表示するには、MudBlazor の `<MudText/>` コンポーネントが使えます。

この `<MudText/>` コンポーネントの `Typo` 属性に、`Typo.caption` を指定すれば OK です。

```html
<MudText Typo="Typo.caption">
    アバター画像ファイル
</MudText>
```

## 選択されたファイル名を表示する入力フィールド

これはファイル選択のコンポーネントなので、キーボードから何かテキストを入力するわけではありません。
代わりに、ぱっと見、テキスト入力コンポーネントっぽい見た目で、選択されたファイル名を表示するようにします。

ただのテキスト (選択されたファイルの名前) の表示ですから、先のファイル選択ボタンを `<label/>` 要素で実装したのと同じように、こちらも `<label/>` 要素で実現します。

外観の調整ではとりわけ、入力フィールドっぽさを醸し出す、灰色の下線の実装がキモです。

この下線の色の指定は、幸い、MudBlazor が配色情報を [CSS 変数](https://developer.mozilla.org/ja/docs/Web/CSS/Using_CSS_custom_properties)として実装していることから、自前の CSS スタイルシート中からも参照可能です。
具体的には、`--mud-palette-lines-inputs` という CSS 変数名で、入力フィールドの下線の色を参照可能です。

なので、自前の要素で、入力フィールドっぽい下線を引くには、以下のような CSS スタイルを適用すれば OK です。

```html
border-bottom: solid 1px var(--mud-palette-lines-inputs);
```

## ヘルプテキスト

MudBlazor の入力系コンポーネントの下部に表示されるヘルプテキストと、同じ見た目のテキストを表示するには、任意の HTML 要素に対し、`mud-input-helper-text` CSS クラスを適用すれば OK です。

```html
<div class="mud-input-helper-text">
    ファイルサイズは 512KB までです。
</div>
```

## エラー表示も統合

以上まで実装すると、ファイル種類やサイズについての入力検証結果を、MudBlazor の他の入力フィールド系と同じように、ヘルプテキスト欄に検証エラーメッセージを表示するようにもできます (下図)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/e2e0344f-4765-eaf5-199a-3918b036f9d2.png)

なお、上図のような入力検証エラー表示にあたり、キャプションや下線、ヘルプテキストの色も変えていますが、この配色も、MudBlazor 由来の CSS 変数で参照・使用できます。

具体的には、CSS 変数 `--mud-palette-error` を参照することでエラー系の色を参照できます。

# まとめ

以上のように少々の手間をかけることで、MudBlazor の他のコンポーネントと並べても違和感のない、見た目 Material Design っていう感じの、ファイル選択のユーザーインターフェースが作れるようになります。

実際の作例全体のサンプルプログラムを、下記 GitHub リポジトリで公開しています。
素の `<InputFile/>` コンポーネントを配置しているだけの状態から、順に変更を積んでいく様子を 1 コミットずつ残しています。
各コミット間の差分など参考になればと思います。

- https://github.com/sample-by-jsakamoto/MudBlazorInputFile/commits/master

また、MudBlazor は配色情報を CSS 変数で定義・実装してくれています。
そのお陰で、MudBlazor のテーマシステムにより具体的な配色が変更・カスタマイズされても、今回紹介の実装は何も変更することなく、テーマ配色に追随するのもうれしいところです。

ただし、今回紹介のファイル選択ユーザーインターフェースには、大きな弱点・難点があります。

それは、**キーボードから操作できない**ということです。

まず`<label/>` 要素にフォーカスがあたりません。
もっとも、この点については、`tabindex="0"` などと、`tabindex` 属性を `<label/>` 要素に指定することでフォーカスがあたるようにはなります。

しかしその先、例えばキーボードのスペースキーの打鍵などでは、ファイル選択のダイアログが開くことがありません。
ここで詰んでしまいます。

この一点、キーボードからの操作ができなくなる点が許容できれば、Material Design なファイル選択のユーザーインターフェースを実現可能、ということになるでしょう。

以上、_Learn, Practice, Share!_