# Select

> A select element to choose from a list of options.

## Usage

Use the `v-model` directive to control the value of the Select or the `default-value` prop to set the initial value when you do not need to control its state.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Backlog',
  'Todo',
  'In Progress',
  'Done',
])
</script>

<template>
  <PSelect model-value="Backlog" :items="items" />
</template>
```

### Items

Use the `items` prop as an array of strings, numbers or booleans:

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Backlog',
  'Todo',
  'In Progress',
  'Done',
])
</script>

<template>
  <PSelect model-value="Backlog" class="w-48" :items="items" />
</template>
```

You can also pass an array of objects with the following properties:

- `label?: string`
- [`value?: string`](#value-key)
- [`type?: "label" | "separator" | "item"`](#with-items-type)
- [`icon?: string`](#with-icons-in-items)
- [`avatar?: AvatarProps`](#with-avatar-in-items)
- [`chip?: ChipProps`](#with-chip-in-items)
- `disabled?: boolean`
- `class?: any`
- `pohon?: { label?: ClassNameValue, separator?: ClassNameValue, item?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLeadingChipSize?: ClassNameValue, itemLeadingChip?: ClassNameValue, itemLabel?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingIcon?: ClassNameValue }`

```vue
<script setup lang="ts">
import type { SelectItem } from 'pohon-ui'

const items = ref<SelectItem[]>([
  {
    label: 'Backlog',
    value: 'backlog',
  },
  {
    label: 'Todo',
    value: 'todo',
  },
  {
    label: 'In Progress',
    value: 'in_progress',
  },
  {
    label: 'Done',
    value: 'done',
  },
])
</script>

<template>
  <PSelect model-value="backlog" class="w-48" :items="items" />
</template>
```

<caution>

When using objects, you need to reference the `value` property of the object in the `v-model` directive or the `default-value` prop.

</caution>

You can also pass an array of arrays to the `items` prop to display separated groups of items.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  [
    'Apple',
    'Banana',
    'Blueberry',
    'Grapes',
    'Pineapple',
  ],
  [
    'Aubergine',
    'Broccoli',
    'Carrot',
    'Courgette',
    'Leek',
  ],
])
</script>

<template>
  <PSelect model-value="Apple" class="w-48" :items="items" />
</template>
```

### Value Key

You can change the property that is used to set the value by using the `value-key` prop. Defaults to `value`.

```vue
<script setup lang="ts">
import type { SelectItem } from 'pohon-ui'

const items = ref<SelectItem[]>([
  {
    label: 'Backlog',
    id: 'backlog',
  },
  {
    label: 'Todo',
    id: 'todo',
  },
  {
    label: 'In Progress',
    id: 'in_progress',
  },
  {
    label: 'Done',
    id: 'done',
  },
])
</script>

<template>
  <PSelect model-value="backlog" value-key="id" class="w-48" :items="items" />
</template>
```

### Multiple

Use the `multiple` prop to allow multiple selections, the selected items will be separated by a comma in the trigger.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Backlog',
  'Todo',
  'In Progress',
  'Done',
])
</script>

<template>
  <PSelect multiple class="w-48" :items="items" />
</template>
```

<caution>

Ensure to pass an array to the `default-value` prop or the `v-model` directive.

</caution>

### Placeholder

Use the `placeholder` prop to set a placeholder text.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Backlog',
  'Todo',
  'In Progress',
  'Done',
])
</script>

<template>
  <PSelect placeholder="Select status" class="w-48" :items="items" />
</template>
```

### Content

Use the `content` prop to control how the Select content is rendered, like its `align` or `side` for example.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Backlog',
  'Todo',
  'In Progress',
  'Done',
])
</script>

<template>
  <PSelect model-value="Backlog" class="w-48" :items="items" />
</template>
```

### Arrow

Use the `arrow` prop to display an arrow on the Select.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Backlog',
  'Todo',
  'In Progress',
  'Done',
])
</script>

<template>
  <PSelect model-value="Backlog" arrow class="w-48" :items="items" />
</template>
```

### Color

Use the `color` prop to change the ring color when the Select is focused.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Backlog',
  'Todo',
  'In Progress',
  'Done',
])
</script>

<template>
  <PSelect model-value="Backlog" color="neutral" highlight class="w-48" :items="items" />
</template>
```

<note>

The `highlight` prop is used here to show the focus state. It's used internally when a validation error occurs.

