🛷

SolidJS で Material-UI(SUID)を試してみた

2022/05/05に公開

最近話題になりつつある(?)SolidJS 向けの Material-UI ライブラリ SUID を少しだけ試してみました。

SUID

https://suid.io/

React 用の MUI を SolidJS 向けに port(移植)するライブラリです。

バージョン 0.4.1(2022/5/29 リリース)時点では 41 のコンポーネントが移植されています。

なお、SUID のサイト自体が SUID を使って作成・構築されています。

試したバージョン

  • SolidJS : 1.4.3
  • SUID : 0.4.1
  • supabase-js : 1.35.3

試した内容

SupabaseQuickstart: SolidJS を TypeScript(TSX)と Material-UI で置き換えつつ、Card 表示も追加で試してみました。

また、0.3.0 で追加された Avatar も試してきました。

サンプルコード(全体)

こちらに置いてあります。タイトルのとおり、2022/5/31 開催予定の 第 33 回 PostgreSQL アンカンファレンス@オンライン 向けに用意したサンプルです。

https://github.com/hmatsu47/pgunconf-sample-app

ここからはサンプルコードでの使用例と気になった点を挙げていきます(当然ですが今後のバージョンで変わる可能性があります)。

TextField(テキスト入力フィールド)

使用例

Auth.tsx(91行目〜)
  <TextField
    required
    id="email"
    label="メールアドレス"
    helperText="メールアドレスを入力してください"
    value={email()}
    onChange={(event, value) => {
      setEmail(value);
    }}
  />

required指定の場合、未入力で Form を Submit しようとするとアラートが表示されブロックされます。ただし Submit を使わない場合は「*」印の表示のみでブロック動作などは生じません(自前で Validation を実装する必要あり)。

inputRefに対応していない

React の MUI ではTextFieldrefの代わりにinputRefが使えますが、SUID では対応していないようです。

今回は画面表示直後のフォーカスの指定に使いたかったのですが、諦めてdocument.getElementById()を使ってフォーカスを移動しました。

これについては元からrefinputRefには対応しない方針なのかもしれません。

setFocus.ts(3行目〜)
  const element = document.getElementById(elementId);
  element?.focus();
Auth.tsx(23行目〜)
  onMount(() => {
    setFocus("email");
  })

multilineに対応していない

SUID では現状TextareaAutosizeに対応していないので代わりにTextFieldMultiline を使おうと思ったのですが、multilinerowなどには対応していませんでした。

仕方なく通常のtextareaタグを使いました。

EditItem.tsx(210行目〜)
  <textarea
    id="note"
    aria-label="Note"
    placeholder="本文を入力してください"
    onchange={(event) => {
      setNote(event.currentTarget.value);
    }}
  >
    {note()}
  </textarea>

Card(カード表示)

使用例

ViewItem.tsx(38行目〜)
  <Card
    id="itemCard"
    variant="outlined"
  >
    <CardContent>
      <Stack spacing={1} direction="row">
        <CardActions sx={{ padding: 0 }}>
          <IconButton onClick={() => toggleExpand()} sx={{ padding: 0 }}>
            <Switch fallback={<></>}>
              <Match when={!expand()}>
                <ExpandMoreIcon aria-label="expand more"/>
              </Match>
              <Match when={expand()}>
                <ExpandLessIcon aria-label="expand less"/>
              </Match>
            </Switch>
          </IconButton>
        </CardActions>
        <Typography variant="h6" gutterBottom>
          {props.article.title}
        </Typography>
(中略)
      </Stack>
      <Show when={expand()} fallback={<></>}>
        <Fade in={expand()} timeout={500}>
          <Box>
            <For each={props.article.note?.split("\n")} fallback={<></>}>
              {(line) =>
                <Typography variant="body1" gutterBottom>
                  {line}
                </Typography>
              }
            </For>
          </Box>
        </Fade>
      </Show>
      <CardActions sx={{ padding: 0 }}>
        <IconButton
          aria-label="edit"
          onClick={() => props.changeArticle(props.article)}
          disabled={
            props.article.userId !== props.session.user!.id &&
            props.article.noteType !== NoteType.Writable
          }
        >
          <EditIcon />
        </IconButton>
(中略)
      </CardActions>
    </CardContent>
  </Card>

※ 「新規投稿」の下から続くカードです。なお「新規投稿」もCardで表示しています。

CollapseAPI に対応していない

React 用 MUI にある Collapse API に対応していないため、上に記したコードでも SolidJS 自体が持つ Show API を使って類似の処理をしています(Collapse API とは違い開閉そのもののアニメーション動作はしませんが、Fadeを使って文字の表示をフェードインさせています)。

ViewItem.tsx(43行目〜:展開ボタン部分)
  <Switch fallback={<></>}>
    <Match when={!expand()}>
      <ExpandMoreIcon aria-label="expand more"/>
    </Match>
    <Match when={expand()}>
      <ExpandLessIcon aria-label="expand less"/>
    </Match>
  </Switch>
ViewItem.tsx(81行目〜:実際に展開する部分)
  <Show when={expand()} fallback={<></>}>
    <Fade in={expand()} timeout={500}>
      <Box>
        <For each={props.article.note?.split("\n")} fallback={<></>}>
          {(line) =>
            <Typography variant="body1" gutterBottom>
              {line}
            </Typography>
          }
        </For>
      </Box>
    </Fade>
  </Show>

使ってみた感想

バージョン 0.1.0 → 0.2.x → 0.3.x で徐々に対応コンポーネントが増えていますが、まともなプロダクトを作るには足りないコンポーネント・API がまだ少なくない印象です。

今後のバージョンアップに期待、ですね。

参考:その他のサンプル画面

プロフィール編集画面

おまけ:アバター(Avatar)表示

SUID 0.3.0 で Avatar に対応したので、一覧表示とタイトルバーで使ってみました。

使用例

ViewItem.tsx(56行目〜)
<Avatar
  alt={props.article.userName}
  src={props.avatar}
  sx={{
    width: 28,
    height: 28
  }}
/>

srcで指定した画像のロードに失敗した場合は自動的にフォールバックしてデフォルトのアバターアイコン(Person)が表示されます。

GitHubで編集を提案

Discussion