NuGrid provides built-in support for row action menus, allowing users to perform actions on individual rows.
Add actions with the actions prop:
<script setup lang="ts">
const actionOptions = {
getActions: (row) => [
{
label: 'Edit',
icon: 'i-lucide-pencil',
onSelect: () => editUser(row.original),
},
{
label: 'Delete',
icon: 'i-lucide-trash',
color: 'error',
onSelect: () => deleteUser(row.original),
},
],
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:actions="actionOptions"
/>
</template>
interface NuGridActionMenuOptions<T> {
getActions: (row: NuGridRow<T>) => NuGridActionMenuItem[]
icon?: string
label?: string
}
{
label: 'View Details',
icon: 'i-lucide-eye',
onSelect: () => { ... },
}
{
label: 'Delete',
icon: 'i-lucide-trash',
color: 'error', // Uses Nuxt UI colors
onSelect: () => { ... },
}
{
label: 'Archive',
icon: 'i-lucide-archive',
disabled: true,
onSelect: () => { ... },
}
{
type: 'label',
label: 'Actions',
}
{
type: 'separator',
}
Generate actions based on row data:
<script setup lang="ts">
function getActions(row) {
const actions = [
{
label: 'View',
icon: 'i-lucide-eye',
onSelect: () => viewUser(row.original),
},
]
// Conditional actions
if (row.original.status === 'active') {
actions.push({
label: 'Deactivate',
icon: 'i-lucide-user-x',
onSelect: () => deactivateUser(row.original),
})
} else {
actions.push({
label: 'Activate',
icon: 'i-lucide-user-check',
onSelect: () => activateUser(row.original),
})
}
// Role-based actions
if (userRole.value === 'admin') {
actions.push(
{ type: 'separator' },
{
label: 'Delete',
icon: 'i-lucide-trash',
color: 'error',
onSelect: () => deleteUser(row.original),
}
)
}
return actions
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:actions="{ getActions }"
/>
</template>
Organize actions into groups:
function getActions(row) {
return [
{
type: 'label',
label: 'Quick Actions',
},
{
label: 'Edit',
icon: 'i-lucide-pencil',
onSelect: () => { ... },
},
{
label: 'Duplicate',
icon: 'i-lucide-copy',
onSelect: () => { ... },
},
{
type: 'separator',
},
{
type: 'label',
label: 'Status',
},
{
label: 'Mark as Complete',
icon: 'i-lucide-check',
onSelect: () => { ... },
},
{
label: 'Archive',
icon: 'i-lucide-archive',
onSelect: () => { ... },
},
{
type: 'separator',
},
{
label: 'Delete',
icon: 'i-lucide-trash',
color: 'error',
onSelect: () => { ... },
},
]
}
Change the action menu trigger icon:
<template>
<NuGrid
:data="data"
:columns="columns"
:actions="{
getActions,
icon: 'i-lucide-settings', // Custom icon
}"
/>
</template>
The action column is automatically added. Pin it to the right:
<script setup lang="ts">
const columnPinning = ref({
left: ['__selection'],
right: ['__actions'], // Pin actions to right
})
</script>
<template>
<NuGrid
v-model:column-pinning="columnPinning"
:data="data"
:columns="columns"
:actions="{ getActions }"
/>
</template>
Show feedback after actions:
<script setup lang="ts">
const toast = useToast()
function getActions(row) {
return [
{
label: 'Copy ID',
icon: 'i-lucide-copy',
onSelect: async () => {
await navigator.clipboard.writeText(String(row.original.id))
toast.add({
title: 'Copied',
description: 'ID copied to clipboard',
color: 'success',
})
},
},
{
label: 'Delete',
icon: 'i-lucide-trash',
color: 'error',
onSelect: async () => {
const confirmed = await confirmDelete()
if (confirmed) {
await deleteUser(row.original.id)
toast.add({
title: 'Deleted',
description: 'User has been deleted',
color: 'success',
})
}
},
},
]
}
</script>
Use modals for dangerous actions:
<script setup lang="ts">
const deleteModalOpen = ref(false)
const userToDelete = ref(null)
function getActions(row) {
return [
{
label: 'Delete',
icon: 'i-lucide-trash',
color: 'error',
onSelect: () => {
userToDelete.value = row.original
deleteModalOpen.value = true
},
},
]
}
async function confirmDelete() {
await api.deleteUser(userToDelete.value.id)
data.value = data.value.filter(u => u.id !== userToDelete.value.id)
deleteModalOpen.value = false
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:actions="{ getActions }"
/>
<UModal v-model:open="deleteModalOpen">
<UCard>
<template #header>Confirm Delete</template>
<p>Are you sure you want to delete {{ userToDelete?.name }}?</p>
<template #footer>
<div class="flex justify-end gap-2">
<UButton variant="outline" @click="deleteModalOpen = false">
Cancel
</UButton>
<UButton color="error" @click="confirmDelete">
Delete
</UButton>
</div>
</template>
</UCard>
</UModal>
</template>
<script setup lang="ts">
import type { NuGridRow, NuGridActionMenuItem } from '#nu-grid/types'
interface User {
id: number
name: string
email: string
status: 'active' | 'inactive' | 'pending'
role: 'admin' | 'user'
}
const toast = useToast()
function getActions(row: NuGridRow<User>): NuGridActionMenuItem[] {
const user = row.original
return [
{
type: 'label',
label: 'Actions',
},
{
label: 'View Profile',
icon: 'i-lucide-user',
onSelect: () => navigateTo(`/users/${user.id}`),
},
{
label: 'Edit',
icon: 'i-lucide-pencil',
onSelect: () => openEditModal(user),
},
{
label: 'Copy Email',
icon: 'i-lucide-copy',
onSelect: async () => {
await navigator.clipboard.writeText(user.email)
toast.add({ title: 'Email copied', color: 'success' })
},
},
{
type: 'separator',
},
{
type: 'label',
label: 'Status',
},
user.status === 'active'
? {
label: 'Deactivate',
icon: 'i-lucide-user-x',
color: 'warning',
onSelect: () => updateStatus(user.id, 'inactive'),
}
: {
label: 'Activate',
icon: 'i-lucide-user-check',
color: 'success',
onSelect: () => updateStatus(user.id, 'active'),
},
{
type: 'separator',
},
{
label: 'Delete',
icon: 'i-lucide-trash',
color: 'error',
disabled: user.role === 'admin',
onSelect: () => confirmDeleteUser(user),
},
]
}
</script>
<template>
<NuGrid
:data="data"
:columns="columns"
:actions="{
getActions,
icon: 'i-lucide-more-vertical',
}"
/>
</template>