</note>

### Variant

Use the `variant` prop to change the variant of the Select.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Backlog',
  'Todo',
  'In Progress',
  'Done',
])
</script>

<template>
  <PSelect model-value="Backlog" color="neutral" variant="subtle" :highlight="false" class="w-48" :items="items" />
</template>
```

### Size

Use the `size` prop to change the size of the Select.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Backlog',
  'Todo',
  'In Progress',
  'Done',
])
</script>

<template>
  <PSelect model-value="Backlog" size="xl" class="w-48" :items="items" />
</template>
```

### Icon

Use the `icon` prop to show an [Icon](/docs/pohon/components/icon) inside the Select.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Backlog',
  'Todo',
  'In Progress',
  'Done',
])
</script>

<template>
  <PSelect model-value="Backlog" icon="i-lucide:search" size="md" class="w-48" :items="items" />
</template>
```

### Trailing Icon

Use the `trailing-icon` prop to customize the trailing [Icon](/docs/pohon/components/icon). Defaults to `i-lucide:chevron-down`.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Backlog',
  'Todo',
  'In Progress',
  'Done',
])
</script>

<template>
  <PSelect model-value="Backlog" trailing-icon="i-lucide:arrow-down" size="md" class="w-48" :items="items" />
</template>
```

<docs-framework-only>
<template v-slot:nuxt="">
<tip to="/docs/pohon/getting-started/integrations/icons/nuxt#theme">

You can customize this icon globally in your `app.config.ts` under `pohon.icons.chevronDown` key.

</tip>
</template>

<template v-slot:vue="">
<tip to="/docs/pohon/getting-started/integrations/icons/vue#theme">

You can customize this icon globally in your `vite.config.ts` under `pohon.icons.chevronDown` key.

</tip>
</template>
</docs-framework-only>

### Selected Icon

Use the `selected-icon` prop to customize the icon when an item is selected. Defaults to `i-lucide:check`.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Backlog',
  'Todo',
  'In Progress',
  'Done',
])
</script>

<template>
  <PSelect model-value="Backlog" selected-icon="i-lucide:flame" size="md" class="w-48" :items="items" />
</template>
```

<docs-framework-only>
<template v-slot:nuxt="">
<tip to="/docs/pohon/getting-started/integrations/icons/nuxt#theme">

You can customize this icon globally in your `app.config.ts` under `pohon.icons.check` key.

</tip>
</template>

<template v-slot:vue="">
<tip to="/docs/pohon/getting-started/integrations/icons/vue#theme">

You can customize this icon globally in your `vite.config.ts` under `pohon.icons.check` key.

</tip>
</template>
</docs-framework-only>

### Avatar

Use the `avatar` prop to show an [Avatar](/docs/pohon/components/avatar) inside the Select.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Nuxt',
  'NuxtHub',
  'NuxtLabs',
  'Nuxt Modules',
  'Nuxt Community',
])
</script>

<template>
  <PSelect model-value="Nuxt" class="w-48" :items="items" />
</template>
```

### Loading

Use the `loading` prop to show a loading icon on the Select.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Backlog',
  'Todo',
  'In Progress',
  'Done',
])
</script>

<template>
  <PSelect model-value="Backlog" loading :trailing="false" class="w-48" :items="items" />
</template>
```

### Loading Icon

Use the `loading-icon` prop to customize the loading icon. Defaults to `i-lucide:loader-circle`.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Backlog',
  'Todo',
  'In Progress',
  'Done',
])
</script>

<template>
  <PSelect model-value="Backlog" loading loading-icon="i-lucide:loader" class="w-48" :items="items" />
</template>
```

<docs-framework-only>
<template v-slot:nuxt="">
<tip to="/docs/pohon/getting-started/integrations/icons/nuxt#theme">

You can customize this icon globally in your `app.config.ts` under `pohon.icons.loading` key.

</tip>
</template>

<template v-slot:vue="">
<tip to="/docs/pohon/getting-started/integrations/icons/vue#theme">

You can customize this icon globally in your `vite.config.ts` under `pohon.icons.loading` key.

</tip>
</template>
</docs-framework-only>

### Disabled

Use the `disabled` prop to disable the Select.

```vue
<script setup lang="ts">
const items = ref<undefined>([
  'Backlog',
  'Todo',
  'In Progress',
  'Done',
])
</script>

<template>
  <PSelect disabled placeholder="Select status" class="w-48" :items="items" />
</template>
```

