> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Select Trigger - Web

> Select Trigger provides a means of displaying selection without using a Popover.

> [!NOTE]
>
> For development, import `SelectTrigger` from
> `@servicetitan/anvil2`. For design, find it in the
> [Anvil2 file](https://www.figma.com/design/xaNQsvRKczoxu7pj0M6QT8/Anvil2-v3).

[selecttrigger-dialog-apply](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/selecttrigger-dialog-apply?embed=1\&hideNavigation=1\&hideExplorer=1\&terminalHeight=0\&file=src/App.tsx)

## Anatomy

Select Trigger consists of two primary elements that work together to display and manage selections.

![Anatomy of a
Select Trigger](https://mintcdn.com/servicetitan/MMRqs3hy702UF0GS/images/docs/web/components/select-trigger/anatomy.png?fit=max\&auto=format\&n=MMRqs3hy702UF0GS\&q=85\&s=2bd364ce099a1715d0e35bead8497481)

1. **Trigger** - Identifies the field and its purpose
2. **Clear button** (Optional) - Removes all selections when clicked

## Options

Select Trigger supports the following configurations to accommodate various selection scenarios.

### Single and multiple selection

selecttrigger-playground

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/selecttrigger-playground?file=src/App.tsx)

```tsx lines theme={null}
import {
  SelectTrigger,
  Flex,
  Dialog,
  Checkbox,
  Button,
  type CheckboxProps,
} from "@servicetitan/anvil2";
import { useState } from "react";

type Item = {
  id: number;
  name: string;
};

const items = [
  { id: 1, name: "First item" },
  { id: 2, name: "Second item" },
  { id: 3, name: "Third item" },
  { id: 4, name: "Fourth item" },
  { id: 5, name: "Fifth item" },
];

function App() {
  const [selectedItems, setSelectedItems] = useState<Item[] | null>([
    items[2],
    items[3],
  ]);
  const [dialogSelectedItems, setDialogSelectedItems] = useState<Item[]>([
    items[2],
    items[3],
  ]);
  const [openDialog, setOpenDialog] = useState(false);

  const handleDialogClose = () => setOpenDialog(false);

  const handleCancelClick = () => setDialogSelectedItems(selectedItems ?? []);

  const handleApplyClick = () => {
    setOpenDialog(false);
    setSelectedItems(dialogSelectedItems);
  };

  const handleCheckboxClick: CheckboxProps["onClick"] = (e, state) => {
    if (e && state) {
      const { checked } = state;

      if (checked) {
        setDialogSelectedItems((prevItems) =>
          prevItems.filter(
            (item) => item.name !== (e.target as HTMLInputElement).value,
          ),
        );
      } else {
        const selectedItem = items.find(
          (item) => item.name === (e.target as HTMLInputElement).value,
        );

        if (selectedItem) {
          setDialogSelectedItems((prevItems) => [...prevItems, selectedItem]);
        }
      }
    }
  };

  return (
    <div style={{ minHeight: "284px" }}>
      <div style={{ padding: "1.5rem" }}>
        <Flex direction="column" gap="6">
          <SelectTrigger
            multiple
            selectedItems={selectedItems ?? []}
            itemToString={(item) => (item ? item.name : "")}
            onChange={(items: Item[] | null) => setSelectedItems(items)}
            onClick={() => {
              setDialogSelectedItems(selectedItems ?? []);
              setOpenDialog(true);
            }}
            label="Label"
            placeholder="Placeholder"
            hint="Hint text"
            moreInfo="More info"
            size="large"
            required
          />
        </Flex>

        <Dialog open={openDialog} onClose={handleDialogClose}>
          <Dialog.Header>Dialog Header</Dialog.Header>

          <Dialog.Content>
            <Checkbox.Group legend="Choose item(s)" required>
              {items.map((item) => (
                <Checkbox
                  key={item.name}
                  label={item.name}
                  value={item.name}
                  checked={dialogSelectedItems.includes(item)}
                  onClick={handleCheckboxClick}
                />
              ))}
            </Checkbox.Group>
          </Dialog.Content>

          <Dialog.Footer>
            <Flex gap="3" justifyContent="flex-end">
              <Dialog.CancelButton onClick={handleCancelClick}>
                Cancel
              </Dialog.CancelButton>
              <Button appearance="primary" onClick={handleApplyClick}>
                Apply
              </Button>
            </Flex>
          </Dialog.Footer>
        </Dialog>
      </div>
    </div>
  );
}

export default App;
```

Select Trigger supports both single and multiple selection modes. In single selection mode, the component displays the selected item as text. In multiple selection mode, selected items appear as Chips that users can remove individually.

### Opening of custom overlays

Clicking the Select Trigger's input area opens up a custom overlay element. This is typically a [Dialog](https://anvil.servicetitan.com/docs/components/dialog/design). This is not explicitly part of the Select Trigger, an implementor will need to customize what content is in the overlay component.

### Field properties

Field properties follow the same options as the [Combobox](https://anvil.servicetitan.com/docs/components/combobox/design). This includes, but not limited to, field sizes, prefixes, suffixes, Chip color customization, and max rows.

## Behavior

Select Trigger responds to user interaction with distinct visual feedback while maintaining consistent interaction patterns.

### Selection display

Selected items appear as Chips in multiple selection mode. Each Chip includes a close button that removes that specific item. The component displays a placeholder when no items are selected.

### Clear selection

A clear button appears when items are selected, allowing users to remove all selections at once. The clear button is hidden when the component is disabled or read-only, or when `disableClearSelection` is set to true.

## Usage Guidelines

Use Select Trigger when you need to display selections in a field-like component without using a Popover, especially for complex selection scenarios that require Dialog interfaces.

| Standard | Requirement |
| - | - |
| DoComplex Dialog-based selection | Complex selection interfaces that require Dialog components |
| DoMulti-step selection scenarios | Selection scenarios with secondary actions, tabs, or multiple steps |
| DoMore space than a Popover | Selection interfaces that need more space than a Popover provides |
| DoIntegration with ListView or CheckboxGroup | Integration with components like ListView, CheckboxGroup, or custom selection UIs |
| Don'tSimple selection needs | Use [Combobox](https://anvil.servicetitan.com/docs/components/combobox/web.md) for basic single or multiple selection with a Popover |
| Don'tDropdown or Popover selection | Use [Combobox](https://anvil.servicetitan.com/docs/components/combobox/web.md) instead. |
| Don'tNo selection interface needed | Use TextField if you don't need a selection interface |

### Alternatives

#### Select Trigger vs Combobox

Combobox provides a complete selection interface with a Popover, including search and filtering capabilities. Use Combobox for standard selection scenarios. Use Select Trigger when you need a Dialog for complex selection interfaces with tabs, secondary actions, or custom layouts.

#### Select Trigger vs Select Field

[Select Field](https://anvil.servicetitan.com/docs/components/select-field/web) displays options in a built-in Popover or Dialog with integrated search and filtering. Select Trigger opens a custom overlay — typically a Dialog — that the implementor fully controls.

### How to Use

#### Basic multiple selection

Use Select Trigger with a Dialog containing a ListView or CheckboxGroup for selection. Maintain separate state for the Select Trigger's selected items and the Dialog's temporary selection state.

selecttrigger-dialog-apply

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/selecttrigger-dialog-apply?file=src/App.tsx)

```tsx lines theme={null}
import {
  SelectTrigger,
  Flex,
  Dialog,
  Grid,
  SearchField,
  ListView,
  Text,
  Button,
} from "@servicetitan/anvil2";
import { useState } from "react";

type Item = {
  id: number;
  name: string;
};

const items = [
  { id: 1, name: "First item" },
  { id: 2, name: "Second item" },
  { id: 3, name: "Third item" },
  { id: 4, name: "Fourth item" },
  { id: 5, name: "Fifth item" },
];

function App() {
  const [selectedItems, setSelectedItems] = useState<Item[] | null>([
    items[2],
    items[3],
  ]);
  const [dialogSelectedItems, setDialogSelectedItems] = useState<Item[]>([
    items[2],
    items[3],
  ]);
  const [openDialog, setOpenDialog] = useState(true);

  const handleDialogClose = () => setOpenDialog(false);

  const handleCancelClick = () => setDialogSelectedItems(selectedItems ?? []);

  const handleApplyClick = () => {
    setOpenDialog(false);
    setSelectedItems(dialogSelectedItems);
  };

  const handleSelectionChange = (selectedLabels: string[]) => {
    const selectedItems = items.filter((item) =>
      selectedLabels.includes(item.name),
    );
    setDialogSelectedItems(selectedItems);
  };

  const selectedLabels = dialogSelectedItems.map((item) => item.name);

  return (
    <div style={{ minHeight: "443px", minWidth: "455px" }}>
      <div style={{ padding: "1.5rem" }}>
        <Flex direction="column" gap="6">
          <SelectTrigger
            multiple
            selectedItems={selectedItems ?? []}
            itemToString={(item) => (item ? item.name : "")}
            onChange={(items: Item[] | null) => setSelectedItems(items)}
            onClick={() => {
              setDialogSelectedItems(selectedItems ?? []);
              setOpenDialog(true);
            }}
            label="Label"
            placeholder="Placeholder"
            hint="Hint text"
            moreInfo="More info"
            size="large"
            required
          />
        </Flex>

        <Dialog open={openDialog} onClose={handleDialogClose}>
          <Dialog.Header>Dialog Header</Dialog.Header>

          <Dialog.Content>
            <Grid flexGrow="1" gap="4">
              <SearchField style={{ gap: 0 }} />
              <ListView
                onSelectionChange={handleSelectionChange}
                selected={selectedLabels}
              >
                {items.map((item) => (
                  <ListView.Option key={item.id} label={item.name}>
                    <Text>{item.name}</Text>
                  </ListView.Option>
                ))}
              </ListView>
            </Grid>
          </Dialog.Content>

          <Dialog.Footer>
            <Flex gap="3" justifyContent="flex-end">
              <Dialog.CancelButton onClick={handleCancelClick}>
                Cancel
              </Dialog.CancelButton>
              <Button appearance="primary" onClick={handleApplyClick}>
                Apply
              </Button>
            </Flex>
          </Dialog.Footer>
        </Dialog>
      </div>
    </div>
  );
}

export default App;
```

Use Select Trigger with Dialog for complex selection scenarios that require more space and flexibility than a Popover.

#### Selection with secondary actions

Use Select Trigger with Dialog when selection items need secondary actions like editing or additional information.

selecttrigger-dialog-secondary

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/selecttrigger-dialog-secondary?file=src/App.tsx)

```tsx lines theme={null}
import {
  SelectTrigger,
  Flex,
  Dialog,
  Grid,
  SearchField,
  ListView,
  Text,
  Tooltip,
  Button,
} from "@servicetitan/anvil2";
import Edit from "@servicetitan/anvil2/assets/icons/material/round/edit.svg";
import { useState } from "react";

type Item = {
  id: number;
  name: string;
};

const items = [
  { id: 1, name: "First item" },
  { id: 2, name: "Second item" },
  { id: 3, name: "Third item" },
  { id: 4, name: "Fourth item" },
  { id: 5, name: "Fifth item" },
];

function App() {
  const [selectedItems, setSelectedItems] = useState<Item[] | null>([
    items[2],
    items[3],
  ]);
  const [dialogSelectedItems, setDialogSelectedItems] = useState<Item[]>([
    items[2],
    items[3],
  ]);
  const [openDialog, setOpenDialog] = useState(true);

  const handleDialogClose = () => setOpenDialog(false);

  const handleCancelClick = () => setDialogSelectedItems(selectedItems ?? []);

  const handleApplyClick = () => {
    setOpenDialog(false);
    setSelectedItems(dialogSelectedItems);
  };

  const handleSelectionChange = (selectedLabels: string[]) => {
    const selectedItems = items.filter((item) =>
      selectedLabels.includes(item.name),
    );
    setDialogSelectedItems(selectedItems);
  };

  const selectedLabels = dialogSelectedItems.map((item) => item.name);

  return (
    <div style={{ minHeight: "497px", minWidth: "455px" }}>
      <div style={{ padding: "1.5rem" }}>
        <Flex direction="column" gap="6">
          <SelectTrigger
            multiple
            selectedItems={selectedItems ?? []}
            itemToString={(item) => (item ? item.name : "")}
            onChange={(items: Item[] | null) => setSelectedItems(items)}
            onClick={() => {
              setDialogSelectedItems(selectedItems ?? []);
              setOpenDialog(true);
            }}
            label="Label"
            placeholder="Placeholder"
            hint="Hint text"
            moreInfo="More info"
            size="large"
            required
          />
        </Flex>

        <Dialog open={openDialog} onClose={handleDialogClose}>
          <Dialog.Header>Dialog Header</Dialog.Header>

          <Dialog.Content>
            <Grid flexGrow="1" gap="4">
              <SearchField style={{ gap: 0 }} />
              <ListView
                onSelectionChange={handleSelectionChange}
                selected={selectedLabels}
              >
                {items.map((item) => (
                  <ListView.Option key={item.id} label={item.name}>
                    <Text flexGrow={1}>{item.name}</Text>
                    <Tooltip placement="top-end">
                      <Tooltip.Content>Edit Item</Tooltip.Content>
                      <Tooltip.Trigger>
                        <Button size="small" icon={Edit} />
                      </Tooltip.Trigger>
                    </Tooltip>
                  </ListView.Option>
                ))}
              </ListView>
            </Grid>
          </Dialog.Content>

          <Dialog.Footer>
            <Flex gap="3" justifyContent="flex-end">
              <Dialog.CancelButton onClick={handleCancelClick}>
                Cancel
              </Dialog.CancelButton>
              <Button appearance="primary" onClick={handleApplyClick}>
                Apply
              </Button>
            </Flex>
          </Dialog.Footer>
        </Dialog>
      </div>
    </div>
  );
}

export default App;
```

Use Select Trigger with Dialog when selected items require secondary actions or additional context.

#### Tabbed selection interface

Use Select Trigger with Dialog containing Tabs to organize selection options, such as showing all items in one tab and selected items in another.

selecttrigger-dialog-tab

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/selecttrigger-dialog-tab?file=src/App.tsx)

