25 October, 2023
Nuxt UI multi-select dropdown filter
The front page of the Nuxt UI docs has a Labels multi-select menu with status colours. Inspired by that element, I wanted to create a filter for a data table that lets you select multiple labels/statuses/whatever, and shows the currently selected items as actionable badges. Here's how.
This is what we're going to build.
First up, this is the dummy data we're working with in our <script setup>
.
const statuses = [{
label: 'Pending',
color: 'amber'
},{
label: 'Approved',
color: 'lime',
},{
label: 'Cancelled',
color: 'rose'
},{
label: 'Processing',
color: 'purple'
},{
label: 'Shipped',
color: 'emerald'
}]
//default selection
const selectedStatuses = ref([
{label: 'Approved', color: 'lime'},
{label: 'Processing', color: 'purple'}
])
Now, let's start off building the customised multi-select menu. We're looking to add in some coloured dots/markers in the main menu to indicate which statuses are currently selected, as well as the relevant dot next to each status in the drop-down menu.
<UFormGroup label="Filter by status" name="filter:status" class="min-w-[12rem]">
<USelectMenu v-model="selectedStatuses" :options="statuses" by="label" multiple>
<template #label>
<span v-if="!selectedStatuses.length">Showing all</span>
<span v-else class="pl-1 flex items-center">
<span
v-for="status in selectedStatuses"
:key="status.label"
:class="[`bg-${status.color}-500`, 'inline-block h-3 w-3 flex-shrink-0 rounded-full -ml-1 border-2 border-white dark:border-gray-900']"
aria-hidden="true" />
<span class="ml-1">{{ selectedStatuses.length }} selected</span>
</span>
</template>
<template #option="{option:status}">
<span :class="[`bg-${status.color}-500`, 'inline-block h-2 w-2 flex-shrink-0 rounded-full']" aria-hidden="true" />
<span>{{status.label}}</span>
</template>
</USelectMenu>
</UFormGroup>
We set the multiple
property on a <USelectMenu/>
component, then use the #label
slot to add the coloured dots to the main menu. The dots have a negative left margin and white border applied so they overlap neatly.
We also use the #option
slot to add the relevant coloured dot next to each status in the drop-down menu.
Next up, we can add in the badges that show which filters are currently being applied. These will appear below the select menu and allow the user to quickly remove a specific status from the selection.
<div class="space-x-2 space-y-2 mt-4">
<UButtonGroup
size="xs"
v-for="status in selectedStatuses"
:key="status.label"
:ui="{ rounded: 'rounded-full' }">
<UBadge
color="white"
class="capitalize ps-3 hover:cursor-default hover:bg-white dark:hover:bg-white-950">
<span :class="[`bg-${status.color}-500`, 'inline-block h-2 w-2 rounded-full']" />
<span class="mx-2">{{ status.label }}</span>
</UBadge>
<UButton
@click="selectedStatuses.splice(selectedStatuses.indexOf(status), 1)"
icon="i-heroicons-x-mark"
color="white"
class="pe-2"
:aria-label="`Remove ${status.label} filter`"
:title="`Remove ${status.label} filter`" />
</UButtonGroup>
</div>
We loop through the selected statuses with a <ButtonGroup/>
component, which wraps the label badge with an actionable remove button at the end.
For simplicity I've put the logic to remove the status inline, but it's probably better to put that in a function inside your <script setup>
.
And there you have it – a good-looking, customisable way of adding multi-select status filters to a data table, using Nuxt UI.
You can find the full example to play around with in this Stackblitz .