Skip to content

Select

Base form select.

Usage

Simple Usage

preview
vue
<template>
  <p-select
    :options="options" />
</template>

<script setup>
  const options = ref(['Apple', 'Banana', 'Grape'])
</script>

with Text and Value

preview
vue
<template>
  <p-select
    v-model="value"
    :options="options" />
</template>

<script setup>
  const options = ref([
    { text: '🍎 Apfel', value: 'Apple' },
    { text: '🍇 Traube', value: 'Grape' },
    { text: '🍌 Bananen', value: 'Banana'},
  ])
</script>

with Disabled option

preview
vue
<template>
  <p-select
    v-model="value"
    :options="options" />
</template>

<script setup>
  const options = ref([
    { text: '🍎 Apfel', value: 'Apple' },
    { text: '🍇 Traube', value: 'Grape', disabled: true },
    { text: '🍌 Bananen', value: 'Banana', disabled: false},
  ])
</script>

Placeholder

You can set input placeholder via placeholder props

preview
vue
<template>
  <p-select placeholder="Pick A Value" />
</template>

Section Label

You can set sectiom label via section-label props

preview
vue
<template>
  <p-select placeholder="Pick A Value"
    :options="optionsA"
    section-label="Fruits" />
</template>

Clearable

preview
vue
<template>
  <p-select
    v-model="value"
    :options="options"
    clearable />
</template>

<script setup>
  const options = ref([
    { text: '🍎 Apfel', value: 'Apple' },
    { text: '🍇 Traube', value: 'Grape' },
    { text: '🍌 Bananen', value: 'Banana'},
  ])
</script>

Sizing

You can set size of select via size prop. Available size are lg, md, sm, xs. Default size is md.

preview
vue
<template>
  <p-select size="xs" v-model="value" :options="options" placeholder="Size xs" />
  <p-select size="sm" v-model="value" :options="options" placeholder="Size sm" />
  <p-select size="md" v-model="value" :options="options" placeholder="Size md" />
  <p-select size="lg" v-model="value" :options="options" placeholder="Size lg" />
</template>

Disabled State

preview
vue
<template>
  <p-select disabled />
</template>

Readonly State

preview
vue
<template>
  <p-select readonly />
</template>

Error State

preview
vue
<template>
  <p-select error />
</template>

Multiple Selection

preview

Result :

[]
vue
<template>
  <p-select :options="optionsD" v-model="multiValue" multiple />
</template>

Binding v-model

preview

Result :

-
vue
<template>
  <p-select
    v-model="value"
    :options="options" />
</template>

Binding raw value

If you want to get original selected item (text and value) not value only. you can use v-model:selected.

preview

v-model

-

v-model:selected

-
vue
<template>
  <p-select
    :options="optionsB"
    v-model="value"
    v-model:selected="selected" />
</template>

This component has build-in Fuzzy-search Adapter, powered by Fuze.js.

Example: try type nn, and you'll got Bananen

preview
vue
<template>
  <p-select
    v-model="value"
    :adapter="FuzzyAdapter"
    :options="options" />
</template>

<script setup>
  import { FuzzyAdapter } from '@privyid/persona/core'

  const options = ref([
    { text: '🍎 Apfel', value: 'Apple' },
    { text: '🍇 Traube', value: 'Grape' },
    { text: '🍌 Bananen', value: 'Banana'},
  ])
</script>

Handling Asynchronous

Somecase you will need to load your options from API. You just need define custom async adapter and request handler. It will take care of loading, inifinite load, and other stuff.

preview

Result:

Multiple value

preview

Result:

[]
vue
<template>
  <p-select
    :adapter="provincesAdapter"
    v-model="province"
    multiple />
</template>

<script setup>
  import { defineAsyncAdapter } from "@privyid/persona/core"
  import { getProvinces } from '~/api/region'

  const provincesAdapter = defineAsyncAdapter(async (keyword, page, perPage) => {
    const response = await getProvinces(keyword, page, perPage)

    return response.data.map((item) => {
      return {
        text : item.name,
        value: item.code,
      }
    })
  }, [])
</script>

Reactivity inside Handler

When you working with chaining select like province -> city form. Normally, when you select the province, it should trigger reload on city based on the province. To do this, you need add the province value as watch dependencies. It will automatically reload when province is changed

preview
vue
<template>
  <p-select
    v-model="province"
    placeholder="Select Province"
    :adapter="provincesAdapter"
    @user-input="city = ''" />
  <p-select
    v-model="city"
    placeholder="Select Cities"
    :adapter="citiesAdapter" />
</template>