## Examples

### With items type

You can use the `type` property with `separator` to display a separator between items or `label` to display a label.

```vue
<script setup lang="ts">
import type { SelectItem } from 'pohon-ui'

const items = ref<SelectItem[]>([
  {
    type: 'label',
    label: 'Fruits',
  },
  'Apple',
  'Banana',
  'Blueberry',
  'Grapes',
  'Pineapple',
  {
    type: 'separator',
  },
  {
    type: 'label',
    label: 'Vegetables',
  },
  'Aubergine',
  'Broccoli',
  'Carrot',
  'Courgette',
  'Leek',
])
</script>

<template>
  <PSelect model-value="Apple" class="w-48" :items="items" />
</template>
```

### With icon in items

You can use the `icon` property to display an [Icon](/docs/pohon/components/icon) inside the items.

```vue [SelectItemsIconExample.vue]
<script setup lang="ts">
import type { PSelectItem } from 'pohon-ui';
import { computed, ref } from 'vue';

const items = ref([
  {
    label: 'Backlog',
    value: 'backlog',
    icon: 'i-lucide:circle-help',
  },
  {
    label: 'Todo',
    value: 'todo',
    icon: 'i-lucide:circle-plus',
  },
  {
    label: 'In Progress',
    value: 'in_progress',
    icon: 'i-lucide:circle-arrow-up',
  },
  {
    label: 'Done',
    value: 'done',
    icon: 'i-lucide:circle-check',
  },
] satisfies Array<PSelectItem>);

const value = ref(items.value[0]?.value);

const icon = computed(() => items.value.find((item) => item.value === value.value)?.icon);
</script>

<template>
  <PSelect
    v-model="value"
    :items="items"
    value-key="value"
    :icon="icon"
    class="w-48"
  />
</template>
```

<note>

In this example, the icon is computed from the `value` property of the selected item.

</note>

<tip>

You can also use the `#leading` slot to display the selected icon.

</tip>

### With avatar in items

You can use the `avatar` property to display an [Avatar](/docs/pohon/components/avatar) inside the items.

```vue [SelectItemsAvatarExample.vue]
<script setup lang="ts">
import type { PSelectItem } from 'pohon-ui';
import { computed, ref } from 'vue';

const items = ref([
  {
    label: 'praburangki',
    value: 'praburangki',
    avatar: {
      src: 'https://github.com/praburangki.png',
      alt: 'praburangki',
    },
  },
  {
    label: 'wahyu-ivan',
    value: 'wahyu-ivan',
    avatar: {
      src: 'https://github.com/wahyu-ivan.png',
      alt: 'wahyu-ivan',
    },
  },
  {
    label: 'GunawanAhmad',
    value: 'GunawanAhmad',
    avatar: {
      src: 'https://github.com/GunawanAhmad.png',
      alt: 'GunawanAhmad',
    },
  },
  {
    label: 'sandros94',
    value: 'sandros94',
    avatar: {
      src: 'https://github.com/sandros94.png',
      alt: 'sandros94',
    },
  },
] satisfies Array<PSelectItem>);

const value = ref(items.value[0]?.value);

const avatar = computed(() => items.value.find((item) => item.value === value.value)?.avatar);
</script>

<template>
  <PSelect
    v-model="value"
    :items="items"
    value-key="value"
    :avatar="avatar"
    class="w-48"
  />
</template>
```

<note>

In this example, the avatar is computed from the `value` property of the selected item.

</note>

<tip>

You can also use the `#leading` slot to display the selected avatar.

</tip>

### With chip in items

You can use the `chip` property to display a [Chip](/docs/pohon/components/chip) inside the items.

```vue [SelectItemsChipExample.vue]
<script setup lang="ts">
import type { PChipProps, PSelectItem } from 'pohon-ui';
import { ref } from 'vue';

const items = ref([
  {
    label: 'bug',
    value: 'bug',
    chip: {
      color: 'error',
    },
  },
  {
    label: 'feature',
    value: 'feature',
    chip: {
      color: 'success',
    },
  },
  {
    label: 'enhancement',
    value: 'enhancement',
    chip: {
      color: 'info',
    },
  },
] satisfies Array<PSelectItem>);

const value = ref(items.value[0]?.value);

function getChip(value: string) {
  return items.value.find((item) => item.value === value)?.chip;
}
</script>

<template>
  <PSelect
    v-model="value"
    :items="items"
    value-key="value"
    class="w-48"
  >
    <template #leading="{ modelValue, pohon }">
      <PChip
        v-if="modelValue"
        v-bind="getChip(modelValue)"
        inset
        standalone
        :size="(pohon.itemLeadingChipSize() as PChipProps['size'])"
        :class="pohon.itemLeadingChip()"
      />
    </template>
  </PSelect>
</template>
```

