Ark UIのSelectとField.Selectの使い分け

## この記事の概要

Ark UIのSelectコンポーネントには[単体のSelect](https://ark-ui.com/react/docs/components/select)と[Filedに属すSelect](https://ark-ui.com/react/docs/components/field#select)が存在します。

これらの違いと使い分けを備忘録がてら記事にしました。

:::note warn
Ark UIは色々なフレームワークで使えますが、この記事での例示コードはReactです。
:::

## 結論

通常のselect要素の見た目、使い勝手で十分な際は`Field.Select`を。
見た目や出現時のアニメーション、選択していることの示し方などにこだわりたいときは`Select`を選ぶと良いと思います。

|  | メリット | デメリット |
| --- | --- | --- |
| Field.Select | コードがシンプルで見やすい・分かりやすい | 通常のselect要素とoption要素でできること以上のことは無い |
| Select | 表示の仕方やアニメーションなど細かい箇所にこだわれる | 単なるselectとして使うには複雑な箇所が多い |

## 両者の違い

説明のしやすさの都合上、Field.Selectから説明します。

### Field.Select

exampleのコードは以下のようになっています。

```tsx:
import { Field } from '@ark-ui/react/field'

export const Select = () => {
  return (
    <Field.Root>
      <Field.Label>Label</Field.Label>
      <Field.Select>
        <option value="1">Option 1</option>
        <option value="2">Option 2</option>
        <option value="3">Option 3</option>
      </Field.Select>
      <Field.HelperText>Some additional Info</Field.HelperText>
      <Field.ErrorText>Error Info</Field.ErrorText>
    </Field.Root>
  )
}
```

- `Filed.Root`
    - `Filed`関連のpropsを伝播させるためのprovider
- `Field.Label`
    - idやdata属性などが良い感じに付与された`label`要素
- `Filed.Select`
    - `id`や`data`属性などが良い感じに付与された`select`要素
- `Filed.HelperText`, `Field.ErrorText`
    - 今回の話にはほぼ関係ないので省略
    - [ErrorTextに関しての記事](https://qiita.com/xrxoxcxox/items/f35fe633d8e2707936de)を別で書いているので良ければこちらもご覧ください

つまり、最終的なマークアップとしては`div`の中に`label`と`select`と少々のテキスト系要素が入っているだけです。

何もせずとも`label`と`select`を紐づけてくれるなど気が利いてはいますが、実態としては単なる`select`要素でしかない、と言えます。

### Select

exampleのコードは以下のようになっています。

```tsx
import { Portal } from '@ark-ui/react/portal'
import { Select, createListCollection } from '@ark-ui/react/select'
import { ChevronDownIcon } from 'lucide-react'

export const Basic = () => {
  const collection = createListCollection({ items: ['React', 'Solid', 'Vue'] })

  return (
    <Select.Root collection={collection}>
      <Select.Label>Framework</Select.Label>
      <Select.Control>
        <Select.Trigger>
          <Select.ValueText placeholder="Select a Framework" />
          <Select.Indicator>
            <ChevronDownIcon />
          </Select.Indicator>
        </Select.Trigger>
        <Select.ClearTrigger>Clear</Select.ClearTrigger>
      </Select.Control>
      <Portal>
        <Select.Positioner>
          <Select.Content>
            <Select.ItemGroup>
              <Select.ItemGroupLabel>Frameworks</Select.ItemGroupLabel>
              {collection.items.map((item) => (
                <Select.Item key={item} item={item}>
                  <Select.ItemText>{item}</Select.ItemText>
                  <Select.ItemIndicator>✓</Select.ItemIndicator>
                </Select.Item>
              ))}
            </Select.ItemGroup>
          </Select.Content>
        </Select.Positioner>
      </Portal>
      <Select.HiddenSelect />
    </Select.Root>
  )
}
```

`Field.Select`とはコードの長さがだいぶ違います。
すべてを説明するとかなり長くなってしまうので、かいつまんで記載します。

- `Select.Root`
    - `Select`関連のpropsを伝播させるためのprovider
- `Select.Control`とその配下
    - ざっくり言えば、これらの要素がクリックされると選択肢一覧が表示される
    - `Select.ValueText`や`Select.Indicator`はあくまで「よくある要素」として使われているに過ぎないというか、どれかを無くしても問題なく動作する
- `Portal`や`Select.Positioner`
    - Ark UIの`Select`は通常の`select`要素とは違い、ポップオーバー的に`option`群が表示される
    - そのため、それらの位置を制御するようなもの
- `Select.Content`とその配下
    - 通常の`select`でいう`option`要素達



また`return`の前に`createListCollection`という関数を使って`collection`を定義しています。

単なる配列やオブジェクトを渡してもダメで、こちらの関数を通してから`Select`に渡す必要があります。
これのおかげで複数選択ができたり前後のオプションの情報を得たり、色々なことができるようになっている、ように見えます[^1]。

[^1]: すみません。型を見る限りそのように見えるものの、ちゃんと検証できてはいません。

#### Selectのおまけ

`Select.Positioner`のスタイルには、親要素の幅・画面上で表示できる幅と高さが計算され、以下のCSSカスタムプロパティに渡ります。

```css
--reference-width:
--available-width:
--available-height:
```

これらを用いると、狙ったスタイリングもしやすいかと思います。

## 参考

https://github.com/chakra-ui/ark/tree/main/packages/react/src/components/select

https://github.com/chakra-ui/ark/blob/main/packages/react/src/components/field/field-select.tsx