🟒 Select

  • Can be controlled or uncontrolled.
  • Offers 2 positioning modes.
  • Supports items, labels, groups of items.
  • Focus is fully managed.
  • Full keyboard navigation.
  • Supports custom placeholder.
  • Typeahead support.
  • Supports Right to Left direction.

Basic

use NSelect to create a range input.

<script setup lang="ts">
const selected = ref()
const items = ['Evan You', 'Anthony Fu', 'Daniel Roe', 'Pooya Parsa', 'SΓ©bastien Chopin', 'Alexander Lichter']
</script>

<template>
  <div class="max-w-50">
    <NSelect
      v-model="selected"
      :items
      placeholder="Select Contributor"
      label="Vue Community"
    />
  </div>
</template>

Multiple Group

multiple-group="{value}" - enable multiple group items.

<script setup lang="ts">
const selected = ref()
const items = [
  {
    label: 'A',
    items: ['Apple', 'Aeroplane', 'Ant'],
  },
  {
    label: 'B',
    items: ['Banana', 'Ball', 'Bat'],
  },
  {
    label: 'C',
    items: ['Cat', 'Car', 'Cup'],
  },
  {
    label: 'D',
    items: ['Dog', 'Duck', 'Doll'],
  },
]
</script>

<template>
  <div class="max-w-50">
    <NSelect
      v-model="selected"
      :items
      placeholder="Select Option"
      multiple-group
    />
  </div>
</template>

Objects

PropDescription
value-attributeThe attribute value to be displayed in the select.
item-attributeThe attribute value to be displayed in the item.
Output:
<script setup lang="ts">
const items = [
  {
    code: '81253',
    label: 'Ford',
  },
  {
    code: '29932',
    label: 'Chevrolet',
  },
  {
    code: '54509',
    label: 'Dodge',
  },
  {
    code: '94253',
    label: 'Toyota',
  },
]

const selected = ref()
</script>

<template>
  <div class="space-y-2">
    <div>
      Output:
      <span class="text-primary">
        {{ selected }}
      </span>
    </div>

    <div class="w-50">
      <NSelect
        v-model="selected"
        :items
        placeholder="Select a brand"
        item-attribute="label"
      />
    </div>
  </div>
</template>

Label

You can use the NFormGroup component to create a label for the select.

Read more about the NFormGroup component here.
Select a contributor from the Vue community

This field is required

<script setup lang="ts">
const selected = ref()
const items = ['Evan You', 'Anthony Fu', 'Daniel Roe', 'Pooya Parsa', 'SΓ©bastien Chopin', 'Alexander Lichter']
</script>

<template>
 <div flex items-end>
   <NFormGroup
     label="Contributor"
     description="Select a contributor from the Vue community"
     :status="selected ? undefined : 'error'"
     :message="selected ? '' : 'This field is required'"
     required
   >
     <NSelect
       v-model="selected"
       placeholder="Options"
       :items
     />
   </NFormGroup>
 </div>
</template>

Variant and Color

select="{variant}-{color}" is used to set the variant of the select. The default variant is soft-black.

select-item="{color}" is used to set the variant of the select item. The default variant is soft-black.

PropDescription
selectSet the select variant and color.
_selectTrigger.selectSet the select variant and color via _selectTrigger.
NSelect is wrapped around the NButton component. This means that all the props and slots of NButton are available through the _selectTrigger prop.
Dynamic colors:

Default color:
<script setup lang="ts">
const selected = ref()
const items = ['Evan You', 'Anthony Fu', 'Daniel Roe', 'Pooya Parsa', 'SΓ©bastien Chopin', 'Alexander Lichter']
</script>

<template>
  <div flex="~ col" gap-4>
    <span class="text-sm font-medium">Dynamic colors:</span>

    <div grid="~ sm:cols-2" gap-4>
      <NSelect
        v-model="selected"
        :items="items"
        select="outline-primary"
        select-item="primary"
        placeholder="This is the primary color (default)"
      />

      <NSelect
        v-model="selected"
        :items="items"
        select="soft-rose"
        select-item="rose"
        placeholder="This is the rose color"
      />
    </div>

    <hr border="base">

    <span class="text-sm font-medium">Default color:</span>

    <div>
      <NSelect
        v-model="selected"
        :items="items"
        select="outline-gray"
        placeholder="This is the gray color"
      />
    </div>
  </div>