<note>

In this example, the `#leading` slot is used to display the selected chip.

</note>

### Control open state

You can control the open state by using the `default-open` prop or the `v-model:open` directive.

```vue [SelectOpenExample.vue]
<script setup lang="ts">
import { defineShortcuts } from '#imports';
import { ref } from 'vue';

const open = ref(false);
const items = ref(['Backlog', 'Todo', 'In Progress', 'Done']);
const value = ref('Backlog');

defineShortcuts({
  o: () => {
    open.value = !open.value;
  },
});
</script>

<template>
  <PSelect
    v-model="value"
    v-model:open="open"
    :items="items"
    class="w-48"
  />
</template>
```

<note>

In this example, leveraging [`defineShortcuts`](/docs/pohon/composables/define-shortcuts), you can toggle the Select by pressing <kbd value="O">



</kbd>

.

</note>

### With rotating icon

Here is an example with a rotating icon that indicates the open state of the Select.

```vue [SelectIconExample.vue]
<script setup lang="ts">
import { ref } from 'vue';

const items = ref(['Backlog', 'Todo', 'In Progress', 'Done']);
const value = ref('Backlog');
</script>

<template>
  <PSelect
    v-model="value"
    :items="items"
    :pohon="{
      trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform-280',
    }"
    class="w-48"
  />
</template>
```

### With fetched items

You can fetch items from an API and use them in the Select.

```vue [SelectFetchExample.vue]
<script setup lang="ts">
import type { PAvatarProps } from 'pohon-ui';
import { useFetch } from '#app';

const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
  key: 'typicode-users',
  transform: (data: Array<{ id: number; name: string }>) => {
    return data?.map((user) => ({
      label: user.name,
      value: String(user.id),
      avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` },
    }));
  },
  lazy: true,
});

function getUserAvatar(value: string) {
  return users.value?.find((user) => user.value === value)?.avatar || {};
}
</script>

<template>
  <PSelect
    :items="users"
    :loading="status === 'pending'"
    icon="i-lucide:user"
    placeholder="Select user"
    value-key="value"
    class="w-48"
  >
    <template #leading="{ modelValue, pohon }">
      <PAvatar
        v-if="modelValue"
        v-bind="getUserAvatar(modelValue)"
        :size="(pohon.leadingAvatarSize() as PAvatarProps['size'])"
        :class="pohon.leadingAvatar()"
      />
    </template>
  </PSelect>
</template>
```

### With infinite scroll

You can use the [`useInfiniteScroll`](https://vueuse.org/core/useInfiniteScroll/) composable to load more data as the user scrolls.

```vue [SelectInfiniteScrollExample.vue]
<script setup lang="ts">
import { useInfiniteScroll } from '@vueuse/core';

type User = {
  firstName: string;
};

type UserResponse = {
  users: Array<User>;
  total: number;
  skip: number;
  limit: number;
};

const skip = ref(0);

const { data, status, execute } = await useFetch('https://dummyjson.com/users?limit=10&select=firstName', {
  key: 'select-menu-users-infinite-scroll',
  params: { skip },
  transform: (data?: UserResponse) => {
    return data?.users.map((user) => user.firstName);
  },
  lazy: true,
  immediate: false,
});

const users = ref<Array<string>>([]);

watch(data, () => {
  users.value = [
    ...users.value,
    ...(data.value || []),
  ];
});

execute();

const select = useTemplateRef('select');

onMounted(() => {
  useInfiniteScroll(() => select.value?.viewportRef, () => {
    skip.value += 10;
  }, {
    canLoadMore: () => {
      return status.value !== 'pending';
    },
  });
});
</script>

<template>
  <PSelect
    ref="select"
    placeholder="Select user"
    :items="users"
  />