```tsx lines theme={null}
import {
  SelectTrigger,
  Flex,
  Dialog,
  Grid,
  SearchField,
  Tab,
  ListView,
  Text,
  Button,
} from "@servicetitan/anvil2";
import { useState } from "react";

type Item = {
  id: number;
  name: string;
};

const items = [
  { id: 1, name: "First item" },
  { id: 2, name: "Second item" },
  { id: 3, name: "Third item" },
  { id: 4, name: "Fourth item" },
  { id: 5, name: "Fifth item" },
];

function App() {
  const [selectedItems, setSelectedItems] = useState<Item[] | null>([
    items[2],
    items[3],
  ]);
  const [dialogSelectedItems, setDialogSelectedItems] = useState<Item[]>([
    items[2],
    items[3],
  ]);
  const [openDialog, setOpenDialog] = useState(true);

  const handleDialogClose = () => setOpenDialog(false);

  const handleCancelClick = () => setDialogSelectedItems(selectedItems ?? []);

  const handleApplyClick = () => {
    setOpenDialog(false);
    setSelectedItems(dialogSelectedItems);
  };

  const handleSelectionChange = (selectedLabels: string[]) => {
    const selectedItems = items.filter((item) =>
      selectedLabels.includes(item.name),
    );
    setDialogSelectedItems(selectedItems);
  };

  const selectedLabels = dialogSelectedItems.map((item) => item.name);

  return (
    <div style={{ minHeight: "443px", minWidth: "455px" }}>
      <div style={{ padding: "1.5rem" }}>
        <Flex direction="column" gap="6">
          <SelectTrigger
            multiple
            selectedItems={selectedItems ?? []}
            itemToString={(item) => (item ? item.name : "")}
            onChange={(items: Item[] | null) => setSelectedItems(items)}
            onClick={() => {
              setDialogSelectedItems(selectedItems ?? []);
              setOpenDialog(true);
            }}
            label="Label"
            placeholder="Placeholder"
            hint="Hint text"
            moreInfo="More info"
            size="large"
            required
          />
        </Flex>

        <Dialog open={openDialog} onClose={handleDialogClose}>
          <Dialog.Header>Dialog Header</Dialog.Header>

          <Dialog.Content>
            <Grid flexGrow="1" gap="2">
              <SearchField style={{ gap: 0 }} />

              <Tab defaultIndex={0} fill>
                <Tab.List>
                  <Tab.Button id="t1" controls="p1">
                    All
                  </Tab.Button>
                  <Tab.Button id="t2" controls="p2">
                    Selected
                  </Tab.Button>
                </Tab.List>
                <Tab.Panel id="p1">
                  <Flex gap="4" direction="column">
                    <ListView
                      onSelectionChange={handleSelectionChange}
                      selected={selectedLabels}
                    >
                      {items.map((item) => (
                        <ListView.Option key={item.id} label={item.name}>
                          <Text>{item.name}</Text>
                        </ListView.Option>
                      ))}
                    </ListView>
                  </Flex>
                </Tab.Panel>
                <Tab.Panel id="p2">
                  <Flex gap="4" direction="column">
                    <ListView
                      onSelectionChange={handleSelectionChange}
                      selected={selectedLabels}
                    >
                      {dialogSelectedItems.map((item) => (
                        <ListView.Option key={item.id} label={item.name}>
                          <Text>{item.name}</Text>
                        </ListView.Option>
                      ))}
                    </ListView>
                  </Flex>
                </Tab.Panel>
              </Tab>
            </Grid>
          </Dialog.Content>

          <Dialog.Footer>
            <Flex gap="3" justifyContent="flex-end">
              <Dialog.CancelButton onClick={handleCancelClick}>
                Cancel
              </Dialog.CancelButton>
              <Button appearance="primary" onClick={handleApplyClick}>
                Apply
              </Button>
            </Flex>
          </Dialog.Footer>
        </Dialog>
      </div>
    </div>
  );
}

export default App;
```