</template>

Disabled

PropDescription
disabledDisable the select.
_selectItem.disabledDisable the specific item.
<script setup lang="ts">
const selected = ref()
const items = ['Evan You', 'Anthony Fu', 'Daniel Roe', 'Pooya Parsa', 'SΓ©bastien Chopin', 'Alexander Lichter']

const selectedObject = ref()
const objectItems = [
  { label: 'Evan You', value: 'evan-you', _selectItem: { disabled: true } }, // disabled
  { label: 'Anthony Fu', value: 'anthony-fu' },
  { label: 'Daniel Roe', value: 'daniel-roe' },
  { label: 'Pooya Parsa', value: 'pooya-parsa', _selectItem: { disabled: true } }, // disabled
  { label: 'SΓ©bastien Chopin', value: 'sebastien-chopin' },
  { label: 'Alexander Lichter', value: 'alexander-lichter' },
]
</script>

<template>
  <div class="flex space-x-2">
    <NSelect
      v-model="selected"
      :items
      disabled
      placeholder="Select Contributor"
      label="Vue Community"
    />

    <NSelect
      v-model="selectedObject"
      :items="objectItems"
      item-attribute="label"
      placeholder="Select Contributor"
      label="Vue Community"
    />
  </div>
</template>

Size

PropDescription
sizeSet the select size.
_selectItem.sizeSet the item size.
_selectTrigger.sizeSet the trigger size.

πŸš€ You can freely adjust the size of the select using any size imaginable. No limits exist, and you aan use breakpoints such as sm:sm, xs:lg to change size based on screen size or states such as hover:lg, focus:3xl to change size based on input state and more.

The height and width of the select scale depends on the select-size. If you want to change the height and width simultaneously, you can always customize it using utility classes.
<script setup lang="ts">
const selected = ref()
const items = ['Evan You', 'Anthony Fu', 'Daniel Roe', 'Pooya Parsa', 'SΓ©bastien Chopin', 'Alexander Lichter']
</script>

<template>
  <div class="flex items-start gap-4">
    <NSelect
      v-model="selected"
      :items
      placeholder="Select Contributor"
      label="Vue Community"
    />

    <NSelect
      v-model="selected"
      :items
      size="md"
      placeholder="Select Contributor"
      label="Vue Community"
    />
  </div>
</template>

Slots

You can use the following slots to customize the select.

NameDescriptionProps
triggerThe trigger slot.value
valueThe value slot.value
contentThe content slot.items
labelThe label slot.label
itemThe item slot.item
groupThe group slot.items

Props

import type { SelectContentProps, SelectGroupProps, SelectItemIndicatorProps, SelectItemProps, SelectItemTextProps, SelectLabelProps, SelectRootProps, SelectScrollDownButtonProps, SelectScrollUpButtonProps, SelectSeparatorProps, SelectTriggerProps, SelectValueProps } from 'radix-vue'
import type { HTMLAttributes } from 'vue'
import type { NButtonProps } from './button'

interface BaseExtensions {
  class?: HTMLAttributes['class']
  size?: HTMLAttributes['class']
}

type RootExtensions = Omit<SelectRootProps, 'modelValue' > & BaseExtensions
type TriggerExtensions = SelectTriggerProps & Omit<NButtonProps, 'una'> & BaseExtensions
type ValueExtensions = SelectValueProps & BaseExtensions
type ScrollDownButtonExtensions = SelectScrollDownButtonProps & BaseExtensions
type ScrollUpButtonExtensions = SelectScrollUpButtonProps & BaseExtensions
type ContentExtensions = SelectContentProps & BaseExtensions
type ItemExtensions = Omit<SelectItemProps, 'value'> & BaseExtensions
type ItemTextExtensions = SelectItemTextProps & BaseExtensions
type GroupExtensions = SelectGroupProps & BaseExtensions
type LabelExtensions = SelectLabelProps & BaseExtensions
type SeparatorExtensions = SelectSeparatorProps & BaseExtensions
type SelectExtensions = NSelectRootProps
  & BaseExtensions
  & Pick<NSelectItemProps, 'selectItem'>
  & Pick<NSelectTriggerProps, 'status' | 'select'>

