Material UI + TypeScript で Mixin を定義する
TypeScript + Materia UI でアプリを書いていて、テーマ関係のメソッドの中に createMixins
というものを見つけたので使い方を調べてみました。
デフォルトの mixin といえば gutter
くらいしかなくて、自分でも登録できたら色々使えそうだな...と思い、型定義を追いかけながらコードを試行錯誤したところ、多少不恰好ですが動くものができたのでメモしておきます。
プロジェクトコードは以下のリポジトリにあります。
プロジェクト作成
プロジェクトは create-react-app
の react-scripts-ts
で生成したものをベースにしています。この辺は特に詳細は説明しません。
create-react-app practice-mui-with-jsonschema-form --scripts-version=react-scripts-ts --use-npm
Theme の定義
Theme
を定義しているファイルの全体のコードは以下のようになります。
import { createMuiTheme } from '@material-ui/core/styles' import createMixins from '@material-ui/core/styles/createMixins' import { CSSProperties } from '@material-ui/core/styles/withStyles' // `Mixins` 定義を拡張する declare module '@material-ui/core/styles/createMixins' { interface Mixins { myBackgroundMixin: CSSProperties, // <- custom mixin! } } // `createMixins` で利用するインスタンスを取得する const { breakpoints, spacing } = createMuiTheme() const mixins = createMixins(breakpoints, spacing, { myBackgroundMixin: { backgroundColor: '#ff0000' } }) export default createMuiTheme({ mixins, palette: { divider: '#ff0000' }, })
以下、個々のポイントを説明していきます。
Mixin の定義を拡張する
createMixins
で参照されている interface Mixins
の定義を見ていると何やら意味深なコメントが書いてあります。
export interface Mixins { gutters: (styles?: CSSProperties) => CSSProperties; toolbar: CSSProperties; // ... use interface declaration merging to add custom mixins }
TypeScript の柔軟であり分かりづらい部分ですが、interface
などの定義は後追い拡張(マージ)が行えます。
ここでは以下のように myBackgroundMixin
を追加定義してみました。
declare module '@material-ui/core/styles/createMixins' { interface Mixins { // (=˘ ꒳ ˘=) 展開先を赤く染め上げる血みどろの Mixin... myBackgroundMixin: CSSProperties, } }
Mixin の定義を行う
これにより createMixins
の引数である MixinsOptions
にも拡張が反映され、createMixins
内で myBackgroundMixin
が定義できるようになります
const mixins = createMixins(breakpoints, spacing, { // (=˘ ꒳ ˘=) 血みどろの実装...容赦のないビビッドな赤 myBackgroundMixin: { backgroundColor: '#ff0000' } })
上の定義の拡張を行わないと Object literal may only specify known properties, and 'myBackgroundMixin' does not exist in type 'MixinsOptions'.
というエラーが出ます。
ちょっと曲者な createMixins の引数
createMixins
の引数には Breakpoint
と Spacing
のインスタンスが必要なのですがいちいち用意したくはないですよね。今回は小手先のアイディアとして一度 createMuiTheme()
を実行してデフォルトのインスタンスを取得しています。
const { breakpoints, spacing } = createMuiTheme()
もっといい方法がある気がしますね。
実際に利用してみる
上記の定義を含めた Theme
インスタンスを MuiThemeProvider
経由で提供してやれば、配下のコンポーネントで今回定義した myBackgroundMixin
を利用することができます。
import * as React from 'react' import Button from '@material-ui/core/Button' import { createStyles, Theme, withStyles, WithStyles } from '@material-ui/core/styles' interface Props extends WithStyles<typeof styles> { name: string } function MyButton({ classes, name }: Props) { return ( <Button className={classes.root}> {name} </Button> ) } const styles = ({ mixins, palette }: Theme) => createStyles({ root: { ...mixins.myBackgroundMixin, color: palette.common.white, } }) export default withStyles(styles)(MyButton)
無事、手元では赤いボタンが表示されました。
ここまで、細切れの説明になりましたので、冒頭で紹介したリポジトリで全体を見ていただくのが分かりやすいかもしれません。