</template>
```

### With full content width

You can expand the content to the full width of its items by adding the `min-w-fit` class on the `pohon.content` slot.

```vue [SelectContentWidthExample.vue]
<script setup lang="ts">
import { useFetch } from '#app';
import { ref } from 'vue';

const value = ref<string>();

const { data: users } = await useFetch('https://jsonplaceholder.typicode.com/users', {
  key: 'typicode-users-email',
  transform: (data: Array<{ id: number; name: string; email: string }>) => {
    return data?.map((user) => ({
      label: user.name,
      email: user.email,
      value: String(user.id),
      avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` },
    }));
  },
  lazy: true,
});
</script>

<template>
  <PSelect
    v-model="value"
    :items="users"
    placeholder="Select user"
    value-key="value"
    :pohon="{ content: 'min-w-fit' }"
    class="w-48"
  >
    <template #item-label="{ item }">
      {{ item.label }}

      <span class="color-text-muted">
        {{ item.email }}
      </span>
    </template>
  </PSelect>
</template>
```

<tip>

You can also change the content width globally in your `app.config.ts`:

```text
export default defineAppConfig({
  pohon: {
    select: {
      slots: {
        content: 'min-w-fit'
      }
    }
  }
})
```

</tip>

## API

### Props

```ts
/**
 * Props for the Select component
 */
interface SelectProps {
  id?: string;
  /**
   * The placeholder text when the select is empty.
   */
  placeholder?: string;
  color?: "primary" | "secondary" | "success" | "info" | "warning" | "error" | "neutral";
  variant?: "outline" | "soft" | "subtle" | "ghost" | "none";
  size?: "md" | "xs" | "sm" | "lg" | "xl";
  /**
   * The icon displayed to open the menu.
   */
  trailingIcon?: string | object;
  /**
   * The icon displayed when an item is selected.
   */
  selectedIcon?: string | object;
  /**
   * The content of the menu.
   */
  content?: Omit<ASelectContentProps, "asChild" | "as" | "forceMount"> & Partial<EmitsToProps<SelectContentImplEmits>>;
  /**
   * Display an arrow alongside the menu.
   */
  arrow?: boolean | Omit<ASelectArrowProps, "asChild" | "as">;
  /**
   * Render the menu in a portal.
   * @default "true"
   */
  portal?: string | boolean | HTMLElement;
  /**
   * When `items` is an array of objects, select the field to use as the value.
   * @default "\"value\" as never"
   */
  valueKey?: VK;
  /**
   * When `items` is an array of objects, select the field to use as the label.
   * @default "\"label\""
   */
  labelKey?: GetItemKeys<T>;
  /**
   * When `items` is an array of objects, select the field to use as the description.
   * @default "\"description\""
   */
  descriptionKey?: GetItemKeys<T>;
  items?: T;
  /**
   * The value of the Select when initially rendered. Use when you do not need to control the state of the Select.
   */
  defaultValue?: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod>;
  /**
   * The controlled value of the Select. Can be bind as `v-model`.
   */
  modelValue?: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod>;
  modelModifiers?: Mod;
  /**
   * Whether multiple options can be selected or not.
   */
  multiple?: M;
  /**
   * @default "0"
   */
  autofocusDelay?: number;
  pohon?: { root?: ClassValue; base?: ClassValue; leading?: ClassValue; leadingIcon?: ClassValue; leadingAvatar?: ClassValue; leadingAvatarSize?: ClassValue; trailing?: ClassValue; trailingIcon?: ClassValue; value?: ClassValue; placeholder?: ClassValue; arrow?: ClassValue; content?: ClassValue; viewport?: ClassValue; group?: ClassValue; empty?: ClassValue; label?: ClassValue; separator?: ClassValue; item?: ClassValue; itemLeadingIcon?: ClassValue; itemLeadingAvatar?: ClassValue; itemLeadingAvatarSize?: ClassValue; itemLeadingChip?: ClassValue; itemLeadingChipSize?: ClassValue; itemTrailing?: ClassValue; itemTrailingIcon?: ClassValue; itemWrapper?: ClassValue; itemLabel?: ClassValue; itemDescription?: ClassValue; };
  /**
   * Native html input `autocomplete` attribute.
   */
  autocomplete?: string;
  /**
   * The name of the field. Submitted with its owning form as part of a name/value pair.
   */
  name?: string;
  /**
   * Display an icon based on the `leading` and `trailing` props.
   */
  icon?: string | object;
  /**
   * Display an avatar on the left side.
   */
  avatar?: PAvatarProps;
  /**
   * Display an icon on the left side.
   */
  leadingIcon?: string | object;
  /**
   * The icon when the `loading` prop is `true`.
   */
  loadingIcon?: string | object;
  form?: string;
  formaction?: string;
  formenctype?: string;
  formmethod?: string;
  formnovalidate?: Booleanish;
  formtarget?: string;
  /**
   * Highlight the ring color like a focus state.
   */
  highlight?: boolean;
  autofocus?: boolean;
  /**
   * When `true`, prevents the user from interacting with Select
   */
  disabled?: boolean;
  /**
   * The controlled open state of the Select. Can be bind as `v-model:open`.
   */
  open?: boolean;
  /**
   * The open state of the select when it is initially rendered. Use when you do not need to control its open state.
   */
  defaultOpen?: boolean;
  /**
   * When `true`, indicates that the user must set the value before the owning form can be submitted.
   */
  required?: boolean;
  /**
   * When `true`, the icon will be displayed on the left side.
   */
  leading?: boolean;
  /**
   * When `true`, the icon will be displayed on the right side.
   */
  trailing?: boolean;
  /**
   * When `true`, the loading icon will be displayed.
   */
  loading?: boolean;
}
```

<callout icon="i-simple-icons:mdnwebdocs" target="_blank" to="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes">

This component also supports all native `<button>` HTML attributes.

</callout>

### Slots

```ts
/**
 * Slots for the Select component
 */
interface SelectSlots {
  leading(): any;
  default(): any;
  trailing(): any;
  item(): any;
  item-leading(): any;
  item-label(): any;
  item-description(): any;
  item-trailing(): any;
  content-top(): any;
  content-bottom(): any;
}
```

### Emits

```ts
/**
 * Emitted events for the Select component
 */
interface SelectEmits {
  update:modelValue: (payload: [value: _Number<_Optional<_Nullable<GetModelValue<T, VK, M, ExcludeItem>, Mod>, Mod>, Mod>]) => void;
  update:open: (payload: [value: boolean]) => void;
  change: (payload: [event: Event]) => void;
  blur: (payload: [event: FocusEvent]) => void;
  focus: (payload: [event: FocusEvent]) => void;
}
```

### Expose

When accessing the component via a template ref, you can use the following:

<table>
<thead>
  <tr>
    <th>
      Name
    </th>
    
    <th>
      Type
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        triggerRef
      </code>
    </td>
    
    <td>
      <code>
        Ref<HTMLButtonElement | null>
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        viewportRef
      </code>
    </td>
    
    <td>
      <code>
        Ref<HTMLDivElement | null>
      </code>
    </td>
  </tr>
</tbody>
</table>

## Theme

```ts [app.config.ts]
export default defineAppConfig({
  pohon: {
    select: {
      slots: {
        leading: 'absolute inset-y-0 start-0 flex items-center',
        leadingIcon: 'shrink-0 color-text-dimmed',
        leadingAvatar: 'shrink-0',
        trailing: 'absolute inset-y-0 end-0 flex items-center',
        trailingIcon: 'shrink-0 color-text-dimmed',
        base: 'group rounded-md inline-flex transition-colors-280 items-center relative focus:outline-none disabled:(opacity-75 cursor-not-allowed)',
        value: 'pointer-events-none truncate',
        placeholder: 'color-text-dimmed truncate',
        arrow: 'fill-fill',
        content: 'flex flex-col rounded-md bg-background max-h-60 w-$akar-select-trigger-width pointer-events-auto ring ring-ring shadow-lg origin-$akar-select-content-transform-origin overflow-hidden data-[state=closed]:(animate-out animate-duration-280 fade-out-0 data-[side=bottom]:slide-out-top-5%) data-[state=open]:(animate-in animate-duration-280 fade-in-0 data-[side=bottom]:slide-in-top-5%)',
        viewport: 'flex-1 relative overflow-y-auto scroll-py-1 divide-divide divide-y',
        group: 'p-1 isolate',
        empty: 'color-text-muted text-center',
        label: 'color-text-highlighted font-semibold',
        separator: 'my-1 bg-border h-px -mx-1',
        item: 'group outline-none flex w-full cursor-pointer select-none transition-colors-280 items-center relative data-[state=checked]:(color-primary before:bg-background-elevated) before:(rounded-md content-empty transition-colors-280 inset-px absolute -z-1) data-[disabled]:(opacity-75 cursor-not-allowed) data-[highlighted]:not-[[data-state=checked]]:(color-black before:bg-primary/30)',
        itemLeadingIcon: 'color-text-dimmed shrink-0 transition-colors-280 group-data-[highlighted]:group-not-[[data-disabled]]:color-text',
        itemLeadingAvatar: 'shrink-0',
        itemLeadingChip: 'shrink-0',
        itemTrailing: 'ms-auto inline-flex gap-1.5 items-center',
        itemTrailingIcon: 'shrink-0',
        itemLabel: 'truncate',
        itemWrapper: 'flex flex-1 flex-col min-w-0',
        itemDescription: 'color-text-muted truncate'
      },
      variants: {
        fieldGroup: {
          horizontal: {
            root: 'group has-focus-visible:z-1',
            base: 'group-not-[*:only-child]:group-first:rounded-e-none group-not-[*:only-child]:group-last:rounded-s-none group-not-last:group-not-first:rounded-none'
          },
          vertical: {
            root: 'group has-focus-visible:z-1',
            base: 'group-not-[*:only-child]:group-first:rounded-b-none group-not-[*:only-child]:group-last:rounded-t-none group-not-last:group-not-first:rounded-none'
          }
        },
        size: {
          xs: {
            base: 'px-2 py-1 text-xs gap-1',
            leading: 'ps-2',
            trailing: 'pe-2',
            leadingIcon: 'size-4',
            leadingAvatarSize: '3xs',
            trailingIcon: 'size-4',
            label: 'text-[10px]/3 p-1 gap-1',
            item: 'text-xs p-1 gap-1',
            itemLeadingIcon: 'size-4',
            itemLeadingAvatarSize: '3xs',
            itemLeadingChip: 'size-4',
            itemLeadingChipSize: 'sm',
            itemTrailingIcon: 'size-4',
            empty: 'text-xs p-1'
          },
          sm: {
            base: 'px-2.5 py-1.5 text-xs gap-1.5',
            leading: 'ps-2.5',
            trailing: 'pe-2.5',
            leadingIcon: 'size-4',
            leadingAvatarSize: '3xs',
            trailingIcon: 'size-4',
            label: 'text-[10px]/3 p-1.5 gap-1.5',
            item: 'text-xs p-1.5 gap-1.5',
            itemLeadingIcon: 'size-4',
            itemLeadingAvatarSize: '3xs',
            itemLeadingChip: 'size-4',
            itemLeadingChipSize: 'sm',
            itemTrailingIcon: 'size-4',
            empty: 'text-xs p-1.5'
          },
          md: {
            base: 'px-2.5 py-1.5 text-sm gap-1.5',
            leading: 'ps-2.5',
            trailing: 'pe-2.5',
            leadingIcon: 'size-5',
            leadingAvatarSize: '2xs',
            trailingIcon: 'size-5',
            label: 'text-xs p-1.5 gap-1.5',
            item: 'text-sm p-1.5 gap-1.5',
            itemLeadingIcon: 'size-5',
            itemLeadingAvatarSize: '2xs',
            itemLeadingChip: 'size-5',
            itemLeadingChipSize: 'md',
            itemTrailingIcon: 'size-5',
            empty: 'text-sm p-1.5'
          },
          lg: {
            base: 'px-3 py-2 text-sm gap-2',
            leading: 'ps-3',
            trailing: 'pe-3',
            leadingIcon: 'size-5',
            leadingAvatarSize: '2xs',
            trailingIcon: 'size-5',
            label: 'text-xs p-2 gap-2',
            item: 'text-sm p-2 gap-2',
            itemLeadingIcon: 'size-5',
            itemLeadingAvatarSize: '2xs',
            itemLeadingChip: 'size-5',
            itemLeadingChipSize: 'md',
            itemTrailingIcon: 'size-5',
            empty: 'text-sm p-2'
          },
          xl: {
            base: 'px-3 py-2 text-base gap-2',
            leading: 'ps-3',
            trailing: 'pe-3',
            leadingIcon: 'size-6',
            leadingAvatarSize: 'xs',
            trailingIcon: 'size-6',
            label: 'text-sm p-2 gap-2',
            item: 'text-base p-2 gap-2',
            itemLeadingIcon: 'size-6',
            itemLeadingAvatarSize: 'xs',
            itemLeadingChip: 'size-6',
            itemLeadingChipSize: 'lg',
            itemTrailingIcon: 'size-6',
            empty: 'text-base p-2'
          }
        },
        variant: {
          outline: 'color-text-highlighted bg-background ring ring-inset ring-ring-accented',
          soft: 'color-text-highlighted bg-background-elevated/50 hover:bg-background-elevated focus:bg-background-elevated disabled:bg-background-elevated/50',
          subtle: 'color-text-highlighted bg-background-elevated ring ring-inset ring-ring-accented',
          ghost: 'color-text-highlighted bg-transparent hover:bg-background-elevated focus:bg-background-elevated disabled:bg-transparent dark:disabled:bg-transparent',
          none: 'color-text-highlighted bg-transparent'
        },
        type: {
          file: 'file:me-1.5 file:font-medium file:color-text-muted file:outline-none'
        }
      },
      compoundVariants: [
        {
          color: 'primary',
          variant: [
            'outline',
            'subtle'
          ],
          class: 'focus:(ring-2 ring-inset) focus:ring-primary'
        },
        {
          color: 'secondary',
          variant: [
            'outline',
            'subtle'
          ],
          class: 'focus:(ring-2 ring-inset) focus:ring-secondary'
        },
        {
          color: 'success',
          variant: [
            'outline',
            'subtle'
          ],
          class: 'focus:(ring-2 ring-inset) focus:ring-success'
        },
        {
          color: 'info',
          variant: [
            'outline',
            'subtle'
          ],
          class: 'focus:(ring-2 ring-inset) focus:ring-info'
        },
        {
          color: 'warning',
          variant: [
            'outline',
            'subtle'
          ],
          class: 'focus:(ring-2 ring-inset) focus:ring-warning'
        },
        {
          color: 'error',
          variant: [
            'outline',
            'subtle'
          ],
          class: 'focus:(ring-2 ring-inset) focus:ring-error'
        },
        {
          color: 'primary',
          highlight: true,
          class: 'ring ring-inset akar:ring-primary'
        },
        {
          color: 'secondary',
          highlight: true,
          class: 'ring ring-inset akar:ring-secondary'
        },
        {
          color: 'success',
          highlight: true,
          class: 'ring ring-inset akar:ring-success'
        },
        {
          color: 'info',
          highlight: true,
          class: 'ring ring-inset akar:ring-info'
        },
        {
          color: 'warning',
          highlight: true,
          class: 'ring ring-inset akar:ring-warning'
        },
        {
          color: 'error',
          highlight: true,
          class: 'ring ring-inset akar:ring-error'
        },
        {
          color: 'neutral',
          variant: [
            'outline',
            'subtle'
          ],
          class: 'focus:(ring-2 ring-inset akar:ring-ring-inverted)'
        },
        {
          color: 'neutral',
          highlight: true,
          class: 'ring ring-inset akar:ring-ring-inverted'
        },
        {
          leading: true,
          size: 'xs',
          class: 'ps-7'
        },
        {
          leading: true,
          size: 'sm',
          class: 'ps-8'
        },
        {
          leading: true,
          size: 'md',
          class: 'ps-9'
        },
        {
          leading: true,
          size: 'lg',
          class: 'ps-10'
        },
        {
          leading: true,
          size: 'xl',
          class: 'ps-11'
        },
        {
          trailing: true,
          size: 'xs',
          class: 'pe-7'
        },
        {
          trailing: true,
          size: 'sm',
          class: 'pe-8'
        },
        {
          trailing: true,
          size: 'md',
          class: 'pe-9'
        },
        {
          trailing: true,
          size: 'lg',
          class: 'pe-10'
        },
        {
          trailing: true,
          size: 'xl',
          class: 'pe-11'
        },
        {
          loading: true,
          leading: true,
          class: {
            leadingIcon: 'animate-spin'
          }
        },
        {
          loading: true,
          leading: false,
          trailing: true,
          class: {
            trailingIcon: 'animate-spin'
          }
        }
      ]
    }
  }
})
```

## Akar

<docs-akar-to-pohon mode="pohon" to="/docs/akar/components/select">



</docs-akar-to-pohon>

## Changelog

<docs-component-changelog>



</docs-component-changelog>