export interface NSelectProps extends SelectExtensions {
  /**
   * The unique id of the select.
   */
  id?: string
  /**
   * Enable multiple group items.
   *
   * @default false
   */
  multipleGroup?: boolean
  /**
   * The attribute name to use to display in the select items.
   *
   */
  itemAttribute?: string | number
  /**
   * The attribute name to use to display in the selected value.
   */
  valueAttribute?: string | number
  /**
   * The placeholder to display when no value is selected.
   */
  placeholder?: string
  /**
   * The label to display above the select items.
   */
  label?: string
  /**
   * The items to display in the select.
   *
   * @default []
   */
  items: any[]

  // sub-components
  _selectScrollUpButton?: Partial<NSelectScrollUpButtonProps>
  _selectItemText?: Partial<NSelectItemTextProps>
  _selectScrollDownButton?: Partial<NSelectScrollDownButtonProps>
  _selectGroup?: Partial<NSelectGroupProps>
  _selectContent?: Partial<NSelectContentProps>
  _selectValue?: Partial<NSelectValueProps>
  _selectTrigger?: Partial<NSelectTriggerProps>
  _selectItem?: Partial<NSelectItemProps>
  _selectLabel?: Partial<NSelectLabelProps>
}

export interface NSelectRootProps extends RootExtensions {
  una?: {
    selectRoot?: HTMLAttributes['class']
  }
}

export interface NSelectTriggerProps extends TriggerExtensions {
  /**
   * Allows you to add `UnaUI` button preset properties,
   * Think of it as a shortcut for adding options or variants to the preset if available.
   *
   * @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/select.ts
   * @example
   * select="solid-green"
   */
  select?: string
  /**
   * The status of the select input.
   */
  status?: 'info' | 'success' | 'warning' | 'error'
  /**
   * `UnaUI` preset configuration
   *
   * @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/select.ts
   */
  una?: {
    selectTrigger?: HTMLAttributes['class']
    selectTriggerTrailing?: HTMLAttributes['class']
    selectTriggerTrailingIcon?: HTMLAttributes['class']
    selectTriggerLeading?: HTMLAttributes['class']

    selectTriggerInfoIcon?: HTMLAttributes['class']
    selectTriggerSuccessIcon?: HTMLAttributes['class']
    selectTriggerWarningIcon?: HTMLAttributes['class']
    selectTriggerErrorIcon?: HTMLAttributes['class']
  } & NButtonProps['una']
}

export interface NSelectValueProps extends ValueExtensions {
  una?: {
    selectValue?: HTMLAttributes['class']
  }
}

export interface NSelectScrollDownButtonProps extends ScrollDownButtonExtensions {
  una?: {
    selectScrollDownButton?: HTMLAttributes['class']
    selectScrollDownButtonIcon?: HTMLAttributes['class']
  }
}

export interface NSelectScrollUpButtonProps extends ScrollUpButtonExtensions {
  una?: {
    selectScrollUpButton?: HTMLAttributes['class']
    selectScrollUpButtonIcon?: HTMLAttributes['class']
  }
}

export interface NSelectContentProps extends ContentExtensions {
  _selectScrollDownButton?: NSelectScrollDownButtonProps
  _selectScrollUpButton?: NSelectScrollUpButtonProps
  _selectSeparator?: NSelectSeparator

  una?: {
    selectContent?: HTMLAttributes['class']
  }
}

export interface NSelectItemIndicatorProps extends SelectItemIndicatorProps {
  icon?: HTMLAttributes['class']
  class?: HTMLAttributes['class']
  una?: {
    selectItemIndicator?: HTMLAttributes['class']
    selectItemIndicatorIcon?: HTMLAttributes
  }
}

export interface NSelectItemProps extends ItemExtensions {
  value: any
  selectItem?: HTMLAttributes['class']
  isSelected?: boolean

  _selectItemText?: NSelectItemTextProps
  _selectItemIndicator?: NSelectItemIndicatorProps

