Advanced

Row Actions

Add action menus to NuGrid rows.

NuGrid provides built-in support for row action menus, allowing users to perform actions on individual rows.

Enabling Actions

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>

Action Menu Options

interface NuGridActionMenuOptions<T> {
  getActions: (row: NuGridRow<T>) => NuGridActionMenuItem[]
  icon?: string
  label?: string
}

Action Item Types

Basic Action

{
  label: 'View Details',
  icon: 'i-lucide-eye',
  onSelect: () => { ... },
}

Colored Action

{
  label: 'Delete',
  icon: 'i-lucide-trash',
  color: 'error',  // Uses Nuxt UI colors
  onSelect: () => { ... },
}

Disabled Action

{
  label: 'Archive',
  icon: 'i-lucide-archive',
  disabled: true,
  onSelect: () => { ... },
}

Label (Non-Clickable)

{
  type: 'label',
  label: 'Actions',
}

Separator

{
  type: 'separator',
}

Dynamic Actions

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>

Grouped Actions

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: () => { ... },
    },
  ]
}

Custom Action Icon

Change the action menu trigger icon:

<template>
  <NuGrid
    :data="data"
    :columns="columns"
    :actions="{
      getActions,
      icon: 'i-lucide-settings',  // Custom icon
    }"
  />
</template>

Action Column Positioning

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>

Handling Action Results

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>

Confirmation Dialogs

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>

Example: Complete Action Menu

<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>

Next Steps

Excel Export

Export data to Excel.

State Persistence

Save and restore grid state.