LoginSignup
6

posted at

updated at

React + MUI v5 でタグのようなUIを作る

Gmail メール作成のアドレス表示のように、入力・選択した文字をタグ表示する方法です。React UIライブラリのMUIで作れると知り試作。共有したい方がいたので、この機会に調べた内容を残すことにしました。ご使用の際は、少しでも調査の時短につながれば嬉しいです。

試作品

スクリーンショット 2023-02-03 ゴール.png
離島名をタグ表示しました。
GitHub: rito-marker

環境

typescript: 4.9.5
react: 18.2.0
react-dom: 18.2.0
mui: 5.11.7

mui v5を使用するには次のバージョンが必要です
react: 17.0.0 以上
react-dom: 17.0.0 以上

公式情報

Peer dependencies
react >= 17.0.0 and react-dom >= 17.0.0 are peer dependencies.

方法

  1. MUIをインストール
  2. Autocompleteコンポーネントを使用
  3. タグのカラーを変更

1. MUIをインストール

npmの場合
npm install @mui/material @emotion/react @emotion/styled
yarnの場合
yarn add @mui/material @emotion/react @emotion/styled

2. Autocompleteコンポーネント

Autocompleteのデモ を参考にしました。必要なコンポーネントをインポートし、次のようにreturnします。

src/components/MarkerCreatingInputField.tsx
import { Autocomplete, TextField } from '@mui/material' //必要なコンポーネントをインポート

// ~~ 省略 ~~

return (

  // ~~ 省略 ~~

  <Autocomplete //importしたコンポーネントを使用
    multiple //複数選択できるようになる --- ①
    freeSolo //任意の入力値を管理できる(デフォルトはオプション選択のみ)
    filterSelectedOptions //選択されたオプションを非表示にする --- ②
    options={islands.map(option => option.name)} //ドロップダウンメニューの項目:文字列の配列
    value={inputValues} //入力欄に表示される値:①のときは文字列の配列、指定しないときは文字列 --- ③
    onChange={handleInputChange} //コールバック関数(オプションを選択か「Enter」を押すとイベントが起きる): function --- ④
    sx={{
      width: 600,
      display: 'inline-block',
    }}
    renderInput={params => (
      <TextField  //importしたコンポーネント
        {...params}
        variant='standard'
        label='離島マーカーを作る' // --- ⑤
        placeholder='離島名を選択か、入力後に「Enter」でタグが表示。「+」でマーカーを作成'
        error={validation.error} //エラー状態(trueのときは⑤labelや⑥helperTextが赤色になる): boolean
        helperText={validation.message} //入力欄の下に表示されるテキスト: node(公式のデモ通り文字列を指定) // --- ⑥
      />
    )}
  />
)

②の補足
選択されたオプションは非表示になります。
スクリーンショット 2023-02-03 選択前.png
スクリーンショット 2023-02-03 選択後.png
ですが、選択後に「+」ボタンでマーカーを作るようにしたので、その場合は再レンダー時にoptionsの配列で初期化され表示されます。そのため、マーカーを作成した離島は取り除きoptionsに含まないようにしました。

src/components/providers/ShapeMarkerProvider.tsx
  // Inputのセレクトオプション。textsにある離島名は削除し表示しない
  const islands = useMemo(
    () => ISLANDS.filter(island => texts.indexOf(island.name) === -1),
    [texts]
  )

④の補足
コールバック関数(handleInputChange)が受け取る主な引数は、function(event, value) です。なので入力した値はevent.target.valueではなくvalueで渡されます。また、multipleのときは文字列の配列で、指定しないときは文字列になります。このvalueを③のvalueへセットする流れになります。

3. タグのカラーを変更

今回はカラーを変更しませんでしたが、追加で調べました。2. AutocompleteデモのCodeSandboxを参考にrenderTags を追加します。図のようにChipコンポーネントを加え、例えばcolor='info'で画像のようにinfoカラーになります。今回はスタイル変更ではなくcolorを指定する簡単な例まででした。

src/components/MarkerCreatingInputField.tsx
import { Autocomplete, TextField, Chip } from '@mui/material' //Chipを追加

// ~~ 省略 ~~

<Autocomplete
  // ~~ 省略 ~~
  renderTags={(value: readonly string[], getTagProps) =>
    value.map((option: string, index: number) => (
      <Chip variant='outlined' label={option} color='info' {...getTagProps({ index })} /> //選択した離島名が各optionに渡される
    ))
  }
  // ~~ 省略 ~~
/>

スクリーンショット 2023-02-04 8.51.22.png
面白い名前の島があるんですね。ちなみに村役場さんの公式サイトを見ると、宝島は「海とサンゴと伝説に彩られたロマンの島」のようです:palm_tree:

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
6