PXD

List

Scrollable listbox driven by the options prop, with groups, single or multiple selection, keyboard navigation and virtualized rows for large option sets.

Default

Use v-model to bind the selected value.

<script setup>
import { ref } from 'vue'

const value = ref('manage-extensions')

const options = [
  { label: 'Figma Import', value: 'figma-import', description: 'Jump to figma import' },
  { label: 'Import Extension', value: 'import-extension', description: 'Jump to import extension' },
  { label: 'Manage Extensions', value: 'manage-extensions', description: 'Jump to manage extensions' },
]
</script>

<template>
  <PList v-model="value" class="w-64" :options="options" />
</template>

Multiple

Set multiple to select several options, the model becomes an array.

<script setup>
import { ref } from 'vue'

const value = ref(['import-extension'])

const options = [
  { label: 'Figma Import', value: 'figma-import' },
  { label: 'Import Extension', value: 'import-extension' },
  { label: 'Manage Extensions', value: 'manage-extensions' },
  { label: 'Flags Explorer', value: 'flags-explorer' },
]
</script>

<template>
  <PList v-model="value" class="w-64" multiple :options="options" />
</template>

Groups

A group entry renders a non-navigable header followed by its options.

<script setup>
import { ref } from 'vue'

const value = ref('flags-explorer')

const options = [
  { label: 'Figma Import', value: 'figma-import' },
  {
    type: 'group',
    label: 'Explorers',
    options: [
      { label: 'Flags Explorer', value: 'flags-explorer' },
      { label: 'File Explorer', value: 'file-explorer' },
    ],
  },
]
</script>

<template>
  <PList v-model="value" class="w-64" :options="options" />
</template>

Disabled and variants

Set disabled to skip an option while navigating, variant to color its text and background.

<script setup>
const options = [
  { label: 'Import Extension', value: 'import-extension' },
  { disabled: true, label: 'Manage Extensions', value: 'manage-extensions' },
  { label: 'Reset Token', value: 'reset-token', variant: 'warning' },
  { label: 'Delete Workspace', value: 'delete-workspace', variant: 'error' },
]
</script>

<template>
  <PList class="w-64" :options="options" />
</template>

Custom item

The item slot replaces the content of an option.

<script setup>
const options = [
  { label: 'Figma Import', value: 'figma-import' },
  { label: 'Manage Extensions', value: 'manage-extensions' },
]
</script>

<template>
  <PList class="w-64" :options="options">
    <template #item="{ item, index }">
      <strong>{{ item.label }}</strong>
      <span class="text-gray-600">#{{ index }}</span>
    </template>
  </PList>
</template>

Empty

<script setup>
const options = []
</script>

<template>
  <PList class="w-64" :options="options">
    <template #empty>No results found.</template>
  </PList>
</template>

Virtual

Set virtual to render only the visible rows, item-size is the estimated row height in pixels.

<script setup>
const options = Array.from({ length: 1000 }, (_, index) => ({
  label: `Extension ${index + 1}`,
  value: `extension-${index + 1}`,
}))
</script>

<template>
  <PList
    class="w-64 max-h-40"
    virtual
    :item-size="36"
    :over-scan="8"
    :options="options"
  />
</template>

Keyboard navigation

The list does not listen to the keyboard by itself, map the keys to the dispatch method through the template ref.

<script setup>
import { ref } from 'vue'

const list = ref()
const value = ref('import-extension')

const keymap = {
  ArrowDown: 'next',
  ArrowUp: 'previous',
  End: 'last',
  Enter: 'activate',
  Home: 'first',
}

const options = [
  { label: 'Figma Import', value: 'figma-import' },
  { label: 'Import Extension', value: 'import-extension' },
  { label: 'Manage Extensions', value: 'manage-extensions' },
  { label: 'Flags Explorer', value: 'flags-explorer' },
]

function onKeydown(ev) {
  const command = keymap[ev.key]

  if (command && list.value?.dispatch(command)) {
    ev.preventDefault()
  }
}
</script>

<template>
  <PStack align="center">
    <PList ref="list" v-model="value" class="w-64" :options="options" @keydown="onKeydown" />

    <PButton size="sm" @click="list?.focus()"> Focus </PButton>
  </PStack>
</template>

Props

NameTypeDefaultDescription
model-valueListModelValue-Selected value, an array when multiple is set
optionsListOptions() => []Options and group entries to render
item-classComponentClass-Class merged into every internally rendered list item
multipleboolean-Let several options be selected at the same time
default-active-indexnumber-1Option active before the user navigates
loopbooleantrueWrap keyboard navigation around both ends
virtualbooleanfalseEnable virtualized rendering for large option sets
item-sizenumber36Estimated row height in px when virtual is enabled
over-scannumber4Extra rows rendered outside the viewport when virtual is enabled

Any other key of an option is forwarded to its root element, so href, to or target work as expected.

ListOption

NameTypeDescription
valuestring | numberValue written to the model when the option is selected
labelstring | number | nullText of the option
descriptionstringSecond line below the label
variant'default' | 'error' | 'warning'Text and background colors of the option
disabledbooleanIgnore clicks and skip the option while navigating
asstring | objectElement or component rendered as the option root
keywordsstring[]Extra terms matched by the filter of PCommandMenu

ListOptionGroup

NameTypeDescription
type'group'Marks the entry as a group
labelstringText of the group header
optionsListOption[]Options rendered under the header

ListItem Props

NameTypeDefaultDescription
valuestring | number-Value written to the model when the item is selected
labelstring | number | null-Text of the item
descriptionstring-Second line below the label
variant'default' | 'error' | 'warning''default'Text and background colors of the item
disabledbooleanfalseIgnore clicks and skip the item while navigating
asstring | object'div'Element or component rendered as the item root
indexnumber-Navigable index inside the parent list
activebooleanfalseWhether the item is the keyboard or pointer active one

ListGroup Props

NameTypeDefaultDescription
labelstring-Text of the group header, also used as its accessible name

Events

NameTypeDescription
change(option: ListOptionSelected) => voidEmitted when an option is selected.
update:modelValue(value: ListModelValue) => voidEmitted when the selection changes.

change carries the selected option without as and keywords:

interface ListOptionSelected {
  label?: string | number
  value: string | number
  disabled?: boolean
  variant?: 'default' | 'error' | 'warning'
  description?: string
}

ListItem Events

NameTypeDescription
click(value: string | number, ev: MouseEvent) => voidEmitted when the item is clicked.

Slots

NameDescription
itemItem content: { item, index, group, groupIndex }
emptyEnable when there is no data to display

Methods

NameTypeDescription
focus() => voidFocus the list container.
dispatch(command: ListNavigationCommand) => booleanRun a navigation command, returns false when it cannot be applied.
setActiveIndex(index: number) => voidMake the option at the given navigable index active.
setFirstAsActive() => voidMake the first enabled option active.
activeIndexnumberNavigable index of the active option, -1 when none is active.
type ListNavigationCommand =
  | 'first'
  | 'last'
  | 'next'
  | 'previous'
  | 'activate'
  | 'enter-child'
  | 'leave-parent'

PList handles first, last, next, previous and activate, while enter-child and leave-parent are provided by the components that embed the list, such as PMenu.

Source

Source