# Select

`Select` is a control that provides a list of options that a user can select.

```tsx
import {Select} from '@gravity-ui/uikit';
```

## Options

Options to select.

### Defining options

You can define options as an array of objects or as the children of a component. The first approach is useful for cases where options require complex preparation and, possibly, memorization. The second one is convenient when there are few options, and their configuration does not require complex calculations.

#### Flat list

```tsx
// Array of objects
<Select
  placeholder="value"
  options={[
    {value: 'val_1', content: 'Value 1'},
    {value: 'val_2', content: 'Value 2'},
    {value: 'val_3', content: 'Value 3'},
    {value: 'val_4', content: 'Value 4'},
  ]}
/>
// Child nodes
<Select placeholder="value">
  <Select.Option value="val_1">Value 1</Select.Option>
  <Select.Option value="val_2">Value 2</Select.Option>
  <Select.Option value="val_3">Value 3</Select.Option>
  <Select.Option value="val_4">Value 4</Select.Option>
</Select>
```

#### Grouped list

```tsx
// Array of objects
<Select
  placeholder="value"
  options={[
    {
      label: 'Group 1',
      options: [
        {value: 'val_1', content: 'Value 1'},
        {value: 'val_2', content: 'Value 2'},
      ],
    },
    {
      label: 'Group 2',
      options: [
        {value: 'val_3', content: 'Value 3'},
        {value: 'val_4', content: 'Value 4'},
      ],
    },
  ]}
/>
// Child nodes
<Select placeholder="value">
  <Select.OptionGroup label="Group 1">
    <Select.Option value="val_1" content="Value 1" />
    <Select.Option value="val_2" content="Value 2" />
  </Select.OptionGroup>
  <Select.OptionGroup label="Group 2">
    <Select.Option value="val_3" content="Value 3" />
    <Select.Option value="val_4" content="Value 4" />
  </Select.OptionGroup>
</Select>
```

### Storing data in options

You can define and store unique data in each option by using the `option.data` property. This can be useful when you need to enrich the data when using the `onUpdate` callback or, for example, when drawing your options with `renderOption`.

## Selecting multiple options

To enable multiple selection, use the `multiple` property. Its default value is `false`.

```tsx
<Select multiple={true} placeholder="values">
  <Select.Option value="val_1">Value 1</Select.Option>
  <Select.Option value="val_2">Value 2</Select.Option>
  <Select.Option value="val_3">Value 3</Select.Option>
  <Select.Option value="val_4">Value 4</Select.Option>
</Select>
```

### Counter

You can add a counter of the selected items to the component using the `hasCounter` property.

```tsx
<Select multiple={true} hasCounter={true} placeholder="values">
  <Select.Option value="val_1">Value 1</Select.Option>
  <Select.Option value="val_2">Value 2</Select.Option>
  <Select.Option value="val_3">Value 3</Select.Option>
  <Select.Option value="val_4">Value 4</Select.Option>
</Select>
```

## Filtering options

To enable filter section, use the `filterable` property. Its default value is `false`.

```tsx
<Select filterable={true} placeholder="Filterable">
  <Select.Option value="val_1">Value 1</Select.Option>
  <Select.Option value="val_2">Value 2</Select.Option>
  <Select.Option value="val_3">Value 3</Select.Option>
  <Select.Option value="val_4">Value 4</Select.Option>
</Select>
```

## Size

To manage the default control and option size, use the `size` property. Its default size is `m`.

```tsx
<Select size="s" placeholder="S Size">
  <Select.Option value="val_1">Value 1</Select.Option>
</Select>
<Select size="m" placeholder="M Size">
  <Select.Option value="val_1">Value 1</Select.Option>
</Select>
<Select size="l" placeholder="L Size">
  <Select.Option value="val_1">Value 1</Select.Option>
</Select>
<Select size="xl" placeholder="XL Size">
  <Select.Option value="val_1">Value 1</Select.Option>
</Select>
```

## Control width

By default, the control width stretches to match the width of the content of the selected options. You can manage it by using the `width` property:

`'max'`: Stretches to the full width of the parent.

`number`: Applies width in pixels.

## Popup width

You can manage the popup width with the `popupWidth` property. The available values are:

`'fit'`: Apply control width.