Use Select Trigger with Dialog containing Tabs to organize complex selection scenarios with multiple views.

## Content

Content within Select Trigger should clearly communicate the selection state and provide appropriate labels and hints.

### Labels and hints

* Use clear, descriptive labels that explain what users are selecting
* Provide hint text when the selection behavior or options need clarification
* Use required indicators when selection is mandatory
* Include moreInfo content when additional context is helpful

### Placeholder text

* Use placeholder text to guide users on what to select
* Keep placeholder text concise and actionable
* Avoid using placeholder text as the primary instruction

## Keyboard Interaction

Users can navigate Select Trigger using standard keyboard controls.

| Key | Description |
| - | - |
| Tab | Moves focus to the Select Trigger input area |
| Enter | Opens the selection interface (Typically a Dialog) |
| Space | Opens the selection interface (Typically a Dialog) |

Select Trigger provides keyboard support for opening the selection interface. Once the Dialog opens, keyboard interaction follows the patterns of that component. Individual Chips in multiple selection mode can be removed using their close buttons, which are accessible via keyboard navigation.

## Code

**Implementation**

selecttrigger-playground

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/selecttrigger-playground?file=src/App.tsx)

```tsx lines expandable theme={null}
import {
  SelectTrigger,
  Flex,
  Dialog,
  Checkbox,
  Button,
  type CheckboxProps,
} from "@servicetitan/anvil2";
import { useState } from "react";

type Item = {
  id: number;
  name: string;
};

const items = [
  { id: 1, name: "First item" },
  { id: 2, name: "Second item" },
  { id: 3, name: "Third item" },
  { id: 4, name: "Fourth item" },
  { id: 5, name: "Fifth item" },
];

function App() {
  const [selectedItems, setSelectedItems] = useState<Item[] | null>([
    items[2],
    items[3],
  ]);
  const [dialogSelectedItems, setDialogSelectedItems] = useState<Item[]>([
    items[2],
    items[3],
  ]);
  const [openDialog, setOpenDialog] = useState(false);

  const handleDialogClose = () => setOpenDialog(false);

  const handleCancelClick = () => setDialogSelectedItems(selectedItems ?? []);

  const handleApplyClick = () => {
    setOpenDialog(false);
    setSelectedItems(dialogSelectedItems);
  };

  const handleCheckboxClick: CheckboxProps["onClick"] = (e, state) => {
    if (e && state) {
      const { checked } = state;

      if (checked) {
        setDialogSelectedItems((prevItems) =>
          prevItems.filter(
            (item) => item.name !== (e.target as HTMLInputElement).value,
          ),
        );
      } else {
        const selectedItem = items.find(
          (item) => item.name === (e.target as HTMLInputElement).value,
        );

        if (selectedItem) {
          setDialogSelectedItems((prevItems) => [...prevItems, selectedItem]);
        }
      }
    }
  };

  return (
    <div style={{ minHeight: "284px" }}>
      <div style={{ padding: "1.5rem" }}>
        <Flex direction="column" gap="6">
          <SelectTrigger
            multiple
            selectedItems={selectedItems ?? []}
            itemToString={(item) => (item ? item.name : "")}
            onChange={(items: Item[] | null) => setSelectedItems(items)}
            onClick={() => {
              setDialogSelectedItems(selectedItems ?? []);
              setOpenDialog(true);
            }}
            label="Label"
            placeholder="Placeholder"
            hint="Hint text"
            moreInfo="More info"
            size="large"
            required
          />
        </Flex>

        <Dialog open={openDialog} onClose={handleDialogClose}>
          <Dialog.Header>Dialog Header</Dialog.Header>

          <Dialog.Content>
            <Checkbox.Group legend="Choose item(s)" required>
              {items.map((item) => (
                <Checkbox
                  key={item.name}
                  label={item.name}
                  value={item.name}
                  checked={dialogSelectedItems.includes(item)}
                  onClick={handleCheckboxClick}
                />
              ))}
            </Checkbox.Group>
          </Dialog.Content>

          <Dialog.Footer>
            <Flex gap="3" justifyContent="flex-end">
              <Dialog.CancelButton onClick={handleCancelClick}>
                Cancel
              </Dialog.CancelButton>
              <Button appearance="primary" onClick={handleApplyClick}>
                Apply
              </Button>
            </Flex>
          </Dialog.Footer>
        </Dialog>
      </div>
    </div>
  );
}

export default App;
```

