はじめに
フレームワークやライブラリを利用して開発を行うと、やはり少なからず、カスタマイズする必要が出てくることがあります。
今回は、「React」 × 「UIフレームワークのMaterial-UI」を使って開発する際にちょっと手こずったポイントがありましたので紹介です。
対象のコンポーネント 【TextField】
インプットフィールドや、セレクトボックスなど、HTMLタグの<input>
として活用できます。
Material-UIについて
Material-UIはもともとあらゆるAPIが用意されており、カスタマイズ性も結構担保されており使いやすい印象です。
しかし、今回紹介するTextField
コンポーネントで入力させたくないdisabled
のUIを変更したい際に問題が出てきました。
基本的なカスタマイズ
基本的に用意されているカスタマイズ方法として、公式ドキュメントをベースにButton
コンポーネントのカスタマイズを行った際のソースコードが以下です。
import React from "react";
import Button from "@material-ui/core/Button";
import FormLabel from "@material-ui/core/FormLabel";
import { makeStyles } from "@material-ui/core/styles";
import "./styles.css";
const styles = makeStyles(theme => ({
rootBtn: { // Buttonクラスのrootクラス
marginRight: "1em",
backgroundColor: "#ff8800",
"&:disabled": {
color: "#bababa",
backgroundColor: "#dadada"
}
}
}));
export default function App() {
const classes = styles();
return (
<div className="App">
<h1>ボタンの例</h1>
<div>
<Button className={classes.rootBtn}>Button</Button>
<FormLabel>通常デザイン</FormLabel>
</div>
<div>
<Button className={classes.rootBtn} disabled>
Button
</Button>
<FormLabel>disabledデザイン</FormLabel>
</div>
</div>
);
}
上が機能するデザインで、下がdisabled
にした場合になります。
公式ドキュメントにあるように、disabled
APIがtrueの場合、rootに対して擬似クラスを宣言するように指示されています。
Pseudo-class applied to the root element if disabled={true}.
参照:公式ドキュメントより
今回、rootクラスにrootBtn
を指定して、その擬似クラスにdisabled
のスタイルを定義しています。
有効になるトリガーは、disabled
のAPIがtrueの場合になるため、下のボタンの時のみこのスタイルが有効になっています。
このように、APIにCSSクラスに用意されたものがあり、簡単にスタイルを環境に合わせてカスタマイズすることができるのは大きな魅力です。
TextFieldでのカスタマイズ
上記のButton
コンポーネントの感じで作っていたら失敗しました。
全然スタイルが効かない。仕方なく、ドキュメントを眺めていても一向に解決方法は見当たりませんでした。
そこで、構造を見にいくと、TextField
コンポーネントは、いくつかのコンポーネントが合わさって構成されているようでした。
<!-- inputタグラップされて形成されている -->
<div class="TextField">
<input class="input-base">
...
</>
</>
disabledのスタイルは<input>
で定義されているようだったので、どうにかここを上書きする方法が必要でした。
ローカルルールを参照させる $ruleName
こんな書き方があったなんて知らなかった。
const styles = {
root: {
'&$disabled': {
color: 'white',
},
},
disabled: {},
};
これを参考にスタイルするとうまくスタイルが効きました。
ソースは以下です。
import React from "react";
import TextField from "@material-ui/core/TextField";
import FormLabel from "@material-ui/core/FormLabel";
import { makeStyles } from "@material-ui/core/styles";
import "./styles.css";
const styles = makeStyles(theme => ({
rootTxt: {
marginRight: "1em",
"&$disabled": {
color: "#bababa",
backgroundColor: "#dadada"
}
},
disabled: {}
}));
export default function App() {
const classes = styles();
return (
<div className="App">
<h1>テキストフィールドの例</h1>
<div>
<TextField
InputProps={{
classes: {
root: classes.rootTxt,
disabled: classes.disabled
}
}}
defaultValue="TextField"
/>
<FormLabel>通常デザイン</FormLabel>
</div>
<br />
<div>
<TextField
InputProps={{
classes: {
root: classes.rootTxt,
disabled: classes.disabled
}
}}
defaultValue="TextField"
disabled
/>
<FormLabel>disabledデザイン</FormLabel>
</div>
</div>
);
}
さいごに
今回取り上げたのは、TextField
コンポーネントですが、他にもコンポーネントを掛け合わせた形で表現されているコンポーネントはたくさんあります。
そういったコンポーネントもおそらくこの書き方が必要になってきそうですね。
前提として知っているのと知っていないので、使い方や設計が大きく変わってきそうだなと感じながら今回はなんとか解決することができました!
紹介した内容のSnadbox
P.S
気がついた頃にはこんなこと書いてた!
The TextField is a convenience wrapper for the most common cases (80%). It cannot be all things to all people, otherwise the API would grow out of control.