  una?: {
    selectItem?: HTMLAttributes['class']

    selectItemIndicatorWrapper?: HTMLAttributes['class']
  }
}

export interface NSelectItemTextProps extends ItemTextExtensions {
  una?: {
    selectItemText?: HTMLAttributes['class']
  }
}

export interface NSelectGroupProps extends GroupExtensions {
  una?: {
    selectGroup?: HTMLAttributes['class']
  }
}

export interface NSelectLabelProps extends LabelExtensions {
  una?: {
    selectLabel?: HTMLAttributes['class']
  }
}

export interface NSelectSeparator extends SeparatorExtensions {
  una?: {
    selectSeparator?: HTMLAttributes['class']
  }
}

Presets

type SelectPrefix = 'select'

export const staticSelect: Record<`${SelectPrefix}-${string}` | SelectPrefix, string> = {
  // configurations
  'select': '',
  'select-default-variant': 'btn-solid-white',
  'select-disabled': 'n-disabled',
  'select-scroll': 'flex cursor-default items-center justify-center py-1',
  'select-trigger-info-icon': 'i-info',
  'select-trigger-error-icon': 'i-error',
  'select-trigger-success-icon': 'i-success',
  'select-trigger-warning-icon': 'i-warning',

  // components
  'select-root': '',
  'select-trigger': 'min-h-2.5em w-full', // [&>span]:line-clamp-1
  'select-trigger-trailing-icon': 'i-lucide-chevrons-up-down !text-1.042em',
  'select-trigger-trailing': 'ml-auto n-disabled',
  'select-trigger-leading': '',

  'select-value': 'h-1.5em',

  'select-content': 'relative z-50 max-h-96 min-w-32 overflow-hidden rounded-md border border-base bg-popover text-popover shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
  'select-content-popper': 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',

  'select-group': 'p-1 w-full',

  'select-separator': '-mx-1 my-1 h-px bg-muted',

  'select-item': 'select-item-gray relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-1em outline-none data-[disabled]:pointer-events-none data-[disabled]:n-disabled',

  'select-item-indicator': 'absolute left-2 h-0.75em w-0.75em flex items-center justify-center',
  'select-item-indicator-icon': 'i-check',

  'select-viewport': 'p-1',
  'select-viewport-popper': 'h-[--radix-select-trigger-height] w-full min-w-[--radix-select-trigger-width]',

  'select-scroll-up-button': 'select-scroll',
  'select-scroll-down-button': 'select-scroll',
  'select-scroll-up-button-icon': 'i-lucide-chevron-up',
  'select-scroll-down-button-icon': 'i-lucide-chevron-down',

  'select-label': 'py-1.5 pl-8 pr-2 text-1em font-semibold',

  // ⚠️ for overriding purposes only
  'select-item-selectItem': '',
}

export const dynamicSelect = [
  [/^select-(\S+)-(\S+)$/, ([, v = 'solid', c = 'gray']) => `btn-${v}-${c}`],

  [/^select-item(-(\S+))?$/, ([, , c = 'gray']) => `focus:bg-${c}-100 focus:text-${c}-800 dark:focus:bg-${c}-800 dark:focus:text-${c}-100`],
]

export const select = [
  ...dynamicSelect,
  staticSelect,
]

Component

<script setup lang="ts">
import { computed, provide } from 'vue'
import type { SelectRootEmits } from 'radix-vue'
import {
  useForwardPropsEmits,
} from 'radix-vue'
import type { NSelectProps } from '../../../types'
import { isEqualObject, omitProps } from '../../../utils'
import SelectRoot from './SelectRoot.vue'
import SelectTrigger from './SelectTrigger.vue'
import SelectGroup from './SelectGroup.vue'
import SelectItem from './SelectItem.vue'
import SelectLabel from './SelectLabel.vue'
import SelectContent from './SelectContent.vue'
import SelectValue from './SelectValue.vue'
import SelectSeparator from './SelectSeparator.vue'

const props = withDefaults(defineProps<NSelectProps>(), {
  size: 'sm',
})

const emits = defineEmits<SelectRootEmits>()

const modelValue = defineModel<any>('modelValue')