## Common Examples

```tsx theme={null}
import { SelectTrigger } from "@servicetitan/anvil2";

function ExampleComponent() {
  return (
    <SelectTrigger
      multiple
      selectedItems={selectedItems}
      itemToString={(item: Item | null) => item?.label ?? ""}
      onClick={() => setOpenDialog(true)}
      onChange={(items: Item[] | null) => setSelectedItems(items ?? [])}
    />
  );
}
```

### Complex selections

The `SelectTrigger` can be used in complex selection cases where the `Combobox` would be insufficient. Instead of using the `Combobox`, the `SelectTrigger` can be paired with `Dialog` to handle use cases like secondary actions and tabbed options. (See our [usage guidelines](https://anvil.servicetitan.com/docs/components/combobox/design#complex-selection-alternatives) for `Combobox`.)

Note that the `SelectTrigger` can modify the selected items on it's own with the "X" button on both individual `Chip`s and on the input itself, so the state setter will need to be used in both the `SelectTrigger`'s `onChange` and in the form controls inside the `Dialog`.

selecttrigger-playground

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/selecttrigger-playground?file=src/App.tsx)

```tsx lines expandable theme={null}
import {
  SelectTrigger,
  Flex,
  Dialog,
  Checkbox,
  Button,
  type CheckboxProps,
} from "@servicetitan/anvil2";
import { useState } from "react";

type Item = {
  id: number;
  name: string;
};

const items = [
  { id: 1, name: "First item" },
  { id: 2, name: "Second item" },
  { id: 3, name: "Third item" },
  { id: 4, name: "Fourth item" },
  { id: 5, name: "Fifth item" },
];

function App() {
  const [selectedItems, setSelectedItems] = useState<Item[] | null>([
    items[2],
    items[3],
  ]);
  const [dialogSelectedItems, setDialogSelectedItems] = useState<Item[]>([
    items[2],
    items[3],
  ]);
  const [openDialog, setOpenDialog] = useState(false);

  const handleDialogClose = () => setOpenDialog(false);

  const handleCancelClick = () => setDialogSelectedItems(selectedItems ?? []);

  const handleApplyClick = () => {
    setOpenDialog(false);
    setSelectedItems(dialogSelectedItems);
  };

  const handleCheckboxClick: CheckboxProps["onClick"] = (e, state) => {
    if (e && state) {
      const { checked } = state;

      if (checked) {
        setDialogSelectedItems((prevItems) =>
          prevItems.filter(
            (item) => item.name !== (e.target as HTMLInputElement).value,
          ),
        );
      } else {
        const selectedItem = items.find(
          (item) => item.name === (e.target as HTMLInputElement).value,
        );

        if (selectedItem) {
          setDialogSelectedItems((prevItems) => [...prevItems, selectedItem]);
        }
      }
    }
  };

  return (
    <div style={{ minHeight: "284px" }}>
      <div style={{ padding: "1.5rem" }}>
        <Flex direction="column" gap="6">
          <SelectTrigger
            multiple
            selectedItems={selectedItems ?? []}
            itemToString={(item) => (item ? item.name : "")}
            onChange={(items: Item[] | null) => setSelectedItems(items)}
            onClick={() => {
              setDialogSelectedItems(selectedItems ?? []);
              setOpenDialog(true);
            }}
            label="Label"
            placeholder="Placeholder"
            hint="Hint text"
            moreInfo="More info"
            size="large"
            required
          />
        </Flex>

        <Dialog open={openDialog} onClose={handleDialogClose}>
          <Dialog.Header>Dialog Header</Dialog.Header>

          <Dialog.Content>
            <Checkbox.Group legend="Choose item(s)" required>
              {items.map((item) => (
                <Checkbox
                  key={item.name}
                  label={item.name}
                  value={item.name}
                  checked={dialogSelectedItems.includes(item)}
                  onClick={handleCheckboxClick}
                />
              ))}
            </Checkbox.Group>
          </Dialog.Content>

          <Dialog.Footer>
            <Flex gap="3" justifyContent="flex-end">
              <Dialog.CancelButton onClick={handleCancelClick}>
                Cancel
              </Dialog.CancelButton>
              <Button appearance="primary" onClick={handleApplyClick}>
                Apply
              </Button>
            </Flex>
          </Dialog.Footer>
        </Dialog>
      </div>
    </div>
  );
}

export default App;
```

### Selected item customization

`SelectTrigger`s with `multiple` select can customize the selected state of an item using the `selectedItemProps` prop. This prop accepts a function that will pass the selected item as an parameter to allow for things like customizing the display color:

selecttrigger-selecteditemprops

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/selecttrigger-selecteditemprops?file=src/App.tsx)