<script setup>
  import { defineAsyncAdapter } from "@privyid/persona/core"
  import { getProvinces } from '~/api/region'

  const province = ref('')
  const city     = ref('')

  const provincesAdapter = defineAsyncAdapter(/* example above */)

  const citiesAdapter = defineAsyncAdapter(async (keyword, page, perPage) => {
    const response = await getCities(province.value, keyword, page, perPage)
                                    // 👆 reactive ref
    return response.data.map((item) => {
      return {
        text : item.name,
        value: item.id,
      }
    })
  }, [province]) // 👈 need to add `province` as watch deps
</script>

Customization

Custom Option

preview
vue
<template>
  <p-select :options="users">
    <template #option="{ item }">
      <div class="flex flex-row space-x-2">
        <div class="shrink-0">
          <p-avatar :src="item.img" />
        </div>
        <div class="grow">
          <div class="font-sans text-base font-normal">{{ item.text }}
          </div>
            <div class="text-xs font-light option-text">ID: {{ item.value }}
          </div>
        </div>
      </div>
    </template>
  </p-select>
</template>

<script setup>
  const users = ref([
    {
      text : 'John Doe',
      value: 1,
      img  : "https://picsum.photos/id/50/50",
    },
    {
      text : 'Tarjono',
      value: 2,
      img  : "https://picsum.photos/id/51/50",
    }
  ])
</script>

Custom Selected (Single)

preview
vue
<template>
  <p-select :options="users">
    <template #selected="{ item }">
      <div class="flex flex-row space-x-2">
        <div class="shrink-0">
          <p-avatar :src="item.img" />
        </div>
        <div class="grow">
          <div class="font-sans text-base font-normal">{{ item.text }}
          </div>
            <div class="text-xs font-light option-text">ID: {{ item.value }}
          </div>
        </div>
      </div>
    </template>
  </p-select>
</template>

<script setup>
  const users = ref([
    {
      text : 'John Doe',
      value: 1,
      img  : "https://picsum.photos/id/50/50",
    },
    {
      text : 'Tarjono',
      value: 2,
      img  : "https://picsum.photos/id/51/50",
    }
  ])
</script>

Custom Selected (Multiple)

preview
vue
<template>
  <p-select :options="users" multiple>
    <template #selected="{ item }">
      <div class="flex w-full">
        <div class="flex items-center pl-3">
          <p-avatar
            v-for="obj of item"
            :src="obj.img"
            class="-ml-3"
            size="xs" />
        </div>
        <div class="pl-2 grow">
          {{ item.length }} People selected
        </div>
      </div>
    </template>
  </p-select>
</template>

<script setup>
  const users = ref([
    {
      text : 'John Doe',
      value: 1,
      img  : "https://picsum.photos/id/50/50",
    },
    {
      text : 'Tarjono',
      value: 2,
      img  : "https://picsum.photos/id/51/50",
    }
  ])
</script>

Hide Caret

When you set the no-caret prop to true, it will hide the caret icon, and users won't see it in the component.

preview
vue
<template>
  <p-select :options="optionsA" />
</template>

<script setup>
  const options = ref(['Apple', 'Banana', 'Grape'])
</script>

API

Props

PropsTypeDefaultDescription
optionsArray-Select option's items
placeholderString-Input placeholder
disabledBooleanfalseDisabled state
readonlyBooleanfalseReadonly state
errorBooleanfalseError state
emptyTextStringNo DataLabel when options is empty
loadingTextStringLoading...Label when loading
sectionLabelString-Add section label
adapterAdapterBaseAdapterAdapter for loading option's items
no-caretBooleanfalseHide caret icon
dividerBoolean-Enable divider in select-item
menuClassString | Array | Object-CSS class to add in the select menu container
menuSizeStringsmSelect menu size, valid value is sm, md, lg and xl
multipleBooleanfalseEnable multiple select
displayLimitNumber2Maximum of visible tags before truncated into single tag
limitTextString+{n} Other(s)Text on truncated tag (Multiple Mode only)
searchableBooleantrueEnable searchbar
noCloseAfterSelectBooleanfalseDisabled close popup after select an item
modelValueAny-v-model value
selectedObject | Array-v-model:selected value

Slots

NameDescription
emptyContent when option is empty or not found
loadingContent when loading
optionContent to place in Option Items
selectedContent to place for selected result
placeholderContent to place for placeholder
activatorContent to replace Select's input
caretElement for opening and closing the dropdown

Events

NameArgumentsDescription
changeObjectEvent when value changed
userInputObjectSimilar to change, but it's only triggered when user really clicked the option

See Also

Released under the MIT License.