const delegatedProps = computed(() => {
  const { class: _, ...delegated } = props

  return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)

const transformerValue = computed(() => {
  if (typeof modelValue.value === 'object') {
    if (forwarded.value.valueAttribute)
      return modelValue.value[forwarded.value.valueAttribute]

    if (forwarded.value.itemAttribute)
      return modelValue.value[forwarded.value.itemAttribute]
  }

  return modelValue.value
})

provide('selectModelValue', modelValue)
</script>

<template>
  <SelectRoot
    v-bind="omitProps(forwarded, ['items', 'multipleGroup', 'itemAttribute', 'placeholder', 'label', 'id', 'select'])"
    :model-value="transformerValue"
  >
    <SelectTrigger
      :id
      :size
      :status
      :select
      v-bind="forwarded._selectTrigger"
    >
      <slot name="trigger" :value="modelValue">
        <SelectValue
          v-bind="forwarded._selectValue"
          :placeholder="forwarded._selectValue?.placeholder || forwarded.placeholder"
        >
          <slot :value="modelValue">
            {{ transformerValue }}
          </slot>
        </SelectValue>
      </slot>
    </SelectTrigger>

    <SelectContent
      :size
      v-bind="{
        ...forwarded._selectContent,
        _selectScrollDownButton: forwarded._selectScrollDownButton,
        _selectScrollUpButton: forwarded._selectScrollUpButton,
        _selectViewport: forwarded._selectViewport,
      }"
    >
      <slot name="content" :items="forwarded.items">
        <!--  single-group -->
        <template v-if="!forwarded.multipleGroup">
          <SelectLabel
            v-if="forwarded.label"
            v-bind="forwarded._selectLabel"
          >
            <slot name="label" :label="forwarded.label">
              {{ forwarded.label }}
            </slot>
          </SelectLabel>

          <template
            v-for="item in items"
            :key="item"
          >
            <SelectItem
              :value="item"
              :size
              :select-item
              v-bind="{ ...props._selectItem, ...item._selectItem }"
              :is-selected="isEqualObject(item, modelValue)"
            >
              <slot name="item" :item="item">
                {{ props.itemAttribute ? item[props.itemAttribute] : item }}
              </slot>
            </SelectItem>
          </template>
        </template>

        <!-- multiple-group -->
        <template
          v-else
        >
          <SelectGroup
            v-for="(groupItems, i) in items"
            :key="i"
            v-bind="forwarded._selectGroup"
          >
            <SelectSeparator
              v-if="i > 0"
              v-bind="forwarded._selectSeparator"
            />

            <slot name="group" :items="groupItems">
              <SelectLabel
                v-if="groupItems.label"
                :size="forwarded.size"
                v-bind="{ ...forwarded._selectLabel, ...groupItems._selectLabel }"
              >
                <slot name="label" :label="groupItems.label">
                  {{ groupItems.label }}
                </slot>
              </SelectLabel>

              <template
                v-for="groupItem in groupItems.items"
                :key="groupItem"
              >
                <SelectItem
                  :value="groupItem "
                  :size="forwarded.size"
                  v-bind="{ ...forwarded._selectItem, ...groupItems?._selectItem, ...groupItem._selectItem }"
                  :is-selected="groupItem === transformerValue"
                >
                  <slot name="item" :item="groupItem">
                    {{ props.itemAttribute ? groupItem[props.itemAttribute] : groupItem }}
                  </slot>
                </SelectItem>
              </template>
            </slot>
          </SelectGroup>
        </template>
        <slot />
      </slot>
    </SelectContent>
  </SelectRoot>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import {
  SelectContent,
  type SelectContentEmits,
  SelectPortal,
  SelectViewport,
  useForwardPropsEmits,
} from 'radix-vue'
import { cn } from '../../../utils'
import type { NSelectContentProps } from '../../../types'
import SelectScrollDownButton from './SelectScrollDownButton.vue'
import SelectScrollUpButton from './SelectScrollUpButton.vue'

defineOptions({
  inheritAttrs: false,
})

const props = withDefaults(
  defineProps<NSelectContentProps>(),
  {
    position: 'popper',
  },
)
const emits = defineEmits<SelectContentEmits>()