```tsx lines expandable theme={null}
import {
  SelectTrigger,
  Flex,
  Dialog,
  Grid,
  Checkbox,
  Button,
  type CheckboxProps,
} from "@servicetitan/anvil2";
import { useState } from "react";

type Item = {
  id: number;
  name: string;
  color: string;
};

const items = [
  { id: 1, name: "Red", color: "red" },
  { id: 2, name: "Purple", color: "purple" },
  { id: 3, name: "Black", color: "black" },
  { id: 4, name: "Gray", color: "gray" },
  { id: 5, name: "Gold", color: "gold" },
  { id: 6, name: "Cyan", color: "cyan" },
  { id: 7, name: "Pink", color: "pink" },
  { id: 8, name: "Blue", color: "blue" },
  { id: 9, name: "Green", color: "green" },
];

function App() {
  const [selectedItems, setSelectedItems] = useState<Item[] | null>([
    items[4],
    items[5],
    items[6],
  ]);
  const [dialogSelectedItems, setDialogSelectedItems] = useState<Item[]>([
    items[4],
    items[5],
    items[6],
  ]);
  const [openDialog, setOpenDialog] = useState(false);

  const handleDialogClose = () => setOpenDialog(false);

  const handleCancelClick = () => setDialogSelectedItems(selectedItems ?? []);

  const handleApplyClick = () => {
    setOpenDialog(false);
    setSelectedItems(dialogSelectedItems);
  };

  const handleCheckboxClick: CheckboxProps["onClick"] = (e, state) => {
    if (e && state) {
      const { checked } = state;

      if (checked) {
        setDialogSelectedItems((prevItems) =>
          prevItems.filter(
            (item) => item.name !== (e.target as HTMLInputElement).value,
          ),
        );
      } else {
        const selectedItem = items.find(
          (item) => item.name === (e.target as HTMLInputElement).value,
        );

        if (selectedItem) {
          setDialogSelectedItems((prevItems) => [...prevItems, selectedItem]);
        }
      }
    }
  };

  return (
    <div style={{ minHeight: "284px" }}>
      <div style={{ padding: "1.5rem" }}>
        <Flex direction="column" gap="6">
          <SelectTrigger
            multiple
            selectedItems={selectedItems ?? []}
            itemToString={(item) => (item ? item.name : "")}
            selectedItemProps={(item) => ({
              color: item.color,
            })}
            onChange={(items: Item[] | null) => setSelectedItems(items)}
            onClick={() => {
              setDialogSelectedItems(selectedItems ?? []);
              setOpenDialog(true);
            }}
            label="Label"
            placeholder="Placeholder"
            hint="Hint text"
            moreInfo="More info"
            size="large"
            required
          />
        </Flex>

        <Dialog open={openDialog} onClose={handleDialogClose}>
          <Dialog.Header>Dialog Header</Dialog.Header>

          <Dialog.Content>
            <Checkbox.Group legend="Choose item(s)" required flexGrow="1">
              <Grid templateColumns="repeat(2, 1fr)" gap="1">
                {items.map((item) => (
                  <Checkbox
                    key={item.name}
                    label={item.name}
                    value={item.name}
                    checked={dialogSelectedItems.includes(item)}
                    onClick={handleCheckboxClick}
                  />
                ))}
              </Grid>
            </Checkbox.Group>
          </Dialog.Content>

          <Dialog.Footer>
            <Flex gap="3" justifyContent="flex-end">
              <Dialog.CancelButton onClick={handleCancelClick}>
                Cancel
              </Dialog.CancelButton>
              <Button appearance="primary" onClick={handleApplyClick}>
                Apply
              </Button>
            </Flex>
          </Dialog.Footer>
        </Dialog>
      </div>
    </div>
  );
}

export default App;
```

**SelectTrigger Props**

```tsx theme={null}
import { SelectTrigger } from "@servicetitan/anvil2";

function ExampleComponent() {
return (

<SelectTrigger
  itemToString={(item) => item?.label ?? ""}
  selectedItem={selectedItem}
  onClick={() => {}}
  onChange={(item) => {}}
/>
); }

```

## `SelectTrigger` Props

The `SelectTrigger` component accepts most props that can be passed to `TextField`, such as `label`, `required`, `moreInfo`, `size`, `prefix`, `suffix`, `description`, `hint`, and `error`.

**disableClearSelection** `boolean` (default: false)

Remove the clear button to disable user clearing the input after selecting.

**itemToKey** `(item: Item) =\> string | number | null`

Function to get a unique key from an item.

**itemToString** `(item: Item) =\> string`

Function to convert an item to a display string.

**multiple** `boolean` (default: false)

**onChange** `(Items[]) =\> unknown`

The `onChange` will fire when the input is cleared or if any individual item
is removed

**onClick** `(MouseEvent) =\> void`

**selectedItemProps** `(Item) =\> ChipProps`

Only if `multiple` is `true`

**selectedItem** `Item`

Only if `multiple` is `false`

**selectedItems** `Item[]`

Only if `multiple` is `true`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.