`number`: Apply width in pixels.

Points to note about the default behavior:

- The popup width is equal to the width of the widest option, but not wider than `90vw`. This does not apply in case you use [virtualization](#virtualized-list).

- Narrow options are stretched to fit the width of the control.

### Virtualized list

For optimal display of a large number of options, `Select` has a built-in list virtualization tool. Virtualization is enabled after overcoming the threshold of the number of options (`50` by default). You can manage this value using the `virtualizationThreshold` property.

When using virtualization, some restrictions apply to the popup element:

- The popup width no longer gets adjusted to the length of the longest option.

- The minimum width of the popup is equal to the width of the control, or `100px` if the control is shorter.

## Advanced usage

There are many ways to customize your `Select`.

### Rendering custom control

To render a custom control, use the `renderControl` property.
Note: You should forward all arguments to your node in order to enable consistent behavior, just as when using the default control.

```tsx
import {Button} from '@gravity-ui/uikit';

const MyComponent = () => {
  const renderControl: SelectProps['renderControl'] = ({onClick, onKeyDown, ref}) => {
    return (
      <Button
        ref={ref}
        onClick={onClick}
        extraProps={{
          onKeyDown,
        }}
      >
        Your control
      </Button>
    );
  };

  return <Select renderControl={renderControl}>/* Your options here */</Select>;
};
```

### Rendering custom filter section

To render a custom filter section, use the `renderFilter` property and set the `filterable` property to `true`.
Note: You need to forward all arguments to your node in order to enable a properly working filter, just as when using the default configuration.

```tsx
import {Button, TextInput} from '@gravity-ui/uikit';
import type {SelectProps} from '@gravity-ui/uikit';

const MyComponent = () => {
  const renderFilter: SelectProps['renderFilter'] = (props) => {
    const {value, ref, onChange, onKeyDown} = props;

    return (
      <div>
        <TextInput
          controlRef={ref}
          controlProps={{size: 1}}
          value={value}
          onUpdate={onChange}
          onKeyDown={onKeyDown}
        />
        <Button>Do smth</Button>
      </div>
    );
  };

  return (
    <Select filterable={true} renderFilter={renderFilter}>
      /* Your options here */
    </Select>
  );
};
```

### Rendering custom options

To render custom options, use the `renderOption` property:

```tsx
import type {SelectProps} from '@gravity-ui/uikit';

const MyComponent = () => {
  const renderOption: SelectProps['renderOption'] = (option) => {
    return <div style={{color: option.data.color}}>{option.children}</div>;
  };

  return (
    <Select renderOption={renderOption}>
      <Select.Option value="val_1" data={{color: '#8FE1A1'}}>
        Value 1
      </Select.Option>
      <Select.Option value="val_2" data={{color: '#38C0A8'}}>
        Value 2
      </Select.Option>
      <Select.Option value="val_3" data={{color: '#3A7AC3'}}>
        Value 3
      </Select.Option>
      <Select.Option value="val_4" data={{color: '#534581'}}>
        Value 4
      </Select.Option>
    </Select>
  );
};
```

### Rendering custom selected options

To render custom selected options, use the `renderSelectedOption` property:

```tsx
import type {SelectProps} from '@gravity-ui/uikit';

const MyComponent = () => {
  const renderSelectedOption: SelectProps['renderSelectedOption'] = (option) => {
    return <div style={{color: option.data.color}}>{option.children}</div>;
  };

  return (
    <Select renderSelectedOption={renderSelectedOption}>
      <Select.Option value="val_1" data={{color: '#8FE1A1'}}>
        Value 1
      </Select.Option>
      <Select.Option value="val_2" data={{color: '#38C0A8'}}>
        Value 2
      </Select.Option>
      <Select.Option value="val_3" data={{color: '#3A7AC3'}}>
        Value 3
      </Select.Option>
      <Select.Option value="val_4" data={{color: '#534581'}}>
        Value 4
      </Select.Option>
    </Select>
  );
};
```

### Rendering options with different heights

Options have a fixed height according to the `size` property. If you need to render options with different heights, you can use the `option.data` property. It will store information about what height you need to set for the options, as well as the `getOptionHeight` property to set this value.

```tsx
import type {SelectProps} from '@gravity-ui/uikit';

const MyComponent = () => {
  const getOptionHeight: SelectProps['getOptionHeight'] = (option) => option.data.height;

  return (
    <Select getOptionHeight={getOptionHeight}>
      <Select.Option value="val_1" data={{height: 20}}>
        Value 1
      </Select.Option>
      <Select.Option value="val_2" data={{height: 40}}>
        Value 2
      </Select.Option>
      <Select.Option value="val_3" data={{height: 60}}>
        Value 3
      </Select.Option>
      <Select.Option value="val_4" data={{height: 80}}>
        Value 4
      </Select.Option>
    </Select>
  );
};
```

### Rendering custom counter

To render a custom counter, use the `renderCounter` property. The counter is only displayed when multiple selection is enabled (`multiple={true}`) and `hasCounter={true}`.

```tsx
import type {SelectProps} from '@gravity-ui/uikit';

const MyComponent = () => {
  const renderCounter: SelectProps['renderCounter'] = (_, {count, disabled}) => {
    if (count === 0) {
      return null;
    }
    if (count >= 2) {
      return (
        <div
          style={{
            padding: '0 8px',
            color: disabled ? '#999' : '#027bf3',
            fontWeight: 'bold',
          }}
        >
          +{count}
        </div>
      );
    }
    return count;
  };

  return (
    <Select multiple={true} hasCounter={true} renderCounter={renderCounter}>
      <Select.Option value="val_1">Value 1</Select.Option>
      <Select.Option value="val_2">Value 2</Select.Option>
      <Select.Option value="val_3">Value 3</Select.Option>
      <Select.Option value="val_4">Value 4</Select.Option>
    </Select>
  );
};
```

### Rendering options list

The `renderPopup` property allows you to control the content of the options list: change the order of standard elements (filter, list), hide them, or add custom elements between, before, or after them.

```tsx
import type {SelectProps} from '@gravity-ui/uikit';

const renderPopup: SelectProps['renderPopup'] = ({renderList, renderFilter}) => {
  return (
    <React.Fragment>
      {renderFilter()}
      <div className="CustomElement" />
      {renderList()}
    </React.Fragment>
  );
};

const MyComponent = () => {
  return (
    <Select filterable renderPopup={renderPopup}>
      <Select.Option value="val_1" data={{color: '#8FE1A1'}}>
        Value 1
      </Select.Option>
      <Select.Option value="val_2" data={{color: '#38C0A8'}}>
        Value 2
      </Select.Option>
      <Select.Option value="val_3" data={{color: '#3A7AC3'}}>
        Value 3
      </Select.Option>
      <Select.Option value="val_4" data={{color: '#534581'}}>
        Value 4
      </Select.Option>
    </Select>
  );
};
```

### Error

This `Select` state is for incorrect user input. To change the `Select` appearance, use the `validationState` property with the `"invalid"` value. Optionally, you can provide an error message through the `errorMessage` property. By default, the message text is rendered outside the component.
You can change this with the `errorPlacement` property.

## Properties

| Name                                                      | Description                                                                                                                      | Type                                     | Default                                                  |
| :-------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------- | :------------------------------------------------------- |
| className                                                 | Control className                                                                                                                | `string`                                 |                                                          |
| defaultValue                                              | Default values that represent selected options in case of using an uncontrolled state                                            | `string[]`                               |                                                          |
| disabled                                                  | Shows that the user cannot work with the control                                                                                 | `boolean`                                | `false`                                                  |
| [filterable](#filtering-options)                          | Shows that select popup has a filter section                                                                                     | `boolean`                                | `false`                                                  |
| filterOption                                              | Used to compare option with filter                                                                                               | `function`                               |                                                          |
| filterPlaceholder                                         | Default filter input placeholder text                                                                                            | `string`                                 |                                                          |
| [getOptionHeight](#render-options-with-different-heights) | Used to set height of customized user options                                                                                    | `function`                               |                                                          |
| getOptionGroupHeight                                      | Used to set height of customized user option group                                                                               | `function`                               |                                                          |
| hasClear                                                  | Enables displaying icon for clearing selected options                                                                            | `boolean`                                | `false`                                                  |
| id                                                        | `id` HTML attribute                                                                                                              | `string`                                 |                                                          |
| label                                                     | Control label                                                                                                                    | `string`                                 |                                                          |
| loading                                                   | Adds the loading item to the end of the option list. Works like a persistent loading indicator while the options list is empty.  | `boolean`                                |                                                          |
| [multiple](#selecting-multiple-options)                   | Shows whether multiple options can be selected in the list                                                                       | `boolean`                                | `false`                                                  |
| name                                                      | Name of the control                                                                                                              | `string`                                 |                                                          |
| onBlur                                                    | Handler that is called when the element loses focus.                                                                             | `function`                               |                                                          |
| filter                                                    | Controlled filter value                                                                                                          | `string`                                 | `''`                                                     |
| onFilterChange                                            | Fires every time after changing the filter                                                                                       | `function`                               |                                                          |
| onFocus                                                   | Handler that is called when the element gets focus                                                                               | `function`                               |                                                          |
| onLoadMore                                                | Fires when the loading indicator gets visible                                                                                    | `function`                               |                                                          |
| onOpenChange                                              | Fires every time after changing popup visibility                                                                                 | `function`                               |                                                          |
| onUpdate                                                  | Fires when an alteration to the `Select` value is committed by the user                                                          | `function`                               |                                                          |
| [options](#options)                                       | Options to select                                                                                                                | `(SelectOption \| SelectOptionGroup)[]`  |                                                          |
| pin                                                       | Control border view                                                                                                              | `string`                                 | `'round-round'`                                          |
| placeholder                                               | Placeholder text                                                                                                                 | `string`                                 |                                                          |
| popupClassName                                            | Popup with the option list `className`                                                                                           | `string`                                 |                                                          |
| popupPlacement                                            | Popup placement                                                                                                                  | `PopupPlacement` `Array<PopupPlacement>` | `['bottom-start', 'bottom-end', 'top-start', 'top-end']` |
| [popupWidth](#popup-width)                                | Popup width                                                                                                                      | `number \| 'fit' \| 'outfit'`            | `'outfit'`                                               |
| qa                                                        | Test id attribute (`data-qa`)                                                                                                    | `string`                                 |                                                          |
| [renderControl](#render-custom-control)                   | Used to render user control                                                                                                      | `function`                               |                                                          |
| [renderCounter](#rendering-custom-counter)                | Used to render user counter. Works only with [hasCounter](#counter).                                                             | `function`                               |                                                          |
| renderEmptyOptions                                        | Used to render a node for an empty option list                                                                                   | `function`                               |                                                          |
| [renderFilter](#render-custom-filter-section)             | Used to render user filter section                                                                                               | `function`                               |                                                          |
| [renderOption](#render-custom-options)                    | Used to render user options                                                                                                      | `function`                               |                                                          |
| renderOptionGroup                                         | Used to render user option groups                                                                                                | `function`                               |                                                          |
| [renderSelectedOption](#render-custom-selected-options)   | Used to render user selected options                                                                                             | `function`                               |                                                          |
| [renderPopup](#rendering-options-list)                    | Used to render options list content                                                                                              | `function`                               |                                                          |
| [size](#size)                                             | Control / options size                                                                                                           | `string`                                 | `'m'`                                                    |
| value                                                     | Values that represent selected options                                                                                           | `string[]`                               |                                                          |
| view                                                      | Control view                                                                                                                     | `string`                                 | `'normal'`                                               |
| [virtualizationThreshold](#virtualized-list)              | Option count threshold after which virtualization is enabled                                                                     | `number`                                 | `50`                                                     |
| [width](#control-width)                                   | Control width                                                                                                                    | `string \| number`                       | `undefined`                                              |
| errorMessage                                              | Error text                                                                                                                       | `string`                                 |                                                          |
| errorPlacement                                            | Error position                                                                                                                   | `outside` `inside`                       | `outside`                                                |
| validationState                                           | Validation state                                                                                                                 | `"invalid"`                              |                                                          |
| [hasCounter](#counter)                                    | Shows the selected option count. The counter appears only when the [multiple](#selecting-multiple-options) selection is enabled. | `boolean`                                |                                                          |

## CSS API

| Name                             | Description                                   |
| :------------------------------- | :-------------------------------------------- |
| `--g-select-focus-outline-color` | Outline color if focused (missing by default) |