const delegatedProps = computed(() => {
  const { class: _, ...delegated } = props

  return delegated
})

const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>

<template>
  <SelectPortal>
    <SelectContent
      v-bind="{ ...forwarded, ...$attrs }"
      :class="cn(
        'select-content',
        position === 'popper'
          && 'select-content-popper',
        props.class,
      )
      "
    >
      <SelectScrollUpButton
        v-bind="forwarded._selectScrollUpButton"
      />
      <SelectViewport
        :class="cn(
          'select-viewport',
          position === 'popper' && 'select-viewport-popper')"
        v-bind="forwarded._selectViewport"
      >
        <slot />
      </SelectViewport>
      <SelectScrollDownButton
        v-bind="forwarded._selectScrollDownButton"
      />
    </SelectContent>
  </SelectPortal>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { SelectGroup } from 'radix-vue'
import { cn } from '../../../utils'
import type { NSelectGroupProps } from '../../../types'

const props = defineProps<NSelectGroupProps>()

const delegatedProps = computed(() => {
  const { class: _, ...delegated } = props

  return delegated
})
</script>

<template>
  <SelectGroup :class="cn('select-group', props.class)" v-bind="delegatedProps">
    <slot />
  </SelectGroup>
</template>
<script setup lang="ts">
import { computed, inject } from 'vue'
import {
  SelectItem,
  useForwardProps,
} from 'radix-vue'
import { cn } from '../../../utils'
import type { NSelectItemProps, NSelectProps } from '../../../types'
import SelectItemText from './SelectItemText.vue'
import SelectItemIndicator from './SelectItemIndicator.vue'

const props = withDefaults(defineProps<NSelectItemProps>(), {})

const delegatedProps = computed(() => {
  const { class: _, ...delegated } = props

  return delegated
})

const forwardedProps = useForwardProps(delegatedProps)
const modelValue = inject<NSelectProps>('selectModelValue')

const isSelected = computed(() => {
  return props.isSelected || modelValue === props.value
})
</script>

<template>
  <SelectItem
    v-bind="forwardedProps"
    :class="
      cn(
        'select-item',
        props.class,
        props.una?.selectItem,
      )
    "
    :select-item
    :aria-selected="isSelected"
    :aria-checked="isSelected"
    :is-selected="isSelected"
    :data-state="true ? 'checked' : 'unchecked'"
  >
    <SelectItemIndicator
      v-if="isSelected"
      v-bind="forwardedProps._selectItemIndicator"
    >
      <slot name="item-indicator" />
    </SelectItemIndicator>

    <SelectItemText
      v-bind="forwardedProps._selectItemText"
    >
      <slot />
    </SelectItemText>
  </SelectItem>
</template>
<script setup lang="ts">
import { SelectItemText } from 'radix-vue'
import type { NSelectItemTextProps } from '../../../types'

const props = defineProps<NSelectItemTextProps>()
</script>

<template>
  <SelectItemText v-bind="props">
    <slot />
  </SelectItemText>
</template>
<script setup lang="ts">
import { SelectLabel } from 'radix-vue'
import { cn } from '../../../utils'
import type { NSelectLabelProps } from '../../../types'

const props = defineProps<NSelectLabelProps>()
</script>

<template>
  <SelectLabel :class="cn('select-label', props.class)">
    <slot />
  </SelectLabel>
</template>
<script setup lang="ts">
import type { SelectRootEmits } from 'radix-vue'
import { SelectRoot, useForwardPropsEmits } from 'radix-vue'
import type { NSelectRootProps } from '../../../types'
import { cn } from '../../../utils'

const props = defineProps<NSelectRootProps>()
const emits = defineEmits<SelectRootEmits>()

const forwarded = useForwardPropsEmits(props, emits)
</script>

<template>
  <SelectRoot
    :class="cn(
      'select-root',
      props.class,
      props.una?.selectRoot,
    )"
    v-bind="forwarded"
  >
    <slot />
  </SelectRoot>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { SelectScrollDownButton, useForwardProps } from 'radix-vue'
import Icon from '../../elements/Icon.vue'
import { cn } from '../../../utils'
import type { NSelectScrollDownButtonProps } from '../../../types'

const props = defineProps<NSelectScrollDownButtonProps>()

const delegatedProps = computed(() => {
  const { class: _, ...delegated } = props

  return delegated
})

const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <SelectScrollDownButton v-bind="forwardedProps" :class="cn('select-scroll-down-button', props.class)">
    <slot>
      <Icon
        :name="forwardedProps?.una?.selectScrollDownButtonIcon || 'select-scroll-down-button-icon'"
      />
    </slot>
  </SelectScrollDownButton>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { SelectScrollUpButton, useForwardProps } from 'radix-vue'
import Icon from '../../elements/Icon.vue'
import { cn } from '../../../utils'
import type { NSelectScrollUpButtonProps } from '../../../types'

const props = defineProps<NSelectScrollUpButtonProps>()

const delegatedProps = computed(() => {
  const { class: _, ...delegated } = props

  return delegated
})

const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <SelectScrollUpButton v-bind="forwardedProps" :class="cn('select-scroll-up-button', props.class)">
    <slot>
      <Icon
        :name="forwardedProps?.una?.selectScrollUpButtonIcon || 'select-scroll-up-button-icon'"
      />
    </slot>
  </SelectScrollUpButton>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { SelectSeparator } from 'radix-vue'
import { cn } from '../../../utils'
import type { NSelectSeparator } from '../../../types'

const props = defineProps<NSelectSeparator>()

const delegatedProps = computed(() => {
  const { class: _, ...delegated } = props

  return delegated
})
</script>

<template>
  <SelectSeparator v-bind="delegatedProps" :class="cn('select-separator', props.class)" />
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { SelectTrigger, useForwardProps } from 'radix-vue'
import { cn, randomId } from '../../../utils'
import type { NSelectTriggerProps } from '../../../types'
import Button from '../../elements/Button.vue'

const props = defineProps<NSelectTriggerProps>()

const delegatedProps = computed(() => {
  const { class: _, ...delegated } = props

  return delegated
})

const forwardedProps = useForwardProps(delegatedProps)

const statusClassVariants = computed(() => {
  const btn = {
    info: 'btn-outline-info',
    success: 'btn-outline-success',
    warning: 'btn-outline-warning',
    error: 'btn-outline-error',
    default: props.select ? `select-${props.select}` : 'select-default-variant',
  }

  const text = {
    info: 'text-info',
    success: 'text-success',
    warning: 'text-warning',
    error: 'text-error',
    default: '',
  }

  const icon = {
    info: props.una?.selectTriggerInfoIcon ?? 'select-trigger-info-icon',
    success: props.una?.selectTriggerSuccessIcon ?? 'select-trigger-success-icon',
    warning: props.una?.selectTriggerWarningIcon ?? 'select-trigger-warning-icon',
    error: props.una?.selectTriggerErrorIcon ?? 'select-trigger-error-icon',
    default: 'select-trigger-trailing-icon',
  }

  return {
    btn: btn[props.status ?? 'default'],
    text: text[props.status ?? 'default'],
    icon: icon[props.status ?? 'default'],
  }
})
</script>

<template>
  <SelectTrigger
    as-child
  >
    <Button
      v-bind="forwardedProps"
      :id="randomId('select-trigger')"
      :class="cn(
        'select-trigger justify-between font-normal',
        props.class,
      )"
      :trailing="statusClassVariants.icon"
      :una="{
        ...props.una,
        ...{
          btnLeading: cn(
            'select-trigger-leading',
            props.una?.btnLeading,
          ),
          btnTrailing: cn(
            'select-trigger-trailing',
            props.una?.btnTrailing,
            statusClassVariants.text,
          ),
          btnDefaultVariant: statusClassVariants.btn,
        },
      }"
    >
      <slot />
    </Button>
  </SelectTrigger>
</template>
<script setup lang="ts">
import { SelectValue } from 'radix-vue'
import type { NSelectValueProps } from '../../../types'

const props = defineProps<NSelectValueProps>()
</script>

<template>
  <SelectValue
    :class="{ 'select-value': !props.placeholder }"
    v-bind="props"
  >
    <slot />
  </SelectValue>
</template>